File:  [LON-CAPA] / loncom / cgi / mimeTeX / mimetex.c
Revision 1.5: download - view: text, annotated - select for diffs
Sat Jun 9 00:58:11 2012 UTC (12 years, 1 month ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, version_2_11_X, version_2_11_5, version_2_11_4_uiuc, version_2_11_4_msu, version_2_11_4, version_2_11_3_uiuc, version_2_11_3_msu, version_2_11_3, version_2_11_2_uiuc, version_2_11_2_msu, version_2_11_2_educog, version_2_11_2, version_2_11_1, version_2_11_0_RC3, version_2_11_0_RC2, version_2_11_0_RC1, version_2_11_0, HEAD
- upgrade to 1.74.

    1: /****************************************************************************
    2:  *
    3:  * Copyright(c) 2002-2012, 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:  *		rastmag(rp,magstep)   new raster magnified by "magstep" to rp
   79:  *		bytemapmag(bytemap,width,height,magstep)      magnify bytemap
   80:  *		rastref(rp,axis)    new raster reflected (axis 1=horz,2=vert)
   81:  *		rastput(target,source,top,left,isopaque)  overlay src on trgt
   82:  *		rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
   83:  *		rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
   84:  *		rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
   85:  *		rastile(tiles,ntiles)      create composite raster from tiles
   86:  *		rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
   87:  *		rastsmashcheck(term)         check if term is "safe" to smash
   88:  *		--- raster "drawing" functions ---
   89:  *		accent_subraster(accent,width,height,direction,pixsz)\hat\vec
   90:  *		arrow_subraster(width,height,drctn,isBig)    left/right arrow
   91:  *		uparrow_subraster(width,height,drctn,isBig)     up/down arrow
   92:  *		rule_raster(rp,top,left,width,height,type)    draw rule in rp
   93:  *		line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
   94:  *		line_recurse(rp,row0,col0,row1,col1,thickness)   recurse line
   95:  *		circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
   96:  *		circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
   97:  *		bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively
   98:  *		border_raster(rp,ntop,nbot,isline,isfree)put border around rp
   99:  *		backspace_raster(rp,nback,pback,minspace,isfree)    neg space
  100:  *		--- raster (and chardef) output functions ---
  101:  *		type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
  102:  *		type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
  103:  *		xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
  104:  *		type_pbmpgm(rp,ptype,file)     pbm or pgm image of rp to file
  105:  *		cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
  106:  *		cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
  107:  *		hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
  108:  *		--- ancillary output functions ---
  109:  *		emit_string(fp,col1,string,comment) emit string and C comment
  110:  *		gftobitmap(rp)        convert .gf-like pixmap to bitmap image
  111:  *		====================== Font Functions =======================
  112:  *		--- font lookup functions ---
  113:  *		get_symdef(symbol)              return mathchardef for symbol
  114:  *		get_ligature(expr,family)  return symtable index for ligature
  115:  *		get_chardef(symdef,size)       return chardef for symdef,size
  116:  *		get_charsubraster(symdef,size)  wrap subraster around chardef
  117:  *		get_symsubraster(symbol,size)    returns subraster for symbol
  118:  *		--- ancillary font functions ---
  119:  *		get_baseline(gfdata)       determine baseline (in our coords)
  120:  *		get_delim(symbol,height,family) delim just larger than height
  121:  *		make_delim(symbol,height) construct delim exactly height size
  122:  *		================= Tokenize/Parse Functions ==================
  123:  *		texchar(expression,chartoken)  retruns next char or \sequence
  124:  *		texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
  125:  *		texleft(expr,subexpr,maxsubsz,ldelim,rdelim)   \left...\right
  126:  *		texscripts(expression,subscript,superscript,which)get scripts
  127:  *		--- ancillary parse functions ---
  128:  *		isbrace(expression,braces,isescape)   check for leading brace
  129:  *		preamble(expression,size,subexpr)              parse preamble
  130:  *		mimeprep(expression) preprocessor converts \left( to \(, etc.
  131:  *		strchange(nfirst,from,to)   change nfirst chars of from to to
  132:  *		strreplace(string,from,to,nreplace)  change from to to in str
  133:  *		strwstr(string,substr,white,sublen)     find substr in string
  134:  *		strdetex(s,mode)    replace math chars like \^_{} for display
  135:  *		strtexchr(string,texchr)                find texchr in string
  136:  *		findbraces(expression,command)    find opening { or closing }
  137:  *		strpspn(s,reject,segment)     non-() chars of s not in reject
  138:  *		isstrstr(string,snippets,iscase)  are any snippets in string?
  139:  *		isnumeric(s)                     determine if s is an integer
  140:  *		evalterm(store,term)     evaluate numeric value of expression
  141:  *		getstore(store,identifier)return value corresponding to ident
  142:  *		unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
  143:  *	PART3	=========== Rasterize an Expression (recursively) ===========
  144:  *		--- here's the primary entry point for all of mimeTeX ---
  145:  *		rasterize(expression,size)     parse and rasterize expression
  146:  *		--- explicitly called handlers that rasterize... ---
  147:  *		rastparen(subexpr,size,basesp)          parenthesized subexpr
  148:  *		rastlimits(expression,size,basesp)    dispatch super/sub call
  149:  *		rastscripts(expression,size,basesp) super/subscripted exprssn
  150:  *		rastdispmath(expression,size,sp)      scripts for displaymath
  151:  *		--- table-driven handlers that rasterize... ---
  152:  *		rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
  153:  *		rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
  154:  *		rastmiddle(expression,size,basesp,arg1,arg2,arg3)     \middle
  155:  *		rastflags(expression,size,basesp,flag,value,arg3)    set flag
  156:  *		rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
  157:  *		rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
  158:  *		rastarrow(expression,size,basesp,width,height,drctn) \longarr
  159:  *		rastuparrow(expression,size,basesp,width,height,drctn)up/down
  160:  *		rastoverlay(expression,size,basesp,overlay,arg2,arg3)    \not
  161:  *		rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
  162:  *		rastackrel(expression,size,basesp,base,arg2,arg3)   \stackrel
  163:  *		rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
  164:  *		rastsqrt(expression,size,basesp,arg1,arg2,arg3)         \sqrt
  165:  *		rastaccent(expression,size,basesp,accent,isabove,isscript)
  166:  *		rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
  167:  *		rastbegin(expression,size,basesp,arg1,arg2,arg3)     \begin{}
  168:  *		rastarray(expression,size,basesp,arg1,arg2,arg3)       \array
  169:  *		rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture
  170:  *		rastline(expression,size,basesp,arg1,arg2,arg3)         \line
  171:  *		rastrule(expression,size,basesp,arg1,arg2,arg3)         \rule
  172:  *		rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle
  173:  *		rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier
  174:  *		rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox
  175:  *		rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox
  176:  *		rastmagnify(expression,size,basesp,arg1,arg2,arg3)   \magnify
  177:  *		rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
  178:  *		rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
  179:  *		rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
  180:  *		rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
  181:  *		rasteval(expression,size,basesp,arg1,arg2,arg3)         \eval
  182:  *		rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today
  183:  *		rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
  184:  *		rastenviron(expression,size,basesp,arg1,arg2,arg3)   \environ
  185:  *		rastmessage(expression,size,basesp,arg1,arg2,arg3)   \message
  186:  *		rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
  187:  *		--- helper functions for handlers ---
  188:  *		rastopenfile(filename,mode)      opens filename[.tex] in mode
  189:  *		rasteditfilename(filename)       edit filename (for security)
  190:  *		rastreadfile(filename,islock,tag,value)   read <tag>...</tag>
  191:  *		rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
  192:  *		calendar(year,month,day)    formats one-month calendar string
  193:  *		timestamp(tzdelta,ifmt)              formats timestamp string
  194:  *		tzadjust(tzdelta,year,month,day,hour)        adjust date/time
  195:  *		daynumber(year,month,day)     #days since Monday, Jan 1, 1973
  196:  *		strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
  197:  *		strnlower(s,n)        lowercase the first n chars of string s
  198:  *		urlprune(url,n)  http://abc.def.ghi.com/etc-->abc.def.ghi.com
  199:  *		urlncmp(url1,url2,n)   compares topmost n levels of two url's
  200:  *		dbltoa(d,npts)                double to comma-separated ascii
  201:  *		=== Anti-alias completed raster (lowpass) or symbols (ss) ===
  202:  *		aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
  203:  *		aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
  204:  *		aapnmlookup(rp,bytemap,grayscale)  aapnm based on aagridnum()
  205:  *		aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
  206:  *		aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
  207:  *		aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
  208:  *		aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
  209:  *		aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
  210:  *		aafollowline(rp,irow,icol,direction)       looks for a "turn"
  211:  *		aagridnum(rp,irow,icol)             calculates gridnum, 0-511
  212:  *		aapatternnum(gridnum)    looks up pattern#, 1-51, for gridnum
  213:  *		aalookup(gridnum)     table lookup for all possible 3x3 grids
  214:  *		aalowpasslookup(rp,bytemap,grayscale)   driver for aalookup()
  215:  *		aasupsamp(rp,aa,sf,grayscale)             or by supersampling
  216:  *		aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
  217:  *		aaweights(width,height)      builds "canonical" weight matrix
  218:  *		aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
  219:  *		=== miscellaneous ===
  220:  *		mimetexsetmsg(newmsglevel,newmsgfp)    set msglevel and msgfp
  221:  *	PART1	========================== Driver ===========================
  222:  *		main(argc,argv) parses math expression and emits mime xbitmap
  223:  *		CreateGifFromEq(expression,gifFileName)  entry pt for win dll
  224:  *		ismonth(month)          is month current month ("jan"-"dec")?
  225:  *		logger(fp,msglevel,logvars)        logs environment variables
  226:  *		emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile
  227:  *		readcachefile(cachefile,buffer)    read cachefile into buffer
  228:  *		advertisement(expression,mode)  wrap expression in ad message
  229:  *		crc16(s)                               16-bit crc of string s
  230:  *		md5str(instr)                      md5 hash library functions
  231:  *		GetPixel(x,y)           callback function for gifsave library
  232:  *
  233:  * Source:	mimetex.c  (needs mimetex.h and texfonts.h to compile,
  234:  *		and also needs gifsave.c when compiled with -DAA or -DGIF)
  235:  *
  236:  * --------------------------------------------------------------------------
  237:  * Notes      o	See individual function entry points for specific comments
  238:  *		about the purpose, calling sequence, side effects, etc
  239:  *		of each mimeTeX function listed above.
  240:  *	      o	See bottom of file for main() driver (and "friends"),
  241:  *		and compile as
  242:  *		   cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
  243:  *		to produce an executable that emits gif images with
  244:  *		anti-aliasing (see Notes below).  You may also compile
  245:  *		   cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
  246:  *		to produce an executable that emits gif images without
  247:  *		anti-aliasing.  Alternatively, compile mimeTeX as
  248:  *		   cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
  249:  *		to produce an executable that just emits mime xbitmaps.
  250:  *		In either case you'll need mimetex.h and texfonts.h,
  251:  *		and with -DAA or -DGIF you'll also need gifsave.c
  252:  *	      o	The font information in texfonts.h was produced by multiple
  253:  *		runs of gfuntype, one run per struct (i.e., one run per font
  254:  *		family at a particular size).  Compile gfuntype as
  255:  *		   cc gfuntype.c mimetex.c -lm -o gfuntype
  256:  *		See gfuntype.c, and also mimetex.html#fonts, for details.
  257:  *	      o	For gif images, the gifsave.c library by Sverre H. Huseby
  258:  *		<http://shh.thathost.com> slightly modified by me to allow
  259:  *		(a)sending output to stdout or returning it in memory,
  260:  *		and (b)specifying a transparent background color index,
  261:  *		is included with mimeTeX, and it's documented in
  262:  *		mimetex.html#gifsave
  263:  *	      o	MimeTeX's principal reusable function is rasterize(),
  264:  *		which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
  265:  *		and returns a (sub)raster representing it as a bit or bytemap.
  266:  *		Your application can do anything it likes with this pixel map.
  267:  *		MimeTeX just outputs it, either as a mime xbitmap or as a gif.
  268:  *		See  mimetex.html#makeraster  for further discussion
  269:  *		and examples.
  270:  *	      o	File mimetex.c also contains library functions implementing
  271:  *		a raster datatype, functions to manipulate rasterized .mf
  272:  *		fonts (see gfuntype.c which rasterizes .mf fonts), functions
  273:  *		to parse LaTeX expressions, etc.  As already mentioned,
  274:  *		a complete list of mimetex.c functions is above.  See their
  275:  *		individual entry points below for further comments.
  276:  *		   As also mentioned, these functions eventually belong in
  277:  *		several different modules, possibly along the lines suggested
  278:  *		by the divisions above.  But until the best decomposition
  279:  *		becomes clear, it seems better to keep mimetex.c
  280:  *		neatly together, avoiding a bad decomposition that
  281:  *		becomes permanent by default.
  282:  *	      o	Optional compile-line -D defined symbols are documented
  283:  *		in mimetex.html#options .  They include (additional -D
  284:  *		switches are discussed at mimetex.html#options)...
  285:  *		-DAA
  286:  *		    Turns on gif anti-aliasing with default values
  287:  *		    (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
  288:  *		    for the following anti-aliasing parameters...
  289:  *		-DCENTERWT=n
  290:  *		-DADJACENTWT=j
  291:  *		-DCORNERWT=k
  292:  *			*** Note: Ignore these three switches because
  293:  *			*** mimeTeX's current anti-aliasing algorithm
  294:  *			*** no longer uses them (as of version 1.60).
  295:  *		    MimeTeX currently provides a lowpass filtering
  296:  *		    algorithm for anti-aliasing, which is applied to the
  297:  *		    existing set of bitmap fonts.  This lowpass filter
  298:  *		    applies default weights
  299:  *				1   2   1
  300:  *				2   8   2
  301:  *				1   2   1
  302:  *		    to neighboring pixels. The defaults weights are
  303:  *		    CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
  304:  *		    which you can adjust to control anti-aliasing.
  305:  *		    Lower CENTERWT values will blur/spread out lines
  306:  *		    while higher values will tend to sharpen lines.
  307:  *		    Experimentation is recommended to determine
  308:  *		    what value works best for you.
  309:  *		-DCACHEPATH=\"path/\"
  310:  *		    This option saves each rendered image to a file
  311:  *		    in directory  path/  which mimeTeX reads rather than
  312:  *		    re-rendering the same image every time it's given
  313:  *		    the same LaTeX expression.  Sometimes mimeTeX disables
  314:  *		    caching, e.g., expressions containing \input{ } are
  315:  *		    re-rendered since the contents of the inputted file
  316:  *		    may have changed.  If compiled without -DCACHEPATH
  317:  *		    mimeTeX always re-renders expressions.  This usually
  318:  *		    isn't too cpu intensive, but if you have unusually
  319:  *		    high hit rates then image caching may be helpful.
  320:  *			The  path/  is relative to mimetex.cgi, and must
  321:  *		    be writable by it.  Files created under  path/  are
  322:  *		    named filename.gif, where filename is the 32-character
  323:  *		    MD5 hash of the LaTeX expression.
  324:  *		-DDEFAULTSIZE=n
  325:  *		    MimeTeX currently has eight font sizes numbered 0-7,
  326:  *		    and always starts in DEFAULTSIZE whose default value
  327:  *		    is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
  328:  *		    on the compile line if you prefer mimeTeX to start in
  329:  *		    larger default size 4 (corresponding to \Large), etc.
  330:  *		-DDISPLAYSIZE=n
  331:  *		    By default, operator limits like \int_a^b are rendered
  332:  *		    \textstyle at font sizes \normalsize and smaller,
  333:  *		    and rendered \displaystyle at font sizes \large and
  334:  *		    larger.  This default corresponds to -DDISPLAYSIZE=3,
  335:  *		    which you can adjust; e.g., -DDISPLAYSIZE=0 always
  336:  *		    defaults to \displaystyle, and 99 (or any large number)
  337:  *		    always defaults to \textstyle.  Note that explicit
  338:  *		    \textstyle, \displaystyle, \limits or \nolimits
  339:  *		    directives in an expression always override
  340:  *		    the DISPLAYSIZE default.
  341:  *		-DERRORSTATUS=n
  342:  *		    The default, 0, means mimeTeX always exits with status 0,
  343:  *		    regardless of whether or not it detects error(s) while
  344:  *		    trying to render your expression.  Specify any non-zero
  345:  *		    value (typically -1) if you write a script/plugin for
  346:  *		    mimeTeX that traps non-zero exit statuses.  MimeTeX then
  347:  *		    exits with its own non-zero status when it detects an
  348:  *		    error it can identify, or with your ERRORSTATUS value
  349:  *		    for errors it can't specifically identify.
  350:  *		-DREFERER=\"domain\"   -or-
  351:  *		-DREFERER=\"domain1,domain2,etc\"
  352:  *		    Blocks mimeTeX requests from unauthorized domains that
  353:  *		    may be using your server's mimetex.cgi without permission.
  354:  *		    If REFERER is defined, mimeTeX checks for the environment
  355:  *		    variable HTTP_REFERER and, if it exists, performs a
  356:  *		    case-insensitive test to make sure it contains 'domain'
  357:  *		    as a substring.  If given several 'domain's (second form)
  358:  *		    then HTTP_REFERER must contain either 'domain1' or
  359:  *		    'domain2', etc, as a (case-insensitive) substring.
  360:  *		    If HTTP_REFERER fails to contain a substring matching
  361:  *		    any of these domain(s), mimeTeX emits an error message
  362:  *		    image corresponding to the expression specified by
  363:  *		    the  invalid_referer_msg  string defined in main().
  364:  *		    Note: if HTTP_REFERER is not an environment variable,
  365:  *		    mimeTeX correctly generates the requested expression
  366:  *		    (i.e., no referer error).
  367:  *		-DWARNINGS=n  -or-
  368:  *		-DNOWARNINGS
  369:  *		    If an expression submitted to mimeTeX contains an
  370:  *		    unrecognzied escape sequence, e.g., "y=x+\abc+1", then
  371:  *		    mimeTeX generates a gif image containing an embedded
  372:  *		    warning in the form "y=x+[\abc?]+1".  If you want these
  373:  *		    warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
  374:  *		    mimeTeX to ignore unrecognized symbols, and the rendered
  375:  *		    image is "y=x++1" instead.
  376:  *		-DWHITE
  377:  *		    MimeTeX usually renders black symbols on a white
  378:  *		    background.  This option renders white symbols on
  379:  *		    a black background instead.
  380:  * --------------------------------------------------------------------------
  381:  * Revision History:
  382:  * 09/18/02	J.Forkosh	Installation.
  383:  * 12/11/02	J.Forkosh	Version 1.00 released.
  384:  * 07/04/03	J.Forkosh	Version 1.01 released.
  385:  * 10/17/03	J.Forkosh	Version 1.20 released.
  386:  * 12/21/03	J.Forkosh	Version 1.30 released.
  387:  * 02/01/04	J.Forkosh	Version 1.40 released.
  388:  * 10/02/04	J.Forkosh	Version 1.50 released.
  389:  * 11/30/04	J.Forkosh	Version 1.60 released.
  390:  * 10/11/05	J.Forkosh	Version 1.64 released.
  391:  * 11/30/06	J.Forkosh	Version 1.65 released.
  392:  * 09/06/08	J.Forkosh	Version 1.70 released.
  393:  * 03/23/09	J.Forkosh	Version 1.71 released.
  394:  * 11/18/09	J.Forkosh	Version 1.72 released.
  395:  * 11/15/11	J.Forkosh	Version 1.73 released.
  396:  * 02/15/12	J.Forkosh	Version 1.74 released.
  397:  * 03/31/12	J.Forkosh	Most recent revision (also see REVISIONDATE)
  398:  * See  http://www.forkosh.com/mimetexchangelog.html  for further details.
  399:  *
  400:  ****************************************************************************/
  401: 
  402: /* -------------------------------------------------------------------------
  403: Program id
  404: -------------------------------------------------------------------------- */
  405: #define	VERSION "1.74"			/* mimeTeX version number */
  406: #define REVISIONDATE "31 March 2012" /* date of most recent revision */
  407: #define COPYRIGHTTEXT "Copyright(c) 2002-2012, John Forkosh Associates, Inc"
  408: 
  409: /* -------------------------------------------------------------------------
  410: header files and macros
  411: -------------------------------------------------------------------------- */
  412: /* --- standard headers --- */
  413: #include <stdio.h>
  414: #include <stdlib.h>
  415: /*#include <unistd.h>*/
  416: #include <string.h>
  417: #include <ctype.h>
  418: #include <math.h>
  419: #include <time.h>
  420: extern	char **environ;		/* for \environment directive */
  421: 
  422: /* -------------------------------------------------------------------------
  423: messages (used mostly by main() and also by rastmessage())
  424: -------------------------------------------------------------------------- */
  425: static	char *copyright1 =		/* copyright, gnu/gpl notice */
  426:  "+-----------------------------------------------------------------------+\n"
  427:  "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT                             "|\n"
  428:  "+-----------------------------------------------------------------------+\n"
  429:  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
  430:  "|           and comes with absolutely no warranty whatsoever.           |",
  431: *copyright2 =
  432:  "|          See http://www.forkosh.com/mimetex.html for details.         |\n"
  433:  "+-----------------------------------------------------------------------+";
  434: static	int maxmsgnum = 3,		/* maximum msgtable[] index */
  435: 	/* --- keep these message numbers updated if table changes --- */
  436: 	invmsgnum = 0,			/* general invalid message */
  437: 	refmsgnum = 3;			/* urlncmp() failed to validate */
  438: static	char *msgtable[] = {		/* messages referenced by [index] */
  439:  "\\red\\small\\rm\\fbox{\\array{"	/* [0] is invalid_referer_msg */
  440:    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
  441:    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
  442:  "\\red\\small\\rm\\fbox{\\array{"	/* [1] */
  443:    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
  444:    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
  445:    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
  446:  "\\red\\small\\rm\\fbox{\\array{"	/* [2] */
  447:    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
  448:    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
  449:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
  450:  "\\red\\small\\rm\\fbox{\\array{"	/* [3] */
  451:    "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
  452:    "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
  453:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
  454:  NULL } ;				/* trailer */
  455: 
  456: /* -------------------------------------------------------------------------
  457: additional symbols
  458: -------------------------------------------------------------------------- */
  459: /* ---
  460:  * windows-specific header info
  461:  * ---------------------------- */
  462: #ifndef WINDOWS			/* -DWINDOWS not supplied by user */
  463:   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
  464:   ||  defined(DJGPP)		/* try to recognize windows compilers */ \
  465:   ||  defined(_USRDLL)		/* must be WINDOWS if compiling for DLL */
  466:     #define WINDOWS		/* signal windows */
  467:   #endif
  468: #endif
  469: #ifdef WINDOWS			/* Windows opens stdout in char mode, and */
  470:   #include <fcntl.h>		/* precedes every 0x0A with spurious 0x0D.*/
  471:   #include <io.h>		/* So emitcache() issues a Win _setmode() */
  472: 				/* call to put stdout in binary mode. */
  473:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
  474:     #define O_BINARY _O_BINARY	/* make O_BINARY available, etc... */
  475:     #define setmode  _setmode
  476:     #define fileno   _fileno
  477:   #endif
  478:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
  479:     #define HAVE_SETMODE	/* so we'll use setmode() */
  480:   #endif
  481:   #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
  482:     /* to show source file and line numbers where memory leaks occur... */
  483:     #define _CRTDBG_MAP_ALLOC	/* ...include this debug macro */
  484:     #include <crtdbg.h>		/* and this debug library */
  485:   #endif
  486:   #define ISWINDOWS 1
  487: #else
  488:   #define ISWINDOWS 0
  489: #endif
  490: 
  491: /* ---
  492:  * check for supersampling or low-pass anti-aliasing
  493:  * ------------------------------------------------- */
  494: #ifdef SS
  495:   #define ISSUPERSAMPLING 1
  496:   #ifndef AAALGORITHM
  497:     #define AAALGORITHM 1		/* default supersampling algorithm */
  498:   #endif
  499:   #ifndef AA				/* anti-aliasing not explicitly set */
  500:     #define AA				/* so define it ourselves */
  501:   #endif
  502:   #ifndef SSFONTS			/* need supersampling fonts */
  503:     #define SSFONTS
  504:   #endif
  505: #else
  506:   #define ISSUPERSAMPLING 0
  507:   #ifndef AAALGORITHM
  508:     #define AAALGORITHM 3 /*2*/		/* default lowpass algorithm */
  509:   #endif
  510: #endif
  511: #ifndef MAXFOLLOW
  512:   #define MAXFOLLOW 8			/* aafollowline() maxturn default */
  513: #endif
  514: 
  515: /* ---
  516:  * set aa (and default gif) if any anti-aliasing options specified
  517:  * --------------------------------------------------------------- */
  518: #if defined(AA) || defined(GIF) || defined(PNG) \
  519: ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
  520: ||  defined(MINADJACENT) || defined(MAXADJACENT)
  521:   #if !defined(GIF) && !defined(AA)	/* aa not explicitly specified */
  522:     #define AA				/* so define it ourselves */
  523:   #endif
  524:   #if !defined(GIF) && !defined(PNG)	/* neither gif nor png specified */
  525:     #define GIF				/* so default to gif */
  526:   #endif
  527: #endif
  528: /* --- resolve output option inconsistencies --- */
  529: #if defined(XBITMAP)			/* xbitmap supercedes gif and png */
  530:   #ifdef AA
  531:     #undef AA
  532:   #endif
  533:   #ifdef GIF
  534:     #undef GIF
  535:   #endif
  536:   #ifdef PNG
  537:     #undef PNG
  538:   #endif
  539: #endif
  540: 
  541: /* ---
  542:  * decide whether or not to compile main()
  543:  * --------------------------------------- */
  544: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
  545:   /* --- yes, compile main() --- */
  546:   #define DRIVER			/* main() driver will be compiled */
  547: #else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */
  548:   #ifndef TEXFONTS
  549:     #define NOTEXFONTS			/* texfonts not required */
  550:   #endif
  551: #endif
  552: 
  553: /* ---
  554:  * application headers
  555:  * ------------------- */
  556: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
  557:   #define TEXFONTS			/* to include texfonts.h */
  558: #endif
  559: #include "mimetex.h"
  560: 
  561: /* ---
  562:  * info needed when gif image returned in memory buffer
  563:  * ---------------------------------------------------- */
  564: #ifdef GIF				/* compiling along with gifsave.c */
  565:   extern int gifSize;
  566:   extern int maxgifSize;
  567: #else					/* or just set dummy values */
  568:   static int gifSize=0, maxgifSize=0;
  569: #endif
  570: /* --- gamma correction --- */
  571: #ifndef GAMMA
  572:   #define GAMMA 1.25 /*1.75*/ /*2.2*/
  573: #endif
  574: #ifndef REVERSEGAMMA
  575:   #define REVERSEGAMMA 0.5		/* for \reverse white-on-black */
  576: #endif
  577: /* --- opaque background (default to transparent) --- */
  578: #ifndef OPAQUE
  579:   #define ISTRANSPARENT 1
  580: #else
  581:   #define ISTRANSPARENT 0
  582: #endif
  583: 
  584: /* ---
  585:  * internal buffer sizes
  586:  * --------------------- */
  587: #if !defined(MAXEXPRSZ)
  588:   #define MAXEXPRSZ (32768-1)		/*max #bytes in input tex expression*/
  589: #endif
  590: #if !defined(MAXSUBXSZ)
  591:   #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
  592: #endif
  593: #if !defined(MAXTOKNSZ)
  594:   #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
  595: #endif
  596: #if !defined(MAXFILESZ)
  597:   #define MAXFILESZ (65536-1)		/*max #bytes in input (output) file*/
  598: #endif
  599: #if !defined(MAXLINESZ)
  600:   #define MAXLINESZ (4096-1)		/* max #chars in line from file */
  601: #endif
  602: #if !defined(MAXGIFSZ)
  603:   #define MAXGIFSZ 131072		/* max #bytes in output GIF image */
  604: #endif
  605: 
  606: /* -------------------------------------------------------------------------
  607: adjustable default values
  608: -------------------------------------------------------------------------- */
  609: /* ---
  610:  * anti-aliasing parameters
  611:  * ------------------------ */
  612: #ifndef	CENTERWT
  613:   /*#define CENTERWT 32*/		/* anti-aliasing centerwt default */
  614:   /*#define CENTERWT 10*/		/* anti-aliasing centerwt default */
  615:   #define CENTERWT 8			/* anti-aliasing centerwt default */
  616: #endif
  617: #ifndef	ADJACENTWT
  618:   /*#define ADJACENTWT 3*/		/* anti-aliasing adjacentwt default*/
  619:   #define ADJACENTWT 2			/* anti-aliasing adjacentwt default*/
  620: #endif
  621: #ifndef	CORNERWT
  622:   #define CORNERWT 1			/* anti-aliasing cornerwt default*/
  623: #endif
  624: #ifndef	MINADJACENT
  625:   #define MINADJACENT 6			/*anti-aliasing minadjacent default*/
  626: #endif
  627: #ifndef	MAXADJACENT
  628:   #define MAXADJACENT 8			/*anti-aliasing maxadjacent default*/
  629: #endif
  630: /* --- variables for anti-aliasing parameters --- */
  631: GLOBAL(int,centerwt,CENTERWT);		/*lowpass matrix center pixel wt */
  632: GLOBAL(int,adjacentwt,ADJACENTWT);	/*lowpass matrix adjacent pixel wt*/
  633: GLOBAL(int,cornerwt,CORNERWT);		/*lowpass matrix corner pixel wt */
  634: GLOBAL(int,minadjacent,MINADJACENT);	/* darken if>=adjacent pts black*/
  635: GLOBAL(int,maxadjacent,MAXADJACENT);	/* darken if<=adjacent pts black */
  636: GLOBAL(int,weightnum,1);		/* font wt, */
  637: GLOBAL(int,maxaaparams,4);		/* #entries in table */
  638: /* --- anti-aliasing parameter values by font weight --- */
  639: #define	aaparameters struct aaparameters_struct /* typedef */
  640: aaparameters
  641:   { int	centerwt;			/* lowpass matrix center   pixel wt*/
  642:     int	adjacentwt;			/* lowpass matrix adjacent pixel wt*/
  643:     int cornerwt;			/* lowpass matrix corner   pixel wt*/
  644:     int	minadjacent;			/* darken if >= adjacent pts black */
  645:     int	maxadjacent;			/* darken if <= adjacent pts black */
  646:     int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
  647: STATIC aaparameters aaparams[]		/* set params by weight */
  648:   #ifdef INITVALS
  649:   =
  650:   { /* ----------------------------------------------------
  651:     centerwt adj corner minadj max  fgalias,only,bgalias,only
  652:     ------------------------------------------------------- */
  653: 	{ 64,  1,  1,    6,  8,     1,0,0,0 },	/* 0 = light */
  654: 	{ CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
  655: 	{ 8,   1,  1,    5,  8,     1,0,0,0 },	/* 2 = semibold */
  656: 	{ 8,   2,  1,    4,  9,     1,0,0,0 }	/* 3 = bold */
  657:   } /* --- end-of-aaparams[] --- */
  658:   #endif
  659:   ;
  660: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
  661: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
  662: 	ispatternnumcount = 1;		/* true to accumulate counts */
  663: 
  664: /* -------------------------------------------------------------------------
  665: other variables
  666: -------------------------------------------------------------------------- */
  667: /* --- black on white background (default), or white on black --- */
  668: #ifdef WHITE
  669:   #define ISBLACKONWHITE 0		/* white on black background */
  670: #else
  671:   #define ISBLACKONWHITE 1		/* black on white background */
  672: #endif
  673: /* --- colors --- */
  674: #define	BGRED   (ISBLACKONWHITE?255:0)
  675: #define	BGGREEN (ISBLACKONWHITE?255:0)
  676: #define	BGBLUE  (ISBLACKONWHITE?255:0)
  677: #ifndef	FGRED
  678:   #define FGRED   (ISBLACKONWHITE?0:255)
  679: #endif
  680: #ifndef	FGGREEN
  681:   #define FGGREEN (ISBLACKONWHITE?0:255)
  682: #endif
  683: #ifndef	FGBLUE
  684:   #define FGBLUE  (ISBLACKONWHITE?0:255)
  685: #endif
  686: /* --- advertisement
  687:    one image in every ADFREQUENCY is wrapped in "advertisement" --- */
  688: #if !defined(ADFREQUENCY)
  689:   #define ADFREQUENCY 0			/* never show advertisement if 0 */
  690: #endif
  691: #ifndef	HOST_SHOWAD
  692:   #define HOST_SHOWAD "\000"		/* show ads on all hosts */
  693: #endif
  694: /* --- "smash" margin (0 means no smashing) --- */
  695: #ifndef SMASHMARGIN
  696:   #ifdef NOSMASH
  697:     #define SMASHMARGIN 0
  698:   #else
  699:     #define SMASHMARGIN 3
  700:   #endif
  701: #endif
  702: #ifndef SMASHCHECK
  703:   #define SMASHCHECK 0
  704: #endif
  705: /* --- textwidth --- */
  706: #ifndef TEXTWIDTH
  707:   #define TEXTWIDTH (400)
  708: #endif
  709: /* --- font "combinations" --- */
  710: #define	CMSYEX (109)			/*select CMSY10, CMEX10 or STMARY10*/
  711: /* --- prefix prepended to all expressions --- */
  712: #ifndef	PREFIX
  713:   #define PREFIX "\000"			/* default no prepended prefix */
  714: #endif
  715: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
  716: #ifdef NOARGSIGNAL
  717:   #define ARGSIGNAL NULL
  718: #endif
  719: #ifndef	ARGSIGNAL
  720:   #define ARGSIGNAL "++"
  721: #endif
  722: /* --- security and logging (inhibit message logging, etc) --- */
  723: #ifndef	SECURITY
  724:   #define SECURITY 999			/* default highest security level */
  725: #endif
  726: #ifndef	LOGFILE
  727:   #define LOGFILE "mimetex.log"		/* default log file */
  728: #endif
  729: #ifndef	CACHELOG
  730:   #define CACHELOG "mimetex.log"	/* default caching log file */
  731: #endif
  732: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
  733:   #define DUMPENVP			/* assume char *envp[] available */
  734: #endif
  735: /* --- max query_string length if no http_referer supplied --- */
  736: #ifndef NOREFMAXLEN
  737:   #define NOREFMAXLEN 9999		/* default to any length query */
  738: #endif
  739: #ifndef NOREFSAFELEN
  740:   #define NOREFSAFELEN 24		/* too small for hack exploit */
  741: #endif
  742: /* --- check whether or not to perform http_referer check --- */
  743: #ifdef REFERER				/* only specified referers allowed */
  744:   #undef NOREFMAXLEN
  745:   #define NOREFMAXLEN NOREFSAFELEN
  746: #else					/* all http_referer's allowed */
  747:   #define REFERER NULL
  748: #endif
  749: /* --- check top levels of http_referer against server_name --- */
  750: #ifdef REFLEVELS			/* #topmost levels to check */
  751:   #undef NOREFMAXLEN
  752:   #define NOREFMAXLEN NOREFSAFELEN
  753: #else
  754:   #ifdef NOREFCHECK
  755:     #define REFLEVELS 0			/* don't match host and referer */
  756:   #else
  757:     #define REFLEVELS 3			/* default matches abc.def.com */
  758:   #endif
  759: #endif
  760: /* --- check whether or not \input, \counter, \environment permitted --- */
  761: #ifdef DEFAULTSECURITY			/* default security specified */
  762:   #define EXPLICITDEFSECURITY		/* don't override explicit default */
  763: #else					/* defualt security not specified */
  764:   #define DEFAULTSECURITY (8)		/* so set default security level */
  765: #endif
  766: #ifdef INPUTREFERER 			/*http_referer's permitted to \input*/
  767:   #ifndef INPUTSECURITY			/* so we need to permit \input{} */
  768:     #define INPUTSECURITY (99999)	/* make sure SECURITY<INPUTSECURITY */
  769:   #endif
  770: #else					/* no INPUTREFERER list supplied */
  771:   #define INPUTREFERER NULL		/* so init it as NULL pointer */
  772: #endif
  773: #ifndef INPUTPATH 			/* \input{} paths permitted for... */
  774:   #define INPUTPATH NULL		/* ...any referer */
  775: #endif
  776: #ifndef INPUTSECURITY			/* \input{} security not specified */
  777:   #ifdef INPUTOK			/* but INPUTOK flag specified */
  778:     #define INPUTSECURITY (99999)	/* so enable \input{} */
  779:     #ifndef EXPLICITDEFSECURITY		/* don't override explicit default */
  780:       #undef  DEFAULTSECURITY		/* but we'll override our default */
  781:       #define DEFAULTSECURITY (99999)	/*let -DINPUTOK enable \counter,etc*/
  782:     #endif
  783:   #else					/* else no \input{} specified */
  784:     #define INPUTSECURITY DEFAULTSECURITY /* set default \input security */
  785:   #endif
  786: #endif
  787: #ifndef COUNTERSECURITY			/*\counter{} security not specified*/
  788:   #ifdef COUNTEROK			/* but COUNTEROK flag specified */
  789:     #define COUNTERSECURITY (99999)	/* so enable \counter{} */
  790:   #else					/* else no \counter{} specified */
  791:     #define COUNTERSECURITY DEFAULTSECURITY /*set default \counter security*/
  792:   #endif
  793: #endif
  794: #ifndef ENVIRONSECURITY			/* \environ security not specified */
  795:   #ifdef ENVIRONOK			/* but ENVIRONOK flag specified */
  796:     #define ENVIRONSECURITY (99999)	/* so enable \environ */
  797:   #else					/* else no \environ specified */
  798:     #define ENVIRONSECURITY DEFAULTSECURITY /*set default \environ security*/
  799:   #endif
  800: #endif
  801: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
  802: #ifndef CACHEPATH
  803:   #define ISCACHING 0			/* no caching */
  804:   #define CACHEPATH "\000"		/* same directory as mimetex.cgi */
  805: #else
  806:   #define ISCACHING 1			/* caching if -DCACHEPATH="path" */
  807: #endif
  808: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
  809: #ifndef PATHPREFIX
  810:   #define PATHPREFIX "\000"		/* paths relative mimetex.cgi */
  811: #endif
  812: /* --- time zone delta t (in hours) --- */
  813: #ifndef TZDELTA
  814:   #define TZDELTA 0
  815: #endif
  816: /* --- treat +'s in query string as blanks? --- */
  817: #ifdef PLUSBLANK			/* + always interpreted as blank */
  818:   #define ISPLUSBLANK 1
  819: #else
  820:   #ifdef PLUSNOTBLANK			/* + never interpreted as blank */
  821:     #define ISPLUSBLANK 0
  822:   #else					/* program tries to determine */
  823:     #define ISPLUSBLANK (-1)
  824:   #endif
  825: #endif
  826: 
  827: /* -------------------------------------------------------------------------
  828: debugging and logging / error reporting
  829: -------------------------------------------------------------------------- */
  830: /* --- debugging and error reporting --- */
  831: #ifndef	MSGLEVEL
  832:   #define MSGLEVEL 1
  833: #endif
  834: #define	DBGLEVEL 9			/* debugging if msglevel>=DBGLEVEL */
  835: #define	LOGLEVEL 3			/* logging if msglevel>=LOGLEVEL */
  836: #ifndef FORMLEVEL
  837:   #define FORMLEVEL LOGLEVEL		/*msglevel if called from html form*/
  838: #endif
  839: #ifndef	ERRORSTATUS			/* exit(ERRORSTATUS) for any error */
  840:   #define ERRORSTATUS 0			/* default doesn't signal errors */
  841: #endif
  842: GLOBAL(int,seclevel,SECURITY);		/* security level */
  843: GLOBAL(int,inputseclevel,INPUTSECURITY); /* \input{} security level */
  844: GLOBAL(int,counterseclevel,COUNTERSECURITY); /* \counter{} security level */
  845: GLOBAL(int,environseclevel,ENVIRONSECURITY); /* \environ{} security level */
  846: GLOBAL(int,msglevel,MSGLEVEL);		/* message level for verbose/debug */
  847: GLOBAL(int,errorstatus,ERRORSTATUS);	/* exit status if error encountered*/
  848: GLOBAL(int,exitstatus,0);		/* exit status (0=success) */
  849: STATIC	FILE *msgfp;			/* output in command-line mode */
  850: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
  851: #ifdef WARNINGS
  852:   #define WARNINGLEVEL WARNINGS
  853: #else
  854:   #ifdef NOWARNINGS
  855:     #define WARNINGLEVEL 0
  856:   #else
  857:     #define WARNINGLEVEL 1
  858:   #endif
  859: #endif
  860: GLOBAL(int,warninglevel,WARNINGLEVEL);	/* warning level */
  861: 
  862: /* -------------------------------------------------------------------------
  863: control flags and values
  864: -------------------------------------------------------------------------- */
  865: GLOBAL(int,daemonlevel,0);		/* incremented in main() */
  866: GLOBAL(int,recurlevel,0);		/* inc/decremented in rasterize() */
  867: GLOBAL(int,scriptlevel,0);		/* inc/decremented in rastlimits() */
  868: GLOBAL(int,isstring,0);			/*pixmap is ascii string, not raster*/
  869: GLOBAL(int,isligature,0);		/* true if ligature found */
  870: GLOBAL(char,*subexprptr,(char *)NULL);	/* ptr within expression to subexpr*/
  871: /*SHARED(int,imageformat,1);*/		/* image is 1=bitmap, 2=.gf-like */
  872: GLOBAL(int,isdisplaystyle,1);		/* displaystyle mode (forced if 2) */
  873: GLOBAL(int,ispreambledollars,0);	/* displaystyle mode set by $$...$$ */
  874: GLOBAL(int,ninputcmds,0);		/* # of \input commands processed */
  875: GLOBAL(int,fontnum,0);			/* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
  876: GLOBAL(int,fontsize,NORMALSIZE);	/* current size */
  877: GLOBAL(int,magstep,1);			/* magstep (1=no change) */
  878: GLOBAL(int,displaysize,DISPLAYSIZE);	/* use \displaystyle when fontsize>=*/
  879: GLOBAL(int,shrinkfactor,3);		/* shrinkfactors[fontsize] */
  880: GLOBAL(int,rastlift,0);			/* rastraise() lift parameter */
  881: GLOBAL(int,rastlift1,0);		/* rastraise() lift for base exprssn*/
  882: GLOBAL(double,unitlength,1.0);		/* #pixels per unit (may be <1.0) */
  883: GLOBAL(int,iunitlength,1);		/* #pixels per unit as int for store*/
  884: /*GLOBAL(int,textwidth,TEXTWIDTH);*/	/* #pixels across line */
  885: GLOBAL(int,adfrequency,ADFREQUENCY);	/* advertisement frequency */
  886: GLOBAL(int,isnocatspace,0);		/* >0 to not add space in rastcat()*/
  887: GLOBAL(int,smashmargin,SMASHMARGIN);	/* minimum "smash" margin */
  888: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
  889: GLOBAL(int,issmashdelta,1);		/* true if smashmargin is a delta */
  890: GLOBAL(int,isexplicitsmash,0);		/* true if \smash explicitly given */
  891: GLOBAL(int,smashcheck,SMASHCHECK);	/* check if terms safe to smash */
  892: GLOBAL(int,isnomath,0);			/* true to inhibit math mode */
  893: GLOBAL(int,isscripted,0);		/* is (lefthand) term text-scripted*/
  894: GLOBAL(int,isdelimscript,0);		/* is \right delim text-scripted */
  895: GLOBAL(int,issmashokay,0);		/*is leading char okay for smashing*/
  896: #define	BLANKSIGNAL (-991234)		/*rastsmash signal right-hand blank*/
  897: GLOBAL(int,blanksignal,BLANKSIGNAL);	/*rastsmash signal right-hand blank*/
  898: GLOBAL(int,blanksymspace,0);		/* extra (or too much) space wanted*/
  899: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
  900: GLOBAL(int,fgred,FGRED);
  901:   GLOBAL(int,fggreen,FGGREEN);
  902:   GLOBAL(int,fgblue,FGBLUE);		/* fg r,g,b */
  903: GLOBAL(int,bgred,BGRED);
  904:   GLOBAL(int,bggreen,BGGREEN);
  905:   GLOBAL(int,bgblue,BGBLUE);		/* bg r,g,b */
  906: GLOBAL(double,gammacorrection,GAMMA);	/* gamma correction */
  907: GLOBAL(int,isplusblank,ISPLUSBLANK);	/*interpret +'s in query as blanks?*/
  908: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
  909: GLOBAL(char,exprprefix[256],PREFIX);	/* prefix prepended to expressions */
  910: GLOBAL(int,aaalgorithm,AAALGORITHM);	/* for lp, 1=aalowpass, 2 =aapnm */
  911: GLOBAL(int,maxfollow,MAXFOLLOW);	/* aafollowline() maxturn parameter*/
  912: GLOBAL(int,fgalias,1);
  913:   GLOBAL(int,fgonly,0);
  914:   GLOBAL(int,bgalias,0);
  915:   GLOBAL(int,bgonly,0);			/* aapnm() params */
  916: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
  917: GLOBAL(int,isss,ISSUPERSAMPLING);	/* supersampling flag for main() */
  918: GLOBAL(int,*workingparam,(int *)NULL);	/* working parameter */
  919: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
  920: GLOBAL(int,isreplaceleft,0);		/* true to replace leftexpression */
  921: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
  922: GLOBAL(mathchardef,*leftsymdef,NULL);	/* mathchardef for preceding symbol*/
  923: GLOBAL(int,fraccenterline,NOVALUE);	/* baseline for punct. after \frac */
  924: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
  925: GLOBAL(int,iscaching,ISCACHING);	/* true if caching images */
  926: GLOBAL(char,cachepath[256],CACHEPATH);	/* relative path to cached files */
  927: GLOBAL(int,isemitcontenttype,1);	/* true to emit mime content-type */
  928: int	iscachecontenttype = 0;		/* true to cache mime content-type */
  929: char	contenttype[2048] = "\000";	/* content-type:, etc buffer */
  930: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
  931: /*GLOBAL(int,iswindows,ISWINDOWS);*/	/* true if compiled for ms windows */
  932: 
  933: /* -------------------------------------------------------------------------
  934: store for evalterm() [n.b., these are stripped-down funcs from nutshell]
  935: -------------------------------------------------------------------------- */
  936: #define	STORE struct store_struct	/* "typedef" for store struct */
  937: #define	MAXSTORE 100			/* max 100 identifiers */
  938: STORE {
  939:   char	*identifier;			/* identifier */
  940:   int	*value;				/* address of corresponding value */
  941:   } ; /* --- end-of-store_struct --- */
  942: static STORE mimestore[MAXSTORE] = {
  943:     { "fontsize", &fontsize },	{ "fs", &fontsize },	/* font size */
  944:     { "fontnum", &fontnum },	{ "fn", &fontnum },	/* font number */
  945:     { "unitlength", &iunitlength },			/* unitlength */
  946:     /*{ "mytestvar", &mytestvar },*/
  947:     { NULL, NULL }					/* end-of-store */
  948:   } ; /* --- end-of-mimestore[] --- */
  949: 
  950: /* -------------------------------------------------------------------------
  951: miscellaneous macros
  952: -------------------------------------------------------------------------- */
  953: #if 0	/* --- these are now #define'd in mimetex.h --- */
  954: #define	max2(x,y)  ((x)>(y)? (x):(y))	/* larger of 2 arguments */
  955: #define	min2(x,y)  ((x)<(y)? (x):(y))	/* smaller of 2 arguments */
  956: #define	max3(x,y,z) max2(max2(x,y),(z))	/* largest of 3 arguments */
  957: #define	min3(x,y,z) min2(min2(x,y),(z))	/* smallest of 3 arguments */
  958: #define absval(x)  ((x)>=0?(x):(-(x)))	/* absolute value */
  959: #define	iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
  960: #define	dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
  961: #endif
  962: #define compress(s,c) if((s)!=NULL)	/* remove embedded c's from s */ \
  963: 	{ char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
  964: #define	slower(s)  if ((s)!=NULL)	/* lowercase all chars in s */ \
  965: 	{ char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
  966: /*subraster *subrastcpy();*/		/* need global module declaration */
  967: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
  968: /*	sp->type=blanksignal */
  969: /* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
  970: #define	eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
  971: /* --- check if a string is empty --- */
  972: #define	isempty(s)  ((s)==NULL?1:(*(s)=='\000'?1:0))
  973: /* --- last char of a string --- */
  974: #define	lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
  975: /* --- lowercase a string --- */
  976: #define	strlower(s) strnlower((s),0)	/* lowercase an entire string */
  977: /* --- strip leading and trailing whitespace (including ~) --- */
  978: #define	trimwhite(thisstr) if ( (thisstr) != NULL ) { \
  979: 	int thislen = strlen(thisstr); \
  980: 	while ( --thislen >= 0 ) \
  981: 	  if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
  982: 	    (thisstr)[thislen] = '\000'; \
  983: 	  else break; \
  984: 	if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
  985: 	  {strsqueeze((thisstr),thislen);} } else
  986: /* --- strncpy() n bytes and make sure it's null-terminated --- */
  987: #define	strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
  988: 	  char *thissource = (source); \
  989: 	  (target)[0] = '\000'; \
  990: 	  if ( (n)>0 && thissource!=NULL ) { \
  991: 	    strncpy((target),thissource,(n)); \
  992: 	    (target)[(n)] = '\000'; } }
  993: /* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
  994: #define	strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
  995: 	int thislen3=strlen(s); \
  996: 	if ((n) >= thislen3) *(s) = '\000'; \
  997: 	else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
  998: /* --- strsqueeze(s,t) with two pointers --- */
  999: #define	strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
 1000: 	int sqlen=strlen((s))-strlen((t)); \
 1001: 	if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
 1002: 
 1003: /* ---
 1004:  * PART2
 1005:  * ------ */
 1006: #if !defined(PARTS) || defined(PART2)
 1007: /* ==========================================================================
 1008:  * Function:	new_raster ( width, height, pixsz )
 1009:  * Purpose:	Allocation and constructor for raster.
 1010:  *		mallocs and initializes memory for width*height pixels,
 1011:  *		and returns raster struct ptr to caller.
 1012:  * --------------------------------------------------------------------------
 1013:  * Arguments:	width (I)	int containing width, in bits,
 1014:  *				of raster pixmap to be allocated
 1015:  *		height (I)	int containing height, in bits/scans,
 1016:  *				of raster pixmap to be allocated
 1017:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
 1018:  * --------------------------------------------------------------------------
 1019:  * Returns:	( raster * )	ptr to allocated and initialized
 1020:  *				raster struct, or NULL for any error.
 1021:  * --------------------------------------------------------------------------
 1022:  * Notes:
 1023:  * ======================================================================= */
 1024: /* --- entry point --- */
 1025: raster	*new_raster ( int width, int height, int pixsz )
 1026: {
 1027: /* -------------------------------------------------------------------------
 1028: Allocations and Declarations
 1029: -------------------------------------------------------------------------- */
 1030: raster	*rp = (raster *)NULL;		/* raster ptr returned to caller */
 1031: pixbyte	*pixmap = NULL;			/* raster pixel map to be malloced */
 1032: int	nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
 1033: int	filler = (isstring?' ':0);	/* pixmap filler */
 1034: int	delete_raster();		/* in case pixmap malloc() fails */
 1035: int	npadding = (0&&issupersampling?8+256:0); /* padding bytes */
 1036: /* -------------------------------------------------------------------------
 1037: allocate and initialize raster struct and embedded bitmap
 1038: -------------------------------------------------------------------------- */
 1039: if ( msgfp!=NULL && msglevel>=9999 )
 1040:   { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
 1041:     width,height,pixsz); fflush(msgfp); }
 1042: /* --- allocate and initialize raster struct --- */
 1043: rp = (raster *)malloc(sizeof(raster));	/* malloc raster struct */
 1044: if ( msgfp!=NULL && msglevel>=9999 )
 1045:   { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
 1046:     sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
 1047: if ( rp == (raster *)NULL )		/* malloc failed */
 1048:   goto end_of_job;			/* return error to caller */
 1049: rp->width = width;			/* store width in raster struct */
 1050: rp->height = height;			/* and store height */
 1051: rp->format = 1;				/* initialize as bitmap format */
 1052: rp->pixsz = pixsz;			/* store #bits per pixel */
 1053: rp->pixmap = (pixbyte *)NULL;		/* init bitmap as null ptr */
 1054: /* --- allocate and initialize bitmap array --- */
 1055: if ( msgfp!=NULL && msglevel>=9999 )
 1056:   { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
 1057:     nbytes); fflush(msgfp); }
 1058: if ( nbytes>0 && nbytes<=pixsz*maxraster )  /* fail if width*height too big*/
 1059:   pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
 1060: if ( msgfp!=NULL && msglevel>=9999 )
 1061:   { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
 1062:     nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
 1063: if ( pixmap == (pixbyte *)NULL )	/* malloc failed */
 1064:   { delete_raster(rp);			/* so free everything */
 1065:     rp = (raster *)NULL;		/* reset pointer */
 1066:     goto end_of_job; }			/* and return error to caller */
 1067: memset((void *)pixmap,filler,nbytes);	/* init bytes to binary 0's or ' 's*/
 1068: *pixmap = (pixbyte)0;			/* and first byte alwasy 0 */
 1069: rp->pixmap = pixmap;			/* store ptr to malloced memory */
 1070: /* -------------------------------------------------------------------------
 1071: Back to caller with address of raster struct, or NULL ptr for any error.
 1072: -------------------------------------------------------------------------- */
 1073: end_of_job:
 1074:   if ( msgfp!=NULL && msglevel>=9999 )
 1075:     { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
 1076:       width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
 1077:   return ( rp );			/* back to caller with raster */
 1078: } /* --- end-of-function new_raster() --- */
 1079: 
 1080: 
 1081: /* ==========================================================================
 1082:  * Function:	new_subraster ( width, height, pixsz )
 1083:  * Purpose:	Allocate a new subraster along with
 1084:  *		an embedded raster of width x height.
 1085:  * --------------------------------------------------------------------------
 1086:  * Arguments:	width (I)	int containing width of embedded raster
 1087:  *		height (I)	int containing height of embedded raster
 1088:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
 1089:  * --------------------------------------------------------------------------
 1090:  * Returns:	( subraster * )	ptr to newly-allocated subraster,
 1091:  *				or NULL for any error.
 1092:  * --------------------------------------------------------------------------
 1093:  * Notes:     o	if width or height <=0, embedded raster not allocated
 1094:  * ======================================================================= */
 1095: /* --- entry point --- */
 1096: subraster *new_subraster ( int width, int height, int pixsz )
 1097: {
 1098: /* -------------------------------------------------------------------------
 1099: Allocations and Declarations
 1100: -------------------------------------------------------------------------- */
 1101: subraster *sp=NULL;			/* subraster returned to caller */
 1102: raster	*new_raster(), *rp=NULL;	/* image raster embedded in sp */
 1103: int	delete_subraster();		/* in case new_raster() fails */
 1104: int	size = NORMALSIZE,		/* default size */
 1105: 	baseline = height-1;		/* and baseline */
 1106: /* -------------------------------------------------------------------------
 1107: allocate and initialize subraster struct
 1108: -------------------------------------------------------------------------- */
 1109: if ( msgfp!=NULL && msglevel>=9999 )
 1110:   { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
 1111:     width,height,pixsz); fflush(msgfp); }
 1112: /* --- allocate subraster struct --- */
 1113: sp = (subraster *)malloc(sizeof(subraster));  /* malloc subraster struct */
 1114: if ( sp == (subraster *)NULL )		/* malloc failed */
 1115:   goto end_of_job;			/* return error to caller */
 1116: /* --- initialize subraster struct --- */
 1117: sp->type = NOVALUE;			/* character or image raster */
 1118: sp->symdef =  (mathchardef *)NULL;	/* mathchardef identifying image */
 1119: sp->baseline = baseline;		/*0 if image is entirely descending*/
 1120: sp->size = size;			/* font size 0-4 */
 1121: sp->toprow = sp->leftcol = (-1);	/* upper-left corner of subraster */
 1122: sp->image = (raster *)NULL;		/*ptr to bitmap image of subraster*/
 1123: /* -------------------------------------------------------------------------
 1124: allocate raster and embed it in subraster, and return to caller
 1125: -------------------------------------------------------------------------- */
 1126: /* --- allocate raster struct if desired --- */
 1127: if ( width>0 && height>0 && pixsz>0 )	/* caller wants raster */
 1128:   { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
 1129:     !=   NULL )				/* if allocate succeeded */
 1130:         sp->image = rp;			/* embed raster in subraster */
 1131:     else				/* or if allocate failed */
 1132:       { delete_subraster(sp);		/* free non-unneeded subraster */
 1133: 	sp = NULL; } }			/* signal error */
 1134: /* --- back to caller with new subraster or NULL --- */
 1135: end_of_job:
 1136:   if ( msgfp!=NULL && msglevel>=9999 )
 1137:     { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
 1138:       width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
 1139:   return ( sp );
 1140: } /* --- end-of-function new_subraster() --- */
 1141: 
 1142: 
 1143: /* ==========================================================================
 1144:  * Function:	new_chardef (  )
 1145:  * Purpose:	Allocates and initializes a chardef struct,
 1146:  *		but _not_ the embedded raster struct.
 1147:  * --------------------------------------------------------------------------
 1148:  * Arguments:	none
 1149:  * --------------------------------------------------------------------------
 1150:  * Returns:	( chardef * )	ptr to allocated and initialized
 1151:  *				chardef struct, or NULL for any error.
 1152:  * --------------------------------------------------------------------------
 1153:  * Notes:
 1154:  * ======================================================================= */
 1155: /* --- entry point --- */
 1156: chardef	*new_chardef (  )
 1157: {
 1158: /* -------------------------------------------------------------------------
 1159: Allocations and Declarations
 1160: -------------------------------------------------------------------------- */
 1161: chardef	*cp = (chardef *)NULL;		/* chardef ptr returned to caller */
 1162: /* -------------------------------------------------------------------------
 1163: allocate and initialize chardef struct
 1164: -------------------------------------------------------------------------- */
 1165: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
 1166: if ( cp == (chardef *)NULL )		/* malloc failed */
 1167:   goto end_of_job;			/* return error to caller */
 1168: cp->charnum = cp->location = 0;		/* init character description */
 1169: cp->toprow = cp->topleftcol = 0;	/* init upper-left corner */
 1170: cp->botrow = cp->botleftcol = 0;	/* init lower-left corner */
 1171: cp->image.width = cp->image.height = 0;	/* init raster dimensions */
 1172: cp->image.format = 0;			/* init raster format */
 1173: cp->image.pixsz = 0;			/* and #bits per pixel */
 1174: cp->image.pixmap = NULL;		/* init raster pixmap as null */
 1175: /* -------------------------------------------------------------------------
 1176: Back to caller with address of chardef struct, or NULL ptr for any error.
 1177: -------------------------------------------------------------------------- */
 1178: end_of_job:
 1179:   return ( cp );
 1180: } /* --- end-of-function new_chardef() --- */
 1181: 
 1182: 
 1183: /* ==========================================================================
 1184:  * Function:	delete_raster ( rp )
 1185:  * Purpose:	Destructor for raster.
 1186:  *		Frees memory for raster bitmap and struct.
 1187:  * --------------------------------------------------------------------------
 1188:  * Arguments:	rp (I)		ptr to raster struct to be deleted.
 1189:  * --------------------------------------------------------------------------
 1190:  * Returns:	( int )		1 if completed successfully,
 1191:  *				or 0 otherwise (for any error).
 1192:  * --------------------------------------------------------------------------
 1193:  * Notes:
 1194:  * ======================================================================= */
 1195: /* --- entry point --- */
 1196: int	delete_raster ( raster *rp )
 1197: {
 1198: /* -------------------------------------------------------------------------
 1199: free raster bitmap and struct
 1200: -------------------------------------------------------------------------- */
 1201: if ( rp != (raster *)NULL )		/* can't free null ptr */
 1202:   {
 1203:   if ( rp->pixmap != (pixbyte *)NULL )	/* can't free null ptr */
 1204:     free((void *)rp->pixmap);		/* free pixmap within raster */
 1205:   free((void *)rp);			/* lastly, free raster struct */
 1206:   } /* --- end-of-if(rp!=NULL) --- */
 1207: return ( 1 );				/* back to caller, 1=okay 0=failed */
 1208: } /* --- end-of-function delete_raster() --- */
 1209: 
 1210: 
 1211: /* ==========================================================================
 1212:  * Function:	delete_subraster ( sp )
 1213:  * Purpose:	Deallocates a subraster (and embedded raster)
 1214:  * --------------------------------------------------------------------------
 1215:  * Arguments:	sp (I)		ptr to subraster struct to be deleted.
 1216:  * --------------------------------------------------------------------------
 1217:  * Returns:	( int )		1 if completed successfully,
 1218:  *				or 0 otherwise (for any error).
 1219:  * --------------------------------------------------------------------------
 1220:  * Notes:
 1221:  * ======================================================================= */
 1222: /* --- entry point --- */
 1223: int	delete_subraster ( subraster *sp )
 1224: {
 1225: /* -------------------------------------------------------------------------
 1226: free subraster struct
 1227: -------------------------------------------------------------------------- */
 1228: int	delete_raster();		/* to delete embedded raster */
 1229: if ( sp != (subraster *)NULL )		/* can't free null ptr */
 1230:   {
 1231:   if ( sp->type != CHARASTER )		/* not static character data */
 1232:     if ( sp->image != NULL )		/*raster allocated within subraster*/
 1233:       delete_raster(sp->image);		/* so free embedded raster */
 1234:   free((void *)sp);			/* and free subraster struct itself*/
 1235:   } /* --- end-of-if(sp!=NULL) --- */
 1236: return ( 1 );				/* back to caller, 1=okay 0=failed */
 1237: } /* --- end-of-function delete_subraster() --- */
 1238: 
 1239: 
 1240: /* ==========================================================================
 1241:  * Function:	delete_chardef ( cp )
 1242:  * Purpose:	Deallocates a chardef (and bitmap of embedded raster)
 1243:  * --------------------------------------------------------------------------
 1244:  * Arguments:	cp (I)		ptr to chardef struct to be deleted.
 1245:  * --------------------------------------------------------------------------
 1246:  * Returns:	( int )		1 if completed successfully,
 1247:  *				or 0 otherwise (for any error).
 1248:  * --------------------------------------------------------------------------
 1249:  * Notes:
 1250:  * ======================================================================= */
 1251: /* --- entry point --- */
 1252: int	delete_chardef ( chardef *cp )
 1253: {
 1254: /* -------------------------------------------------------------------------
 1255: free chardef struct
 1256: -------------------------------------------------------------------------- */
 1257: if ( cp != (chardef *)NULL )		/* can't free null ptr */
 1258:   {
 1259:   if ( cp->image.pixmap != NULL )	/* pixmap allocated within raster */
 1260:     free((void *)cp->image.pixmap);	/* so free embedded pixmap */
 1261:   free((void *)cp);			/* and free chardef struct itself */
 1262:   } /* --- end-of-if(cp!=NULL) --- */
 1263: /* -------------------------------------------------------------------------
 1264: Back to caller with 1=okay, 0=failed.
 1265: -------------------------------------------------------------------------- */
 1266: return ( 1 );
 1267: } /* --- end-of-function delete_chardef() --- */
 1268: 
 1269: 
 1270: /* ==========================================================================
 1271:  * Function:	rastcpy ( rp )
 1272:  * Purpose:	makes duplicate copy of rp
 1273:  * --------------------------------------------------------------------------
 1274:  * Arguments:	rp (I)		ptr to raster struct to be copied
 1275:  * --------------------------------------------------------------------------
 1276:  * Returns:	( raster * )	ptr to new copy rp,
 1277:  *				or NULL for any error.
 1278:  * --------------------------------------------------------------------------
 1279:  * Notes:     o
 1280:  * ======================================================================= */
 1281: /* --- entry point --- */
 1282: raster	*rastcpy ( raster *rp )
 1283: {
 1284: /* -------------------------------------------------------------------------
 1285: Allocations and Declarations
 1286: -------------------------------------------------------------------------- */
 1287: raster	*new_raster(), *newrp=NULL;	/*copied raster returned to caller*/
 1288: int	height= (rp==NULL?0:rp->height), /* original and copied height */
 1289: 	width = (rp==NULL?0:rp->width),	/* original and copied width */
 1290: 	pixsz = (rp==NULL?0:rp->pixsz),	/* #bits per pixel */
 1291: 	nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
 1292: /* -------------------------------------------------------------------------
 1293: allocate rotated raster and fill it
 1294: -------------------------------------------------------------------------- */
 1295: /* --- allocate copied raster with same width,height, and copy bitmap --- */
 1296: if ( rp != NULL )			/* nothing to copy if ptr null */
 1297:   if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
 1298:   !=   NULL )				/* check that allocate succeeded */
 1299:     memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
 1300: return ( newrp );			/* return copied raster to caller */
 1301: } /* --- end-of-function rastcpy() --- */
 1302: 
 1303: 
 1304: /* ==========================================================================
 1305:  * Function:	subrastcpy ( sp )
 1306:  * Purpose:	makes duplicate copy of sp
 1307:  * --------------------------------------------------------------------------
 1308:  * Arguments:	sp (I)		ptr to subraster struct to be copied
 1309:  * --------------------------------------------------------------------------
 1310:  * Returns:	( subraster * )	ptr to new copy sp,
 1311:  *				or NULL for any error.
 1312:  * --------------------------------------------------------------------------
 1313:  * Notes:     o
 1314:  * ======================================================================= */
 1315: /* --- entry point --- */
 1316: subraster *subrastcpy ( subraster *sp )
 1317: {
 1318: /* -------------------------------------------------------------------------
 1319: Allocations and Declarations
 1320: -------------------------------------------------------------------------- */
 1321: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
 1322: raster	*rastcpy(), *newrp=NULL;	/* and new raster image within it */
 1323: int	delete_subraster();		/* dealloc newsp if rastcpy() fails*/
 1324: /* -------------------------------------------------------------------------
 1325: make copy, and return it to caller
 1326: -------------------------------------------------------------------------- */
 1327: if ( sp == NULL ) goto end_of_job;	/* nothing to copy */
 1328: /* --- allocate new subraster "envelope" for copy --- */
 1329: if ( (newsp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
 1330: ==   NULL ) goto end_of_job;		/* and quit if we fail to allocate */
 1331: /* --- transparently copy original envelope to new one --- */
 1332: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
 1333: /* --- make a copy of the rasterized image itself, if there is one --- */
 1334: if ( sp->image != NULL )		/* there's an image embedded in sp */
 1335:   if ( (newrp = rastcpy(sp->image))	/* so copy rasterized image in sp */
 1336:   ==   NULL )				/* failed to copy successfully */
 1337:     { delete_subraster(newsp);		/* won't need newsp any more */
 1338:       newsp = NULL;			/* because we're returning error */
 1339:       goto end_of_job; }		/* back to caller with error signal*/
 1340: /* --- set new params in new envelope --- */
 1341: newsp->image = newrp;			/* new raster image we just copied */
 1342: switch ( sp->type )			/* set new raster image type */
 1343:   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
 1344:     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
 1345:     case FRACRASTER:                   newsp->type = FRACRASTER;   break;
 1346:     case BLANKSIGNAL:                  newsp->type = blanksignal;  break;
 1347:     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
 1348: /* --- return copy of sp to caller --- */
 1349: end_of_job:
 1350:   return ( newsp );			/* copy back to caller */
 1351: } /* --- end-of-function subrastcpy() --- */
 1352: 
 1353: 
 1354: /* ==========================================================================
 1355:  * Function:	rastrot ( rp )
 1356:  * Purpose:	rotates rp image 90 degrees right/clockwise
 1357:  * --------------------------------------------------------------------------
 1358:  * Arguments:	rp (I)		ptr to raster struct to be rotated
 1359:  * --------------------------------------------------------------------------
 1360:  * Returns:	( raster * )	ptr to new raster rotated relative to rp,
 1361:  *				or NULL for any error.
 1362:  * --------------------------------------------------------------------------
 1363:  * Notes:     o	An underbrace is } rotated 90 degrees clockwise,
 1364:  *		a hat is <, etc.
 1365:  * ======================================================================= */
 1366: /* --- entry point --- */
 1367: raster	*rastrot ( raster *rp )
 1368: {
 1369: /* -------------------------------------------------------------------------
 1370: Allocations and Declarations
 1371: -------------------------------------------------------------------------- */
 1372: raster	*new_raster(), *rotated=NULL;	/*rotated raster returned to caller*/
 1373: int	height = rp->height, irow,	/* original height, row index */
 1374: 	width = rp->width, icol,	/* original width, column index */
 1375: 	pixsz = rp->pixsz;		/* #bits per pixel */
 1376: /* -------------------------------------------------------------------------
 1377: allocate rotated raster and fill it
 1378: -------------------------------------------------------------------------- */
 1379: /* --- allocate rotated raster with flipped width<-->height --- */
 1380: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
 1381: !=   NULL )				/* check that allocation succeeded */
 1382:   /* --- fill rotated raster --- */
 1383:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
 1384:     for ( icol=0; icol<width; icol++ )	/* and each column of rp */
 1385:       {	int value = getpixel(rp,irow,icol);
 1386: 	/* setpixel(rotated,icol,irow,value); } */
 1387: 	setpixel(rotated,icol,(height-1-irow),value); }
 1388: return ( rotated );			/* return rotated raster to caller */
 1389: } /* --- end-of-function rastrot() --- */
 1390: 
 1391: 
 1392: /* ==========================================================================
 1393:  * Function:	rastmag ( rp, magstep )
 1394:  * Purpose:	magnifies rp by integer magstep,
 1395:  *		e.g., double-height and double-width if magstep=2
 1396:  * --------------------------------------------------------------------------
 1397:  * Arguments:	rp (I)		ptr to raster struct to be "magnified"
 1398:  *		magstep (I)	int containing magnification scale,
 1399:  *				e.g., 2 to double the width and height of rp
 1400:  * --------------------------------------------------------------------------
 1401:  * Returns:	( raster * )	ptr to new raster magnified relative to rp,
 1402:  *				or NULL for any error.
 1403:  * --------------------------------------------------------------------------
 1404:  * Notes:     o
 1405:  * ======================================================================= */
 1406: /* --- entry point --- */
 1407: raster	*rastmag ( raster *rp, int magstep )
 1408: {
 1409: /* -------------------------------------------------------------------------
 1410: Allocations and Declarations
 1411: -------------------------------------------------------------------------- */
 1412: raster	*new_raster(), *magnified=NULL;	/* magnified raster back to caller */
 1413: int	height = rp->height, irow,	/* height, row index */
 1414: 	width = rp->width, icol,	/* width, column index */
 1415: 	mrow = 0, mcol = 0,		/* dup pixels magstep*magstep times*/
 1416: 	pixsz = rp->pixsz;		/* #bits per pixel */
 1417: /* -------------------------------------------------------------------------
 1418: check args
 1419: -------------------------------------------------------------------------- */
 1420: if ( rp == NULL ) goto end_of_job;	/* no input raster supplied */
 1421: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
 1422: /* -------------------------------------------------------------------------
 1423: allocate magnified raster and fill it
 1424: -------------------------------------------------------------------------- */
 1425: /* --- allocate magnified raster with magstep*width, magstep*height --- */
 1426: if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
 1427: !=   NULL )				/* check that allocation succeeded */
 1428:   /* --- fill reflected raster --- */
 1429:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
 1430:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
 1431:       for ( icol=0; icol<width; icol++ ) /* and for each column of rp */
 1432:         for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
 1433:          int value = getpixel(rp,irow,icol);
 1434: 	 int row1 = irow*magstep, col1 = icol*magstep;
 1435:          setpixel(magnified,(row1+mrow),(col1+mcol),value); }
 1436: end_of_job:
 1437:   return ( magnified );			/*return magnified raster to caller*/
 1438: } /* --- end-of-function rastmag() --- */
 1439: 
 1440: 
 1441: /* ==========================================================================
 1442:  * Function:	bytemapmag ( bytemap, width, height, magstep )
 1443:  * Purpose:	magnifies a bytemap by integer magstep,
 1444:  *		e.g., double-height and double-width if magstep=2
 1445:  * --------------------------------------------------------------------------
 1446:  * Arguments:	bytemap (I)	intbyte * ptr to byte map to be "magnified"
 1447:  *		width (I)	int containing #cols in original bytemap
 1448:  *		height (I)	int containing #rows in original bytemap
 1449:  *		magstep (I)	int containing magnification scale,
 1450:  *				e.g., 2 to double the width and height of rp
 1451:  * --------------------------------------------------------------------------
 1452:  * Returns:	( intbyte * )	ptr to new bytemap magnified relative to
 1453:  *				original bytemap, or NULL for any error.
 1454:  * --------------------------------------------------------------------------
 1455:  * Notes:     o	Apply EPX/Scale2x/AdvMAME2x  for magstep 2,
 1456:  *		and Scale3x/AdvMAME3x  for magstep 3,
 1457:  *		as described by http://en.wikipedia.org/wiki/2xSaI
 1458:  * ======================================================================= */
 1459: /* --- entry point --- */
 1460: intbyte	*bytemapmag ( intbyte *bytemap, int width, int height, int magstep )
 1461: {
 1462: /* -------------------------------------------------------------------------
 1463: Allocations and Declarations
 1464: -------------------------------------------------------------------------- */
 1465: intbyte	*magnified=NULL;		/* magnified bytemap back to caller*/
 1466: int	irow, icol,			/* original height, width indexes */
 1467: 	mrow=0, mcol=0;			/* dup bytes magstep*magstep times */
 1468: int	imap = (-1),			/* original bytemap[] index */
 1469: 	byteval = 0;			/* byteval=bytemap[imap] */
 1470: int	isAdvMAME = 1;			/* true to apply AdvMAME2x and 3x */
 1471: int	icell[10],			/* bytemap[] nearest neighbors */
 1472: 	bmmdiff = 64;			/* nearest neighbor diff allowed */
 1473: #define	bmmeq(i,j) ((absval((icell[i]-icell[j]))<=bmmdiff)) /*approx equal*/
 1474: /* -------------------------------------------------------------------------
 1475: check args
 1476: -------------------------------------------------------------------------- */
 1477: if ( bytemap == NULL ) goto end_of_job;	/* no input bytemap supplied */
 1478: if ( width<1 || height<1 ) goto end_of_job; /* invalid bytemap dimensions */
 1479: if ( width*height>100000 ) goto end_of_job; /* sanity check */
 1480: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
 1481: /* -------------------------------------------------------------------------
 1482: allocate magnified bytemap and fill it
 1483: -------------------------------------------------------------------------- */
 1484: /* --- allocate bytemap for magstep*width, magstep*height --- */
 1485: if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
 1486: !=   NULL )				/* check that allocation succeeded */
 1487:   /* --- fill reflected raster --- */
 1488:   for ( irow=0; irow<height; irow++ )	/* for each row of bytemap */
 1489:    for ( icol=0; icol<width; icol++ ) { /* and for each column of bytemap */
 1490:     int imag1 = (icol + irow*(width*magstep))*magstep; /*upper-left corner*/
 1491:     imap++;				/* bump bytemap[] index */
 1492:     byteval = (int)(bytemap[imap]);	/* grayscale value at this pixel */
 1493:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
 1494:      for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
 1495:       int idup = mcol + mrow*(width*magstep); /* offset from imag1 */
 1496:       int imag = imag1+idup;		/* adjust magnified[imag] */
 1497:       magnified[imag] = (intbyte)(byteval);
 1498:       /* --- apply AdvMAME2x and 3x (if desired) --- */
 1499:       if ( isAdvMAME ) {		/* AdvMAME2x and 3x wanted */
 1500:        int mcell = 1 + mcol + magstep*mrow; /*1,2,3,4 or 1,2,3,4,5,6,7,8,9*/
 1501:        icell[5]= byteval,		/* center cell of 3x3 bytemap[] */
 1502:        icell[4]= (icol>0?(int)(bytemap[imap-1]):byteval), /*left of center*/
 1503:        icell[6]= (icol<width?(int)(bytemap[imap+1]):byteval), /*right*/
 1504:        icell[2]= (irow>0?(int)(bytemap[imap-width]):byteval),/*above center*/
 1505:        icell[8]= (irow<height?(int)(bytemap[imap+width]):byteval), /*below*/
 1506:        icell[1]= (irow>0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
 1507:        icell[3]= (irow>0&&icol<width?(int)(bytemap[imap-width+1]):byteval),
 1508:        icell[7]= (irow<height&&icol>0?(int)(bytemap[imap+width-1]):byteval),
 1509:       icell[9]=(irow<height&&icol<width?(int)(bytemap[imap+width+1]):byteval);
 1510:        switch ( magstep ) {		/* 2x magstep=2, 3x magstep=3 */
 1511:         default: break;			/* no AdvMAME at other magsteps */
 1512:         case 2:				/* AdvMAME2x */
 1513:          if ( mcell == 1 )
 1514:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
 1515:              magnified[imag] = icell[2];
 1516:          if ( mcell == 2 )
 1517:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
 1518:              magnified[imag] = icell[6];
 1519:          if ( mcell == 4 )
 1520:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
 1521:              magnified[imag] = icell[8];
 1522:          if ( mcell == 3 )
 1523:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
 1524:              magnified[imag] = icell[4];
 1525:          break;
 1526:         case 3:				/* AdvMAME3x */
 1527:          if ( mcell == 1 )
 1528:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
 1529:              magnified[imag] = icell[4];
 1530:          if ( mcell == 2 )
 1531:            if ( (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,3))
 1532:              || (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,1)) )
 1533:              magnified[imag] = icell[2];
 1534:          if ( mcell == 3 )
 1535:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
 1536:              magnified[imag] = icell[6];
 1537:          if ( mcell == 4 )
 1538:            if ( (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,1))
 1539:              || (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,7)) )
 1540:              magnified[imag] = icell[4];
 1541:          if ( mcell == 6 )
 1542:            if ( (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,9))
 1543:              || (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,3)) )
 1544:              magnified[imag] = icell[6];
 1545:          if ( mcell == 7 )
 1546:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
 1547:              magnified[imag] = icell[4];
 1548:          if ( mcell == 8 )
 1549:            if ( (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,7))
 1550:              || (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,9)) )
 1551:              magnified[imag] = icell[8];
 1552:          if ( mcell == 9 )
 1553:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
 1554:              magnified[imag] = icell[6];
 1555:          break;
 1556:         } } /* --- end-of-switch(magstep) --- */
 1557:       } /* --- end-of-for(mrow,mcol) --- */
 1558:     } /* --- end-of-for(irow,icol) --- */
 1559: end_of_job:
 1560:   return ( magnified );			/*return magnified raster to caller*/
 1561: } /* --- end-of-function bytemapmag() --- */
 1562: 
 1563: 
 1564: /* ==========================================================================
 1565:  * Function:	rastref ( rp, axis )
 1566:  * Purpose:	reflects rp, horizontally about y-axis |_ becomes _| if axis=1
 1567:  *		or vertically about x-axis M becomes W if axis=2.
 1568:  * --------------------------------------------------------------------------
 1569:  * Arguments:	rp (I)		ptr to raster struct to be reflected
 1570:  *		axis (I)	int containing 1 for horizontal reflection,
 1571:  *				or 2 for vertical
 1572:  * --------------------------------------------------------------------------
 1573:  * Returns:	( raster * )	ptr to new raster reflected relative to rp,
 1574:  *				or NULL for any error.
 1575:  * --------------------------------------------------------------------------
 1576:  * Notes:     o
 1577:  * ======================================================================= */
 1578: /* --- entry point --- */
 1579: raster	*rastref ( raster *rp, int axis )
 1580: {
 1581: /* -------------------------------------------------------------------------
 1582: Allocations and Declarations
 1583: -------------------------------------------------------------------------- */
 1584: raster	*new_raster(), *reflected=NULL;	/* reflected raster back to caller */
 1585: int	height = rp->height, irow,	/* height, row index */
 1586: 	width = rp->width, icol,	/* width, column index */
 1587: 	pixsz = rp->pixsz;		/* #bits per pixel */
 1588: /* -------------------------------------------------------------------------
 1589: allocate reflected raster and fill it
 1590: -------------------------------------------------------------------------- */
 1591: /* --- allocate reflected raster with same width, height --- */
 1592: if ( axis==1 || axis==2 )		/* first validate axis arg */
 1593:  if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
 1594:  !=   NULL )				/* check that allocation succeeded */
 1595:   /* --- fill reflected raster --- */
 1596:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
 1597:     for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
 1598:       int value = getpixel(rp,irow,icol);
 1599:       if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
 1600:       if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
 1601: return ( reflected );			/*return reflected raster to caller*/
 1602: } /* --- end-of-function rastref() --- */
 1603: 
 1604: 
 1605: /* ==========================================================================
 1606:  * Function:	rastput ( target, source, top, left, isopaque )
 1607:  * Purpose:	Overlays source onto target,
 1608:  *		with the 0,0-bit of source onto the top,left-bit of target.
 1609:  * --------------------------------------------------------------------------
 1610:  * Arguments:	target (I)	ptr to target raster struct
 1611:  *		source (I)	ptr to source raster struct
 1612:  *		top (I)		int containing 0 ... target->height - 1
 1613:  *		left (I)	int containing 0 ... target->width - 1
 1614:  *		isopaque (I)	int containing false (zero) to allow
 1615:  *				original 1-bits of target to "show through"
 1616:  *				0-bits of source.
 1617:  * --------------------------------------------------------------------------
 1618:  * Returns:	( int )		1 if completed successfully,
 1619:  *				or 0 otherwise (for any error).
 1620:  * --------------------------------------------------------------------------
 1621:  * Notes:
 1622:  * ======================================================================= */
 1623: /* --- entry point --- */
 1624: int	rastput ( raster *target, raster *source,
 1625: 		int top, int left, int isopaque )
 1626: {
 1627: /* -------------------------------------------------------------------------
 1628: Allocations and Declarations
 1629: -------------------------------------------------------------------------- */
 1630: int	irow, icol,		/* indexes over source raster */
 1631: 	twidth=target->width, theight=target->height, /*target width,height*/
 1632: 	tpix, ntpix = twidth*theight; /* #pixels in target */
 1633: int	isfatal = 0,		/* true to abend on out-of-bounds error */
 1634: 	isstrict = 0/*1*/,	/* true for strict bounds check - no "wrap"*/
 1635: 	isokay = 1;		/* true if no pixels out-of-bounds */
 1636: /* -------------------------------------------------------------------------
 1637: superimpose source onto target, one bit at a time
 1638: -------------------------------------------------------------------------- */
 1639: if ( isstrict && (top<0 || left<0) )		/* args fail strict test */
 1640:  isokay = 0;					/* so just return error */
 1641: else
 1642:  for ( irow=0; irow<source->height; irow++ )	/* for each scan line */
 1643:   {
 1644:   tpix = (top+irow)*target->width + left - 1;	/*first target pixel (-1)*/
 1645:   for ( icol=0; icol<source->width; icol++ )	/* each pixel in scan line */
 1646:     {
 1647:     int svalue = getpixel(source,irow,icol);	/* source pixel value */
 1648:     ++tpix;					/* bump target pixel */
 1649:     if ( msgfp!=NULL && msglevel>=9999 )	/* debugging output */
 1650:       {	fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
 1651: 	"left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
 1652: 	left,icol,twidth);  fflush(msgfp); }
 1653:     if ( tpix >= ntpix				/* bounds check failed */
 1654:     ||   (isstrict && (irow+top>=theight || icol+left>=twidth)) )
 1655:       {	isokay = 0;				/* reset okay flag */
 1656: 	if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
 1657: 	else break; }				/*or just go on to next row*/
 1658:     if ( tpix >= 0 )				/* bounds check okay */
 1659:      if ( svalue!=0 || isopaque ) {		/*got dark or opaque source*/
 1660:       setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
 1661:     } /* --- end-of-for(icol) --- */
 1662:   } /* --- end-of-for(irow) --- */
 1663: /* -------------------------------------------------------------------------
 1664: Back to caller with 1=okay, 0=failed.
 1665: -------------------------------------------------------------------------- */
 1666: end_of_job:
 1667:   return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
 1668: } /* --- end-of-function rastput() --- */
 1669: 
 1670: 
 1671: /* ==========================================================================
 1672:  * Function:	rastcompose ( sp1, sp2, offset2, isalign, isfree )
 1673:  * Purpose:	Overlays sp2 on top of sp1, leaving both unchanged
 1674:  *		and returning a newly-allocated composite subraster.
 1675:  *		Frees/deletes input sp1 and/or sp2 depending on value
 1676:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
 1677:  * --------------------------------------------------------------------------
 1678:  * Arguments:	sp1 (I)		subraster *  to "underneath" subraster,
 1679:  *				whose baseline is preserved
 1680:  *		sp2 (I)		subraster *  to "overlaid" subraster
 1681:  *		offset2 (I)	int containing 0 or number of pixels
 1682:  *				to horizontally shift sp2 relative to sp1,
 1683:  *				either positive (right) or negative
 1684:  *		isalign (I)	int containing 1 to align baselines,
 1685:  *				or 0 to vertically center sp2 over sp1.
 1686:  *				For isalign=2, images are vertically
 1687:  *				centered, but then adjusted by \raisebox
 1688:  *				lifts, using global variables rastlift1
 1689:  *				for sp1 and rastlift for sp2.
 1690:  *		isfree (I)	int containing 1=free sp1 before return,
 1691:  *				2=free sp2, 3=free both, 0=free none.
 1692:  * --------------------------------------------------------------------------
 1693:  * Returns:	( subraster * )	pointer to constructed subraster
 1694:  *				or  NULL for any error
 1695:  * --------------------------------------------------------------------------
 1696:  * Notes:     o	The top-left corner of each raster box has coords (0,0),
 1697:  *		down to (h-1,w-1) for a box of height h and width w.
 1698:  *	      o	A raster's baseline, b, is typically 0 <= b < h.
 1699:  *		But b can actually go out-of-bounds, b>=h or b<0, for
 1700:  *		an image additionally lifted (b>=h) or lowered (b<0)
 1701:  *		with respect to the surrounding expression.
 1702:  *	      o	Note that b=h-1 means no descenders and the bottom
 1703:  *		of the symbol rests exactly on the baseline,
 1704:  *		whereas b=0 means the top pixel of the symbol rests
 1705:  *		on the baseline, and all other pixels are descenders.
 1706:  *	      o	The composite raster is constructed as follows...
 1707:  *		The base image is labelled height h1 and baseline b1,
 1708:  *		the overlay h2 and b2, and the composite H and B.
 1709:  *		     base       overlay
 1710:  *	    --- +------------------------+ ---   For the overlay to be
 1711:  *	     ^  |   ^        +----------+|  ^    vertically centered with
 1712:  *	     |  |   |        |          ||  |    respect to the base,
 1713:  *	     |  |   |B-b1    |          ||  |      B - b1 = H-B -(h1-b1), so
 1714:  *	     |  |   v        |          ||  |      2*B = H-h1 + 2*b1
 1715:  *	     |  |+----------+|          ||  |      B = b1 + (H-h1)/2
 1716:  *	     B  ||  ^    ^  ||          ||  |    And when the base image is
 1717:  *	     |  ||  |    |  ||          ||  |    bigger, H=h1 and B=b1 is
 1718:  *	     |  ||  b1   |  ||          ||  |    the obvious correct answer.
 1719:  *	     |  ||  |    h1 ||          || H=h2
 1720:  *	     v  ||  v    |  ||          ||  |
 1721:  *    ----------||-------|--||          ||--|--------
 1722:  *    baseline  || h1-b1 v  || overlay  ||  |
 1723:  *    for base  |+----------+| baseline ||  |
 1724:  *    and com-  |   ^        | ignored  ||  |
 1725:  *    posite    |   |H-B-    |----------||  |
 1726:  *		|   | (h1-b1)|          ||  |
 1727:  *		|   v        +----------+|  v
 1728:  *		+------------------------+ ---
 1729:  * ======================================================================= */
 1730: /* --- entry point --- */
 1731: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
 1732: 			int isalign, int isfree )
 1733: {
 1734: /* -------------------------------------------------------------------------
 1735: Allocations and Declarations
 1736: -------------------------------------------------------------------------- */
 1737: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
 1738: raster	*rp=(raster *)NULL;		/* new composite raster in sp */
 1739: int	delete_subraster();		/* in case isfree non-zero */
 1740: int	rastput();			/*place sp1,sp2 in composite raster*/
 1741: int	base1   = sp1->baseline,	/*baseline for underlying subraster*/
 1742: 	height1 = (sp1->image)->height,	/* height for underlying subraster */
 1743: 	width1  = (sp1->image)->width,	/* width for underlying subraster */
 1744: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for underlying subraster */
 1745: 	base2   = sp2->baseline,	/*baseline for overlaid subraster */
 1746: 	height2 = (sp2->image)->height,	/* height for overlaid subraster */
 1747: 	width2  = (sp2->image)->width,	/* width for overlaid subraster */
 1748: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for overlaid subraster */
 1749: int	height  = max2(height1,height2), /*composite height if sp2 centered*/
 1750: 	base    = base1 + (height-height1)/2, /* and composite baseline */
 1751: 	tlc2    = (height-height2)/2,	/* top-left corner for overlay */
 1752: 	width=0, pixsz=0;		/* other params for composite */
 1753: int	lift1   = rastlift1,		/* vertical \raisebox lift for sp1 */
 1754: 	lift2   = rastlift;		/* vertical \raisebox lift for sp2 */
 1755: /* -------------------------------------------------------------------------
 1756: Initialization
 1757: -------------------------------------------------------------------------- */
 1758: /* --- determine height, width and baseline of composite raster --- */
 1759: switch ( isalign ) {
 1760:   default:
 1761:   case 0:				/* centered, baselines not aligned */
 1762:     height = max2(height1,height2);	/* max height */
 1763:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
 1764:     break;
 1765:   case 1:				/* baselines of sp1,sp2 aligned */
 1766:     height = max2(base1+1,base2+1)	/* max height above baseline */
 1767:            + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
 1768:     base   = max2(base1,base2);		/* max space above baseline */
 1769:     break;
 1770:   case 2:				/* centered +/- \raisebox lifts */
 1771:     base1 -= lift1;  base2 -= lift2;	/* reset to unlifted images */
 1772:     /* --- start with default for centered, unlifted images --- */
 1773:     height2 += 2*absval(lift2);		/* "virtual" height of overlay */
 1774:     height = max2(height1,height2);	/* max height */
 1775:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
 1776:     tlc2   = (height-height2)/2		/* top-left corner for overlay */
 1777:            + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
 1778:     break;
 1779:   } /* --- end-of-switch(isalign) --- */
 1780: width = max2(width1,width2+abs(offset2)); /* max width */
 1781: pixsz = max2(pixsz1,pixsz2);		/* bitmap,bytemap becomes bytemap */
 1782: /* -------------------------------------------------------------------------
 1783: allocate concatted composite subraster
 1784: -------------------------------------------------------------------------- */
 1785: /* --- allocate returned subraster (and then initialize it) --- */
 1786: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 1787: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
 1788: /* --- initialize subraster parameters --- */
 1789: sp->type = IMAGERASTER;			/* image */
 1790: sp->baseline = base;			/* composite baseline */
 1791: sp->size = sp1->size;			/* underlying char is sp1 */
 1792: if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
 1793: /* --- extract raster from subraster --- */
 1794: rp = sp->image;				/* raster allocated in subraster */
 1795: /* -------------------------------------------------------------------------
 1796: overlay sp1 and sp2 in new composite raster
 1797: -------------------------------------------------------------------------- */
 1798: switch ( isalign ) {
 1799:   default:
 1800:   case 0:				/* centered, baselines not aligned */
 1801:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
 1802:     rastput (rp, sp2->image, (height-height2)/2,		/*overlaid*/
 1803: 		(width-width2)/2+offset2, 0);
 1804:     break;
 1805:   case 1:				/* baselines of sp1,sp2 aligned */
 1806:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
 1807:     rastput (rp, sp2->image, base-base2,			/*overlaid*/
 1808: 		(width-width2)/2+offset2, 0);
 1809:     break;
 1810:   case 2: if(1){			/* centered +/- \raisebox lifts */
 1811:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
 1812:     rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
 1813:     break;
 1814:   } /* --- end-of-switch(isalign) --- */
 1815: /* -------------------------------------------------------------------------
 1816: free input if requested
 1817: -------------------------------------------------------------------------- */
 1818: if ( isfree > 0 )			/* caller wants input freed */
 1819:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
 1820:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
 1821: /* -------------------------------------------------------------------------
 1822: Back to caller with pointer to concatted subraster or with null for error
 1823: -------------------------------------------------------------------------- */
 1824: end_of_job:
 1825:   return ( sp );			/* back with subraster or null ptr */
 1826: } /* --- end-of-function rastcompose() --- */
 1827: 
 1828: 
 1829: /* ==========================================================================
 1830:  * Function:	rastcat ( sp1, sp2, isfree )
 1831:  * Purpose:	"Concatanates" subrasters sp1||sp2, leaving both unchanged
 1832:  *		and returning a newly-allocated subraster.
 1833:  *		Frees/deletes input sp1 and/or sp2 depending on value
 1834:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
 1835:  * --------------------------------------------------------------------------
 1836:  * Arguments:	sp1 (I)		subraster *  to left-hand subraster
 1837:  *		sp2 (I)		subraster *  to right-hand subraster
 1838:  *		isfree (I)	int containing 1=free sp1 before return,
 1839:  *				2=free sp2, 3=free both, 0=free none.
 1840:  * --------------------------------------------------------------------------
 1841:  * Returns:	( subraster * )	pointer to constructed subraster sp1||sp2
 1842:  *				or  NULL for any error
 1843:  * --------------------------------------------------------------------------
 1844:  * Notes:
 1845:  * ======================================================================= */
 1846: /* --- entry point --- */
 1847: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
 1848: {
 1849: /* -------------------------------------------------------------------------
 1850: Allocations and Declarations
 1851: -------------------------------------------------------------------------- */
 1852: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
 1853: raster	*rp=(raster *)NULL;		/* new concatted raster */
 1854: int	delete_subraster();		/* in case isfree non-zero */
 1855: int	rastput();			/*place sp1,sp2 in concatted raster*/
 1856: int	type_raster();			/* debugging display */
 1857: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
 1858: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
 1859: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
 1860: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for left-hand subraster */
 1861: 	type1   = sp1->type,		/* image type for left-hand */
 1862: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
 1863: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
 1864: 	width2  = (sp2->image)->width,	/* width for right-hand subraster */
 1865: 	pixsz2  = (sp2->image)->pixsz,	/* pixsz for right-hand subraster */
 1866: 	type2   = sp2->type;		/* image type for right-hand */
 1867: int	height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
 1868: int	issmash = (smashmargin!=0?1:0),	/* true to "squash" sp1||sp2 */
 1869: 	isopaque = (issmash?0:1),	/* not oppaque if smashing */
 1870: 	rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
 1871: 	oldsmashmargin = smashmargin,	/* save original smashmargin */
 1872: 	oldblanksymspace = blanksymspace, /* save original blanksymspace */
 1873: 	oldnocatspace = isnocatspace;	/* save original isnocatspace */
 1874: mathchardef *symdef1 = sp1->symdef,	/*mathchardef of last left-hand char*/
 1875: 	*symdef2 = sp2->symdef;		/* mathchardef of right-hand char */
 1876: int	class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
 1877: 	class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
 1878: 	smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
 1879: 		  class1==OPENING||class1==CLOSING||class1==PUNCTION),
 1880: 	smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
 1881: 		  class2==OPENING||class2==CLOSING||class2==PUNCTION),
 1882: 	space = fontsize/2+1;		/* #cols between sp1 and sp2 */
 1883: int	isfrac = (type1 == FRACRASTER	/* sp1 is a \frac */
 1884: 		  && class2 == PUNCTION); /* and sp2 is punctuation */
 1885: /* -------------------------------------------------------------------------
 1886: Initialization
 1887: -------------------------------------------------------------------------- */
 1888: /* --- determine inter-character space from character class --- */
 1889: if ( !isstring )
 1890:   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
 1891: else space = 1;				/* space for ascii string */
 1892: if ( isnocatspace > 0 ) {		/* spacing explicitly turned off */
 1893:   space = 0;				/* reset space */
 1894:   isnocatspace--; }			/* and decrement isnocatspace flag */
 1895: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
 1896: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
 1897:   space = 0;				/* no extra space between spaces */
 1898: if ( sp2->type != BLANKSIGNAL )		/* not a blank space signal */
 1899:   if ( blanksymspace != 0 ) {		/* and we have a space adjustment */
 1900:     space = max2(0,space+blanksymspace); /* adjust as much as possible */
 1901:     blanksymspace = 0; }		/* and reset adjustment */
 1902: if ( msgfp!=NULL && msglevel>=999 )	/* display space results */
 1903:   { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
 1904:     space,oldblanksymspace,oldnocatspace);  fflush(msgfp); }
 1905: /* --- determine smash --- */
 1906: if ( !isstring && !isfrac )		/* don't smash strings or \frac's */
 1907:  if ( issmash ) {			/* raster smash wanted */
 1908:    int	maxsmash = rastsmash(sp1,sp2),	/* calculate max smash space */
 1909: 	margin = smashmargin;		/* init margin without delta */
 1910:    if ( (1 && smash1 && smash2)		/* concatanating two chars */
 1911:    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
 1912: 	   && type1!=FRACRASTER  && type2!=FRACRASTER ) )
 1913:      /*maxsmash = 0;*/			/* turn off smash */
 1914:      margin = max2(space-1,0);		/* force small smashmargin */
 1915:    else					/* adjust for delta if images */
 1916:      if ( issmashdelta )		/* smashmargin is a delta value */
 1917:        margin += fontsize;		/* add displaystyle base to margin */
 1918:    if ( maxsmash == blanksignal )	/* sp2 is intentional blank */
 1919:      isblank = 1;			/* set blank flag signal */
 1920:    else					/* see how much extra space we have*/
 1921:      if ( maxsmash > margin )		/* enough space for adjustment */
 1922:        nsmash = maxsmash-margin;	/* make adjustment */
 1923:    if ( msgfp!=NULL && msglevel>=99 )	/* display smash results */
 1924:      { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
 1925:        maxsmash,margin,nsmash);
 1926:        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
 1927:        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
 1928:        fflush(msgfp); }
 1929:    } /* --- end-of-if(issmash) --- */
 1930: /* --- determine height, width and baseline of composite raster --- */
 1931: if ( !isstring )
 1932:  { height = max2(base1+1,base2+1)	/* max height above baseline */
 1933:           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
 1934:    width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
 1935:    width  = max3(width,width1,width2); } /* don't "over-smash" composite */
 1936: else					/* ascii string */
 1937:  { height = 1;				/* default */
 1938:    width  = width1 + width2 + space - 1; } /* no need for two nulls */
 1939: pixsz  = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
 1940: base   = max2(base1,base2);		/* max space above baseline */
 1941: if ( msgfp!=NULL && msglevel>=9999 )	/* display components */
 1942:   { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
 1943:     height1,width1,pixsz1,base1);
 1944:     type_raster(sp1->image,msgfp);	/* display left-hand raster */
 1945:     fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
 1946:     height2,width2,pixsz2,base2);
 1947:     type_raster(sp2->image,msgfp);	/* display right-hand raster */
 1948:     fprintf(msgfp,
 1949:     "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
 1950:     height,width,nsmash,pixsz,base);
 1951:     fflush(msgfp); }			/* flush msgfp buffer */
 1952: /* -------------------------------------------------------------------------
 1953: allocate concatted composite subraster
 1954: -------------------------------------------------------------------------- */
 1955: /* --- allocate returned subraster (and then initialize it) --- */
 1956: if ( msgfp!=NULL && msglevel>=9999 )
 1957:   { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
 1958:     width,height,pixsz); fflush(msgfp); }
 1959: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 1960: ==   (subraster *)NULL )		/* failed */
 1961:   { if ( msgfp!=NULL && msglevel>=1 )	/* report failure */
 1962:       {	fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
 1963: 	width,height,pixsz); fflush(msgfp); }
 1964:     goto end_of_job; }			/* failed, so quit */
 1965: /* --- initialize subraster parameters --- */
 1966: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
 1967: if ( !isstring )
 1968:   sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
 1969: 	(type2!=CHARASTER? type2 :
 1970: 	(type1!=CHARASTER&&type1!=BLANKSIGNAL
 1971: 	 &&type1!=FRACRASTER?type1:IMAGERASTER));
 1972: else
 1973:   sp->type = ASCIISTRING;		/* concatted ascii string */
 1974: sp->symdef = symdef2;			/* rightmost char is sp2 */
 1975: sp->baseline = base;			/* composite baseline */
 1976: sp->size = sp2->size;			/* rightmost char is sp2 */
 1977: if ( isblank )				/* need to propagate blanksignal */
 1978:   sp->type = blanksignal;		/* may not be completely safe??? */
 1979: /* --- extract raster from subraster --- */
 1980: rp = sp->image;				/* raster allocated in subraster */
 1981: /* -------------------------------------------------------------------------
 1982: overlay sp1 and sp2 in new composite raster
 1983: -------------------------------------------------------------------------- */
 1984: if ( msgfp!=NULL && msglevel>=9999 )
 1985:   { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
 1986:     fflush(msgfp); }			/* flush msgfp buffer */
 1987: if ( !isstring )
 1988:  rastput (rp, sp1->image, base-base1,	/* overlay left-hand */
 1989:  max2(0,nsmash-width1), 1);		/* plus any residual smash space */
 1990: else
 1991:  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
 1992: if ( msgfp!=NULL && msglevel>=9999 )
 1993:   { type_raster(sp->image,msgfp);	/* display composite raster */
 1994:     fflush(msgfp); }			/* flush msgfp buffer */
 1995: if ( !isstring )
 1996:  { int	fracbase = ( isfrac?		/* baseline for punc after \frac */
 1997: 	max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
 1998:    rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
 1999:    max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
 2000:    if ( 1 && type1 == FRACRASTER	/* we're done with \frac image */
 2001:    &&   type2 != FRACRASTER )		/* unless we have \frac\frac */
 2002:      fraccenterline = NOVALUE;		/* so reset centerline signal */
 2003:    if ( fraccenterline != NOVALUE )	/* sp2 is a fraction */
 2004:      fraccenterline += (base-base2); }	/* so adjust its centerline */
 2005: else
 2006:  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
 2007:    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
 2008: if ( msgfp!=NULL && msglevel>=9999 )
 2009:   { type_raster(sp->image,msgfp);	/* display composite raster */
 2010:     fflush(msgfp); }			/* flush msgfp buffer */
 2011: /* -------------------------------------------------------------------------
 2012: free input if requested
 2013: -------------------------------------------------------------------------- */
 2014: if ( isfree > 0 )			/* caller wants input freed */
 2015:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
 2016:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
 2017: /* -------------------------------------------------------------------------
 2018: Back to caller with pointer to concatted subraster or with null for error
 2019: -------------------------------------------------------------------------- */
 2020: end_of_job:
 2021:   smashmargin = oldsmashmargin;		/* reset original smashmargin */
 2022:   return ( sp );			/* back with subraster or null ptr */
 2023: } /* --- end-of-function rastcat() --- */
 2024: 
 2025: 
 2026: /* ==========================================================================
 2027:  * Function:	rastack ( sp1, sp2, base, space, iscenter, isfree )
 2028:  * Purpose:	Stack subrasters sp2 atop sp1, leaving both unchanged
 2029:  *		and returning a newly-allocated subraster,
 2030:  *		whose baseline is sp1's if base=1, or sp2's if base=2.
 2031:  *		Frees/deletes input sp1 and/or sp2 depending on value
 2032:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
 2033:  * --------------------------------------------------------------------------
 2034:  * Arguments:	sp1 (I)		subraster *  to lower subraster
 2035:  *		sp2 (I)		subraster *  to upper subraster
 2036:  *		base (I)	int containing 1 if sp1 is baseline,
 2037:  *				or 2 if sp2 is baseline.
 2038:  *		space (I)	int containing #rows blank space inserted
 2039:  *				between sp1's image and sp2's image.
 2040:  *		iscenter (I)	int containing 1 to center both sp1 and sp2
 2041:  *				in stacked array, 0 to left-justify both
 2042:  *		isfree (I)	int containing 1=free sp1 before return,
 2043:  *				2=free sp2, 3=free both, 0=free none.
 2044:  * --------------------------------------------------------------------------
 2045:  * Returns:	( subraster * )	pointer to constructed subraster sp2 atop sp1
 2046:  *				or  NULL for any error
 2047:  * --------------------------------------------------------------------------
 2048:  * Notes:
 2049:  * ======================================================================= */
 2050: /* --- entry point --- */
 2051: subraster *rastack ( subraster *sp1, subraster *sp2,
 2052: 			int base, int space, int iscenter, int isfree )
 2053: {
 2054: /* -------------------------------------------------------------------------
 2055: Allocations and Declarations
 2056: -------------------------------------------------------------------------- */
 2057: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
 2058: raster	*rp=(raster *)NULL;		/* new stacked raster in sp */
 2059: int	delete_subraster();		/* in case isfree non-zero */
 2060: int	rastput();			/* place sp1,sp2 in stacked raster */
 2061: int	base1   = sp1->baseline,	/* baseline for lower subraster */
 2062: 	height1 = (sp1->image)->height,	/* height for lower subraster */
 2063: 	width1  = (sp1->image)->width,	/* width for lower subraster */
 2064: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for lower subraster */
 2065: 	base2   = sp2->baseline,	/* baseline for upper subraster */
 2066: 	height2 = (sp2->image)->height,	/* height for upper subraster */
 2067: 	width2  = (sp2->image)->width,	/* width for upper subraster */
 2068: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for upper subraster */
 2069: int	height=0, width=0, pixsz=0, baseline=0;	/*for stacked sp2 atop sp1*/
 2070: mathchardef *symdef1 = sp1->symdef,	/* mathchardef of right lower char */
 2071: 	*symdef2 = sp2->symdef;		/* mathchardef of right upper char */
 2072: /* -------------------------------------------------------------------------
 2073: Initialization
 2074: -------------------------------------------------------------------------- */
 2075: /* --- determine height, width and baseline of composite raster --- */
 2076: height   = height1 + space + height2;	/* sum of heights plus space */
 2077: width    = max2(width1,width2);		/* max width is overall width */
 2078: pixsz    = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
 2079: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
 2080: /* -------------------------------------------------------------------------
 2081: allocate stacked composite subraster (with embedded raster)
 2082: -------------------------------------------------------------------------- */
 2083: /* --- allocate returned subraster (and then initialize it) --- */
 2084: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 2085: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
 2086: /* --- initialize subraster parameters --- */
 2087: sp->type = IMAGERASTER;			/* stacked rasters */
 2088: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
 2089: sp->baseline = baseline;		/* composite baseline */
 2090: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
 2091: /* --- extract raster from subraster --- */
 2092: rp = sp->image;				/* raster embedded in subraster */
 2093: /* -------------------------------------------------------------------------
 2094: overlay sp1 and sp2 in new composite raster
 2095: -------------------------------------------------------------------------- */
 2096: if ( iscenter == 1 )			/* center both sp1 and sp2 */
 2097:   { rastput (rp, sp2->image, 0, (width-width2)/2, 1);  /* overlay upper */
 2098:     rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
 2099: else					/* left-justify both sp1 and sp2 */
 2100:   { rastput (rp, sp2->image, 0, 0, 1);  /* overlay upper */
 2101:     rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
 2102: /* -------------------------------------------------------------------------
 2103: free input if requested
 2104: -------------------------------------------------------------------------- */
 2105: if ( isfree > 0 )			/* caller wants input freed */
 2106:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
 2107:     if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
 2108: /* -------------------------------------------------------------------------
 2109: Back to caller with pointer to stacked subraster or with null for error
 2110: -------------------------------------------------------------------------- */
 2111: end_of_job:
 2112:   return ( sp );			/* back with subraster or null ptr */
 2113: } /* --- end-of-function rastack() --- */
 2114: 
 2115: 
 2116: /* ==========================================================================
 2117:  * Function:	rastile ( tiles, ntiles )
 2118:  * Purpose:	Allocate and build up a composite raster
 2119:  *		from the ntiles components/characters supplied in tiles.
 2120:  * --------------------------------------------------------------------------
 2121:  * Arguments:	tiles (I)	subraster *  to array of subraster structs
 2122:  *				describing the components and their locations
 2123:  *		ntiles (I)	int containing number of subrasters in tiles[]
 2124:  * --------------------------------------------------------------------------
 2125:  * Returns:	( raster * )	ptr to composite raster,
 2126:  *				or NULL for any error.
 2127:  * --------------------------------------------------------------------------
 2128:  * Notes:     o	The top,left corner of a raster is row=0,col=0
 2129:  *		with row# increasing as you move down,
 2130:  *		and col# increasing as you move right.
 2131:  *		Metafont numbers rows with the baseline=0,
 2132:  *		so the top row is a positive number that
 2133:  *		decreases as you move down.
 2134:  *	      o	rastile() is no longer used.
 2135:  *		It was used by an earlier rasterize() algorithm,
 2136:  *		and I've left it in place should it be needed again.
 2137:  *		But recent changes haven't been tested/exercised.
 2138:  * ======================================================================= */
 2139: /* --- entry point --- */
 2140: raster	*rastile ( subraster *tiles, int ntiles )
 2141: {
 2142: /* -------------------------------------------------------------------------
 2143: Allocations and Declarations
 2144: -------------------------------------------------------------------------- */
 2145: raster	*new_raster(), *composite=(raster *)NULL;  /*raster back to caller*/
 2146: int	width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
 2147: 	toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
 2148: 	botrow=-999, leftcol=9999;  /* extreme lower-left corner of tiles */
 2149: int	itile;			/* tiles[] index */
 2150: int	rastput();		/* overlay each tile in composite raster */
 2151: /* -------------------------------------------------------------------------
 2152: run through tiles[] to determine dimensions for composite raster
 2153: -------------------------------------------------------------------------- */
 2154: /* --- determine row and column bounds of composite raster --- */
 2155: for ( itile=0; itile<ntiles; itile++ )
 2156:   {
 2157:   subraster *tile = &(tiles[itile]);		/* ptr to current tile */
 2158:   /* --- upper-left corner of composite --- */
 2159:   toprow = min2(toprow, tile->toprow);
 2160:   leftcol = min2(leftcol, tile->leftcol);
 2161:   /* --- lower-right corner of composite --- */
 2162:   botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
 2163:   rightcol = max2(rightcol, tile->leftcol + (tile->image)->width  - 1);
 2164:   /* --- pixsz of composite --- */
 2165:   pixsz = max2(pixsz,(tile->image)->pixsz);
 2166:   } /* --- end-of-for(itile) --- */
 2167: /* --- calculate width and height from bounds --- */
 2168: width  = rightcol - leftcol + 1;
 2169: height = botrow - toprow + 1;
 2170: /* --- sanity check (quit if bad dimensions) --- */
 2171: if ( width<1 || height<1 ) goto end_of_job;
 2172: /* -------------------------------------------------------------------------
 2173: allocate composite raster, and embed tiles[] within it
 2174: -------------------------------------------------------------------------- */
 2175: /* --- allocate composite raster --- */
 2176: if ( (composite=new_raster(width,height,pixsz))	/*allocate composite raster*/
 2177: ==   (raster *)NULL ) goto end_of_job;		/* and quit if failed */
 2178: /* --- embed tiles[] in composite --- */
 2179: for ( itile=0; itile<ntiles; itile++ )
 2180:   { subraster *tile = &(tiles[itile]);		/* ptr to current tile */
 2181:     rastput (composite, tile->image,		/* overlay tile image at...*/
 2182:       tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
 2183: /* -------------------------------------------------------------------------
 2184: Back to caller with composite raster (or null for any error)
 2185: -------------------------------------------------------------------------- */
 2186: end_of_job:
 2187:   return ( composite );			/* back with composite or null ptr */
 2188: } /* --- end-of-function rastile() --- */
 2189: 
 2190: 
 2191: /* ==========================================================================
 2192:  * Function:	rastsmash ( sp1, sp2 )
 2193:  * Purpose:	When concatanating sp1||sp2, calculate #pixels
 2194:  *		we can "smash sp2 left"
 2195:  * --------------------------------------------------------------------------
 2196:  * Arguments:	sp1 (I)		subraster *  to left-hand raster
 2197:  *		sp2 (I)		subraster *  to right-hand raster
 2198:  * --------------------------------------------------------------------------
 2199:  * Returns:	( int )		max #pixels we can smash sp1||sp2,
 2200:  *				or "blanksignal" if sp2 intentionally blank,
 2201:  *				or 0 for any error.
 2202:  * --------------------------------------------------------------------------
 2203:  * Notes:     o
 2204:  * ======================================================================= */
 2205: /* --- entry point --- */
 2206: int	rastsmash ( subraster *sp1, subraster *sp2 )
 2207: {
 2208: /* -------------------------------------------------------------------------
 2209: Allocations and Declarations
 2210: -------------------------------------------------------------------------- */
 2211: int	nsmash = 0;			/* #pixels to smash sp1||sp2 */
 2212: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
 2213: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
 2214: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
 2215: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
 2216: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
 2217: 	width2  = (sp2->image)->width;	/* width for right-hand subraster */
 2218: int	base = max2(base1,base2),	/* max ascenders - 1 above baseline*/
 2219: 	top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
 2220: 	bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
 2221: 	height = max2(bot1,bot2)+1;	/* total height */
 2222: int	irow1=0,irow2=0, icol=0;	/* row,col indexes */
 2223: int	firstcol1[1025], nfirst1=0,	/* 1st sp1 col containing set pixel*/
 2224: 	firstcol2[1025], nfirst2=0;	/* 1st sp2 col containing set pixel*/
 2225: int	smin=9999, xmin=9999,ymin=9999;	/* min separation (s=x+y) */
 2226: int	type_raster();			/* display debugging output */
 2227: /* -------------------------------------------------------------------------
 2228: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
 2229: -------------------------------------------------------------------------- */
 2230: /* --- check args --- */
 2231: if ( isstring ) goto end_of_job;	/* ignore string rasters */
 2232: if ( 0 && istextmode ) goto end_of_job;	/* don't smash in text mode */
 2233: if ( height > 1023 ) goto end_of_job;	/* don't try to smash huge image */
 2234: if ( sp2->type == blanksignal )		/*blanksignal was propagated to us*/
 2235:   goto end_of_job;			/* don't smash intentional blank */
 2236: /* --- init firstcol1[], firstcol2[] --- */
 2237: for ( irow1=0; irow1<height; irow1++ )	/* for each row */
 2238:   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
 2239: /* --- set firstcol2[] indicating left edge of sp2 --- */
 2240: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
 2241:   for ( icol=0; icol<width2; icol++ )	/* find first non-empty col in row */
 2242:     if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
 2243:       {	firstcol2[irow2] = icol;	/* icol is #cols from left edge */
 2244: 	nfirst2++;			/* bump #rows containing set pixels*/
 2245: 	break; }			/* and go on to next row */
 2246: if ( nfirst2 < 1 )			/*right-hand sp2 is completely blank*/
 2247:   { nsmash = blanksignal;		/* signal intentional blanks */
 2248:     goto end_of_job; }			/* don't smash intentional blanks */
 2249: /* --- now check if preceding image in sp1 was an intentional blank --- */
 2250: if ( sp1->type == blanksignal )		/*blanksignal was propagated to us*/
 2251:   goto end_of_job;			/* don't smash intentional blank */
 2252: /* --- set firstcol1[] indicating right edge of sp1 --- */
 2253: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
 2254:   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
 2255:     if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
 2256:       {	firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
 2257: 	nfirst1++;			/* bump #rows containing set pixels*/
 2258: 	break; }			/* and go on to next row */
 2259: if ( nfirst1 < 1 )			/*left-hand sp1 is completely blank*/
 2260:   goto end_of_job;			/* don't smash intentional blanks */
 2261: /* -------------------------------------------------------------------------
 2262: find minimum separation
 2263: -------------------------------------------------------------------------- */
 2264: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
 2265:  int margin1, margin2=firstcol2[irow2];	/* #cols to first set pixel */
 2266:  if ( margin2 != blanksignal ) {	/* irow2 not an empty/blank row */
 2267:   for ( irow1=max2(irow2-smin,top1); ; irow1++ )
 2268:    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
 2269:    else
 2270:     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
 2271:      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
 2272:      if ( ds >= smin ) continue;	/* min unchanged */
 2273:      if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
 2274:      smin=ds; xmin=dx; ymin=dy;		/* set new min */
 2275:      } /* --- end-of-if(margin1!=blanksignal) --- */
 2276:   } /* --- end-of-if(margin2!=blanksignal) --- */
 2277:  if ( smin<2 ) goto end_of_job;		/* can't smash */
 2278:  } /* --- end-of-for(irow2) --- */
 2279: /*nsmash = min2(xmin,width2);*/		/* permissible smash */
 2280: nsmash = xmin;				/* permissible smash */
 2281: /* -------------------------------------------------------------------------
 2282: Back to caller with #pixels to smash sp1||sp2
 2283: -------------------------------------------------------------------------- */
 2284: end_of_job:
 2285:   /* --- debugging output --- */
 2286:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
 2287:     { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
 2288:       nsmash,smashmargin);
 2289:       if ( msglevel >= 999 )		/* also display rasters */
 2290: 	{ fprintf(msgfp,"rastsmash>left-hand image...\n");
 2291: 	  if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
 2292: 	  fprintf(msgfp,"rastsmash>right-hand image...\n");
 2293: 	  if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
 2294:       fflush(msgfp); }
 2295:   return ( nsmash );			/* back with #smash pixels */
 2296: } /* --- end-of-function rastsmash() --- */
 2297: 
 2298: 
 2299: /* ==========================================================================
 2300:  * Function:	rastsmashcheck ( term )
 2301:  * Purpose:	Check an exponent term to see if its leading symbol
 2302:  *		would make smashing dangerous
 2303:  * --------------------------------------------------------------------------
 2304:  * Arguments:	term (I)	char *  to null-terminated string
 2305:  *				containing right-hand exponent term about to
 2306:  *				be smashed against existing left-hand.
 2307:  * --------------------------------------------------------------------------
 2308:  * Returns:	( int )		1 if it's okay to smash term, or
 2309:  *				0 if smash is dangerous.
 2310:  * --------------------------------------------------------------------------
 2311:  * Notes:     o
 2312:  * ======================================================================= */
 2313: /* --- entry point --- */
 2314: int	rastsmashcheck ( char *term )
 2315: {
 2316: /* -------------------------------------------------------------------------
 2317: Allocations and Declarations
 2318: -------------------------------------------------------------------------- */
 2319: int	isokay = 0;		/* 1 to signal okay to caller */
 2320: static	char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
 2321: static	char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
 2322: static	char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
 2323: 	"\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
 2324: char	*expression = term;	/* local ptr to beginning of expression */
 2325: char	*token = NULL;  int i;	/* token = nosmashstrs[i] or grayspace[i] */
 2326: /* -------------------------------------------------------------------------
 2327: see if smash check enabled
 2328: -------------------------------------------------------------------------- */
 2329: if ( smashcheck < 1 ) {		/* no smash checking wanted */
 2330:   if ( smashcheck >= 0 )	/* -1 means check should always fail */
 2331:     isokay = 1;			/* otherwise (if 0), signal okay to smash */
 2332:   goto end_of_job; }		/* return to caller */
 2333: /* -------------------------------------------------------------------------
 2334: skip leading white and gray space
 2335: -------------------------------------------------------------------------- */
 2336: /* --- first check input --- */
 2337: if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
 2338: /* --- skip leading white space --- */
 2339: skipwhite(term);		/* skip leading white space */
 2340: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
 2341: /* --- skip leading gray space --- */
 2342: skipgray:
 2343:  for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
 2344:   if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
 2345:    term += strlen(token);	/* skip past this grayspace token */
 2346:    skipwhite(term);		/* and skip any subsequent white space */
 2347:    if ( *term == '\000' ) {	/* nothing left so quit */
 2348:      if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
 2349:        fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
 2350:      goto end_of_job; }
 2351:    goto skipgray; }		/* restart grayspace check from beginning */
 2352: /* -------------------------------------------------------------------------
 2353: check for leading no-smash single char
 2354: -------------------------------------------------------------------------- */
 2355: /* --- don't smash if term begins with a "nosmash" char --- */
 2356: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
 2357:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
 2358:     fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
 2359:   goto end_of_job; }
 2360: /* -------------------------------------------------------------------------
 2361: check for leading no-smash token
 2362: -------------------------------------------------------------------------- */
 2363: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
 2364:  if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
 2365:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
 2366:     fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
 2367:   goto end_of_job; }		/* so don't smash term */
 2368: /* -------------------------------------------------------------------------
 2369: back to caller
 2370: -------------------------------------------------------------------------- */
 2371: isokay = 1;			/* no problem, so signal okay to smash */
 2372: end_of_job:
 2373:   if ( msgfp!=NULL && msglevel >= 999 )	/* display for debugging */
 2374:     fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
 2375:     isokay,(expression==NULL?"<no input>":expression));
 2376:   return ( isokay );		/* back to caller with 1 if okay to smash */
 2377: } /* --- end-of-function rastsmashcheck() --- */
 2378: 
 2379: 
 2380: /* ==========================================================================
 2381:  * Function:	accent_subraster ( accent, width, height, direction, pixsz )
 2382:  * Purpose:	Allocate a new subraster of width x height
 2383:  *		(or maybe different dimensions, depending on accent),
 2384:  *		and draw an accent (\hat or \vec or \etc) that fills it
 2385:  * --------------------------------------------------------------------------
 2386:  * Arguments:	accent (I)	int containing either HATACCENT or VECACCENT,
 2387:  *				etc, indicating the type of accent desired
 2388:  *		width (I)	int containing desired width of accent (#cols)
 2389:  *		height (I)	int containing desired height of accent(#rows)
 2390:  *		direction (I)	int containing desired direction of accent,
 2391:  *				+1=right, -1=left, 0=left/right
 2392:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
 2393:  * --------------------------------------------------------------------------
 2394:  * Returns:	( subraster * )	ptr to newly-allocated subraster with accent,
 2395:  *				or NULL for any error.
 2396:  * --------------------------------------------------------------------------
 2397:  * Notes:     o	Some accents have internally-determined dimensions,
 2398:  *		and caller should check dimensions in returned subraster
 2399:  * ======================================================================= */
 2400: /* --- entry point --- */
 2401: subraster *accent_subraster (  int accent, int width, int height,
 2402: int direction, int pixsz )
 2403: {
 2404: /* -------------------------------------------------------------------------
 2405: Allocations and Declarations
 2406: -------------------------------------------------------------------------- */
 2407: /* --- general info --- */
 2408: raster	*new_raster(), *rp=NULL;	/*raster containing desired accent*/
 2409: subraster *new_subraster(), *sp=NULL;	/* subraster returning accent */
 2410: int	delete_raster(), delete_subraster(); /*free allocated raster on err*/
 2411: int	line_raster(),			/* draws lines */
 2412: 	rule_raster(),			/* draw solid boxes */
 2413: 	thickness = 1;			/* line thickness */
 2414: /*int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
 2415: /* --- other working info --- */
 2416: int	col0, col1,			/* cols for line */
 2417: 	row0, row1;			/* rows for line */
 2418: subraster *get_delim(), *accsp=NULL;	/*find suitable cmex10 symbol/accent*/
 2419: /* --- info for under/overbraces, tildes, etc --- */
 2420: char	brace[16];			/*"{" for over, "}" for under, etc*/
 2421: raster	*rastrot(),			/* rotate { for overbrace, etc */
 2422: 	*rastcpy();			/* may need copy of original */
 2423: subraster *arrow_subraster();		/* rightarrow for vec */
 2424: subraster *rastack();			/* stack accent atop extra space */
 2425: int	iswidthneg = 0;			/* set true if width<0 arg passed */
 2426: int	serifwidth=0;			/* serif for surd */
 2427: int	isBig=0;			/* true for ==>arrow, false for -->*/
 2428: /* -------------------------------------------------------------------------
 2429: initialization
 2430: -------------------------------------------------------------------------- */
 2431: if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
 2432: /* -------------------------------------------------------------------------
 2433: outer switch() traps accents that may change caller's height,width
 2434: -------------------------------------------------------------------------- */
 2435: switch ( accent )
 2436:  {
 2437:  default:
 2438:   /* -----------------------------------------------------------------------
 2439:   inner switch() first allocates fixed-size raster for accents that don't
 2440:   ------------------------------------------------------------------------ */
 2441:   if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
 2442:   !=   NULL )				/* and if we succeeded... */
 2443:    switch ( accent )			/* ...draw requested accent in it */
 2444:     {
 2445:     /* --- unrecognized request --- */
 2446:     default: delete_raster(rp);		/* unrecognized accent requested */
 2447: 	rp = NULL;  break;		/* so free raster and signal error */
 2448:     /* --- bar request --- */
 2449:     case UNDERBARACCENT:
 2450:     case BARACCENT:
 2451: 	thickness = 1; /*height-1;*/	/* adjust thickness */
 2452: 	if ( accent == BARACCENT )	/* bar is above expression */
 2453: 	 { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
 2454: 	   line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
 2455: 	else				/* underbar is below expression */
 2456: 	 { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
 2457: 	   line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
 2458: 	break;
 2459:     /* --- dot request --- */
 2460:     case DOTACCENT:
 2461: 	thickness = height-1;		/* adjust thickness */
 2462: 	/*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
 2463: 	rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
 2464: 	break;
 2465:     /* --- ddot request --- */
 2466:     case DDOTACCENT:
 2467: 	thickness = height-1;		/* adjust thickness */
 2468: 	col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
 2469: 	col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
 2470: 	if ( col0+thickness >= col1 )	/* dots overlap */
 2471: 	  { col0 = max2(col0-1,0);	/* try moving left dot more left */
 2472: 	    col1 = min2(col1+1,width-thickness); } /* and right dot right */
 2473: 	if ( col0+thickness >= col1 )	/* dots _still_ overlap */
 2474: 	  thickness = max2(thickness-1,1); /* so try reducing thickness */
 2475: 	/*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
 2476: 	/*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
 2477: 	rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
 2478: 	rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
 2479: 	break;
 2480:     /* --- hat request --- */
 2481:     case HATACCENT:
 2482: 	thickness = 1; /*(width<=12? 2 : 3);*/	/* adjust thickness */
 2483: 	line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
 2484: 	line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
 2485: 	break;
 2486:     /* --- sqrt request --- */
 2487:     case SQRTACCENT:
 2488: 	serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
 2489: 	col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
 2490: 	/*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
 2491: 	col0 = (col1-serifwidth+1)/2;	/* midpoint col of sqrt */
 2492: 	row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
 2493: 	row1 = height-1;		/* bottom row of sqrt */
 2494: 	/*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
 2495: 	line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
 2496: 	line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
 2497: 	line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
 2498: 	line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
 2499: 	break;
 2500:     } /* --- end-of-inner-switch(accent) --- */
 2501:     break;				/* break from outer accent switch */
 2502:  /* --- underbrace, overbrace request --- */
 2503:  case UNDERBRACE:
 2504:  case OVERBRACE:
 2505:     if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
 2506:     if ( accent ==  OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
 2507:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
 2508:     !=  NULL )				/* found desired brace */
 2509:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
 2510: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
 2511:     break;
 2512:  /* --- hat request --- */
 2513:  case HATACCENT:
 2514:     if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
 2515:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
 2516:     !=  NULL )				/* found desired brace */
 2517:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
 2518: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
 2519:     break;
 2520:  /* --- vec request --- */
 2521:  case VECACCENT:
 2522:     height = 2*(height/2) + 1;		/* force height odd */
 2523:     if ( absval(direction) >= 9 ) {	/* want ==> arrow rather than --> */
 2524:       isBig = 1;			/* signal "Big" arrow */
 2525:       direction -= 10; }		/* reset direction = +1, -1, or 0 */
 2526:     if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
 2527:     !=  NULL )				/* succeeded */
 2528: 	{ rp = accsp->image;		/* "extract" raster with bitmap */
 2529: 	  free((void *)accsp); }	/* and free subraster "envelope" */
 2530:     break;
 2531:  /* --- tilde request --- */
 2532:  case TILDEACCENT:
 2533:     accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
 2534: 		     get_delim("~",-width,CMEX10)); /*width search for tilde*/
 2535:     if ( accsp !=  NULL )		/* found desired tilde */
 2536:       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
 2537:       !=  NULL )			/* have tilde with space below it */
 2538: 	{ rp = sp->image;		/* "extract" raster with bitmap */
 2539: 	  free((void *)sp);		/* and free subraster "envelope" */
 2540: 	  leftsymdef = NULL; }		/* so \tilde{x}^2 works properly */
 2541:     break;
 2542:  } /* --- end-of-outer-switch(accent) --- */
 2543: /* -------------------------------------------------------------------------
 2544: if we constructed accent raster okay, embed it in a subraster and return it
 2545: -------------------------------------------------------------------------- */
 2546: /* --- if all okay, allocate subraster to contain constructed raster --- */
 2547: if ( rp != NULL ) {			/* accent raster constructed okay */
 2548:   if ( (sp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
 2549:   ==   NULL )				/* and if we fail to allocate */
 2550:     delete_raster(rp);			/* free now-unneeded raster */
 2551:   else					/* subraster allocated okay */
 2552:     { /* --- init subraster parameters, embedding raster in it --- */
 2553:       sp->type = IMAGERASTER;		/* constructed image */
 2554:       sp->image = rp;			/* raster we just constructed */
 2555:       sp->size = (-1);			/* can't set font size here */
 2556:       sp->baseline = 0; }		/* can't set baseline here */
 2557:   } /* --- end-of-if(rp!=NULL) --- */
 2558: /* --- return subraster containing desired accent to caller --- */
 2559: return ( sp );				/* return accent or NULL to caller */
 2560: } /* --- end-of-function accent_subraster() --- */
 2561: 
 2562: 
 2563: /* ==========================================================================
 2564:  * Function:	arrow_subraster ( width, height, pixsz, drctn, isBig )
 2565:  * Purpose:	Allocate a raster/subraster and draw left/right arrow in it
 2566:  * --------------------------------------------------------------------------
 2567:  * Arguments:	width (I)	int containing number of cols for arrow
 2568:  *		height (I)	int containing number of rows for arrow
 2569:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
 2570:  *		drctn (I)	int containing +1 for right arrow,
 2571:  *				or -1 for left, 0 for leftright
 2572:  *		isBig (I)	int containing 1/true for \Long arrows,
 2573:  *				or false for \long arrows, i.e.,
 2574:  *				true for ===> or false for --->.
 2575:  * --------------------------------------------------------------------------
 2576:  * Returns:	( subraster * )	ptr to constructed left/right arrow
 2577:  *				or NULL for any error.
 2578:  * --------------------------------------------------------------------------
 2579:  * Notes:     o
 2580:  * ======================================================================= */
 2581: /* --- entry point --- */
 2582: subraster *arrow_subraster ( int width, int height, int pixsz,
 2583: 				int drctn, int isBig )
 2584: {
 2585: /* -------------------------------------------------------------------------
 2586: Allocations and Declarations
 2587: -------------------------------------------------------------------------- */
 2588: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 2589: int	rule_raster();			/* draw arrow line */
 2590: int	irow, midrow=height/2;		/* index, midrow is arrowhead apex */
 2591: int	icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
 2592: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 2593: int	ipix,				/* raster pixmap[] index */
 2594: 	npix = width*height;		/* #pixels malloced in pixmap[] */
 2595: /* -------------------------------------------------------------------------
 2596: allocate raster/subraster and draw arrow line
 2597: -------------------------------------------------------------------------- */
 2598: if ( height < 3 ) { height=3; midrow=1; }	/* set minimum height */
 2599: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
 2600: ==   NULL ) goto end_of_job;			/* and quit if failed */
 2601: if ( !isBig )					/* single line */
 2602:   rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
 2603: else
 2604:   { int	delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
 2605:     rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
 2606:     rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
 2607: /* -------------------------------------------------------------------------
 2608: construct arrowhead(s)
 2609: -------------------------------------------------------------------------- */
 2610: for ( irow=0; irow<height; irow++ )		/* for each row of arrow */
 2611:   {
 2612:   int	delta = abs(irow-midrow);		/*arrowhead offset for irow*/
 2613:   /* --- right arrowhead --- */
 2614:   if ( drctn >= 0 )				/* right arrowhead wanted */
 2615:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
 2616:      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
 2617:        if ( ipix >= 0 ) {				/* bounds check */
 2618: 	if ( pixsz == 1 )			/* have a bitmap */
 2619: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
 2620: 	else					/* should have a bytemap */
 2621: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
 2622: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
 2623:   /* --- left arrowhead (same as right except for ipix calculation) --- */
 2624:   if ( drctn <= 0 )				/* left arrowhead wanted */
 2625:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
 2626:      { ipix = irow*width + delta + icol;	/* leftmost bit+delta+icol */
 2627:        if ( ipix < npix ) {			/* bounds check */
 2628: 	if ( pixsz == 1 )			/* have a bitmap */
 2629: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
 2630: 	else					/* should have a bytemap */
 2631: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
 2632: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
 2633:   } /* --- end-of-for(irow) --- */
 2634: end_of_job:
 2635:   return ( arrowsp );			/*back to caller with arrow or NULL*/
 2636: } /* --- end-of-function arrow_subraster() --- */
 2637: 
 2638: 
 2639: /* ==========================================================================
 2640:  * Function:	uparrow_subraster ( width, height, pixsz, drctn, isBig )
 2641:  * Purpose:	Allocate a raster/subraster and draw up/down arrow in it
 2642:  * --------------------------------------------------------------------------
 2643:  * Arguments:	width (I)	int containing number of cols for arrow
 2644:  *		height (I)	int containing number of rows for arrow
 2645:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
 2646:  *		drctn (I)	int containing +1 for up arrow,
 2647:  *				or -1 for down, or 0 for updown
 2648:  *		isBig (I)	int containing 1/true for \Long arrows,
 2649:  *				or false for \long arrows, i.e.,
 2650:  *				true for ===> or false for --->.
 2651:  * --------------------------------------------------------------------------
 2652:  * Returns:	( subraster * )	ptr to constructed up/down arrow
 2653:  *				or NULL for any error.
 2654:  * --------------------------------------------------------------------------
 2655:  * Notes:     o
 2656:  * ======================================================================= */
 2657: /* --- entry point --- */
 2658: subraster *uparrow_subraster ( int width, int height, int pixsz,
 2659: 					int drctn, int isBig )
 2660: {
 2661: /* -------------------------------------------------------------------------
 2662: Allocations and Declarations
 2663: -------------------------------------------------------------------------- */
 2664: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 2665: int	rule_raster();			/* draw arrow line */
 2666: int	icol, midcol=width/2;		/* index, midcol is arrowhead apex */
 2667: int	irow, thickness=(width>15?2:2);	/* arrowhead thickness and index */
 2668: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 2669: int	ipix,				/* raster pixmap[] index */
 2670: 	npix = width*height;		/* #pixels malloced in pixmap[] */
 2671: /* -------------------------------------------------------------------------
 2672: allocate raster/subraster and draw arrow line
 2673: -------------------------------------------------------------------------- */
 2674: if ( width < 3 ) { width=3; midcol=1; }		/* set minimum width */
 2675: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
 2676: ==   NULL ) goto end_of_job;			/* and quit if failed */
 2677: if ( !isBig )					/* single line */
 2678:   rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
 2679: else
 2680:   { int	delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
 2681:     rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
 2682:     rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
 2683: /* -------------------------------------------------------------------------
 2684: construct arrowhead(s)
 2685: -------------------------------------------------------------------------- */
 2686: for ( icol=0; icol<width; icol++ )		/* for each col of arrow */
 2687:   {
 2688:   int	delta = abs(icol-midcol);		/*arrowhead offset for icol*/
 2689:   /* --- up arrowhead --- */
 2690:   if ( drctn >= 0 )				/* up arrowhead wanted */
 2691:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
 2692:      { ipix = (irow+delta)*width + icol;	/* leftmost+icol */
 2693:        if ( ipix < npix ) {			/* bounds check */
 2694: 	if ( pixsz == 1 )			/* have a bitmap */
 2695: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
 2696: 	else					/* should have a bytemap */
 2697: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
 2698: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
 2699:   /* --- down arrowhead (same as up except for ipix calculation) --- */
 2700:   if ( drctn <= 0 )				/* down arrowhead wanted */
 2701:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
 2702:      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
 2703:        if ( ipix > 0 ) {			/* bounds check */
 2704: 	if ( pixsz == 1 )			/* have a bitmap */
 2705: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
 2706: 	else					/* should have a bytemap */
 2707: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
 2708: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
 2709:   } /* --- end-of-for(icol) --- */
 2710: end_of_job:
 2711:   return ( arrowsp );			/*back to caller with arrow or NULL*/
 2712: } /* --- end-of-function uparrow_subraster() --- */
 2713: 
 2714: 
 2715: /* ==========================================================================
 2716:  * Function:	rule_raster ( rp, top, left, width, height, type )
 2717:  * Purpose:	Draw a solid or dashed line (or box) in existing raster rp,
 2718:  *		starting at top,left with dimensions width,height.
 2719:  * --------------------------------------------------------------------------
 2720:  * Arguments:	rp (I)		raster *  to raster in which rule
 2721:  *				will be drawn
 2722:  *		top (I)		int containing row at which top-left corner
 2723:  *				of rule starts (0 is topmost)
 2724:  *		left (I)	int containing col at which top-left corner
 2725:  *				of rule starts (0 is leftmost)
 2726:  *		width (I)	int containing number of cols for rule
 2727:  *		height (I)	int containing number of rows for rule
 2728:  *		type (I)	int containing 0 for solid rule,
 2729:  *				1 for horizontal dashes, 2 for vertical
 2730:  *				3 for solid rule with corners removed (bevel)
 2731:  *				4 for strut (nothing drawn)
 2732:  * --------------------------------------------------------------------------
 2733:  * Returns:	( int )		1 if rule drawn okay,
 2734:  *				or 0 for any error.
 2735:  * --------------------------------------------------------------------------
 2736:  * Notes:     o	Rule line is implicitly "horizontal" or "vertical" depending
 2737:  *		on relative width,height dimensions.  It's a box if they're
 2738:  *		more or less comparable.
 2739:  * ======================================================================= */
 2740: /* --- entry point --- */
 2741: int	rule_raster ( raster *rp, int top, int left,
 2742: 		int width, int height, int type )
 2743: {
 2744: /* -------------------------------------------------------------------------
 2745: Allocations and Declarations
 2746: -------------------------------------------------------------------------- */
 2747: int	irow=0, icol=0;		/* indexes over rp raster */
 2748: int	ipix = 0,		/* raster pixmap[] index */
 2749: 	npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
 2750: int	isfatal = 0;		/* true to abend on out-of-bounds error */
 2751: int	hdash=1, vdash=2,	/* type for horizontal, vertical dashes */
 2752: 	bevel=99/*3*/, strut=4;	/* type for bevel (turned off), strut */
 2753: int	dashlen=3, spacelen=2,	/* #pixels for dash followed by space */
 2754: 	isdraw=1;		/* true when drawing dash (init for solid) */
 2755: /* -------------------------------------------------------------------------
 2756: Check args
 2757: -------------------------------------------------------------------------- */
 2758: if ( rp == (raster *)NULL ) {	/* no raster arg supplied */
 2759:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
 2760:     rp = workingbox->image;	/* use workingbox if possible */
 2761:   else return ( 0 ); }		/* otherwise signal error to caller */
 2762: if ( type == bevel )		/* remove corners of solid box */
 2763:   if ( width<3 || height<3 ) type=0; /* too small to remove corners */
 2764: /* -------------------------------------------------------------------------
 2765: Fill line/box
 2766: -------------------------------------------------------------------------- */
 2767: if ( width > 0 )				/* zero width implies strut*/
 2768:  for ( irow=top; irow<top+height; irow++ )	/* for each scan line */
 2769:   {
 2770:   if ( type == strut ) isdraw = 0;		/* draw nothing for strut */
 2771:   if ( type == vdash )				/*set isdraw for vert dash*/
 2772:     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
 2773:   ipix = irow*rp->width + left - 1;		/*first pixel preceding icol*/
 2774:   for ( icol=left; icol<left+width; icol++ )	/* each pixel in scan line */
 2775:     {
 2776:     if ( type == bevel ) {			/* remove corners of box */
 2777:       if ( (irow==top && icol==left)		/* top-left corner */
 2778:       ||   (irow==top && icol>=left+width-1)	/* top-right corner */
 2779:       ||   (irow>=top+height-1 && icol==left)	/* bottom-left corner */
 2780:       ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
 2781: 	isdraw = 0;  else isdraw = 1; }		/*set isdraw to skip corner*/
 2782:     if ( type == hdash )			/*set isdraw for horiz dash*/
 2783:       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
 2784:     if ( ++ipix >= npix )			/* bounds check failed */
 2785:          if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
 2786:          else break;				/*or just go on to next row*/
 2787:     else					/*ibit is within rp bounds*/
 2788:       if ( isdraw ) {				/*and we're drawing this bit*/
 2789: 	if ( rp->pixsz == 1 )			/* have a bitmap */
 2790: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
 2791: 	else					/* should have a bytemap */
 2792: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
 2793: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
 2794:     } /* --- end-of-for(icol) --- */
 2795:   } /* --- end-of-for(irow) --- */
 2796: end_of_job:
 2797:   return ( isfatal? (ipix<npix? 1:0) : 1 );
 2798: } /* --- end-of-function rule_raster() --- */
 2799: 
 2800: 
 2801: /* ==========================================================================
 2802:  * Function:	line_raster ( rp,  row0, col0,  row1, col1,  thickness )
 2803:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
 2804:  *		in existing raster rp.
 2805:  * --------------------------------------------------------------------------
 2806:  * Arguments:	rp (I)		raster *  to raster in which a line
 2807:  *				will be drawn
 2808:  *		row0 (I)	int containing row at which
 2809:  *				line will start (0 is topmost)
 2810:  *		col0 (I)	int containing col at which
 2811:  *				line will start (0 is leftmost)
 2812:  *		row1 (I)	int containing row at which
 2813:  *				line will end (rp->height-1 is bottom-most)
 2814:  *		col1 (I)	int containing col at which
 2815:  *				line will end (rp->width-1 is rightmost)
 2816:  *		thickness (I)	int containing number of pixels/bits
 2817:  *				thick the line will be
 2818:  * --------------------------------------------------------------------------
 2819:  * Returns:	( int )		1 if line drawn okay,
 2820:  *				or 0 for any error.
 2821:  * --------------------------------------------------------------------------
 2822:  * Notes:     o	if row0==row1, a horizontal line is drawn
 2823:  *		between col0 and col1, with row0(==row1) the top row
 2824:  *		and row0+(thickness-1) the bottom row
 2825:  *	      o	if col0==col1, a vertical bar is drawn
 2826:  *		between row0 and row1, with col0(==col1) the left col
 2827:  *		and col0+(thickness-1) the right col
 2828:  *	      o	if both the above, you get a square thickness x thickness
 2829:  *		whose top-left corner is row0,col0.
 2830:  * ======================================================================= */
 2831: /* --- entry point --- */
 2832: int	line_raster ( raster *rp, int row0, int col0,
 2833: 	int row1, int col1, int thickness )
 2834: {
 2835: /* -------------------------------------------------------------------------
 2836: Allocations and Declarations
 2837: -------------------------------------------------------------------------- */
 2838: int	irow=0, icol=0,		/* indexes over rp raster */
 2839: 	locol=col0, hicol=col1,	/* col limits at irow */
 2840: 	lorow=row0, hirow=row1;	/* row limits at icol */
 2841: int	width=rp->width, height=rp->height; /* dimensions of input raster */
 2842: int	ipix = 0,		/* raster pixmap[] index */
 2843: 	npix = width*height;	/* #pixels malloced in rp->pixmap[] */
 2844: int	isfatal = 0;		/* true to abend on out-of-bounds error */
 2845: int	isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
 2846: double	dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
 2847: 	dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
 2848: 	a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
 2849: 	xcol=0, xrow=0;		/* calculated col at irow, or row at icol */
 2850: double	ar = ASPECTRATIO,	/* aspect ratio width/height of one pixel */
 2851: 	xwidth= (isline? 0.0 :	/*#pixels per row to get sloped line thcknss*/
 2852: 		((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
 2853: 	xheight = 1.0;
 2854: int	line_recurse(), isrecurse=1; /* true to draw line recursively */
 2855: /* -------------------------------------------------------------------------
 2856: Check args
 2857: -------------------------------------------------------------------------- */
 2858: if ( rp == (raster *)NULL ) {	/* no raster arg supplied */
 2859:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
 2860:     rp = workingbox->image;	/* use workingbox if possible */
 2861:   else return ( 0 ); }		/* otherwise signal error to caller */
 2862: /* -------------------------------------------------------------------------
 2863: Initialization
 2864: -------------------------------------------------------------------------- */
 2865: if ( msgfp!=NULL && msglevel>=29 ) {		/* debugging */
 2866:    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
 2867:    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
 2868:    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth); fflush(msgfp); }
 2869: /* --- check for recursive line drawing --- */
 2870: if ( isrecurse ) {		/* drawing lines recursively */
 2871:  for ( irow=0; irow<thickness; irow++ )		/* each line 1 pixel thick */
 2872:   { double xrow0=(double)row0, xcol0=(double)col0,
 2873: 	xrow1=(double)row1, xcol1=(double)col1;
 2874:     if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
 2875:     else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
 2876:     if( xrow0>(-0.001) && xcol0>(-0.001)	/*check line inside raster*/
 2877:     &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
 2878:       line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
 2879:  return ( 1 ); }
 2880: /* --- set params for horizontal line or vertical bar --- */
 2881: if ( isline )					/*interpret row as top row*/
 2882:   row1 = row0 + (thickness-1);			/* set bottom row for line */
 2883: if ( 0&&isbar )					/*interpret col as left col*/
 2884:   hicol = col0 + (thickness-1);			/* set right col for bar */
 2885: /* -------------------------------------------------------------------------
 2886: draw line one row at a time
 2887: -------------------------------------------------------------------------- */
 2888: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
 2889:   {
 2890:   if ( !isbar && !isline )			/* neither vert nor horiz */
 2891:     { xcol  = col0 + ((double)(irow-row0))/a;	/* "middle" col in irow */
 2892:       locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
 2893:       hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
 2894:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
 2895:     fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
 2896:     irow,xcol,locol,hicol);
 2897:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
 2898:   for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
 2899:     if ( ++ipix >= npix )			/* bounds check failed */
 2900: 	if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
 2901: 	else break;				/*or just go on to next row*/
 2902:     else					/* turn on pixel in line */
 2903: 	if ( rp->pixsz == 1 )			/* have a pixel bitmap */
 2904: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
 2905: 	else					/* should have a bytemap */
 2906: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
 2907: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
 2908:   } /* --- end-of-for(irow) --- */
 2909: /* -------------------------------------------------------------------------
 2910: now _redraw_ line one col at a time to avoid "gaps"
 2911: -------------------------------------------------------------------------- */
 2912: if ( 1 )
 2913:  for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
 2914:   {
 2915:   if ( !isbar && !isline )			/* neither vert nor horiz */
 2916:     { xrow  = row0 + ((double)(icol-col0))*a;	/* "middle" row in icol */
 2917:       lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
 2918:       hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
 2919:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
 2920:     fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
 2921:     icol,xrow,lorow,hirow);
 2922:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
 2923:   for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
 2924:     if ( irow<0 || irow>=rp->height
 2925:     ||   icol<0 || icol>=rp->width )		/* bounds check */
 2926:       if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
 2927:       else continue;				/*or just go on to next row*/
 2928:     else
 2929:       setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
 2930:   } /* --- end-of-for(irow) --- */
 2931: /* -------------------------------------------------------------------------
 2932: Back to caller with 1=okay, 0=failed.
 2933: -------------------------------------------------------------------------- */
 2934: end_of_job:
 2935:   return ( isfatal? (ipix<npix? 1:0) : 1 );
 2936: } /* --- end-of-function line_raster() --- */
 2937: 
 2938: 
 2939: /* ==========================================================================
 2940:  * Function:	line_recurse ( rp,  row0, col0,  row1, col1,  thickness )
 2941:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
 2942:  *		in existing raster rp.
 2943:  * --------------------------------------------------------------------------
 2944:  * Arguments:	rp (I)		raster *  to raster in which a line
 2945:  *				will be drawn
 2946:  *		row0 (I)	double containing row at which
 2947:  *				line will start (0 is topmost)
 2948:  *		col0 (I)	double containing col at which
 2949:  *				line will start (0 is leftmost)
 2950:  *		row1 (I)	double containing row at which
 2951:  *				line will end (rp->height-1 is bottom-most)
 2952:  *		col1 (I)	double containing col at which
 2953:  *				line will end (rp->width-1 is rightmost)
 2954:  *		thickness (I)	int containing number of pixels/bits
 2955:  *				thick the line will be
 2956:  * --------------------------------------------------------------------------
 2957:  * Returns:	( int )		1 if line drawn okay,
 2958:  *				or 0 for any error.
 2959:  * --------------------------------------------------------------------------
 2960:  * Notes:     o	Recurses, drawing left- and right-halves of line
 2961:  *		until a horizontal or vertical segment is found
 2962:  * ======================================================================= */
 2963: /* --- entry point --- */
 2964: int	line_recurse ( raster *rp, double row0, double col0,
 2965: 	double row1, double col1, int thickness )
 2966: {
 2967: /* -------------------------------------------------------------------------
 2968: Allocations and Declarations
 2969: -------------------------------------------------------------------------- */
 2970: double	delrow = fabs(row1-row0),	/* 0 if line horizontal */
 2971: 	delcol = fabs(col1-col0),	/* 0 if line vertical */
 2972: 	tolerance = 0.5;		/* draw line when it goes to point */
 2973: double	midrow = 0.5*(row0+row1),	/* midpoint row */
 2974: 	midcol = 0.5*(col0+col1);	/* midpoint col */
 2975: /* -------------------------------------------------------------------------
 2976: recurse if either delta > tolerance
 2977: -------------------------------------------------------------------------- */
 2978: if ( delrow > tolerance			/* row hasn't converged */
 2979: ||   delcol > tolerance )		/* col hasn't converged */
 2980:   { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
 2981:     line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
 2982:     return ( 1 ); }
 2983: /* -------------------------------------------------------------------------
 2984: draw converged point
 2985: -------------------------------------------------------------------------- */
 2986: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
 2987: return ( 1 );
 2988: } /* --- end-of-function line_recurse() --- */
 2989: 
 2990: 
 2991: /* ==========================================================================
 2992:  * Function:	circle_raster ( rp,  row0, col0,  row1, col1,
 2993:  *		thickness, quads )
 2994:  * Purpose:	Draw quad(rant)s of an ellipse in box determined by
 2995:  *		diagonally opposite corner points (row0,col0) and
 2996:  *		(row1,col1), of thickness pixels in existing raster rp.
 2997:  * --------------------------------------------------------------------------
 2998:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
 2999:  *				will be drawn
 3000:  *		row0 (I)	int containing 1st corner row bounding ellipse
 3001:  *				(0 is topmost)
 3002:  *		col0 (I)	int containing 1st corner col bounding ellipse
 3003:  *				(0 is leftmost)
 3004:  *		row1 (I)	int containing 2nd corner row bounding ellipse
 3005:  *				(rp->height-1 is bottom-most)
 3006:  *		col1 (I)	int containing 2nd corner col bounding ellipse
 3007:  *				(rp->width-1 is rightmost)
 3008:  *		thickness (I)	int containing number of pixels/bits
 3009:  *				thick the ellipse arc line will be
 3010:  *		quads (I)	char * to null-terminated string containing
 3011:  *				any subset/combination of "1234" specifying
 3012:  *				which quadrant(s) of ellipse to draw.
 3013:  *				NULL ptr draws all four quadrants;
 3014:  *				otherwise 1=upper-right quadrant,
 3015:  *				2=uper-left, 3=lower-left, 4=lower-right,
 3016:  *				i.e., counterclockwise from 1=positive quad.
 3017:  * --------------------------------------------------------------------------
 3018:  * Returns:	( int )		1 if ellipse drawn okay,
 3019:  *				or 0 for any error.
 3020:  * --------------------------------------------------------------------------
 3021:  * Notes:     o	row0==row1 or col0==col1 are errors
 3022:  *	      o	using ellipse equation x^2/a^2 + y^2/b^2 = 1
 3023:  * ======================================================================= */
 3024: /* --- entry point --- */
 3025: int	circle_raster ( raster *rp, int row0, int col0,
 3026: 	int row1, int col1, int thickness, char *quads )
 3027: {
 3028: /* -------------------------------------------------------------------------
 3029: Allocations and Declarations
 3030: -------------------------------------------------------------------------- */
 3031: /* --- lower-left and upper-right bounding points (in our coords) --- */
 3032: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
 3033: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
 3034: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
 3035: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
 3036: /* --- a and b ellipse params --- */
 3037: int	width = hicol-locol+1,		/* width of bounding box */
 3038: 	height= hirow-lorow+1,		/* height of bounding box */
 3039: 	islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
 3040: double	a = ((double)width)/2.0,	/* x=a when y=0 */
 3041: 	b = ((double)height)/2.0,	/* y=b when x=0 */
 3042: 	abmajor = (islandscape? a : b),	/* max2(a,b) */
 3043: 	abminor = (islandscape? b : a),	/* min2(a,b) */
 3044: 	abmajor2 = abmajor*abmajor,	/* abmajor^2 */
 3045: 	abminor2 = abminor*abminor;	/* abminor^2 */
 3046: /* --- other stuff --- */
 3047: int	imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
 3048: 	iminor=0, nminor=min2(width,height); /* solved index on minor axis */
 3049: int	irow, icol,			/* raster indexes at circumference */
 3050: 	rsign=1, csign=1;		/* row,col signs, both +1 in quad 1*/
 3051: double	midrow= ((double)(row0+row1))/2.0, /* center row */
 3052: 	midcol= ((double)(col0+col1))/2.0; /* center col */
 3053: double	xy, xy2,			/* major axis ellipse coord */
 3054: 	yx2, yx;			/* solved minor ellipse coord */
 3055: int	isokay = 1;			/* true if no pixels out-of-bounds */
 3056: char	*qptr=NULL, *allquads="1234";	/* quadrants if input quads==NULL */
 3057: int	circle_recurse(), isrecurse=1;	/* true to draw ellipse recursively*/
 3058: /* -------------------------------------------------------------------------
 3059: pixel-by-pixel along positive major axis, quit when it goes negative
 3060: -------------------------------------------------------------------------- */
 3061: if ( quads == NULL ) quads = allquads;	/* draw all quads, or only user's */
 3062: if ( msgfp!=NULL && msglevel>=39 )	/* debugging */
 3063:   fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
 3064:   width,height,quads);
 3065: if ( nmajor < 1 ) isokay = 0;		/* problem with input args */
 3066: else
 3067:  {
 3068:  if ( isrecurse )			/* use recursive algorithm */
 3069:   {
 3070:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
 3071:    {
 3072:    double theta0=0.0, theta1=0.0;	/* set thetas based on quadrant */
 3073:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
 3074:     { default:				/* unrecognized, assume quadrant 1 */
 3075:       case '1': theta0=  0.0; theta1= 90.0; break;   /* first quadrant */
 3076:       case '2': theta0= 90.0; theta1=180.0; break;   /* second quadrant */
 3077:       case '3': theta0=180.0; theta1=270.0; break;   /* third quadrant */
 3078:       case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
 3079:    circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
 3080:    } /* --- end-of-for(qptr) --- */
 3081:   return ( 1 );
 3082:   } /* --- end-of-if(isrecurse) --- */
 3083:  for ( imajor=(nmajor+1)/2; ; imajor-- )
 3084:   {
 3085:   /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
 3086:   xy  = ((double)imajor);		/* xy = abmajor ... 0 */
 3087:   if ( xy < 0.0 ) break;		/* negative side symmetrical */
 3088:   yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
 3089:   yx  = (yx2>0.0? sqrt(yx2) : 0.0);	/* take sqrt if possible */
 3090:   iminor = iround(yx);			/* nearest integer */
 3091:   /* --- set pixels for each requested quadrant --- */
 3092:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
 3093:    {
 3094:    rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
 3095:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
 3096:     { default: break;			/* unrecognized, assume quadrant 1 */
 3097:       case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
 3098:       case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
 3099:       case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
 3100:    irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
 3101:    irow = min2(hirow,max2(lorow,irow));	/* keep irow in bounds */
 3102:    icol = iround(midcol + (double)csign*(islandscape?xy:yx));
 3103:    icol = min2(hicol,max2(locol,icol));	/* keep icol in bounds */
 3104:    if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
 3105:      fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
 3106:      imajor,iminor,*qptr,irow,icol);
 3107:    if ( irow<0 || irow>=rp->height	/* row outside raster */
 3108:    ||   icol<0 || icol>=rp->width )	/* col outside raster */
 3109:       {	isokay = 0;			/* signal out-of-bounds pixel */
 3110: 	continue; }			/* but still try remaining points */
 3111:    setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
 3112:    } /* --- end-of-for(qptr) --- */
 3113:   } /* --- end-of-for(imajor) --- */
 3114:  /* ------------------------------------------------------------------------
 3115:  now do it _again_ along minor axis to avoid "gaps"
 3116:  ------------------------------------------------------------------------- */
 3117:  if ( 1 && iminor>0 )
 3118:   for ( iminor=(nminor+1)/2; ; iminor-- )
 3119:    {
 3120:    /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
 3121:    yx  = ((double)iminor);		/* yx = abminor ... 0 */
 3122:    if ( yx < 0.0 ) break;		/* negative side symmetrical */
 3123:    xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
 3124:    xy  = (xy2>0.0? sqrt(xy2) : 0.0);	/* take sqrt if possible */
 3125:    imajor = iround(xy);			/* nearest integer */
 3126:    /* --- set pixels for each requested quadrant --- */
 3127:    for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
 3128:     {
 3129:     rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
 3130:     switch ( *qptr )			/* check for quadrant 1,2,3,4 */
 3131:      { default: break;			/* unrecognized, assume quadrant 1 */
 3132:        case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
 3133:        case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
 3134:        case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
 3135:     irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
 3136:     irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
 3137:     icol = iround(midcol + (double)csign*(islandscape?xy:yx));
 3138:     icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
 3139:     if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
 3140:      fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
 3141:      iminor,imajor,*qptr,irow,icol);
 3142:     if ( irow<0 || irow>=rp->height	/* row outside raster */
 3143:     ||   icol<0 || icol>=rp->width )	/* col outside raster */
 3144:       {	isokay = 0;			/* signal out-of-bounds pixel */
 3145: 	continue; }			/* but still try remaining points */
 3146:     setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
 3147:     } /* --- end-of-for(qptr) --- */
 3148:    } /* --- end-of-for(iminor) --- */
 3149:  } /* --- end-of-if/else(nmajor<1) --- */
 3150: return ( isokay );
 3151: } /* --- end-of-function circle_raster() --- */
 3152: 
 3153: 
 3154: /* ==========================================================================
 3155:  * Function:	circle_recurse ( rp,  row0, col0,  row1, col1,
 3156:  *		thickness, theta0, theta1 )
 3157:  * Purpose:	Recursively draws arc theta0<=theta<=theta1 of the ellipse
 3158:  *		in box determined by diagonally opposite corner points
 3159:  *		(row0,col0) and (row1,col1), of thickness pixels in raster rp.
 3160:  * --------------------------------------------------------------------------
 3161:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
 3162:  *				will be drawn
 3163:  *		row0 (I)	int containing 1st corner row bounding ellipse
 3164:  *				(0 is topmost)
 3165:  *		col0 (I)	int containing 1st corner col bounding ellipse
 3166:  *				(0 is leftmost)
 3167:  *		row1 (I)	int containing 2nd corner row bounding ellipse
 3168:  *				(rp->height-1 is bottom-most)
 3169:  *		col1 (I)	int containing 2nd corner col bounding ellipse
 3170:  *				(rp->width-1 is rightmost)
 3171:  *		thickness (I)	int containing number of pixels/bits
 3172:  *				thick the ellipse arc line will be
 3173:  *		theta0 (I)	double containing first angle -360 -> +360
 3174:  *		theta1 (I)	double containing second angle -360 -> +360
 3175:  *				0=x-axis, positive moving counterclockwise
 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:  *		Then, with x=r*cos(theta), y=r*sin(theta), ellipse
 3183:  *		equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
 3184:  * ======================================================================= */
 3185: /* --- entry point --- */
 3186: int	circle_recurse ( raster *rp, int row0, int col0,
 3187: 	int row1, int col1, int thickness, double theta0, double theta1 )
 3188: {
 3189: /* -------------------------------------------------------------------------
 3190: Allocations and Declarations
 3191: -------------------------------------------------------------------------- */
 3192: /* --- lower-left and upper-right bounding points (in our coords) --- */
 3193: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
 3194: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
 3195: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
 3196: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
 3197: /* --- a and b ellipse params --- */
 3198: int	width = hicol-locol+1,		/* width of bounding box */
 3199: 	height= hirow-lorow+1;		/* height of bounding box */
 3200: double	a = ((double)width)/2.0,	/* col x=a when row y=0 */
 3201: 	b = ((double)height)/2.0,	/* row y=b when col x=0 */
 3202: 	ab=a*b, a2=a*a, b2=b*b;		/* product and squares */
 3203: /* --- arc parameters --- */
 3204: double	rads = 0.017453292,		/* radians per degree = 1/57.29578 */
 3205: 	lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
 3206: 	hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
 3207: 	locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
 3208: 	hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
 3209: 	rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
 3210: 	rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
 3211: 	xlo=rlo*locos, ylo=rlo*losin,	/*col,row pixel coords for lotheta*/
 3212: 	xhi=rhi*hicos, yhi=rhi*hisin,	/*col,row pixel coords for hitheta*/
 3213: 	xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
 3214: 	tolerance = 0.5;		/* convergence tolerance */
 3215: /* -------------------------------------------------------------------------
 3216: recurse if either delta > tolerance
 3217: -------------------------------------------------------------------------- */
 3218: if ( ydelta > tolerance			/* row hasn't converged */
 3219: ||   xdelta > tolerance )		/* col hasn't converged */
 3220:   { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
 3221:     circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta);  /*lo*/
 3222:     circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
 3223: /* -------------------------------------------------------------------------
 3224: draw converged point
 3225: -------------------------------------------------------------------------- */
 3226: else
 3227:   { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi),    /* relative to center*/
 3228: 	centerrow = 0.5*((double)(lorow+hirow)),      /* ellipse y-center */
 3229: 	centercol = 0.5*((double)(locol+hicol)),      /* ellipse x-center */
 3230: 	midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
 3231:     setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
 3232: return ( 1 );
 3233: } /* --- end-of-function circle_recurse() --- */
 3234: 
 3235: 
 3236: /* ==========================================================================
 3237:  * Function:	bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
 3238:  * Purpose:	Recursively draw bezier from r0,c0 to r1,c1
 3239:  *		(with tangent point rt,ct) in existing raster rp.
 3240:  * --------------------------------------------------------------------------
 3241:  * Arguments:	rp (I)		raster *  to raster in which a line
 3242:  *				will be drawn
 3243:  *		r0 (I)		double containing row at which
 3244:  *				bezier will start (0 is topmost)
 3245:  *		c0 (I)		double containing col at which
 3246:  *				bezier will start (0 is leftmost)
 3247:  *		r1 (I)		double containing row at which
 3248:  *				bezier will end (rp->height-1 is bottom-most)
 3249:  *		c1 (I)		double containing col at which
 3250:  *				bezier will end (rp->width-1 is rightmost)
 3251:  *		rt (I)		double containing row for tangent point
 3252:  *		ct (I)		double containing col for tangent point
 3253:  * --------------------------------------------------------------------------
 3254:  * Returns:	( int )		1 if line drawn okay,
 3255:  *				or 0 for any error.
 3256:  * --------------------------------------------------------------------------
 3257:  * Notes:     o	Recurses, drawing left- and right-halves of bezier curve
 3258:  *		until a point is found
 3259:  * ======================================================================= */
 3260: /* --- entry point --- */
 3261: int	bezier_raster ( raster *rp, double r0, double c0,
 3262: 	double r1, double c1, double rt, double ct )
 3263: {
 3264: /* -------------------------------------------------------------------------
 3265: Allocations and Declarations
 3266: -------------------------------------------------------------------------- */
 3267: double	delrow = fabs(r1-r0),		/* 0 if same row */
 3268: 	delcol = fabs(c1-c0),		/* 0 if same col */
 3269: 	tolerance = 0.5;		/* draw curve when it goes to point*/
 3270: double	midrow = 0.5*(r0+r1),		/* midpoint row */
 3271: 	midcol = 0.5*(c0+c1);		/* midpoint col */
 3272: int	irow=0, icol=0;			/* point to be drawn */
 3273: int	status = 1;			/* return status */
 3274: /* -------------------------------------------------------------------------
 3275: recurse if either delta > tolerance
 3276: -------------------------------------------------------------------------- */
 3277: if ( delrow > tolerance			/* row hasn't converged */
 3278: ||   delcol > tolerance )		/* col hasn't converged */
 3279:   { bezier_raster(rp, r0,c0,		/* left half */
 3280: 	0.5*(rt+midrow), 0.5*(ct+midcol),
 3281: 	0.5*(r0+rt), 0.5*(c0+ct) );
 3282:     bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
 3283: 	r1,c1,
 3284: 	0.5*(r1+rt), 0.5*(c1+ct) );
 3285:     return ( 1 ); }
 3286: /* -------------------------------------------------------------------------
 3287: draw converged point
 3288: -------------------------------------------------------------------------- */
 3289: /* --- get integer point --- */
 3290: irow = iround(midrow);			/* row pixel coord */
 3291: icol = iround(midcol);			/* col pixel coord */
 3292: /* --- bounds check --- */
 3293: if ( irow>=0 && irow<rp->height		/* row in bounds */
 3294: &&   icol>=0 && icol<rp->width )	/* col in bounds */
 3295: 	setpixel(rp,irow,icol,255);	/* so set pixel at irow,icol*/
 3296: else	status = 0;			/* bad status if out-of-bounds */
 3297: return ( status );
 3298: } /* --- end-of-function bezier_raster() --- */
 3299: 
 3300: 
 3301: /* ==========================================================================
 3302:  * Function:	border_raster ( rp, ntop, nbot, isline, isfree )
 3303:  * Purpose:	Allocate a new raster containing a copy of input rp,
 3304:  *		along with ntop extra rows at top and nbot at bottom,
 3305:  *		and whose width is either adjusted correspondingly,
 3306:  *		or is automatically enlarged to a multiple of 8
 3307:  *		with original bitmap centered
 3308:  * --------------------------------------------------------------------------
 3309:  * Arguments:	rp (I)		raster *  to raster on which a border
 3310:  *				is to be placed
 3311:  *		ntop (I)	int containing number extra rows at top.
 3312:  *				if negative, abs(ntop) used, and same
 3313:  *				number of extra cols added at left.
 3314:  *		nbot (I)	int containing number extra rows at bottom.
 3315:  *				if negative, abs(nbot) used, and same
 3316:  *				number of extra cols added at right.
 3317:  *		isline (I)	int containing 0 to leave border pixels clear
 3318:  *				or >0 to draw a line around border of
 3319:  *				thickness isline pixels.  See Notes below.
 3320:  *		isfree (I)	int containing true to free rp before return
 3321:  * --------------------------------------------------------------------------
 3322:  * Returns:	( raster * )	ptr to bordered raster,
 3323:  *				or NULL for any error.
 3324:  * --------------------------------------------------------------------------
 3325:  * Notes:     o	The isline arg also controls which sides border lines
 3326:  *		are drawn for.  To do this, isline is interpreted as
 3327:  *		thickness + 100*sides  so that, e.g., passing isline=601
 3328:  *		is interpreted as sides=6 and thickness=1.  And
 3329:  *		sides is further interpreted as 1=left side, 2=top,
 3330:  *		4=right and 8=bottom.  For example, sides=6 where 6=2+4
 3331:  *		draws the top and right borders only.  15 draws all four
 3332:  *		sides.  And 0 (no sides value embedded in isline)
 3333:  *		draws all four sides, too.
 3334:  * ======================================================================= */
 3335: /* --- entry point --- */
 3336: raster	*border_raster ( raster *rp, int ntop, int nbot,
 3337: 			int isline, int isfree )
 3338: {
 3339: /* -------------------------------------------------------------------------
 3340: Allocations and Declarations
 3341: -------------------------------------------------------------------------- */
 3342: raster	*new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/
 3343: int	rastput();		/* overlay rp in new bordered raster */
 3344: int	width  = (rp==NULL?0:rp->width),  /* width of raster */
 3345: 	height = (rp==NULL?0:rp->height), /* height of raster */
 3346: 	istopneg=0, isbotneg=0,		/* true if ntop or nbot negative */
 3347: 	leftmargin = 0;		/* adjust width to whole number of bytes */
 3348: int	left=1, top=1, right=1, bot=1;	/* frame sides to draw */
 3349: int	delete_raster();		/* free input rp if isfree is true */
 3350: /* -------------------------------------------------------------------------
 3351: Initialization
 3352: -------------------------------------------------------------------------- */
 3353: if ( rp == NULL ) goto end_of_job;	/* no input raster provided */
 3354: if ( isstring || (1 && rp->height==1) )	/* explicit string signal or infer */
 3355:   { bp=rp; goto end_of_job; }		/* return ascii string unchanged */
 3356: /* --- check for negative args --- */
 3357: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
 3358: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
 3359: /* --- adjust height for ntop and nbot margins --- */
 3360: height += (ntop+nbot);			/* adjust height for margins */
 3361: /* --- adjust width for left and right margins --- */
 3362: if ( istopneg || isbotneg )	/*caller wants nleft=ntop and/or nright=nbot*/
 3363:   { /* --- adjust width (and leftmargin) as requested by caller -- */
 3364:     if ( istopneg ) { width += ntop; leftmargin = ntop; }
 3365:     if ( isbotneg )   width += nbot;  }
 3366: else
 3367:   { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
 3368:     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
 3369:     width += leftmargin;		/* width now multiple of 8 */
 3370:     leftmargin /= 2; }			/* center original raster */
 3371: /* --- check which sides to draw --- */
 3372: if ( isline > 100 ) {			/* sides arg embedded in isline */
 3373:   int iside=0, sides=isline/100;	/* index, sides=1-15 from 101-1599 */
 3374:   isline -= 100*sides;			/* and remove sides from isline */
 3375:   for ( iside=1; iside<=4; iside++ ) {	/* check left, top, right, bot */
 3376:     int shift = sides/2;		/* shift sides left one bit */
 3377:     if ( sides == 2*shift )		/* low-order bit is >>not<< set */
 3378:       switch ( iside ) {		/* don't draw corresponding side */
 3379:         default: break;			/* internal error */
 3380:         case 1: left = 0; break;	/* 1 = left side */
 3381:         case 2: top  = 0; break;	/* 2 = top side */
 3382:         case 3: right= 0; break;	/* 4 = tight side */
 3383:         case 4: bot  = 0; break; }	/* 8 = bottom side */
 3384:     sides = shift;			/* ready for next side */
 3385:     } /* --- end-of-for(iside) --- */
 3386:   } /* --- end-of-if(isline>100) --- */
 3387: /* -------------------------------------------------------------------------
 3388: allocate bordered raster, and embed rp within it
 3389: -------------------------------------------------------------------------- */
 3390: /* --- allocate bordered raster --- */
 3391: if ( (bp=new_raster(width,height,rp->pixsz))  /*allocate bordered raster*/
 3392: ==   (raster *)NULL ) goto end_of_job;	/* and quit if failed */
 3393: /* --- embed rp in it --- */
 3394: rastput(bp,rp,ntop,leftmargin,1);	/* rp embedded in bp */
 3395: /* -------------------------------------------------------------------------
 3396: draw border if requested
 3397: -------------------------------------------------------------------------- */
 3398: if ( isline )
 3399:  { int	irow, icol, nthick=isline;	/*height,width index, line thickness*/
 3400:   /* --- draw left- and right-borders --- */
 3401:   for ( irow=0; irow<height; irow++ )	/* for each row of bp */
 3402:     for ( icol=0; icol<nthick; icol++ )	/* and each pixel of thickness */
 3403:       {	if(left){setpixel(bp,irow,icol,255);}		/* left border */
 3404: 	if(right){setpixel(bp,irow,width-1-icol,255);} } /* right border */
 3405:   /* --- draw top- and bottom-borders --- */
 3406:   for ( icol=0; icol<width; icol++ )	/* for each col of bp */
 3407:     for ( irow=0; irow<nthick; irow++ )	/* and each pixel of thickness */
 3408:       {	if(top){setpixel(bp,irow,icol,255);}		/* top border */
 3409: 	if(bot){setpixel(bp,height-1-irow,icol,255);} }	/* bottom border */
 3410:  } /* --- end-of-if(isline) --- */
 3411: /* -------------------------------------------------------------------------
 3412: free rp if no longer needed
 3413: -------------------------------------------------------------------------- */
 3414: if ( isfree )					/*caller no longer needs rp*/
 3415:   delete_raster(rp);				/* so free it for him */
 3416: /* -------------------------------------------------------------------------
 3417: Back to caller with bordered raster (or null for any error)
 3418: -------------------------------------------------------------------------- */
 3419: end_of_job:
 3420:   return ( bp );			/* back with bordered or null ptr */
 3421: } /* --- end-of-function border_raster() --- */
 3422: 
 3423: 
 3424: /* ==========================================================================
 3425:  * Function:	backspace_raster ( rp, nback, pback, minspace, isfree )
 3426:  * Purpose:	Allocate a new raster containing a copy of input rp,
 3427:  *		but with trailing nback columns removed.
 3428:  *		If minspace>=0 then (at least) that many columns
 3429:  *		of whitespace will be left in place, regardless of nback.
 3430:  *		If minspace<0 then existing black pixels will be deleted
 3431:  *		as required.
 3432:  * --------------------------------------------------------------------------
 3433:  * Arguments:	rp (I)		raster *  to raster on which a border
 3434:  *				is to be placed
 3435:  *		nback (I)	int containing number of columns to
 3436:  *				backspace (>=0)
 3437:  *		pback (O)	ptr to int returning #pixels actually
 3438:  *				backspaced (or NULL to not use)
 3439:  *		minspace (I)	int containing number of columns
 3440:  *				of whitespace to be left in place
 3441:  *		isfree (I)	int containing true to free rp before return
 3442:  * --------------------------------------------------------------------------
 3443:  * Returns:	( raster * )	ptr to backspaced raster,
 3444:  *				or NULL for any error.
 3445:  * --------------------------------------------------------------------------
 3446:  * Notes:     o	For \! negative space, for \hspace{-10}, etc.
 3447:  * ======================================================================= */
 3448: /* --- entry point --- */
 3449: raster	*backspace_raster ( raster *rp, int nback, int *pback, int minspace,
 3450: 	int isfree )
 3451: {
 3452: /* -------------------------------------------------------------------------
 3453: Allocations and Declarations
 3454: -------------------------------------------------------------------------- */
 3455: raster	*new_raster(), *bp=(raster *)NULL;  /* raster returned to caller */
 3456: int	delete_raster();		/* free input rp if isfree is true */
 3457: int	width  = (rp==NULL?0:rp->width),  /* original width of raster */
 3458: 	height = (rp==NULL?0:rp->height), /* height of raster */
 3459: 	mback = nback,			/* nback adjusted for minspace */
 3460: 	newwidth = width,		/* adjusted width after backspace */
 3461: 	icol=0, irow=0;			/* col,row index */
 3462: if ( rp == NULL ) goto end_of_job;	/* no input given */
 3463: /* -------------------------------------------------------------------------
 3464: locate rightmost column of rp containing ink, and determine backspaced width
 3465: -------------------------------------------------------------------------- */
 3466: /* --- locate rightmost column of rp containing ink --- */
 3467: if ( minspace >= 0 )			/* only needed if given minspace */
 3468:  for ( icol=width-1; icol>=0; icol-- )	/* find first non-empty col in row */
 3469:   for ( irow=0; irow<height; irow++ )	/* for each row inside rp */
 3470:    if ( getpixel(rp,irow,icol) != 0 ) {	/* found a set pixel */
 3471:      int whitecols = (width-1) - icol;	/* #cols containing white space */
 3472:      mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
 3473:      goto gotright; }			/* no need to look further */
 3474: /* --- determine width of new backspaced raster --- */
 3475: gotright:				/* found col with ink (or rp empty)*/
 3476:   if ( mback > width ) mback = width;	/* can't backspace before beginning*/
 3477:   newwidth = max2(1,width-mback);	/* #cols in backspaced raster */
 3478:   if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
 3479: /* -------------------------------------------------------------------------
 3480: allocate new raster and fill it with leftmost cols of rp
 3481: -------------------------------------------------------------------------- */
 3482: /* --- allocate backspaced raster --- */
 3483: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
 3484: ==   (raster *)NULL ) goto end_of_job;	/* and quit if failed */
 3485: /* --- fill new raster --- */
 3486: if ( 1 || width-nback > 0 )		/* don't fill 1-pixel wide empty bp*/
 3487:  for ( icol=0; icol<newwidth; icol++ )	/* find first non-empty col in row */
 3488:   for ( irow=0; irow<height; irow++ )	/* for each row inside rp */
 3489:     { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
 3490:       setpixel(bp,irow,icol,value); }	/* saved in backspaced raster */
 3491: /* -------------------------------------------------------------------------
 3492: Back to caller with backspaced raster (or null for any error)
 3493: -------------------------------------------------------------------------- */
 3494: end_of_job:
 3495:   if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
 3496:    "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
 3497:    nback,minspace,mback,width,newwidth); fflush(msgfp); }
 3498:   if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
 3499:   return ( bp );			/* back with backspaced or null ptr*/
 3500: } /* --- end-of-function backspace_raster() --- */
 3501: 
 3502: 
 3503: /* ==========================================================================
 3504:  * Function:	type_raster ( rp, fp )
 3505:  * Purpose:	Emit an ascii dump representing rp, on fp.
 3506:  * --------------------------------------------------------------------------
 3507:  * Arguments:	rp (I)		ptr to raster struct for which an
 3508:  *				ascii dump is to be constructed.
 3509:  *		fp (I)		File ptr to output device (defaults to
 3510:  *				stdout if passed as NULL).
 3511:  * --------------------------------------------------------------------------
 3512:  * Returns:	( int )		1 if completed successfully,
 3513:  *				or 0 otherwise (for any error).
 3514:  * --------------------------------------------------------------------------
 3515:  * Notes:
 3516:  * ======================================================================= */
 3517: /* --- entry point --- */
 3518: int	type_raster ( raster *rp, FILE *fp )
 3519: {
 3520: /* -------------------------------------------------------------------------
 3521: Allocations and Declarations
 3522: -------------------------------------------------------------------------- */
 3523: static	int display_width = 72;		/* max columns for display */
 3524: static	char display_chars[16] =	/* display chars for bytemap */
 3525: 	{ ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
 3526: char	scanline[133];			/* ascii image for one scan line */
 3527: int	scan_width;			/* #chars in scan (<=display_width)*/
 3528: int	irow, locol,hicol=(-1);		/* height index, width indexes */
 3529: raster	*gftobitmap(), *bitmaprp=rp;	/* convert .gf to bitmap if needed */
 3530: int	delete_raster();		/*free bitmap converted for display*/
 3531: /* --------------------------------------------------------------------------
 3532: initialization
 3533: -------------------------------------------------------------------------- */
 3534: /* --- redirect null fp --- */
 3535: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
 3536: if ( msglevel >= 999 ) { fprintf(fp,	/* debugging diagnostics */
 3537:   "type_raster> width=%d height=%d ...\n",
 3538:   rp->width,rp->height); fflush(fp); }
 3539: /* --- check for ascii string --- */
 3540: if ( isstring				/* pixmap has string, not raster */
 3541: ||   (0 && rp->height==1) )		/* infer input rp is a string */
 3542:  {
 3543:  char *string = (char *)(rp->pixmap);	/*interpret pixmap as ascii string*/
 3544:  int width = strlen(string);		/* #chars in ascii string */
 3545:  while ( width > display_width-2 )	/* too big for one line */
 3546:   { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
 3547:     string += (display_width-2);	/* bump string past displayed chars*/
 3548:     width -= (display_width-2); }	/* decrement remaining width */
 3549:  fprintf(fp,"\"%.*s\"\n",width,string);	/* display trailing chars */
 3550:  return ( 1 );
 3551:  } /* --- end-of-if(isstring) --- */
 3552: /* --------------------------------------------------------------------------
 3553: display ascii dump of bitmap image (in segments if display_width < rp->width)
 3554: -------------------------------------------------------------------------- */
 3555: if ( rp->format == 2			/* input is .gf-formatted */
 3556: ||   rp->format == 3 )
 3557:   bitmaprp = gftobitmap(rp);		/* so convert it for display */
 3558: if ( bitmaprp != NULL )			/* if we have image for display */
 3559:  while ( (locol=hicol+1) < rp->width )	/*start where prev segment left off*/
 3560:   {
 3561:   /* --- set hicol for this pass (locol set above) --- */
 3562:   hicol += display_width;		/* show as much as display allows */
 3563:   if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
 3564:   scan_width = hicol-locol+1;		/* #chars in this scan */
 3565:   if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
 3566:   /* ------------------------------------------------------------------------
 3567:   display all scan lines for this local...hicol segment range
 3568:   ------------------------------------------------------------------------ */
 3569:   for ( irow=0; irow<rp->height; irow++ )  /* all scan lines for col range */
 3570:     {
 3571:     /* --- allocations and declarations --- */
 3572:     int	ipix,				/* pixmap[] index for this scan */
 3573: 	lopix = irow*rp->width + locol;	/*first pixmap[] pixel in this scan*/
 3574:     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
 3575:     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
 3576:       if ( bitmaprp->pixsz == 1 )	/*' '=0 or '*'=1 to display bitmap*/
 3577: 	scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
 3578:       else				/* should have a bytemap */
 3579:        if ( bitmaprp->pixsz == 8 )	/* double-check pixsz for bytemap */
 3580: 	{ int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
 3581: 	  ichar = min2(15,pixval/16);	/* index for ' ', '1'...'e', '*' */
 3582: 	  scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
 3583:     /* --- display completed scan line --- */
 3584:     fprintf(fp,"%.*s\n",scan_width,scanline);	
 3585:     } /* --- end-of-for(irow) --- */
 3586:   } /* --- end-of-while(hicol<rp->width) --- */
 3587: /* -------------------------------------------------------------------------
 3588: Back to caller with 1=okay, 0=failed.
 3589: -------------------------------------------------------------------------- */
 3590: if ( rp->format == 2			/* input was .gf-format */
 3591: ||   rp->format == 3 )
 3592:   if ( bitmaprp != NULL )		/* and we converted it for display */
 3593:     delete_raster(bitmaprp);		/* no longer needed, so free it */
 3594: return ( 1 );
 3595: } /* --- end-of-function type_raster() --- */
 3596: 
 3597: 
 3598: /* ==========================================================================
 3599:  * Function:	type_bytemap ( bp, grayscale, width, height, fp )
 3600:  * Purpose:	Emit an ascii dump representing bp, on fp.
 3601:  * --------------------------------------------------------------------------
 3602:  * Arguments:	bp (I)		intbyte * to bytemap for which an
 3603:  *				ascii dump is to be constructed.
 3604:  *		grayscale (I)	int containing #gray shades, 256 for 8-bit
 3605:  *		width (I)	int containing #cols in bytemap
 3606:  *		height (I)	int containing #rows in bytemap
 3607:  *		fp (I)		File ptr to output device (defaults to
 3608:  *				stdout if passed as NULL).
 3609:  * --------------------------------------------------------------------------
 3610:  * Returns:	( int )		1 if completed successfully,
 3611:  *				or 0 otherwise (for any error).
 3612:  * --------------------------------------------------------------------------
 3613:  * Notes:
 3614:  * ======================================================================= */
 3615: /* --- entry point --- */
 3616: int	type_bytemap ( intbyte *bp, int grayscale,
 3617: 			int width, int height, FILE *fp )
 3618: {
 3619: /* -------------------------------------------------------------------------
 3620: Allocations and Declarations
 3621: -------------------------------------------------------------------------- */
 3622: static	int display_width = 72;		/* max columns for display */
 3623: int	byte_width = 3,			/* cols to display byte (ff+space) */
 3624: 	maxbyte = 0;			/* if maxbyte<16, set byte_width=2 */
 3625: int	white_byte = 0,			/* show dots for white_byte's */
 3626: 	black_byte = grayscale-1;	/* show stars for black_byte's */
 3627: char	scanline[133];			/* ascii image for one scan line */
 3628: int	scan_width,			/* #chars in scan (<=display_width)*/
 3629: 	scan_cols;			/* #cols in scan (hicol-locol+1) */
 3630: int	ibyte,				/* bp[] index */
 3631: 	irow, locol,hicol=(-1);		/* height index, width indexes */
 3632: /* --------------------------------------------------------------------------
 3633: initialization
 3634: -------------------------------------------------------------------------- */
 3635: /* --- redirect null fp --- */
 3636: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
 3637: /* --- check for ascii string --- */
 3638: if ( isstring )				/* bp has ascii string, not raster */
 3639:  { width = strlen((char *)bp);		/* #chars in ascii string */
 3640:    height = 1; }			/* default */
 3641: /* --- see if we can get away with byte_width=1 --- */
 3642: for ( ibyte=0; ibyte<width*height; ibyte++ )  /* check all bytes */
 3643:   { int	byteval = (int)bp[ibyte];	/* current byte value */
 3644:     if ( byteval < black_byte )		/* if it's less than black_byte */
 3645:       maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
 3646: if ( maxbyte < 16 )			/* bytevals will fit in one column */
 3647:   byte_width = 1;			/* so reset display byte_width */
 3648: /* --------------------------------------------------------------------------
 3649: display ascii dump of bitmap image (in segments if display_width < rp->width)
 3650: -------------------------------------------------------------------------- */
 3651: while ( (locol=hicol+1) < width )	/*start where prev segment left off*/
 3652:   {
 3653:   /* --- set hicol for this pass (locol set above) --- */
 3654:   hicol += display_width/byte_width;	/* show as much as display allows */
 3655:   if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
 3656:   scan_cols = hicol-locol+1;		/* #cols in this scan */
 3657:   scan_width = byte_width*scan_cols;	/* #chars in this scan */
 3658:   if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
 3659:   /* ------------------------------------------------------------------------
 3660:   display all scan lines for this local...hicol segment range
 3661:   ------------------------------------------------------------------------ */
 3662:   for ( irow=0; irow<height; irow++ )	/* all scan lines for col range */
 3663:     {
 3664:     /* --- allocations and declarations --- */
 3665:     int  lobyte = irow*width + locol;	/* first bp[] byte in this scan */
 3666:     char scanbyte[32];			/* sprintf() buffer for byte */
 3667:     /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
 3668:     memset(scanline,' ',scan_width);	/* blank out scanline */
 3669:     for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
 3670:       {	int byteval = (int)bp[lobyte+ibyte];  /* value of current byte */
 3671: 	memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
 3672: 	if ( byteval == black_byte )	/* but if we have a black byte */
 3673: 	  memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
 3674: 	if ( byte_width > 1 )		/* don't blank out single char */
 3675: 	  scanbyte[byte_width-1] = ' ';	/* blank-fill rightmost character */
 3676: 	if ( byteval != white_byte	/* format bytes that are non-white */
 3677: 	&&   byteval != black_byte )	/* and that are non-black */
 3678: 	  sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
 3679: 	memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
 3680:     /* --- display completed scan line --- */
 3681:     fprintf(fp,"%.*s\n",scan_width,scanline);	
 3682:     } /* --- end-of-for(irow) --- */
 3683:   } /* --- end-of-while(hicol<width) --- */
 3684: /* -------------------------------------------------------------------------
 3685: Back to caller with 1=okay, 0=failed.
 3686: -------------------------------------------------------------------------- */
 3687: return ( 1 );
 3688: } /* --- end-of-function type_bytemap() --- */
 3689: 
 3690: 
 3691: /* ==========================================================================
 3692:  * Function:	xbitmap_raster ( rp, fp )
 3693:  * Purpose:	Emit a mime xbitmap representing rp, on fp.
 3694:  * --------------------------------------------------------------------------
 3695:  * Arguments:	rp (I)		ptr to raster struct for which a mime
 3696:  *				xbitmap is to be constructed.
 3697:  *		fp (I)		File ptr to output device (defaults to
 3698:  *				stdout if passed as NULL).
 3699:  * --------------------------------------------------------------------------
 3700:  * Returns:	( int )		1 if completed successfully,
 3701:  *				or 0 otherwise (for any error).
 3702:  * --------------------------------------------------------------------------
 3703:  * Notes:
 3704:  * ======================================================================= */
 3705: /* --- entry point --- */
 3706: int	xbitmap_raster ( raster *rp, FILE *fp )
 3707: {
 3708: /* -------------------------------------------------------------------------
 3709: Allocations and Declarations
 3710: -------------------------------------------------------------------------- */
 3711: char	*title = "image";		/* dummy title */
 3712: int	hex_bitmap();			/* dump bitmap as hex bytes */
 3713: /* --------------------------------------------------------------------------
 3714: emit text to display mime xbitmap representation of rp->bitmap image
 3715: -------------------------------------------------------------------------- */
 3716: /* --- first redirect null fp --- */
 3717: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
 3718: /* --- check for ascii string --- */
 3719: if ( isstring )				/* pixmap has string, not raster */
 3720:  return ( 0 );				/* can't handle ascii string */
 3721: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
 3722: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
 3723: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
 3724: 	title,rp->width, title,rp->height );
 3725: fprintf( fp, "static char %s_bits[] = {\n", title );
 3726: hex_bitmap(rp,fp,0,0);			/* emit hex dump of bitmap bytes */
 3727: fprintf (fp,"};\n");			/* ending with "};" for C array */
 3728: /* -------------------------------------------------------------------------
 3729: Back to caller with 1=okay, 0=failed.
 3730: -------------------------------------------------------------------------- */
 3731: return ( 1 );
 3732: } /* --- end-of-function xbitmap_raster() --- */
 3733: 
 3734: 
 3735: /* ==========================================================================
 3736:  * Function:	type_pbmpgm ( rp, ptype, file )
 3737:  * Purpose:	Write pbm or pgm image of rp to file
 3738:  * --------------------------------------------------------------------------
 3739:  * Arguments:	rp (I)		ptr to raster struct for which
 3740:  *				a pbm/pgm file is to be written.
 3741:  *		ptype (I)	int containing 1 for pbm, 2 for pgm, or
 3742:  *				0 to determine ptype from values in rp
 3743:  *		file (I)	ptr to null-terminated char string
 3744:  *				containing name of fuke to be written
 3745:  *				(see notes below).
 3746:  * --------------------------------------------------------------------------
 3747:  * Returns:	( int )		total #bytes written,
 3748:  *				or 0 for any error.
 3749:  * --------------------------------------------------------------------------
 3750:  * Notes:     o	(a) If file==NULL, output is written to stdout;
 3751:  *		(b) if *file=='\000' then file is taken as the
 3752:  *		    address of an output buffer to which output
 3753:  *		    is written (and is followed by a terminating
 3754:  *		    '\0' which is not counted in #bytes returned);
 3755:  *		(c) otherwise file is the filename (opened and
 3756:  *		    closed internally) to which output is written,
 3757:  *		    except that any final .ext extension is replaced
 3758:  *		    by .pbm or .pgm depending on ptype.
 3759:  * ======================================================================= */
 3760: /* --- entry point --- */
 3761: int	type_pbmpgm ( raster *rp, int ptype, char *file )
 3762: {
 3763: /* -------------------------------------------------------------------------
 3764: Allocations and Declarations
 3765: -------------------------------------------------------------------------- */
 3766: int	isokay=0, nbytes=0;	/* completion flag, total #bytes written */
 3767: int	irow=0, jcol=0;		/*height(row), width(col) indexes in raster*/
 3768: int	pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
 3769: 	ngray = 0;		/* #gray scale values */
 3770: FILE	/* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
 3771: char	outline[1024], outfield[256], /* output line, field */
 3772: 	cr[16] = "\n\000";	/* cr at end-of-line */
 3773: int	maxlinelen = 70;	/* maximum allowed line length */
 3774: int	pixfrac=6;		/* use (pixmax-pixmin)/pixfrac as step */
 3775: static	char
 3776: 	*suffix[] = { NULL, ".pbm", ".pgm" },	/* file.suffix[ptype] */
 3777: 	*magic[] = { NULL, "P1", "P2" },	/*identifying "magic number"*/
 3778: 	*mode[] = { NULL, "w", "w" };		/* fopen() mode[ptype] */
 3779: /* -------------------------------------------------------------------------
 3780: check input, determine grayscale,  and set up output file if necessary
 3781: -------------------------------------------------------------------------- */
 3782: /* --- check input args --- */
 3783: if ( rp == NULL ) goto end_of_job;	/* no input raster provided */
 3784: if ( ptype != 0 )			/* we'll determine ptype below */
 3785:  if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
 3786: /* --- determine largest (and smallest) value in pixmap --- */
 3787: for ( irow=0; irow<rp->height; irow++ )	/* for each row, top-to-bottom */
 3788:  for ( jcol=0; jcol<rp->width; jcol++ )	/* for each col, left-to-right */
 3789:   { int	pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
 3790:     pixmin = min2(pixmin,pixval);	/* new minimum */
 3791:     pixmax = max2(pixmax,pixval); }	/* new maximum */
 3792: ngray = 1 + (pixmax-pixmin);		/* should be 2 for b/w bitmap */
 3793: if ( ptype == 0 )			/* caller wants us to set ptype */
 3794:   ptype = (ngray>=3?2:1);		/* use grayscale if >2 shades */
 3795: /* --- open output file if necessary --- */
 3796: if ( file == NULL ) fp = stdout;	/*null ptr signals output to stdout*/
 3797: else if ( *file != '\000' ) {		/* explicit filename provided, so...*/
 3798:   char	fname[512], *pdot=NULL;		/* file.ext, ptr to last . in fname*/
 3799:   strncpy(fname,file,255);		/* local copy of file name */
 3800:   fname[255] = '\000';			/* make sure it's null terminated */
 3801:   if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
 3802:     strcat(fname,suffix[ptype]);	/* so add extension */
 3803:   else					/* we already have an extension */
 3804:     strcpy(pdot,suffix[ptype]);		/* so replace original extension */
 3805:   if ( (fp = fopen(fname,mode[ptype]))	/* open output file */
 3806:   ==   (FILE *)NULL ) goto end_of_job;	/* quit if failed to open */
 3807:   } /* --- ens-of-if(*file!='\0') --- */
 3808: /* -------------------------------------------------------------------------
 3809: format and write header
 3810: -------------------------------------------------------------------------- */
 3811: /* --- format header info --- */
 3812: *outline = '\000';			/* initialize line buffer */
 3813: strcat(outline,magic[ptype]);		/* begin file with "magic number" */
 3814: strcat(outline,cr);			/* followed by cr to end line */
 3815: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
 3816: strcat(outline,outfield);		/* add width and height to header */
 3817: strcat(outline,cr);			/* followed by cr to end line */
 3818: if ( ptype == 2 )			/* need max grayscale value */
 3819:   { sprintf(outfield,"%d",pixmax);	/* format maximum pixel value */
 3820:     strcat(outline,outfield);		/* add max value to header */
 3821:     strcat(outline,cr); }		/* followed by cr to end line */
 3822: /* --- write header to file or memory buffer --- */
 3823: if ( fp == NULL )			/* if we have no open file... */
 3824:   strcat(file,outline);			/* add header to caller's buffer */
 3825: else					/* or if we have an open file... */
 3826:   if ( fputs(outline,fp)		/* try writing header to open file */
 3827:   ==   EOF ) goto end_of_job;		/* return with error if failed */
 3828: nbytes += strlen(outline);		/* bump output byte count */
 3829: /* -------------------------------------------------------------------------
 3830: format and write pixels
 3831: -------------------------------------------------------------------------- */
 3832: *outline = '\000';			/* initialize line buffer */
 3833: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
 3834:  for ( jcol=0; jcol<rp->width; jcol++ )	{ /* for each col, left-to-right */
 3835:   /* --- format value at irow,jcol--- */
 3836:   *outfield = '\000';			/* init empty field */
 3837:   if ( irow < rp->height ) {		/* check row index */
 3838:     int	pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
 3839:     if ( ptype == 1 )			/* pixval must be 1 or 0 */
 3840:       pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
 3841:     sprintf(outfield,"%d ",pixval); }	/* format pixel value */
 3842:   /* --- write line if this value won't fit on it (or last line) --- */
 3843:   if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
 3844:   ||   irow >= rp->height ) {		/* force writing last line */
 3845:     strcat(outline,cr);			/* add cr to end current line */
 3846:     if ( fp == NULL )			/* if we have no open file... */
 3847:       strcat(file,outline);		/* add header to caller's buffer */
 3848:     else				/* or if we have an open file... */
 3849:       if ( fputs(outline,fp)		/* try writing header to open file */
 3850:       ==   EOF ) goto end_of_job;	/* return with error if failed */
 3851:     nbytes += strlen(outline);		/* bump output byte count */
 3852:     *outline = '\000';			/* re-initialize line buffer */
 3853:     } /* --- end-of-if(strlen>=maxlinelen) --- */
 3854:   if ( irow >= rp->height ) break;	/* done after writing last line */
 3855:   /* --- concatanate value to line -- */
 3856:   strcat(outline,outfield);		/* concatanate value to line */
 3857:   } /* --- end-of-for(jcol,irow) --- */
 3858: isokay = 1;				/* signal successful completion */
 3859: /* -------------------------------------------------------------------------
 3860: Back to caller with total #bytes written, or 0=failed.
 3861: -------------------------------------------------------------------------- */
 3862: end_of_job:
 3863:   if ( fp != NULL			/* output written to an open file */
 3864:   &&   fp != stdout )			/* and it's not just stdout */
 3865:     fclose(fp);				/* so close file before returning */
 3866:   return ( (isokay?nbytes:0) );		/*back to caller with #bytes written*/
 3867: } /* --- end-of-function type_pbmpgm() --- */
 3868: 
 3869: 
 3870: /* ==========================================================================
 3871:  * Function:	cstruct_chardef ( cp, fp, col1 )
 3872:  * Purpose:	Emit a C struct of cp on fp, starting in col1.
 3873:  * --------------------------------------------------------------------------
 3874:  * Arguments:	cp (I)		ptr to chardef struct for which
 3875:  *				a C struct is to be generated.
 3876:  *		fp (I)		File ptr to output device (defaults to
 3877:  *				stdout if passed as NULL).
 3878:  *		col1 (I)	int containing 0...65; output lines
 3879:  *				are preceded by col1 blanks.
 3880:  * --------------------------------------------------------------------------
 3881:  * Returns:	( int )		1 if completed successfully,
 3882:  *				or 0 otherwise (for any error).
 3883:  * --------------------------------------------------------------------------
 3884:  * Notes:
 3885:  * ======================================================================= */
 3886: /* --- entry point --- */
 3887: int	cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
 3888: {
 3889: /* -------------------------------------------------------------------------
 3890: Allocations and Declarations
 3891: -------------------------------------------------------------------------- */
 3892: char	field[64];		/* field within output line */
 3893: int	cstruct_raster(),	/* emit a raster */
 3894: 	emit_string();		/* emit a string and comment */
 3895: /* -------------------------------------------------------------------------
 3896: emit   charnum, location, name  /  hirow, hicol,  lorow, locol
 3897: -------------------------------------------------------------------------- */
 3898: /* --- charnum, location, name --- */
 3899: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
 3900: emit_string ( fp, col1, field, "character number, location");
 3901: /* --- toprow, topleftcol,   botrow, botleftcol  --- */
 3902: sprintf(field,"  %3d,%2d,  %3d,%2d,\n",		/* format... */
 3903:   cp->toprow,cp->topleftcol,			/* toprow, topleftcol, */
 3904:   cp->botrow,cp->botleftcol);			/* and botrow, botleftcol */
 3905: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
 3906: /* -------------------------------------------------------------------------
 3907: emit raster and chardef's closing brace, and then return to caller
 3908: -------------------------------------------------------------------------- */
 3909: cstruct_raster(&cp->image,fp,col1+4);		/* emit raster */
 3910: emit_string ( fp, 0, "  }", NULL);		/* emit closing brace */
 3911: return ( 1 );			/* back to caller with 1=okay, 0=failed */
 3912: } /* --- end-of-function cstruct_chardef() --- */
 3913: 
 3914: 
 3915: /* ==========================================================================
 3916:  * Function:	cstruct_raster ( rp, fp, col1 )
 3917:  * Purpose:	Emit a C struct of rp on fp, starting in col1.
 3918:  * --------------------------------------------------------------------------
 3919:  * Arguments:	rp (I)		ptr to raster struct for which
 3920:  *				a C struct is to be generated.
 3921:  *		fp (I)		File ptr to output device (defaults to
 3922:  *				stdout if passed as NULL).
 3923:  *		col1 (I)	int containing 0...65; output lines
 3924:  *				are preceded by col1 blanks.
 3925:  * --------------------------------------------------------------------------
 3926:  * Returns:	( int )		1 if completed successfully,
 3927:  *				or 0 otherwise (for any error).
 3928:  * --------------------------------------------------------------------------
 3929:  * Notes:
 3930:  * ======================================================================= */
 3931: /* --- entry point --- */
 3932: int	cstruct_raster ( raster *rp, FILE *fp, int col1 )
 3933: {
 3934: /* -------------------------------------------------------------------------
 3935: Allocations and Declarations
 3936: -------------------------------------------------------------------------- */
 3937: char	field[64];		/* field within output line */
 3938: char	typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
 3939: int	hex_bitmap();		/* to emit raster bitmap */
 3940: int	emit_string();		/* emit a string and comment */
 3941: /* -------------------------------------------------------------------------
 3942: emit width and height
 3943: -------------------------------------------------------------------------- */
 3944: sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
 3945: 	rp->width,rp->height,rp->format,rp->pixsz,typecast);
 3946: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
 3947: /* -------------------------------------------------------------------------
 3948: emit bitmap and closing brace, and return to caller
 3949: -------------------------------------------------------------------------- */
 3950: hex_bitmap(rp,fp,col1+2,1);	/* emit bitmap */
 3951: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
 3952: return ( 1 );			/* back to caller with 1=okay, 0=failed */
 3953: } /* --- end-of-function cstruct_raster() --- */
 3954: 
 3955: 
 3956: /* ==========================================================================
 3957:  * Function:	hex_bitmap ( rp, fp, col1, isstr )
 3958:  * Purpose:	Emit a hex dump of the bitmap of rp on fp, starting in col1.
 3959:  *		If isstr (is string) is true, the dump is of the form
 3960:  *			"\x01\x02\x03\x04\x05..."
 3961:  *		Otherwise, if isstr is false, the dump is of the form
 3962:  *			0x01,0x02,0x03,0x04,0x05...
 3963:  * --------------------------------------------------------------------------
 3964:  * Arguments:	rp (I)		ptr to raster struct for which
 3965:  *				a hex dump is to be constructed.
 3966:  *		fp (I)		File ptr to output device (defaults to
 3967:  *				stdout if passed as NULL).
 3968:  *		col1 (I)	int containing 0...65; output lines
 3969:  *				are preceded by col1 blanks.
 3970:  *		isstr (I)	int specifying dump format as described above
 3971:  * --------------------------------------------------------------------------
 3972:  * Returns:	( int )		1 if completed successfully,
 3973:  *				or 0 otherwise (for any error).
 3974:  * --------------------------------------------------------------------------
 3975:  * Notes:
 3976:  * ======================================================================= */
 3977: /* --- entry point --- */
 3978: int	hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
 3979: {
 3980: /* -------------------------------------------------------------------------
 3981: Allocations and Declarations
 3982: -------------------------------------------------------------------------- */
 3983: int	ibyte,				/* pixmap[ibyte] index */
 3984: 	nbytes = pixbytes(rp);		/*#bytes in bitmap or .gf-formatted*/
 3985: char	stub[64]="                                ";/* col1 leading blanks */
 3986: int	linewidth = 64,			/* (roughly) rightmost column */
 3987: 	colwidth = (isstr? 4:5);	/* #cols required for each byte */
 3988: int	ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
 3989: /* --------------------------------------------------------------------------
 3990: initialization
 3991: -------------------------------------------------------------------------- */
 3992: /* --- redirect null fp --- */
 3993: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
 3994: /* --- emit initial stub if wanted --- */
 3995: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
 3996: /* --------------------------------------------------------------------------
 3997: emit hex dump of rp->bitmap image
 3998: -------------------------------------------------------------------------- */
 3999: if ( isstr ) fprintf(fp,"\"");		/* opening " before first line */
 4000: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* one byte at a time */
 4001:   {
 4002:   /* --- display a byte as hex char or number, depending on isstr --- */
 4003:   if ( isstr )				/* string format wanted */
 4004:     fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex char*/
 4005:   else					/* comma-separated format wanted */
 4006:     fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex number*/
 4007:   /* --- add a separator and newline, etc, as necessary --- */
 4008:   if ( ibyte < nbytes-1)		/* not the last byte yet */
 4009:     {
 4010:     if ( !isstr ) fprintf(fp,",");	/* follow hex number with comma */
 4011:     if ( (ibyte+1)%ncols==0 ) {		/* need new line after every ncols */
 4012:       if ( !isstr )			/* for hex numbers format ... */
 4013: 	fprintf(fp,"\n%.*s",col1,stub);	/* ...just need newline and stub */
 4014:       else				/* for string format... */
 4015: 	fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
 4016:     } /* --- end-of-if(ibyte<nbytes-1) --- */
 4017:   } /* --- end-of-for(ibyte) --- */
 4018: if ( isstr ) fprintf(fp,"\"");		/* closing " after last line */
 4019: return ( 1 );				/* back with 1=okay, 0=failed */
 4020: } /* --- end-of-function hex_bitmap() --- */
 4021: 
 4022: 
 4023: /* ==========================================================================
 4024:  * Function:	emit_string ( fp, col1, string, comment )
 4025:  * Purpose:	Emit string on fp, starting in col1,
 4026:  *		and followed by right-justified comment.
 4027:  * --------------------------------------------------------------------------
 4028:  * Arguments:	fp (I)		File ptr to output device (defaults to
 4029:  *				stdout if passed as NULL).
 4030:  *		col1 (I)	int containing 0 or #blanks preceding string
 4031:  *		string (I)	char *  containing string to be emitted.
 4032:  *				If last char of string is '\n',
 4033:  *				the emitted line ends with a newline,
 4034:  *				otherwise not.
 4035:  *		comment (I)	NULL or char * containing right-justified
 4036:  *				comment (we enclose between /star and star/)
 4037:  * --------------------------------------------------------------------------
 4038:  * Returns:	( int )		1 if completed successfully,
 4039:  *				or 0 otherwise (for any error).
 4040:  * --------------------------------------------------------------------------
 4041:  * Notes:     o
 4042:  * ======================================================================= */
 4043: /* --- entry point --- */
 4044: int	emit_string ( FILE *fp, int col1, char *string, char *comment )
 4045: {
 4046: /* -------------------------------------------------------------------------
 4047: Allocations and Declarations
 4048: -------------------------------------------------------------------------- */
 4049: char	line[256];		/* construct line with caller's fields */
 4050: int	fieldlen;		/* #chars in one of caller's fields */
 4051: int	linelen = 72;		/*line length (for right-justified comment)*/
 4052: int	isnewline = 0;		/* true to emit \n at end of line */
 4053: /* --------------------------------------------------------------------------
 4054: construct line containing prolog, string, epilog, and finally comment
 4055: -------------------------------------------------------------------------- */
 4056: /* --- init line --- */
 4057: memset(line,' ',255);			/* start line with blanks */
 4058: /* --- embed string into line --- */
 4059: if ( string != NULL )			/* if caller gave us a string... */
 4060:   { fieldlen = strlen(string);		/* #cols required for string */
 4061:     if ( string[fieldlen-1] == '\n' )	/* check last char for newline */
 4062:       {	isnewline = 1;			/* got it, so set flag */
 4063: 	fieldlen--; }			/* but don't print it yet */
 4064:     memcpy(line+col1,string,fieldlen);	/* embid string starting at col1 */
 4065:     col1 += fieldlen; }			/* bump col past epilog */
 4066: /* --- embed comment into line --- */
 4067: if ( comment != NULL )			/* if caller gave us a comment... */
 4068:   { fieldlen = 6 + strlen(comment);	/* plus  /star, star/, 2 spaces */
 4069:     if ( linelen-fieldlen < col1 )	/* comment won't fit */
 4070:       fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
 4071:     if ( fieldlen > 6 )			/* can fit all or part of comment */
 4072:       sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
 4073: 	'*', fieldlen-6,comment, '*');
 4074:     col1 = linelen; }			/* indicate line filled */
 4075: /* --- line completed --- */
 4076: line[col1] = '\000';			/* null-terminate completed line */
 4077: /* -------------------------------------------------------------------------
 4078: emit line, then back to caller with 1=okay, 0=failed.
 4079: -------------------------------------------------------------------------- */
 4080: /* --- first redirect null fp --- */
 4081: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
 4082: /* --- emit line (and optional newline) --- */
 4083: fprintf(fp,"%.*s",linelen,line);	/* no more than linelen chars */
 4084: if ( isnewline ) fprintf(fp,"\n");	/*caller wants terminating newline*/
 4085: return ( 1 );
 4086: } /* --- end-of-function emit_string() --- */
 4087: 
 4088: 
 4089: /* ==========================================================================
 4090:  * Function:	gftobitmap ( gf )
 4091:  * Purpose:	convert .gf-like pixmap to bitmap image
 4092:  * --------------------------------------------------------------------------
 4093:  * Arguments:	gf (I)		raster * to struct in .gf-format
 4094:  * --------------------------------------------------------------------------
 4095:  * Returns:	( raster * )	image-format raster * if successful,
 4096:  *				or NULL for any error.
 4097:  * --------------------------------------------------------------------------
 4098:  * Notes:     o
 4099:  * ======================================================================= */
 4100: /* --- entry point --- */
 4101: raster	*gftobitmap ( raster *gf )
 4102: {
 4103: /* -------------------------------------------------------------------------
 4104: Allocations and Declarations
 4105: -------------------------------------------------------------------------- */
 4106: raster	*new_raster(), *rp=NULL;	/* image raster retuned to caller */
 4107: int	width=0, height=0, totbits=0;	/* gf->width, gf->height, #bits */
 4108: int	format=0, icount=0, ncounts=0,	/*.gf format, count index, #counts*/
 4109: 	ibit=0, bitval=0;		/* bitmap index, bit value */
 4110: int	isrepeat = 1,			/* true to process repeat counts */
 4111: 	repeatcmds[2] = {255,15},	/*opcode for repeat/duplicate count*/
 4112: 	nrepeats=0, irepeat=0,		/* scan line repeat count,index */
 4113: 	wbits = 0;			/* count bits to width of scan line*/
 4114: /* -------------------------------------------------------------------------
 4115: initialization
 4116: -------------------------------------------------------------------------- */
 4117: /* --- check args --- */
 4118: if ( gf == NULL ) goto end_of_job;	/* input raster not provided */
 4119: format = gf->format;			/* 2 or 3 */
 4120: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
 4121: ncounts = gf->pixsz;			/*pixsz is really #counts in pixmap*/
 4122: /* --- allocate output raster with proper dimensions for bitmap --- */
 4123: width=gf->width;  height=gf->height;	/* dimensions of raster */
 4124: if ( (rp = new_raster(width,height,1))	/* allocate new raster and bitmap */
 4125: ==   NULL ) goto end_of_job;		/* quit if failed to allocate */
 4126: totbits = width*height;			/* total #bits in image */
 4127: /* -------------------------------------------------------------------------
 4128: fill bitmap
 4129: -------------------------------------------------------------------------- */
 4130: for ( icount=0,bitval=0; icount<ncounts; icount++ )
 4131:   {
 4132:   int	nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
 4133:   if ( isrepeat				/* we're proxessing repeat counts */
 4134:   &&   nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
 4135:    if ( nrepeats == 0 )			/* recursive repeat is error */
 4136:     { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
 4137:       nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
 4138:       icount += 2; }			/* bump byte/nibble count */
 4139:    else					/* some internal error occurred */
 4140:     if ( msgfp!=NULL && msglevel>=1 )	/* report error */
 4141:      fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
 4142:   if ( 0 )
 4143:     fprintf(stdout,
 4144:     "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
 4145:     icount,bitval,nbits,ibit,totbits);
 4146:   for ( ; nbits>0; nbits-- )		/* count down */
 4147:     { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
 4148:       for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
 4149:        if ( bitval == 1 )		/* set pixel */
 4150: 	{ setlongbit(rp->pixmap,(ibit+irepeat*width)); }
 4151:        else				/* clear pixel */
 4152: 	{ unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
 4153:       if ( nrepeats > 0 ) wbits++;	/* count another repeated bit */
 4154:       ibit++; }				/* bump bit index */
 4155:   bitval = 1-bitval;			/* flip bit value */
 4156:   if ( wbits >= width ) {		/* completed repeats */
 4157:    ibit += nrepeats*width;		/*bump bit count past repeated scans*/
 4158:    if ( wbits > width )			/* out-of alignment error */
 4159:     if ( msgfp!=NULL && msglevel>=1 )	/* report error */
 4160:      fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
 4161:    wbits = nrepeats = 0; }		/* reset repeat counts */
 4162:   } /* --- end-of-for(icount) --- */
 4163: end_of_job:
 4164:   return ( rp );			/* back to caller with image */
 4165: } /* --- end-of-function gftobitmap() --- */
 4166: 
 4167: 
 4168: /* ==========================================================================
 4169:  * Function:	get_symdef ( symbol )
 4170:  * Purpose:	returns mathchardef struct for symbol
 4171:  * --------------------------------------------------------------------------
 4172:  * Arguments:	symbol (I)	char *  containing symbol
 4173:  *				whose corresponding mathchardef is wanted
 4174:  * --------------------------------------------------------------------------
 4175:  * Returns:	( mathchardef * )  pointer to struct defining symbol,
 4176:  *				or NULL for any error
 4177:  * --------------------------------------------------------------------------
 4178:  * Notes:     o	Input symbol need only contain a leading substring to match,
 4179:  *		e.g., \gam passed in symbol will match \gamma in the table.
 4180:  *		If the table contains two or more possible matches,
 4181:  *		the shortest is returned, e.g., input \e will return with
 4182:  *		data for \eta rather than \epsilon.  To get \epsilon,
 4183:  *		you must pass a leading substring long enough to eliminate
 4184:  *		shorter table matches, i.e., in this case \ep
 4185:  * ======================================================================= */
 4186: /* --- entry point --- */
 4187: mathchardef *get_symdef ( char *symbol )
 4188: {
 4189: /* -------------------------------------------------------------------------
 4190: Allocations and Declarations
 4191: -------------------------------------------------------------------------- */
 4192: mathchardef *symdefs = symtable;	/* table of mathchardefs */
 4193: int	ligdef=0, get_ligature();	/* or we may have a ligature */
 4194: int	idef = 0,			/* symdefs[] index */
 4195: 	bestdef = (-9999);		/*index of shortest matching symdef*/
 4196: int	symlen = strlen(symbol),	/* length of input symbol */
 4197: 	deflen, minlen=9999;		/*length of shortest matching symdef*/
 4198: int	/*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
 4199: 	alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */
 4200: 	slashsym = (*symbol=='\\');	/* or \backslashed symbol */
 4201: int	family = fontinfo[fontnum].family; /* current font family */
 4202: static	char *displaysyms[][2] = {	/*xlate to Big sym for \displaystyle*/
 4203: 	/* --- see table on page 536 in TLC2 --- */
 4204: 	{"\\int",	"\\Bigint"},
 4205: 	{"\\oint",	"\\Bigoint"},
 4206: 	{"\\sum",	"\\Bigsum"},
 4207: 	{"\\prod",	"\\Bigprod"},
 4208: 	{"\\coprod",	"\\Bigcoprod"},
 4209: 	/* --- must be 'big' when related to similar binary operators --- */
 4210: 	{"\\bigcup",	"\\Bigcup"},
 4211: 	{"\\bigsqcup",	"\\Bigsqcup"},
 4212: 	{"\\bigcap",	"\\Bigcap"},
 4213: 	/*{"\\bigsqcap", "\\sqcap"},*/	/* don't have \Bigsqcap */
 4214: 	{"\\bigodot",	"\\Bigodot"},
 4215: 	{"\\bigoplus",	"\\Bigoplus"},
 4216: 	{"\\bigominus",	"\\ominus"},
 4217: 	{"\\bigotimes",	"\\Bigotimes"},
 4218: 	{"\\bigoslash",	"\\oslash"},
 4219: 	{"\\biguplus",	"\\Biguplus"},
 4220: 	{"\\bigwedge",	"\\Bigwedge"},
 4221: 	{"\\bigvee",	"\\Bigvee"},
 4222: 	{NULL, NULL} };
 4223: /* -------------------------------------------------------------------------
 4224: First check for ligature
 4225: -------------------------------------------------------------------------- */
 4226: isligature = 0;				/* init signal for no ligature */
 4227: if ( family == CYR10 )			/*only check for cyrillic ligatures*/
 4228:  if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
 4229:  >=    0  )				/* found a ligature */
 4230:   { bestdef = ligdef;			/* set bestdef for ligature */
 4231:     isligature = 1;			/* signal we found a ligature */
 4232:     goto end_of_job; }			/* so just give it to caller */
 4233: /* -------------------------------------------------------------------------
 4234: If in \displaystyle mode, first xlate int to Bigint, etc.
 4235: -------------------------------------------------------------------------- */
 4236: if ( isdisplaystyle > 1 )		/* we're in \displaystyle mode */
 4237:   for ( idef=0; ; idef++ ) {		/* lookup symbol in displaysyms */
 4238:     char *fromsym = displaysyms[idef][0], /* look for this symbol */
 4239: 	 *tosym = displaysyms[idef][1];	  /* and xlate it to this symbol */
 4240:     if ( fromsym == NULL ) break;	/* end-of-table */
 4241:     if ( !strcmp(symbol,fromsym) )	/* found a match */
 4242:       {	if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
 4243: 	 { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
 4244: 	   isdisplaystyle,symbol,tosym); fflush(msgfp); }
 4245: 	symbol = tosym;			/* so look up tosym instead */
 4246: 	symlen = strlen(symbol);	/* reset symbol length */
 4247: 	break; }			/* no need to search further */
 4248:     } /* --- end-of-for(idef) --- */
 4249: /* -------------------------------------------------------------------------
 4250: search symdefs[] in order for first occurrence of symbol
 4251: -------------------------------------------------------------------------- */
 4252: for ( idef=0; ;idef++ )			/* until trailer record found */
 4253:   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
 4254:   else					/* check against caller's symbol */
 4255:     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
 4256:      if ( (fontnum==0||family==CYR10)	/* mathmode, so check every match */
 4257:      || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
 4258:      || (1 && istextmode && slashsym)	/*text mode and \backslashed symbol*/
 4259:      || (0 && istextmode && (!alphasym	/* text mode and not alpha symbol */
 4260: 	|| symdefs[idef].handler!=NULL))   /* or text mode and directive */
 4261:      || (symdefs[idef].family==family	/* have correct family */
 4262: 	&& symdefs[idef].handler==NULL) )  /* and not a handler collision */
 4263: #if 0
 4264:      || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
 4265:      || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
 4266:      || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
 4267: 	&& symdefs[idef].handler==NULL)
 4268:      || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
 4269: 	&& symdefs[idef].handler==NULL) )
 4270: #endif
 4271:       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
 4272: 	{ bestdef = idef;		/* save index of new best match */
 4273: 	  if ( (minlen = deflen)	/* and save its len for next test */
 4274: 	  ==  symlen ) break; }		/*perfect match, so return with it*/
 4275: if ( bestdef < 0 )			/* failed to look up symbol */
 4276:   if ( fontnum != 0 )			/* we're in a restricted font mode */
 4277:     { int oldfontnum = fontnum;		/* save current font family */
 4278:       mathchardef *symdef = NULL;	/* lookup result with fontnum=0 */
 4279:       fontnum = 0;			/*try to look up symbol in any font*/
 4280:       symdef = get_symdef(symbol);	/* repeat lookup with fontnum=0 */
 4281:       fontnum = oldfontnum;		/* reset font family */
 4282:       return symdef; }			/* caller gets fontnum=0 lookup */
 4283: end_of_job:
 4284:  if ( msgfp!=NULL && msglevel>=999 )	/* debugging output */
 4285:   { fprintf(msgfp,
 4286:     "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
 4287:     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
 4288:     fflush(msgfp); }
 4289:  return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
 4290: } /* --- end-of-function get_symdef() --- */
 4291: 
 4292: 
 4293: /* ==========================================================================
 4294:  * Function:	get_ligature ( expression, family )
 4295:  * Purpose:	returns symtable[] index for ligature
 4296:  * --------------------------------------------------------------------------
 4297:  * Arguments:	expression (I)	char *  containing ligature
 4298:  *				whose corresponding mathchardef is wanted
 4299:  *		family (I)	int containing NOVALUE for any family,
 4300:  *				or, e.g., CYR10 for cyrillic, etc.
 4301:  * --------------------------------------------------------------------------
 4302:  * Returns:	( int ) 	symtable[] index defining ligature,
 4303:  *				or -9999 if no ligature found or for any error
 4304:  * --------------------------------------------------------------------------
 4305:  * Notes:     o
 4306:  * ======================================================================= */
 4307: /* --- entry point --- */
 4308: int	get_ligature ( char *expression, int family )
 4309: {
 4310: /* -------------------------------------------------------------------------
 4311: Allocations and Declarations
 4312: -------------------------------------------------------------------------- */
 4313: mathchardef *symdefs = symtable;	/* table of mathchardefs */
 4314: char	*ligature = expression /*- 1*/,	/* expression ptr */
 4315: 	*symbol = NULL;			/* symdefs[idef].symbol */
 4316: int	liglen = strlen(ligature);	/* #chars remaining in expression */
 4317: int	iscyrfam = (family==CYR10);	/* true for cyrillic families */
 4318: int	idef = 0,			/* symdefs[] index */
 4319: 	bestdef = (-9999),		/*index of longest matching symdef*/
 4320: 	maxlen=(-9999);			/*length of longest matching symdef*/
 4321: /* -------------------------------------------------------------------------
 4322: search symdefs[] in order for first occurrence of symbol
 4323: -------------------------------------------------------------------------- */
 4324: if ( !isstring ) {			/* no ligatures in "string" mode */
 4325:  for ( idef=0; ;idef++ )		/* until trailer record found */
 4326:   if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
 4327:   else {				/* check against caller's ligature */
 4328:     int symlen = strlen(symbol);	/* #chars in symbol */
 4329:     if ( ( symlen>1 || iscyrfam )	/*ligature >1 char long or cyrillic*/
 4330:     &&   symlen <= liglen		/* and enough remaining chars */
 4331:     &&   ( *symbol!='\\' || iscyrfam )	/* not escaped or cyrillic */
 4332:     &&   symdefs[idef].handler == NULL ) /* and not a handler */
 4333:      if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
 4334:       if ( family < 0			/* no family specifies */
 4335:       ||   symdefs[idef].family == family ) /* or have correct family */
 4336:        if ( symlen > maxlen )		/* new longest ligature */
 4337: 	{ bestdef = idef;		/* save index of new best match */
 4338: 	  maxlen = symlen; }		/* and save its len for next test */
 4339:     } /* --- end-of-if/else(symbol==NULL) --- */
 4340:  if ( msgfp!=NULL && msglevel>=999 )	/* debugging output */
 4341:   { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
 4342:     ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
 4343:     fflush(msgfp); }
 4344:  } /* --- end-of-if(!isstring) --- */
 4345: return ( bestdef );			/* -9999 or index of best symdef[] */
 4346: } /* --- end-of-function get_ligature --- */
 4347: 
 4348: 
 4349: /* ==========================================================================
 4350:  * Function:	get_chardef ( symdef, size )
 4351:  * Purpose:	returns chardef ptr containing data for symdef at given size
 4352:  * --------------------------------------------------------------------------
 4353:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol
 4354:  *				whose corresponding chardef is wanted
 4355:  *		size (I)	int containing 0-5 for desired size
 4356:  * --------------------------------------------------------------------------
 4357:  * Returns:	( chardef * )	pointer to struct defining symbol at size,
 4358:  *				or NULL for any error
 4359:  * --------------------------------------------------------------------------
 4360:  * Notes:     o	if size unavailable, the next-closer-to-normalsize
 4361:  *		is returned instead.
 4362:  * ======================================================================= */
 4363: /* --- entry point --- */
 4364: chardef	*get_chardef ( mathchardef *symdef, int size )
 4365: {
 4366: /* -------------------------------------------------------------------------
 4367: Allocations and Declarations
 4368: -------------------------------------------------------------------------- */
 4369: fontfamily  *fonts = fonttable;		/* table of font families */
 4370: chardef	**fontdef,			/*tables for desired font, by size*/
 4371: 	*gfdata = (chardef *)NULL;	/* chardef for symdef,size */
 4372: int	ifont;				/* fonts[] index */
 4373: int	family, charnum;		/* indexes retrieved from symdef */
 4374: int	sizeinc = 0,			/*+1 or -1 to get closer to normal*/
 4375: 	normalsize = 2;			/* this size always present */
 4376: int	isBig = 0;			/*true if symbol's 1st char is upper*/
 4377: char	*symptr = NULL;			/* look for 1st alpha of symbol */
 4378: /* -------------------------------------------------------------------------
 4379: initialization
 4380: -------------------------------------------------------------------------- */
 4381: /* --- check symdef --- */
 4382: if ( symdef == NULL ) goto end_of_job;	/* get_symdef() probably failed */
 4383: /* --- get local copy of indexes from symdef --- */
 4384: family = symdef->family;		/* font family containing symbol */
 4385: charnum = symdef->charnum;		/* char# of symbol within font */
 4386: /* --- check for supersampling --- */
 4387: if ( issupersampling )			/* check for supersampling fonts */
 4388:  if ( fonts != ssfonttable )		/* uh oh--probably internal error */
 4389:   { fonts = ssfonttable; }		/* force it */
 4390: /* --- check requested size, and set size increment --- */
 4391: if ( 0 && issupersampling )		/* set size index for supersampling */
 4392:   size = LARGESTSIZE+1;			/* index 1 past largest size */
 4393: else					/* low pass indexes 0...LARGESTSIZE */
 4394:   {
 4395:   if( size<0 ) size = 0;		/* size was definitely too small */
 4396:   if( size>LARGESTSIZE ) size = LARGESTSIZE;  /* or definitely too large */
 4397:   if( size<normalsize ) sizeinc = (+1);	/*use next larger if size too small*/
 4398:   if( size>normalsize ) sizeinc = (-1);	/*or next smaller if size too large*/
 4399:   }
 4400: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
 4401: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
 4402:   if ( isalpha(*symptr) )		/* found leading alpha char */
 4403:     { isBig = isupper(*symptr);		/* is 1st char of name uppercase? */
 4404:       if ( !isBig			/* 1st char lowercase */
 4405:       &&   strlen(symptr) >= 4 )	/* but followed by at least 3 chars */
 4406:        isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
 4407: 	|| !memcmp(symptr,"bigg",4);	/* or with bigg */
 4408:       break; }				/* don't check beyond 1st char */
 4409: /* -------------------------------------------------------------------------
 4410: find font family in table of fonts[]
 4411: -------------------------------------------------------------------------- */
 4412: /* --- look up font family --- */
 4413: for ( ifont=0; ;ifont++ )		/* until trailer record found */
 4414:   if ( fonts[ifont].family < 0 ) {	/* error, no such family */
 4415:     if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
 4416:      fprintf(msgfp,"get_chardef> failed to find font family %d\n",
 4417:      family); fflush(msgfp); }
 4418:     goto end_of_job; }			/* quit if can't find font family*/
 4419:   else if ( fonts[ifont].family == family ) break; /* found font family */
 4420: /* --- get local copy of table for this family by size --- */
 4421: fontdef = fonts[ifont].fontdef;		/* font by size */
 4422: /* -------------------------------------------------------------------------
 4423: get font in desired size, or closest available size, and return symbol
 4424: -------------------------------------------------------------------------- */
 4425: /* --- get font in desired size --- */
 4426: while ( 1 )				/* find size or closest available */
 4427:   if ( fontdef[size] != NULL ) break;	/* found available size */
 4428:   else					/* adjust size closer to normal */
 4429:     if ( size == NORMALSIZE		/* already normal so no more sizes,*/
 4430:     || sizeinc == 0 ) {			/* or must be supersampling */
 4431:       if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
 4432: 	fprintf(msgfp,"get_chardef> failed to find font size %d\n",
 4433: 	size); fflush(msgfp); }
 4434:       goto end_of_job; }		/* quit if can't find desired size */
 4435:     else				/*bump size 1 closer to NORMALSIZE*/
 4436:       size += sizeinc;			/* see if adjusted size available */
 4437: /* --- ptr to chardef struct --- */
 4438: gfdata = &((fontdef[size])[charnum]);	/*ptr to chardef for symbol in size*/
 4439: /* -------------------------------------------------------------------------
 4440: kludge to tweak CMEX10 (which appears to have incorrect descenders)
 4441: -------------------------------------------------------------------------- */
 4442: if ( family == CMEX10 )			/* cmex10 needs tweak */
 4443:   { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
 4444:     gfdata->botrow = (isBig? (-height/3) : (-height/4));
 4445:     gfdata->toprow = gfdata->botrow + gfdata->image.height; }
 4446: /* -------------------------------------------------------------------------
 4447: return subraster containing chardef data for symbol in requested size
 4448: -------------------------------------------------------------------------- */
 4449: end_of_job:
 4450:  if ( msgfp!=NULL && msglevel>=999 )
 4451:   { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
 4452:     else
 4453:      fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d  %s\n",
 4454:      symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
 4455:     fflush(msgfp); }
 4456:  return ( gfdata );			/*ptr to chardef for symbol in size*/
 4457: } /* --- end-of-function get_chardef() --- */
 4458: 
 4459: 
 4460: /* ==========================================================================
 4461:  * Function:	get_charsubraster ( symdef, size )
 4462:  * Purpose:	returns new subraster ptr containing
 4463:  *		data for symdef at given size
 4464:  * --------------------------------------------------------------------------
 4465:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol whose
 4466:  *				corresponding chardef subraster is wanted
 4467:  *		size (I)	int containing 0-5 for desired size
 4468:  * --------------------------------------------------------------------------
 4469:  * Returns:	( subraster * )	pointer to struct defining symbol at size,
 4470:  *				or NULL for any error
 4471:  * --------------------------------------------------------------------------
 4472:  * Notes:     o	just wraps a subraster envelope around get_chardef()
 4473:  * ======================================================================= */
 4474: /* --- entry point --- */
 4475: subraster *get_charsubraster ( mathchardef *symdef, int size )
 4476: {
 4477: /* -------------------------------------------------------------------------
 4478: Allocations and Declarations
 4479: -------------------------------------------------------------------------- */
 4480: chardef	*get_chardef(), *gfdata=NULL;	/* chardef struct for symdef,size */
 4481: int	get_baseline();			/* baseline of gfdata */
 4482: subraster *new_subraster(), *sp=NULL;	/* subraster containing gfdata */
 4483: raster	*bitmaprp=NULL, *gftobitmap();	/* convert .gf-format to bitmap */
 4484: int	delete_subraster();		/* in case gftobitmap() fails */
 4485: int	aasupsamp(),			/*antialias char with supersampling*/
 4486: 	grayscale=256;			/* aasupersamp() parameters */
 4487: /* -------------------------------------------------------------------------
 4488: look up chardef for symdef at size, and embed data (gfdata) in subraster
 4489: -------------------------------------------------------------------------- */
 4490: if ( (gfdata=get_chardef(symdef,size))	/* look up chardef for symdef,size */
 4491: !=   NULL )				/* and check that we found it */
 4492:  if ( (sp=new_subraster(0,0,0))		/* allocate subraster "envelope" */
 4493:  !=   NULL )				/* and check that we succeeded */
 4494:   {
 4495:   raster *image = &(gfdata->image);	/* ptr to chardef's bitmap or .gf */
 4496:   int format = image->format;		/* 1=bitmap, else .gf */
 4497:   sp->symdef = symdef;			/* replace NULL with caller's arg */
 4498:   sp->size = size;			/*replace default with caller's size*/
 4499:   sp->baseline = get_baseline(gfdata);	/* get baseline of character */
 4500:   if ( format == 1 )			/* already a bitmap */
 4501:    { sp->type = CHARASTER;		/* static char raster */
 4502:      sp->image = image; }		/* store ptr to its bitmap */
 4503:   else					/* need to convert .gf-to-bitmap */
 4504:    if ( (bitmaprp = gftobitmap(image))	/* convert */
 4505:    !=   (raster *)NULL )		/* successful */
 4506:     { sp->type = IMAGERASTER;		/* allocated raster will be freed */
 4507:       sp->image = bitmaprp; }		/* store ptr to converted bitmap */
 4508:    else					/* conversion failed */
 4509:     { delete_subraster(sp);		/* free unneeded subraster */
 4510:       sp = (subraster *)NULL;		/* signal error to caller */
 4511:       goto end_of_job; }		/* quit */
 4512:   if ( issupersampling )		/* antialias character right here */
 4513:     {
 4514:     raster *aa = NULL;			/* antialiased char raster */
 4515:     int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
 4516:     if ( status )			/* supersampled successfully */
 4517:       {	int baseline = sp->baseline;	/* baseline before supersampling */
 4518: 	int height = gfdata->image.height; /* #rows before supersampling */
 4519: 	sp->image = aa;			/* replace chardef with ss image */
 4520: 	if ( baseline >= height-1 )	/* baseline at bottom of char */
 4521: 	  sp->baseline = aa->height -1;	/* so keep it at bottom */
 4522: 	else				/* char has descenders */
 4523: 	  sp->baseline /= shrinkfactor;	/* rescale baseline */
 4524: 	sp->type = IMAGERASTER; }	/* character is an image raster */
 4525:     } /* --- end-of-if(issupersampling) --- */
 4526:   } /* --- end-of-if(sp!=NULL) --- */
 4527: end_of_job:
 4528:  if ( msgfp!=NULL && msglevel>=999 )
 4529:   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
 4530:     " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
 4531:     (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
 4532:     fflush(msgfp); }
 4533: return ( sp );				/* back to caller */
 4534: } /* --- end-of-function get_charsubraster() --- */
 4535: 
 4536: 
 4537: /* ==========================================================================
 4538:  * Function:	get_symsubraster ( symbol, size )
 4539:  * Purpose:	returns new subraster ptr containing
 4540:  *		data for symbol at given size
 4541:  * --------------------------------------------------------------------------
 4542:  * Arguments:	symbol (I)	char *  corresponding to symbol
 4543:  *				whose corresponding subraster is wanted
 4544:  *		size (I)	int containing 0-5 for desired size
 4545:  * --------------------------------------------------------------------------
 4546:  * Returns:	( subraster * )	pointer to struct defining symbol at size,
 4547:  *				or NULL for any error
 4548:  * --------------------------------------------------------------------------
 4549:  * Notes:     o	just combines get_symdef() and get_charsubraster()
 4550:  * ======================================================================= */
 4551: /* --- entry point --- */
 4552: subraster *get_symsubraster ( char *symbol, int size )
 4553: {
 4554: /* -------------------------------------------------------------------------
 4555: Allocations and Declarations
 4556: -------------------------------------------------------------------------- */
 4557: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
 4558: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
 4559: /* -------------------------------------------------------------------------
 4560: look up mathchardef for symbol
 4561: -------------------------------------------------------------------------- */
 4562: if ( symbol != NULL )			/* user supplied input symbol */
 4563:   symdef = get_symdef(symbol);		/*look up corresponding mathchardef*/
 4564: /* -------------------------------------------------------------------------
 4565: look up chardef for mathchardef and wrap a subraster structure around data
 4566: -------------------------------------------------------------------------- */
 4567: if ( symdef != NULL )			/* lookup succeeded */
 4568:   sp = get_charsubraster(symdef,size);	/* so get symbol data in subraster */
 4569: return ( sp );				/* back to caller with sp or NULL */
 4570: } /* --- end-of-function get_symsubraster() --- */
 4571: 
 4572: 
 4573: /* ==========================================================================
 4574:  * Function:	get_baseline ( gfdata )
 4575:  * Purpose:	returns baseline for a chardef struct
 4576:  * --------------------------------------------------------------------------
 4577:  * Arguments:	gfdata (I)	chardef *  containing chardef for symbol
 4578:  *				whose baseline is wanted
 4579:  * --------------------------------------------------------------------------
 4580:  * Returns:	( int )		baseline for symdef,
 4581:  *				or -1 for any error
 4582:  * --------------------------------------------------------------------------
 4583:  * Notes:     o	Unlike TeX, the top-left corners of our rasters are (0,0),
 4584:  *		with (row,col) increasing as you move down and right.
 4585:  *		Baselines are calculated with respect to this scheme,
 4586:  *		so 0 would mean the very top row is on the baseline
 4587:  *		and everything else descends below the baseline.
 4588:  * ======================================================================= */
 4589: /* --- entry point --- */
 4590: int	get_baseline ( chardef *gfdata )
 4591: {
 4592: /* -------------------------------------------------------------------------
 4593: Allocations and Declarations
 4594: -------------------------------------------------------------------------- */
 4595: int	/*toprow = gfdata->toprow,*/	/*TeX top row from .gf file info*/
 4596: 	botrow = gfdata->botrow,	/*TeX bottom row from .gf file info*/
 4597: 	height = gfdata->image.height;	/* #rows comprising symbol */
 4598: /* -------------------------------------------------------------------------
 4599: give caller baseline
 4600: -------------------------------------------------------------------------- */
 4601: return ( (height-1) + botrow );		/* note: descenders have botrow<0 */
 4602: } /* --- end-of-function get_baseline() --- */
 4603: 
 4604: 
 4605: /* ==========================================================================
 4606:  * Function:	get_delim ( char *symbol, int height, int family )
 4607:  * Purpose:	returns subraster corresponding to the samllest
 4608:  *		character containing symbol, but at least as large as height,
 4609:  *		and in caller's family (if specified).
 4610:  *		If no symbol character as large as height is available,
 4611:  *		then the largest availabale character is returned instead.
 4612:  * --------------------------------------------------------------------------
 4613:  * Arguments:	symbol (I)	char *  containing (substring of) desired
 4614:  *				symbol, e.g., if symbol="(", then any
 4615:  *				mathchardef like "(" or "\\(", etc, match.
 4616:  *		height (I)	int containing minimum acceptable height
 4617:  *				for returned character
 4618:  *		family (I)	int containing -1 to consider all families,
 4619:  *				or, e.g., CMEX10 for only that family
 4620:  * --------------------------------------------------------------------------
 4621:  * Returns:	( subraster * )	best matching character available,
 4622:  *				or NULL for any error
 4623:  * --------------------------------------------------------------------------
 4624:  * Notes:     o	If height is passed as negative, its absolute value is used
 4625:  *		but the best-fit width is searched for (rather than height)
 4626:  * ======================================================================= */
 4627: /* --- entry point --- */
 4628: subraster *get_delim ( char *symbol, int height, int family )
 4629: {
 4630: /* -------------------------------------------------------------------------
 4631: Allocations and Declarations
 4632: -------------------------------------------------------------------------- */
 4633: mathchardef *symdefs = symtable;	/* table of mathchardefs */
 4634: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
 4635: subraster *make_delim();		/* construct delim if can't find it*/
 4636: chardef	*get_chardef(), *gfdata=NULL;	/* get chardef struct for a symdef */
 4637: char	lcsymbol[256], *symptr,		/* lowercase symbol for comparison */
 4638: 	*unescsymbol = symbol;		/* unescaped symbol */
 4639: int	symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
 4640: 	deflen = 0;			/* length of symdef (aka lcsymbol) */
 4641: int	idef = 0,			/* symdefs[] index */
 4642: 	bestdef = (-9999),		/* index of best fit symdef */
 4643: 	bigdef = (-9999);		/*index of biggest (in case no best)*/
 4644: int	size = 0,			/* size index 0...LARGESTSIZE */
 4645: 	bestsize = (-9999),		/* index of best fit size */
 4646: 	bigsize = (-9999);		/*index of biggest (in case no best)*/
 4647: int	defheight, bestheight=9999,	/* height of best fit symdef */
 4648: 	bigheight = (-9999);		/*height of biggest(in case no best)*/
 4649: int	iswidth = 0;			/* true if best-fit width desired */
 4650: int	isunesc = 0,			/* true if leading escape removed */
 4651: 	issq=0, isoint=0;		/* true for \sqcup,etc, \oint,etc */
 4652: int	iscurly = 0;			/* true for StMary's curly symbols */
 4653: char	*bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
 4654: /* -------------------------------------------------------------------------
 4655: determine if searching height or width, and search symdefs[] for best-fit
 4656: -------------------------------------------------------------------------- */
 4657: /* --- arg checks --- */
 4658: if ( symlen < 1 ) return (sp);		/* no input symbol suplied */
 4659: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
 4660: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
 4661: /* --- ignore leading escapes for CMEX10 --- */
 4662: if ( 1 )				/* ignore leading escape */
 4663:  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
 4664:   if ( strstr(symbol,"sq") != NULL )	/* \sq symbol requested */
 4665:      issq = 1;				/* seq \sq signal */
 4666:   if ( strstr(symbol,"oint") != NULL )	/* \oint symbol requested */
 4667:      isoint = 1;			/* seq \oint signal */
 4668:   if ( *symbol=='\\' )			/* have leading \ */
 4669:    { unescsymbol = symbol+1;		/* push past leading \ */
 4670:      if ( --symlen < 1 ) return(sp);	/* one less char */
 4671:      if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
 4672:        unescsymbol = bigint;		/* but big version looks better */
 4673:      if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
 4674:        unescsymbol = bigoint;		/* but big version looks better */
 4675:      symlen = strlen(unescsymbol);	/* explicitly recalculate length */
 4676:      isunesc = 1; }			/* signal leading escape removed */
 4677:   } /* --- end-of-if(family) --- */
 4678: /* --- determine whether searching for best-fit height or width --- */
 4679: if ( height < 0 )			/* negative signals width search */
 4680:   { height = (-height);			/* flip "height" positive */
 4681:     iswidth = 1; }			/* set flag for width search */
 4682: /* --- search symdefs[] for best-fit height (or width) --- */
 4683: for ( idef=0; ;idef++ )			/* until trailer record found */
 4684:  {
 4685:  char *defsym = symdefs[idef].symbol;	/* local copies */
 4686:  int  deffam  = symdefs[idef].family;
 4687:  if ( defsym == NULL ) break;		/* reached end-of-table */
 4688:  else					/* check against caller's symbol */
 4689:   if ( family<0 || deffam == family	/* if explicitly in caller's family*/
 4690:   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
 4691:     {
 4692:     strcpy(lcsymbol,defsym);		/* local copy of symdefs[] symbol */
 4693:     if ( isunesc && *lcsymbol=='\\' )	/* ignored leading \ in symbol */
 4694:      {strsqueeze(lcsymbol,1);}		/*so squeeze it out of lcsymbol too*/
 4695:     if ( 0 )				/* don't ignore case */
 4696:      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ )/*for each symbol ch*/
 4697:       if ( isalpha(*symptr) ) *symptr=tolower(*symptr);/*lowercase the char*/
 4698:     deflen = strlen(lcsymbol);		/* #chars in symbol we're checking */
 4699:     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
 4700:      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
 4701:      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
 4702:       if ( ( deffam == CMSY10 ?		/* CMSY10 or not CMSY10 */
 4703: 	  symptr == lcsymbol		/* caller's sym is a prefix */
 4704:           && deflen == symlen:		/* and same length */
 4705: 	  (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
 4706: 	  (symptr == lcsymbol		/* caller's sym is a prefix */
 4707:           || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
 4708:        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
 4709: 	if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
 4710: 	  { defheight = gfdata->image.height;	/* height of this character */
 4711: 	    if ( iswidth )		/* width search wanted instead... */
 4712: 	      defheight = gfdata->image.width;	/* ...so substitute width */
 4713: 	    leftsymdef = &(symdefs[idef]);	/* set symbol class, etc */
 4714: 	    if ( defheight>=height && defheight<bestheight ) /*new best fit*/
 4715: 	      { bestdef=idef; bestsize=size;	/* save indexes of best fit */
 4716: 		bestheight = defheight; }	/* and save new best height */
 4717: 	    if ( defheight >= bigheight )	/* new biggest character */
 4718: 	      { bigdef=idef; bigsize=size;	/* save indexes of biggest */
 4719: 		bigheight = defheight; }	/* and save new big height */
 4720:           } /* --- end-of-if(gfdata!=NULL) --- */
 4721:     } /* --- end-of-if(family) --- */
 4722:  } /* --- end-of-for(idef) --- */
 4723: /* -------------------------------------------------------------------------
 4724: construct subraster for best fit character, and return it to caller
 4725: -------------------------------------------------------------------------- */
 4726: if ( bestdef >= 0 )			/* found a best fit for caller */
 4727:   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
 4728: if ( (sp==NULL && height-bigheight>5)	/* try to construct delim */
 4729: ||   bigdef < 0 )			/* delim not in font tables */
 4730:   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
 4731: if ( sp==NULL && bigdef>=0 )		/* just give biggest to caller */
 4732:   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
 4733: if ( msgfp!=NULL && msglevel>=99 )
 4734:     fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
 4735:     (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
 4736: return ( sp );
 4737: } /* --- end-of-function get_delim() --- */
 4738: 
 4739: 
 4740: /* ==========================================================================
 4741:  * Function:	make_delim ( char *symbol, int height )
 4742:  * Purpose:	constructs subraster corresponding to symbol
 4743:  *		exactly as large as height,
 4744:  * --------------------------------------------------------------------------
 4745:  * Arguments:	symbol (I)	char *  containing, e.g., if symbol="("
 4746:  *				for desired delimiter
 4747:  *		height (I)	int containing height
 4748:  *				for returned character
 4749:  * --------------------------------------------------------------------------
 4750:  * Returns:	( subraster * )	constructed delimiter
 4751:  *				or NULL for any error
 4752:  * --------------------------------------------------------------------------
 4753:  * Notes:     o	If height is passed as negative, its absolute value is used
 4754:  *		and interpreted as width (rather than height)
 4755:  * ======================================================================= */
 4756: /* --- entry point --- */
 4757: subraster *make_delim ( char *symbol, int height )
 4758: {
 4759: /* -------------------------------------------------------------------------
 4760: Allocations and Declarations
 4761: -------------------------------------------------------------------------- */
 4762: subraster *sp = (subraster *)NULL,	/* subraster returned to caller */
 4763: 	*new_subraster();		/* allocate subraster */
 4764: subraster *get_symsubraster(),		/* look up delim pieces in cmex10 */
 4765: 	*symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL,	/* pieces */
 4766: 	*topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL,	/* +filler */
 4767: 	*rastack(), *rastcat();		/* stack pieces, concat filler */
 4768: int	isdrawparen = 0;		/*1=draw paren, 0=build from pieces*/
 4769: raster	*rasp = (raster *)NULL;		/* sp->image */
 4770: int	isokay=0, delete_subraster();	/* set true if delimiter drawn ok */
 4771: int	pixsz = 1,			/* pixels are one bit each */
 4772: 	symsize = 0;			/* size arg for get_symsubraster() */
 4773: int	thickness = 1;			/* drawn lines are one pixel thick */
 4774: int	aspectratio = 8;		/* default height/width for parens */
 4775: int	iswidth = 0,			/*true if width specified by height*/
 4776: 	width = height;			/* #pixels width (e.g., of ellipse)*/
 4777: char	*lp=NULL,  *rp=NULL,		/* check symbol for left or right */
 4778: 	*lp2=NULL, *rp2=NULL,		/* synonym for lp,rp */
 4779: 	*lp3=NULL, *rp3=NULL,		/* synonym for lp,rp */
 4780: 	*lp4=NULL, *rp4=NULL;		/* synonym for lp,rp */
 4781: int	circle_raster(),		/* ellipse for ()'s in sp->image */
 4782: 	rule_rsater(),			/* horizontal or vertical lines */
 4783: 	line_raster();			/* line between two points */
 4784: subraster *uparrow_subraster();		/* up/down arrows */
 4785: int	isprealloc = 1;			/*pre-alloc subraster, except arrow*/
 4786: int	oldsmashmargin = smashmargin,	/* save original smashmargin */
 4787: 	wasnocatspace = isnocatspace;	/* save original isnocatspace */
 4788: /* -------------------------------------------------------------------------
 4789: initialization
 4790: -------------------------------------------------------------------------- */
 4791: /* --- determine whether constructing height or width --- */
 4792: if ( height < 0 )			/* negative "height" signals width */
 4793:   { width = height = (-height);		/* flip height positive */
 4794:     iswidth = 1; }			/* set flag for width */
 4795: if ( height < 3 ) goto end_of_job;	/* too small, must be error */
 4796: /* --- set default width (or height) accordingly --- */
 4797: if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
 4798: else            width = (height+(aspectratio+1)/2)/aspectratio;
 4799: if ( strchr(symbol,'=') != NULL		/* left or right || bracket wanted */
 4800: ||   strstr(symbol,"\\|") != NULL	/* same || in standard tex notation*/
 4801: ||   strstr(symbol,"dbl") != NULL )	/* semantic bracket with ||'s */
 4802:   width = max2(width,6);		/* need space between two |'s */
 4803: if ( width < 2 ) width=2;		/* set min width */
 4804: if ( strchr(symbol,'(') != NULL		/* if left ( */
 4805: ||   strchr(symbol,')') != NULL )	/* or right ) paren wanted */
 4806:   { width = (3*width)/2;		/* adjust width */
 4807:     if ( !isdrawparen ) isprealloc=0; }	/* don't prealloc if building */
 4808: if ( strchr(symbol,'/') != NULL		/* left / */
 4809: ||   strstr(symbol,"\\\\") != NULL	/* or \\ for right \ */
 4810: ||   strstr(symbol,"backsl") != NULL )	/* or \backslash for \ */
 4811:   width = max2(height/3,5);
 4812: if ( strstr(symbol,"arrow") != NULL )	/* arrow wanted */
 4813:   { width = min2(height/3,20);		/* adjust width */
 4814:     isprealloc = 0; }			/* don't preallocate subraster */
 4815: if ( strchr(symbol,'{') != NULL		/* if left { */
 4816: ||   strchr(symbol,'}') != NULL )	/* or right } brace wanted */
 4817:   { isprealloc = 0; }			/* don't preallocate */
 4818: /* --- allocate and initialize subraster for constructed delimiter --- */
 4819: if ( isprealloc )			/* pre-allocation wanted */
 4820:  { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 4821:    ==   NULL )  goto end_of_job;	/* quit if failed */
 4822:    /* --- initialize delimiter subraster parameters --- */
 4823:    sp->type = IMAGERASTER;		/* image */
 4824:    sp->symdef = NULL;			/* not applicable for image */
 4825:    sp->baseline = height/2 + 2;		/* is a little above center good? */
 4826:    sp->size = NORMALSIZE;		/* size (probably unneeded) */
 4827:    rasp = sp->image; }			/* pointer to image in subraster */
 4828: /* -------------------------------------------------------------------------
 4829: ( ) parens
 4830: -------------------------------------------------------------------------- */
 4831: if ( (lp=strchr(symbol,'(')) != NULL	/* left ( paren wanted */
 4832: ||   (rp=strchr(symbol,')')) != NULL )	/* right ) paren wanted */
 4833:   {
 4834:   if ( isdrawparen ) {			/* draw the paren */
 4835:    int	mywidth = min2(width,20);	/* max width for ()'s */
 4836:    circle_raster ( rasp,		/* embedded raster image */
 4837: 	0, 0,				/* row0,col0 are upper-left corner */
 4838: 	height-1, mywidth-1,		/* row1,col1 are lower-right */
 4839: 	thickness,			/* line thickness is 1 pixel */
 4840: 	(rp==NULL?"23":"41") );		/* "1234" quadrants to be drawn */
 4841:    isokay = 1; }			/* set flag */
 4842:   else {
 4843:    int	isleft = (lp!=NULL?1:0);	/* true for left, false for right */
 4844:    char	*parentop = (isleft?"\\leftparentop":"\\rightparentop"),
 4845: 	*parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
 4846: 	*parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
 4847:    int	baseht=0, barht=0,		/* height of base=top+bot, bar */
 4848: 	ibar=0, nbars=0;		/* bar index, #bars between top&bot*/
 4849:    int	largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
 4850: 	topfill=(isleft?0:0), botfill=(isleft?0:0),
 4851: 	barfill=(isleft?0:7);		/* alignment fillers */
 4852:    /* --- get pieces at largest size smaller than total height --- */
 4853:    for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
 4854:     {
 4855:     /* --- get pieces at current test size --- */
 4856:     isokay = 1;				/* check for all pieces */
 4857:     if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
 4858:     if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
 4859:     if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
 4860:     /* --- check sum of pieces against total desired height --- */
 4861:     if ( isokay ) {			/* all pieces retrieved */
 4862:       baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
 4863:       barht  = (symbar->image)->height;	/* bar height */
 4864:       if ( baseht < height+5 ) break;	/* largest base that's not too big */
 4865:       if ( symsize < 1 ) break;		/* or smallest available base */
 4866:       } /* --- end-of-if(isokay) --- */
 4867:     /* --- free test pieces that were too big --- */
 4868:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
 4869:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
 4870:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
 4871:     isokay = 0;				/* nothing available */
 4872:     if ( symsize < 1 ) break;		/* leave isokay=0 after smallest */
 4873:     } /* --- end-of-for(symsize) --- */
 4874:    /* --- construct brace from pieces --- */
 4875:    if ( isokay ) {			/* we have the pieces */
 4876:     /* --- add alignment fillers --- */
 4877:     smashmargin=0;  isnocatspace=99;	/*turn off rastcat smashing,space*/
 4878:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
 4879:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
 4880:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
 4881:     smashmargin = oldsmashmargin;	/* reset smashmargin */
 4882:     isnocatspace = wasnocatspace;	/* reset isnocatspace */
 4883:     /* --- #bars needed between top and bot --- */
 4884:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
 4885:     /* --- stack pieces --- */
 4886:     sp = topsym;			/* start with top piece */
 4887:     if ( nbars > 0 )			/* need nbars between top and bot */
 4888:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
 4889:     sp = rastack(botsym,sp,1,0,0,3);	/* bottom below bars or middle */
 4890:     delete_subraster(barsym);		/* barsym no longer needed */
 4891:     } /* --- end-of-if(isokay) --- */
 4892:    } /* --- end-of-if/else(isdrawparen) --- */
 4893:   } /* --- end-of-if(left- or right-() paren wanted) --- */
 4894: /* -------------------------------------------------------------------------
 4895: { } braces
 4896: -------------------------------------------------------------------------- */
 4897: else
 4898:  if ( (lp=strchr(symbol,'{')) != NULL	/* left { brace wanted */
 4899:  ||   (rp=strchr(symbol,'}')) != NULL )	/* right } brace wanted */
 4900:   {
 4901:   int	isleft = (lp!=NULL?1:0);	/* true for left, false for right */
 4902:   char	*bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
 4903: 	*bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
 4904: 	*bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
 4905: 	*bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
 4906:   int	baseht=0, barht=0,		/* height of base=top+bot+mid, bar */
 4907: 	ibar=0, nbars=0;		/* bar index, #bars above,below mid*/
 4908:   int	largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
 4909: 	topfill=(isleft?4:0), botfill=(isleft?4:0),
 4910: 	midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
 4911:   /* --- get pieces at largest size smaller than total height --- */
 4912:   for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
 4913:     {
 4914:     /* --- get pieces at current test size --- */
 4915:     isokay = 1;				/* check for all pieces */
 4916:     if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
 4917:     if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
 4918:     if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
 4919:     if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
 4920:     /* --- check sum of pieces against total desired height --- */
 4921:     if ( isokay ) {			/* all pieces retrieved */
 4922:       baseht = (symtop->image)->height + (symbot->image)->height
 4923: 	+ (symmid->image)->height;	/* top+bot+mid height */
 4924:       barht = (symbar->image)->height;	/* bar height */
 4925:       if ( baseht < height+5 ) break;	/* largest base that's not too big */
 4926:       if ( symsize < 1 ) break;		/* or smallest available base */
 4927:       } /* --- end-of-if(isokay) --- */
 4928:     /* --- free test pieces that were too big --- */
 4929:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
 4930:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
 4931:     if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
 4932:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
 4933:     isokay = 0;				/* nothing available */
 4934:     if ( symsize < 1 ) break;		/* leave isokay=0 after smallest */
 4935:     } /* --- end-of-for(symsize) --- */
 4936:   /* --- construct brace from pieces --- */
 4937:   if ( isokay ) {			/* we have the pieces */
 4938:     /* --- add alignment fillers --- */
 4939:     smashmargin=0;  isnocatspace=99;	/*turn off rastcat smashing,space*/
 4940:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
 4941:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
 4942:     midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
 4943:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
 4944:     smashmargin = oldsmashmargin;	/* reset smashmargin */
 4945:     isnocatspace = wasnocatspace;	/* reset isnocatspace */
 4946:     /* --- #bars needed on each side of mid piece --- */
 4947:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
 4948:     /* --- stack pieces --- */
 4949:     sp = topsym;			/* start with top piece */
 4950:     if ( nbars > 0 )			/* need nbars above middle */
 4951:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
 4952:     sp = rastack(midsym,sp,1,0,0,3);	/*mid after top or bars*/
 4953:     if ( nbars > 0 )			/* need nbars below middle */
 4954:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
 4955:     sp = rastack(botsym,sp,1,0,0,3);	/* bottom below bars or middle */
 4956:     delete_subraster(barsym);		/* barsym no longer needed */
 4957:     } /* --- end-of-if(isokay) --- */
 4958:   } /* --- end-of-if(left- or right-{} brace wanted) --- */
 4959: /* -------------------------------------------------------------------------
 4960: [ ] brackets
 4961: -------------------------------------------------------------------------- */
 4962: else
 4963:  if ( (lp=strchr(symbol,'[')) != NULL	/* left [ bracket wanted */
 4964:  ||   (rp=strchr(symbol,']')) != NULL	/* right ] bracket wanted */
 4965:  ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
 4966:  ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
 4967:  ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
 4968:  ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
 4969:  ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
 4970:  ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
 4971:   {
 4972:   /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
 4973:   int	mywidth = min2(width,12),	/* max width for horizontal bars */
 4974: 	wthick = 1;			/* thickness of top.bottom bars */
 4975:   thickness = (height<25?1:2);		/* set lines 1 or 2 pixels thick */
 4976:   if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
 4977:     wthick = thickness;			/* same thickness for top/bot bar */
 4978:   if ( lp3==NULL && rp3==NULL )		/* set top bar if floor not wanted */
 4979:     rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
 4980:   if ( lp2==NULL && rp2==NULL )		/* set bot bar if ceil not wanted */
 4981:     rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
 4982:   if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
 4983:    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
 4984:   if ( lp4 != NULL )			/* 2nd left vertical bar needed */
 4985:    rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
 4986:   if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
 4987:    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
 4988:   if ( rp4 != NULL )			/* 2nd right vertical bar needed */
 4989:    rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
 4990:   isokay = 1;				/* set flag */
 4991:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
 4992: /* -------------------------------------------------------------------------
 4993: < > brackets
 4994: -------------------------------------------------------------------------- */
 4995: else
 4996:  if ( (lp=strchr(symbol,'<')) != NULL	/* left < bracket wanted */
 4997:  ||   (rp=strchr(symbol,'>')) != NULL )	/* right > bracket wanted */
 4998:   {
 4999:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
 5000:   int	mywidth = min2(width,12),	/* max width for brackets */
 5001: 	mythick = 1;			/* all lines one pixel thick */
 5002:   thickness = (height<25?1:2);		/* set line pixel thickness */
 5003:   if ( lp != NULL )			/* left < bracket wanted */
 5004:     { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
 5005:       if ( thickness>1 )
 5006: 	line_raster(rasp,height/2,1,0,mywidth-1,mythick);
 5007:       line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
 5008:       if ( thickness>1 )
 5009: 	line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
 5010:   if ( rp != NULL )			/* right > bracket wanted */
 5011:     { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
 5012:       if ( thickness>1 )
 5013: 	line_raster(rasp,height/2,mywidth-2,0,0,mythick);
 5014:       line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
 5015:       if ( thickness>1 )
 5016: 	line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
 5017:   isokay = 1;				/* set flag */
 5018:   } /* --- end-of-if(left- or right-<> bracket wanted) --- */
 5019: /* -------------------------------------------------------------------------
 5020: / \ delimiters
 5021: -------------------------------------------------------------------------- */
 5022: else
 5023:  if ( (lp=strchr(symbol,'/')) != NULL	/* left /  wanted */
 5024:  ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
 5025:  ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
 5026:   {
 5027:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
 5028:   int	mywidth = width;		/* max width for / \ */
 5029:   thickness = 1;			/* set line pixel thickness */
 5030:   if ( lp != NULL )			/* left / wanted */
 5031:     line_raster(rasp,0,mywidth-1,height-1,0,thickness);
 5032:   if ( rp!=NULL || rp2!=NULL )		/* right \ wanted */
 5033:     line_raster(rasp,0,0,height-1,mywidth-1,thickness);
 5034:   isokay = 1;				/* set flag */
 5035:   } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
 5036: /* -------------------------------------------------------------------------
 5037: arrow delimiters
 5038: -------------------------------------------------------------------------- */
 5039: else
 5040:  if ( strstr(symbol,"arrow") != NULL )	/* arrow delimiter wanted */
 5041:   {
 5042:   /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
 5043:   int	mywidth = width;		/* max width for / \ */
 5044:   int	isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
 5045: 		|| strstr(symbol,"Down")!=NULL); /* or a Down */
 5046:   int	drctn = +1;			/* init for uparrow */
 5047:   if ( strstr(symbol,"down")!=NULL	/* down if we have down */
 5048:   ||   strstr(symbol,"Down")!=NULL )	/* or Down */
 5049:    { drctn = (-1);			/* reset direction to down */
 5050:      if ( strstr(symbol,"up")!=NULL	/* updown if we have up or Up */
 5051:      ||   strstr(symbol,"Up")!=NULL )	/* and down or Down */
 5052:       drctn = 0; }			/* reset direction to updown */
 5053:   sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
 5054:   if ( sp != NULL )
 5055:    { sp->type = IMAGERASTER;		/* image */
 5056:      sp->symdef = NULL;			/* not applicable for image */
 5057:      sp->baseline = height/2 + 2;	/* is a little above center good? */
 5058:      sp->size = NORMALSIZE;		/* size (probably unneeded) */
 5059:      isokay = 1; }			/* set flag */
 5060:   } /* --- end-of-if(arrow delimiter wanted) --- */
 5061: /* -------------------------------------------------------------------------
 5062: \- for | | brackets or \= for || || brackets
 5063: -------------------------------------------------------------------------- */
 5064: else
 5065:  if ( (lp=strchr(symbol,'-')) != NULL	/* left or right | bracket wanted */
 5066:  ||  (lp2=strchr(symbol,'|')) != NULL	/* synonym for | bracket */
 5067:  ||   (rp=strchr(symbol,'=')) != NULL	/* left or right || bracket wanted */
 5068:  || (rp2=strstr(symbol,"\\|"))!= NULL )	/* || in standard tex notation */
 5069:   {
 5070:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
 5071:   int	midcol = width/2;		/* middle col, left of mid if even */
 5072:   if ( rp  != NULL			/* left or right || bracket wanted */
 5073:   ||   rp2 != NULL )			/* or || in standard tex notation */
 5074:    { thickness = (height<75?1:2);	/* each | of || 1 or 2 pixels thick*/
 5075:      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
 5076:      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
 5077:   else					/*nb, lp2 spuriously set if rp2 set*/
 5078:    if ( lp  != NULL			/* left or right | bracket wanted */
 5079:    ||   lp2 != NULL )			/* ditto for synomym */
 5080:     { thickness = (height<75?1:2);	/* set | 1 or 2 pixels thick */
 5081:       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
 5082:   isokay = 1;				/* set flag */
 5083:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
 5084: /* -------------------------------------------------------------------------
 5085: back to caller
 5086: -------------------------------------------------------------------------- */
 5087: end_of_job:
 5088:   if ( msgfp!=NULL && msglevel>=99 )
 5089:     fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
 5090:     (symbol==NULL?"null":symbol),isokay);
 5091:   if ( !isokay )			/* don't have requested delimiter */
 5092:     { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
 5093:       sp = NULL; }			/* and signal error to caller */
 5094:   return ( sp );			/*back to caller with delim or NULL*/
 5095: } /* --- end-of-function make_delim() --- */
 5096: 
 5097: 
 5098: /* ==========================================================================
 5099:  * Function:	texchar ( expression, chartoken )
 5100:  * Purpose:	scans expression, returning either its first character,
 5101:  *		or the next \sequence if that first char is \,
 5102:  *		and a pointer to the first expression char past that.
 5103:  * --------------------------------------------------------------------------
 5104:  * Arguments:	expression (I)	char * to first char of null-terminated
 5105:  *				string containing valid LaTeX expression
 5106:  *				to be scanned
 5107:  *		chartoken (O)	char * to null-terminated string returning
 5108:  *				either the first (non-whitespace) character
 5109:  *				of expression if that char isn't \, or else
 5110:  *				the \ and everything following it up to
 5111:  *				the next non-alphabetic character (but at
 5112:  *				least one char following the \ even if
 5113:  *				it's non-alpha)
 5114:  * --------------------------------------------------------------------------
 5115:  * Returns:	( char * )	ptr to the first char of expression
 5116:  *				past returned chartoken,
 5117:  *				or NULL for any parsing error.
 5118:  * --------------------------------------------------------------------------
 5119:  * Notes:     o	Does *not* skip leading whitespace, but simply
 5120:  *		returns any whitespace character as the next character.
 5121:  * ======================================================================= */
 5122: /* --- entry point --- */
 5123: char	*texchar ( char *expression, char *chartoken )
 5124: {
 5125: /* -------------------------------------------------------------------------
 5126: Allocations and Declarations
 5127: -------------------------------------------------------------------------- */
 5128: int	esclen = 0,				/*length of escape sequence*/
 5129: 	maxesclen = 128;			/* max len of esc sequence */
 5130: char	*ptoken = chartoken;			/* ptr into chartoken */
 5131: int	iprefix = 0;				/* prefix index */
 5132: static	char *prefixes[] =			/*e.g., \big followed by ( */
 5133: 	{ /* "\\left", "\\right", */
 5134: 	  "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
 5135: 	  "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
 5136: 	  "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
 5137: static	char *starred[] =			/* may be followed by * */
 5138: 	{ "\\hspace",  "\\!",  NULL };
 5139: /* -------------------------------------------------------------------------
 5140: just return the next char if it's not \
 5141: -------------------------------------------------------------------------- */
 5142: /* --- error check for end-of-string --- */
 5143: *ptoken = '\000';				/* init in case of error */
 5144: if ( expression == NULL ) return(NULL);		/* nothing to scan */
 5145: if ( *expression == '\000' ) return(NULL);	/* nothing to scan */
 5146: /* --- always returning first character (either \ or some other char) --- */
 5147: *ptoken++ = *expression++;			/* here's first character */
 5148: /* --- if first char isn't \, then just return it to caller --- */
 5149: if ( !isthischar(*(expression-1),ESCAPE) )	/* not a \, so return char */
 5150:   { *ptoken = '\000';				/* add a null terminator */
 5151:     goto end_of_job; }				/* ptr past returned char */
 5152: if ( *expression == '\000' )			/* \ is very last char */
 5153:   { *chartoken = '\000';			/* flush bad trailing \ */
 5154:     return(NULL); }				/* and signal end-of-job */
 5155: /* -------------------------------------------------------------------------
 5156: we have an escape sequence, so return all alpha chars following \
 5157: -------------------------------------------------------------------------- */
 5158: /* --- accumulate chars until first non-alpha char found --- */
 5159: for ( ; isalpha(*expression); esclen++ )	/* till first non-alpha... */
 5160:   { if ( esclen < maxesclen-3 )			/* more room in chartoken */
 5161:       *ptoken++ = *expression;			/*copy alpha char, bump ptr*/
 5162:     expression++; }				/* bump expression ptr */
 5163: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
 5164: *ptoken = '\000';				/* set null for compare */
 5165: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
 5166:  if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
 5167:   { char nextchar[256];  int nextlen=0;		/* texchar after prefix */
 5168:     skipwhite(expression);			/* skip space after prefix*/
 5169:     expression = texchar(expression,nextchar);	/* get nextchar */
 5170:     if ( (nextlen = strlen(nextchar)) > 0 )	/* #chars in nextchar */
 5171:       { strcpy(ptoken,nextchar);		/* append nextchar */
 5172:         ptoken += strlen(nextchar);		/* point to null terminator*/
 5173:         esclen += strlen(nextchar); }		/* and bump escape length */
 5174:     break; }					/* stop checking prefixes */
 5175: /* --- every \ must be followed by at least one char, e.g., \[ --- */
 5176: if ( esclen < 1 )				/* \ followed by non-alpha */
 5177:   *ptoken++ = *expression++;			/*copy non-alpha, bump ptrs*/
 5178: *ptoken = '\000';				/* null-terminate token */
 5179: /* --- check for \hspace* or other starred commands --- */
 5180: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
 5181:  if ( strcmp(chartoken,starred[iprefix]) == 0 )	/* have an exact match */
 5182:   if ( *expression == '*' )			/* follows by a * */
 5183:    { *ptoken++ = *expression++;			/* copy * and bump ptr */
 5184:      *ptoken = '\000';				/* null-terminate token */
 5185:      break; }					/* stop checking */
 5186: /* --- respect spaces in text mode, except first space after \escape --- */
 5187: if ( esclen >= 1 ) {				/*only for alpha \sequences*/
 5188:   if ( istextmode )				/* in \rm or \it text mode */
 5189:    if ( isthischar(*expression,WHITEDELIM) )	/* delim follows \sequence */
 5190:     expression++; }				/* so flush delim */
 5191: /* --- back to caller --- */
 5192: end_of_job:
 5193:   if ( msgfp!=NULL && msglevel>=999 )
 5194:     { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
 5195:       fflush(msgfp); }
 5196:   return ( expression );			/*ptr to 1st non-alpha char*/
 5197: } /* --- end-of-function texchar() --- */
 5198: 
 5199: 
 5200: /* ==========================================================================
 5201:  * Function:	texsubexpr (expression,subexpr,maxsubsz,
 5202:  *		left,right,isescape,isdelim)
 5203:  * Purpose:	scans expression, returning everything between a balanced
 5204:  *		left{...right} subexpression if the first non-whitespace
 5205:  *		char of expression is an (escaped or unescaped) left{,
 5206:  *		or just the next texchar() otherwise,
 5207:  *		and a pointer to the first expression char past that.
 5208:  * --------------------------------------------------------------------------
 5209:  * Arguments:	expression (I)	char * to first char of null-terminated
 5210:  *				string containing valid LaTeX expression
 5211:  *				to be scanned
 5212:  *		subexpr (O)	char * to null-terminated string returning
 5213:  *				either everything between a balanced {...}
 5214:  *				subexpression if the first char is {,
 5215:  *				or the next texchar() otherwise.
 5216:  *		maxsubsz (I)	int containing max #bytes returned
 5217:  *				in subexpr buffer (0 means unlimited)
 5218:  *		left (I)	char * specifying allowable left delimiters
 5219:  *				that begin subexpression, e.g., "{[(<"
 5220:  *		right (I)	char * specifying matching right delimiters
 5221:  *				in the same order as left, e.g., "}])>"
 5222:  *		isescape (I)	int controlling whether escaped and/or
 5223:  *				unescaped left,right are matched;
 5224:  *				see isbrace() comments below for details.
 5225:  *		isdelim (I)	int containing true (non-zero) to return
 5226:  *				the leading left and trailing right delims
 5227:  *				(if any were found) along with subexpr,
 5228:  *				or containing false=0 to return subexpr
 5229:  *				without its delimiters
 5230:  * --------------------------------------------------------------------------
 5231:  * Returns:	( char * )	ptr to the first char of expression
 5232:  *				past returned subexpr (see Notes),
 5233:  *				or NULL for any parsing error.
 5234:  * --------------------------------------------------------------------------
 5235:  * Notes:     o	If subexpr is of the form left{...right},
 5236:  *		the outer {}'s are returned as part of subexpr
 5237:  *		if isdelim is true; if isdelim is false the {}'s aren't
 5238:  *		returned.  In either case the returned pointer is
 5239:  *		*always* bumped past the closing right}, even if
 5240:  *		that closing right} isn't returned in subexpr.
 5241:  *	      o	If subexpr is not of the form left{...right},
 5242:  *		the returned pointer is on the character immediately
 5243:  *		following the last character returned in subexpr
 5244:  *	      o	\. acts as LaTeX \right. and matches any \left(
 5245:  *		And it also acts as a LaTeX \left. and matches any \right)
 5246:  * ======================================================================= */
 5247: /* --- entry point --- */
 5248: char	*texsubexpr ( char *expression, char *subexpr, int maxsubsz,
 5249: 	char *left, char *right, int isescape, int isdelim )
 5250: {
 5251: /* -------------------------------------------------------------------------
 5252: Allocations and Declarations
 5253: -------------------------------------------------------------------------- */
 5254: char	*texchar();		/*next char (or \sequence) from expression*/
 5255: char	*leftptr, leftdelim[256] = "(\000", /* left( found in expression */
 5256: 	rightdelim[256] = ")\000"; /* and matching right) */
 5257: char	*origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
 5258: char	*strtexchr(), *texleft(); /* check for \left, and get it */
 5259: int	gotescape = 0,		/* true if leading char of expression is \ */
 5260: 	prevescape = 0;		/* while parsing, true if preceding char \ */
 5261: int	isbrace();		/* check for left,right braces */
 5262: int	isanyright = 1;		/* true matches any right with left, (...] */
 5263: int	isleftdot = 0;		/* true if left brace is a \. */
 5264: int	nestlevel = 1;		/* current # of nested braces */
 5265: int	subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
 5266: /* -------------------------------------------------------------------------
 5267: skip leading whitespace and just return the next char if it's not {
 5268: -------------------------------------------------------------------------- */
 5269: /* --- skip leading whitespace and error check for end-of-string --- */
 5270: *subexpr = '\000';				/* init in case of error */
 5271: if ( expression == NULL ) return(NULL);		/*can't dereference null ptr*/
 5272: skipwhite(expression);				/* leading whitespace gone */
 5273: if ( *expression == '\000' ) return(NULL);	/* nothing left to scan */
 5274: /* --- set maxsubsz --- */
 5275: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2;	/* input 0 means unlimited */
 5276: /* --- check for escape --- */
 5277: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
 5278:   gotescape = 1;				/* so set flag accordingly */
 5279: /* --- check for \left...\right --- */
 5280: if ( gotescape )				/* begins with \ */
 5281:  if ( memcmp(expression+1,"left",4) )		/* and followed by left */
 5282:   if ( strchr(left,'l') != NULL )		/* caller wants \left's */
 5283:    if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
 5284:     { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
 5285: 	(isdelim?NULL:leftdelim),rightdelim);
 5286:       if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
 5287:       return ( pright );			/*back to caller past \right*/
 5288:     } /* --- end-of-if(expression=="\\left") --- */
 5289: /* --- if first char isn't left{ or script, just return it to caller --- */
 5290: if ( !isbrace(expression,left,isescape) ) {	/* not a left{ */
 5291:   if ( !isthischar(*expression,SCRIPTS) )	/* and not a script */
 5292:     return ( texchar(expression,subexpr) );	/* next char to caller */
 5293:   else /* --- kludge for super/subscripts to accommodate texscripts() --- */
 5294:     { *subexpr++ = *expression;			/* signal script */
 5295:       *subexpr = '\000';			/* null-terminate subexpr */
 5296:       return ( expression ); } }		/* leave script in stream */
 5297: /* --- extract left and find matching right delimiter --- */
 5298: *leftdelim  = *(expression+gotescape);		/* the left( in expression */
 5299: if ( (gotescape && *leftdelim == '.')		/* we have a left \. */
 5300: ||   (gotescape && isanyright) )		/*or are matching any right*/
 5301:   { isleftdot = 1;				/* so just set flag */
 5302:     *leftdelim = '\000'; }			/* and reset leftdelim */
 5303: else						/* find matching \right */
 5304:   if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
 5305:     *rightdelim = right[(int)(leftptr-left)];	/* get the matching right) */
 5306:   else						/* can't happen -- pgm bug */
 5307:     return ( NULL );				/*just signal eoj to caller*/
 5308: /* -------------------------------------------------------------------------
 5309: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
 5310: -------------------------------------------------------------------------- */
 5311: /* --- first initialize by bumping past left{ or \{ --- */
 5312: if ( isdelim )   *subexpr++ = *expression++;	/*caller wants { in subexpr*/
 5313:   else expression++;				/* always bump past left{ */
 5314: if ( gotescape ) {				/*need to bump another char*/
 5315:   if ( isdelim ) *subexpr++ = *expression++;	/* caller wants char, too */
 5316:   else expression++; }				/* else just bump past it */
 5317: /* --- set maximum size for numerical arguments --- */
 5318: if ( 0 )					/* check turned on or off? */
 5319:  if ( !isescape && !isdelim )			/*looking for numerical arg*/
 5320:   maxsubsz = 96;				/* set max arg size */
 5321: /* --- search for matching right} --- */
 5322: while ( 1 )					/*until balanced right} */
 5323:   {
 5324:   /* --- error check for end-of-string --- */
 5325:   if ( *expression == '\000' )			/* premature end-of-string */
 5326:     { if ( 0 && (!isescape && !isdelim) )	/*looking for numerical arg,*/
 5327: 	{ expression = origexpression;		/* so end-of-string is error*/
 5328: 	  subexpr = origsubexpr; }		/* so reset all ptrs */
 5329:       if ( isdelim ) {				/* generate fake right */
 5330: 	if ( gotescape )			/* need escaped right */
 5331: 	  { *subexpr++ = '\\';			/* set escape char */
 5332: 	    *subexpr++ = '.'; }			/* and fake \right. */
 5333: 	else					/* escape not wanted */
 5334: 	    *subexpr++ = *rightdelim; }		/* so fake actual right */
 5335:       *subexpr = '\000';			/* null-terminate subexpr */
 5336:       return ( expression ); }			/* back with final token */
 5337:   /* --- check preceding char for escape --- */
 5338:   if ( isthischar(*(expression-1),ESCAPE) )	/* previous char was \ */
 5339: 	prevescape = 1-prevescape;		/* so flip escape flag */
 5340:   else	prevescape = 0;				/* or turn flag off */
 5341:   /* --- check for { and } (un/escaped as per leading left) --- */
 5342:   if ( gotescape == prevescape )		/* escaped iff leading is */
 5343:     { /* --- check for (closing) right delim and see if we're done --- */
 5344:       if ( isthischar(*expression,rightdelim)	/* found a right} */
 5345:       ||   (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
 5346:       ||   (prevescape && isthischar(*expression,".")) ) /*or found \right. */
 5347:         if ( --nestlevel < 1 )			/*\right balances 1st \left*/
 5348: 	  { if ( isdelim ) 			/*caller wants } in subexpr*/
 5349: 	      *subexpr++ = *expression;		/* so end subexpr with } */
 5350: 	    else				/*check for \ before right}*/
 5351: 	      if ( prevescape )			/* have unwanted \ */
 5352: 		*(subexpr-1) = '\000';		/* so replace it with null */
 5353: 	    *subexpr = '\000';			/* null-terminate subexpr */
 5354: 	    return ( expression+1 ); }		/* back with char after } */
 5355:       /* --- check for (another) left{ --- */
 5356:       if ( isthischar(*expression,leftdelim)	/* found another left{ */
 5357:       ||   (isleftdot && isthischar(*expression,left)) ) /* any left{ */
 5358: 	nestlevel++;
 5359:     } /* --- end-of-if(gotescape==prevescape) --- */
 5360:   /* --- not done, so copy char to subexpr and continue with next char --- */
 5361:   if ( ++subsz < maxsubsz-5 )			/* more room in subexpr */
 5362:     *subexpr++ = *expression;			/* so copy char and bump ptr*/
 5363:   expression++;					/* bump expression ptr */
 5364:   } /* --- end-of-while(1) --- */
 5365: } /* --- end-of-function texsubexpr() --- */
 5366: 
 5367: 
 5368: /* ==========================================================================
 5369:  * Function:	texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
 5370:  * Purpose:	scans expression, starting after opening \left,
 5371:  *		and returning ptr after matching closing \right.
 5372:  *		Everything between is returned in subexpr, if given.
 5373:  *		Likewise, if given, ldelim returns delimiter after \left
 5374:  *		and rdelim returns delimiter after \right.
 5375:  *		If ldelim is given, the returned subexpr doesn't include it.
 5376:  *		If rdelim is given, the returned pointer is after that delim.
 5377:  * --------------------------------------------------------------------------
 5378:  * Arguments:	expression (I)	char * to first char of null-terminated
 5379:  *				string immediately following opening \left
 5380:  *		subexpr (O)	char * to null-terminated string returning
 5381:  *				either everything between balanced
 5382:  *				\left ... \right.  If leftdelim given,
 5383:  *				subexpr does _not_ contain that delimiter.
 5384:  *		maxsubsz (I)	int containing max #bytes returned
 5385:  *				in subexpr buffer (0 means unlimited)
 5386:  *		ldelim (O)	char * returning delimiter following
 5387:  *				opening \left
 5388:  *		rdelim (O)	char * returning delimiter following
 5389:  *				closing \right
 5390:  * --------------------------------------------------------------------------
 5391:  * Returns:	( char * )	ptr to the first char of expression
 5392:  *				past closing \right, or past closing
 5393:  *				right delimiter if rdelim!=NULL,
 5394:  *				or NULL for any error.
 5395:  * --------------------------------------------------------------------------
 5396:  * Notes:     o
 5397:  * ======================================================================= */
 5398: /* --- entry point --- */
 5399: char	*texleft ( char *expression, char *subexpr, int maxsubsz,
 5400: 	char *ldelim, char *rdelim )
 5401: {
 5402: /* -------------------------------------------------------------------------
 5403: Allocations and Declarations
 5404: -------------------------------------------------------------------------- */
 5405: char	*texchar(),			/* get delims after \left,\right */
 5406: 	*strtexchr(), *pright=expression; /* locate matching \right */
 5407: static	char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
 5408: int	sublen = 0;			/* #chars between \left...\right */
 5409: /* -------------------------------------------------------------------------
 5410: initialization
 5411: -------------------------------------------------------------------------- */
 5412: /* --- init output --- */
 5413: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
 5414: if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
 5415: if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
 5416: /* --- check args --- */
 5417: if ( expression == NULL ) goto end_of_job; /* no input supplied */
 5418: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
 5419: /* --- determine left delimiter  --- */
 5420: if ( ldelim != NULL )			/* caller wants left delim */
 5421:  { skipwhite(expression);		/* interpret \left ( as \left( */
 5422:    expression = texchar(expression,ldelim); } /*delim from expression*/
 5423: /* -------------------------------------------------------------------------
 5424: locate \right balancing opening \left
 5425: -------------------------------------------------------------------------- */
 5426: /* --- first \right following \left --- */
 5427: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
 5428: !=   NULL ) {				/* found it */
 5429:  /* --- find matching \right by pushing past any nested \left's --- */
 5430:  char *pleft = expression;		/* start after first \left( */
 5431:  while ( 1 ) {				/*break when matching \right found*/
 5432:   /* -- locate next nested \left if there is one --- */
 5433:   if ( (pleft=strtexchr(pleft,left))	/* find next \left */
 5434:   ==   NULL ) break;			/*no more, so matching \right found*/
 5435:   pleft += strlen(left);		/* push ptr past \left token */
 5436:   if ( pleft >= pright ) break;		/* not nested if \left after \right*/
 5437:   /* --- have nested \left, so push forward to next \right --- */
 5438:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
 5439:   ==   NULL ) break;			/* ran out of \right's */
 5440:   } /* --- end-of-while(1) --- */
 5441:  } /* --- end-of-if(pright!=NULL) --- */
 5442: /* --- set subexpression length, push pright past \right --- */
 5443: if ( pright != (char *)NULL )		/* found matching \right */
 5444:  { sublen = (int)(pright-expression);	/* #chars between \left...\right */
 5445:    pright += strlen(right); }		/* so push pright past \right */
 5446: /* -------------------------------------------------------------------------
 5447: get rightdelim and subexpr between \left...\right
 5448: -------------------------------------------------------------------------- */
 5449: /* --- get delimiter following \right --- */
 5450: if ( rdelim != NULL ) {			/* caller wants right delim */
 5451:  if ( pright == (char *)NULL )		/* assume \right. at end of exprssn*/
 5452:   { strcpy(rdelim,".");			/* set default \right. */
 5453:     sublen = strlen(expression);	/* use entire remaining expression */
 5454:     pright = expression + sublen; }	/* and push pright to end-of-string*/
 5455:  else					/* have explicit matching \right */
 5456:   { skipwhite(pright);			/* interpret \right ) as \right) */
 5457:     pright = texchar(pright,rdelim);	/* pull delim from expression */
 5458:     if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
 5459: /* --- get subexpression between \left...\right --- */
 5460: if ( sublen > 0 )			/* have subexpr */
 5461:  if ( subexpr != NULL ) {		/* and caller wants it */
 5462:   if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
 5463:   memcpy(subexpr,expression,sublen);	/* stuff between \left...\right */
 5464:   subexpr[sublen] = '\000'; }		/* null-terminate subexpr */
 5465: end_of_job:
 5466:   if ( msglevel>=99 && msgfp!=NULL )
 5467:     { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
 5468:       (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
 5469:       (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
 5470:   return ( pright );
 5471: } /* --- end-of-function texleft --- */
 5472: 
 5473: 
 5474: /* ==========================================================================
 5475:  * Function:	texscripts ( expression, subscript, superscript, which )
 5476:  * Purpose:	scans expression, returning subscript and/or superscript
 5477:  *		if expression is of the form _x^y or ^{x}_{y},
 5478:  *		or any (valid LaTeX) permutation of the above,
 5479:  *		and a pointer to the first expression char past "scripts"
 5480:  * --------------------------------------------------------------------------
 5481:  * Arguments:	expression (I)	char * to first char of null-terminated
 5482:  *				string containing valid LaTeX expression
 5483:  *				to be scanned
 5484:  *		subscript (O)	char * to null-terminated string returning
 5485:  *				subscript (without _), if found, or "\000"
 5486:  *		superscript (O)	char * to null-terminated string returning
 5487:  *				superscript (without ^), if found, or "\000"
 5488:  *		which (I)	int containing 1 for subscript only,
 5489:  *				2 for superscript only, >=3 for either/both
 5490:  * --------------------------------------------------------------------------
 5491:  * Returns:	( char * )	ptr to the first char of expression
 5492:  *				past returned "scripts" (unchanged
 5493:  *				except for skipped whitespace if
 5494:  *				neither subscript nor superscript found),
 5495:  *				or NULL for any parsing error.
 5496:  * --------------------------------------------------------------------------
 5497:  * Notes:     o	an input expression like ^a^b_c will return superscript="b",
 5498:  *		i.e., totally ignoring all but the last "script" encountered
 5499:  * ======================================================================= */
 5500: /* --- entry point --- */
 5501: char	*texscripts ( char *expression, char *subscript,
 5502: 			char *superscript, int which )
 5503: {
 5504: /* -------------------------------------------------------------------------
 5505: Allocations and Declarations
 5506: -------------------------------------------------------------------------- */
 5507: char	*texsubexpr();		/* next subexpression from expression */
 5508: int	gotsub=0, gotsup=0;	/* check that we don't eat, e.g., x_1_2 */
 5509: /* -------------------------------------------------------------------------
 5510: init "scripts"
 5511: -------------------------------------------------------------------------- */
 5512: if ( subscript != NULL ) *subscript = '\000';	/*init in case no subscript*/
 5513: if ( superscript!=NULL ) *superscript = '\000';	/*init in case no super*/
 5514: /* -------------------------------------------------------------------------
 5515: get subscript and/or superscript from expression
 5516: -------------------------------------------------------------------------- */
 5517: while ( expression != NULL ) {
 5518:   skipwhite(expression);			/* leading whitespace gone */
 5519:   if ( *expression == '\000' ) return(expression); /* nothing left to scan */
 5520:   if ( isthischar(*expression,SUBSCRIPT)	/* found _ */
 5521:   &&   (which==1 || which>2 ) )			/* and caller wants it */
 5522:     { if ( gotsub				/* found 2nd subscript */
 5523:       ||   subscript == NULL ) break;		/* or no subscript buffer */
 5524:       gotsub = 1;				/* set subscript flag */
 5525:       expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
 5526:   else						/* no _, check for ^ */
 5527:     if ( isthischar(*expression,SUPERSCRIPT)	/* found ^ */
 5528:     &&   which>=2  )				/* and caller wants it */
 5529:       {	if ( gotsup				/* found 2nd superscript */
 5530: 	||   superscript == NULL ) break;	/* or no superscript buffer*/
 5531: 	gotsup = 1;				/* set superscript flag */
 5532: 	expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
 5533:     else					/* neither _ nor ^ */
 5534:       return ( expression );			/*return ptr past "scripts"*/
 5535:   } /* --- end-of-while(expression!=NULL) --- */
 5536: return ( expression );
 5537: } /* --- end-of-function texscripts() --- */
 5538: 
 5539: 
 5540: /* ==========================================================================
 5541:  * Function:	isbrace ( expression, braces, isescape )
 5542:  * Purpose:	checks leading char(s) of expression for a brace,
 5543:  *		either escaped or unescaped depending on isescape,
 5544:  *		except that { and } are always matched, if they're
 5545:  *		in braces, regardless of isescape.
 5546:  * --------------------------------------------------------------------------
 5547:  * Arguments:	expression (I)	char * to first char of null-terminated
 5548:  *				string containing a valid LaTeX expression
 5549:  *				whose leading char(s) are checked for braces
 5550:  *				that begin subexpression, e.g., "{[(<"
 5551:  *		braces (I)	char * specifying matching brace delimiters
 5552:  *				to be checked for, e.g., "{[(<" or "}])>"
 5553:  *		isescape (I)	int containing 0 to match only unescaped
 5554:  *				braces, e.g., (...) or {...}, etc,
 5555:  *				or containing 1 to match only escaped
 5556:  *				braces, e.g., \(...\) or \[...\], etc,
 5557:  *				or containing 2 to match either.
 5558:  *				But note: if {,} are in braces
 5559:  *				then they're *always* matched whether
 5560:  *				escaped or not, regardless of isescape.
 5561:  * --------------------------------------------------------------------------
 5562:  * Returns:	( int )		1 if the leading char(s) of expression
 5563:  *				is a brace, or 0 if not.
 5564:  * --------------------------------------------------------------------------
 5565:  * Notes:     o
 5566:  * ======================================================================= */
 5567: /* --- entry point --- */
 5568: int	isbrace ( char *expression, char *braces, int isescape )
 5569: {
 5570: /* -------------------------------------------------------------------------
 5571: Allocations and Declarations
 5572: -------------------------------------------------------------------------- */
 5573: int	gotescape = 0,		/* true if leading char is an escape */
 5574: 	gotbrace = 0;		/*true if first non-escape char is a brace*/
 5575: /* -------------------------------------------------------------------------
 5576: check for brace
 5577: -------------------------------------------------------------------------- */
 5578: /* --- first check for end-of-string or \= ligature --- */
 5579: if ( *expression == '\000'			/* nothing to check */
 5580: ||   isligature ) goto end_of_job;		/* have a \= ligature */
 5581: /* --- check leading char for escape --- */
 5582: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
 5583:   { gotescape = 1;				/* so set flag accordingly */
 5584:     expression++; }				/* and bump past escape */
 5585: /* --- check (maybe next char) for brace --- */
 5586: if ( isthischar(*expression,braces) )		/* expression is braced */
 5587:   gotbrace = 1;					/* so set flag accordingly */
 5588: if ( gotescape && *expression == '.' )		/* \. matches any brace */
 5589:   gotbrace = 1;					/* set flag */
 5590: /* --- check for TeX brace { or } --- */
 5591: if ( gotbrace && isthischar(*expression,"{}") )	/*expression has TeX brace*/
 5592:   if ( isescape ) isescape = 2;			/* reset escape flag */
 5593: /* -------------------------------------------------------------------------
 5594: back to caller
 5595: -------------------------------------------------------------------------- */
 5596: end_of_job:
 5597:  if ( msglevel>=999 && msgfp!=NULL )
 5598:   { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
 5599:     expression,gotbrace,isligature); fflush(msgfp); }
 5600:  if ( gotbrace &&				/* found a brace */
 5601:      ( isescape==2 ||				/* escape irrelevant */
 5602:        gotescape==isescape )			/* un/escaped as requested */
 5603:    ) return ( 1 );  return ( 0 );		/* return 1,0 accordingly */
 5604: } /* --- end-of-function isbrace() --- */
 5605: 
 5606: 
 5607: /* ==========================================================================
 5608:  * Function:	preamble ( expression, size, subexpr )
 5609:  * Purpose:	parses $-terminated preamble, if present, at beginning
 5610:  *		of expression, re-setting size if necessary, and
 5611:  *		returning any other parameters besides size in subexpr.
 5612:  * --------------------------------------------------------------------------
 5613:  * Arguments:	expression (I)	char * to first char of null-terminated
 5614:  *				string containing LaTeX expression possibly
 5615:  *				preceded by $-terminated preamble
 5616:  *		size (I/O)	int *  containing 0-4 default font size,
 5617:  *				and returning size modified by first
 5618:  *				preamble parameter (or unchanged)
 5619:  *		subexpr(O)	char *  returning any remaining preamble
 5620:  *				parameters past size
 5621:  * --------------------------------------------------------------------------
 5622:  * Returns:	( char * )	ptr to first char past preamble in expression
 5623:  *				or NULL for any parsing error.
 5624:  * --------------------------------------------------------------------------
 5625:  * Notes:     o	size can be any number >=0. If preceded by + or -, it's
 5626:  *		interpreted as an increment to input size; otherwise
 5627:  *		it's interpreted as the size.
 5628:  *	      o	if subexpr is passed as NULL ptr, then returned expression
 5629:  *		ptr will have "flushed" and preamble parameters after size
 5630:  * ======================================================================= */
 5631: /* --- entry point --- */
 5632: char	*preamble ( char *expression, int *size, char *subexpr )
 5633: {
 5634: /* -------------------------------------------------------------------------
 5635: Allocations and Declarations
 5636: -------------------------------------------------------------------------- */
 5637: char	pretext[512], *prep=expression,	/*pream from expression, ptr into it*/
 5638: 	*dollar, *comma;		/* preamble delimiters */
 5639: int	prelen = 0,			/* preamble length */
 5640: 	sizevalue = 0,			/* value of size parameter */
 5641: 	isfontsize = 0,			/*true if leading fontsize present*/
 5642: 	isdelta = 0;			/*true to increment passed size arg*/
 5643: /* -------------------------------------------------------------------------
 5644: initialization
 5645: -------------------------------------------------------------------------- */
 5646: if ( subexpr != NULL )			/* caller passed us an address */
 5647:   *subexpr = '\000';			/* so init assuming no preamble */
 5648: if ( expression == NULL ) goto end_of_job; /* no input */
 5649: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
 5650: /* -------------------------------------------------------------------------
 5651: process preamble if present
 5652: -------------------------------------------------------------------------- */
 5653: /*process_preamble:*/
 5654: if ( (dollar=strchr(expression,'$'))	/* $ signals preceding preamble */
 5655: !=   NULL ) {				/* found embedded $ */
 5656:  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
 5657:  > 0 ) {				/* must have preamble preceding $ */
 5658:   if ( prelen < 65 ) {			/* too long for a prefix */
 5659:    memcpy(pretext,expression,prelen);	/* local copy of preamble */
 5660:    pretext[prelen] = '\000';		/* null-terminated */
 5661:    if ( strchr(pretext,*(ESCAPE))==NULL	/*shouldn't be an escape in preamble*/
 5662:    &&   strchr(pretext,'{') == NULL ) {	/*shouldn't be a left{ in preamble*/
 5663:     /* --- skip any leading whitespace  --- */
 5664:     prep = pretext;			/* start at beginning of preamble */
 5665:     skipwhite(prep);			/* skip any leading white space */
 5666:     /* --- check for embedded , or leading +/- (either signalling size) --- */
 5667:     if ( isthischar(*prep,"+-") )	/* have leading + or - */
 5668:      isdelta = 1;			/* so use size value as increment */
 5669:     comma = strchr(pretext,',');	/* , signals leading size param */
 5670:     /* --- process leading size parameter if present --- */
 5671:     if ( comma != NULL			/* size param explicitly signalled */
 5672:     ||   isdelta || isdigit(*prep) ) {	/* or inferred implicitly */
 5673:       /* --- parse size parameter and reset size accordingly --- */
 5674:       if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
 5675:       sizevalue = atoi(prep);		/* convert size string to integer */
 5676:       if ( size != NULL )		/* caller passed address for size */
 5677: 	*size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
 5678:       /* --- finally, set flag and shift size parameter out of preamble --- */
 5679:       isfontsize = 1;			/*set flag showing font size present*/
 5680:       if ( comma != NULL )		/*2/15/12-isn't this superfluous???*/
 5681:         {strsqueezep(pretext,comma+1);}	/* squeeze out leading size param */
 5682:      } /* --- end-of-if(comma!=NULL||etc) --- */
 5683:     /* --- copy any preamble params following size to caller's subexpr --- */
 5684:     if ( comma != NULL || !isfontsize )	/*preamb contains params past size*/
 5685:      if ( subexpr != NULL )		/* caller passed us an address */
 5686:       strcpy(subexpr,pretext);		/*so return extra params to caller*/
 5687:     /* --- finally, set prep to shift preamble out of expression --- */
 5688:     prep = expression + prelen+1;	/* set prep past $ in expression */
 5689:     } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
 5690:    } /* --- end-of-if(prelen<65) --- */
 5691:   } /* --- end-of-if(prelen>0) --- */
 5692:  else {					/* $ is first char of expression */
 5693:   int ndollars = 0;			/* number of $...$ pairs removed */
 5694:   prep = expression;			/* start at beginning of expression*/
 5695:   while ( *prep == '$' ) {		/* remove all matching $...$'s */
 5696:    int	explen = strlen(prep)-1;	/* index of last char in expression*/
 5697:    if ( explen < 2 ) break;		/* no $...$'s left to remove */
 5698:    if ( prep[explen] != '$' ) break;	/* unmatched $ */
 5699:    prep[explen] = '\000';		/* remove trailing $ */
 5700:    prep++;				/* and remove matching leading $ */
 5701:    ndollars++;				/* count another pair removed */
 5702:    } /* --- end-of-while(*prep=='$') --- */
 5703:   ispreambledollars = ndollars;		/* set flag to fix \displaystyle */
 5704:   if ( ndollars == 1 )			/* user submitted $...$ expression */
 5705:     isdisplaystyle = 0;			/* so set \textstyle */
 5706:   if ( ndollars > 1 )			/* user submitted $$...$$ */
 5707:     isdisplaystyle = 2;			/* so set \displaystyle */
 5708:   /*goto process_preamble;*/		/*check for preamble after leading $*/
 5709:   } /* --- end-of-if/else(prelen>0) --- */
 5710:  } /* --- end-of-if(dollar!=NULL) --- */
 5711: /* -------------------------------------------------------------------------
 5712: back to caller
 5713: -------------------------------------------------------------------------- */
 5714: end_of_job:
 5715:   return ( prep );			/*expression, or ptr past preamble*/
 5716: } /* --- end-of-function preamble() --- */
 5717: 
 5718: 
 5719: /* ==========================================================================
 5720:  * Function:	mimeprep ( expression )
 5721:  * Purpose:	preprocessor for mimeTeX input, e.g.,
 5722:  *		(a) removes comments,
 5723:  *		(b) converts \left( to \( and \right) to \),
 5724:  *		(c) xlates &html; special chars to equivalent latex
 5725:  *		Should only be called once (after unescape_url())
 5726:  * --------------------------------------------------------------------------
 5727:  * Arguments:	expression (I/O) char * to first char of null-terminated
 5728:  *				string containing mimeTeX/LaTeX expression,
 5729:  *				and returning preprocessed string
 5730:  * --------------------------------------------------------------------------
 5731:  * Returns:	( char * )	ptr to input expression,
 5732:  *				or NULL for any parsing error.
 5733:  * --------------------------------------------------------------------------
 5734:  * Notes:     o
 5735:  * ======================================================================= */
 5736: /* --- entry point --- */
 5737: char	*mimeprep ( char *expression )
 5738: {
 5739: /* -------------------------------------------------------------------------
 5740: Allocations and Declarations
 5741: -------------------------------------------------------------------------- */
 5742: char	*expptr=expression,		/* ptr within expression */
 5743: 	*tokptr=NULL,			/*ptr to token found in expression*/
 5744: 	*texsubexpr(), argval[8192];	/*parse for macro args after token*/
 5745: char	*strchange();			/* change leading chars of string */
 5746: int	strreplace();			/* replace nnn with actual num, etc*/
 5747: char	*strwstr();			/*use strwstr() instead of strstr()*/
 5748: char	*findbraces();			/*find left { and right } for \atop*/
 5749: int	idelim=0,			/* left- or right-index */
 5750: 	isymbol=0;			/*symbols[],rightcomment[],etc index*/
 5751: int	xlateleft = 0;			/* true to xlate \left and \right */
 5752: /* ---
 5753:  * comments
 5754:  * -------- */
 5755: char	*leftptr=NULL;			/* find leftcomment in expression */
 5756: static	char *leftcomment = "%%",	/* open comment */
 5757: 	*rightcomment[] = {"\n", "%%", NULL}; /* close comments */
 5758: /* ---
 5759:  * special long (more than 1-char) \left and \right delimiters
 5760:  * ----------------------------------------------------------- */
 5761: static	char *leftfrom[] =		/* xlate any \left suffix... */
 5762:    { "\\|",				/* \left\| */
 5763:      "\\{",				/* \left\{ */
 5764:      "\\langle",			/* \left\langle */
 5765:      NULL } ; /* --- end-of-leftfrom[] --- */
 5766: static	char *leftto[] =		/* ...to this instead */
 5767:    { "=",				/* = */
 5768:      "{",				/* { */
 5769:      "<",				/* < */
 5770:      NULL } ; /* --- end-of-leftto[] --- */
 5771: static	char *rightfrom[] =		/* xlate any \right suffix... */
 5772:    { "\\|",				/* \right\| */
 5773:      "\\}",				/* \right\} */
 5774:      "\\rangle",			/* \right\rangle */
 5775:      NULL } ; /* --- end-of-rightfrom[] --- */
 5776: static	char *rightto[] =		/* ...to this instead */
 5777:    { "=",				/* = */
 5778:      "}",				/* } */
 5779:      ">",				/* > */
 5780:      NULL } ; /* --- end-of-rightto[] --- */
 5781: /* ---
 5782:  * { \atop }-like commands
 5783:  * ----------------------- */
 5784: char	*atopsym=NULL;			/* atopcommands[isymbol] */
 5785: static	char *atopcommands[] =		/* list of {a+b\command c+d}'s */
 5786:    { "\\over",				/* plain tex for \frac */
 5787:      "\\choose",			/* binomial coefficient */
 5788:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
 5789:      "\\atop",
 5790:    #endif
 5791:      NULL } ; /* --- end-of-atopcommands[] --- */
 5792: static	char *atopdelims[] =		/* delims for atopcommands[] */
 5793:    { NULL, NULL,			/* \\over has no delims */
 5794:      "\\left(", "\\right)",		/* \\choose has ( ) delims*/
 5795:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
 5796:      NULL, NULL,			/* \\atop has no delims */
 5797:    #endif
 5798:      NULL, NULL } ; /* --- end-of-atopdelims[] --- */
 5799: /* ---
 5800:  * html special/escape chars converted to latex equivalents
 5801:  * -------------------------------------------------------- */
 5802: char	*htmlsym=NULL;			/* symbols[isymbol].html */
 5803: static	struct { char *html; char *args; char *latex; } symbols[] =
 5804:  { /* --------------------------------------------
 5805:      user-supplied newcommands
 5806:    -------------------------------------------- */
 5807:    #ifdef NEWCOMMANDS			/* -DNEWCOMMANDS=\"filename.h\" */
 5808:      #include NEWCOMMANDS
 5809:    #endif
 5810:    /* --------------------------------------------
 5811:      Specials        termchar  value...
 5812:    -------------------------------------------- */
 5813:    { "\\version",	NULL, "{\\small\\red\\text \\fbox{\\begin{gather}"
 5814: 	"mime\\TeX version \\versionnumber \\\\"
 5815: 	"last revised \\revisiondate \\\\ \\copyrighttext \\\\"
 5816: 	"see \\homepagetext for details \\end{gather}}}" },
 5817:    { "\\copyright",	NULL,
 5818: 	"{\\small\\red\\text \\fbox{\\begin{gather}"
 5819: 	"mimeTeX \\copyrighttext \\\\"
 5820: 	"see \\homepagetext for details \\end{gather}}}" },
 5821:    { "\\versionnumber",	NULL, "{\\text " VERSION "}" },
 5822:    { "\\revisiondate",	NULL, "{\\text " REVISIONDATE "}" },
 5823:    { "\\copyrighttext",	NULL, "{\\text " COPYRIGHTTEXT ".}" },
 5824:    { "\\homepagetext",	NULL,
 5825: 	"{\\text http://www.forkosh.com/mimetex.html}" },
 5826:    /* --------------------------------------------
 5827:      Cyrillic  termchar  mimeTeX equivalent...
 5828:    -------------------------------------------- */
 5829:    { "\\\'G",	"embed\\","{\\acute{G}}" },
 5830:    { "\\\'g",	"embed\\","{\\acute{g}}" },
 5831:    { "\\\'K",	"embed\\","{\\acute{K}}" },
 5832:    { "\\\'k",	"embed\\","{\\acute{k}}" },
 5833:    { "\\u U",	"embed\\","{\\breve{U}}" },
 5834:    { "\\u u",	"embed\\","{\\breve{u}}" },
 5835:    /*{ "\\\"E",	"embed\\","{\\ddot{E}}" },*/
 5836:    /*{ "\\\"e",	"embed\\","{\\ddot{e}}" },*/
 5837:    { "\\\"I",	"embed\\","{\\ddot{\\=I}}" },
 5838:    { "\\\"\\i",	"embed\\","{\\ddot{\\=\\i}}" },
 5839:    /* --------------------------------------------
 5840:      LaTeX Macro #args,default  template...
 5841:    -------------------------------------------- */
 5842:    { "\\lvec",	"2n",	"{#2_1,\\cdots,#2_{#1}}" },
 5843:    { "\\grave", "1",	"{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
 5844:    { "\\acute", "1",	"{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
 5845:    { "\\check", "1",	"{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
 5846:    { "\\breve", "1",	"{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
 5847:    { "\\buildrel","3",	"{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
 5848:    { "\\overset", NULL,	"\\stackrel" },		/* just an alias */
 5849:    { "\\underset", "2",	"\\relstack{#2}{#1}" },	/* reverse args */
 5850:    { "\\dfrac", "2",	"{\\frac{#1}{#2}}" },
 5851:    { "\\binom", "2",	"{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" },
 5852:    { "\\aangle","26",	"{\\boxaccent{#1}{#2}}" },
 5853:    { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/
 5854:    { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" },
 5855:    /* --------------------------------------------
 5856:      html char termchar  LaTeX equivalent...
 5857:    -------------------------------------------- */
 5858:    { "&quot",	";",	"\"" },		/* &quot; is first, &#034; */
 5859:    { "&amp",	";",	"&" },
 5860:    { "&lt",	";",	"<" },
 5861:    { "&gt",	";",	">" },
 5862:    /*{ "&#092",	";",	"\\" },*/	/* backslash */
 5863:    { "&backslash",";",	"\\" },
 5864:    { "&nbsp",	";",	"~" },
 5865:    { "&iexcl",	";",	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
 5866:    { "&brvbar",	";",	"|" },
 5867:    { "&plusmn",	";",	"\\pm" },
 5868:    { "&sup2",	";",	"{{}^2}" },
 5869:    { "&sup3",	";",	"{{}^3}" },
 5870:    { "&micro",	";",	"\\mu" },
 5871:    { "&sup1",	";",	"{{}^1}" },
 5872:    { "&frac14",	";",	"{\\frac14}" },
 5873:    { "&frac12",	";",	"{\\frac12}" },
 5874:    { "&frac34",	";",	"{\\frac34}" },
 5875:    { "&iquest",	";",	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
 5876:    { "&Acirc",	";",	"{\\rm~\\hat~A}" },
 5877:    { "&Atilde",	";",	"{\\rm~\\tilde~A}" },
 5878:    { "&Auml",	";",	"{\\rm~\\ddot~A}" },
 5879:    { "&Aring",	";",	"{\\rm~A\\limits^{-1$o}}" },
 5880:    { "&atilde",	";",	"{\\rm~\\tilde~a}" },
 5881:    { "&yuml",	";",	"{\\rm~\\ddot~y}" }, /* &yuml; is last, &#255; */
 5882:    { "&#",	";",	"{[\\&\\#nnn?]}" },  /* all other explicit &#nnn's */
 5883:    /* --------------------------------------------
 5884:      html tag     termchar    LaTeX equivalent...
 5885:    -------------------------------------------- */
 5886:    { "< br >",    "embed\\i", "\\\\" },
 5887:    { "< br / >",  "embed\\i", "\\\\" },
 5888:    { "< dd >",    "embed\\i", " \000" },
 5889:    { "< / dd >",  "embed\\i", " \000" },
 5890:    { "< dl >",    "embed\\i", " \000" },
 5891:    { "< / dl >",  "embed\\i", " \000" },
 5892:    { "< p >",     "embed\\i", " \000" },
 5893:    { "< / p >",   "embed\\i", " \000" },
 5894:    /* --------------------------------------------
 5895:      garbage      termchar  LaTeX equivalent...
 5896:    -------------------------------------------- */
 5897:    { "< tex >",   "embed\\i", " \000" },
 5898:    { "< / tex >", "embed\\i", " \000" },
 5899:    /* --------------------------------------------
 5900:      LaTeX   termchar   mimeTeX equivalent...
 5901:    -------------------------------------------- */
 5902:    { "\\AA",	NULL,	"{\\rm~A\\limits^{-1$o}}" },
 5903:    { "\\aa",	NULL,	"{\\rm~a\\limits^{-1$o}}" },
 5904:    { "\\bmod",	NULL,	"{\\hspace2{\\rm~mod}\\hspace2}" },
 5905:    { "\\vdots",	NULL,	"{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
 5906:    { "\\dots",	NULL,	"{\\cdots}" },
 5907:    { "\\cdots",	NULL,	"{\\raisebox3{\\ldots}}" },
 5908:    { "\\ldots",	NULL,	"{\\fs4.\\hspace1.\\hspace1.}" },
 5909:    { "\\ddots",	NULL,	"{\\fs4\\raisebox8.\\hspace1\\raisebox4."
 5910: 			"\\hspace1\\raisebox0.}"},
 5911:    { "\\notin",	NULL,	"{\\not\\in}" },
 5912:    { "\\neq",	NULL,	"{\\not=}" },
 5913:    { "\\ne",	NULL,	"{\\not=}" },
 5914:    { "\\mapsto", NULL,	"{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
 5915:    { "\\hbar",	NULL,	"{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
 5916:    { "\\angle",	NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
 5917:    { "\\textcelsius", NULL, "{\\textdegree C}"},
 5918:    { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
 5919:    { "\\cr",	NULL,	"\\\\" },
 5920:    /*{ "\\colon",	NULL,	"{:}" },*/
 5921:    { "\\iiint",	NULL,	"{\\int\\int\\int}\\limits" },
 5922:    { "\\iint",	NULL,	"{\\int\\int}\\limits" },
 5923:    { "\\Bigiint", NULL,	"{\\Bigint\\Bigint}\\limits" },
 5924:    { "\\bigsqcap",NULL,	"{\\fs{+4}\\sqcap}" },
 5925:    { "\\_",	"embed","{\\underline{\\ }}" }, /* displayed underscore */
 5926:    { "!`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
 5927:    { "?`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
 5928:    { "^\'",	"embed","\'" }, /* avoid ^^ when re-xlating \' below */
 5929:    { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
 5930:    { "\'\'\'",	"embed","^{\\fs{-1}\\prime\\prime\\prime}" },
 5931:    { "\'\'",	"embed","^{\\fs{-1}\\prime\\prime}" },
 5932:    { "\'",	"embed","^{\\fs{-1}\\prime}" },
 5933:    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
 5934:    { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
 5935:    { "\\LaTeX",	NULL,	"{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
 5936:    { "\\TeX",	NULL,	"{\\rm~T\\raisebox{-3}{E}X}" },
 5937:    { "\\cyan",	NULL,	"{\\reverse\\red\\reversebg}" },
 5938:    { "\\magenta",NULL,	"{\\reverse\\green\\reversebg}" },
 5939:    { "\\yellow",NULL,	"{\\reverse\\blue\\reversebg}" },
 5940:    { "\\cancel",NULL,	"\\Not" },
 5941:    { "\\hhline",NULL,	"\\Hline" },
 5942:    { "\\Hline", NULL,	"\\hline\\,\\\\\\hline" },
 5943:    /* -----------------------------------------------------------------------
 5944:      As per emails with Zbigniew Fiedorowicz <fiedorow@math.ohio-state.edu>
 5945:      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
 5946:    ----------------------------------------------------------------------- */
 5947:    { "sqrt",	"1",	"{\\sqrt{#1}}" },
 5948:    { "sin",	"1",	"{\\sin{#1}}" },
 5949:    { "cos",	"1",	"{\\cos{#1}}" },
 5950:    { "asin",	"1",	"{\\sin^{-1}{#1}}" },
 5951:    { "acos",	"1",	"{\\cos^{-1}{#1}}" },
 5952:    { "exp",	"1",	"{{\\rm~e}^{#1}}" },
 5953:    { "det",	"1",	"{\\left|{#1}\\right|}" },
 5954:    /* --------------------------------------------
 5955:      LaTeX Constant    termchar   value...
 5956:    -------------------------------------------- */
 5957:    { "\\thinspace",	NULL,	"\\," },
 5958:    { "\\thinmathspace",	NULL,	"\\," },
 5959:    { "\\textwidth",	NULL,	"400" },
 5960:    /* --- end-of-table indicator --- */
 5961:    { NULL,	NULL,	NULL }
 5962:  } ; /* --- end-of-symbols[] --- */
 5963: /* ---
 5964:  * html &#nn chars converted to latex equivalents
 5965:  * ---------------------------------------------- */
 5966: int	htmlnum=0;			/* numbers[inum].html */
 5967: static	struct { int html; char *latex; } numbers[] =
 5968:  { /* ---------------------------------------
 5969:     html num  LaTeX equivalent...
 5970:    --------------------------------------- */
 5971:    { 9,		" " },			/* horizontal tab */
 5972:    { 10,	" " },			/* line feed */
 5973:    { 13,	" " },			/* carriage return */
 5974:    { 32,	" " },			/* space */
 5975:    { 33,	"!" },			/* exclamation point */
 5976:    { 34,	"\"" },			/* &quot; */
 5977:    { 35,	"#" },			/* hash mark */
 5978:    { 36,	"$" },			/* dollar */
 5979:    { 37,	"%" },			/* percent */
 5980:    { 38,	"&" },			/* &amp; */
 5981:    { 39,	"\'" },			/* apostrophe (single quote) */
 5982:    { 40,	")" },			/* left parenthesis */
 5983:    { 41,	")" },			/* right parenthesis */
 5984:    { 42,	"*" },			/* asterisk */
 5985:    { 43,	"+" },			/* plus */
 5986:    { 44,	"," },			/* comma */
 5987:    { 45,	"-" },			/* hyphen (minus) */
 5988:    { 46,	"." },			/* period */
 5989:    { 47,	"/" },			/* slash */
 5990:    { 58,	":" },			/* colon */
 5991:    { 59,	";" },			/* semicolon */
 5992:    { 60,	"<" },			/* &lt; */
 5993:    { 61,	"=" },			/* = */
 5994:    { 62,	">" },			/* &gt; */
 5995:    { 63,	"\?" },			/* question mark */
 5996:    { 64,	"@" },			/* commercial at sign */
 5997:    { 91,	"[" },			/* left square bracket */
 5998:    { 92,	"\\" },			/* backslash */
 5999:    { 93,	"]" },			/* right square bracket */
 6000:    { 94,	"^" },			/* caret */
 6001:    { 95,	"_" },			/* underscore */
 6002:    { 96,	"`" },			/* grave accent */
 6003:    { 123,	"{" },			/* left curly brace */
 6004:    { 124,	"|" },			/* vertical bar */
 6005:    { 125,	"}" },			/* right curly brace */
 6006:    { 126,	"~" },			/* tilde */
 6007:    { 160,	"~" },			/* &nbsp; (use tilde for latex) */
 6008:    { 166,	"|" },			/* &brvbar; (broken vertical bar) */
 6009:    { 173,	"-" },			/* &shy; (soft hyphen) */
 6010:    { 177,	"{\\pm}" },		/* &plusmn; (plus or minus) */
 6011:    { 215,	"{\\times}" },		/* &times; (plus or minus) */
 6012:    { -999,	NULL }
 6013:  } ; /* --- end-of-numbers[] --- */
 6014: /* -------------------------------------------------------------------------
 6015: first remove comments
 6016: -------------------------------------------------------------------------- */
 6017: expptr = expression;			/* start search at beginning */
 6018: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
 6019:   {
 6020:   char	*rightsym=NULL;			/* rightcomment[isymbol] */
 6021:   expptr = leftptr+strlen(leftcomment);	/* start rightcomment search here */
 6022:   /* --- check for any closing rightcomment, in given precedent order --- */
 6023:   if ( *expptr != '\000' )		/*have chars after this leftcomment*/
 6024:    for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
 6025:     if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
 6026:      { tokptr += strlen(rightsym);	/* first char after rightcomment */
 6027:        if ( *tokptr == '\000' )		/*nothing after this rightcomment*/
 6028: 	{ *leftptr = '\000';		/*so terminate expr at leftcomment*/
 6029: 	  break; }			/* and stop looking for comments */
 6030:        *leftptr = '~';			/* replace entire comment by ~ */
 6031:        strsqueezep(leftptr+1,tokptr);	/* squeeze out comment */
 6032:        goto next_comment; }		/* stop looking for rightcomment */
 6033:   /* --- no rightcomment after opening leftcomment --- */
 6034:   *leftptr = '\000';			/* so terminate expression */
 6035:   /* --- resume search past squeezed-out comment --- */
 6036:   next_comment:
 6037:     if ( *leftptr == '\000' ) break;	/* reached end of expression */
 6038:     expptr = leftptr+1;			/*resume search after this comment*/
 6039:   } /* --- end-of-while(leftptr!=NULL) --- */
 6040: /* -------------------------------------------------------------------------
 6041: run thru table, converting all occurrences of each macro to its expansion
 6042: -------------------------------------------------------------------------- */
 6043: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
 6044:   {
 6045:   int	htmllen = strlen(htmlsym);	/* length of escape, _without_ ; */
 6046:   int	isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
 6047:   int	isembedded = 0,			/* true to xlate even if embedded */
 6048: 	istag=0, isamp=0,		/* true for <tag>, &char; symbols */
 6049: 	isstrwstr = 0,			/* true to use strwstr() */
 6050: 	wstrlen = 0;			/* length of strwstr() match */
 6051:   char	*aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
 6052:   char	embedkeywd[99] = "embed",	/* keyword to signal embedded token*/
 6053: 	embedterm = '\000';		/* char immediately after embed */
 6054:   int	embedlen = strlen(embedkeywd);	/* #chars in embedkeywd */
 6055:   char	*args = symbols[isymbol].args,	/* number {}-args, optional []-arg */
 6056: 	*htmlterm = args,		/*if *args nonumeric, then html term*/
 6057: 	*latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
 6058: 	errorsym[256];			/*or latexsym may point to error msg*/
 6059:   char	abuff[8192];  int iarg,nargs=0;	/* macro expansion params */
 6060:   char	wstrwhite[99];			/* whitespace chars for strwstr() */
 6061:   skipwhite(htmlsym);			/*skip any bogus leading whitespace*/
 6062:   htmllen = strlen(htmlsym);		/* reset length of html token */
 6063:   istag = (isthischar(*htmlsym,"<")?1:0); /* html <tag> starts with < */
 6064:   isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
 6065:   if ( args != NULL )			/*we have args (or htmlterm) param*/
 6066:    if ( *args != '\000' ) {		/* and it's not an empty string */
 6067:     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
 6068:      { htmlterm = NULL;			/* if so, then we have no htmlterm */
 6069:        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
 6070:        nargs = atoi(abuff); }		/* interpret #args to numeric */
 6071:     else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
 6072:      { int arglen = strlen(args);	/* length of "embed..." string */
 6073:        htmlterm = NULL;			/* if so, then we have no htmlterm */
 6074:        isembedded = 1 ;			/* turn on embedded flag */
 6075:        if ( arglen > embedlen )		/* have embed "allow escape" flag */
 6076:          embedterm = args[embedlen];	/* char immediately after "embed" */
 6077:        if (arglen > embedlen+1) {	/* have embed,flag,white for strwstr*/
 6078: 	 isstrwstr = 1;			/* turn on strwtsr flag */
 6079: 	 strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
 6080:     } /* --- end-of-if(*args!='\000') --- */
 6081:   expptr = expression;			/* re-start search at beginning */
 6082:   while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
 6083:   strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
 6084:   != NULL ) {				/* found another sym */
 6085:       int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
 6086:       char termchar = *(tokptr+toklen),	/* char terminating html sequence */
 6087:            prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
 6088:       int  isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
 6089:       int  escapelen = toklen;		/* total length of escape sequence */
 6090:       int  isflush = 0;			/* true to flush (don't xlate) */
 6091:       /* --- check odd/even backslashes preceding tokens --- */
 6092:       if ( isescaped ) {		/* have one preceding backslash */
 6093: 	char *p = tokptr-1;		/* ptr to that preceding backslash */
 6094: 	while ( p != expptr ) {		/* and we may have more preceding */
 6095: 	  p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
 6096: 	  isescaped = 1-isescaped; } }	/* or flip isescaped flag if we do */
 6097:       /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
 6098:       *abuff = '\000';			/* default to empty string */
 6099:       if ( latexsym != NULL )		/* table has .latex xlation */
 6100:        if ( *latexsym != '\000' )	/* and it's not an empty string */
 6101: 	strcpy(abuff,latexsym);		/* so get local copy */
 6102:       if ( !isembedded )		/*embedded sequences not terminated*/
 6103:        if ( htmlterm != NULL )		/* sequence may have terminator */
 6104: 	escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
 6105:       /* --- don't xlate if we just found prefix of longer symbol, etc --- */
 6106:       if ( !isembedded ) {		/* not embedded */
 6107: 	if ( isescaped )		/* escaped */
 6108: 	  isflush = 1;			/* set flag to flush escaped token */
 6109: 	if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
 6110: 	  isflush = 1;			/* so just a prefix of longer symbol*/
 6111: 	if ( isalpha((int)(*htmlsym)) )	/* symbol starts with alpha */
 6112:           if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
 6113: 	    isflush = 1; }		/* set flag to flush token */
 6114:       if ( isembedded )			/* for embedded token */
 6115:        if ( isescaped )			/* and embedded \token escaped */
 6116: 	if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
 6117: 	  isflush = 1;			/* set flag to flush token */
 6118:       if ( isflush )			/* don't xlate this token */
 6119: 	{ expptr = tokptr+1;/*toklen;*/	/* just resume search after token */
 6120: 	  continue; }			/* but don't replace it */
 6121:       /* --- check for &# prefix signalling &#nnn; --- */
 6122:       if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
 6123:        /* --- accumulate chars comprising number following &# --- */
 6124:        char anum[32];			/* chars comprising number after &# */
 6125:        int  inum = 0;			/* no chars accumulated yet */
 6126:        while ( termchar != '\000' ) {	/* don't go past end-of-string */
 6127:          if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
 6128:          if ( inum > 10 ) break;	/* some syntax error in expression */
 6129:          anum[inum] = termchar;		/* accumulate this digit */
 6130:          inum++;  toklen++;		/* bump field length, token length */
 6131:          termchar = *(tokptr+toklen); }	/* char terminating html sequence */
 6132:        anum[inum] = '\000';		/* null-terminate anum */
 6133:        escapelen = toklen;		/* length of &#nnn; sequence */
 6134:        if ( htmlterm != NULL )		/* sequence may have terminator */
 6135:          escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
 6136:        /* --- look up &#nnn in number[] table --- */
 6137:        htmlnum = atoi(anum);		/* convert anum[] to an integer */
 6138:        strninit(errorsym,latexsym,128);	/* init error message */
 6139:        latexsym = errorsym;		/* init latexsym as error message */
 6140:        strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/
 6141:        for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
 6142:          if ( htmlnum ==  numbers[inum].html ) { /* till we find a match */
 6143:            latexsym = numbers[inum].latex; /* latex replacement */
 6144:            break; }			/* no need to look any further */
 6145:        if ( latexsym != NULL )		/* table has .latex xlation */
 6146:         if ( *latexsym != '\000' )	/* and it's not an empty string */
 6147: 	 strcpy(abuff,latexsym);	/* so get local copy */
 6148:        } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */
 6149:       /* --- substitute macro arguments --- */
 6150:       if ( nargs > 0 )			/*substitute #1,#2,... in latexsym*/
 6151:        {
 6152:        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
 6153:        char *optarg = args+1;		/* ptr 1 char past #args digit 0-9 */
 6154:        expptr = arg1ptr;		/* ptr to beginning of next arg */
 6155:        for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
 6156: 	{
 6157: 	char argsignal[32] = "#1",	/* #1...#9 signals arg replacement */
 6158: 	*argsigptr = NULL;		/* ptr to argsignal in abuff[] */
 6159: 	/* --- get argument value --- */
 6160: 	*argval = '\000';		/* init arg as empty string */
 6161: 	skipwhite(expptr);		/* and skip leading white space */
 6162: 	if ( iarg==1 && *optarg!='\000'	/* check for optional [arg] */
 6163: 	&&   !isalgebra )		/* but not in "algebra syntax" */
 6164: 	 { strcpy(argval,optarg);	/* init with default value */
 6165: 	   if ( *expptr == '[' )	/* but user gave us [argval] */
 6166: 	     expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
 6167: 	else				/* not optional, so get {argval} */
 6168: 	 if ( *expptr != '\000' ) {	/* check that some argval provided */
 6169: 	   if ( !isalgebra )		/* only { } delims for latex macro */
 6170: 	     expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
 6171: 	   else {			/*any delim for algebra syntax macro*/
 6172: 	     expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
 6173: 	     if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
 6174: 	       if ( *argval != '{' ) {	/* and it's not { }-enclosed */
 6175: 	         strchange(0,argval,"\\left"); /* insert opening \left, */
 6176: 	         strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
 6177: 	  } /* --- end-of-if(*expptr!='\000') --- */
 6178: 	/* --- (recursively) call mimeprep() to prep the argument --- */
 6179: 	if ( !isempty(argval) )		/* have an argument */
 6180: 	  mimeprep(argval);		/* so (recursively) prep it */
 6181: 	/* --- replace #`iarg` in macro with argval --- */
 6182: 	sprintf(argsignal,"#%d",iarg);	/* #1...#9 signals argument */
 6183: 	while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
 6184: 	 {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
 6185: 	while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
 6186: 	 strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
 6187: 	} /* --- end-of-for(iarg) --- */
 6188:        escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
 6189:        } /* --- end-of-if(nargs>0) --- */
 6190:       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
 6191:       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
 6192:       } /* --- end-of-while(tokptr!=NULL) --- */
 6193:   } /* --- end-of-for(isymbol) --- */
 6194: /* -------------------------------------------------------------------------
 6195: convert \left( to \(  and  \right) to \),  etc.
 6196: -------------------------------------------------------------------------- */
 6197: if ( xlateleft )			/* \left...\right xlation wanted */
 6198:  for ( idelim=0; idelim<2; idelim++ )	/* 0 for \left  and  1 for \right */
 6199:   {
 6200:   char	*lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
 6201:   int	lrlen   = (idelim==0?5:6);	/* strlen() of \left or \right */
 6202:   char	*braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
 6203: 	**lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
 6204: 	**lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
 6205: 	*lrsym  = NULL;			/* lrfrom[isymbol] */
 6206:   expptr = expression;			/* start search at beginning */
 6207:   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
 6208:     {
 6209:     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
 6210:       {	strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
 6211: 	expptr = tokptr+2; }		/* and resume search past brace */
 6212:     else				/* may be a "long" brace like \| */
 6213:       {
 6214:       expptr = tokptr+lrlen;		/*init to resume search past\left\rt*/
 6215:       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
 6216: 	{ int symlen = strlen(lrsym);	/* #chars in delim, e.g., 2 for \| */
 6217: 	  if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
 6218: 	    { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
 6219: 	      *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
 6220: 	      expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
 6221: 	      break; }			/* no need to check more lrsym's */
 6222: 	} /* --- end-of-for(isymbol) --- */
 6223:       } /* --- end-of-if/else(isthischar()) --- */
 6224:     } /* --- end-of-while(tokptr!=NULL) --- */
 6225:   } /* --- end-of-for(idelim) --- */
 6226: /* -------------------------------------------------------------------------
 6227: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
 6228: -------------------------------------------------------------------------- */
 6229: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
 6230:   {
 6231:   int	atoplen = strlen(atopsym);	/* #chars in \atop */
 6232:   expptr = expression;			/* re-start search at beginning */
 6233:   while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
 6234:     { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
 6235:       char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
 6236:       if ( msgfp!=NULL && msglevel>=999 )
 6237: 	{ fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
 6238: 	  (int)(tokptr-expression),tokptr);
 6239: 	  fflush(msgfp); }
 6240:       if ( isalpha((int)termchar) )	/*we just have prefix of longer sym*/
 6241: 	{ expptr = tokptr+atoplen;	/* just resume search after prefix */
 6242: 	  continue; }			/* but don't process it */
 6243:       leftbrace  = findbraces(expression,tokptr);     /* find left { */
 6244:       rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
 6245:       if ( leftbrace==NULL || rightbrace==NULL )
 6246: 	{ expptr += atoplen;  continue; } /* skip command if didn't find */
 6247:       else				/* we have bracketed { \atop } */
 6248: 	{
 6249: 	int  leftlen  = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
 6250: 	     rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
 6251: 	     totlen   = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
 6252: 	char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
 6253: 	char arg[8192], command[8192];	/* left/right args, new \atop{}{} */
 6254: 	*command = '\000';		/* start with null string */
 6255: 	if (open!=NULL) strcat(command,open); /* add open delim if needed */
 6256: 	strcat(command,atopsym);	/* add command with \atop */
 6257: 	arg[0] = '{';			/* arg starts with { */
 6258: 	memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
 6259: 	arg[leftlen+1] = '\000';	/* and null terminate it */
 6260: 	strcat(command,arg);		/* concatanate {left-arg to \atop */
 6261: 	strcat(command,"}{");		/* close left-arg, open right-arg */
 6262: 	memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
 6263: 	arg[rightlen] = '}';		/* add closing } */
 6264: 	arg[rightlen+1] = '\000';	/* and null terminate it */
 6265: 	if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
 6266: 	  {strsqueeze(arg,1);}		/* so squeeze it out */
 6267: 	strcat(command,arg);		/* concatanate right-arg} */
 6268: 	if (close!=NULL) strcat(command,close); /* add close delim if needed*/
 6269: 	strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
 6270: 	expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
 6271: 	}
 6272:     } /* --- end-of-while(tokptr!=NULL) --- */
 6273:   } /* --- end-of-for(isymbol) --- */
 6274: /* -------------------------------------------------------------------------
 6275: back to caller with preprocessed expression
 6276: -------------------------------------------------------------------------- */
 6277: if ( msgfp!=NULL && msglevel>=99 )	/* display preprocessed expression */
 6278:   { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
 6279:     fflush(msgfp); }
 6280: return ( expression );
 6281: } /* --- end-of-function mimeprep() --- */
 6282: 
 6283: 
 6284: /* ==========================================================================
 6285:  * Function:	strchange ( int nfirst, char *from, char *to )
 6286:  * Purpose:	Changes the nfirst leading chars of `from` to `to`.
 6287:  *		For example, to change char x[99]="12345678" to "123ABC5678"
 6288:  *		call strchange(1,x+3,"ABC")
 6289:  * --------------------------------------------------------------------------
 6290:  * Arguments:	nfirst (I)	int containing #leading chars of `from`
 6291:  *				that will be replace by `to`
 6292:  *		from (I/O)	char * to null-terminated string whose nfirst
 6293:  *				leading chars will be replaced by `to`
 6294:  *		to (I)		char * to null-terminated string that will
 6295:  *				replace the nfirst leading chars of `from`
 6296:  * --------------------------------------------------------------------------
 6297:  * Returns:	( char * )	ptr to first char of input `from`
 6298:  *				or NULL for any error.
 6299:  * --------------------------------------------------------------------------
 6300:  * Notes:     o	If strlen(to)>nfirst, from must have memory past its null
 6301:  *		(i.e., we don't do a realloc)
 6302:  * ======================================================================= */
 6303: /* --- entry point --- */
 6304: char	*strchange ( int nfirst, char *from, char *to )
 6305: {
 6306: /* -------------------------------------------------------------------------
 6307: Allocations and Declarations
 6308: -------------------------------------------------------------------------- */
 6309: int	tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
 6310: 	nshift = abs(tolen-nfirst);	/*need to shift from left or right*/
 6311: /* -------------------------------------------------------------------------
 6312: shift from left or right to accommodate replacement of its nfirst chars by to
 6313: -------------------------------------------------------------------------- */
 6314: if ( tolen < nfirst )			/* shift left is easy */
 6315:   {strsqueeze(from,nshift);}		/* memmove avoids overlap memory */
 6316: if ( tolen > nfirst )			/* need more room at start of from */
 6317:   { char *pfrom = from+strlen(from);	/* ptr to null terminating from */
 6318:     for ( ; pfrom>=from; pfrom-- )	/* shift all chars including null */
 6319:       *(pfrom+nshift) = *pfrom; }	/* shift chars nshift places right */
 6320: /* -------------------------------------------------------------------------
 6321: from has exactly the right number of free leading chars, so just put to there
 6322: -------------------------------------------------------------------------- */
 6323: if ( tolen != 0 )			/* make sure to not empty or null */
 6324:   memcpy(from,to,tolen);		/* chars moved into place */
 6325: return ( from );			/* changed string back to caller */
 6326: } /* --- end-of-function strchange() --- */
 6327: 
 6328: 
 6329: /* ==========================================================================
 6330:  * Function:	strreplace (char *string, char *from, char *to, int nreplace)
 6331:  * Purpose:	Changes the first nreplace occurrences of 'from' to 'to'
 6332:  *		in string, or all occurrences if nreplace=0.
 6333:  * --------------------------------------------------------------------------
 6334:  * Arguments:	string (I/0)	char * to null-terminated string in which
 6335:  *				occurrence of 'from' will be replaced by 'to'
 6336:  *		from (I)	char * to null-terminated string
 6337:  *				to be replaced by 'to'
 6338:  *		to (I)		char * to null-terminated string that will
 6339:  *				replace 'from'
 6340:  *		nreplace (I)	int containing (maximum) number of
 6341:  *				replacements, or 0 to replace all.
 6342:  * --------------------------------------------------------------------------
 6343:  * Returns:	( int )		number of replacements performed,
 6344:  *				or 0 for no replacements or -1 for any error.
 6345:  * --------------------------------------------------------------------------
 6346:  * Notes:     o
 6347:  * ======================================================================= */
 6348: /* --- entry point --- */
 6349: int	strreplace ( char *string, char *from, char *to, int nreplace )
 6350: {
 6351: /* -------------------------------------------------------------------------
 6352: Allocations and Declarations
 6353: -------------------------------------------------------------------------- */
 6354: int	fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
 6355: 	tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
 6356: char	*pfrom = (char *)NULL,		/*ptr to 1st char of from in string*/
 6357: 	*pstring = string,		/*ptr past previously replaced from*/
 6358: 	*strchange();			/* change 'from' to 'to' */
 6359: int	nreps = 0;			/* #replacements returned to caller*/
 6360: /* -------------------------------------------------------------------------
 6361: repace occurrences of 'from' in string to 'to'
 6362: -------------------------------------------------------------------------- */
 6363: if ( string == (char *)NULL		/* no input string */
 6364: ||   (fromlen<1 && nreplace<=0) )	/* replacing empty string forever */
 6365:   nreps = (-1);				/* so signal error */
 6366: else					/* args okay */
 6367:   while (nreplace<1 || nreps<nreplace)	/* up to #replacements requested */
 6368:     {
 6369:     if ( fromlen > 0 )			/* have 'from' string */
 6370:       pfrom = strstr(pstring,from);	/*ptr to 1st char of from in string*/
 6371:     else  pfrom = pstring;		/*or empty from at start of string*/
 6372:     if ( pfrom == (char *)NULL ) break;	/*no more from's, so back to caller*/
 6373:     if ( strchange(fromlen,pfrom,to)	/* leading 'from' changed to 'to' */
 6374:     ==   (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
 6375:     nreps++;				/* count another replacement */
 6376:     pstring = pfrom+tolen;		/* pick up search after 'to' */
 6377:     if ( *pstring == '\000' ) break;	/* but quit at end of string */
 6378:     } /* --- end-of-while() --- */
 6379: return ( nreps );			/* #replacements back to caller */
 6380: } /* --- end-of-function strreplace() --- */
 6381: 
 6382: 
 6383: /* ==========================================================================
 6384:  * Function:	strwstr (char *string, char *substr, char *white, int *sublen)
 6385:  * Purpose:	Find first substr in string, but wherever substr contains
 6386:  *		a whitespace char (in white), string may contain any number
 6387:  *		(including 0) of whitespace chars. If white contains I or i,
 6388:  *		then match is case-insensitive (and I,i _not_ whitespace).
 6389:  * --------------------------------------------------------------------------
 6390:  * Arguments:	string (I)	char * to null-terminated string in which
 6391:  *				first occurrence of substr will be found
 6392:  *		substr (I)	char * to null-terminated string containing
 6393:  *				"template" that will be searched for
 6394:  *		white (I)	char * to null-terminated string containing
 6395:  *				whitespace chars.  If NULL or empty, then
 6396:  *				"~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
 6397:  *				If white contains I or i, then match is
 6398:  *				case-insensitive (and I,i _not_ considered
 6399:  *				whitespace).
 6400:  *		sublen (O)	address of int returning "length" of substr
 6401:  *				found in string (which may be longer or
 6402:  *				shorter than substr itself).
 6403:  * --------------------------------------------------------------------------
 6404:  * Returns:	( char * )	ptr to first char of substr in string
 6405:  *				or NULL if not found or for any error.
 6406:  * --------------------------------------------------------------------------
 6407:  * Notes:     o	Wherever a single whitespace char appears in substr,
 6408:  *		the corresponding position in string may contain any
 6409:  *		number (including 0) of whitespace chars, e.g.,
 6410:  *		string="abc   def" and string="abcdef" both match
 6411:  *		substr="c d" at offset 2 of string.
 6412:  *	      o	If substr="c  d" (two spaces between c and d),
 6413:  *		then string must have at least one space, so now "abcdef"
 6414:  *		doesn't match.  In general, the minimum number of spaces
 6415:  *		in string is the number of spaces in substr minus 1
 6416:  *		(so 1 space in substr permits 0 spaces in string).
 6417:  *	      o	Embedded spaces are counted in sublen, e.g.,
 6418:  *		string="c   d" (three spaces) matches substr="c d"
 6419:  *		with sublen=5 returned.  But string="ab   c   d" will
 6420:  *		also match substr="  c d" returning sublen=5 and
 6421:  *		a ptr to the "c".  That is, the mandatory preceding
 6422:  *		space is _not_ counted as part of the match.
 6423:  *		But all the embedded space is counted.
 6424:  *		(An inconsistent bug/feature is that mandatory
 6425:  *		terminating space is counted.)
 6426:  *	      o	Moreover, string="c   d" matches substr="  c d", i.e.,
 6427:  *		the very beginning of a string is assumed to be preceded
 6428:  *		by "virtual blanks".
 6429:  * ======================================================================= */
 6430: /* --- entry point --- */
 6431: char	*strwstr ( char *string, char *substr, char *white, int *sublen )
 6432: {
 6433: /* -------------------------------------------------------------------------
 6434: Allocations and Declarations
 6435: -------------------------------------------------------------------------- */
 6436: char	*psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
 6437: 	*pfound = (char *)NULL;		/*ptr to found substr back to caller*/
 6438: char	*pwhite=NULL, whitespace[256];	/* callers white whithout i,I */
 6439: int	iscase = (white==NULL?1:	/* case-sensitive if i,I in white */
 6440: 	strchr(white,'i')==NULL && strchr(white,'I')==NULL);
 6441: int	foundlen = 0;			/* length of substr found in string*/
 6442: int	nstrwhite=0, nsubwhite=0,	/* #leading white chars in str,sub */
 6443: 	nminwhite=0;			/* #mandatory leading white in str */
 6444: int	nstrchars=0, nsubchars=0,	/* #non-white chars to be matched */
 6445: 	isncmp=0;			/*strncmp() or strncasecmp() result*/
 6446: /* -------------------------------------------------------------------------
 6447: Initialization
 6448: -------------------------------------------------------------------------- */
 6449: /* --- set up whitespace --- */
 6450: strcpy(whitespace,WHITEMATH);		/*default if no user input for white*/
 6451: if ( white != NULL )			/*user provided ptr to white string*/
 6452:  if ( *white != '\000' ) {		/*and it's not just an empty string*/
 6453:    strcpy(whitespace,white);		/* so use caller's white spaces */
 6454:    while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
 6455:      {strsqueeze(pwhite,1);}		/* so squeeze it out */
 6456:    while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
 6457:      {strsqueeze(pwhite,1);}		/* so squeeze it out */
 6458:    if ( *whitespace == '\000' )		/* caller's white just had i,I */
 6459:      strcpy(whitespace,WHITEMATH); }	/* so revert back to default */
 6460: /* -------------------------------------------------------------------------
 6461: Find first occurrence of substr in string
 6462: -------------------------------------------------------------------------- */
 6463: if ( string != NULL )			/* caller passed us a string ptr */
 6464:  while ( *pstring != '\000' ) {		/* break when string exhausted */
 6465:   char	*pstrptr = pstring;		/* (re)start at next char in string*/
 6466:   int	leadingwhite = 0;		/* leading whitespace */
 6467:   psubstr = substr;			/* start at beginning of substr */
 6468:   foundlen = 0;				/* reset length of found substr */
 6469:   if ( substr != NULL )			/* caller passed us a substr ptr */
 6470:    while ( *psubstr != '\000' ) {	/*see if pstring begins with substr*/
 6471:     /* --- check for end-of-string before finding match --- */
 6472:     if ( *pstrptr == '\000' )		/* end-of-string without a match */
 6473:       goto nextstrchar;			/* keep trying with next char */
 6474:     /* --- actual amount of whitespace in string and substr --- */
 6475:     nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
 6476:     nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
 6477:     nminwhite = max2(0,nsubwhite-1);	/* #mandatory leading white in str */
 6478:     /* --- check for mandatory leading whitespace in string --- */
 6479:     if ( pstrptr != string )		/*not mandatory at start of string*/
 6480:       if ( nstrwhite < nminwhite )	/* too little leading white space */
 6481: 	goto nextstrchar;		/* keep trying with next char */
 6482:     /* ---hold on to #whitespace chars in string preceding substr match--- */
 6483:     if ( pstrptr == pstring )		/* whitespace at start of substr */
 6484:       leadingwhite = nstrwhite;		/* save it as leadingwhite */
 6485:     /* --- check for optional whitespace --- */
 6486:     if ( psubstr != substr )		/* always okay at start of substr */
 6487:       if ( nstrwhite>0 && nsubwhite<1 )	/* too much leading white space */
 6488: 	goto nextstrchar;		/* keep trying with next char */
 6489:     /* --- skip any leading whitespace in substr and string --- */
 6490:     psubstr += nsubwhite;		/* push past leading sub whitespace*/
 6491:     pstrptr += nstrwhite;		/* push past leading str whitespace*/
 6492:     /* --- now get non-whitespace chars that we have to match --- */
 6493:     nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
 6494:     nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
 6495:     if ( nstrchars < nsubchars )	/* too few chars for match */
 6496:       goto nextstrchar;			/* keep trying with next char */
 6497:     /* --- see if next nsubchars are a match --- */
 6498:     isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
 6499: 		strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
 6500:     if ( isncmp != 0 )			/* no match */
 6501:       goto nextstrchar;			/* keep trying with next char */
 6502:     /* --- push past matched chars --- */
 6503:     psubstr += nsubchars;  pstrptr += nsubchars;  /*nsubchars were matched*/
 6504:     } /* --- end-of-while(*psubstr!='\000') --- */
 6505:   pfound = pstring + leadingwhite;	/* found match starting at pstring */
 6506:   foundlen = (int)(pstrptr-pfound);	/* consisting of this many chars */
 6507:   goto end_of_job;			/* back to caller */
 6508:   /* ---failed to find substr, continue trying with next char in string--- */
 6509:   nextstrchar:				/* continue outer loop */
 6510:     pstring++;				/* bump to next char in string */
 6511:   } /* --- end-of-while(*pstring!='\000') --- */
 6512: /* -------------------------------------------------------------------------
 6513: Back to caller with ptr to first occurrence of substr in string
 6514: -------------------------------------------------------------------------- */
 6515: end_of_job:
 6516:   if ( msglevel>=999 && msgfp!=NULL) {	/* debugging/diagnostic output */
 6517:     fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
 6518:     string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
 6519:   if ( sublen != NULL )			/*caller wants length of found substr*/
 6520:     *sublen = foundlen;			/* give it to him along with ptr */
 6521:   return ( pfound );			/*ptr to first found substr, or NULL*/
 6522: } /* --- end-of-function strwstr() --- */
 6523: 
 6524: 
 6525: /* ==========================================================================
 6526:  * Function:	strdetex ( s, mode )
 6527:  * Purpose:	Removes/replaces any LaTeX math chars in s
 6528:  *		so that s can be displayed "verbatim",
 6529:  *		e.g., for error messages.
 6530:  * --------------------------------------------------------------------------
 6531:  * Arguments:	s (I)		char * to null-terminated string
 6532:  *				whose math chars are to be removed/replaced
 6533:  *		mode (I)	int containing 0 to _not_ use macros (i.e.,
 6534:  *				mimeprep won't be called afterwards),
 6535:  *				or containing 1 to use macros that will
 6536:  *				be expanded by a subsequent call to mimeprep.
 6537:  * --------------------------------------------------------------------------
 6538:  * Returns:	( char * )	ptr to "cleaned" copy of s
 6539:  *				or "" (empty string) for any error.
 6540:  * --------------------------------------------------------------------------
 6541:  * Notes:     o	The returned pointer addresses a static buffer,
 6542:  *		so don't call strdetex() again until you're finished
 6543:  *		with output from the preceding call.
 6544:  * ======================================================================= */
 6545: /* --- entry point --- */
 6546: char	*strdetex ( char *s, int mode )
 6547: {
 6548: /* -------------------------------------------------------------------------
 6549: Allocations and Declarations
 6550: -------------------------------------------------------------------------- */
 6551: static	char sbuff[4096];		/* copy of s with no math chars */
 6552: int	strreplace();			/* replace _ with -, etc */
 6553: /* -------------------------------------------------------------------------
 6554: Make a clean copy of s
 6555: -------------------------------------------------------------------------- */
 6556: /* --- check input --- */
 6557: *sbuff = '\000';			/* initialize in case of error */
 6558: if ( isempty(s) ) goto end_of_job;	/* no input */
 6559: /* --- start with copy of s --- */
 6560: strninit(sbuff,s,2048);			/* leave room for replacements */
 6561: /* --- make some replacements -- we *must* replace \ { } first --- */
 6562: strreplace(sbuff,"\\","\\backslash~\\!\\!",0);	/*change all \'s to text*/
 6563: strreplace(sbuff,"{", "\\lbrace~\\!\\!",0);	/*change all {'s to \lbrace*/
 6564: strreplace(sbuff,"}", "\\rbrace~\\!\\!",0);	/*change all }'s to \rbrace*/
 6565: /* --- now our further replacements may contain \directives{args} --- */
 6566: if( mode >= 1 ) strreplace(sbuff,"_","\\_",0);	/* change all _'s to \_ */
 6567: else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
 6568: if(0)strreplace(sbuff,"<","\\textlangle ",0);	/* change all <'s to text */
 6569: if(0)strreplace(sbuff,">","\\textrangle ",0);	/* change all >'s to text */
 6570: if(0)strreplace(sbuff,"$","\\textdollar ",0);	/* change all $'s to text */
 6571: strreplace(sbuff,"$","\\$",0);			/* change all $'s to \$ */
 6572: strreplace(sbuff,"&","\\&",0);			/* change all &'s to \& */
 6573: strreplace(sbuff,"%","\\%",0);			/* change all %'s to \% */
 6574: strreplace(sbuff,"#","\\#",0);			/* change all #'s to \# */
 6575: /*strreplace(sbuff,"~","\\~",0);*/		/* change all ~'s to \~ */
 6576: strreplace(sbuff,"^","{\\fs{+2}\\^}",0);	/* change all ^'s to \^ */
 6577: end_of_job:
 6578:   return ( sbuff );			/* back with clean copy of s */
 6579: } /* --- end-of-function strdetex() --- */
 6580: 
 6581: 
 6582: /* ==========================================================================
 6583:  * Function:	strtexchr ( char *string, char *texchr )
 6584:  * Purpose:	Find first texchr in string, but texchr must be followed
 6585:  *		by non-alpha
 6586:  * --------------------------------------------------------------------------
 6587:  * Arguments:	string (I)	char * to null-terminated string in which
 6588:  *				first occurrence of delim will be found
 6589:  *		texchr (I)	char * to null-terminated string that
 6590:  *				will be searched for
 6591:  * --------------------------------------------------------------------------
 6592:  * Returns:	( char * )	ptr to first char of texchr in string
 6593:  *				or NULL if not found or for any error.
 6594:  * --------------------------------------------------------------------------
 6595:  * Notes:     o	texchr should contain its leading \, e.g., "\\left"
 6596:  * ======================================================================= */
 6597: /* --- entry point --- */
 6598: char	*strtexchr ( char *string, char *texchr )
 6599: {
 6600: /* -------------------------------------------------------------------------
 6601: Allocations and Declarations
 6602: -------------------------------------------------------------------------- */
 6603: char	delim, *ptexchr=(char *)NULL;	/* ptr returned to caller*/
 6604: char	*pstring = string;		/* start or continue up search here*/
 6605: int	texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
 6606: /* -------------------------------------------------------------------------
 6607: locate texchr in string
 6608: -------------------------------------------------------------------------- */
 6609: if ( string != (char *)NULL		/* check that we got input string */
 6610: &&   texchrlen > 0 ) {			/* and a texchr to search for */
 6611:  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
 6612:  != (char *)NULL )			/* found it */
 6613:   if ( (delim = ptexchr[texchrlen])	/* char immediately after texchr */
 6614:   ==   '\000' ) break;			/* texchr at very end of string */
 6615:   else					/* if there are chars after texchr */
 6616:    if ( isalpha(delim)			/*texchr is prefix of longer symbol*/
 6617:    ||   0 )				/* other tests to be determined */
 6618:     pstring = ptexchr + texchrlen;	/* continue search after texchr */
 6619:    else					/* passed all tests */
 6620:     break; }				/*so return ptr to texchr to caller*/
 6621: return ( ptexchr );			/* ptr to texchar back to caller */
 6622: } /* --- end-of-function strtexchr() --- */
 6623: 
 6624: 
 6625: /* ==========================================================================
 6626:  * Function:	findbraces ( char *expression, char *command )
 6627:  * Purpose:	If expression!=NULL, finds opening left { preceding command;
 6628:  *		if expression==NULL, finds closing right } after command.
 6629:  *		For example, to parse out {a+b\over c+d} call findbraces()
 6630:  *		twice.
 6631:  * --------------------------------------------------------------------------
 6632:  * Arguments:	expression (I)	NULL to find closing right } after command,
 6633:  *				or char * to null-terminated string to find
 6634:  *				left opening { preceding command.
 6635:  *		command (I)	char * to null-terminated string whose
 6636:  *				first character is usually the \ of \command
 6637:  * --------------------------------------------------------------------------
 6638:  * Returns:	( char * )	ptr to either opening { or closing },
 6639:  *				or NULL for any error.
 6640:  * --------------------------------------------------------------------------
 6641:  * Notes:     o
 6642:  * ======================================================================= */
 6643: /* --- entry point --- */
 6644: char	*findbraces ( char *expression, char *command )
 6645: {
 6646: /* -------------------------------------------------------------------------
 6647: Allocations and Declarations
 6648: -------------------------------------------------------------------------- */
 6649: int	isopen = (expression==NULL?0:1); /* true to find left opening { */
 6650: char	*left="{", *right="}",		/* delims bracketing {x\command y} */
 6651: 	*delim = (isopen?left:right),	/* delim we want,  { if isopen */
 6652: 	*match = (isopen?right:left),	/* matching delim, } if isopen */
 6653: 	*brace = NULL;			/* ptr to delim returned to caller */
 6654: int	inc = (isopen?-1:+1);		/* pointer increment */
 6655: int	level = 1;			/* nesting level, for {{}\command} */
 6656: char	*ptr = command;			/* start search here */
 6657: int	setbrace = 1;			/* true to set {}'s if none found */
 6658: /* -------------------------------------------------------------------------
 6659: search for left opening { before command, or right closing } after command
 6660: -------------------------------------------------------------------------- */
 6661: while ( 1 )				/* search for brace, or until end */
 6662:   {
 6663:   /* --- next char to check for delim --- */
 6664:   ptr += inc;				/* bump ptr left or right */
 6665:   /* --- check for beginning or end of expression --- */
 6666:   if ( isopen )				/* going left, check for beginning */
 6667:        { if ( ptr < expression ) break;	} /* went before start of string */
 6668:   else { if ( *ptr == '\000' ) break; }	/* went past end of string */
 6669:   /* --- don't check this char if it's escaped --- */
 6670:   if ( !isopen || ptr>expression )	/* very first char can't be escaped*/
 6671:     if ( isthischar(*(ptr-1),ESCAPE) )	/* escape char precedes current */
 6672:       continue;				/* so don't check this char */
 6673:   /* --- check for delim --- */
 6674:   if ( isthischar(*ptr,delim) )		/* found delim */
 6675:     if ( --level == 0 )			/* and it's not "internally" nested*/
 6676:       {	brace = ptr;			/* set ptr to brace */
 6677: 	goto end_of_job; }		/* and return it to caller */
 6678:   /* --- check for matching delim --- */
 6679:   if ( isthischar(*ptr,match) )		/* found matching delim */
 6680:     level++;				/* so bump nesting level */
 6681:   } /* --- end-of-while(1) --- */
 6682: end_of_job:
 6683:   if ( brace == (char *)NULL )		/* open{ or close} not found */
 6684:     if ( setbrace )			/* want to force one at start/end? */
 6685:       brace = ptr;			/* { before expressn, } after cmmnd*/
 6686:   return ( brace );			/*back to caller with delim or NULL*/
 6687: } /* --- end-of-function findbraces() --- */
 6688: 
 6689: 
 6690: /* ==========================================================================
 6691:  * Function:	strpspn ( char *s, char *reject, char *segment )
 6692:  * Purpose:	finds the initial segment of s containing no chars
 6693:  *		in reject that are outside (), [] and {} parens, e.g.,
 6694:  *		   strpspn("abc(---)def+++","+-",segment) returns
 6695:  *		   segment="abc(---)def" and a pointer to the first '+' in s
 6696:  *		because the -'s are enclosed in () parens.
 6697:  * --------------------------------------------------------------------------
 6698:  * Arguments:	s (I)		(char *)pointer to null-terminated string
 6699:  *				whose initial segment is desired
 6700:  *		reject (I)	(char *)pointer to null-terminated string
 6701:  *				containing the "reject chars"
 6702:  *		segment (O)	(char *)pointer returning null-terminated
 6703:  *				string comprising the initial segment of s
 6704:  *				that contains non-rejected chars outside
 6705:  *				(),[],{} parens, i.e., all the chars up to
 6706:  *				but not including the returned pointer.
 6707:  *				(That's the entire string if no non-rejected
 6708:  *				chars are found.)
 6709:  * --------------------------------------------------------------------------
 6710:  * Returns:	( char * )	pointer to first reject-char found in s
 6711:  *				outside parens, or a pointer to the
 6712:  *				terminating '\000' of s if there are
 6713:  *				no reject chars in s outside all () parens.
 6714:  * --------------------------------------------------------------------------
 6715:  * Notes:     o	the return value is _not_ like strcspn()'s
 6716:  *	      o	improperly nested (...[...)...] are not detected,
 6717:  *		but are considered "balanced" after the ]
 6718:  *	      o	if reject not found, segment returns the entire string s
 6719:  *	      o	leading/trailing whitespace is trimmed from returned segment
 6720:  * ======================================================================= */
 6721: /* --- entry point --- */
 6722: char	*strpspn ( char *s, char *reject, char *segment )
 6723: {
 6724: /* -------------------------------------------------------------------------
 6725: Allocations and Declarations
 6726: -------------------------------------------------------------------------- */
 6727: char	*ps = s;			/* current pointer into s */
 6728: int	depth = 0;			/* () paren nesting level */
 6729: int	seglen=0, maxseg=2047;		/* segment length, max allowed */
 6730: /* -------------------------------------------------------------------------
 6731: initialization
 6732: -------------------------------------------------------------------------- */
 6733: /* --- check arguments --- */
 6734: if ( isempty(s) )			/* no input string supplied */
 6735:   goto end_of_job;			/* no reject chars supplied */
 6736: /* -------------------------------------------------------------------------
 6737: find first char from s outside () parens (and outside ""'s) and in reject
 6738: -------------------------------------------------------------------------- */
 6739: while ( *ps != '\000' ) {		/* search till end of input string */
 6740:   if ( isthischar(*ps,"([{") ) depth++;	/* push another paren */
 6741:   if ( isthischar(*ps,")]}") ) depth--;	/* or pop another paren */
 6742:   if ( depth < 1 ) {			/* we're outside all parens */
 6743:     if ( isempty(reject) ) break;	/* no reject so break immediately */
 6744:     if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
 6745:   if ( segment != NULL )		/* caller gave us segment */
 6746:     if ( seglen < maxseg )		/* don't overflow segment buffer */
 6747:       memcpy(segment+seglen,ps,1);	/* so copy non-reject char */
 6748:   seglen += 1;  ps += 1;		/* bump to next char */
 6749:   } /* --- end-of-while(*ps!=0) --- */
 6750: end_of_job:
 6751:   if ( segment != NULL ) {		/* caller gave us segment */
 6752:     if ( isempty(reject) ) {		/* no reject char */
 6753:       segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
 6754:     segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
 6755:     trimwhite(segment); }		/* trim leading/trailing whitespace*/
 6756:   return ( ps );			/* back to caller */
 6757: } /* --- end-of-function strpspn() --- */
 6758: 
 6759: 
 6760: /* ==========================================================================
 6761:  * Function:	isstrstr ( char *string, char *snippets, int iscase )
 6762:  * Purpose:	determine whether any substring of 'string'
 6763:  *		matches any of the comma-separated list of 'snippets',
 6764:  *		ignoring case if iscase=0.
 6765:  * --------------------------------------------------------------------------
 6766:  * Arguments:	string (I)	char * containing null-terminated
 6767:  *				string that will be searched for
 6768:  *				any one of the specified snippets
 6769:  *		snippets (I)	char * containing null-terminated,
 6770:  *				comma-separated list of snippets
 6771:  *				to be searched for in string
 6772:  *		iscase (I)	int containing 0 for case-insensitive
 6773:  *				comparisons, or 1 for case-sensitive
 6774:  * --------------------------------------------------------------------------
 6775:  * Returns:	( int )		1 if any snippet is a substring of
 6776:  *				string, 0 if not
 6777:  * --------------------------------------------------------------------------
 6778:  * Notes:     o
 6779:  * ======================================================================= */
 6780: /* --- entry point --- */
 6781: int	isstrstr ( char *string, char *snippets, int iscase )
 6782: {
 6783: /* -------------------------------------------------------------------------
 6784: Allocations and Declarations
 6785: -------------------------------------------------------------------------- */
 6786: int	status = 0;			/*1 if any snippet found in string*/
 6787: char	snip[256], *snipptr = snippets,	/* munge through each snippet */
 6788: 	delim = ',', *delimptr = NULL;	/* separated by delim's */
 6789: char	stringcp[4096], *cp = stringcp;	/*maybe lowercased copy of string*/
 6790: /* -------------------------------------------------------------------------
 6791: initialization
 6792: -------------------------------------------------------------------------- */
 6793: /* --- arg check --- */
 6794: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
 6795: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
 6796: /* --- copy string and lowercase it if case-insensitive --- */
 6797: strninit(stringcp,string,4064);		/* local copy of string */
 6798: if ( !iscase )				/* want case-insensitive compares */
 6799:   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
 6800:     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
 6801: /* -------------------------------------------------------------------------
 6802: extract each snippet and see if it's a substring of string
 6803: -------------------------------------------------------------------------- */
 6804: while ( snipptr != NULL )		/* while we still have snippets */
 6805:   {
 6806:   /* --- extract next snippet --- */
 6807:   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
 6808:   ==   NULL )				/*not found following last snippet*/
 6809:     { strninit(snip,snipptr,255);	/* local copy of last snippet */
 6810:       snipptr = NULL; }			/* signal end-of-string */
 6811:   else					/* snippet ends just before delim */
 6812:     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
 6813:       memcpy(snip,snipptr,sniplen);	/* local copy of snippet chars */
 6814:       snip[sniplen] = '\000';		/* null-terminated snippet */
 6815:       snipptr = delimptr + 1; }		/* next snippet starts after delim */
 6816:   /* --- lowercase snippet if case-insensitive --- */
 6817:   if ( !iscase )			/* want case-insensitive compares */
 6818:     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
 6819:       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
 6820:   /* --- check if snippet in string --- */
 6821:   if ( strstr(stringcp,snip) != NULL )	/* found snippet in string */
 6822:     { status = 1;			/* so reset return status */
 6823:       break; }				/* no need to check any further */
 6824:   } /* --- end-of-while(*snipptr!=0) --- */
 6825: end_of_job: return ( status );		/*1 if snippet found in list, else 0*/
 6826: } /* --- end-of-function isstrstr() --- */
 6827: 
 6828: 
 6829: /* ==========================================================================
 6830:  * Function:	isnumeric ( s )
 6831:  * Purpose:	determine if s is an integer
 6832:  * --------------------------------------------------------------------------
 6833:  * Arguments:	s (I)		(char *)pointer to null-terminated string
 6834:  *				that's checked for a leading + or -
 6835:  *				followed by digits
 6836:  * --------------------------------------------------------------------------
 6837:  * Returns:	( int )		1 if s is numeric, 0 if it is not
 6838:  * --------------------------------------------------------------------------
 6839:  * Notes:     o
 6840:  * ======================================================================= */
 6841: /* --- entry point --- */
 6842: int	isnumeric ( char *s )
 6843: {
 6844: /* -------------------------------------------------------------------------
 6845: determine whether s is an integer
 6846: -------------------------------------------------------------------------- */
 6847: int	status = 0;			/* return 0 if not numeric, 1 if is*/
 6848: char	*p = s;				/* pointer into s */
 6849: if ( isempty(s) ) goto end_of_job;	/* missing arg or empty string */
 6850: skipwhite(p);				/*check for leading +or- after space*/
 6851: if ( *p=='+' || *p=='-' ) p++;		/* skip leading + or - */
 6852: for ( ; *p != '\000'; p++ ) {		/* check rest of s for digits */
 6853:   if ( isdigit(*p) ) continue;		/* still got uninterrupted digits */
 6854:   if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
 6855:   skipwhite(p);				/* skip all subsequent whitespace */
 6856:   if ( *p == '\000' ) break;		/* trailing whitespace okay */
 6857:   goto end_of_job;			/* embedded whitespace non-numeric */
 6858:   } /* --- end-of-for(*p) --- */
 6859: status = 1;				/* numeric after checks succeeded */
 6860: end_of_job:
 6861:   return ( status );			/*back to caller with 1=string, 0=no*/
 6862: } /* --- end-of-function isnumeric() --- */
 6863: 
 6864: 
 6865: /* ==========================================================================
 6866:  * Function:	evalterm ( STORE *store, char *term )
 6867:  * Purpose:	evaluates a term
 6868:  * --------------------------------------------------------------------------
 6869:  * Arguments:	store (I/O)	STORE * containing environment
 6870:  *				in which term is to be evaluated
 6871:  *		term (I)	char * containing null-terminated string
 6872:  *				with a term like "3" or "a" or "a+3"
 6873:  *				whose value is to be determined
 6874:  * --------------------------------------------------------------------------
 6875:  * Returns:	( int )		value of term,
 6876:  *				or NOVALUE for any error
 6877:  * --------------------------------------------------------------------------
 6878:  * Notes:     o	Also evaluates index?a:b:c:etc, returning a if index<=0,
 6879:  *		b if index=1, etc, and the last value if index is too large.
 6880:  *		Each a:b:c:etc can be another expression, including another
 6881:  *		(index?a:b:c:etc) which must be enclosed in parentheses.
 6882:  * ======================================================================= */
 6883: /* --- entry point --- */
 6884: int	evalterm ( STORE *store, char *term )
 6885: {
 6886: /* -------------------------------------------------------------------------
 6887: Allocations and Declarations
 6888: -------------------------------------------------------------------------- */
 6889: int	termval = 0;			/* term value returned to caller */
 6890: char	token[2048] = "\000",		/* copy term */
 6891: 	*delim = NULL;			/* delim '(' or '?' in token */
 6892: /*int	evalwff(),*/			/* recurse to evaluate terms */
 6893: /*	evalfunc();*/			/* evaluate function(arg1,arg2,...)*/
 6894: char	*strpspn();			/* span delims */
 6895: int	getstore();			/* lookup variables */
 6896: int	isnumeric();			/* numeric=constant, else variable */
 6897: static	int evaltermdepth = 0;		/* recursion depth */
 6898: int	novalue = (-89123456);		/* dummy (for now) error signal */
 6899: /* -------------------------------------------------------------------------
 6900: Initialization
 6901: -------------------------------------------------------------------------- */
 6902: if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
 6903: if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
 6904: skipwhite(term);			/* skip any leading whitespace */
 6905: /* -------------------------------------------------------------------------
 6906: First look for conditional of the form term?term:term:...
 6907: -------------------------------------------------------------------------- */
 6908: /* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
 6909: delim = strpspn(term,"?",token);	/* chars preceding ? outside () */
 6910: if ( *delim != '\000' ) {		/* found conditional expression */
 6911:   int ncolons = 0;			/* #colons we've found so far */
 6912:   if ( *token != '\000' )		/* evaluate "index" value on left */
 6913:     if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
 6914:     == novalue ) goto end_of_job;	/* return error if failed */
 6915:   while ( *delim != '\000' ) {		/* still have chars in term */
 6916:     delim++; *token='\000';		/* initialize for next "value:" */
 6917:     if ( *delim == '\000' ) break;	/* no more values */
 6918:     delim = strpspn(delim,":",token);	/* chars preceding : outside () */
 6919:     if ( ncolons++ >= termval ) break;	/* have corresponding term */
 6920:     } /* --- end-of-while(*delim!='\000')) --- */
 6921:   if ( *token != '\000' )		/* have x:x:value:x:x on right */
 6922:     termval=evalterm(store,token);	/* so evaluate it */
 6923:   goto end_of_job;			/* return result to caller */
 6924:   } /* --- end-of-if(*delim!='\000')) --- */
 6925: /* -------------------------------------------------------------------------
 6926: evaluate a+b recursively
 6927: -------------------------------------------------------------------------- */
 6928: /* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
 6929: term = strpspn(term,"/+-*%",token);	/* chars preceding /+-*% outside ()*/
 6930: /* --- evaluate a+b, a-b, etc --- */
 6931: if ( *term != '\000' ) {		/* found arithmetic operation */
 6932:   int leftval=0, rightval=0;		/* init leftval for unary +a or -a */
 6933:   if ( *token != '\000' )		/* or eval for binary a+b or a-b */
 6934:     if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
 6935:     == novalue ) goto end_of_job;	/* return error if failed */
 6936:   if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
 6937:   == novalue ) goto end_of_job;		/* return error if failed */
 6938:   switch ( *term ) {			/* perform requested arithmetic */
 6939:     default: break;			/* internal error */
 6940:     case '+': termval = leftval+rightval;  break;  /* addition */
 6941:     case '-': termval = leftval-rightval;  break;  /* subtraction */
 6942:     case '*': termval = leftval*rightval;  break;  /* multiplication */
 6943:     case '/': if ( rightval != 0 )	/* guard against divide by zero */
 6944:                 termval = leftval/rightval;  break; /* integer division */
 6945:     case '%': if ( rightval != 0 )	/* guard against divide by zero */
 6946:                 termval = leftval%rightval;  break; /*left modulo right */
 6947:     } /* --- end-of-switch(*relation) --- */
 6948:   goto end_of_job;			/* return result to caller */
 6949:   } /* --- end-of-if(*term!='\000')) --- */
 6950: /* -------------------------------------------------------------------------
 6951: check for parenthesized expression or term of the form function(arg1,arg2,...)
 6952: -------------------------------------------------------------------------- */
 6953: if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
 6954:   /* --- strip trailing paren (if there hopefully is one) --- */
 6955:   int  toklen = strlen(token);		/* total #chars in token */
 6956:   if ( token[toklen-1] == ')' )		/* found matching ) at end of token*/
 6957:     token[--toklen] = '\000';		/* remove trailing ) */
 6958:   /* --- handle parenthesized subexpression --- */
 6959:   if ( *token == '(' ) {		/* have parenthesized expression */
 6960:     strsqueeze(token,1);		/* so squeeze out leading ( */
 6961:     /* --- evaluate edited term --- */
 6962:     trimwhite(token);			/* trim leading/trailing whitespace*/
 6963:     termval = evalterm(store,token); }	/* evaluate token recursively */
 6964:   /* --- handle function(arg1,arg2,...) --- */
 6965:   else {				/* have function(arg1,arg2,...) */
 6966:     *delim = '\000';			/* separate function name and args */
 6967:     /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
 6968:   goto end_of_job; }			/* return result to caller */
 6969: /* -------------------------------------------------------------------------
 6970: evaluate constants directly, or recursively evaluate variables, etc
 6971: -------------------------------------------------------------------------- */
 6972: if ( *token != '\000' ) {		/* empty string */
 6973:   if ( isnumeric(token) )		/* have a constant */
 6974:     termval = atoi(token);		/* convert ascii-to-int */
 6975:   else {				/* variable or "stored proposition"*/
 6976:     termval = getstore(store,token); }	/* look up token */
 6977:   } /* --- end-of-if(*token!=0) --- */
 6978: /* -------------------------------------------------------------------------
 6979: back to caller with truth value of proposition
 6980: -------------------------------------------------------------------------- */
 6981: end_of_job:
 6982:   /* --- back to caller --- */
 6983:   if ( evaltermdepth > 0 ) evaltermdepth--;  /* pop recursion depth */
 6984:   return ( termval );			/* back to caller with value */
 6985: } /* --- end-of-function evalterm() --- */
 6986: 
 6987: 
 6988: /* ==========================================================================
 6989:  * Function:	getstore ( store, identifier )
 6990:  * Purpose:	finds identifier in store and returns corresponding value
 6991:  * --------------------------------------------------------------------------
 6992:  * Arguments:	store (I)	(STORE *)pointer to store containing
 6993:  *				the desired identifier
 6994:  *		identifier (I)	(char *)pointer to null-terminated string
 6995:  *				containing the identifier whose value
 6996:  *				is to be returned
 6997:  * --------------------------------------------------------------------------
 6998:  * Returns:	( int )		identifier's corresponding value,
 6999:  *				or 0 if identifier not found (or any error)
 7000:  * --------------------------------------------------------------------------
 7001:  * Notes:     o
 7002:  * ======================================================================= */
 7003: /* --- entry point --- */
 7004: int	getstore ( STORE *store, char *identifier )
 7005: {
 7006: /* -------------------------------------------------------------------------
 7007: Allocations and Declarations
 7008: -------------------------------------------------------------------------- */
 7009: int	value = 0;		/* store[istore].value for identifier */
 7010: int	istore=0;		/* store[] index containing identifier */
 7011: char	seek[512], hide[512];	/* identifier arg, identifier in store */
 7012: /* --- first check args --- */
 7013: if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
 7014: strninit(seek,identifier,500);	/* local copy of caller's identifier */
 7015: trimwhite(seek);		/* remove leading/trailing whitespace */
 7016: /* --- loop over store --- */
 7017: for ( istore=0; istore<MAXSTORE; istore++ ) { /* until end-of-table */
 7018:   char *idstore = store[istore].identifier; /* ptr to identifier in store */
 7019:   if ( isempty(idstore) )	/* empty id signals eot */
 7020:     break;			/* de-reference any default/error value */
 7021:   strninit(hide,idstore,500);	/* local copy of store[] identifier */
 7022:   trimwhite(hide);		/* remove leading/trailing whitespace */
 7023:   if ( !strcmp(hide,seek) )	/* found match */
 7024:     break;			/* de-reference corresponding value */
 7025:   } /* --- end-of-for(istore) --- */
 7026: if ( store[istore].value != NULL ) /* address of int supplied */
 7027:   value = *(store[istore].value);  /* return de-referenced int */
 7028: end_of_job:
 7029:   return ( value );			/* store->values[istore] or NULL */
 7030: } /* --- end-of-function getstore() --- */
 7031: 
 7032: 
 7033: /* ==========================================================================
 7034:  * Functions:	int  unescape_url ( char *url, int isescape )
 7035:  *		char x2c ( char *what )
 7036:  * Purpose:	unescape_url replaces 3-character sequences %xx in url
 7037:  *		    with the single character represented by hex xx.
 7038:  *		x2c returns the single character represented by hex xx
 7039:  *		    passed as a 2-character sequence in what.
 7040:  * --------------------------------------------------------------------------
 7041:  * Arguments:	url (I)		char * containing null-terminated
 7042:  *				string with embedded %xx sequences
 7043:  *				to be converted.
 7044:  *		isescape (I)	int containing 1 to _not_ unescape
 7045:  *				\% sequences (0 would be NCSA default)
 7046:  *		what (I)	char * whose first 2 characters are
 7047:  *				interpreted as ascii representations
 7048:  *				of hex digits.
 7049:  * --------------------------------------------------------------------------
 7050:  * Returns:	( int )		unescape_url always returns 0.
 7051:  *		( char )	x2c returns the single char
 7052:  *				corresponding to hex xx passed in what.
 7053:  * --------------------------------------------------------------------------
 7054:  * Notes:     o	These two functions were taken verbatim from util.c in
 7055:  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
 7056:  *	      o	Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
 7057:  *		so unescape_url() can be safely applied to input which may or
 7058:  *		may not have been url-encoded.  (Note: currently, all calls
 7059:  *		to unescape_url() pass iescape=0, so it's not used.)
 7060:  *	      o	Added +++'s to blank xlation on 24-Sep-06
 7061:  *	      o	Added ^M,^F,etc to blank xlation 0n 01-Oct-06
 7062:  * ======================================================================= */
 7063: /* --- entry point --- */
 7064: int unescape_url(char *url, int isescape) {
 7065:     int x=0,y=0,prevescape=0,gotescape=0;
 7066:     int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
 7067:     int strreplace();			/* replace + with blank, if needed */
 7068:     char x2c();
 7069:     static char *hex="0123456789ABCDEFabcdef";
 7070:     /* ---
 7071:      * xlate ctrl chars to blanks
 7072:      * -------------------------- */
 7073:     if ( 1 ) {				/* xlate ctrl chars to blanks */
 7074:       char *ctrlchars = "\n\t\v\b\r\f\a\015";
 7075:       int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
 7076:       int  urllen = strlen(url);	/* total length of url string */
 7077:       /* --- first, entirely remove ctrlchars from beginning and end --- */
 7078:       if ( seglen > 0 ) {		/*have ctrlchars at start of string*/
 7079: 	strsqueeze(url,seglen);		/* squeeze out initial ctrlchars */
 7080: 	urllen -= seglen; }		/* string is now shorter */
 7081:       while ( --urllen >= 0 )		/* now remove ctrlchars from end */
 7082: 	if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
 7083: 	  url[urllen] = '\000';		/* re-terminate string before it */
 7084: 	else break;			/* or we're done */
 7085:       urllen++;				/* length of url string */
 7086:       /* --- now, replace interior ctrlchars with ~ blanks --- */
 7087:       while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
 7088: 	url[seglen] = '~';		/* replace ctrlchar with ~ */
 7089:       } /* --- end-of-if(1) --- */
 7090:     /* ---
 7091:      * xlate +'s to blanks if requested or if deemed necessary
 7092:      * ------------------------------------------------------- */
 7093:     if ( isplusblank == (-1) ) {	/*determine whether or not to xlate*/
 7094:       char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
 7095: 	"+=+","+-+", NULL };
 7096:       int  isearch = 0,			/* searchfor[] index */
 7097: 	   nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
 7098:       /* --- locate occurrences of searchfor[] strings in url --- */
 7099:       for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
 7100: 	char *psearch = url;		/* start search at beginning */
 7101: 	nfound[isearch] = 0;		/* init #occurrences count */
 7102: 	while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
 7103: 	  nfound[isearch] += 1;		/* count another occurrence */
 7104: 	  psearch += strlen(searchfor[isearch]); } /*resume search after it*/
 7105: 	} /* --- end-of-for(isearch) --- */
 7106:       /* --- apply some common-sense logic --- */
 7107:       if ( nfound[0] + nfound[1] > 0 )	/* we have actual " "s or "%20"s */
 7108: 	isplusblank = xlateplus = 0;	/* so +++'s aren't blanks */
 7109:       if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
 7110:         if ( isplusblank != 0 )		/* and haven't disabled xlation */
 7111: 	  isplusblank = xlateplus = 1;	/* so +++'s are blanks */
 7112: 	else				/* we have _both_ "%20" and "%2b" */
 7113: 	  xlateplus = 0; }		/* tough call */
 7114:       if ( nfound[4] + nfound[5] > 0	/* we have multiple ++'s */
 7115:       ||   nfound[6] + nfound[7] > 0 )	/* or we have a +=+ or +-+ */
 7116: 	if ( isplusblank != 0 )		/* and haven't disabled xlation */
 7117: 	  xlateplus = 1;		/* so xlate +++'s to blanks */
 7118:       } /* --- end-of-if(isplusblank==-1) --- */
 7119:     if ( xlateplus > 0 ) {		/* want +'s xlated to blanks */
 7120:       char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
 7121:       while ( xlateplus > 0 ) {		/* still have +++'s to xlate */
 7122: 	char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
 7123: 	plusses[xlateplus] = '\000';	/* null-terminate +++'s */
 7124: 	strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
 7125: 	xlateplus--;			/* next shorter +++ string */
 7126: 	} /* --- end-of-while(xlateplus>0) --- */
 7127:       } /* --- end-of-if(xlateplus) --- */
 7128:     isplusblank = 0;			/* don't iterate this xlation */
 7129:     /* ---
 7130:      * xlate %nn to corresponding char
 7131:      * ------------------------------- */
 7132:     for(;url[y];++x,++y) {
 7133: 	gotescape = prevescape;
 7134: 	prevescape = (url[x]=='\\');
 7135: 	if((url[x] = url[y]) == '%')
 7136: 	 if(!isescape || !gotescape)
 7137: 	  if(isthischar(url[y+1],hex)
 7138: 	  && isthischar(url[y+2],hex))
 7139: 	    { url[x] = x2c(&url[y+1]);
 7140: 	      y+=2; }
 7141:     }
 7142:     url[x] = '\0';
 7143:     return 0;
 7144: } /* --- end-of-function unescape_url() --- */
 7145: /* --- entry point --- */
 7146: char x2c(char *what) {
 7147:     char digit;
 7148:     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
 7149:     digit *= 16;
 7150:     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
 7151:     return(digit);
 7152: } /* --- end-of-function x2c() --- */
 7153: #endif /* PART2 */
 7154: 
 7155: /* ---
 7156:  * PART3
 7157:  * ------ */
 7158: #if !defined(PARTS) || defined(PART3)
 7159: /* ==========================================================================
 7160:  * Function:	rasterize ( expression, size )
 7161:  * Purpose:	returns subraster corresponding to (a valid LaTeX) expression
 7162:  *		at font size
 7163:  * --------------------------------------------------------------------------
 7164:  * Arguments:	expression (I)	char * to first char of null-terminated
 7165:  *				string containing valid LaTeX expression
 7166:  *				to be rasterized
 7167:  *		size (I)	int containing 0-4 default font size
 7168:  * --------------------------------------------------------------------------
 7169:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
 7170:  *				or NULL for any parsing error.
 7171:  * --------------------------------------------------------------------------
 7172:  * Notes:     o	This is mimeTeX's "main" reusable entry point.  Easy to use:
 7173:  *		just call it with a LaTeX expression, and get back a bitmap
 7174:  *		of that expression.  Then do what you want with the bitmap.
 7175:  * ======================================================================= */
 7176: /* --- entry point --- */
 7177: subraster *rasterize ( char *expression, int size )
 7178: {
 7179: /* -------------------------------------------------------------------------
 7180: Allocations and Declarations
 7181: -------------------------------------------------------------------------- */
 7182: char	*preamble(), pretext[512];	/* process preamble, if present */
 7183: char	chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
 7184: 	*subexpr = chartoken;		/* token may be parenthesized expr */
 7185: int	isbrace();			/* check subexpr for braces */
 7186: mathchardef *symdef, *get_symdef();	/*get mathchardef struct for symbol*/
 7187: int	ligdef, get_ligature();		/*get symtable[] index for ligature*/
 7188: int	natoms=0;			/* #atoms/tokens processed so far */
 7189: int	type_raster();			/* display debugging output */
 7190: subraster *rasterize(),			/* recurse */
 7191: 	*rastparen(),			/* handle parenthesized subexpr's */
 7192: 	*rastlimits();			/* handle sub/superscripted expr's */
 7193: subraster *rastcat(),			/* concatanate atom subrasters */
 7194: 	*subrastcpy(),			/* copy final result if a charaster*/
 7195: 	*new_subraster();		/* new subraster for isstring mode */
 7196: subraster *get_charsubraster(),		/* character subraster */
 7197: 	*sp=NULL, *prevsp=NULL,		/* raster for current, prev char */
 7198: 	*expraster = (subraster *)NULL;	/* raster returned to caller */
 7199: int	delete_subraster();		/* free everything before returning*/
 7200: int	family = fontinfo[fontnum].family; /* current font family */
 7201: int	isleftscript = 0,		/* true if left-hand term scripted */
 7202: 	wasscripted = 0,		/* true if preceding token scripted*/
 7203: 	wasdelimscript = 0;		/* true if preceding delim scripted*/
 7204: /*int	pixsz = 1;*/			/*default #bits per pixel, 1=bitmap*/
 7205: char	*strdetex();			/* detex token for error message */
 7206: /* --- global values saved/restored at each recursive iteration --- */
 7207: int	wasstring = isstring,		/* initial isstring mode flag */
 7208: 	wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
 7209: 	oldfontnum = fontnum,		/* initial font family */
 7210: 	oldfontsize = fontsize,		/* initial fontsize */
 7211: 	olddisplaysize = displaysize,	/* initial \displaystyle size */
 7212: 	oldshrinkfactor = shrinkfactor,	/* initial shrinkfactor */
 7213: 	oldsmashmargin = smashmargin,	/* initial smashmargin */
 7214: 	oldissmashdelta = issmashdelta, /* initial issmashdelta */
 7215: 	oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
 7216: 	oldisscripted = isscripted,	/* initial isscripted */
 7217: 	*oldworkingparam = workingparam; /* initial working parameter */
 7218: subraster *oldworkingbox = workingbox,	/* initial working box */
 7219: 	*oldleftexpression = leftexpression; /*left half rasterized so far*/
 7220: double	oldunitlength = unitlength;	/* initial unitlength */
 7221: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
 7222: /* -------------------------------------------------------------------------
 7223: initialization
 7224: -------------------------------------------------------------------------- */
 7225: recurlevel++;				/* wind up one more recursion level*/
 7226: leftexpression = NULL;			/* no leading left half yet */
 7227: isreplaceleft = 0;			/* reset replaceleft flag */
 7228: if(1)fraccenterline = NOVALUE;		/* reset \frac baseline signal */
 7229: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
 7230: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
 7231: rastlift = 0;				/* reset global rastraise() lift */
 7232: if ( msgfp!=NULL && msglevel >= 9 ) {	/*display expression for debugging*/
 7233:  fprintf(msgfp,
 7234:  "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
 7235:  recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
 7236: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
 7237: /* -------------------------------------------------------------------------
 7238: preocess optional $-terminated preamble preceding expression
 7239: -------------------------------------------------------------------------- */
 7240: expression = preamble(expression,&size,pretext); /* size may be modified */
 7241: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
 7242: fontsize = size;			/* start at requested size */
 7243: if ( isdisplaystyle == 1 )		/* displaystyle enabled but not set*/
 7244:  if ( !ispreambledollars )		/* style fixed by $$...$$'s */
 7245:   isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
 7246: /* -------------------------------------------------------------------------
 7247: build up raster one character (or subexpression) at a time
 7248: -------------------------------------------------------------------------- */
 7249: while ( 1 )
 7250:   {
 7251:   /* --- kludge for \= cyrillic ligature --- */
 7252:   isligature = 0;			/* no ligature found yet */
 7253:   family = fontinfo[fontnum].family;	/* current font family */
 7254:   if ( family == CYR10 )		/* may have cyrillic \= ligature */
 7255:    if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
 7256:    >=    0 )				/* got some ligature */
 7257:     if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
 7258:      isligature = 1;			/* signal \= ligature */
 7259:   /* --- get next character/token or subexpression --- */
 7260:   subexprptr = expression;		/* ptr within expression to subexpr*/
 7261:   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
 7262:   subexpr = chartoken;			/* "local" copy of chartoken ptr */
 7263:   leftsymdef = NULL;			/* no character identified yet */
 7264:   sp = NULL;				/* no subraster yet */
 7265:   size = fontsize;			/* in case reset by \tiny, etc */
 7266:   /*isleftscript = isdelimscript;*/	/*was preceding term scripted delim*/
 7267:   wasscripted = isscripted;		/* true if preceding token scripted*/
 7268:   wasdelimscript = isdelimscript;	/* preceding \right delim scripted */
 7269:   if(1)isscripted = 0;			/* no subscripted expression yet */
 7270:   isdelimscript = 0;			/* reset \right delim scripted flag*/
 7271:   /* --- debugging output --- */
 7272:   if ( msgfp!=NULL && msglevel >= 9 ) {	/* display chartoken for debugging */
 7273:    fprintf(msgfp,
 7274:    "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
 7275:    recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
 7276:   if ( expression == NULL		/* no more tokens */
 7277:   &&   *subexpr == '\000' ) break;	/* and this token empty */
 7278:   if ( *subexpr == '\000' ) break;	/* enough if just this token empty */
 7279:   /* --- check for parenthesized subexpression --- */
 7280:   if ( isbrace(subexpr,LEFTBRACES,1) )	/* got parenthesized subexpression */
 7281:     { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
 7282:       ==   NULL )  continue; }		/* flush it if failed to rasterize */
 7283:   else /* --- single-character atomic token --- */
 7284:    if ( !isthischar(*subexpr,SCRIPTS) )	/* scripts handled below */
 7285:     {
 7286:     /* --- first check for opening $ in \text{ if $n-m$ even} --- */
 7287:     if ( istextmode			/* we're in \text mode */
 7288:     &&   *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
 7289:      char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
 7290:      int  exprlen = 0;			/* length of $expression$ */
 7291:      int  textfontnum = fontnum;	/* current text font number */
 7292:      /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
 7293:      if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
 7294:        exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
 7295:      else {				/* no closing $ found */
 7296:        exprlen = strlen(expression);	/* just assume entire expression */
 7297:        endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
 7298:      exprlen = min2(exprlen,MAXSUBXSZ);	/* don't overflow mathexpr[] */
 7299:      if ( exprlen > 0 ) {		/* have something between $$ */
 7300:        memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
 7301:        mathexpr[exprlen] = '\000';	/* null-terminate it */
 7302:        fontnum = 0;			/* set math mode */
 7303:        sp = rasterize(mathexpr,size);	/* and rasterize $expression$ */
 7304:        fontnum = textfontnum; }		/* set back to text mode */
 7305:      expression = endptr+1;		/* push expression past closing $ */
 7306:      } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
 7307:     else
 7308:      /* --- otherwise, look up mathchardef for atomic token in table --- */
 7309:      if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
 7310:      ==  NULL )				/* lookup failed */
 7311:       { char literal[512] = "[?]";	/*display for unrecognized literal*/
 7312:         int  oldfontnum = fontnum;	/* error display in default mode */
 7313:         if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
 7314: 	 { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
 7315: 	   chartoken); fflush(msgfp); }
 7316:         sp = (subraster *)NULL;		/* init to signal failure */
 7317:         if ( warninglevel < 1 ) continue; /* warnings not wanted */
 7318:         fontnum = 0;			/* reset from \mathbb, etc */
 7319:         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
 7320: 	 { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
 7321: 	   strcpy(literal,"{\\rm~[");	/* init error message token */
 7322: 	   strcat(literal,strdetex(chartoken,0)); /* detex the token */
 7323: 	   strcat(literal,"?]}"); }	/* add closing ? and brace */
 7324:         sp = rasterize(literal,size-1);	/* rasterize literal token */
 7325:         fontnum = oldfontnum;		/* reset font family */
 7326:         if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
 7327:      else /* --- check if we have special handler to process this token --- */
 7328:       if ( symdef->handler != NULL )	/* have a handler for this token */
 7329:        { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
 7330:          if ( (sp = (subraster *)	/* returned void* is subraster* */
 7331: 	 (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
 7332: 	   continue; }			/* flush token if handler failed */
 7333:       else /* --- no handler, so just get subraster for this character --- */
 7334:        if ( !isstring )			/* rasterizing */
 7335: 	{ if ( isligature )		/* found a ligature */
 7336: 	   expression = subexprptr + strlen(symdef->symbol); /*push past it*/
 7337: 	  if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
 7338: 	  ==  NULL )  continue; }	/* flush token if failed */
 7339:        else				/* constructing ascii string */
 7340: 	{ char *symbol = symdef->symbol; /* symbol for ascii string */
 7341: 	  int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
 7342: 	  if ( symlen < 1 ) continue;	/* no symbol for ascii string */
 7343: 	  if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
 7344: 	  ==  NULL )  continue;		/* flush token if malloc failed */
 7345: 	  sp->type = ASCIISTRING;	/* set subraster type */
 7346: 	  sp->symdef = symdef;		/* and set symbol definition */
 7347: 	  sp->baseline = 1;		/* default (should be unused) */
 7348: 	  strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
 7349: 	  /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
 7350:     } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
 7351:   /* --- handle any super/subscripts following symbol or subexpression --- */
 7352:   sp = rastlimits(&expression,size,sp);
 7353:   isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
 7354:   /* --- debugging output --- */
 7355:   if ( msgfp!=NULL && msglevel >= 9 ) {	/* display raster for debugging */
 7356:    fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
 7357:    recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
 7358:    if ( msglevel >= 9 ) fprintf(msgfp,
 7359:     "  isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
 7360:     isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
 7361:    if ( msglevel >= 99 )
 7362:     if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
 7363:    fflush(msgfp); }			/* flush msgfp buffer */
 7364:   /* --- accumulate atom or parenthesized subexpression --- */
 7365:   if ( natoms < 1			/* nothing previous to concat */
 7366:   ||   expraster == NULL		/* or previous was complete error */
 7367:   ||   isreplaceleft )			/* or we're replacing previous */
 7368:     { if ( 1 && expraster!=NULL )	/* probably replacing left */
 7369: 	delete_subraster(expraster);	/* so first free original left */
 7370:       expraster = subrastcpy(sp);	/* copy static CHARASTER or left */
 7371:       isreplaceleft = 0; }		/* reset replacement flag */
 7372:   else					/*we've already built up atoms so...*/
 7373:    if ( sp != NULL ) {			/* ...if we have a new term */
 7374:     int prevsmashmargin = smashmargin;	/* save current smash margin */
 7375:     if ( isleftscript ) {		/* don't smash against scripts */
 7376:      isdelimscript = 0;			/* reset \right delim scripted flag*/
 7377:      if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
 7378:     expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
 7379:     smashmargin = prevsmashmargin; }	/* restore current smash margin */
 7380:   delete_subraster(prevsp);		/* free prev (if not a CHARASTER) */
 7381:   prevsp = sp;				/* current becomes previous */
 7382:   leftexpression = expraster;		/* left half rasterized so far */
 7383:   /* --- bump count --- */
 7384:   natoms++;				/* bump #atoms count */
 7385:   } /* --- end-of-while(expression!=NULL) --- */
 7386: /* -------------------------------------------------------------------------
 7387: back to caller with rasterized expression
 7388: -------------------------------------------------------------------------- */
 7389: end_of_job:
 7390:   delete_subraster(prevsp);		/* free last (if not a CHARASTER) */
 7391:   /* --- debugging output --- */
 7392:   if ( msgfp!=NULL && msglevel >= 999 )	/* display raster for debugging */
 7393:     { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
 7394:       recurlevel,natoms);
 7395:       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
 7396: 	type_raster(expraster->image,msgfp); /* display completed raster */
 7397:       fflush(msgfp); }			/* flush msgfp buffer */
 7398:   /* --- set final raster buffer --- */
 7399:   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
 7400:     { int type = expraster->type;	/* type of constructed image */
 7401:       if ( type != FRACRASTER )		/* leave \frac alone */
 7402: 	expraster->type = IMAGERASTER;	/* set type to constructed image */
 7403:       if ( istextmode )			/* but in text mode */
 7404:         expraster->type = blanksignal;	/* set type to avoid smash */
 7405:       expraster->size = fontsize; }	/* set original input font size */
 7406:   /* --- restore flags/values to original saved values --- */
 7407:   isstring = wasstring;			/* string mode reset */
 7408:   isdisplaystyle = wasdisplaystyle;	/* displaystyle mode reset */
 7409:   fontnum = oldfontnum;			/* font family reset */
 7410:   fontsize = oldfontsize;		/* fontsize reset */
 7411:   displaysize = olddisplaysize;		/* \displaystyle size reset */
 7412:   shrinkfactor = oldshrinkfactor;	/* shrinkfactor reset */
 7413:   smashmargin = oldsmashmargin;		/* smashmargin reset */
 7414:   issmashdelta = oldissmashdelta;	/* issmashdelta reset */
 7415:   isexplicitsmash = oldisexplicitsmash;	/* isexplicitsmash reset */
 7416:   isscripted = oldisscripted;		/* isscripted reset */
 7417:   workingparam = oldworkingparam;	/* working parameter reset */
 7418:   workingbox = oldworkingbox;		/* working box reset */
 7419:   leftexpression = oldleftexpression;	/* leftexpression reset */
 7420:   leftsymdef = oldleftsymdef;		/* leftsymdef reset */
 7421:   unitlength = oldunitlength;		/* unitlength reset */
 7422:   iunitlength = (int)(unitlength+0.5);	/* iunitlength reset */
 7423:   recurlevel--;				/* unwind one recursion level */
 7424:   /* --- return final subraster to caller --- */
 7425:   return ( expraster );
 7426: } /* --- end-of-function rasterize() --- */
 7427: 
 7428: 
 7429: /* ==========================================================================
 7430:  * Function:	rastparen ( subexpr, size, basesp )
 7431:  * Purpose:	parentheses handler, returns a subraster corresponding to
 7432:  *		parenthesized subexpression at font size
 7433:  * --------------------------------------------------------------------------
 7434:  * Arguments:	subexpr (I)	char **  to first char of null-terminated
 7435:  *				string beginning with a LEFTBRACES
 7436:  *				to be rasterized
 7437:  *		size (I)	int containing 0-7 default font size
 7438:  *		basesp (I)	subraster *  to character (or subexpression)
 7439:  *				immediately preceding leading left{
 7440:  *				(unused, but passed for consistency)
 7441:  * --------------------------------------------------------------------------
 7442:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
 7443:  *				or NULL for any parsing error
 7444:  * --------------------------------------------------------------------------
 7445:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
 7446:  *		but is called directly from rasterize(), as necessary.
 7447:  *	      o	Though subexpr is returned unchanged, it's passed as char **
 7448:  *		for consistency with other handlers.  Ditto, basesp is unused
 7449:  *		but passed for consistency
 7450:  * ======================================================================= */
 7451: /* --- entry point --- */
 7452: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
 7453: {
 7454: /* -------------------------------------------------------------------------
 7455: Allocations and Declarations
 7456: -------------------------------------------------------------------------- */
 7457: char	*expression = *subexpr;		/* dereference subexpr to get char* */
 7458: int	explen = strlen(expression);	/* total #chars, including parens */
 7459: int	isescape = 0,			/* true if parens \escaped */
 7460: 	isrightdot = 0,			/* true if right paren is \right. */
 7461: 	isleftdot = 0;			/* true if left paren is \left. */
 7462: char	left[32], right[32];		/* parens enclosing expresion */
 7463: char	noparens[MAXSUBXSZ+1];		/* get subexpr without parens */
 7464: subraster *rasterize(), *sp=NULL;	/* rasterize what's between ()'s */
 7465: int	isheight = 1;			/*true=full height, false=baseline*/
 7466: int	height,				/* height of rasterized noparens[] */
 7467: 	baseline;			/* and its baseline */
 7468: int	family = /*CMSYEX*/ CMEX10;	/* family for paren chars */
 7469: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
 7470: subraster *rastcat();			/* concatanate subrasters */
 7471: int	delete_subraster();		/*in case of error after allocation*/
 7472: /* -------------------------------------------------------------------------
 7473: rasterize "interior" of expression, i.e., without enclosing parens
 7474: -------------------------------------------------------------------------- */
 7475: /* --- first see if enclosing parens are \escaped --- */
 7476: if ( isthischar(*expression,ESCAPE) )	/* expression begins with \escape */
 7477:   isescape = 1;				/* so set flag accordingly */
 7478: /* --- get expression *without* enclosing parens --- */
 7479: strcpy(noparens,expression);		/* get local copy of expression */
 7480: noparens[explen-(1+isescape)] = '\000';	/* null-terminate before right} */
 7481: strsqueeze(noparens,(1+isescape));	/* and then squeeze out left{ */
 7482: /* --- rasterize it --- */
 7483: if ( (sp = rasterize(noparens,size))	/*rasterize "interior" of expression*/
 7484: ==   NULL ) goto end_of_job;		/* quit if failed */
 7485: /* --- no need to add parentheses for unescaped { --- */
 7486: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
 7487:   goto end_of_job;			/* just return sp to caller */
 7488: /* -------------------------------------------------------------------------
 7489: obtain paren characters to enclose noparens[] raster with
 7490: -------------------------------------------------------------------------- */
 7491: /* --- first get left and right parens from expression --- */
 7492: memset(left,0,16);  memset(right,0,16);	/* init parens with nulls */
 7493: left[0] = *(expression+isescape);	/* left{ is 1st or 2nd char */
 7494: right[0] = *(expression+explen-1);	/* right} is always last char */
 7495: isleftdot  = (isescape && isthischar(*left,".")); /* true if \left. */
 7496: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
 7497: /* --- need height of noparens[] raster as minimum parens height --- */
 7498: height = (sp->image)->height;		/* height of noparens[] raster */
 7499: baseline = sp->baseline;		/* baseline of noparens[] raster */
 7500: if ( !isheight ) height = baseline+1;	/* parens only enclose baseline up */
 7501: /* --- get best-fit parentheses characters --- */
 7502: if ( !isleftdot )			/* if not \left. */
 7503:   lp = get_delim(left,height+1,family);	/* get left paren char */
 7504: if ( !isrightdot )			/* and if not \right. */
 7505:   rp = get_delim(right,height+1,family); /* get right paren char */
 7506: if ( (lp==NULL && !isleftdot)		/* check that we got left( */
 7507: ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
 7508:   { delete_subraster(sp);		/* if failed, free subraster */
 7509:     if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
 7510:     if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
 7511:     sp = (subraster *)NULL;		/* signal error to caller */
 7512:     goto end_of_job; }			/* and quit */
 7513: /* -------------------------------------------------------------------------
 7514: set paren baselines to center on noparens[] raster, and concat components
 7515: -------------------------------------------------------------------------- */
 7516: /* --- set baselines to center paren chars on raster --- */
 7517: if ( lp != NULL )			/* ignore for \left. */
 7518:   lp->baseline = baseline + ((lp->image)->height - height)/2;
 7519: if ( rp != NULL )			/* ignore for \right. */
 7520:   rp->baseline = baseline + ((rp->image)->height - height)/2;
 7521: /* --- concat lp||sp||rp to obtain final result --- */
 7522: if ( lp != NULL )			/* ignore \left. */
 7523:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
 7524: if ( sp != NULL )			/* succeeded or ignored \left. */
 7525:   if ( rp != NULL )			/* ignore \right. */
 7526:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
 7527: /* --- back to caller --- */
 7528: end_of_job:
 7529:   return ( sp );
 7530: } /* --- end-of-function rastparen() --- */
 7531: 
 7532: 
 7533: /* ==========================================================================
 7534:  * Function:	rastlimits ( expression, size, basesp )
 7535:  * Purpose:	\limits, \nolimts, _ and ^ handler,
 7536:  *		dispatches call to rastscripts() or to rastdispmath()
 7537:  *		as necessary, to handle sub/superscripts following symbol
 7538:  * --------------------------------------------------------------------------
 7539:  * Arguments:	expression (I)	char **  to first char of null-terminated
 7540:  *				LaTeX expression (unused/unchanged)
 7541:  *		size (I)	int containing base font size (not used,
 7542:  *				just stored in subraster)
 7543:  *		basesp (I)	subraster *  to current character (or
 7544:  *				subexpression) immediately preceding script
 7545:  *				indicator
 7546:  * --------------------------------------------------------------------------
 7547:  * Returns:	( subraster * )	ptr to subraster returned by rastscripts()
 7548:  *				or rastdispmath(), or NULL for any error
 7549:  * --------------------------------------------------------------------------
 7550:  * Notes:     o
 7551:  * ======================================================================= */
 7552: /* --- entry point --- */
 7553: subraster *rastlimits ( char **expression, int size, subraster *basesp )
 7554: {
 7555: /* -------------------------------------------------------------------------
 7556: Allocations and Declarations
 7557: -------------------------------------------------------------------------- */
 7558: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
 7559: 	*rastcat(),			/* may need to concat scripts */
 7560: 	*rasterize(),			/* may need to construct dummy base*/
 7561: 	*scriptsp = basesp,		/* and this will become the result */
 7562: 	*dummybase = basesp;		/* for {}_i construct a dummy base */
 7563: int	isdisplay = (-1);		/* set 1 for displaystyle, else 0 */
 7564: int	oldsmashmargin = smashmargin;	/* save original smashmargin */
 7565: int	type_raster();			/* display debugging output */
 7566: int	delete_subraster();		/* free dummybase, if necessary */
 7567: int	rastsmashcheck();		/* check if okay to smash scripts */
 7568: /* --- to check for \limits or \nolimits preceding scripts --- */
 7569: char	*texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
 7570: int	toklen=0;			/* strlen(limtoken) */
 7571: mathchardef *tokdef, *get_symdef();	/* mathchardef struct for limtoken */
 7572: int	class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
 7573: /* -------------------------------------------------------------------------
 7574: determine whether or not to use displaymath
 7575: -------------------------------------------------------------------------- */
 7576: scriptlevel++;				/* first, increment subscript level*/
 7577: *limtoken = '\000';			/* no token yet */
 7578: isscripted = 0;				/* signal term not (text) scripted */
 7579: if ( msgfp!=NULL && msglevel>=999 )
 7580:  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
 7581:    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
 7582: if ( isstring ) goto end_of_job;	/* no scripts for ascii string */
 7583: /* --- check for \limits or \nolimits --- */
 7584: skipwhite(exprptr);			/* skip white space before \limits */
 7585: if ( !isempty(exprptr) )		/* non-empty expression supplied */
 7586:   exprptr = texchar(exprptr,limtoken);	/* retrieve next token */
 7587: if ( *limtoken != '\000' )		/* have token */
 7588:  if ( (toklen=strlen(limtoken)) >= 3 )	/* which may be \[no]limits */
 7589:   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
 7590:   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
 7591:    if ( (tokdef= get_symdef(limtoken))	/* look up token to be sure */
 7592:    !=   NULL ) {			/* found token in table */
 7593:     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
 7594:       isdisplay = 1;			/* so explicitly set displaymath */
 7595:     else				/* wasn't \limits */
 7596:       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
 7597: 	isdisplay = 0; }		/* so explicitly reset displaymath */
 7598: /* --- see if we found \[no]limits --- */
 7599: if ( isdisplay != (-1) )		/* explicit directive found */
 7600:   *expression = exprptr;		/* so bump expression past it */
 7601: else					/* noexplicit directive */
 7602:   { isdisplay = 0;			/* init displaymath flag off */
 7603:     if ( isdisplaystyle ) {		/* we're in displaystyle math mode */
 7604:       if ( isdisplaystyle >= 5 )	/* and mode irrevocably forced true */
 7605: 	{ if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
 7606: 	    isdisplay = 1; }		/* set flag if mode forced true */
 7607:       else
 7608:        if ( isdisplaystyle >= 2 )	/*or mode forced conditionally true*/
 7609: 	{ if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
 7610: 	  &&   class!=OPENING  && class!=CLOSING  /*don't force ('s and )'s*/
 7611: 	  &&   class!=BINARYOP		/* don't force binary operators */
 7612: 	  &&   class!=NOVALUE )		/* finally, don't force "images" */
 7613: 	    isdisplay = 1; }		/* set flag if mode forced true */
 7614:        else				/* determine mode from base symbol */
 7615: 	if ( class == DISPOPER )	/* it's a displaystyle operator */
 7616: 	  isdisplay = 1; } }		/* so set flag */
 7617: /* -------------------------------------------------------------------------
 7618: dispatch call to create sub/superscripts
 7619: -------------------------------------------------------------------------- */
 7620: if ( isdisplay )			/* scripts above/below base symbol */
 7621:   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
 7622: else {					/* scripts alongside base symbol */
 7623:   if ( dummybase == NULL )		/* no base symbol preceding scripts*/
 7624:     dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
 7625:   issmashokay = 1;			/*haven't found a no-smash char yet*/
 7626:   if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
 7627:     scriptsp = basesp;			/* so just return unscripted symbol*/
 7628:   else {				/* symbols followed by scripts */
 7629:     isscripted = 1;			/*signal current term text-scripted*/
 7630:     if ( basesp != NULL )		/* have base symbol */
 7631:      { /*if(0)smashmargin = 0;*/	/*don't smash script (doesn't work)*/
 7632:        /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
 7633:        /* --- smash (or just concat) script raster against base symbol --- */
 7634:        if ( !issmashokay )		/* don't smash leading - */
 7635:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
 7636:        scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
 7637:        if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
 7638:        /* --- smash (or just concat) scripted term to stuff to its left --- */
 7639:        issmashokay = 1;			/* okay to smash base expression */
 7640:        if ( 0 && smashcheck > 1 )	/* smashcheck=2 to check base */
 7641:          /* note -- we _don't_ have base expression available to check */
 7642:          issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
 7643:        if ( !issmashokay )		/* don't smash leading - */
 7644:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
 7645:        scriptsp->size = size; } } }	/* and set font size */
 7646: end_of_job:
 7647:   smashmargin = oldsmashmargin;		/* reset original smashmargin */
 7648:   if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
 7649:   if ( msgfp!=NULL && msglevel>=99 )
 7650:     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
 7651: 	scriptlevel,(scriptsp==NULL?"null":"..."));
 7652:       if ( scriptsp != NULL )		/* have a constructed raster */
 7653: 	type_raster(scriptsp->image,msgfp); /*display constructed raster*/
 7654:       fflush(msgfp); }
 7655:   scriptlevel--;			/*lastly, decrement subscript level*/
 7656:   return ( scriptsp );
 7657: } /* --- end-of-function rastlimits() --- */
 7658: 
 7659: 
 7660: /* ==========================================================================
 7661:  * Function:	rastscripts ( expression, size, basesp )
 7662:  * Purpose:	super/subscript handler, returns subraster for the leading
 7663:  *		scripts in expression, whose base symbol is at font size
 7664:  * --------------------------------------------------------------------------
 7665:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 7666:  *				string beginning with a super/subscript,
 7667:  *				and returning ptr immediately following
 7668:  *				last script character processed.
 7669:  *		size (I)	int containing 0-7 default font size
 7670:  *		basesp (I)	subraster *  to character (or subexpression)
 7671:  *				immediately preceding leading script
 7672:  *				(scripts will be placed relative to base)
 7673:  * --------------------------------------------------------------------------
 7674:  * Returns:	( subraster * )	ptr to subraster corresponding to scripts,
 7675:  *				or NULL for any parsing error
 7676:  * --------------------------------------------------------------------------
 7677:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
 7678:  *		but is called directly from rasterize(), as necessary.
 7679:  * ======================================================================= */
 7680: /* --- entry point --- */
 7681: subraster *rastscripts ( char **expression, int size, subraster *basesp )
 7682: {
 7683: /* -------------------------------------------------------------------------
 7684: Allocations and Declarations
 7685: -------------------------------------------------------------------------- */
 7686: char	*texscripts(),			/* parse expression for scripts */
 7687: 	subscript[512], supscript[512];	/* scripts parsed from expression */
 7688: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
 7689: subraster *new_subraster(), *sp=NULL,	/* super- over subscript subraster */
 7690: 	*rastack();			/*sets scripts in displaymath mode*/
 7691: raster	*rp=NULL;			/* image raster embedded in sp */
 7692: int	height=0, width=0,  baseline=0,	/* height,width,baseline of sp */
 7693: 	subht=0,  subwidth=0,  subln=0,	/* height,width,baseline of sub */
 7694: 	supht=0,  supwidth=0,  supln=0,	/* height,width,baseline of sup */
 7695: 	baseht=0, baseln=0;		/* height,baseline of base */
 7696: int	bdescend=0, sdescend=0;		/* descender of base, subscript */
 7697: int	issub=0, issup=0, isboth=0,	/* true if we have sub,sup,both */
 7698: 	isbase=0;			/* true if we have base symbol */
 7699: int	szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
 7700: 	vbetween = 2,			/* vertical space between scripts */
 7701: 	vabove   = szval+1,		/*sup's top/bot above base's top/bot*/
 7702: 	vbelow   = szval+1,		/*sub's top/bot below base's top/bot*/
 7703: 	vbottom  = szval+1;		/*sup's bot above (sub's below) bsln*/
 7704: /*int	istweak = 1;*/			/* true to tweak script positioning */
 7705: int	rastput();			/*put scripts in constructed raster*/
 7706: int	delete_subraster();		/* free work areas */
 7707: int	rastsmashcheck();		/* check if okay to smash scripts */
 7708: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
 7709: /* -------------------------------------------------------------------------
 7710: Obtain subscript and/or superscript expressions, and rasterize them/it
 7711: -------------------------------------------------------------------------- */
 7712: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
 7713: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
 7714: if ( *expression == NULL ) goto end_of_job; /* no expression given */
 7715: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
 7716: *expression = texscripts(*expression,subscript,supscript,3);
 7717: /* --- rasterize scripts --- */
 7718: if ( *subscript != '\000' )		/* have a subscript */
 7719:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
 7720: if ( *supscript != '\000' )		/* have a superscript */
 7721:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
 7722: /* --- set flags for convenience --- */
 7723: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
 7724: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
 7725: isboth = (issub && issup);		/* true if we have both */
 7726: if (!issub && !issup) goto end_of_job;	/* quit if we have neither */
 7727: /* --- check for leading no-smash chars (if enabled) --- */
 7728: issmashokay = 0;			/* default, don't smash scripts */
 7729: if ( smashcheck > 0 ) {			/* smash checking wanted */
 7730:  issmashokay = 1;			/*haven't found a no-smash char yet*/
 7731:  if ( issub )				/* got a subscript */
 7732:   issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
 7733:  if ( issmashokay )			/* clean sub, so check sup */
 7734:   if ( issup )				/* got a superscript */
 7735:    issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
 7736:  } /* --- end-of-if(smashcheck>0) --- */
 7737: /* -------------------------------------------------------------------------
 7738: get height, width, baseline of scripts,  and height, baseline of base symbol
 7739: -------------------------------------------------------------------------- */
 7740: /* --- get height and width of components --- */
 7741: if ( issub )				/* we have a subscript */
 7742:   { subht    = (subsp->image)->height;	/* so get its height */
 7743:     subwidth = (subsp->image)->width;	/* and width */
 7744:     subln    =  subsp->baseline; }	/* and baseline */
 7745: if ( issup )				/* we have a superscript */
 7746:   { supht    = (supsp->image)->height;	/* so get its height */
 7747:     supwidth = (supsp->image)->width;	/* and width */
 7748:     supln    =  supsp->baseline; }	/* and baseline */
 7749: /* --- get height and baseline of base, and descender of base and sub --- */
 7750: if ( basesp == (subraster *)NULL )	/* no base symbol for scripts */
 7751:   basesp = leftexpression;		/* try using left side thus far */
 7752: if ( basesp != (subraster *)NULL )	/* we have base symbol for scripts */
 7753:   { baseht   = (basesp->image)->height;	/* height of base symbol */
 7754:     baseln   =  basesp->baseline;	/* and its baseline */
 7755:     bdescend =  baseht-(baseln+1);	/* and base symbol descender */
 7756:     sdescend =  bdescend + vbelow;	/*sub must descend by at least this*/
 7757:     if ( baseht > 0 ) isbase = 1; }	/* set flag */
 7758: /* -------------------------------------------------------------------------
 7759: determine width of constructed raster
 7760: -------------------------------------------------------------------------- */
 7761: width = max2(subwidth,supwidth);	/*widest component is overall width*/
 7762: /* -------------------------------------------------------------------------
 7763: determine height and baseline of constructed raster
 7764: -------------------------------------------------------------------------- */
 7765: /* --- both super/subscript --- */
 7766: if ( isboth )				/*we have subscript and superscript*/
 7767:   { height = max2(subht+vbetween+supht,	/* script heights + space bewteen */
 7768: 		vbelow+baseht+vabove);	/*sub below base bot, sup above top*/
 7769:     baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
 7770: /* --- superscript only --- */
 7771: if ( !issub )				/* we only have a superscript */
 7772:   { height = max3(baseln+1+vabove,	/* sup's top above base symbol top */
 7773: 		supht+vbottom,		/* sup's bot above baseln */
 7774: 		supht+vabove-bdescend);	/* sup's bot above base symbol bot */
 7775:     baseline = height-1; }		/*sup's baseline at bottom of raster*/
 7776: /* --- subscript only --- */
 7777: if ( !issup ) {				/* we only have a subscript */
 7778:   if ( subht > sdescend )		/*sub can descend below base bot...*/
 7779:     { height = subht;			/* ...without extra space on top */
 7780:       baseline = height-(sdescend+1);	/* sub's bot below base symbol bot */
 7781:       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
 7782:   else					/* sub's top will be below baseln */
 7783:     { height = sdescend+1;		/* sub's bot below base symbol bot */
 7784:       baseline = 0; } }			/* sub's baseline at top of raster */
 7785: /* -------------------------------------------------------------------------
 7786: construct raster with superscript over subscript
 7787: -------------------------------------------------------------------------- */
 7788: /* --- allocate subraster containing constructed raster --- */
 7789: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
 7790: ==   NULL )				/* and if we fail to allocate */
 7791:   goto end_of_job;			/* quit */
 7792: /* --- initialize subraster parameters --- */
 7793: sp->type  = IMAGERASTER;		/* set type as constructed image */
 7794: sp->size  = size;			/* set given size */
 7795: sp->baseline = baseline;		/* composite scripts baseline */
 7796: rp = sp->image;				/* raster embedded in subraster */
 7797: /* --- place super/subscripts in new raster --- */
 7798: if ( issup )				/* we have a superscript */
 7799:  rastput(rp,supsp->image,0,0,1);	/* it goes in upper-left corner */
 7800: if ( issub )				/* we have a subscript */
 7801:  rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
 7802: /* -------------------------------------------------------------------------
 7803: free unneeded component subrasters and return final result to caller
 7804: -------------------------------------------------------------------------- */
 7805: end_of_job:
 7806:   if ( issub ) delete_subraster(subsp);	/* free unneeded subscript */
 7807:   if ( issup ) delete_subraster(supsp);	/* and superscript */
 7808:   return ( sp );
 7809: } /* --- end-of-function rastscripts() --- */
 7810: 
 7811: 
 7812: /* ==========================================================================
 7813:  * Function:	rastdispmath ( expression, size, sp )
 7814:  * Purpose:	displaymath handler, returns sp along with
 7815:  *		its immediately following super/subscripts
 7816:  * --------------------------------------------------------------------------
 7817:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 7818:  *				string immediately following sp to be
 7819:  *				rasterized along with its super/subscripts,
 7820:  *				and returning ptr immediately following last
 7821:  *				character processed.
 7822:  *		size (I)	int containing 0-7 default font size
 7823:  *		sp (I)		subraster *  to display math operator
 7824:  *				to which super/subscripts will be added
 7825:  * --------------------------------------------------------------------------
 7826:  * Returns:	( subraster * )	ptr to subraster corresponding to sp
 7827:  *				plus its scripts, or NULL for any error
 7828:  * --------------------------------------------------------------------------
 7829:  * Notes:     o	sp returned unchanged if no super/subscript(s) follow it.
 7830:  * ======================================================================= */
 7831: /* --- entry point --- */
 7832: subraster *rastdispmath ( char **expression, int size, subraster *sp )
 7833: {
 7834: /* -------------------------------------------------------------------------
 7835: Allocations and Declarations
 7836: -------------------------------------------------------------------------- */
 7837: char	*texscripts(),			/* parse expression for scripts */
 7838: 	subscript[512], supscript[512];	/* scripts parsed from expression */
 7839: int	issub=0, issup=0;		/* true if we have sub,sup */
 7840: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
 7841: 	*rastack(),			/* stack operator with scripts */
 7842: 	*new_subraster();		/* for dummy base sp, if needed */
 7843: int	vspace = 1;			/* vertical space between scripts */
 7844: /* -------------------------------------------------------------------------
 7845: Obtain subscript and/or superscript expressions, and rasterize them/it
 7846: -------------------------------------------------------------------------- */
 7847: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
 7848: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
 7849: if ( *expression == NULL ) goto end_of_job; /* no expression given */
 7850: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
 7851: *expression = texscripts(*expression,subscript,supscript,3);
 7852: /* --- rasterize scripts --- */
 7853: if ( *subscript != '\000' )		/* have a subscript */
 7854:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
 7855: if ( *supscript != '\000' )		/* have a superscript */
 7856:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
 7857: /* --- set flags for convenience --- */
 7858: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
 7859: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
 7860: if (!issub && !issup) goto end_of_job;	/*return operator alone if neither*/
 7861: /* -------------------------------------------------------------------------
 7862: stack operator and its script(s)
 7863: -------------------------------------------------------------------------- */
 7864: /* --- stack superscript atop operator --- */
 7865: if ( issup ) {				/* we have a superscript */
 7866:  if ( sp == NULL )			/* but no base expression */
 7867:   sp = supsp;				/* so just use superscript */
 7868:  else					/* have base and superscript */
 7869:   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
 7870:   ==   NULL ) goto end_of_job; }	/* and quit if failed */
 7871: /* --- stack operator+superscript atop subscript --- */
 7872: if ( issub ) {				/* we have a subscript */
 7873:  if ( sp == NULL )			/* but no base expression */
 7874:   sp = subsp;				/* so just use subscript */
 7875:  else					/* have base and subscript */
 7876:   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
 7877:   ==   NULL ) goto end_of_job; }	/* and quit if failed */
 7878: sp->type = IMAGERASTER;			/* flip type of composite object */
 7879: sp->size = size;			/* and set font size */
 7880: /* -------------------------------------------------------------------------
 7881: free unneeded component subrasters and return final result to caller
 7882: -------------------------------------------------------------------------- */
 7883: end_of_job:
 7884:   return ( sp );
 7885: } /* --- end-of-function rastdispmath() --- */
 7886: 
 7887: 
 7888: /* ==========================================================================
 7889:  * Function:	rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
 7890:  * Purpose:	\left...\right handler, returns a subraster corresponding to
 7891:  *		delimited subexpression at font size
 7892:  * --------------------------------------------------------------------------
 7893:  * Arguments:	expression (I)	char **  to first char of null-terminated
 7894:  *				string beginning with a \left
 7895:  *				to be rasterized
 7896:  *		size (I)	int containing 0-7 default font size
 7897:  *		basesp (I)	subraster *  to character (or subexpression)
 7898:  *				immediately preceding leading left{
 7899:  *				(unused, but passed for consistency)
 7900:  *		ildelim (I)	int containing ldelims[] index of
 7901:  *				left delimiter
 7902:  *		arg2 (I)	int unused
 7903:  *		arg3 (I)	int unused
 7904:  * --------------------------------------------------------------------------
 7905:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
 7906:  *				or NULL for any parsing error
 7907:  * --------------------------------------------------------------------------
 7908:  * Notes:     o
 7909:  * ======================================================================= */
 7910: /* --- entry point --- */
 7911: subraster *rastleft ( char **expression, int size, subraster *basesp,
 7912: 			int ildelim, int arg2, int arg3 )
 7913: {
 7914: /* -------------------------------------------------------------------------
 7915: Allocations and Declarations
 7916: -------------------------------------------------------------------------- */
 7917: subraster *rasterize(), *sp=NULL;	/*rasterize between \left...\right*/
 7918: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
 7919: subraster *rastlimits();		/*handle sub/super scripts on lp,rp*/
 7920: subraster *rastcat();			/* concat lp||sp||rp subrasters */
 7921: int	family=CMSYEX,			/* get_delim() family */
 7922: 	height=0, rheight=0,		/* subexpr, right delim height */
 7923: 	margin=(size+1),		/* delim height margin over subexpr*/
 7924: 	opmargin=(5);			/* extra margin for \int,\sum,\etc */
 7925: char	/* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
 7926: char	*texchar(),			/* get delims after \left,\right */
 7927: 	ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
 7928: char	*strtexchr(), *pleft, *pright;	/*locate \right matching our \left*/
 7929: int	isleftdot=0, isrightdot=0;	/* true if \left. or \right. */
 7930: int	isleftscript=0, isrightscript=0; /* true if delims are scripted */
 7931: int	sublen=0;			/* strlen(subexpr) */
 7932: int	idelim=0;			/* 1=left,2=right */
 7933: /* int	gotldelim = 0; */		/* true if ildelim given by caller */
 7934: int	delete_subraster();		/* free subraster if rastleft fails*/
 7935: int	wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
 7936: int	istextleft=0, istextright=0;	/* true for non-displaystyle delims*/
 7937: /* --- recognized delimiters --- */
 7938: static	char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
 7939: static	char *ldelims[] = {
 7940:    "unused", ".",			/* 1   for \left., \right. */
 7941: 	"(", ")",			/* 2,3 for \left(, \right) */
 7942: 	"\\{","\\}",			/* 4,5 for \left\{, \right\} */
 7943: 	"[", "]",			/* 6,7 for \left[, \right] */
 7944: 	"<", ">",			/* 8,9 for \left<, \right> */
 7945: 	"|", "\\|",			/* 10,11 for \left,\right |,\|*/
 7946: 	NULL };
 7947: /* --- recognized operator delimiters --- */
 7948: static	char *opdelims[] = {		/* operator delims from cmex10 */
 7949:      "int",	  "sum",	"prod",
 7950:      "cup",	  "cap",	"dot",
 7951:      "plus",	  "times",	"wedge",
 7952:      "vee",
 7953:      NULL }; /* --- end-of-opdelims[] --- */
 7954: /* --- delimiter xlation --- */
 7955: static	char *xfrom[] =			/* xlate any delim suffix... */
 7956:    { "\\|",				/* \| */
 7957:      "\\{",				/* \{ */
 7958:      "\\}",				/* \} */
 7959:      "\\lbrace",			/* \lbrace */
 7960:      "\\rbrace",			/* \rbrace */
 7961:      "\\langle",			/* \langle */
 7962:      "\\rangle",			/* \rangle */
 7963:      NULL } ; /* --- end-of-xfrom[] --- */
 7964: static	char *xto[] =			/* ...to this instead */
 7965:    { "=",				/* \| to = */
 7966:      "{",				/* \{ to { */
 7967:      "}",				/* \} to } */
 7968:      "{",				/* \lbrace to { */
 7969:      "}",				/* \rbrace to } */
 7970:      "<",				/* \langle to < */
 7971:      ">",				/* \rangle to > */
 7972:      NULL } ; /* --- end-of-xto[] --- */
 7973: /* --- non-displaystyle delimiters --- */
 7974: static	char *textdelims[] =		/* these delims _aren't_ display */
 7975:    { "|", "=",
 7976:      "(", ")",
 7977:      "[", "]",
 7978:      "<", ">",
 7979:      "{", "}",
 7980:      "dbl",				/* \lbrackdbl and \rbrackdbl */
 7981:      NULL } ; /* --- end-of-textdelims[] --- */
 7982: /* -------------------------------------------------------------------------
 7983: initialization
 7984: -------------------------------------------------------------------------- */
 7985: /* --- check args --- */
 7986: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
 7987: /* --- determine left delimiter, and set default \right. delimiter --- */
 7988: if ( ildelim!=NOVALUE && ildelim>=1 )	/* called with explicit left delim */
 7989:  { strcpy(ldelim,ldelims[ildelim]);	/* so just get a local copy */
 7990:    /* gotldelim = 1; */ }		/* and set flag that we got it */
 7991: else					/* trapped \left without delim */
 7992:  { skipwhite(*expression);		/* interpret \left ( as \left( */
 7993:    if ( *(*expression) == '\000' )	/* end-of-string after \left */
 7994:       goto end_of_job;			/* so return NULL */
 7995:    *expression = texchar(*expression,ldelim); /*pull delim from expression*/
 7996:    if ( *expression == NULL		/* probably invalid end-of-string */
 7997:    ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
 7998: strcpy(rdelim,".");			/* init default \right. delim */
 7999: /* -------------------------------------------------------------------------
 8000: locate \right balancing our opening \left
 8001: -------------------------------------------------------------------------- */
 8002: /* --- first \right following \left --- */
 8003: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
 8004: !=   NULL ) {				/* found it */
 8005:  /* --- find matching \right by pushing past any nested \left's --- */
 8006:  pleft = *expression;			/* start after first \left( */
 8007:  while ( 1 ) {				/*break when matching \right found*/
 8008:   /* -- locate next nested \left if there is one --- */
 8009:   if ( (pleft=strtexchr(pleft,left))	/* find next \left */
 8010:   ==   NULL ) break;			/*no more, so matching \right found*/
 8011:   pleft += strlen(left);		/* push ptr past \left token */
 8012:   if ( pleft >= pright ) break;		/* not nested if \left after \right*/
 8013:   /* --- have nested \left, so push forward to next \right --- */
 8014:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
 8015:   ==   NULL ) break;			/* ran out of \right's */
 8016:   } /* --- end-of-while(1) --- */
 8017:  } /* --- end-of-if(pright!=NULL) --- */
 8018: /* -------------------------------------------------------------------------
 8019: push past \left(_a^b sub/superscripts, if present
 8020: -------------------------------------------------------------------------- */
 8021: pleft = *expression;			/*reset pleft after opening \left( */
 8022: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
 8023: !=   NULL )				/* found actual _a^b scripts, too */
 8024:   { delete_subraster(lp);		/* but we don't need them */
 8025:     lp = NULL; }			/* reset pointer, too */
 8026: /* -------------------------------------------------------------------------
 8027: get \right delimiter and subexpression between \left...\right, xlate delims
 8028: -------------------------------------------------------------------------- */
 8029: /* --- get delimiter following \right --- */
 8030: if ( pright == (char *)NULL ) {		/* assume \right. at end of exprssn*/
 8031:   strcpy(rdelim,".");			/* set default \right. */
 8032:   sublen = strlen(*expression);		/* use entire remaining expression */
 8033:   memcpy(subexpr,*expression,sublen);	/* copy all remaining chars */
 8034:   *expression += sublen; }		/* and push expression to its null */
 8035: else {					/* have explicit matching \right */
 8036:   sublen = (int)(pright-(*expression));	/* #chars between \left...\right */
 8037:   memcpy(subexpr,*expression,sublen);	/* copy chars preceding \right */
 8038:   *expression = pright+strlen(right);	/* push expression past \right */
 8039:   skipwhite(*expression);		/* interpret \right ) as \right) */
 8040:   *expression = texchar(*expression,rdelim); /*pull delim from expression*/
 8041:   if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
 8042: /* --- get subexpression between \left...\right --- */
 8043: if ( sublen < 1 ) goto end_of_job;	/* nothing between delimiters */
 8044: subexpr[sublen] = '\000';		/* and null-terminate it */
 8045: /* --- adjust margin for expressions containing \middle's --- */
 8046: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
 8047:   margin = 1;				/* so don't "overwhelm" them */
 8048: /* --- check for operator delimiter --- */
 8049: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
 8050:   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
 8051:     { margin += opmargin;		/* extra height for operator */
 8052:       if ( *ldelim == '\\' )		/* have leading escape */
 8053: 	{strsqueeze(ldelim,1);}		/* squeeze it out */
 8054:       break; }				/* no need to check rest of table */
 8055: /* --- xlate delimiters and check for textstyle --- */
 8056: for ( idelim=1; idelim<=2; idelim++ ) {	/* 1=left, 2=right */
 8057:   char	*lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
 8058:   int	ix;  char *xdelim;		/* xfrom[] and xto[] index, delim */
 8059:   for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
 8060:     if ( strcmp(lrdelim,xdelim) == 0 )	/* found delim to xlate */
 8061:       {	strcpy(lrdelim,xto[ix]);	/* replace with corresponding xto[]*/
 8062: 	break; }			/* no need to check further */
 8063:   for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
 8064:     if ( strstr(lrdelim,xdelim) != 0 )	/* found textstyle delim */
 8065:       {	if ( idelim == 1 )		/* if it's the \left one */
 8066: 	  istextleft = 1;		/* set left textstyle flag */
 8067: 	else istextright = 1;		/* else set right textstyle flag */
 8068: 	break; }			/* no need to check further */
 8069:   } /* --- end-of-for(idelim) --- */
 8070: /* --- debugging --- */
 8071: if ( msgfp!=NULL && msglevel>=99 )
 8072:   fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
 8073:   ldelim,rdelim,subexpr);
 8074: /* -------------------------------------------------------------------------
 8075: rasterize subexpression
 8076: -------------------------------------------------------------------------- */
 8077: /* --- rasterize subexpression --- */
 8078: if ( (sp = rasterize(subexpr,size))	/* rasterize chars between delims */
 8079: ==   NULL ) goto end_of_job;		/* quit if failed */
 8080: height = (sp->image)->height;		/* height of subexpr raster */
 8081: rheight = height+margin;		/*default rheight as subexpr height*/
 8082: /* -------------------------------------------------------------------------
 8083: rasterize delimiters, reset baselines, and add  sub/superscripts if present
 8084: -------------------------------------------------------------------------- */
 8085: /* --- check for dot delimiter --- */
 8086: isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
 8087: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
 8088: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
 8089: isdisplaystyle = (istextleft?0:9);	/* force \displaystyle */
 8090: if ( !isleftdot )			/* if not \left. */
 8091:  { /* --- first get requested \left delimiter --- */
 8092:    lp = get_delim(ldelim,rheight,family); /* get \left delim char */
 8093:    /* --- reset lp delim baseline to center delim on subexpr raster --- */
 8094:    if ( lp != NULL )			/* if get_delim() succeeded */
 8095:     { int lheight = (lp->image)->height; /* actual height of left delim */
 8096:       lp->baseline = sp->baseline + (lheight - height)/2;
 8097:       if ( lheight > rheight )		/* got bigger delim than requested */
 8098: 	rheight = lheight-1; }		/* make sure right delim matches */
 8099:    /* --- then add on any sub/superscripts attached to \left( --- */
 8100:    lp = rastlimits(&pleft,size,lp);	/*\left(_a^b and push pleft past b*/
 8101:    isleftscript = isscripted; }		/* check if left delim scripted */
 8102: isdisplaystyle = (istextright?0:9);	/* force \displaystyle */
 8103: if ( !isrightdot )			/* and if not \right. */
 8104:  { /* --- first get requested \right delimiter --- */
 8105:    rp = get_delim(rdelim,rheight,family); /* get \right delim char */
 8106:    /* --- reset rp delim baseline to center delim on subexpr raster --- */
 8107:    if ( rp != NULL )			/* if get_delim() succeeded */
 8108:      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
 8109:    /* --- then add on any sub/superscripts attached to \right) --- */
 8110:    rp = rastlimits(expression,size,rp);	/*\right)_c^d, expression past d*/
 8111:    isrightscript = isscripted; }	/* check if right delim scripted */
 8112: isdisplaystyle = wasdisplaystyle;	/* original \displystyle default */
 8113: /* --- check that we got delimiters --- */
 8114: if ( 0 )
 8115:  if ( (lp==NULL && !isleftdot)		/* check that we got left( */
 8116:  ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
 8117:   { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
 8118:     if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
 8119:     if (0) { delete_subraster(sp);	/* if failed, free subraster */
 8120:              sp = (subraster *)NULL; }	/* signal error to caller */
 8121:     goto end_of_job; }			/* and quit */
 8122: /* -------------------------------------------------------------------------
 8123: concat  lp || sp || rp  components
 8124: -------------------------------------------------------------------------- */
 8125: /* --- concat lp||sp||rp to obtain final result --- */
 8126: if ( lp != NULL )			/* ignore \left. */
 8127:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
 8128: if ( sp != NULL )			/* succeeded or ignored \left. */
 8129:   if ( rp != NULL )			/* ignore \right. */
 8130:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
 8131: /* --- back to caller --- */
 8132: end_of_job:
 8133:   isdelimscript = isrightscript;	/* signal if right delim scripted */
 8134:   return ( sp );
 8135: } /* --- end-of-function rastleft() --- */
 8136: 
 8137: 
 8138: /* ==========================================================================
 8139:  * Function:	rastright ( expression, size, basesp, ildelim, arg2, arg3 )
 8140:  * Purpose:	...\right handler, intercepts an unexpected/unbalanced \right
 8141:  * --------------------------------------------------------------------------
 8142:  * Arguments:	expression (I)	char **  to first char of null-terminated
 8143:  *				string beginning with a \right
 8144:  *				to be rasterized
 8145:  *		size (I)	int containing 0-7 default font size
 8146:  *		basesp (I)	subraster *  to character (or subexpression)
 8147:  *				immediately preceding leading left{
 8148:  *				(unused, but passed for consistency)
 8149:  *		ildelim (I)	int containing rdelims[] index of
 8150:  *				right delimiter
 8151:  *		arg2 (I)	int unused
 8152:  *		arg3 (I)	int unused
 8153:  * --------------------------------------------------------------------------
 8154:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
 8155:  *				or NULL for any parsing error
 8156:  * --------------------------------------------------------------------------
 8157:  * Notes:     o
 8158:  * ======================================================================= */
 8159: /* --- entry point --- */
 8160: subraster *rastright ( char **expression, int size, subraster *basesp,
 8161: 			int ildelim, int arg2, int arg3 )
 8162: {
 8163: /* -------------------------------------------------------------------------
 8164: Allocations and Declarations
 8165: -------------------------------------------------------------------------- */
 8166: subraster /* *rasterize(),*/ *sp=NULL;	/*rasterize \right subexpr's*/
 8167:   if ( sp != NULL )			/* returning entire expression */
 8168:     {
 8169:       isreplaceleft = 1;		/* set flag to replace left half*/
 8170:     }
 8171: return ( sp );
 8172: } /* --- end-of-function rastright() --- */
 8173: 
 8174: 
 8175: /* ==========================================================================
 8176:  * Function:	rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
 8177:  * Purpose:	\middle handler, returns subraster corresponding to
 8178:  *		entire expression with \middle delimiter(s) sized to fit.
 8179:  * --------------------------------------------------------------------------
 8180:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 8181:  *				string immediately following \middle to be
 8182:  *				rasterized, and returning ptr immediately
 8183:  *				to terminating null.
 8184:  *		size (I)	int containing 0-7 default font size
 8185:  *		basesp (I)	subraster *  to character (or subexpression)
 8186:  *				immediately preceding \middle
 8187:  *				(unused, but passed for consistency)
 8188:  *		arg1 (I)	int unused
 8189:  *		arg2 (I)	int unused
 8190:  *		arg3 (I)	int unused
 8191:  * --------------------------------------------------------------------------
 8192:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
 8193:  *				or NULL for any parsing error
 8194:  *				(expression ptr unchanged if error occurs)
 8195:  * --------------------------------------------------------------------------
 8196:  * Notes:     o
 8197:  * ======================================================================= */
 8198: /* --- entry point --- */
 8199: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
 8200: 			int arg1, int arg2, int arg3 )
 8201: {
 8202: /* -------------------------------------------------------------------------
 8203: Allocations and Declarations
 8204: -------------------------------------------------------------------------- */
 8205: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
 8206: char	*exprptr = *expression,		/* local copy of ptr to expression */
 8207: 	*texchar(), delim[32][132],	/* delimiters following \middle's */
 8208: 	*strtexchr(),			/* locate \middle's */
 8209: 	subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
 8210: int	height=0, habove=0, hbelow=0;	/* height, above & below baseline */
 8211: int	idelim, ndelims=0,		/* \middle count (max 32) */
 8212: 	family = CMSYEX;		/* delims from CMSY10 or CMEX10 */
 8213: subraster *subrastcpy(),		/* copy subraster */
 8214: 	*rastcat(),			/* concatanate subraster */
 8215: 	*get_delim();			/* get rasterized delimiter */
 8216: int	delete_subraster();		/* free work area subsp[]'s at eoj */
 8217: /* -------------------------------------------------------------------------
 8218: initialization
 8219: -------------------------------------------------------------------------- */
 8220: subsp[0] = leftexpression;		/* expressn preceding 1st \middle */
 8221: subsp[1] = NULL;			/* set first null */
 8222: /* -------------------------------------------------------------------------
 8223: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
 8224: -------------------------------------------------------------------------- */
 8225: while ( ndelims < 30 )			/* max of 31 \middle's */
 8226:   {
 8227:   /* --- maintain max height above,below baseline --- */
 8228:   if ( subsp[ndelims] != NULL )		/*exprssn preceding current \middle*/
 8229:    { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
 8230:      height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
 8231:      habove = max2(habove,baseline);	/* max #rows above baseline */
 8232:      hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
 8233:   /* --- get delimter after \middle --- */
 8234:   skipwhite(exprptr);			/*skip space betwn \middle & \delim*/
 8235:   exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
 8236:   if ( *(delim[ndelims]) == '\000' )	/* \middle at end-of-expression */
 8237:     break;				/* ignore it and consider job done */
 8238:   ndelims++;				/* count another \middle\delim */
 8239:   /* --- get subexpression between \delim and next \middle --- */
 8240:   subsp[ndelims] = NULL;		/* no subexpresion yet */
 8241:   if ( *exprptr == '\000' )		/* end-of-expression after \delim */
 8242:     break;				/* so we have all subexpressions */
 8243:   if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
 8244:   ==   NULL )				/* no more \middle's */
 8245:    { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
 8246:      subexpr[MAXSUBXSZ] = '\000';	/* make sure it's null-terminated */
 8247:      exprptr += strlen(exprptr); }	/* push exprptr to terminating '\0'*/
 8248:   else					/* have another \middle */
 8249:    { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
 8250:      memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
 8251:      subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
 8252:      exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
 8253:   /* --- rasterize subexpression --- */
 8254:   subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
 8255:   } /* --- end-of-while(1) --- */
 8256: /* -------------------------------------------------------------------------
 8257: construct \middle\delim's and concatanate them between subexpressions
 8258: -------------------------------------------------------------------------- */
 8259: if ( ndelims < 1			/* no delims */
 8260: ||   (height=habove+hbelow) < 1 )	/* or no subexpressions? */
 8261:   goto end_of_job;			/* just flush \middle directive */
 8262: for ( idelim=0; idelim<=ndelims; idelim++ )
 8263:   {
 8264:   /* --- first add on subexpression preceding delim --- */
 8265:   if ( subsp[idelim] != NULL ) {	/* have subexpr preceding delim */
 8266:     if ( sp == NULL )			/* this is first piece */
 8267:      { sp = subsp[idelim];		/* so just use it */
 8268:        if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
 8269:     else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
 8270:   /* --- now construct delimiter --- */
 8271:   if ( *(delim[idelim]) != '\000' )	/* have delimter */
 8272:    { subraster *delimsp = get_delim(delim[idelim],height,family);
 8273:      if ( delimsp != NULL )		/* rasterized delim */
 8274:       {	delimsp->baseline = habove;	/* set baseline */
 8275: 	if ( sp == NULL )		/* this is first piece */
 8276: 	  sp = delimsp;			/* so just use it */
 8277: 	else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
 8278:   } /* --- end-of-for(idelim) --- */
 8279: /* --- back to caller --- */
 8280: end_of_job:
 8281:   if ( 0 ) /* now handled above */
 8282:     for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
 8283:      if ( subsp[idelim] != NULL )	/* have allocated subraster */
 8284:       delete_subraster(subsp[idelim]);	/* so free it */
 8285:   if ( sp != NULL )			/* returning entire expression */
 8286:     { int newht = (sp->image)->height;	/* height of returned subraster */
 8287:       sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
 8288:       isreplaceleft = 1;		/* set flag to replace left half*/
 8289:       *expression += strlen(*expression); } /* and push to terminating null*/
 8290:   return ( sp );
 8291: } /* --- end-of-function rastmiddle() --- */
 8292: 
 8293: 
 8294: /* ==========================================================================
 8295:  * Function:	rastflags ( expression, size, basesp,  flag, value, arg3 )
 8296:  * Purpose:	sets an internal flag, e.g., for \rm, or sets an internal
 8297:  *		value, e.g., for \unitlength=<value>, and returns NULL
 8298:  *		so nothing is displayed
 8299:  * --------------------------------------------------------------------------
 8300:  * Arguments:	expression (I)	char **  to first char of null-terminated
 8301:  *				LaTeX expression (unused/unchanged)
 8302:  *		size (I)	int containing base font size (not used,
 8303:  *				just stored in subraster)
 8304:  *		basesp (I)	subraster *  to character (or subexpression)
 8305:  *				immediately preceding "flags" directive
 8306:  *				(unused but passed for consistency)
 8307:  *		flag (I)	int containing #define'd symbol specifying
 8308:  *				internal flag to be set
 8309:  *		value (I)	int containing new value of flag
 8310:  *		arg3 (I)	int unused
 8311:  * --------------------------------------------------------------------------
 8312:  * Returns:	( subraster * )	NULL so nothing is displayed
 8313:  * --------------------------------------------------------------------------
 8314:  * Notes:     o
 8315:  * ======================================================================= */
 8316: /* --- entry point --- */
 8317: subraster *rastflags ( char **expression, int size, subraster *basesp,
 8318: 			int flag, int value, int arg3 )
 8319: {
 8320: /* -------------------------------------------------------------------------
 8321: Allocations and Declarations
 8322: -------------------------------------------------------------------------- */
 8323: char	*texsubexpr(),			/* parse expression for... */
 8324: 	valuearg[1024]="NOVALUE";	/* value from expression, if needed */
 8325: int	argvalue=NOVALUE,		/* atoi(valuearg) */
 8326: 	isdelta=0,			/* true if + or - precedes valuearg */
 8327: 	valuelen=0;			/* strlen(valuearg) */
 8328: double	dblvalue=(-99.), strtod();	/*convert ascii {valuearg} to double*/
 8329: static	int displaystylelevel = (-99);	/* \displaystyle set at recurlevel */
 8330: /* -------------------------------------------------------------------------
 8331: set flag or value
 8332: -------------------------------------------------------------------------- */
 8333: switch ( flag )
 8334:   {
 8335:   default: break;			/* unrecognized flag */
 8336:   case ISFONTFAM:
 8337:     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
 8338:       (*expression)++;			/* skip leading ~ after \rm */
 8339:     fontnum = value;			/* set font family */
 8340:     break;
 8341:   case ISSTRING: isstring=value; break;	/* set string/image mode */
 8342:   case ISDISPLAYSTYLE:			/* set \displaystyle mode */
 8343:     displaystylelevel = recurlevel;	/* \displaystyle set at recurlevel */
 8344:     isdisplaystyle=value; break;
 8345:   case ISOPAQUE:  istransparent=value; break; /* set transparent/opaque */
 8346:   case ISREVERSE:			/* reverse video */
 8347:     if ( value==1 || value==NOVALUE )
 8348:       {	fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
 8349:     if ( value==2 || value==NOVALUE )
 8350:       {	bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
 8351:     if ( value==2 || value==NOVALUE )
 8352:       isblackonwhite = !isblackonwhite;
 8353:     if ( gammacorrection > 0.0001 )	/* have gamma correction */
 8354:       gammacorrection = REVERSEGAMMA;	/* use reverse video gamma instead */
 8355:     break;
 8356:   case ISSUPER:				/* set supersampling/lowpass flag */
 8357:     #ifndef SSFONTS			/* don't have ss fonts loaded */
 8358:       value = 0;			/* so force lowpass */
 8359:     #endif
 8360:     isss = issupersampling = value;
 8361:     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
 8362:     break;
 8363:   case ISFONTSIZE:			/* set fontsize */
 8364:   case ISMAGSTEP:			/* set magstep */
 8365:   case ISDISPLAYSIZE:			/* set displaysize */
 8366:   case ISCONTENTTYPE:			/*enable/disable content-type lines*/
 8367:   case ISCONTENTCACHED:			/* write content-type to cache file*/
 8368:   case ISSHRINK:			/* set shrinkfactor */
 8369:   case ISAAALGORITHM:			/* set anti-aliasing algorithm */
 8370:   case ISWEIGHT:			/* set font weight */
 8371:   case ISCENTERWT:			/* set lowpass center pixel weight */
 8372:   case ISADJACENTWT:			/* set lowpass adjacent weight */
 8373:   case ISCORNERWT:			/* set lowpass corner weight */
 8374:   case ISCOLOR:				/* set red(1),green(2),blue(3) */
 8375:   case ISSMASH:				/* set (minimum) "smash" margin */
 8376:   case ISGAMMA:				/* set gamma correction */
 8377:     if ( value != NOVALUE )		/* passed a fixed value to be set */
 8378:       {	argvalue = value;		/* set given fixed int value */
 8379: 	dblvalue = (double)value; }	/* or maybe interpreted as double */
 8380:     else				/* get value from expression */
 8381:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
 8382: 	if ( *valuearg != '\000' )	/* guard against empty string */
 8383: 	 if ( !isalpha(*valuearg) )	/* and against alpha string args */
 8384: 	  if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
 8385: 	   { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
 8386: 	     if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
 8387: 	       { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
 8388: 	     switch ( flag ) {		/* convert to double or int */
 8389: 	      default: argvalue = atoi(valuearg); break; /* convert to int */
 8390: 	      case ISGAMMA:
 8391: 		dblvalue = strtod(valuearg,NULL); break; } /* or to double */
 8392: 	   } /* --- end-of-if(*valuearg!='?') --- */
 8393:       } /* --- end-of-if(value==NOVALUE) --- */
 8394:     switch ( flag )
 8395:       {
 8396:       default: break;
 8397:       case ISCOLOR:			/* set color */
 8398: 	slower(valuearg);		/* convert arg to lower case */
 8399: 	if ( argvalue==1 || strstr(valuearg,"red") )
 8400: 	  { fggreen = fgblue = (isblackonwhite?0:255);
 8401: 	    fgred = (isblackonwhite?255:0); }
 8402: 	if ( argvalue==2 || strstr(valuearg,"green") )
 8403: 	  { fgred = fgblue = (isblackonwhite?0:255);
 8404: 	    fggreen = (isblackonwhite?255:0); }
 8405: 	if ( argvalue==3 || strstr(valuearg,"blue") )
 8406: 	  { fgred = fggreen = (isblackonwhite?0:255);
 8407: 	    fgblue = (isblackonwhite?255:0); }
 8408: 	if ( argvalue==0 || strstr(valuearg,"black") )
 8409: 	    fgred = fggreen = fgblue = (isblackonwhite?0:255);
 8410: 	if ( argvalue==7 || strstr(valuearg,"white") )
 8411: 	    fgred = fggreen = fgblue = (isblackonwhite?255:0);
 8412: 	break;
 8413:       case ISFONTSIZE:			/* set fontsize */
 8414: 	if ( argvalue != NOVALUE )	/* got a value */
 8415: 	  { int largestsize = (issupersampling?16:LARGESTSIZE);
 8416: 	    fontsize = (isdelta? fontsize+argvalue : argvalue);
 8417: 	    fontsize = max2(0,min2(fontsize,largestsize));
 8418: 	    shrinkfactor = shrinkfactors[fontsize];
 8419: 	    if ( isdisplaystyle == 1	/* displaystyle enabled but not set*/
 8420: 	    ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
 8421: 	    ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
 8422: 	     if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
 8423: 	      if ( !ispreambledollars )	{ /* respect $$...$$'s */
 8424: 	       if ( fontsize >= displaysize )
 8425: 		isdisplaystyle = 2;	/* forced */
 8426: 	       else isdisplaystyle = 1; }
 8427: 	    /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
 8428: 	else				/* embed font size in expression */
 8429: 	  { sprintf(valuearg,"%d",fontsize); /* convert size */
 8430: 	    valuelen = strlen(valuearg); /* ought to be 1 */
 8431: 	    if ( *expression != '\000' ) /* ill-formed expression */
 8432: 	     { *expression = (char *)(*expression-valuelen); /*back up buff*/
 8433: 	       memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
 8434: 	break;
 8435:       case ISMAGSTEP:			/* set magstep */
 8436: 	if ( argvalue != NOVALUE ) {	/* got a value */
 8437: 	  int largestmag = 10;
 8438: 	  magstep = (isdelta? magstep+argvalue : argvalue);
 8439: 	  magstep = max2(1,min2(magstep,largestmag)); }
 8440: 	break;
 8441:       case ISDISPLAYSIZE:		/* set displaysize */
 8442: 	if ( argvalue != NOVALUE )	/* got a value */
 8443: 	    displaysize = (isdelta? displaysize+argvalue : argvalue);
 8444: 	break;
 8445:       case ISCONTENTTYPE:		/*enable/disable content-type lines*/
 8446: 	if ( argvalue != NOVALUE )	/* got a value */
 8447: 	    isemitcontenttype = (argvalue>0?1:0);
 8448: 	break;
 8449:       case ISCONTENTCACHED:		/* write content-type to cache file*/
 8450: 	if ( argvalue != NOVALUE )	/* got a value */
 8451: 	    iscachecontenttype = (argvalue>0?1:0);
 8452: 	break;
 8453:       case ISSMASH:			/* set (minimum) "smash" margin */
 8454: 	if ( argvalue != NOVALUE )	/* got a value */
 8455: 	  { smashmargin = argvalue;	/* set value */
 8456: 	    if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
 8457: 	    issmashdelta = (isdelta?1:0); } /* and set delta flag */
 8458: 	smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
 8459: 	isexplicitsmash = 1;		/* signal explicit \smash directive*/
 8460: 	break;
 8461:       case ISSHRINK:			/* set shrinkfactor */
 8462: 	if ( argvalue != NOVALUE )	/* got a value */
 8463: 	  shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
 8464: 	shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
 8465: 	break;
 8466:       case ISAAALGORITHM:		/* set anti-aliasing algorithm */
 8467: 	if ( argvalue != NOVALUE ) {	/* got a value */
 8468: 	  if ( argvalue >= 0 ) {	/* non-negative to set algorithm */
 8469: 	      aaalgorithm = argvalue;	/* set algorithm number */
 8470: 	    aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
 8471: 	  else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
 8472: 	break;
 8473:       case ISWEIGHT:			/* set font weight number */
 8474: 	value =	(argvalue==NOVALUE? NOVALUE : /* don't have a value */
 8475: 		(isdelta? weightnum+argvalue : argvalue));
 8476: 	if ( value>=0 && value<maxaaparams ) /* in range */
 8477: 	  { weightnum   = value;	/* reset weightnum index */
 8478: 	    minadjacent = aaparams[weightnum].minadjacent;
 8479: 	    maxadjacent = aaparams[weightnum].maxadjacent;
 8480: 	    cornerwt    = aaparams[weightnum].cornerwt;
 8481: 	    adjacentwt  = aaparams[weightnum].adjacentwt;
 8482: 	    centerwt    = aaparams[weightnum].centerwt;
 8483: 	    fgalias     = aaparams[weightnum].fgalias;
 8484: 	    fgonly      = aaparams[weightnum].fgonly;
 8485: 	    bgalias     = aaparams[weightnum].bgalias;
 8486: 	    bgonly      = aaparams[weightnum].bgonly; }
 8487: 	break;
 8488:       case ISCENTERWT:			/* set lowpass center pixel weight */
 8489: 	if ( argvalue != NOVALUE )	/* got a value */
 8490: 	  centerwt = argvalue;		/* set lowpass center weight */
 8491: 	break;
 8492:       case ISADJACENTWT:		/* set lowpass adjacent weight */
 8493: 	if ( argvalue != NOVALUE )	/* got a value */
 8494: 	  adjacentwt = argvalue;	/* set lowpass adjacent weight */
 8495: 	break;
 8496:       case ISCORNERWT:			/* set lowpass corner weight */
 8497: 	if ( argvalue != NOVALUE )	/* got a value */
 8498: 	  cornerwt = argvalue;		/* set lowpass corner weight */
 8499: 	break;
 8500:       case ISGAMMA:			/* set gamma correction */
 8501: 	if ( dblvalue >= 0.0 )		/* got a value */
 8502: 	  gammacorrection = dblvalue;	/* set gamma correction */
 8503: 	break;
 8504:       } /* --- end-of-switch() --- */
 8505:     break;
 8506:   case PNMPARAMS:			/*set fgalias,fgonly,bgalias,bgonly*/
 8507:     *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
 8508:     valuelen = strlen(valuearg);	/* ought to be 1-4 */
 8509:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
 8510:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
 8511:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
 8512:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
 8513:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
 8514:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
 8515:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
 8516:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
 8517:     break;
 8518:   case UNITLENGTH:
 8519:     if ( value != NOVALUE )		/* passed a fixed value to be set */
 8520: 	unitlength = (double)(value);	/* set given fixed value */
 8521:     else				/* get value from expression */
 8522:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
 8523: 	if ( *valuearg != '\000' )	/* guard against empty string */
 8524: 	  unitlength = strtod(valuearg,NULL); } /* convert to double */
 8525:     iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
 8526:     break;
 8527:   } /* --- end-of-switch(flag) --- */
 8528: return ( NULL );			/*just set value, nothing to display*/
 8529: } /* --- end-of-function rastflags() --- */
 8530: 
 8531: 
 8532: /* ==========================================================================
 8533:  * Function:	rastspace(expression, size, basesp,  width, isfill, isheight)
 8534:  * Purpose:	returns a blank/space subraster width wide,
 8535:  *		with baseline and height corresponding to basep
 8536:  * --------------------------------------------------------------------------
 8537:  * Arguments:	expression (I)	char **  to first char of null-terminated
 8538:  *				LaTeX expression (unused/unchanged)
 8539:  *		size (I)	int containing base font size (not used,
 8540:  *				just stored in subraster)
 8541:  *		basesp (I)	subraster *  to character (or subexpression)
 8542:  *				immediately preceding space, whose baseline
 8543:  *				and height params are transferred to space
 8544:  *		width (I)	int containing #bits/pixels for space width
 8545:  *		isfill (I)	int containing true to \hfill complete
 8546:  *				expression out to width
 8547:  *				(Kludge: isfill=99 signals \hspace*
 8548:  *				for negative space)
 8549:  *		isheight (I)	int containing true (but not NOVALUE)
 8550:  *				to treat width arg as height
 8551:  * --------------------------------------------------------------------------
 8552:  * Returns:	( subraster * )	ptr to empty/blank subraster
 8553:  *				or NULL for any error
 8554:  * --------------------------------------------------------------------------
 8555:  * Notes:     o
 8556:  * ======================================================================= */
 8557: /* --- entry point --- */
 8558: subraster *rastspace ( char **expression, int size, subraster *basesp,
 8559: 			int width, int isfill, int isheight )
 8560: {
 8561: /* -------------------------------------------------------------------------
 8562: Allocations and Declarations
 8563: -------------------------------------------------------------------------- */
 8564: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
 8565: raster	*bp=NULL, *backspace_raster();	/* for negative space */
 8566: int	delete_subraster();		/* if fail, free unneeded subraster*/
 8567: int	baseht=1, baseln=0;		/* height,baseline of base symbol */
 8568: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
 8569: int	isstar=0, minspace=0;		/* defaults for negative hspace */
 8570: char	*texsubexpr(), widtharg[256];	/* parse for optional {width} */
 8571: int	evalterm(), evalue=0;		/* evaluate [args], {args} */
 8572: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
 8573: subraster *rastcat();			/* cat rightsp after \hfill */
 8574: /* -------------------------------------------------------------------------
 8575: initialization
 8576: -------------------------------------------------------------------------- */
 8577: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
 8578: if ( isfill == NOVALUE ) isfill=0;	/* novalue means false */
 8579: if ( isheight == NOVALUE ) isheight=0;	/* novalue means false */
 8580: minspace = (isstar?(-1):0);		/* reset default minspace */
 8581: /* -------------------------------------------------------------------------
 8582: determine width if not given (e.g., \hspace{width}, \hfill{width})
 8583: -------------------------------------------------------------------------- */
 8584: if ( width == 0 ) {			/* width specified in expression */
 8585:   double dwidth;  int widthval;		/* test {width} before using it */
 8586:   int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
 8587:   /* --- check if optional [minspace] given for negative \hspace --- */
 8588:   if ( *(*expression) == '[' ) {	/* [minspace] if leading char is [ */
 8589:     /* ---parse [minspace], bump expression past it, evaluate expression--- */
 8590:     *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
 8591:     if ( !isempty(widtharg) ) {		/* got [minspace] */
 8592:       evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
 8593:       minspace = iround(unitlength*((double)evalue)); } /* in pixels */
 8594:     } /* --- end-of-if(*(*expression)=='[') --- */
 8595:   width = 1;				/* set default width */
 8596:   *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
 8597:   dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
 8598:   widthval =				/* convert {width} to integer */
 8599: 		(int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
 8600:   if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
 8601:     width = widthval;			/* replace deafault width */
 8602:   } /* --- end-of-if(width==0) --- */
 8603: /* -------------------------------------------------------------------------
 8604: first check for negative space
 8605: -------------------------------------------------------------------------- */
 8606: if ( width < 0 ) {			/* have negative hspace */
 8607:  if ( leftexpression != (subraster *)NULL ) /* can't backspace */
 8608:   if ( (spacesp=new_subraster(0,0,0))	/* get new subraster for backspace */
 8609:   !=   NULL ) {				/* and if we succeed... */
 8610:    int nback=(-width), pback;		/*#pixels wanted,actually backspaced*/
 8611:    if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
 8612:    !=    NULL ) {			/* and if backspace succeeds... */
 8613:      spacesp->image = bp;		/* save backspaced image */
 8614:      /*spacesp->type = leftexpression->type;*/ /* copy original type */
 8615:      spacesp->type = blanksignal;	/* need to propagate blanks */
 8616:      spacesp->size = leftexpression->size; /* copy original font size */
 8617:      spacesp->baseline = leftexpression->baseline; /* and baseline */
 8618:      blanksymspace += -(nback-pback);	/* wanted more than we got */
 8619:      isreplaceleft = 1; }		/*signal to replace entire expressn*/
 8620:    else {				/* backspace failed */
 8621:      delete_subraster(spacesp);		/* free unneeded envelope */
 8622:      spacesp = (subraster *)NULL; } }	/* and signal failure */
 8623:  goto end_of_job;
 8624:  } /* --- end-of-if(width<0) --- */
 8625: /* -------------------------------------------------------------------------
 8626: see if width is "absolute" or fill width
 8627: -------------------------------------------------------------------------- */
 8628: if ( isfill				/* called as \hfill{} */
 8629: &&   !isheight )			/* parameter conflict */
 8630:  { if ( leftexpression != NULL )	/* if we have left half */
 8631:     width -= (leftexpression->image)->width; /*reduce left width from total*/
 8632:    if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
 8633:    != NULL )				/* succeeded */
 8634:     width -= (rightsp->image)->width; } /* reduce right width from total */
 8635: /* -------------------------------------------------------------------------
 8636: construct blank subraster, and return it to caller
 8637: -------------------------------------------------------------------------- */
 8638: /* --- get parameters from base symbol --- */
 8639: if ( basesp != (subraster *)NULL )	/* we have base symbol for space */
 8640:   { baseht = (basesp->image)->height; 	/* height of base symbol */
 8641:     baseln =  basesp->baseline; }	/* and its baseline */
 8642: /* --- flip params for height --- */
 8643: if ( isheight )				/* width is actually height */
 8644:   { baseht = width;			/* use given width as height */
 8645:     width = 1; }			/* and set default width */
 8646: /* --- generate and init space subraster --- */
 8647: if ( width > 0 )			/*make sure we have positive width*/
 8648:  if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
 8649:  !=   NULL )				/* and if we succeed... */
 8650:   { /* --- ...re-init subraster parameters --- */
 8651:     spacesp->size = size;		/*propagate base font size forward*/
 8652:     if(1)spacesp->type = blanksignal;	/* need to propagate blanks (???) */
 8653:     spacesp->baseline = baseln; }	/* ditto baseline */
 8654: /* -------------------------------------------------------------------------
 8655: concat right half if \hfill-ing
 8656: -------------------------------------------------------------------------- */
 8657: if ( rightsp != NULL )			/* we have a right half after fill */
 8658:   { spacesp = (spacesp==NULL? rightsp:	/* no space, so just use right half*/
 8659: 	rastcat(spacesp,rightsp,3));	/* or cat right half after space */
 8660:     spacesp->type = blanksignal;	/* need to propagate blanks */
 8661:     *expression += strlen((*expression)); } /* push expression to its null */
 8662: end_of_job:
 8663:   return ( spacesp );
 8664: } /* --- end-of-function rastspace() --- */
 8665: 
 8666: 
 8667: /* ==========================================================================
 8668:  * Function:	rastnewline ( expression, size, basesp,  arg1, arg2, arg3 )
 8669:  * Purpose:	\\ handler, returns subraster corresponding to
 8670:  *		left-hand expression preceding \\ above right-hand expression
 8671:  * --------------------------------------------------------------------------
 8672:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 8673:  *				string immediately following \\ to be
 8674:  *				rasterized, and returning ptr immediately
 8675:  *				to terminating null.
 8676:  *		size (I)	int containing 0-7 default font size
 8677:  *		basesp (I)	subraster *  to character (or subexpression)
 8678:  *				immediately preceding \\
 8679:  *				(unused, but passed for consistency)
 8680:  *		arg1 (I)	int unused
 8681:  *		arg2 (I)	int unused
 8682:  *		arg3 (I)	int unused
 8683:  * --------------------------------------------------------------------------
 8684:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
 8685:  *				or NULL for any parsing error
 8686:  *				(expression ptr unchanged if error occurs)
 8687:  * --------------------------------------------------------------------------
 8688:  * Notes:     o
 8689:  * ======================================================================= */
 8690: /* --- entry point --- */
 8691: subraster *rastnewline ( char **expression, int size, subraster *basesp,
 8692: 			int arg1, int arg2, int arg3 )
 8693: {
 8694: /* -------------------------------------------------------------------------
 8695: Allocations and Declarations
 8696: -------------------------------------------------------------------------- */
 8697: subraster *rastack(), *newlsp=NULL;	/* subraster for both lines */
 8698: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
 8699: char	*texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
 8700: int	evalterm(), evalue=0;		/* evaluate [arg], {arg} */
 8701: int	vspace = size+2;		/* #pixels between lines */
 8702: /* -------------------------------------------------------------------------
 8703: obtain optional [vspace] argument immediately following \\ command
 8704: -------------------------------------------------------------------------- */
 8705: /* --- check if [vspace] given --- */
 8706: if ( *(*expression) == '[' )		/*have [vspace] if leading char is [*/
 8707:   {
 8708:   /* ---parse [vspace] and bump expression past it, interpret as double--- */
 8709:   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
 8710:   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
 8711:   evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
 8712:   vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
 8713:   } /* --- end-of-if(*(*expression)=='[') --- */
 8714: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
 8715: /* -------------------------------------------------------------------------
 8716: rasterize right half of expression and stack left half above it
 8717: -------------------------------------------------------------------------- */
 8718: /* --- rasterize right half --- */
 8719: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
 8720: == NULL ) goto end_of_job;		/* quit if failed */
 8721: /* --- stack left half above it --- */
 8722: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
 8723: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
 8724: /* --- back to caller --- */
 8725: end_of_job:
 8726:   if ( newlsp != NULL )			/* returning entire expression */
 8727:     { int newht = (newlsp->image)->height; /* height of returned subraster */
 8728:       newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
 8729:       isreplaceleft = 1;		/* so set flag to replace left half*/
 8730:       *expression += strlen(*expression); } /* and push to terminating null*/
 8731:   return ( newlsp );			/* 1st line over 2nd, or null=error*/
 8732: } /* --- end-of-function rastnewline() --- */
 8733: 
 8734: 
 8735: /* ==========================================================================
 8736:  * Function:	rastarrow ( expression, size, basesp,  drctn, isBig, arg3 )
 8737:  * Purpose:	returns left/right arrow subraster (e.g., for \longrightarrow)
 8738:  * --------------------------------------------------------------------------
 8739:  * Arguments:	expression (I)	char **  to first char of null-terminated
 8740:  *				LaTeX expression (unused/unchanged)
 8741:  *		size (I)	int containing base font size (not used,
 8742:  *				just stored in subraster)
 8743:  *		basesp (I)	subraster *  to character (or subexpression)
 8744:  *				immediately preceding space, whose baseline
 8745:  *				and height params are transferred to space
 8746:  *		drctn (I)	int containing +1 for right, -1 for left,
 8747:  *				or 0 for leftright
 8748:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
 8749:  *		arg3 (I)	int unused
 8750:  * --------------------------------------------------------------------------
 8751:  * Returns:	( subraster * )	ptr to left/right arrow subraster
 8752:  *				or NULL for any error
 8753:  * --------------------------------------------------------------------------
 8754:  * Notes:     o	An optional argument [width] may *immediately* follow
 8755:  *		the \longxxx to explicitly set the arrow's width in pixels.
 8756:  *		For example, \longrightarrow calculates a default width
 8757:  *		(as usual in LaTeX), whereas \longrightarrow[50] explicitly
 8758:  *		draws a 50-pixel long arrow.  This can be used, e.g.,
 8759:  *		to draw commutative diagrams in conjunction with
 8760:  *		\array (and maybe with \stackrel and/or \relstack, too).
 8761:  *	      o	In case you really want to render, say, [f]---->[g], just
 8762:  *		use an intervening space, i.e., [f]\longrightarrow~[g].
 8763:  *		In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
 8764:  * ======================================================================= */
 8765: /* --- entry point --- */
 8766: subraster *rastarrow ( char **expression, int size, subraster *basesp,
 8767: 			int drctn, int isBig, int arg3 )
 8768: {
 8769: /* -------------------------------------------------------------------------
 8770: Allocations and Declarations
 8771: -------------------------------------------------------------------------- */
 8772: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
 8773: char	*texsubexpr(), widtharg[256];	/* parse for optional [width] */
 8774: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
 8775: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
 8776: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
 8777: int	delete_subraster();		/*free work areas in case of error*/
 8778: int	evalterm();			/* evaluate [arg], {arg} */
 8779: int	width = 10 + 8*size,  height;	/* width, height for \longxxxarrow */
 8780: int	islimits = 1;			/*true to handle limits internally*/
 8781: int	limsize = size-1;		/* font size for limits */
 8782: int	vspace = 1;			/* #empty rows below arrow */
 8783: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
 8784: /* -------------------------------------------------------------------------
 8785: construct longleft/rightarrow subraster, with limits, and return it to caller
 8786: -------------------------------------------------------------------------- */
 8787: /* --- check for optional width arg and replace default width --- */
 8788: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
 8789:   { int widthval;			/* test [width] before using it */
 8790:     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
 8791:     widthval =				/* convert [width] to integer */
 8792: 	(int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
 8793:     if ( widthval>=2 && widthval<=600 )	/* sanity check */
 8794:       width = widthval; }		/* replace deafault width */
 8795: /* --- now parse for limits, and bump expression past it(them) --- */
 8796: if ( islimits )				/* handling limits internally */
 8797:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
 8798:     if ( *sub != '\000' )		/*have a subscript following arrow*/
 8799:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
 8800:     if ( *super != '\000' )		/*have superscript following arrow*/
 8801:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
 8802: /* --- set height based on width --- */
 8803: height = min2(17,max2(9,(width+2)/6));	/* height based on width */
 8804: height = 1 + (height/2)*2;		/* always force odd height */
 8805: /* --- generate arrow subraster --- */
 8806: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
 8807: ==   NULL ) goto end_of_job;		/* and quit if we failed */
 8808: /* --- add space below arrow --- */
 8809: if ( vspace > 0 )			/* if we have space below arrow */
 8810:   if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
 8811:   !=   NULL )				/* and if we succeeded */
 8812:     if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
 8813:     ==   NULL ) goto end_of_job;	/* and quit if we failed */
 8814: /* --- init arrow subraster parameters --- */
 8815: arrowsp->size = size;			/*propagate base font size forward*/
 8816: arrowsp->baseline = height+vspace-1;	/* set baseline at bottom of arrow */
 8817: /* --- add limits above/below arrow, as necessary --- */
 8818: if ( subsp != NULL )			/* stack subscript below arrow */
 8819:   if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
 8820:   ==   NULL ) goto end_of_job;		/* quit if failed */
 8821: if ( supsp != NULL )			/* stack superscript above arrow */
 8822:   if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
 8823:   ==   NULL ) goto end_of_job;		/* quit if failed */
 8824: /* --- return arrow (or NULL) to caller --- */
 8825: end_of_job:
 8826:   return ( arrowsp );
 8827: } /* --- end-of-function rastarrow() --- */
 8828: 
 8829: 
 8830: /* ==========================================================================
 8831:  * Function:	rastuparrow ( expression, size, basesp,  drctn, isBig, arg3 )
 8832:  * Purpose:	returns an up/down arrow subraster (e.g., for \longuparrow)
 8833:  * --------------------------------------------------------------------------
 8834:  * Arguments:	expression (I)	char **  to first char of null-terminated
 8835:  *				LaTeX expression (unused/unchanged)
 8836:  *		size (I)	int containing base font size (not used,
 8837:  *				just stored in subraster)
 8838:  *		basesp (I)	subraster *  to character (or subexpression)
 8839:  *				immediately preceding space, whose baseline
 8840:  *				and height params are transferred to space
 8841:  *		drctn (I)	int containing +1 for up, -1 for down,
 8842:  *				or 0 for updown
 8843:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
 8844:  *		arg3 (I)	int unused
 8845:  * --------------------------------------------------------------------------
 8846:  * Returns:	( subraster * )	ptr to up/down arrow subraster
 8847:  *				or NULL for any error
 8848:  * --------------------------------------------------------------------------
 8849:  * Notes:     o	An optional argument [height] may *immediately* follow
 8850:  *		the \longxxx to explicitly set the arrow's height in pixels.
 8851:  *		For example, \longuparrow calculates a default height
 8852:  *		(as usual in LaTeX), whereas \longuparrow[25] explicitly
 8853:  *		draws a 25-pixel high arrow.  This can be used, e.g.,
 8854:  *		to draw commutative diagrams in conjunction with
 8855:  *		\array (and maybe with \stackrel and/or \relstack, too).
 8856:  *	      o	In case you really want to render, say, [f]---->[g], just
 8857:  *		use an intervening space, i.e., [f]\longuparrow~[g].
 8858:  *		In text use two spaces {\rm~[f]\longuparrow~~[g]}.
 8859:  * ======================================================================= */
 8860: /* --- entry point --- */
 8861: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
 8862: 			int drctn, int isBig, int arg3 )
 8863: {
 8864: /* -------------------------------------------------------------------------
 8865: Allocations and Declarations
 8866: -------------------------------------------------------------------------- */
 8867: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
 8868: char	*texsubexpr(), heightarg[256];	/* parse for optional [height] */
 8869: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
 8870: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
 8871: subraster *rastcat();			/* cat superscript left, sub right */
 8872: int	evalterm();			/* evaluate [arg], {arg} */
 8873: int	height = 8 + 2*size,  width;	/* height, width for \longxxxarrow */
 8874: int	islimits = 1;			/*true to handle limits internally*/
 8875: int	limsize = size-1;		/* font size for limits */
 8876: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
 8877: /* -------------------------------------------------------------------------
 8878: construct blank subraster, and return it to caller
 8879: -------------------------------------------------------------------------- */
 8880: /* --- check for optional height arg and replace default height --- */
 8881: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
 8882:   { int heightval;			/* test height before using it */
 8883:     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
 8884:     heightval =				/* convert [height] to integer */
 8885: 	(int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
 8886:     if ( heightval>=2 && heightval<=600 ) /* sanity check */
 8887:       height = heightval; }		/* replace deafault height */
 8888: /* --- now parse for limits, and bump expression past it(them) --- */
 8889: if ( islimits )				/* handling limits internally */
 8890:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
 8891:     if ( *sub != '\000' )		/*have a subscript following arrow*/
 8892:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
 8893:     if ( *super != '\000' )		/*have superscript following arrow*/
 8894:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
 8895: /* --- set width based on height --- */
 8896: width = min2(17,max2(9,(height+2)/4));	/* width based on height */
 8897: width = 1 + (width/2)*2;		/* always force odd width */
 8898: /* --- generate arrow subraster --- */
 8899: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
 8900: ==   NULL ) goto end_of_job;		/* and quit if we failed */
 8901: /* --- init arrow subraster parameters --- */
 8902: arrowsp->size = size;			/*propagate base font size forward*/
 8903: arrowsp->baseline = height-1;		/* set baseline at bottom of arrow */
 8904: /* --- add limits above/below arrow, as necessary --- */
 8905: if ( supsp != NULL )			/* cat superscript to left of arrow*/
 8906:   { int	supht = (supsp->image)->height,	/* superscript height */
 8907: 	deltab = (1+abs(height-supht))/2; /* baseline difference to center */
 8908:   supsp->baseline = supht-1;		/* force script baseline to bottom */
 8909:   if ( supht <= height )		/* arrow usually taller than script*/
 8910: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
 8911:   else	supsp->baseline -= deltab;	/* else bottom of arrow goes here */
 8912:   if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
 8913:     ==   NULL ) goto end_of_job; }	/* quit if failed */
 8914: if ( subsp != NULL )			/* cat subscript to right of arrow */
 8915:   { int	subht = (subsp->image)->height,	/* subscript height */
 8916: 	deltab = (1+abs(height-subht))/2; /* baseline difference to center */
 8917:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
 8918:   subsp->baseline = subht-1;		/* force script baseline to bottom */
 8919:   if ( subht <= height )		/* arrow usually taller than script*/
 8920: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
 8921:   else	subsp->baseline -= deltab;	/* else bottom of arrow goes here */
 8922:   if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
 8923:     ==   NULL ) goto end_of_job; }	/* quit if failed */
 8924: /* --- return arrow (or NULL) to caller --- */
 8925: end_of_job:
 8926:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
 8927:   return ( arrowsp );
 8928: } /* --- end-of-function rastuparrow() --- */
 8929: 
 8930: 
 8931: /* ==========================================================================
 8932:  * Function:	rastoverlay (expression, size, basesp, overlay, offset2, arg3)
 8933:  * Purpose:	overlays one raster on another
 8934:  * --------------------------------------------------------------------------
 8935:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 8936:  *				string immediately following overlay \cmd to
 8937:  *				be rasterized, and returning ptr immediately
 8938:  *				following last character processed.
 8939:  *		size (I)	int containing 0-7 default font size
 8940:  *		basesp (I)	subraster *  to character (or subexpression)
 8941:  *				immediately preceding overlay \cmd
 8942:  *				(unused, but passed for consistency)
 8943:  *		overlay (I)	int containing 1 to overlay / (e.g., \not)
 8944:  *				or NOVALUE to pick up 2nd arg from expression
 8945:  *		offset2 (I)	int containing #pixels to horizontally offset
 8946:  *				overlay relative to underlying symbol,
 8947:  *				positive(right) or negative or 0,
 8948:  *				or NOVALUE to pick up optional [offset] arg
 8949:  *		arg3 (I)	int unused
 8950:  * --------------------------------------------------------------------------
 8951:  * Returns:	( subraster * )	ptr to subraster corresponding to composite,
 8952:  *				or NULL for any parsing error
 8953:  * --------------------------------------------------------------------------
 8954:  * Notes:     o
 8955:  * ======================================================================= */
 8956: /* --- entry point --- */
 8957: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
 8958: 			int overlay, int offset2, int arg3 )
 8959: {
 8960: /* -------------------------------------------------------------------------
 8961: Allocations and Declarations
 8962: -------------------------------------------------------------------------- */
 8963: char	*texsubexpr(),			/*parse expression for base,overlay*/
 8964: 	expr1[512], expr2[512];		/* base, overlay */
 8965: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
 8966: 	*new_subraster();		/*explicitly alloc sp2 if necessary*/
 8967: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
 8968: int	isalign = 0;			/* true to align baselines */
 8969: int	line_raster();			/* draw diagonal for \Not */
 8970: int	evalterm();			/* evaluate [arg], {arg} */
 8971: /* -------------------------------------------------------------------------
 8972: Obtain base, and maybe overlay, and rasterize them
 8973: -------------------------------------------------------------------------- */
 8974: /* --- check for optional offset2 arg  --- */
 8975: if ( offset2 == NOVALUE )		/* only if not explicitly specified*/
 8976:  if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
 8977:   { int offsetval;			/* test before using it */
 8978:     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
 8979:     offsetval =				/* convert [offset2] to int */
 8980: 	(int)(((double)evalterm(mimestore,expr2))+0.5);
 8981:     if ( abs(offsetval) <= 25 )		/* sanity check */
 8982:       offset2 = offsetval; }		/* replace deafault */
 8983: if ( offset2 == NOVALUE ) offset2 = 0;	/* novalue means no offset */
 8984: /* --- parse for base, bump expression past it, and rasterize it --- */
 8985: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
 8986: if ( isempty(expr1) ) goto end_of_job;	/* nothing to overlay, so quit */
 8987: rastlift1 = rastlift = 0;		/* reset all raisebox() lifts */
 8988: if ( strstr(expr1,"\\raise") != NULL )	/* have a \raisebox */
 8989:   isalign = 2;				/* so align baselines */
 8990: if ( (sp1=rasterize(expr1,size))	/* rasterize base expression */
 8991: ==   NULL ) goto end_of_job;		/* quit if failed to rasterize */
 8992: overlaysp = sp1;			/*in case we return with no overlay*/
 8993: rastlift1 = rastlift;			/* save lift for base expression */
 8994: /* --- get overlay expression, and rasterize it --- */
 8995: if ( overlay == NOVALUE )		/* get overlay from input stream */
 8996:   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
 8997:     if ( !isempty(expr2) ) {		/* have an overlay */
 8998:       if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
 8999: 	isalign = 2;			/* so align baselines */
 9000:       sp2 = rasterize(expr2,size); } }	/* rasterize overlay expression */
 9001: else					/* use specific built-in overlay */
 9002:   switch ( overlay )
 9003:     {
 9004:     default: break;
 9005:     case 1:				/* e.g., \not overlays slash */
 9006:       sp2 = rasterize("/",size+1);	/* rasterize overlay expression */
 9007:       isalign = 0;			/* automatically handled correctly */
 9008:       offset2 = max2(1,size-3);		/* push / right a bit */
 9009:       offset2 = 0;
 9010:       break;
 9011:     case 2:				/* e.g., \Not draws diagonal */
 9012:       sp2 = NULL;			/* no overlay required */
 9013:       isalign = 0;			/* automatically handled correctly */
 9014:       if ( overlaysp != NULL )		/* check that we have raster */
 9015: 	{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
 9016: 	  int width=rp->width, height=rp->height; /* raster dimensions */
 9017: 	  if ( 0 )			/* diagonal within bounding box */
 9018: 	   line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
 9019: 	  else				/* construct "wide" diagonal */
 9020: 	   { int margin=3;		/* desired extra margin width */
 9021: 	     sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
 9022: 	     if ( sp2 != NULL )		/* allocated successfully */
 9023: 	      line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
 9024:       break;
 9025:     case 3:				/* e.g., \sout for strikeout */
 9026:       sp2 = NULL;			/* no overlay required */
 9027:       if ( overlaysp != NULL )		/* check that we have raster */
 9028: 	{ raster *rp = overlaysp->image; /* raster to be \sout-ed */
 9029: 	  int width=rp->width, height=rp->height; /* raster dimensions */
 9030: 	  int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
 9031: 	  int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
 9032: 	  if ( 1 )			/* strikeout within bounding box */
 9033: 	    line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
 9034:       break;
 9035:     } /* --- end-of-switch(overlay) --- */
 9036: if ( sp2 == NULL ) goto end_of_job;	/*return sp1 if failed to rasterize*/
 9037: /* -------------------------------------------------------------------------
 9038: construct composite overlay
 9039: -------------------------------------------------------------------------- */
 9040: overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
 9041: end_of_job:
 9042:   return ( overlaysp );
 9043: } /* --- end-of-function rastoverlay() --- */
 9044: 
 9045: 
 9046: /* ==========================================================================
 9047:  * Function:	rastfrac ( expression, size, basesp,  isfrac, arg2, arg3 )
 9048:  * Purpose:	\frac,\atop handler, returns a subraster corresponding to
 9049:  *		expression (immediately following \frac,\atop) at font size
 9050:  * --------------------------------------------------------------------------
 9051:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9052:  *				string immediately following \frac to be
 9053:  *				rasterized, and returning ptr immediately
 9054:  *				following last character processed.
 9055:  *		size (I)	int containing 0-7 default font size
 9056:  *		basesp (I)	subraster *  to character (or subexpression)
 9057:  *				immediately preceding \frac
 9058:  *				(unused, but passed for consistency)
 9059:  *		isfrac (I)	int containing true to draw horizontal line
 9060:  *				between numerator and denominator,
 9061:  *				or false not to draw it (for \atop).
 9062:  *		arg2 (I)	int unused
 9063:  *		arg3 (I)	int unused
 9064:  * --------------------------------------------------------------------------
 9065:  * Returns:	( subraster * )	ptr to subraster corresponding to fraction,
 9066:  *				or NULL for any parsing error
 9067:  * --------------------------------------------------------------------------
 9068:  * Notes:     o
 9069:  * ======================================================================= */
 9070: /* --- entry point --- */
 9071: subraster *rastfrac ( char **expression, int size, subraster *basesp,
 9072: 			int isfrac, int arg2, int arg3 )
 9073: {
 9074: /* -------------------------------------------------------------------------
 9075: Allocations and Declarations
 9076: -------------------------------------------------------------------------- */
 9077: char	*texsubexpr(),			/*parse expression for numer,denom*/
 9078: 	numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
 9079: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
 9080: subraster *rastack(), *fracsp=NULL;	/* subraster for numer/denom */
 9081: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
 9082: int	width=0,			/* width of constructed raster */
 9083: 	numheight=0;			/* height of numerator */
 9084: int	baseht=0, baseln=0;		/* height,baseline of base symbol */
 9085: /*int	istweak = 1;*/			/*true to tweak baseline alignment*/
 9086: int	rule_raster(),			/* draw horizontal line for frac */
 9087: 	lineheight = 1;			/* thickness of fraction line */
 9088: int	vspace = (size>2?2:1);		/*vertical space between components*/
 9089: int	delete_subraster();		/*free work areas in case of error*/
 9090: int	type_raster();			/* display debugging output */
 9091: /* -------------------------------------------------------------------------
 9092: Obtain numerator and denominator, and rasterize them
 9093: -------------------------------------------------------------------------- */
 9094: /* --- parse for numerator,denominator and bump expression past them --- */
 9095: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
 9096: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
 9097: if ( *numer=='\000' && *denom=='\000' )	/* missing both components of frac */
 9098:   goto end_of_job;			/* nothing to do, so quit */
 9099: /* --- rasterize numerator, denominator --- */
 9100: if ( *numer != '\000' )			/* have a numerator */
 9101:  if ( (numsp = rasterize(numer,size-1))	/* so rasterize numer at size-1 */
 9102:  ==   NULL ) goto end_of_job;		/* and quit if failed */
 9103: if ( *denom != '\000' )			/* have a denominator */
 9104:  if ( (densp = rasterize(denom,size-1))	/* so rasterize denom at size-1 */
 9105:  ==   NULL )				/* failed */
 9106:   { if ( numsp != NULL )		/* already rasterized numerator */
 9107:       delete_subraster(numsp);		/* so free now-unneeded numerator */
 9108:     goto end_of_job; }			/* and quit */
 9109: /* --- if one componenet missing, use a blank space for it --- */
 9110: if ( numsp == NULL )			/* no numerator given */
 9111:   numsp = rasterize("[?]",size-1);	/* missing numerator */
 9112: if ( densp == NULL )			/* no denominator given */
 9113:   densp = rasterize("[?]",size-1);	/* missing denominator */
 9114: /* --- check that we got both components --- */
 9115: if ( numsp==NULL || densp==NULL )	/* some problem */
 9116:   { delete_subraster(numsp);		/*delete numerator (if it existed)*/
 9117:     delete_subraster(densp);		/*delete denominator (if it existed)*/
 9118:     goto end_of_job; }			/* and quit */
 9119: /* --- get height of numerator (to determine where line belongs) --- */
 9120: numheight = (numsp->image)->height;	/* get numerator's height */
 9121: /* -------------------------------------------------------------------------
 9122: construct raster with numerator stacked over denominator
 9123: -------------------------------------------------------------------------- */
 9124: /* --- construct raster with numer/denom --- */
 9125: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
 9126: ==  NULL )				/* failed to construct numer/denom */
 9127:   { delete_subraster(numsp);		/* so free now-unneeded numerator */
 9128:     delete_subraster(densp);		/* and now-unneeded denominator */
 9129:     goto end_of_job; }			/* and then quit */
 9130: /* --- determine width of constructed raster --- */
 9131: width = (fracsp->image)->width;		/*just get width of embedded image*/
 9132: /* --- initialize subraster parameters --- */
 9133: fracsp->size = size;			/* propagate font size forward */
 9134: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
 9135: fracsp->type = FRACRASTER;		/* signal \frac image */
 9136: if ( basesp != (subraster *)NULL )	/* we have base symbol for frac */
 9137:   { baseht = (basesp->image)->height; 	/* height of base symbol */
 9138:     baseln =  basesp->baseline;		/* and its baseline */
 9139:   } /* --- end-of-if(basesp!=NULL) --- */
 9140: /* -------------------------------------------------------------------------
 9141: draw horizontal line between numerator and denominator
 9142: -------------------------------------------------------------------------- */
 9143: fraccenterline = numheight+vspace;	/* signal that we have a \frac */
 9144: if ( isfrac )				/*line for \frac, but not for \atop*/
 9145:   rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
 9146: /* -------------------------------------------------------------------------
 9147: return final result to caller
 9148: -------------------------------------------------------------------------- */
 9149: end_of_job:
 9150:   if ( msgfp!=NULL && msglevel>=99 )
 9151:     { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
 9152:       if ( fracsp != NULL )		/* have a constructed raster */
 9153: 	type_raster(fracsp->image,msgfp); } /* display constructed raster */
 9154:   return ( fracsp );
 9155: } /* --- end-of-function rastfrac() --- */
 9156: 
 9157: 
 9158: /* ==========================================================================
 9159:  * Function:	rastackrel ( expression, size, basesp,  base, arg2, arg3 )
 9160:  * Purpose:	\stackrel handler, returns a subraster corresponding to
 9161:  *		stacked relation
 9162:  * --------------------------------------------------------------------------
 9163:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9164:  *				string immediately following \stackrel to be
 9165:  *				rasterized, and returning ptr immediately
 9166:  *				following last character processed.
 9167:  *		size (I)	int containing 0-7 default font size
 9168:  *		basesp (I)	subraster *  to character (or subexpression)
 9169:  *				immediately preceding \stackrel
 9170:  *				(unused, but passed for consistency)
 9171:  *		base (I)	int containing 1 if upper/first subexpression
 9172:  *				is base relation, or 2 if lower/second is
 9173:  *		arg2 (I)	int unused
 9174:  *		arg3 (I)	int unused
 9175:  * --------------------------------------------------------------------------
 9176:  * Returns:	( subraster * )	ptr to subraster corresponding to stacked
 9177:  *				relation, or NULL for any parsing error
 9178:  * --------------------------------------------------------------------------
 9179:  * Notes:     o
 9180:  * ======================================================================= */
 9181: /* --- entry point --- */
 9182: subraster *rastackrel ( char **expression, int size, subraster *basesp,
 9183: 			int base, int arg2, int arg3 )
 9184: {
 9185: /* -------------------------------------------------------------------------
 9186: Allocations and Declarations
 9187: -------------------------------------------------------------------------- */
 9188: char	*texsubexpr(),			/*parse expression for upper,lower*/
 9189: 	upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1];	/* parsed upper, lower */
 9190: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
 9191: subraster *rastack(), *relsp=NULL;	/* subraster for upper/lower */
 9192: int	upsize  = (base==1? size:size-1), /* font size for upper component */
 9193: 	lowsize = (base==2? size:size-1); /* font size for lower component */
 9194: int	vspace = 1;			/*vertical space between components*/
 9195: int	delete_subraster();		/*free work areas in case of error*/
 9196: /* -------------------------------------------------------------------------
 9197: Obtain numerator and denominator, and rasterize them
 9198: -------------------------------------------------------------------------- */
 9199: /* --- parse for numerator,denominator and bump expression past them --- */
 9200: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
 9201: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
 9202: if ( *upper=='\000' || *lower=='\000' )	/* missing either component */
 9203:   goto end_of_job;			/* nothing to do, so quit */
 9204: /* --- rasterize upper, lower --- */
 9205: if ( *upper != '\000' )			/* have upper component */
 9206:  if ( (upsp = rasterize(upper,upsize))	/* so rasterize upper component */
 9207:  ==   NULL ) goto end_of_job;		/* and quit if failed */
 9208: if ( *lower != '\000' )			/* have lower component */
 9209:  if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
 9210:  ==   NULL )				/* failed */
 9211:   { if ( upsp != NULL )			/* already rasterized upper */
 9212:       delete_subraster(upsp);		/* so free now-unneeded upper */
 9213:     goto end_of_job; }			/* and quit */
 9214: /* -------------------------------------------------------------------------
 9215: construct stacked relation raster
 9216: -------------------------------------------------------------------------- */
 9217: /* --- construct stacked relation --- */
 9218: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
 9219: ==   NULL ) goto end_of_job;		/* quit if failed */
 9220: /* --- initialize subraster parameters --- */
 9221: relsp->size = size;			/* propagate font size forward */
 9222: /* -------------------------------------------------------------------------
 9223: return final result to caller
 9224: -------------------------------------------------------------------------- */
 9225: end_of_job:
 9226:   return ( relsp );
 9227: } /* --- end-of-function rastackrel() --- */
 9228: 
 9229: 
 9230: /* ==========================================================================
 9231:  * Function:	rastmathfunc ( expression, size, basesp,  base, arg2, arg3 )
 9232:  * Purpose:	\log, \lim, etc handler, returns a subraster corresponding
 9233:  *		to math functions
 9234:  * --------------------------------------------------------------------------
 9235:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9236:  *				string immediately following \mathfunc to be
 9237:  *				rasterized, and returning ptr immediately
 9238:  *				following last character processed.
 9239:  *		size (I)	int containing 0-7 default font size
 9240:  *		basesp (I)	subraster *  to character (or subexpression)
 9241:  *				immediately preceding \mathfunc
 9242:  *				(unused, but passed for consistency)
 9243:  *		mathfunc (I)	int containing 1=arccos, 2=arcsin, etc.
 9244:  *		islimits (I)	int containing 1 if function may have
 9245:  *				limits underneath, e.g., \lim_{n\to\infty}
 9246:  *		arg3 (I)	int unused
 9247:  * --------------------------------------------------------------------------
 9248:  * Returns:	( subraster * )	ptr to subraster corresponding to mathfunc,
 9249:  *				or NULL for any parsing error
 9250:  * --------------------------------------------------------------------------
 9251:  * Notes:     o
 9252:  * ======================================================================= */
 9253: /* --- entry point --- */
 9254: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
 9255: 			int mathfunc, int islimits, int arg3 )
 9256: {
 9257: /* -------------------------------------------------------------------------
 9258: Allocations and Declarations
 9259: -------------------------------------------------------------------------- */
 9260: char	*texscripts(),			/* parse expression for _limits */
 9261: 	func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
 9262: char	*texsubexpr(),			/* parse expression for arg */
 9263: 	funcarg[MAXTOKNSZ+1];		/* optional func arg */
 9264: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
 9265: subraster *rastack(), *mathfuncsp=NULL;	/* subraster for mathfunc/limits */
 9266: int	limsize = size-1;		/* font size for limits */
 9267: int	vspace = 1;			/*vertical space between components*/
 9268: int	delete_subraster();		/*free work areas in case of error*/
 9269: /* --- table of function names by mathfunc number --- */
 9270: static	int  numnames = 34;		/* number of names in table */
 9271: static	char *funcnames[] = {
 9272: 	"error",			/*  0 index is illegal/error bucket*/
 9273: 	"arccos",  "arcsin",  "arctan",	/*  1 -  3 */
 9274: 	"arg",     "cos",     "cosh",	/*  4 -  6 */
 9275: 	"cot",     "coth",    "csc",	/*  7 -  9 */
 9276: 	"deg",     "det",     "dim",	/* 10 - 12 */
 9277: 	"exp",     "gcd",     "hom",	/* 13 - 15 */
 9278: 	"inf",     "ker",     "lg",	/* 16 - 18 */
 9279: 	"lim",     "liminf",  "limsup",	/* 19 - 21 */
 9280: 	"ln",      "log",     "max",	/* 22 - 24 */
 9281: 	"min",     "Pr",      "sec",	/* 25 - 27 */
 9282: 	"sin",     "sinh",    "sup",	/* 28 - 30 */
 9283: 	"tan",     "tanh",		/* 31 - 32 */
 9284: 	/* --- extra mimetex funcnames --- */
 9285: 	"tr",				/* 33 */
 9286: 	"pmod"				/* 34 */
 9287: 	} ;
 9288: /* -------------------------------------------------------------------------
 9289: set up and rasterize function name in \rm
 9290: -------------------------------------------------------------------------- */
 9291: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
 9292: switch ( mathfunc )			/* check for special processing */
 9293:   {
 9294:   default:				/* no special processing */
 9295:     strcpy(func,"{\\rm~");		/* init string with {\rm~ */
 9296:     strcat(func,funcnames[mathfunc]);	/* concat function name */
 9297:     strcat(func,"}");			/* and add terminating } */
 9298:     break;
 9299:   case 34:				/* \pmod{x} --> (mod x) */
 9300:     /* --- parse for \pmod{arg} argument --- */
 9301:     *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
 9302:     strcpy(func,"{\\({\\rm~mod}");	/* init with {\left({\rm~mod} */
 9303:     strcat(func,"\\hspace2");		/* concat space */
 9304:     strcat(func,funcarg);		/* and \pmodargument */
 9305:     strcat(func,"\\)}");		/* and add terminating \right)} */
 9306:     break;
 9307:   } /* --- end-of-switch(mathfunc) --- */
 9308: if ( (funcsp = rasterize(func,size))	/* rasterize function name */
 9309: ==   NULL ) goto end_of_job;		/* and quit if failed */
 9310: mathfuncsp = funcsp;			/* just return funcsp if no limits */
 9311: if ( !islimits ) goto end_of_job;	/* treat any subscript normally */
 9312: /* -------------------------------------------------------------------------
 9313: Obtain limits, if permitted and if provided, and rasterize them
 9314: -------------------------------------------------------------------------- */
 9315: /* --- parse for subscript limits, and bump expression past it(them) --- */
 9316: *expression = texscripts(*expression,limits,limits,1);
 9317: if ( *limits=='\000') goto end_of_job;	/* no limits, nothing to do, quit */
 9318: /* --- rasterize limits --- */
 9319: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
 9320: ==   NULL ) goto end_of_job;		/* and quit if failed */
 9321: /* -------------------------------------------------------------------------
 9322: construct func atop limits
 9323: -------------------------------------------------------------------------- */
 9324: /* --- construct func atop limits --- */
 9325: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
 9326: ==   NULL ) goto end_of_job;		/* quit if failed */
 9327: /* --- initialize subraster parameters --- */
 9328: mathfuncsp->size = size;		/* propagate font size forward */
 9329: /* -------------------------------------------------------------------------
 9330: return final result to caller
 9331: -------------------------------------------------------------------------- */
 9332: end_of_job:
 9333:   return ( mathfuncsp );
 9334: } /* --- end-of-function rastmathfunc() --- */
 9335: 
 9336: 
 9337: /* ==========================================================================
 9338:  * Function:	rastsqrt ( expression, size, basesp,  arg1, arg2, arg3 )
 9339:  * Purpose:	\sqrt handler, returns a subraster corresponding to
 9340:  *		expression (immediately following \sqrt) at font size
 9341:  * --------------------------------------------------------------------------
 9342:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9343:  *				string immediately following \sqrt to be
 9344:  *				rasterized, and returning ptr immediately
 9345:  *				following last character processed.
 9346:  *		size (I)	int containing 0-7 default font size
 9347:  *		basesp (I)	subraster *  to character (or subexpression)
 9348:  *				immediately preceding \accent
 9349:  *				(unused, but passed for consistency)
 9350:  *		arg1 (I)	int unused
 9351:  *		arg2 (I)	int unused
 9352:  *		arg3 (I)	int unused
 9353:  * --------------------------------------------------------------------------
 9354:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
 9355:  *				or NULL for any parsing error
 9356:  *				(expression ptr unchanged if error occurs)
 9357:  * --------------------------------------------------------------------------
 9358:  * Notes:     o
 9359:  * ======================================================================= */
 9360: /* --- entry point --- */
 9361: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
 9362: 			int arg1, int arg2, int arg3 )
 9363: {
 9364: /* -------------------------------------------------------------------------
 9365: Allocations and Declarations
 9366: -------------------------------------------------------------------------- */
 9367: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
 9368: 	rootarg[MAXSUBXSZ+1];		/* optional \sqrt[rootarg]{...} */
 9369: subraster *rasterize(), *subsp=NULL;	/* rasterize subexpr */
 9370: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
 9371: 	*new_subraster(), *rootsp=NULL;	/* optionally preceded by [rootarg]*/
 9372: int	sqrtheight=0, sqrtwidth=0, surdwidth=0,	/* height,width of sqrt */
 9373: 	rootheight=0, rootwidth=0,	/* height,width of rootarg raster */
 9374: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
 9375: int	rastput();			/* put subexpr in constructed sqrt */
 9376: int	overspace = 2;			/*space between subexpr and overbar*/
 9377: int	delete_subraster();		/* free work areas */
 9378: /* -------------------------------------------------------------------------
 9379: Obtain subexpression to be sqrt-ed, and rasterize it
 9380: -------------------------------------------------------------------------- */
 9381: /* --- first check for optional \sqrt[rootarg]{...} --- */
 9382: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
 9383:   { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
 9384:     if ( *rootarg != '\000' )		/* got rootarg */
 9385:      if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
 9386:      != NULL )				/* rasterized successfully */
 9387:       {	rootheight = (rootsp->image)->height;  /* get height of rootarg */
 9388: 	rootwidth  = (rootsp->image)->width; } /* and its width */
 9389:   } /* --- end-of-if(**expression=='[') --- */
 9390: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
 9391: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
 9392: if ( *subexpr == '\000' )		/* couldn't get subexpression */
 9393:   goto end_of_job;			/* nothing to do, so quit */
 9394: /* --- rasterize subexpression to be accented --- */
 9395: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
 9396: ==   NULL ) goto end_of_job;		/* quit if failed */
 9397: /* -------------------------------------------------------------------------
 9398: determine height and width of sqrt raster to be constructed
 9399: -------------------------------------------------------------------------- */
 9400: /* --- first get height and width of subexpr --- */
 9401: subheight = (subsp->image)->height;	/* height of subexpr */
 9402: subwidth  = (subsp->image)->width;	/* and its width */
 9403: pixsz     = (subsp->image)->pixsz;	/* pixsz remains constant */
 9404: /* --- determine height and width of sqrt to contain subexpr --- */
 9405: sqrtheight = subheight + overspace;	/* subexpr + blank line + overbar */
 9406: surdwidth  = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
 9407: sqrtwidth  = subwidth + surdwidth + 1;	/* total width */
 9408: /* -------------------------------------------------------------------------
 9409: construct sqrt (with room to move in subexpr) and embed subexpr in it
 9410: -------------------------------------------------------------------------- */
 9411: /* --- construct sqrt --- */
 9412: if ( (sqrtsp=accent_subraster(SQRTACCENT,
 9413: (rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
 9414: ==   NULL ) goto end_of_job;		/* quit if failed to build sqrt */
 9415: /* --- embed subexpr in sqrt at lower-right corner--- */
 9416: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
 9417: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
 9418: /* --- "embed" rootarg at upper-left --- */
 9419: if ( rootsp != NULL )			/*have optional \sqrt[rootarg]{...}*/
 9420:   {
 9421:   /* --- allocate full raster to contain sqrtsp and rootsp --- */
 9422:   int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
 9423:       fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
 9424:   subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
 9425:   if ( fullsp != NULL )			/* allocated successfully */
 9426:     { /* --- embed sqrtsp exactly at lower-right corner --- */
 9427:       rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
 9428: 	fullheight-sqrtheight,fullwidth-sqrtwidth,1);
 9429:       /* --- embed rootsp near upper-left, nestled above leading surd --- */
 9430:       rastput(fullsp->image,rootsp->image,
 9431: 	0,max2(0,surdwidth-rootwidth-2-size),0);
 9432:       /* --- replace sqrtsp with fullsp --- */
 9433:       delete_subraster(sqrtsp);		/* free original sqrtsp */
 9434:       sqrtsp = fullsp;			/* and repoint it to fullsp instead*/
 9435:       sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
 9436:   } /* --- end-of-if(rootsp!=NULL) --- */
 9437: /* --- initialize subraster parameters --- */
 9438: sqrtsp->size = size;			/* propagate font size forward */
 9439: /* -------------------------------------------------------------------------
 9440: free unneeded component subrasters and return final result to caller
 9441: -------------------------------------------------------------------------- */
 9442: end_of_job:
 9443:   if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
 9444:   return ( sqrtsp );
 9445: } /* --- end-of-function rastsqrt() --- */
 9446: 
 9447: 
 9448: /* ==========================================================================
 9449:  * Function:	rastaccent (expression,size,basesp,accent,isabove,isscript)
 9450:  * Purpose:	\hat, \vec, \etc handler, returns a subraster corresponding
 9451:  *		to expression (immediately following \accent) at font size
 9452:  * --------------------------------------------------------------------------
 9453:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9454:  *				string immediately following \accent to be
 9455:  *				rasterized, and returning ptr immediately
 9456:  *				following last character processed.
 9457:  *		size (I)	int containing 0-7 default font size
 9458:  *		basesp (I)	subraster *  to character (or subexpression)
 9459:  *				immediately preceding \accent
 9460:  *				(unused, but passed for consistency)
 9461:  *		accent (I)	int containing HATACCENT or VECACCENT, etc,
 9462:  *				between numerator and denominator,
 9463:  *				or false not to draw it (for \over).
 9464:  *		isabove (I)	int containing true if accent is above
 9465:  *				expression to be accented, or false
 9466:  *				if accent is below (e.g., underbrace)
 9467:  *		isscript (I)	int containing true if sub/superscripts
 9468:  *				allowed (for under/overbrace), or 0 if not.
 9469:  * --------------------------------------------------------------------------
 9470:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
 9471:  *				or NULL for any parsing error
 9472:  *				(expression ptr unchanged if error occurs)
 9473:  * --------------------------------------------------------------------------
 9474:  * Notes:     o	Also handles \overbrace{}^{} and \underbrace{}_{} by way
 9475:  *		of isabove and isscript args.
 9476:  * ======================================================================= */
 9477: /* --- entry point --- */
 9478: subraster *rastaccent ( char **expression, int size, subraster *basesp,
 9479: 			int accent, int isabove, int isscript )
 9480: {
 9481: /* -------------------------------------------------------------------------
 9482: Allocations and Declarations
 9483: -------------------------------------------------------------------------- */
 9484: char	*texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
 9485: char	*texscripts(), *script=NULL,	/* \under,overbrace allow scripts */
 9486: 	subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1];	/* parsed scripts */
 9487: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
 9488: subraster *rastack(), *accsubsp=NULL;	/* stack accent, subexpr, script */
 9489: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
 9490: int	accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
 9491: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
 9492: int	delete_subraster();		/*free work areas in case of error*/
 9493: int	vspace = 0;			/*vertical space between accent,sub*/
 9494: /* -------------------------------------------------------------------------
 9495: Obtain subexpression to be accented, and rasterize it
 9496: -------------------------------------------------------------------------- */
 9497: /* --- parse for subexpr to be accented, and bump expression past it --- */
 9498: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
 9499: if ( *subexpr=='\000' )			/* couldn't get subexpression */
 9500:   goto end_of_job;			/* nothing to do, so quit */
 9501: /* --- rasterize subexpression to be accented --- */
 9502: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
 9503: ==   NULL ) goto end_of_job;		/* quit if failed */
 9504: /* -------------------------------------------------------------------------
 9505: determine desired accent width and height
 9506: -------------------------------------------------------------------------- */
 9507: /* --- first get height and width of subexpr --- */
 9508: subheight = (subsp->image)->height;	/* height of subexpr */
 9509: subwidth  = (subsp->image)->width;	/* and its width is overall width */
 9510: pixsz     = (subsp->image)->pixsz;	/* original pixsz remains constant */
 9511: /* --- determine desired width, height of accent --- */
 9512: accwidth = subwidth;			/* same width as subexpr */
 9513: accheight = 4;				/* default for bars */
 9514: switch ( accent )
 9515:   { default: break;			/* default okay */
 9516:   case DOTACCENT: case DDOTACCENT:
 9517:     accheight = (size<4? 3:4);		/* default for dots */
 9518:     break;
 9519:   case VECACCENT:
 9520:     vspace = 1;				/* set 1-pixel vertical space */
 9521:     accdir = isscript;			/* +1=right,-1=left,0=lr; +10for==>*/
 9522:     isscript = 0;			/* >>don't<< signal sub/supscript */
 9523:   case HATACCENT:
 9524:     accheight = 7;			/* default */
 9525:     if ( subwidth < 10 ) accheight = 5;	/* unless small width */
 9526:       else if ( subwidth > 25 ) accheight = 9; /* or large */
 9527:     break;
 9528:   } /* --- end-of-switch(accent) --- */
 9529: accheight = min2(accheight,subheight);	/*never higher than accented subexpr*/
 9530: /* -------------------------------------------------------------------------
 9531: construct accent, and construct subraster with accent over (or under) subexpr
 9532: -------------------------------------------------------------------------- */
 9533: /* --- first construct accent --- */
 9534: if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
 9535: ==   NULL ) goto end_of_job;		/* quit if failed to build accent */
 9536: /* --- now stack accent above (or below) subexpr, and free both args --- */
 9537: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
 9538:            : rastack(accsp,subsp,2,vspace,1,3));      /*accent below subexpr*/
 9539: if ( accsubsp == NULL )			/* failed to stack accent */
 9540:   { delete_subraster(subsp);		/* free unneeded subsp */
 9541:     delete_subraster(accsp);		/* and unneeded accsp */
 9542:     goto end_of_job; }			/* and quit */
 9543: /* -------------------------------------------------------------------------
 9544: look for super/subscript (annotation for over/underbrace)
 9545: -------------------------------------------------------------------------- */
 9546: /* --- first check whether accent permits accompanying annotations --- */
 9547: if ( !isscript ) goto end_of_job;	/* no annotations for this accent */
 9548: /* --- now get scripts if there actually are any --- */
 9549: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
 9550: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
 9551: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
 9552: /* --- rasterize script annotation at size-2 --- */
 9553: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
 9554: ==   NULL ) goto end_of_job;		/* quit if failed */
 9555: /* --- stack annotation above (or below) accent, and free both args --- */
 9556: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
 9557:            : rastack(scrsp,accsubsp,2,0,1,3));       /* accent below base */
 9558: /* -------------------------------------------------------------------------
 9559: return final result to caller
 9560: -------------------------------------------------------------------------- */
 9561: end_of_job:
 9562:   if ( accsubsp != NULL )		/* initialize subraster parameters */
 9563:     accsubsp->size = size;		/* propagate font size forward */
 9564:   return ( accsubsp );
 9565: } /* --- end-of-function rastaccent() --- */
 9566: 
 9567: 
 9568: /* ==========================================================================
 9569:  * Function:	rastfont (expression,size,basesp,ifontnum,arg2,arg3)
 9570:  * Purpose:	\cal{}, \scr{}, \etc handler, returns subraster corresponding
 9571:  *		to char(s) within {}'s rendered at size
 9572:  * --------------------------------------------------------------------------
 9573:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9574:  *				string immediately following \font to be
 9575:  *				rasterized, and returning ptr immediately
 9576:  *				following last character processed.
 9577:  *		size (I)	int containing 0-7 default font size
 9578:  *		basesp (I)	subraster *  to character (or subexpression)
 9579:  *				immediately preceding \accent
 9580:  *				(unused, but passed for consistency)
 9581:  *		ifontnum (I)	int containing 1 for \cal{}, 2 for \scr{}
 9582:  *		arg2 (I)	int unused
 9583:  *		arg3 (I)	int unused
 9584:  * --------------------------------------------------------------------------
 9585:  * Returns:	( subraster * )	ptr to subraster corresponding to chars
 9586:  *				between {}'s, or NULL for any parsing error
 9587:  * --------------------------------------------------------------------------
 9588:  * Notes:     o
 9589:  * ======================================================================= */
 9590: /* --- entry point --- */
 9591: subraster *rastfont ( char **expression, int size, subraster *basesp,
 9592: 			int ifontnum, int arg2, int arg3 )
 9593: {
 9594: /* -------------------------------------------------------------------------
 9595: Allocations and Declarations
 9596: -------------------------------------------------------------------------- */
 9597: char	*texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
 9598: 	subexpr[MAXSUBXSZ+1];		/* turn \cal{AB} into \calA\calB */
 9599: char	*pfchars=fontchars, fchar='\0';	/* run thru fontchars one at a time*/
 9600: char	*name = NULL;			/* fontinfo[ifontnum].name */
 9601: int	family = 0,			/* fontinfo[ifontnum].family */
 9602: 	istext = 0,			/* fontinfo[ifontnum].istext */
 9603: 	class = 0;			/* fontinfo[ifontnum].class */
 9604: subraster *rasterize(), *fontsp=NULL,	/* rasterize chars in font */
 9605: 	*rastflags();			/* or just set flag to switch font */
 9606: int	oldsmashmargin = smashmargin;	/* turn off smash in text mode */
 9607: #if 0
 9608: /* --- fonts recognized by rastfont --- */
 9609: static	int  nfonts = 11;		/* legal font #'s are 1...nfonts */
 9610: static	struct {char *name; int class;}
 9611:   fonts[] =
 9612:     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
 9613: 	{ "\\math",	0 },
 9614: 	{ "\\mathcal",	1 },		/*(1) calligraphic, uppercase */
 9615: 	{ "\\mathscr",	1 },		/*(2) rsfs/script, uppercase */
 9616: 	{ "\\textrm",	-1 },		/*(3) \rm,\text{abc} --> {\rm~abc} */
 9617: 	{ "\\textit",	-1 },		/*(4) \it,\textit{abc}-->{\it~abc} */
 9618: 	{ "\\mathbb",	-1 },		/*(5) \bb,\mathbb{abc}-->{\bb~abc} */
 9619: 	{ "\\mathbf",	-1 },		/*(6) \bf,\mathbf{abc}-->{\bf~abc} */
 9620: 	{ "\\mathrm",   -1 },		/*(7) \mathrm */
 9621: 	{ "\\cyr",      -1 },		/*(8) \cyr */
 9622: 	{ "\\textgreek",-1 },		/*(9) \textgreek */
 9623: 	{ "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
 9624: 	{ "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
 9625: 	{ NULL,		0 }
 9626:     } ; /* --- end-of-fonts[] --- */
 9627: #endif
 9628: /* -------------------------------------------------------------------------
 9629: first get font name and class to determine type of conversion desired
 9630: -------------------------------------------------------------------------- */
 9631: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
 9632: name   = fontinfo[ifontnum].name;	/* font name */
 9633: family = fontinfo[ifontnum].family;	/* font family */
 9634: istext = fontinfo[ifontnum].istext;	/*true in text mode (respect space)*/
 9635: class  = fontinfo[ifontnum].class;	/* font class */
 9636: if ( istext )				/* text (respect blanks) */
 9637:  { mathsmashmargin = smashmargin;	/* needed for \text{if $n-m$ even} */
 9638:    smashmargin = 0; }			/* don't smash internal blanks */
 9639: /* -------------------------------------------------------------------------
 9640: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
 9641: -------------------------------------------------------------------------- */
 9642: if ( 1 || class<0 )			/* not character-by-character */
 9643:  { 
 9644:  /* ---
 9645:  if \font not immediately followed by { then it has no arg, so just set flag
 9646:  ------------------------------------------------------------------------ */
 9647:  if ( *(*expression) != '{' )		/* no \font arg, so just set flag */
 9648:     {
 9649:     if ( msgfp!=NULL && msglevel>=99 )
 9650:      fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
 9651:     fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
 9652:     goto end_of_job;
 9653:     } /* --- end-of-if(*(*expression)!='{') --- */
 9654:  /* ---
 9655:  convert \font{abc} --> {\font~abc}
 9656:  ---------------------------------- */
 9657:  /* --- parse for {fontchars} arg, and bump expression past it --- */
 9658:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
 9659:  if ( msgfp!=NULL && msglevel>=99 )
 9660:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
 9661:  /* --- convert all fontchars at the same time --- */
 9662:  strcpy(subexpr,"{");			/* start off with opening { */
 9663:  strcat(subexpr,name);			/* followed by font name */
 9664:  strcat(subexpr,"~");			/* followed by whitespace */
 9665:  strcat(subexpr,fontchars);		/* followed by all the chars */
 9666:  strcat(subexpr,"}");			/* terminate with closing } */
 9667:  } /* --- end-of-if(class<0) --- */
 9668: else					/* character-by-character */
 9669:  {
 9670:  /* ---
 9671:  convert ABC to \calA\calB\calC
 9672:  ------------------------------ */
 9673:  int	isprevchar=0;			/* true if prev char converted */
 9674:  /* --- parse for {fontchars} arg, and bump expression past it --- */
 9675:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
 9676:  if ( msgfp!=NULL && msglevel>=99 )
 9677:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
 9678:  /* --- convert fontchars one at a time --- */
 9679:  strcpy(subexpr,"{\\rm~");		/* start off with opening {\rm */
 9680:  strcpy(subexpr,"{");			/* nope, just start off with { */
 9681:  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
 9682:   {
 9683:   if ( isthischar(fchar,WHITEMATH) )	/* some whitespace */
 9684:     { if ( 0 || istext )		/* and we're in a text mode font */
 9685: 	strcat(subexpr,"\\;"); }	/* so respect whitespace */
 9686:   else					/* char to be displayed in font */
 9687:     { int exprlen = 0;			/* #chars in subexpr before fchar */
 9688:       int isinclass = 0;		/* set true if fchar in font class */
 9689:       /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
 9690:       switch ( class )			/* check if fchar is in font class */
 9691: 	{ default: break;		/* no chars in unrecognized class */
 9692: 	  case 1: if ( isupper((int)fchar) ) isinclass=1; break;
 9693: 	  case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
 9694: 	  case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
 9695: 	  case 4: if ( islower((int)fchar) ) isinclass=1; break;
 9696: 	  case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
 9697: 	  case 9: isinclass=1; break; }
 9698:       if ( isinclass )			/* convert current char to \font */
 9699: 	{ strcat(subexpr,name);		/* by prefixing it with font name */
 9700: 	  isprevchar = 1; }		/* and set flag to signal separator*/
 9701:       else				/* current char not in \font */
 9702: 	{ if ( isprevchar )		/* extra separator only after \font*/
 9703: 	   if ( isalpha(fchar) )	/* separator only before alpha */
 9704: 	    strcat(subexpr,"~");	/* need separator after \font */
 9705: 	  isprevchar = 0; }		/* reset flag for next char */
 9706:       exprlen = strlen(subexpr);	/* #chars so far */
 9707:       subexpr[exprlen] = fchar;		/*fchar immediately after \fontname*/
 9708:       subexpr[exprlen+1] = '\000'; }	/* replace terminating '\0' */
 9709:   } /* --- end-of-for(pfchars) --- */
 9710:  strcat(subexpr,"}");			/* add closing } */
 9711:  } /* --- end-of-if/else(class<0) --- */
 9712: /* -------------------------------------------------------------------------
 9713: rasterize subexpression containing chars to be rendered at font
 9714: -------------------------------------------------------------------------- */
 9715: if ( msgfp!=NULL && msglevel>=99 )
 9716:   fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
 9717: if ( (fontsp = rasterize(subexpr,size))	/* rasterize chars in font */
 9718: ==   NULL ) goto end_of_job;		/* and quit if failed */
 9719: /* -------------------------------------------------------------------------
 9720: back to caller with chars rendered in font
 9721: -------------------------------------------------------------------------- */
 9722: end_of_job:
 9723:   smashmargin = oldsmashmargin;		/* restore smash */
 9724:   mathsmashmargin = SMASHMARGIN;	/* this one probably not necessary */
 9725:   if ( istext && fontsp!=NULL )		/* raster contains text mode font */
 9726:     fontsp->type = blanksignal;		/* signal nosmash */
 9727:   return ( fontsp );			/* chars rendered in font */
 9728: } /* --- end-of-function rastfont() --- */
 9729: 
 9730: 
 9731: /* ==========================================================================
 9732:  * Function:	rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
 9733:  * Purpose:	\begin{}...\end{}  handler, returns a subraster corresponding
 9734:  *		to array expression within environment, i.e., rewrites
 9735:  *		\begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
 9736:  * --------------------------------------------------------------------------
 9737:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9738:  *				string immediately following \begin to be
 9739:  *				rasterized, and returning ptr immediately
 9740:  *				following last character processed.
 9741:  *		size (I)	int containing 0-7 default font size
 9742:  *		basesp (I)	subraster *  to character (or subexpression)
 9743:  *				immediately preceding \begin
 9744:  *				(unused, but passed for consistency)
 9745:  *		arg1 (I)	int unused
 9746:  *		arg2 (I)	int unused
 9747:  *		arg3 (I)	int unused
 9748:  * --------------------------------------------------------------------------
 9749:  * Returns:	( subraster * )	ptr to subraster corresponding to array
 9750:  *				expression, or NULL for any parsing error
 9751:  * --------------------------------------------------------------------------
 9752:  * Notes:     o
 9753:  * ======================================================================= */
 9754: /* --- entry point --- */
 9755: subraster *rastbegin ( char **expression, int size, subraster *basesp,
 9756: 			int arg1, int arg2, int arg3 )
 9757: {
 9758: /* -------------------------------------------------------------------------
 9759: Allocations and Declarations
 9760: -------------------------------------------------------------------------- */
 9761: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
 9762: 	*exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
 9763: char	*begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
 9764: int	strreplace();			/* replace substring in string */
 9765: char	*strchange();			/*\begin...\end --> {\begin...\end}*/
 9766: char	*delims = (char *)NULL;		/* mdelims[ienviron] */
 9767: subraster *rasterize(), *sp=NULL;	/* rasterize environment */
 9768: int	ienviron = 0;			/* environs[] index */
 9769: int	nbegins = 0;			/* #\begins nested beneath this one*/
 9770: int	envlen=0, sublen=0;		/* #chars in environ, subexpr */
 9771: static	int blevel = 0;			/* \begin...\end nesting level */
 9772: static	char *mdelims[] = { NULL, NULL, NULL, NULL,
 9773: 	"()","[]","{}","||","==",	/* for pbBvVmatrix */
 9774: 	NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
 9775: 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 9776: static	char *environs[] = {		/* types of environments we process*/
 9777: 	"eqnarray",			/* 0 eqnarray environment */
 9778: 	"array",			/* 1 array environment */
 9779: 	"matrix",			/* 2 array environment */
 9780: 	"tabular",			/* 3 array environment */
 9781: 	"pmatrix",			/* 4 ( ) */
 9782: 	"bmatrix",			/* 5 [ ] */
 9783: 	"Bmatrix",			/* 6 { } */
 9784: 	"vmatrix",			/* 7 | | */
 9785: 	"Vmatrix",			/* 8 || || */
 9786: 	"gather",			/* 9 gather environment */
 9787: 	"align",			/* 10 align environment */
 9788: 	"verbatim",			/* 11 verbatim environment */
 9789: 	"picture",			/* 12 picture environment */
 9790: 	"cases",			/* 13 cases environment */
 9791: 	"equation",			/* 14 for \begin{equation} */
 9792: 	NULL };				/* trailer */
 9793: /* -------------------------------------------------------------------------
 9794: determine type of environment we're beginning
 9795: -------------------------------------------------------------------------- */
 9796: /* --- first bump nesting level --- */
 9797: blevel++;				/* count \begin...\begin...'s */
 9798: /* --- \begin must be followed by {type_of_environment} --- */
 9799: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
 9800: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
 9801: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
 9802:   {strsqueeze(delims,1);}		/* treat it as environment */
 9803: /* --- look up environment in our table --- */
 9804: for ( ienviron=0; ;ienviron++ )		/* search table till NULL */
 9805:   if ( environs[ienviron] == NULL )	/* found NULL before match */
 9806:     goto end_of_job;			/* so quit */
 9807:   else					/* see if we have an exact match */
 9808:     if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
 9809:       break;				/* leave loop with ienviron index */
 9810: /* --- accumulate any additional params for this environment --- */
 9811: *subexpr = '\000';			/* reset subexpr to empty string */
 9812: delims = mdelims[ienviron];		/* mdelims[] string for ienviron */
 9813: if ( delims != NULL )			/* add appropriate opening delim */
 9814:   { strcpy(subexpr,"\\");		/* start with \ for (,[,{,|,= */
 9815:     strcat(subexpr,delims);		/* then add opening delim */
 9816:     subexpr[2] = '\000'; }		/* remove extraneous closing delim */
 9817: switch ( ienviron )
 9818:   {
 9819:   default: goto end_of_job;		/* environ not implemented yet */
 9820:   case 0:				/* \begin{eqnarray} */
 9821:     strcpy(subexpr,"\\array{rcl$");	/* set default rcl for eqnarray */
 9822:     break;
 9823:   case 1:  case 2:  case 3:		/* \begin{array} followed by {lcr} */
 9824:     strcpy(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
 9825:     skipwhite(exprptr);			/* bump to next non-white char */
 9826:     if ( *exprptr == '{' )		/* assume we have {lcr} argument */
 9827:       {	exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
 9828: 	if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
 9829: 	strcat(subexpr,"$"); }		/* add terminating $ to lcr */
 9830:     break;
 9831:   case 4:  case 5:  case 6:		/* \begin{pmatrix} or b,B,v,Vmatrix */
 9832:   case 7:  case 8:
 9833:     strcat(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
 9834:     break;
 9835:   case 9:				/* gather */
 9836:     strcat(subexpr,"\\array{c$");	/* center equations */
 9837:     break;
 9838:   case 10:				/* align */
 9839:     strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
 9840:     break;
 9841:   case 11:				/* verbatim */
 9842:     strcat(subexpr,"{\\rm ");		/* {\rm ...} */
 9843:     /*strcat(subexpr,"\\\\{\\rm ");*/	/* \\{\rm } doesn't work in context */
 9844:     break;
 9845:   case 12:				/* picture */
 9846:     strcat(subexpr,"\\picture");	/* picture environment */
 9847:     skipwhite(exprptr);			/* bump to next non-white char */
 9848:     if ( *exprptr == '(' )		/*assume we have (width,height) arg*/
 9849:       {	exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
 9850: 	if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
 9851:     strcat(subexpr,"{");		/* opening {  after (width,height) */
 9852:     break;
 9853:   case 13:				/* cases */
 9854:     strcat(subexpr,"\\array{ll$");	/* a&b \\ c&d etc */
 9855:     break;
 9856:   case 14:				/* \begin{equation} */
 9857:     strcat(subexpr,"{");		/* just enclose expression in {}'s */
 9858:     break;
 9859:   } /* --- end-of-switch(ienviron) --- */
 9860: /* -------------------------------------------------------------------------
 9861: locate matching \end{...}
 9862: -------------------------------------------------------------------------- */
 9863: /* --- first \end following \begin --- */
 9864: if ( (endptr=strstr(exprptr,endtoken))	/* find 1st \end following \begin */
 9865: ==   NULL ) goto end_of_job;		/* and quit if no \end found */
 9866: /* --- find matching endptr by pushing past any nested \begin's --- */
 9867: begptr = exprptr;			/* start after first \begin{...} */
 9868: while ( 1 )				/*break when we find matching \end*/
 9869:   {
 9870:   /* --- first, set ptr to closing } terminating current \end{...} --- */
 9871:   if ( (braceptr=strchr(endptr+1,'}'))	/* find 1st } following \end{ */
 9872:   ==   NULL ) goto end_of_job;		/* and quit if no } found */
 9873:   /* -- locate next nested \begin --- */
 9874:   if ( (begptr=strstr(begptr,begtoken))	/* find next \begin{...} */
 9875:   ==   NULL ) break;			/*no more, so we have matching \end*/
 9876:   begptr += strlen(begtoken);		/* push ptr past token */
 9877:   if ( begptr >= endptr ) break;	/* past endptr, so not nested */
 9878:   /* --- have nested \begin, so push forward to next \end --- */
 9879:   nbegins++;				/* count another nested \begin */
 9880:   if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
 9881:   ==   NULL ) goto end_of_job;		/* and quit if none found */
 9882:   } /* --- end-of-while(1) --- */
 9883: /* --- push expression past closing } of \end{} --- */
 9884: *expression = braceptr+1;		/* resume processing after } */
 9885: /* -------------------------------------------------------------------------
 9886: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
 9887: -------------------------------------------------------------------------- */
 9888: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
 9889: sublen = strlen(subexpr);		/* #chars in "preamble" */
 9890: envlen = (int)(endptr-exprptr);		/* #chars between \begin{}{}...\end */
 9891: memcpy(subexpr+sublen,exprptr,envlen);	/*concatanate environ after subexpr*/
 9892: subexpr[sublen+envlen] = '\000';	/* and null-terminate */
 9893: if ( 2 > 1 )				/* always... */
 9894:   strcat(subexpr,"}");			/* ...followed by terminating } */
 9895: /* --- add terminating \right), etc, if necessary --- */
 9896: if ( delims != (char *)NULL )		/* need closing delim */
 9897:  { strcat(subexpr,"\\");		/* start with \ for ),],},|,= */
 9898:    strcat(subexpr,delims+1); }		/* add appropriate closing delim */
 9899: /* -------------------------------------------------------------------------
 9900: change nested \begin...\end to {\begin...\end} so \array{} can handle them
 9901: -------------------------------------------------------------------------- */
 9902: if ( nbegins > 0 )			/* have nested begins */
 9903:  if ( blevel < 2 )			/* only need to do this once */
 9904:   {
 9905:   begptr = subexpr;			/* start at beginning of subexpr */
 9906:   while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
 9907:     { strchange(0,begptr,"{");		/* \begin --> {\begin */
 9908:       begptr += strlen(begtoken); }	/* continue past {\begin */
 9909:   endptr = subexpr;			/* start at beginning of subexpr */
 9910:   while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
 9911:     if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
 9912:     ==   NULL ) goto end_of_job;	/* and quit if no } found */
 9913:     else				/* found terminating } */
 9914:      { strchange(0,braceptr,"}");	/* \end{...} --> \end{...}} */
 9915:        endptr = braceptr+1; }		/* continue past \end{...} */
 9916:   } /* --- end-of-if(nbegins>0) --- */
 9917: /* -------------------------------------------------------------------------
 9918: post process as necessary
 9919: -------------------------------------------------------------------------- */
 9920: switch ( ienviron )
 9921:   {
 9922:   default: break;			/* no post-processing required */
 9923:   case 10:				/* align */
 9924:     strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
 9925:     strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
 9926:     strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
 9927:     strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
 9928:     strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
 9929:     strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
 9930:     strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
 9931:     if ( nbegins < 1 )			/* don't modify nested arrays */
 9932:       strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
 9933:     strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
 9934:     strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
 9935:     strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
 9936:     strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
 9937:     strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
 9938:     break;
 9939:   case 11:				/* verbatim */
 9940:     strreplace(subexpr,"\n","\\\\",0);	/* xlate \n newline to latex \\ */
 9941:     /*strcat(subexpr,"\\\\");*/		/* add final latex \\ newline */
 9942:     break;
 9943:   case 12:				/* picture */
 9944:     strreplace(subexpr,"\\put "," ",0);	/*remove \put's (not really needed)*/
 9945:     strreplace(subexpr,"\\put(","(",0);	/*remove \put's (not really needed)*/
 9946:     strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
 9947:     break;
 9948:   } /* --- end-of-switch(ienviron) --- */
 9949: /* -------------------------------------------------------------------------
 9950: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
 9951: -------------------------------------------------------------------------- */
 9952: /* --- debugging output --- */
 9953: if ( msgfp!=NULL && msglevel>=99 )
 9954:   fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
 9955: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
 9956: sp = rasterize(subexpr,size);		/* rasterize subexpr */
 9957: end_of_job:
 9958:   blevel--;				/* decrement \begin nesting level */
 9959:   return ( sp );			/* back to caller with sp or NULL */
 9960: } /* --- end-of-function rastbegin() --- */
 9961: 
 9962: 
 9963: /* ==========================================================================
 9964:  * Function:	rastarray ( expression, size, basesp, arg1, arg2, arg3 )
 9965:  * Purpose:	\array handler, returns a subraster corresponding to array
 9966:  *		expression (immediately following \array) at font size
 9967:  * --------------------------------------------------------------------------
 9968:  * Arguments:	expression (I/O) char **  to first char of null-terminated
 9969:  *				string immediately following \array to be
 9970:  *				rasterized, and returning ptr immediately
 9971:  *				following last character processed.
 9972:  *		size (I)	int containing 0-7 default font size
 9973:  *		basesp (I)	subraster *  to character (or subexpression)
 9974:  *				immediately preceding \array
 9975:  *				(unused, but passed for consistency)
 9976:  *		arg1 (I)	int unused
 9977:  *		arg2 (I)	int unused
 9978:  *		arg3 (I)	int unused
 9979:  * --------------------------------------------------------------------------
 9980:  * Returns:	( subraster * )	ptr to subraster corresponding to array
 9981:  *				expression, or NULL for any parsing error
 9982:  * --------------------------------------------------------------------------
 9983:  * Notes:     o	Summary of syntax...
 9984:  *			\array{3,lcrBC$a&b&c\\d&e&f\\etc}
 9985:  *	      o	The 3,lcrBC$ part is an optional "preamble".  The lcr means
 9986:  *		what you think, i.e., "horizontal" left,center,right
 9987:  *		justification down corresponding column.  The new BC means
 9988:  *		"vertical" baseline,center justification across corresponding
 9989:  *		row.  The leading 3 specifies the font size 0-4 to be used.
 9990:  *		You may also specify +1,-1,+2,-2, etc, which is used as an
 9991:  *		increment to the current font size, e.g., -1,lcr$ uses
 9992:  *		one font size smaller than current.  Without a leading
 9993:  *		+ or -, the font size is "absolute".
 9994:  *	      o	The preamble can also be just lcrBC$ without a leading
 9995:  *		size-part, or just 3$ without a trailing lcrBC-part.
 9996:  *		The default size is whatever is current, and the
 9997:  *		default justification is c(entered) and B(aseline).
 9998:  * ======================================================================= */
 9999: /* --- entry point --- */
10000: subraster *rastarray ( char **expression, int size, subraster *basesp,
10001: 			int arg1, int arg2, int arg3 )
10002: {
10003: /* -------------------------------------------------------------------------
10004: Allocations and Declarations
10005: -------------------------------------------------------------------------- */
10006: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
10007: 	 subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
10008: 	 token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */
10009: 	*preamble(),   *preptr=token;	/*process optional size,lcr preamble*/
10010: char	*coldelim="&", *rowdelim="\\";	/* need escaped rowdelim */
10011: int	maxarraysz = 63;		/* max #rows, cols */
10012: 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 */
10013: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10014: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10015: 	  hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
10016: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10017: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10018: 	  vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
10019: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10020: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10021:        colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10022: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10023: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10024:       rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10025: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10026: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10027:      fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10028: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10029: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10030:      fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10031: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10032: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10033:       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
10034: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10035: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10036:       vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
10037: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10038: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10039:       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10040: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10041: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10042: static int /* --- propagate global values across arrays --- */
10043:        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 */
10044: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10045: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10046:       gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10047: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10048: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10049:      growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10050: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10051: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10052:     gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10053: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10054: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10055:     gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10056: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10057: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10058:      growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10059: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10060: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10061: int	rowglobal=0, colglobal=0,	/* true to set global values */
10062: 	rowpropagate=0, colpropagate=0;	/* true if propagating values */
10063: int	irow,nrows=0, icol,ncols[65],	/*#rows in array, #cols in each row*/
10064: 	maxcols=0;			/* max# cols in any single row */
10065: int	itoken, ntokens=0,		/* index, total #tokens in array */
10066: 	subtoklen=0,			/* strlen of {...} subtoken */
10067: 	istokwhite=1,			/* true if token all whitespace */
10068: 	nnonwhite=0;			/* #non-white tokens */
10069: int	isescape=0,wasescape=0,		/* current,prev chars escape? */
10070: 	ischarescaped=0,		/* is current char escaped? */
10071: 	nescapes=0;			/* #consecutive escapes */
10072: subraster *rasterize(), *toksp[1025],	/* rasterize tokens */
10073: 	*new_subraster(), *arraysp=NULL; /* subraster for entire array */
10074: raster	*arrayrp=NULL;			/* raster for entire array */
10075: int	delete_subraster();		/* free toksp[] workspace at eoj */
10076: int	rowspace=2, colspace=4,		/* blank space between rows, cols */
10077: 	hspace=1, vspace=1;		/*space to accommodate hline,vline*/
10078: int	width=0, height=0,		/* width,height of array */
10079: 	leftcol=0, toprow=0;		/*upper-left corner for cell in it*/
10080: int	rastput();			/* embed tokens/cells in array */
10081: int	rule_raster();			/* draw hlines and vlines in array */
10082: char	*hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
10083: char	*texchar(), hltoken[1025];	/* extract \hline from token */
10084: int	ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
10085: int	isnewrow=1;			/* true for new row */
10086: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
10087: int	evalterm(), evalue=0;		/* evaluate [arg], {arg} */
10088: static	int mydaemonlevel = 0;		/* check against global daemonlevel*/
10089: /* -------------------------------------------------------------------------
10090: Macros to determine extra raster space required for vline/hline
10091: -------------------------------------------------------------------------- */
10092: #define	vlinespace(icol) \
10093: 	( vline[icol] == 0?  0 :	/* no vline so no space needed */   \
10094: 	  ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
10095: #define	hlinespace(irow) \
10096: 	( hline[irow] == 0?  0 :	/* no hline so no space needed */   \
10097: 	  ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
10098: /* -------------------------------------------------------------------------
10099: Obtain array subexpression
10100: -------------------------------------------------------------------------- */
10101: /* --- parse for array subexpression, and bump expression past it --- */
10102: subexpr[1] = *subexpr = ' ';		/* set two leading blanks */
10103: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
10104: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display array */
10105:   fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
10106: if ( *(subexpr+2)=='\000' )		/* couldn't get subexpression */
10107:   goto end_of_job;			/* nothing to do, so quit */
10108: /* -------------------------------------------------------------------------
10109: reset static arrays if main re-entered as daemon (or dll)
10110: -------------------------------------------------------------------------- */
10111: if ( mydaemonlevel != daemonlevel ) {	/* main re-entered */
10112:   for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
10113:     gjustify[icol]    = gcolwidth[icol]   = growheight[icol] =
10114:     gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
10115:   mydaemonlevel = daemonlevel; }	/* update mydaemonlevel */
10116: /* -------------------------------------------------------------------------
10117: process optional size,lcr preamble if present
10118: -------------------------------------------------------------------------- */
10119: /* --- reset size, get lcr's, and push exprptr past preamble --- */
10120: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
10121: /* --- init with global values --- */
10122: for(icol=0; icol<=maxarraysz; icol++) {	/* propagate global values... */
10123:   justify[icol] = gjustify[icol];	/* -1,0,+1 = l,c,r */
10124:   colwidth[icol] = gcolwidth[icol];	/* column width */
10125:   rowheight[icol] = growheight[icol];	/* row height */
10126:   fixcolsize[icol] = gfixcolsize[icol];	/* 1=fixed col width */
10127:   fixrowsize[icol] = gfixrowsize[icol];	/* 1=fixed row height */
10128:   rowcenter[icol] = growcenter[icol]; }	/* true = vcenter row */
10129: /* --- process lcr's, etc in preamble --- */
10130: itoken = 0;				/* debugging flag */
10131: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display preamble */
10132:  if ( *preptr != '\000' )		/* if we have one */
10133:   fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
10134:   preptr);
10135: irow = icol = 0;			/* init lcr counts */
10136: while (  *preptr != '\000' )		/* check preamble text for lcr */
10137:   {
10138:   char	prepchar = *preptr;		/* current preamble character */
10139:   int	prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
10140:   if ( irow<maxarraysz && icol<maxarraysz )
10141:    switch ( /*tolower*/(prepchar) )
10142:     {  default: break;			/* just flush unrecognized chars */
10143:       case 'l': justify[icol] = (-1);		/*left-justify this column*/
10144: 		if (colglobal) gjustify[irow] = justify[irow]; break;
10145:       case 'c': justify[icol] = (0);		/* center this column */
10146: 		if (colglobal) gjustify[irow] = justify[irow]; break;
10147:       case 'r': justify[icol] = (+1);		/* right-justify this col */
10148: 		if (colglobal) gjustify[irow] = justify[irow]; break;
10149:       case '|': vline[icol] += 1;   break;	/* solid vline left of col */
10150:       case '.': vline[icol] = (-1); break;	/*dashed vline left of col */
10151:       case 'b': prepchar='B'; prepcase=2;	/* alias for B */
10152:       case 'B': break;				/* baseline-justify row */
10153:       case 'v': prepchar='C'; prepcase=2;	/* alias for C */
10154:       case 'C': rowcenter[irow] = 1;		/* vertically center row */
10155: 		if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
10156:       case 'g': colglobal=1; prepcase=0; break;	/* set global col values */
10157:       case 'G': rowglobal=1; prepcase=0; break;	/* set global row values */
10158:       case '#': colglobal=rowglobal=1; break; }	/* set global col,row vals */
10159:   if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10160:     fprintf(msgfp," %c[%d]",prepchar,
10161:     (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
10162:   preptr++;				/* check next char for lcr */
10163:   itoken++;				/* #lcr's processed (debugging only)*/
10164:   /* --- check for number or +number specifying colwidth or rowheight --- */
10165:   if ( prepcase != 0 )			/* only check upper,lowercase */
10166:    {
10167:    int	ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
10168:    if ( ispropagate ) {			/* set row or col propagation */
10169:      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
10170:      else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
10171:    if ( !colpropagate && prepcase == 1 )
10172:       {	colwidth[icol] = 0;		/* reset colwidth */
10173: 	fixcolsize[icol] = 0; }		/* reset width flag */
10174:    if ( !rowpropagate && prepcase == 2 )
10175:       {	rowheight[irow] = 0;		/* reset row height */
10176: 	fixrowsize[irow] = 0; }		/* reset height flag */
10177:    if ( ispropagate ) preptr++;		/* bump past leading + */
10178:    if ( isdigit(*preptr) )		/* digit follows character */
10179:      { char *endptr = NULL;		/* preptr set to 1st char after num*/
10180:        int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
10181:        char *whchars="?wh";		/* debugging width/height labels */
10182:        preptr = endptr;			/* skip over all digits */
10183:        if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
10184: 	int index;			/* icol,irow...maxarraysz index */
10185: 	if ( prepcase == 1 )		/* lowercase signifies colwidth */
10186: 	 for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
10187: 	  colwidth[index] = size;	/* set colwidth to fixed size */
10188: 	  fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
10189: 	  justify[index] = justify[icol]; /* and propagate justification */
10190: 	  if ( colglobal ) {		/* set global values */
10191: 	    gcolwidth[index] = colwidth[index]; /* set global col width */
10192: 	    gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
10193: 	    gjustify[index] = justify[icol]; } /* set global col justify */
10194: 	  if ( !ispropagate ) break; }	/* don't propagate */
10195: 	else				/* uppercase signifies rowheight */
10196: 	 for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
10197: 	  rowheight[index] = size;	/* set rowheight to size */
10198: 	  fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
10199: 	  rowcenter[index] = rowcenter[irow]; /* and propagate row center */
10200: 	  if ( rowglobal ) {		/* set global values */
10201: 	    growheight[index] = rowheight[index]; /* set global row height */
10202: 	    gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
10203: 	    growcenter[index] = rowcenter[irow]; } /*set global row center*/
10204: 	  if ( !ispropagate ) break; }	/* don't propagate */
10205:         } /* --- end-of-if(size>=3&&size<=500) --- */
10206:        if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10207: 	 fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
10208: 	 (prepcase==1?colwidth[icol]:rowheight[irow]),
10209: 	 (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
10210:      } /* --- end-of-if(isdigit()) --- */
10211:    } /* --- end-of-if(prepcase!=0) --- */
10212:   if ( prepcase == 1 ) icol++;		/* bump col if lowercase lcr */
10213:     else if ( prepcase == 2 ) irow++;	/* bump row if uppercase BC */
10214:   } /* --- end-of-while(*preptr!='\000') --- */
10215: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, emit final newline */
10216:  if ( itoken > 0 )			/* if we have preamble */
10217:   fprintf(msgfp,"\n");
10218: /* -------------------------------------------------------------------------
10219: tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
10220: -------------------------------------------------------------------------- */
10221: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
10222: nrows = 0;				/* start with top row */
10223: ncols[nrows] = 0;			/* no tokens/cols in top row yet */
10224: while ( 1 )				/* scan chars till end */
10225:   {
10226:   /* --- local control flags --- */
10227:   int	iseox = (*exprptr == '\000'),	/* null signals end-of-expression */
10228: 	iseor = iseox,			/* \\ or eox signals end-of-row */
10229: 	iseoc = iseor;			/* & or eor signals end-of-col */
10230:   /* --- check for escapes --- */
10231:   isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
10232:   wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
10233:   nescapes = (wasescape?nescapes+1:0);	/* # preceding consecutive escapes */
10234:   ischarescaped = (nescapes%2==0?0:1);	/* is current char escaped? */
10235:   /* -----------------------------------------------------------------------
10236:   check for {...} subexpression starting from where we are now
10237:   ------------------------------------------------------------------------ */
10238:   if ( *exprptr == '{'			/* start of {...} subexpression */
10239:   &&   !ischarescaped )			/* if not escaped \{ */
10240:     {
10241:     subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
10242:     subtoklen = strlen(subtok);		/* #chars in {...} */
10243:     memcpy(tokptr,exprptr,subtoklen);	/* copy {...} to accumulated token */
10244:     tokptr  += subtoklen;		/* bump tokptr to end of token */
10245:     exprptr += subtoklen;		/* and bump exprptr past {...} */
10246:     istokwhite = 0;			/* signal non-empty token */
10247:     continue;				/* continue with char after {...} */
10248:     } /* --- end-of-if(*exprptr=='{') --- */
10249:   /* -----------------------------------------------------------------------
10250:   check for end-of-row(\\) and/or end-of-col(&)
10251:   ------------------------------------------------------------------------ */
10252:   /* --- check for (escaped) end-of-row delimiter --- */
10253:   if ( isescape && !ischarescaped )	/* current char is escaped */
10254:     if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
10255:     ||   *(exprptr+1) == '\000' )	/* or a pathological null */
10256:       {	iseor = 1;			/* so set end-of-row flag */
10257: 	wasescape=isescape=nescapes = 0; } /* reset flags for new row */
10258:   /* --- check for end-of-col delimiter --- */
10259:   if (iseor				/* end-of-row signals end-of-col */
10260:   ||  (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
10261:       iseoc = 1;			/* so set end-of-col flag */
10262:   /* -----------------------------------------------------------------------
10263:   rasterize completed token
10264:   ------------------------------------------------------------------------ */
10265:   if ( iseoc )				/* we have a completed token */
10266:     {
10267:     *tokptr = '\000';			/* first, null-terminate token */
10268:     /* --- check first token in row for [len] and/or \hline or \hdash --- */
10269:     ishonly = 0;			/*init for token not only an \hline*/
10270:     if ( ncols[nrows] == 0 )		/*\hline must be first token in row*/
10271:       {
10272:       tokptr=token; skipwhite(tokptr);	/* skip whitespace after // */
10273:       /* --- first check for optional [len] --- */
10274:       if ( *tokptr == '[' ) {		/* have [len] if leading char is [ */
10275:         /* ---parse [len] and bump tokptr past it, interpret as double--- */
10276:         char lenexpr[128];  int len;	/* chars between [...] as int */
10277:         tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
10278:         if ( *lenexpr != '\000' ) {	/* got [len] expression */
10279: 	  evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
10280:           len = iround(unitlength*((double)evalue)); /* len in pixels */
10281:           if ( len>=(-63) && len<=255 ) { /* sanity check */
10282:             vrowspace[nrows] = len;	/* extra vspace before this row */
10283: 	    strsqueezep(token,tokptr);	/* flush [len] from token */
10284:             tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
10285:         } /* --- end-of-if(*tokptr=='[') --- */
10286:       /* --- now check for \hline or \hdash --- */
10287:       tokptr = texchar(tokptr,hltoken);	/* extract first char from token */
10288:       hltoklen = strlen(hltoken);	/* length of first char */
10289:       if ( hltoklen >= minhltoklen ) {	/*token must be at least \hl or \hd*/
10290: 	if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
10291: 	   hline[nrows] += 1;		/* bump \hline count for row */
10292: 	else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
10293: 	   hline[nrows] = (-1); }	/* set \hdash flag for row */
10294:       if ( hline[nrows] != 0 )		/* \hline or \hdash prefixes token */
10295: 	{ skipwhite(tokptr);		/* flush whitespace after \hline */
10296: 	  if ( *tokptr == '\000'	/* end-of-expression after \hline */
10297: 	  ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
10298: 	    { istokwhite = 1;		/* so token contains \hline only */
10299: 	      if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
10300: 	  else				/* token contains more than \hline */
10301: 	    {strsqueezep(token,tokptr);} } /* so flush \hline */
10302:       } /* --- end-of-if(ncols[nrows]==0) --- */
10303:     /* --- rasterize completed token --- */
10304:     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
10305:       rasterize(token,size));		/* rasterize non-empty token */
10306:     if ( toksp[ntokens] != NULL )	/* have a rasterized token */
10307:       nnonwhite++;			/* bump rasterized token count */
10308:     /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
10309:     if ( toksp[ntokens] != NULL )	/* we have a rasterized token */
10310:       {
10311:       /* --- update max token "height" in current row, and baseline --- */
10312:       int twidth = ((toksp[ntokens])->image)->width,  /* width of token */
10313: 	theight = ((toksp[ntokens])->image)->height, /* height of token */
10314: 	tbaseln =  (toksp[ntokens])->baseline, /* baseline of token */
10315: 	rheight = rowheight[nrows],	/* current max height for row */
10316: 	rbaseln = rowbaseln[nrows];	/* current baseline for max height */
10317:       if ( 0 || fixrowsize[nrows]==0 )	/* rowheight not fixed */
10318:        rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
10319: 	max2(rbaseln+1,tbaseln+1)	/* max height above baseline */
10320: 	+ max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
10321:       rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
10322:       /* --- update max token width in current column --- */
10323:       icol = ncols[nrows];		/* current column index */
10324:       if ( 0 || fixcolsize[icol]==0 )	/* colwidth not fixed */
10325:        colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
10326:       } /* --- end-of-if(toksp[]!=NULL) --- */
10327:     /* --- bump counters --- */
10328:     if ( !ishonly )			/* don't count only an \hline */
10329:       if ( ncols[nrows] < maxarraysz )	/* don't overflow arrays */
10330: 	{ ntokens++;			/* bump total token count */
10331: 	  ncols[nrows] += 1; }		/* and bump #cols in current row */
10332:     /* --- get ready for next token --- */
10333:     tokptr = token;			/* reset ptr for next token */
10334:     istokwhite = 1;			/* next token starts all white */
10335:     } /* --- end-of-if(iseoc) --- */
10336:   /* -----------------------------------------------------------------------
10337:   bump row as necessary
10338:   ------------------------------------------------------------------------ */
10339:   if ( iseor )				/* we have a completed row */
10340:     {
10341:     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
10342:     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
10343:       if ( nrows < maxarraysz )		/* don't overflow arrays */
10344:         nrows++;			/* bump row count */
10345:     ncols[nrows] = 0;			/* no cols in this row yet */
10346:     if ( !iseox )			/* don't have a null yet */
10347:       {	exprptr++;			/* bump past extra \ in \\ delim */
10348: 	iseox = (*exprptr == '\000'); }	/* recheck for pathological \null */
10349:     isnewrow = 1;			/* signal start of new row */
10350:     } /* --- end-of-if(iseor) --- */
10351:   else
10352:     isnewrow = 0;			/* no longer first col of new row */
10353:   /* -----------------------------------------------------------------------
10354:   quit when done, or accumulate char in token and proceed to next char
10355:   ------------------------------------------------------------------------ */
10356:   /* --- quit when done --- */
10357:   if ( iseox ) break;			/* null terminator signalled done */
10358:   /* --- accumulate chars in token --- */
10359:   if ( !iseoc )				/* don't accumulate delimiters */
10360:     { *tokptr++ = *exprptr;		/* accumulate non-delim char */
10361:       if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
10362: 	istokwhite = 0; }		/* so reset flag to rasterize it */
10363:   /* --- ready for next char --- */
10364:   exprptr++;				/* bump ptr */
10365:   } /* --- end-of-while(*exprptr!='\000') --- */
10366: /* --- make sure we got something to do --- */
10367: if ( nnonwhite < 1 )			/* completely empty array */
10368:   goto end_of_job;			/* NULL back to caller */
10369: /* -------------------------------------------------------------------------
10370: determine dimensions of array raster and allocate it
10371: -------------------------------------------------------------------------- */
10372: /* --- adjust colspace --- */
10373: colspace = 2 + 2*size;			/* temp kludge */
10374: /* --- reset propagated sizes at boundaries of array --- */
10375: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
10376: /* --- determine width of array raster --- */
10377: width = colspace*(maxcols-1);		/* empty space between cols */
10378: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10379:   fprintf(msgfp,"rastarray> %d cols,  widths: ",maxcols);
10380: for ( icol=0; icol<=maxcols; icol++ )	/* and for each col */
10381:   { width += colwidth[icol];		/*width of this col (0 for maxcols)*/
10382:     width += vlinespace(icol);		/*plus space for vline, if present*/
10383:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10384:      fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
10385: /* --- determine height of array raster --- */
10386: height = rowspace*(nrows-1);		/* empty space between rows */
10387: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10388:   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
10389: for ( irow=0; irow<=nrows; irow++ )	/* and for each row */
10390:   { height += rowheight[irow];		/*height of this row (0 for nrows)*/
10391:     height += vrowspace[irow];		/*plus extra //[len], if present*/
10392:     height += hlinespace(irow);		/*plus space for hline, if present*/
10393:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10394:      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
10395: /* --- allocate subraster and raster for array --- */
10396: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
10397:   fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
10398:   width,colspace, height,rowspace);
10399: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
10400: ==   NULL )  goto end_of_job;		/* quit if failed */
10401: /* --- initialize subraster parameters --- */
10402: arraysp->type = IMAGERASTER;		/* image */
10403: arraysp->symdef = NULL;			/* not applicable for image */
10404: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
10405: arraysp->size = size;			/* size (probably unneeded) */
10406: arrayrp = arraysp->image;		/* raster embedded in subraster */
10407: /* -------------------------------------------------------------------------
10408: embed tokens/cells in array
10409: -------------------------------------------------------------------------- */
10410: itoken = 0;				/* start with first token */
10411: toprow = 0;				/* start at top row of array */
10412: for ( irow=0; irow<=nrows; irow++ )	/*tokens were accumulated row-wise*/
10413:   {
10414:   /* --- initialization for row --- */
10415:   int	baseline = rowbaseln[irow];	/* baseline for this row */
10416:   if ( hline[irow] != 0 )		/* need hline above this row */
10417:     { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
10418:       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
10419:       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
10420:   if ( irow >= nrows ) break;		/*just needed \hline for irow=nrows*/
10421:   toprow += vrowspace[irow];		/* extra //[len] space above irow */
10422:   if ( toprow < 0 ) toprow = 0;		/* check for large negative [-len] */
10423:   toprow += hlinespace(irow);		/* space for hline above irow */
10424:   leftcol = 0;				/* start at leftmost column */
10425:   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
10426:     {
10427:     subraster *tsp = toksp[itoken];	/* token that belongs in this cell */
10428:     /* --- first adjust leftcol for vline to left of icol, if present ---- */
10429:     leftcol += vlinespace(icol);	/* space for vline to left of col */
10430:     /* --- now rasterize cell ---- */
10431:     if ( tsp != NULL )			/* have a rasterized cell token */
10432:       {
10433:       /* --- local parameters --- */
10434:       int cwidth = colwidth[icol],	/* total column width */
10435: 	  twidth = (tsp->image)->width,	/* token width */
10436: 	  theight= (tsp->image)->height, /* token height */
10437: 	  tokencol = 0,			/*H offset (init for left justify)*/
10438: 	  tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
10439:       /* --- adjust leftcol for vline to left of icol, if present ---- */
10440:       /*leftcol += vlinespace(icol);*/	/* space for vline to left of col */
10441:       /* --- reset justification (if not left-justified) --- */
10442:       if ( justify[icol] == 0 )		/* but user wants it centered */
10443: 	  tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
10444:       else if ( justify[icol] == 1 )	/* or user wants right-justify */
10445: 	  tokencol = cwidth-twidth;	/* so put entire margin at left */
10446:       /* --- reset vertical centering (if not baseline-aligned) --- */
10447:       if ( rowcenter[irow] )		/* center cells in row vertically */
10448: 	  tokenrow = (rowheight[irow]-theight)/2; /* center row */
10449:       /* --- embed token raster at appropriate place in array raster --- */
10450:       rastput(arrayrp,tsp->image,	/* overlay cell token in array */
10451: 	  toprow+ tokenrow,		/*with aligned baseline or centered*/
10452: 	  leftcol+tokencol, 1);		/* and justified as requested */
10453:       } /* --- end-of-if(tsp!=NULL) --- */
10454:     itoken++;				/* bump index for next cell */
10455:     leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
10456:       /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
10457:     } /* --- end-of-for(icol) --- */
10458:   toprow += rowheight[irow] + rowspace;	/* move toprow down for next row */
10459:   } /* --- end-of-for(irow) --- */
10460: /* -------------------------------------------------------------------------
10461: draw vlines as necessary
10462: -------------------------------------------------------------------------- */
10463: leftcol = 0;				/* start at leftmost column */
10464: for ( icol=0; icol<=maxcols; icol++ )	/* check each col for a vline */
10465:   {
10466:   if ( vline[icol] != 0 )		/* need vline to left of this col */
10467:     { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
10468:       if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
10469:       rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
10470:   leftcol += vlinespace(icol);		/* space for vline to left of col */
10471:   if ( icol < maxcols )			/* don't address past end of array */
10472:     leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
10473:   } /* --- end-of-for(icol) --- */
10474: /* -------------------------------------------------------------------------
10475: free workspace and return final result to caller
10476: -------------------------------------------------------------------------- */
10477: end_of_job:
10478:   /* --- free workspace --- */
10479:   if ( ntokens > 0 )			/* if we have workspace to free */
10480:     while ( --ntokens >= 0 )		/* free each token subraster */
10481:       if ( toksp[ntokens] != NULL )	/* if we rasterized this cell */
10482: 	delete_subraster(toksp[ntokens]); /* then free it */
10483:   /* --- return final result to caller --- */
10484:   return ( arraysp );
10485: } /* --- end-of-function rastarray() --- */
10486: 
10487: 
10488: /* ==========================================================================
10489:  * Function:	rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
10490:  * Purpose:	\picture handler, returns subraster corresponding to picture
10491:  *		expression (immediately following \picture) at font size
10492:  * --------------------------------------------------------------------------
10493:  * Arguments:	expression (I/O) char **  to first char of null-terminated
10494:  *				string immediately following \picture to be
10495:  *				rasterized, and returning ptr immediately
10496:  *				following last character processed.
10497:  *		size (I)	int containing 0-7 default font size
10498:  *		basesp (I)	subraster *  to character (or subexpression)
10499:  *				immediately preceding \picture
10500:  *				(unused, but passed for consistency)
10501:  *		arg1 (I)	int unused
10502:  *		arg2 (I)	int unused
10503:  *		arg3 (I)	int unused
10504:  * --------------------------------------------------------------------------
10505:  * Returns:	( subraster * )	ptr to subraster corresponding to picture
10506:  *				expression, or NULL for any parsing error
10507:  * --------------------------------------------------------------------------
10508:  * Notes:     o	Summary of syntax...
10509:  *		  \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
10510:  *	      o	
10511:  * ======================================================================= */
10512: /* --- entry point --- */
10513: subraster *rastpicture ( char **expression, int size, subraster *basesp,
10514: 			int arg1, int arg2, int arg3 )
10515: {
10516: /* -------------------------------------------------------------------------
10517: Allocations and Declarations
10518: -------------------------------------------------------------------------- */
10519: char	*texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
10520: 	putexpr[256], *putptr,*multptr,	/*[multi]put (x,y[;xinc,yinc;num])*/
10521: 	pream[96], *preptr,		/* optional put preamble */
10522: 	picelem[1025];			/* picture element following put */
10523: subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
10524: 	*new_subraster(), *picturesp=NULL, /* subraster for entire picture */
10525: 	*oldworkingbox = workingbox;	/* save working box on entry */
10526: raster	*picturerp=NULL;		/* raster for entire picture */
10527: int	delete_subraster();		/* free picelemsp[] workspace */
10528: int	pixsz = 1;			/* pixels are one bit each */
10529: double	x=0.0,y=0.0,			/* x,y-coords for put,multiput*/
10530: 	xinc=0.0,yinc=0.0;		/* x,y-incrementss for multiput*/
10531: int	width=0,  height=0,		/* #pixels width,height of picture */
10532: 	ewidth=0, eheight=0,		/* pic element width,height */
10533: 	ix=0,xpos=0, iy=0,ypos=0,	/* mimeTeX x,y pixel coords */
10534: 	num=1, inum;			/* number reps, index of element */
10535: int	evalterm();			/* evaluate [arg] and {arg}'s */
10536: int	iscenter=0;			/* center or lowerleft put position*/
10537: int	*oldworkingparam = workingparam, /* save working param on entry */
10538: 	origin = 0;			/* x,yinc ++=00 +-=01 -+=10 --=11 */
10539: int	rastput();			/* embed elements in picture */
10540: int	type_raster();			/* display debugging output */
10541: /* -------------------------------------------------------------------------
10542: First obtain (width,height) arguments immediately following \picture command
10543: -------------------------------------------------------------------------- */
10544: /* --- parse for (width,height) arguments, and bump expression past it --- */
10545: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
10546: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
10547: /* --- now interpret width,height returned in putexpr --- */
10548: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
10549:   *putptr = '\000';			/* found it, so replace ',' by '\0'*/
10550: width=height = eround(putexpr);		/*width pixels*/
10551: if ( putptr != NULL )			/* 2nd arg, if present, is height */
10552:   height = eround(putptr+1);		/*in pixels*/
10553: /* -------------------------------------------------------------------------
10554: Then obtain entire picture {...} subexpression following (width,height)
10555: -------------------------------------------------------------------------- */
10556: /* --- parse for picture subexpression, and bump expression past it --- */
10557: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
10558: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
10559: /* -------------------------------------------------------------------------
10560: allocate subraster and raster for complete picture
10561: -------------------------------------------------------------------------- */
10562: /* --- sanity check on width,height args --- */
10563: if ( width < 2 ||  width > 600
10564: ||  height < 2 || height > 600 ) goto end_of_job;
10565: /* --- allocate and initialize subraster for constructed picture --- */
10566: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
10567: ==   NULL )  goto end_of_job;		/* quit if failed */
10568: workingbox = picturesp;			/* set workingbox to our picture */
10569: /* --- initialize picture subraster parameters --- */
10570: picturesp->type = IMAGERASTER;		/* image */
10571: picturesp->symdef = NULL;		/* not applicable for image */
10572: picturesp->baseline = height/2 + 2;	/* is a little above center good? */
10573: picturesp->size = size;			/* size (probably unneeded) */
10574: picturerp = picturesp->image;		/* raster embedded in subraster */
10575: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10576:   fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
10577: /* -------------------------------------------------------------------------
10578: parse out each picture element, rasterize it, and place it in picture
10579: -------------------------------------------------------------------------- */
10580: while ( *picptr != '\000' )		/* until we run out of pic_elems */
10581:   {
10582:   /* -----------------------------------------------------------------------
10583:   first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
10584:   ------------------------------------------------------------------------ */
10585:   /* --- init default values in case not explicitly supplied in args --- */
10586:   x=y=0.0;  xinc=yinc=0.0;  num=1;	/* init default values */
10587:   iscenter = origin = 0;		/* center, origin */
10588:   /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
10589:   while ( *picptr != '\000' )		/* skip invalid chars preceding ( */
10590:     if ( *picptr == '(' ) break;	/* found opening ( */
10591:     else picptr++;			/* else skip invalid char */
10592:   picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
10593:   if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
10594:   /* --- first look for $-terminated or for any non-digit preamble --- */
10595:   *pream = '\000';			/* init preamble as empty string */
10596:   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
10597:     { *putptr++ = '\000';		/* replace $ by '\0', bump past $ */
10598:       strninit(pream,putexpr,92); }	/* copy leading preamble from put */
10599:   else					/* look for any non-digit preamble */
10600:     { int npream = 0;			/* #chars in preamble */
10601:       for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ )
10602: 	if ( *putptr == '\000'		/* end-of-putdata signalled */
10603: 	||   !isalpha((int)(*putptr))	/* or found non-alpha char */
10604: 	||   npream > 92 ) break;	/* or preamble too long */
10605: 	else *preptr++ = *putptr;	/* copy alpha char to preamble */
10606:       *preptr = '\000'; }		/* null-terminate preamble */
10607:   /* --- interpret preamble --- */
10608:   for ( preptr=pream; ; preptr++ )	/* examine each preamble char */
10609:     if ( *preptr == '\000' ) break;	/* end-of-preamble signalled */
10610:     else switch ( tolower(*preptr) )	/* check lowercase preamble char */
10611:       {
10612:       default: break;			/* unrecognized flag */
10613:       case 'c': iscenter=1; break;	/* center pic_elem at x,y coords */
10614:       } /* --- end-of-switch --- */
10615:   /* --- interpret x,y;xinc,yinc;num following preamble --- */      
10616:   if ( *putptr != '\000' )		/*check for put data after preamble*/
10617:    {
10618:    /* --- first squeeze preamble out of put expression --- */
10619:    if ( *pream != '\000' )		/* have preamble */
10620:      {strsqueezep(putexpr,putptr);}	/* squeeze it out */
10621:    /* --- interpret x,y --- */
10622:    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
10623:      *multptr = '\000';			/* replace semicolon by '\0' */
10624:    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
10625:      *putptr = '\000';			/* replace comma by '\0'  */
10626:    if ( *putexpr != '\000' )		/* leading , may be placeholder */
10627:      x = (double)(eround(putexpr));	/* x coord in pixels*/
10628:    if ( putptr != NULL )		/* 2nd arg, if present, is y coord */
10629:      y = (double)(eround(putptr+1));	/* in pixels */
10630:    /* --- interpret xinc,yinc,num if we have a multiput --- */
10631:    if ( multptr != NULL )		/* found ';' signalling multiput */
10632:      {
10633:      if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
10634:        *preptr = '\000';		/* replace ';' by '\0' */
10635:      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
10636:        *putptr = '\000';		/* replace ',' by '\0' */
10637:      if ( *(multptr+1) != '\000' )	/* leading , may be placeholder */
10638:        xinc = (double)(eround(multptr+1)); /* xinc in pixels */
10639:      if ( putptr != NULL )		/* 2nd arg, if present, is yinc */
10640:        yinc = (double)(eround(putptr+1)); /* in user pixels */
10641:      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
10642:      } /* --- end-of-if(multptr!=NULL) --- */
10643:    } /* --- end-of-if(*preptr!='\000') --- */
10644:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10645:     fprintf(msgfp,
10646:     "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
10647:     pream,x,y,xinc,yinc,num);
10648:   /* -----------------------------------------------------------------------
10649:   now obtain {...} picture element following [multi]put, and rasterize it
10650:   ------------------------------------------------------------------------ */
10651:   /* --- parse for {...} picture element and bump picptr past it --- */
10652:   picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
10653:   if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
10654:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10655:     fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
10656:   /* --- rasterize picture element --- */
10657:   origin = 0;				/* init origin as working param */
10658:   workingparam = &origin;		/* and point working param to it */
10659:   picelemsp = rasterize(picelem,size);	/* rasterize picture element */
10660:   if ( picelemsp == NULL ) continue;	/* failed to rasterize, skip elem */
10661:   ewidth  = (picelemsp->image)->width;	/* width of element, in pixels */
10662:   eheight = (picelemsp->image)->height;	/* height of element, in pixels */
10663:   if ( origin == 55 ) iscenter = 1;	/* origin set to (.5,.5) for center*/
10664:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10665:     { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
10666:       ewidth,eheight,origin,num);
10667:       if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
10668:   /* -----------------------------------------------------------------------
10669:   embed element in picture (once, or multiple times if requested)
10670:   ------------------------------------------------------------------------ */
10671:   for ( inum=0; inum<num; inum++ )	/* once, or for num repetitions */
10672:     {
10673:     /* --- set x,y-coords for this iteration --- */
10674:     ix = iround(x);  iy = iround(y);	/* round x,y to nearest integer */
10675:     if ( iscenter )			/* place center of element at x,y */
10676:       {	xpos = ix - ewidth/2;		/* x picture coord to center elem */
10677: 	ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
10678:     else				/* default places lower-left at x,y*/
10679:       {	xpos = ix;			/* set x pixel coord for left */
10680: 	if ( origin==10 || origin==11 )	/* x,yinc's are -+ or -- */
10681: 	  xpos = ix - ewidth;		/* so set for right instead */
10682: 	ypos = height - iy - eheight;	/* set y pixel coord for lower */
10683: 	if ( origin==1 || origin==11 )	/* x,yinc's are +- or -- */
10684: 	  ypos = height - iy; }		/* so set for upper instead */
10685:     if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10686:       fprintf(msgfp,
10687:       "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
10688:       inum,x,y,ix,iy,xpos,ypos);
10689:     /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
10690:     if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
10691:     /* --- apply increment --- */
10692:     if ( xinc==0. && yinc==0. ) break;	/* quit if both increments zero */
10693:     x += xinc;  y += yinc;		/* increment coords for next iter */
10694:     } /* --- end-of-for(inum) --- */
10695:   /* --- free picture element subraster after embedding it in picture --- */
10696:   delete_subraster(picelemsp);		/* done with subraster, so free it */
10697:   } /* --- end-of-while(*picptr!=0) --- */
10698: /* -------------------------------------------------------------------------
10699: return picture constructed from pic_elements to caller
10700: -------------------------------------------------------------------------- */
10701: end_of_job:
10702:   workingbox = oldworkingbox;		/* restore original working box */
10703:   workingparam = oldworkingparam;	/* restore original working param */
10704:   return ( picturesp );			/* return our picture to caller */
10705: } /* --- end-of-function rastpicture() --- */
10706: 
10707: 
10708: /* ==========================================================================
10709:  * Function:	rastline ( expression, size, basesp, arg1, arg2, arg3 )
10710:  * Purpose:	\line handler, returns subraster corresponding to line
10711:  *		parameters (xinc,yinc){xlen}
10712:  * --------------------------------------------------------------------------
10713:  * Arguments:	expression (I/O) char **  to first char of null-terminated
10714:  *				string immediately following \line to be
10715:  *				rasterized, and returning ptr immediately
10716:  *				following last character processed.
10717:  *		size (I)	int containing 0-7 default font size
10718:  *		basesp (I)	subraster *  to character (or subexpression)
10719:  *				immediately preceding \line
10720:  *				(unused, but passed for consistency)
10721:  *		arg1 (I)	int unused
10722:  *		arg2 (I)	int unused
10723:  *		arg3 (I)	int unused
10724:  * --------------------------------------------------------------------------
10725:  * Returns:	( subraster * )	ptr to subraster corresponding to line
10726:  *				requested, or NULL for any parsing error
10727:  * --------------------------------------------------------------------------
10728:  * Notes:     o	Summary of syntax...
10729:  *		  \line(xinc,yinc){xlen}
10730:  *	      o	if {xlen} not given, then it's assumed xlen = |xinc|
10731:  * ======================================================================= */
10732: /* --- entry point --- */
10733: subraster *rastline ( char **expression, int size, subraster *basesp,
10734: 			int arg1, int arg2, int arg3 )
10735: {
10736: /* -------------------------------------------------------------------------
10737: Allocations and Declarations
10738: -------------------------------------------------------------------------- */
10739: char	*texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
10740: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
10741: /*char	*origexpression = *expression;*/ /*original expression after \line*/
10742: int	pixsz = 1;			/* pixels are one bit each */
10743: int	thickness = 1;			/* line thickness */
10744: double	xinc=0.0, yinc=0.0,		/* x,y-increments for line, */
10745: 	xlen=0.0, ylen=0.0;		/* x,y lengths for line */
10746: int	width=0,  height=0,		/* #pixels width,height of line */
10747: 	rwidth=0, rheight=0;		/*alloc width,height plus thickness*/
10748: int	evalterm();			/* evaluate [arg] and {arg}'s */
10749: int	istop=0,  isright=0,		/* origin at bot-left if x,yinc>=0 */
10750: 	origin = 0;			/* x,yinc: ++=00 +-=01 -+=10 --=11 */
10751: int	line_raster();			/* draw line in linesp->image */
10752: /* -------------------------------------------------------------------------
10753: obtain (xinc,yinc) arguments immediately following \line command
10754: -------------------------------------------------------------------------- */
10755: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
10756: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
10757: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
10758: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
10759: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
10760:   { *xptr = '\000';			/* terminate linexpr at ; */
10761:     thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
10762: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
10763:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
10764: if ( *linexpr != '\000' )		/* check against missing 1st arg */
10765:   xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
10766: if ( xptr != NULL )			/* 2nd arg, if present, is yinc */
10767:   yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
10768: /* -------------------------------------------------------------------------
10769: obtain optional {xlen} following (xinc,yinc), and calculate ylen
10770: -------------------------------------------------------------------------- */
10771: /* --- check if {xlen} given --- */
10772: if ( *(*expression) == '{' )		/*have {xlen} if leading char is { */
10773:   {
10774:   /* --- parse {xlen} and bump expression past it, interpret as double --- */
10775:   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
10776:   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
10777:   xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
10778:   /* --- set other values accordingly --- */
10779:   if ( xlen  < 0.0 ) xinc = -xinc;	/* if xlen negative, flip xinc sign*/
10780:   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
10781:   else xlen  = 0.0;			/* can't have xlen if xinc=0 */
10782:   } /* --- end-of-if(*(*expression)=='{') --- */
10783: /* -------------------------------------------------------------------------
10784: calculate width,height, etc, based on xlen,ylen, etc
10785: -------------------------------------------------------------------------- */
10786: /* --- force lengths positive --- */
10787: xlen = absval(xlen);			/* force xlen positive */
10788: ylen = absval(ylen);			/* force ylen positive */
10789: /* --- calculate corresponding lengths in pixels --- */
10790: width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
10791: height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
10792: rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
10793: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
10794: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
10795: if ( xinc < 0.0 ) isright = 1;		/*negative xinc, so corner is (1,?)*/
10796: if ( yinc < 0.0 ) istop = 1;		/*negative yinc, so corner is (?,1)*/
10797: origin = isright*10 + istop;		/* interpret 0=(0,0), 11=(1,1), etc*/
10798: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
10799:   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
10800:   width,height,origin,xinc,yinc);
10801: /* -------------------------------------------------------------------------
10802: allocate subraster and raster for line
10803: -------------------------------------------------------------------------- */
10804: /* --- sanity check on width,height,thickness args --- */
10805: if ( width < 1 ||  width > 600
10806: ||  height < 1 || height > 600
10807: ||  thickness<1||thickness>25 ) goto end_of_job;
10808: /* --- allocate and initialize subraster for constructed line --- */
10809: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
10810: ==   NULL )  goto end_of_job;		/* quit if failed */
10811: /* --- initialize line subraster parameters --- */
10812: linesp->type = IMAGERASTER;		/* image */
10813: linesp->symdef = NULL;			/* not applicable for image */
10814: linesp->baseline = height/2 + 2		/* is a little above center good? */
10815: 	+ (rheight-height)/2;		/* account for line thickness too */
10816: linesp->size = size;			/* size (probably unneeded) */
10817: /* -------------------------------------------------------------------------
10818: draw the line
10819: -------------------------------------------------------------------------- */
10820: line_raster ( linesp->image,		/* embedded raster image */
10821: 	(istop?   0 : height-1),	/* row0, from bottom or top */
10822: 	(isright?  width-1 : 0),	/* col0, from left or right */
10823: 	(istop?   height-1 : 0),	/* row1, to top or bottom */
10824: 	(isright? 0 :  width-1),	/* col1, to right or left */
10825: 	thickness );			/* line thickness (usually 1 pixel)*/
10826: /* -------------------------------------------------------------------------
10827: return constructed line to caller
10828: -------------------------------------------------------------------------- */
10829: end_of_job:
10830:   if ( workingparam != NULL )		/* caller wants origin */
10831:     *workingparam = origin;		/* return origin corner to caller */
10832:   return ( linesp );			/* return line to caller */
10833: } /* --- end-of-function rastline() --- */
10834: 
10835: 
10836: /* ==========================================================================
10837:  * Function:	rastrule ( expression, size, basesp, arg1, arg2, arg3 )
10838:  * Purpose:	\rule handler, returns subraster corresponding to rule
10839:  *		parameters [lift]{width}{height}
10840:  * --------------------------------------------------------------------------
10841:  * Arguments:	expression (I/O) char **  to first char of null-terminated
10842:  *				string immediately following \rule to be
10843:  *				rasterized, and returning ptr immediately
10844:  *				following last character processed.
10845:  *		size (I)	int containing 0-7 default font size
10846:  *		basesp (I)	subraster *  to character (or subexpression)
10847:  *				immediately preceding \rule
10848:  *				(unused, but passed for consistency)
10849:  *		arg1 (I)	int unused
10850:  *		arg2 (I)	int unused
10851:  *		arg3 (I)	int unused
10852:  * --------------------------------------------------------------------------
10853:  * Returns:	( subraster * )	ptr to subraster corresponding to rule
10854:  *				requested, or NULL for any parsing error
10855:  * --------------------------------------------------------------------------
10856:  * Notes:     o	Summary of syntax...
10857:  *		  \rule[lift]{width}{height}
10858:  *	      o	if [lift] not given, then bottom of rule on baseline
10859:  *	      o	if width=0 then you get an invisible strut 1 (one) pixel wide
10860:  * ======================================================================= */
10861: /* --- entry point --- */
10862: subraster *rastrule ( char **expression, int size, subraster *basesp,
10863: 			int arg1, int arg2, int arg3 )
10864: {
10865: /* -------------------------------------------------------------------------
10866: Allocations and Declarations
10867: -------------------------------------------------------------------------- */
10868: char	*texsubexpr(), rulexpr[257];	/* rule[lift]{wdth}{hgt} */
10869: subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
10870: int	pixsz = 1;			/* pixels are one bit each */
10871: int	lift=0, width=0, height=0;	/* default rule parameters */
10872: double	dval;				/* convert ascii params to doubles */
10873: int	rwidth=0, rheight=0;		/* alloc width, height plus lift */
10874: int	rule_raster();			/* draw rule in rulesp->image */
10875: int	evalterm();			/* evaluate args */
10876: /* -------------------------------------------------------------------------
10877: Obtain lift,width,height
10878: -------------------------------------------------------------------------- */
10879: /* --- check for optional lift arg  --- */
10880: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
10881:   { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
10882:     dval = evalterm(mimestore,rulexpr);	/* convert [lift] to int */
10883:     if ( dval <= 99 && dval >= (-99) )	/* sanity check */
10884:       lift = iround(unitlength*dval); }	/* scale by unitlength and round */
10885: /* --- parse for width --- */
10886: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
10887: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
10888: dval = evalterm(mimestore,rulexpr);	/* convert {width} to int */
10889: if ( dval <= 500 && dval >= 0 )		/* sanity check */
10890:   width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
10891: /* --- parse for height --- */
10892: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
10893: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
10894: dval = evalterm(mimestore,rulexpr);	/* convert {height} to int */
10895: if ( dval <= 500 && dval > 0 )		/* sanity check */
10896:   height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
10897: /* --- raster width,height in pixels --- */
10898: rwidth  = max2(1,width);		/* raster must be at least 1 pixel*/
10899: rheight = height + (lift>=0?lift:	/* raster height plus lift */
10900:   (-lift<height?0:-lift-height+1));	/* may need empty space above rule */
10901: /* -------------------------------------------------------------------------
10902: allocate subraster and raster for rule
10903: -------------------------------------------------------------------------- */
10904: /* --- sanity check on width,height,thickness args --- */
10905: if ( rwidth < 1 ||  rwidth > 600
10906: ||  rheight < 1 || rheight > 600 ) goto end_of_job;
10907: /* --- allocate and initialize subraster for constructed rule --- */
10908: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
10909: ==   NULL )  goto end_of_job;		/* quit if failed */
10910: /* --- initialize line subraster parameters --- */
10911: rulesp->type = IMAGERASTER;		/* image */
10912: rulesp->symdef = NULL;			/* not applicable for image */
10913: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
10914: rulesp->size = size;			/* size (probably unneeded) */
10915: /* -------------------------------------------------------------------------
10916: draw the rule
10917: -------------------------------------------------------------------------- */
10918: rule_raster ( rulesp->image,		/* embedded raster image */
10919: 	(-lift<height?0:rheight-height), /* topmost row for top-left corner*/
10920: 	0,				/* leftmost col for top-left corner*/
10921: 	width,				/* rule width */
10922: 	height,				/* rule height */
10923: 	( width>0? 0:4 ) );		/* rule type */
10924: /* -------------------------------------------------------------------------
10925: return constructed rule to caller
10926: -------------------------------------------------------------------------- */
10927: end_of_job:
10928:   return ( rulesp );			/* return rule to caller */
10929: } /* --- end-of-function rastrule() --- */
10930: 
10931: 
10932: /* ==========================================================================
10933:  * Function:	rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
10934:  * Purpose:	\circle handler, returns subraster corresponding to ellipse
10935:  *		parameters (xdiam[,ydiam])
10936:  * --------------------------------------------------------------------------
10937:  * Arguments:	expression (I/O) char **  to first char of null-terminated
10938:  *				string immediately following \circle to be
10939:  *				rasterized, and returning ptr immediately
10940:  *				following last character processed.
10941:  *		size (I)	int containing 0-7 default font size
10942:  *		basesp (I)	subraster *  to character (or subexpression)
10943:  *				immediately preceding \circle
10944:  *				(unused, but passed for consistency)
10945:  *		arg1 (I)	int unused
10946:  *		arg2 (I)	int unused
10947:  *		arg3 (I)	int unused
10948:  * --------------------------------------------------------------------------
10949:  * Returns:	( subraster * )	ptr to subraster corresponding to ellipse
10950:  *				requested, or NULL for any parsing error
10951:  * --------------------------------------------------------------------------
10952:  * Notes:     o	Summary of syntax...
10953:  *		  \circle(xdiam[,ydiam])
10954:  *	      o
10955:  * ======================================================================= */
10956: /* --- entry point --- */
10957: subraster *rastcircle ( char **expression, int size, subraster *basesp,
10958: 			int arg1, int arg2, int arg3 )
10959: {
10960: /* -------------------------------------------------------------------------
10961: Allocations and Declarations
10962: -------------------------------------------------------------------------- */
10963: char	*texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
10964: char	*qptr=NULL, quads[256]="1234";	/* default to draw all quadrants */
10965: double	theta0=0.0, theta1=0.0;		/* ;theta0,theta1 instead of ;quads*/
10966: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
10967: int	pixsz = 1;			/* pixels are one bit each */
10968: double	xdiam=0.0, ydiam=0.0;		/* x,y major/minor axes/diameters */
10969: int	width=0,  height=0;		/* #pixels width,height of ellipse */
10970: int	thickness = 1;			/* drawn lines are one pixel thick */
10971: int	evalterm();			/* evaluate [arg],{arg} expressions*/
10972: int	origin = 55;			/* force origin centered */
10973: int	circle_raster(),		/* draw ellipse in circsp->image */
10974: 	circle_recurse();		/* for theta0,theta1 args */
10975: /* -------------------------------------------------------------------------
10976: obtain (xdiam[,ydiam]) arguments immediately following \circle command
10977: -------------------------------------------------------------------------- */
10978: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
10979: *expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
10980: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
10981: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
10982: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
10983:   { *qptr = '\000';			/* replace semicolon by '\0' */
10984:     strninit(quads,qptr+1,128);		/* save user-requested quads */
10985:     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
10986:       {	*qptr = '\000';			/* replace , with null */
10987: 	theta0 = (double)evalterm(mimestore,quads);  /* theta0 precedes , */
10988: 	theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
10989: 	qptr = NULL; }			/* signal thetas instead of quads */
10990:     else
10991: 	qptr = quads; }			/* set qptr arg for circle_raster()*/
10992: else					/* no ;quads at all */
10993:   qptr = quads;				/* default to all 4 quadrants */
10994: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
10995:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
10996: xdiam = ydiam =				/* xdiam=ydiam in user units */
10997:   (double)evalterm(mimestore,circexpr);	/* evaluate expression */
10998: if ( xptr != NULL )			/* 2nd arg, if present, is ydiam */
10999:   ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
11000: /* -------------------------------------------------------------------------
11001: calculate width,height, etc
11002: -------------------------------------------------------------------------- */
11003: /* --- calculate width,height in pixels --- */
11004: width  = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
11005: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
11006: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
11007:   fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
11008:   width,height,(qptr==NULL?"default":qptr));
11009: /* -------------------------------------------------------------------------
11010: allocate subraster and raster for complete picture
11011: -------------------------------------------------------------------------- */
11012: /* --- sanity check on width,height args --- */
11013: if ( width < 1 ||  width > 600
11014: ||  height < 1 || height > 600 ) goto end_of_job;
11015: /* --- allocate and initialize subraster for constructed ellipse --- */
11016: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11017: ==   NULL )  goto end_of_job;		/* quit if failed */
11018: /* --- initialize ellipse subraster parameters --- */
11019: circsp->type = IMAGERASTER;		/* image */
11020: circsp->symdef = NULL;			/* not applicable for image */
11021: circsp->baseline = height/2 + 2;	/* is a little above center good? */
11022: circsp->size = size;			/* size (probably unneeded) */
11023: /* -------------------------------------------------------------------------
11024: draw the ellipse
11025: -------------------------------------------------------------------------- */
11026: if ( qptr != NULL )			/* have quads */
11027:   circle_raster ( circsp->image,	/* embedded raster image */
11028: 	0, 0,				/* row0,col0 are upper-left corner */
11029: 	height-1, width-1,		/* row1,col1 are lower-right */
11030: 	thickness,			/* line thickness is 1 pixel */
11031: 	qptr );				/* "1234" quadrants to be drawn */
11032: else					/* have theta0,theta1 */
11033:   circle_recurse ( circsp->image,	/* embedded raster image */
11034: 	0, 0,				/* row0,col0 are upper-left corner */
11035: 	height-1, width-1,		/* row1,col1 are lower-right */
11036: 	thickness,			/* line thickness is 1 pixel */
11037: 	theta0,theta1 );		/* theta0,theta1 arc to be drawn */
11038: /* -------------------------------------------------------------------------
11039: return constructed ellipse to caller
11040: -------------------------------------------------------------------------- */
11041: end_of_job:
11042:   if ( workingparam != NULL )		/* caller wants origin */
11043:     *workingparam = origin;		/* return center origin to caller */
11044:   return ( circsp );			/* return ellipse to caller */
11045: } /* --- end-of-function rastcircle() --- */
11046: 
11047: 
11048: /* ==========================================================================
11049:  * Function:	rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
11050:  * Purpose:	\bezier handler, returns subraster corresponding to bezier
11051:  *		parameters (col0,row0)(col1,row1)(colt,rowt)
11052:  * --------------------------------------------------------------------------
11053:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11054:  *				string immediately following \bezier to be
11055:  *				rasterized, and returning ptr immediately
11056:  *				following last character processed.
11057:  *		size (I)	int containing 0-7 default font size
11058:  *		basesp (I)	subraster *  to character (or subexpression)
11059:  *				immediately preceding \bezier
11060:  *				(unused, but passed for consistency)
11061:  *		arg1 (I)	int unused
11062:  *		arg2 (I)	int unused
11063:  *		arg3 (I)	int unused
11064:  * --------------------------------------------------------------------------
11065:  * Returns:	( subraster * )	ptr to subraster corresponding to bezier
11066:  *				requested, or NULL for any parsing error
11067:  * --------------------------------------------------------------------------
11068:  * Notes:     o	Summary of syntax...
11069:  *		  \bezier(col1,row1)(colt,rowt)
11070:  *	      o	col0=0,row0=0 assumed, i.e., given by
11071:  *		\picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
11072:  * ======================================================================= */
11073: /* --- entry point --- */
11074: subraster *rastbezier ( char **expression, int size, subraster *basesp,
11075: 			int arg1, int arg2, int arg3 )
11076: {
11077: /* -------------------------------------------------------------------------
11078: Allocations and Declarations
11079: -------------------------------------------------------------------------- */
11080: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
11081: char	*texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
11082: double	r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
11083: 	rmid=0.0, cmid=0.0,		/* coords at parameterized midpoint*/
11084: 	rmin=0.0, cmin=0.0,		/* minimum r,c */
11085: 	rmax=0.0, cmax=0.0,		/* maximum r,c */
11086: 	rdelta=0.0, cdelta=0.0,		/* rmax-rmin, cmax-cmin */
11087: 	r=0.0, c=0.0;			/* some point */
11088: int	evalterm();			/* evaluate [arg],{arg} expressions*/
11089: int	iarg=0;				/* 0=r0,c0 1=r1,c1 2=rt,ct */
11090: int	width=0, height=0;		/* dimensions of bezier raster */
11091: int	pixsz = 1;			/* pixels are one bit each */
11092: /*int	thickness = 1;*/		/* drawn lines are one pixel thick */
11093: int	origin = 0;			/*c's,r's reset to lower-left origin*/
11094: int	bezier_raster();		/* draw bezier in bezsp->image */
11095: /* -------------------------------------------------------------------------
11096: obtain (c1,r1)(ct,rt) args immediately following \bezier command
11097: -------------------------------------------------------------------------- */
11098: for ( iarg=1; iarg<=2; iarg++ )		/* 0=c0,r0 1=c1,r1 2=ct,rt */
11099:   {
11100:   /* --- parse for (r,c) args, and bump expression past them all --- */
11101:   *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
11102:   if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
11103:   /* --- now interpret (r,c) returned in bezexpr --- */
11104:   c = r = 0.0;				/* init x-coord=col, y-coord=row */
11105:   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
11106:     { *xptr = '\000';			/* found it, so replace ',' by '\0'*/
11107:       /* --- row=y-coord in pixels --- */
11108:       r = unitlength*((double)evalterm(mimestore,xptr+1)); }
11109:   /* --- col=x-coord in pixels --- */
11110:   c = unitlength*((double)evalterm(mimestore,bezexpr));
11111:   /* --- store r,c --- */
11112:   switch ( iarg )
11113:     { case 0: r0=r; c0=c; break;
11114:       case 1: r1=r; c1=c; break;
11115:       case 2: rt=r; ct=c; break; }
11116:   } /* --- end-of-for(iarg) --- */
11117: /* --- determine midpoint and maximum,minimum points --- */
11118: rmid = 0.5*(rt + 0.5*(r0+r1));		/* y-coord at middle of bezier */
11119: cmid = 0.5*(ct + 0.5*(c0+c1));		/* x-coord at middle of bezier */
11120: rmin = min3(r0,r1,rmid);		/* lowest row */
11121: cmin = min3(c0,c1,cmid);		/* leftmost col */
11122: rmax = max3(r0,r1,rmid);		/* highest row */
11123: cmax = max3(c0,c1,cmid);		/* rightmost col */
11124: rdelta = rmax-rmin;			/* height */
11125: cdelta = cmax-cmin;			/* width */
11126: /* --- rescale coords so we start at 0,0 --- */
11127: r0 -= rmin;  c0 -= cmin;		/* rescale r0,c0 */
11128: r1 -= rmin;  c1 -= cmin;		/* rescale r1,c1 */
11129: rt -= rmin;  ct -= cmin;		/* rescale rt,ct */
11130: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
11131: r0 = rdelta - r0 + 1;			/* map 0-->height-1, height-1-->0 */
11132: r1 = rdelta - r1 + 1;
11133: rt = rdelta - rt + 1;
11134: /* --- determine width,height of raster needed for bezier --- */
11135: width  = (int)(cdelta + 0.9999) + 1;	/* round width up */
11136: height = (int)(rdelta + 0.9999) + 1;	/* round height up */
11137: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
11138:   fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
11139:   "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
11140:   width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
11141: /* -------------------------------------------------------------------------
11142: allocate raster
11143: -------------------------------------------------------------------------- */
11144: /* --- sanity check on width,height args --- */
11145: if ( width < 1 ||  width > 600
11146: ||  height < 1 || height > 600 ) goto end_of_job;
11147: /* --- allocate and initialize subraster for constructed bezier --- */
11148: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11149: ==   NULL )  goto end_of_job;		/* quit if failed */
11150: /* --- initialize bezier subraster parameters --- */
11151: bezsp->type = IMAGERASTER;		/* image */
11152: bezsp->symdef = NULL;			/* not applicable for image */
11153: bezsp->baseline = height/2 + 2;		/* is a little above center good? */
11154: bezsp->size = size;			/* size (probably unneeded) */
11155: /* -------------------------------------------------------------------------
11156: draw the bezier
11157: -------------------------------------------------------------------------- */
11158: bezier_raster ( bezsp->image,		/* embedded raster image */
11159: 	r0, c0,				/* row0,col0 are lower-left corner */
11160: 	r1, c1,				/* row1,col1 are upper-right */
11161: 	rt, ct );			/* bezier tangent point */
11162: /* -------------------------------------------------------------------------
11163: return constructed bezier to caller
11164: -------------------------------------------------------------------------- */
11165: end_of_job:
11166:   if ( workingparam != NULL )		/* caller wants origin */
11167:     *workingparam = origin;		/* return center origin to caller */
11168:   return ( bezsp );			/* return bezier to caller */
11169: } /* --- end-of-function rastbezier() --- */
11170: 
11171: 
11172: /* ==========================================================================
11173:  * Function:	rastraise ( expression, size, basesp, arg1, arg2, arg3 )
11174:  * Purpose:	\raisebox{lift}{subexpression} handler, returns subraster
11175:  *		containing subexpression with its baseline "lifted" by lift
11176:  *		pixels, scaled by \unitlength, or "lowered" if lift arg
11177:  *		negative
11178:  * --------------------------------------------------------------------------
11179:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11180:  *				string immediately following \raisebox to be
11181:  *				rasterized, and returning ptr immediately
11182:  *				following last character processed.
11183:  *		size (I)	int containing 0-7 default font size
11184:  *		basesp (I)	subraster *  to character (or subexpression)
11185:  *				immediately preceding \raisebox
11186:  *				(unused, but passed for consistency)
11187:  *		arg1 (I)	int unused
11188:  *		arg2 (I)	int unused
11189:  *		arg3 (I)	int unused
11190:  * --------------------------------------------------------------------------
11191:  * Returns:	( subraster * )	ptr to subraster corresponding to \raisebox
11192:  *				requested, or NULL for any parsing error
11193:  * --------------------------------------------------------------------------
11194:  * Notes:     o	Summary of syntax...
11195:  *		  \raisebox{lift}{subexpression}
11196:  *	      o
11197:  * ======================================================================= */
11198: /* --- entry point --- */
11199: subraster *rastraise ( char **expression, int size, subraster *basesp,
11200: 			int arg1, int arg2, int arg3 )
11201: {
11202: /* -------------------------------------------------------------------------
11203: Allocations and Declarations
11204: -------------------------------------------------------------------------- */
11205: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
11206: subraster *rasterize(), *raisesp=NULL;	/* rasterize subexpr to be raised */
11207: int	lift=0;				/* amount to raise/lower baseline */
11208: int	evalterm();			/* evaluate [arg],{arg} expressions*/
11209: /* -------------------------------------------------------------------------
11210: obtain {lift} argument immediately following \raisebox command
11211: -------------------------------------------------------------------------- */
11212: rastlift = 0;				/* reset global lift adjustment */
11213: /* --- parse for {lift} arg, and bump expression past it --- */
11214: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
11215: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
11216: lift = eround(liftexpr);		/* {lift} to integer */
11217: if ( abs(lift) > 200 ) lift=0;		/* sanity check */
11218: /* -------------------------------------------------------------------------
11219: obtain {subexpr} argument after {lift}, and rasterize it
11220: -------------------------------------------------------------------------- */
11221: /* --- parse for {subexpr} arg, and bump expression past it --- */
11222: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11223: /* --- rasterize subexpression to be raised/lowered --- */
11224: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
11225: ==   NULL ) goto end_of_job;		/* and quit if failed */
11226: /* -------------------------------------------------------------------------
11227: raise/lower baseline and return it to caller
11228: -------------------------------------------------------------------------- */
11229: /* --- raise/lower baseline --- */
11230: raisesp->baseline += lift;		/* new baseline (no height checks) */
11231: rastlift = lift;			/* set global to signal adjustment */
11232: /* --- return raised subexpr to caller --- */
11233: end_of_job:
11234:   return ( raisesp );			/* return raised subexpr to caller */
11235: } /* --- end-of-function rastraise() --- */
11236: 
11237: 
11238: /* ==========================================================================
11239:  * Function:	rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
11240:  * Purpose:	\rotatebox{degrees}{subexpression} handler, returns subraster
11241:  *		containing subexpression rotated by degrees (counterclockwise
11242:  *		if degrees positive)
11243:  * --------------------------------------------------------------------------
11244:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11245:  *				string immediately following \rotatebox to be
11246:  *				rasterized, and returning ptr immediately
11247:  *				following last character processed.
11248:  *		size (I)	int containing 0-7 default font size
11249:  *		basesp (I)	subraster *  to character (or subexpression)
11250:  *				immediately preceding \rotatebox
11251:  *				(unused, but passed for consistency)
11252:  *		arg1 (I)	int unused
11253:  *		arg2 (I)	int unused
11254:  *		arg3 (I)	int unused
11255:  * --------------------------------------------------------------------------
11256:  * Returns:	( subraster * )	ptr to subraster corresponding to \rotatebox
11257:  *				requested, or NULL for any parsing error
11258:  * --------------------------------------------------------------------------
11259:  * Notes:     o	Summary of syntax...
11260:  *		  \rotatebox{degrees}{subexpression}
11261:  *	      o
11262:  * ======================================================================= */
11263: /* --- entry point --- */
11264: subraster *rastrotate ( char **expression, int size, subraster *basesp,
11265: 			int arg1, int arg2, int arg3 )
11266: {
11267: /* -------------------------------------------------------------------------
11268: Allocations and Declarations
11269: -------------------------------------------------------------------------- */
11270: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
11271: subraster *rasterize(), *rotsp=NULL;	/* subraster for rotated subexpr */
11272: raster	*rastrot(), *rotrp=NULL;	/* rotate subraster->image 90 degs */
11273: int	delete_raster();		/* delete intermediate rasters */
11274: int	baseline=0;			/* baseline of rasterized image */
11275: double	degrees=0.0, ipart,fpart;	/* degrees to be rotated */
11276: int	idegrees=0, isneg=0;		/* positive ipart, isneg=1 if neg */
11277: int	n90=0, isn90=1;			/* degrees is n90 multiples of 90 */
11278: int	evalterm();			/* evaluate [arg],{arg} expressions*/
11279: /* -------------------------------------------------------------------------
11280: obtain {degrees} argument immediately following \rotatebox command
11281: -------------------------------------------------------------------------- */
11282: /* --- parse for {degrees} arg, and bump expression past it --- */
11283: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
11284: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
11285: degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
11286: if ( degrees < 0.0 )			/* clockwise rotation desired */
11287:   { degrees = -degrees;			/* flip sign so degrees positive */
11288:     isneg = 1; }			/* and set flag to indicate flip */
11289: fpart = modf(degrees,&ipart);		/* integer and fractional parts */
11290: ipart = (double)(((int)degrees)%360);	/* degrees mod 360 */
11291: degrees = ipart + fpart;		/* restore fractional part */
11292: if ( isneg )				/* if clockwise rotation requested */
11293:   degrees = 360.0 - degrees;		/* do equivalent counterclockwise */
11294: idegrees = (int)(degrees+0.5);		/* integer degrees */
11295: n90 = idegrees/90;			/* degrees is n90 multiples of 90 */
11296: isn90 = (90*n90==idegrees);		/*true if degrees is multiple of 90*/
11297: isn90 = 1;				/* forced true for time being */
11298: /* -------------------------------------------------------------------------
11299: obtain {subexpr} argument after {degrees}, and rasterize it
11300: -------------------------------------------------------------------------- */
11301: /* --- parse for {subexpr} arg, and bump expression past it --- */
11302: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11303: /* --- rasterize subexpression to be rotated --- */
11304: if ( (rotsp = rasterize(subexpr,size))	/* rasterize subexpression */
11305: ==   NULL ) goto end_of_job;		/* and quit if failed */
11306: /* --- return unmodified image if no rotation requested --- */
11307: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
11308: /* --- extract params for image to be rotated --- */
11309: rotrp = rotsp->image;			/* unrotated rasterized image */
11310: baseline = rotsp->baseline;		/* and baseline of that image */
11311: /* -------------------------------------------------------------------------
11312: rotate by multiples of 90 degrees
11313: -------------------------------------------------------------------------- */
11314: if ( isn90 )				/* rotation by multiples of 90 */
11315:  if ( n90 > 0 )				/* do nothing for 0 degrees */
11316:   {
11317:   n90 = 4-n90;				/* rasrot() rotates clockwise */
11318:   while ( n90 > 0 )			/* still have remaining rotations */
11319:     { raster *nextrp = rastrot(rotrp);	/* rotate raster image */
11320:       if ( nextrp == NULL ) break;	/* something's terribly wrong */
11321:       delete_raster(rotrp);		/* free previous raster image */
11322:       rotrp = nextrp;			/* and replace it with rotated one */
11323:       n90--; }				/* decrement remaining count */
11324:   } /* --- end-of-if(isn90) --- */
11325: /* -------------------------------------------------------------------------
11326: requested rotation not multiple of 90 degrees
11327: -------------------------------------------------------------------------- */
11328: if ( !isn90 )				/* explicitly construct rotation */
11329:   { ; }					/* not yet implemented */
11330: /* -------------------------------------------------------------------------
11331: re-populate subraster envelope with rotated image
11332: -------------------------------------------------------------------------- */
11333: /* --- re-init various subraster parameters, embedding raster in it --- */
11334: if ( rotrp != NULL )			/* rotated raster constructed okay */
11335:  { rotsp->type = IMAGERASTER;		/* signal constructed image */
11336:    rotsp->image = rotrp;		/* raster we just constructed */
11337:    /* --- now try to guess pleasing baseline --- */
11338:    if ( idegrees > 2 ) {		/* leave unchanged if unrotated */
11339:     if ( strlen(subexpr) < 3		/* we rotated a short expression */
11340:     ||   abs(idegrees-180) < 3 )	/* or just turned it upside-down */
11341:       baseline = rotrp->height - 1;	/* so set with nothing descending */
11342:     else				/* rotated a long expression */
11343:       baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
11344:    rotsp->baseline = baseline; }	/* set baseline as calculated above*/
11345: /* --- return rotated subexpr to caller --- */
11346: end_of_job:
11347:   return ( rotsp );			/*return rotated subexpr to caller*/
11348: } /* --- end-of-function rastrotate() --- */
11349: 
11350: 
11351: /* ==========================================================================
11352:  * Function:	rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
11353:  * Purpose:	\magnify{magstep}{subexpression} handler, returns subraster
11354:  *		containing magnified subexpression
11355:  * --------------------------------------------------------------------------
11356:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11357:  *				string immediately following \reflectbox to
11358:  *				be rasterized, and returning ptr immediately
11359:  *				following last character processed.
11360:  *		size (I)	int containing 0-7 default font size
11361:  *		basesp (I)	subraster *  to character (or subexpression)
11362:  *				immediately preceding \reflectbox
11363:  *				(unused, but passed for consistency)
11364:  *		arg1 (I)	int unused
11365:  *		arg2 (I)	int unused
11366:  *		arg3 (I)	int unused
11367:  * --------------------------------------------------------------------------
11368:  * Returns:	( subraster * )	ptr to subraster corresponding to \magnify
11369:  *				requested, or NULL for any parsing error
11370:  * --------------------------------------------------------------------------
11371:  * Notes:     o	Summary of syntax...
11372:  *		  \magnify{magstep}{subexpression}
11373:  *	      o
11374:  * ======================================================================= */
11375: /* --- entry point --- */
11376: subraster *rastmagnify ( char **expression, int size, subraster *basesp,
11377: 			int arg1, int arg2, int arg3 )
11378: {
11379: /* -------------------------------------------------------------------------
11380: Allocations and Declarations
11381: -------------------------------------------------------------------------- */
11382: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
11383: subraster *rasterize(), *magsp=NULL;	/* subraster for magnified subexpr */
11384: raster	*rastmag(), *magrp=NULL;	/* magnify subraster->image */
11385: int	magstep = 1;			/* default magnification */
11386: int	delete_raster();		/* delete intermediate raster */
11387: int	baseline=0;			/* baseline of rasterized image */
11388: /* -------------------------------------------------------------------------
11389: obtain {magstep} argument immediately following \magnify command
11390: -------------------------------------------------------------------------- */
11391: /* --- parse for {magstep} arg, and bump expression past it --- */
11392: *expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
11393: magstep = atoi(magexpr);		/* convert {magstep} to int */
11394: if ( magstep<1 || magstep>10 )		/* check magstep input */
11395:   magstep = 1;				/* back to default if illegal */
11396: /* -------------------------------------------------------------------------
11397: obtain {subexpr} argument after {magstep}, and rasterize it
11398: -------------------------------------------------------------------------- */
11399: /* --- parse for {subexpr} arg, and bump expression past it --- */
11400: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11401: /* --- rasterize subexpression to be reflected --- */
11402: if ( (magsp = rasterize(subexpr,size))	/* rasterize subexpression */
11403: ==   NULL ) goto end_of_job;		/* and quit if failed */
11404: /* --- return unmodified image if no magnification requested --- */
11405: if ( magstep<=1 ) goto end_of_job;	/* don't bother magnifying image */
11406: /* --- extract params for image to be magnified --- */
11407: magrp = magsp->image;			/* unmagnified rasterized image */
11408: baseline = magsp->baseline;		/* and baseline of that image */
11409: /* -------------------------------------------------------------------------
11410: magnify image and adjust its parameters
11411: -------------------------------------------------------------------------- */
11412: /* --- magnify image --- */
11413: magrp = rastmag(magsp->image,magstep);	/* magnify raster image */
11414: if ( magrp == NULL ) goto end_of_job;	/* failed to magnify image */
11415: delete_raster(magsp->image);		/* free original raster image */
11416: magsp->image = magrp;			/*and replace it with magnified one*/
11417: /* --- adjust parameters --- */
11418: baseline *= magstep;			/* scale baseline */
11419: if ( baseline > 0 ) baseline += 1;	/* adjust for no descenders */
11420: magsp->baseline = baseline;		/*reset baseline of magnified image*/
11421: /* --- return magnified subexpr to caller --- */
11422: end_of_job:
11423:   return ( magsp );			/*back to caller with magnified expr*/
11424: } /* --- end-of-function rastmagnify() --- */
11425: 
11426: 
11427: /* ==========================================================================
11428:  * Function:	rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
11429:  * Purpose:	\reflectbox[axis]{subexpression} handler, returns subraster
11430:  *		containing subexpression reflected horizontally (i.e., around
11431:  *		vertical axis, |_ becomes _|) if [axis] not given or axis=1,
11432:  *		or reflected vertically if axis=2 given.
11433:  * --------------------------------------------------------------------------
11434:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11435:  *				string immediately following \reflectbox to
11436:  *				be rasterized, and returning ptr immediately
11437:  *				following last character processed.
11438:  *		size (I)	int containing 0-7 default font size
11439:  *		basesp (I)	subraster *  to character (or subexpression)
11440:  *				immediately preceding \reflectbox
11441:  *				(unused, but passed for consistency)
11442:  *		arg1 (I)	int unused
11443:  *		arg2 (I)	int unused
11444:  *		arg3 (I)	int unused
11445:  * --------------------------------------------------------------------------
11446:  * Returns:	( subraster * )	ptr to subraster corresponding to \reflectbox
11447:  *				requested, or NULL for any parsing error
11448:  * --------------------------------------------------------------------------
11449:  * Notes:     o	Summary of syntax...
11450:  *		  \reflectbox[axis]{subexpression}
11451:  *	      o
11452:  * ======================================================================= */
11453: /* --- entry point --- */
11454: subraster *rastreflect ( char **expression, int size, subraster *basesp,
11455: 			int arg1, int arg2, int arg3 )
11456: {
11457: /* -------------------------------------------------------------------------
11458: Allocations and Declarations
11459: -------------------------------------------------------------------------- */
11460: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
11461: subraster *rasterize(), *refsp=NULL;	/* subraster for reflected subexpr */
11462: raster	*rastref(), *refrp=NULL;	/* reflect subraster->image */
11463: int	axis = 1;			/* default horizontal reflection */
11464: int	delete_raster();		/* delete intermediate raster */
11465: int	baseline=0;			/* baseline of rasterized image */
11466: /* -------------------------------------------------------------------------
11467: obtain [axis] argument immediately following \reflectbox command, if given
11468: -------------------------------------------------------------------------- */
11469: /* --- check for optional [axis] arg  --- */
11470: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
11471:   { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
11472:     axis = atoi(axisexpr);		/* convert [axis] to int */
11473:     if ( axis<1 || axis>2 )		/* check axis input */
11474:       axis = 1; }			/* back to default if illegal */
11475: /* -------------------------------------------------------------------------
11476: obtain {subexpr} argument after optional [axis], and rasterize it
11477: -------------------------------------------------------------------------- */
11478: /* --- parse for {subexpr} arg, and bump expression past it --- */
11479: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11480: /* --- rasterize subexpression to be reflected --- */
11481: if ( (refsp = rasterize(subexpr,size))	/* rasterize subexpression */
11482: ==   NULL ) goto end_of_job;		/* and quit if failed */
11483: /* --- return unmodified image if no reflection requested --- */
11484: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
11485: /* --- extract params for image to be reflected --- */
11486: refrp = refsp->image;			/* unreflected rasterized image */
11487: baseline = refsp->baseline;		/* and baseline of that image */
11488: /* -------------------------------------------------------------------------
11489: reflect image and adjust its parameters
11490: -------------------------------------------------------------------------- */
11491: /* --- reflect image --- */
11492: refrp = rastref(refsp->image,axis);	/* reflect raster image */
11493: if ( refrp == NULL ) goto end_of_job;	/* failed to reflect image */
11494: delete_raster(refsp->image);		/* free original raster image */
11495: refsp->image = refrp;			/*and replace it with reflected one*/
11496: /* --- adjust parameters --- */
11497: if ( axis == 2 )			/* for vertical reflection */
11498:   baseline = refrp->height - 1;		/* set with nothing descending */
11499: refsp->baseline = baseline;		/* reset baseline of reflected image*/
11500: /* --- return reflected subexpr to caller --- */
11501: end_of_job:
11502:   return ( refsp );			/*back to caller with reflected expr*/
11503: } /* --- end-of-function rastreflect() --- */
11504: 
11505: 
11506: /* ==========================================================================
11507:  * Function:	rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
11508:  * Purpose:	\fbox{subexpression} handler, returns subraster
11509:  *		containing subexpression with frame box drawn around it
11510:  * --------------------------------------------------------------------------
11511:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11512:  *				string immediately following \fbox to be
11513:  *				rasterized, and returning ptr immediately
11514:  *				following last character processed.
11515:  *		size (I)	int containing 0-7 default font size
11516:  *		basesp (I)	subraster *  to character (or subexpression)
11517:  *				immediately preceding \fbox
11518:  *				(unused, but passed for consistency)
11519:  *		arg1 (I)	int unused
11520:  *		arg2 (I)	int unused
11521:  *		arg3 (I)	int unused
11522:  * --------------------------------------------------------------------------
11523:  * Returns:	( subraster * )	ptr to subraster corresponding to \fbox
11524:  *				requested, or NULL for any parsing error
11525:  * --------------------------------------------------------------------------
11526:  * Notes:     o	Summary of syntax...
11527:  *		  \fbox[width][height]{subexpression}
11528:  *	      o
11529:  * ======================================================================= */
11530: /* --- entry point --- */
11531: subraster *rastfbox ( char **expression, int size, subraster *basesp,
11532: 			int arg1, int arg2, int arg3 )
11533: {
11534: /* -------------------------------------------------------------------------
11535: Allocations and Declarations
11536: -------------------------------------------------------------------------- */
11537: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
11538: subraster *rasterize(), *framesp=NULL;	/* rasterize subexpr to be framed */
11539: raster	*border_raster(), *bp=NULL;	/* framed image raster */
11540: int	evalterm(), evalue=0;		/* interpret [width][height] */
11541: int	fwidth=6, fthick=1,		/*extra frame width, line thickness*/
11542: 	fsides=0;		/* frame sides: 1=left,2=top,4=right,8=bot */
11543: int	width=(-1), height=(-1),	/* optional [width][height] args */
11544: 	iscompose = 0;			/* set true if optional args given */
11545: /* -------------------------------------------------------------------------
11546: obtain optional [width][height] arguments immediately following \fbox
11547: -------------------------------------------------------------------------- */
11548: /* --- first check for optional \fbox[width] --- */
11549: if ( *(*expression) == '[' ) {		/* check for []-enclosed width arg */
11550:   *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
11551:   if ( !isempty(widtharg) ) {		/* got widtharg */
11552:     char *comma = strchr(widtharg,',');	/* look for [width,sides] */
11553:     if ( comma == (char *)NULL )	/* no comma */
11554:       comma = strchr(widtharg,';');	/* permit semicolon [width;sides] */
11555:     if ( comma != (char *)NULL ) {	/* optional [width,fsides] found */
11556:       fsides = atoi(comma+1);		/* interpret fsides after comma */
11557:       if ( size < 5 )			/* for smaller fonts */
11558:         { fwidth = 2;  fthick = 1; }	/* tighten frame, thinner accent */
11559:       else { fwidth = 3;  fthick = 2; }	/* loosen frame, thicken accent */
11560:       *comma = '\000';			/* null-terminate width at comma */
11561:       trimwhite(widtharg); }		/*remove leading/trailing whitespace*/
11562:     if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
11563:       height = 1;			/* default explicit height, too */
11564:       if ( fsides == 0 ) {		/* a normal framebox */
11565: 	evalue = eround(widtharg);	/* interpret and scale width */
11566:         width = max2(1,evalue);		/* must be >0 */
11567:         fwidth = 2; iscompose = 1; }
11568:       else				/* absolute pixels for "accents" */
11569: 	width = evalterm(mimestore,widtharg); }
11570:     } /* --- end-of-if(!isempty(widtharg)) --- */
11571:   } /* --- end-of-if(**expression=='[') --- */
11572: if ( width > 0 || fsides > 0)		/* found leading [width], so... */
11573:  if ( *(*expression) == '[' )		/* check for []-enclosed height arg */
11574:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
11575:     if ( !isempty(widtharg) ) {		/* got widtharg */
11576:       if ( fsides == 0 ) {		/* a normal framebox */
11577: 	evalue = eround(widtharg);	/* interpret and scale height */
11578:         height = max2(1,evalue);	/* must be >0 */
11579:         fwidth = 0; }			/* no extra border */
11580:       else				/* absolute pixels for "accents" */
11581: 	height = evalterm(mimestore,widtharg); }
11582:   } /* --- end-of-if(**expression=='[') --- */
11583: /* -------------------------------------------------------------------------
11584: obtain {subexpr} argument
11585: -------------------------------------------------------------------------- */
11586: /* --- parse for {subexpr} arg, and bump expression past it --- */
11587: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11588: /* --- rasterize subexpression to be framed --- */
11589: if ( width<0 || height<0 )		/* no explicit dimensions given */
11590:   { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
11591:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
11592: else
11593:   { char composexpr[8192];		/* compose subexpr with empty box */
11594:     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
11595:     width,height,subexpr);
11596:     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
11597:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
11598: /* -------------------------------------------------------------------------
11599: draw frame, reset params, and return it to caller
11600: -------------------------------------------------------------------------- */
11601: /* --- draw border --- */
11602: if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
11603: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
11604: ==   NULL ) goto end_of_job;		/* draw border and quit if failed */
11605: /* --- replace original image and raise baseline to accommodate frame --- */
11606: framesp->image = bp;			/* replace image with framed one */
11607: if ( !iscompose )			/* simple border around subexpr */
11608:   framesp->baseline += fwidth;		/* so just raise baseline */
11609: else
11610:   framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
11611: /* --- return framed subexpr to caller --- */
11612: end_of_job:
11613:   return ( framesp );			/* return framed subexpr to caller */
11614: } /* --- end-of-function rastfbox() --- */
11615: 
11616: 
11617: /* ==========================================================================
11618:  * Function:	rastinput ( expression, size, basesp, arg1, arg2, arg3 )
11619:  * Purpose:	\input{filename} handler, reads filename and returns
11620:  *		subraster containing image of expression read from filename
11621:  * --------------------------------------------------------------------------
11622:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11623:  *				string immediately following \input to be
11624:  *				rasterized, and returning ptr immediately
11625:  *				following last character processed.
11626:  *		size (I)	int containing 0-7 default font size
11627:  *		basesp (I)	subraster *  to character (or subexpression)
11628:  *				immediately preceding \input
11629:  *				(unused, but passed for consistency)
11630:  *		arg1 (I)	int unused
11631:  *		arg2 (I)	int unused
11632:  *		arg3 (I)	int unused
11633:  * --------------------------------------------------------------------------
11634:  * Returns:	( subraster * )	ptr to subraster corresponding to expression
11635:  *				in filename, or NULL for any parsing error
11636:  * --------------------------------------------------------------------------
11637:  * Notes:     o	Summary of syntax...
11638:  *		  \input{filename}     reads entire file named filename
11639:  *		  \input{filename:tag} reads filename, but returns only
11640:  *		  those characters between <tag>...</tag> in that file.
11641:  *	      o
11642:  * ======================================================================= */
11643: /* --- entry point --- */
11644: subraster *rastinput ( char **expression, int size, subraster *basesp,
11645: 			int arg1, int arg2, int arg3 )
11646: {
11647: /* -------------------------------------------------------------------------
11648: Allocations and Declarations
11649: -------------------------------------------------------------------------- */
11650: char	*texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
11651: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
11652: int	status, rastreadfile();	/* read input file */
11653: int	format=0, npts=0;	/* don't reformat (numerical) input */
11654: int	isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
11655: /*int	evalterm();*/		/* evaluate expressions */
11656: char	*inputpath = INPUTPATH;	/* permitted \input{} paths for any user */
11657: int	isstrstr();		/* search for valid inputpath in filename */
11658: char	subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
11659: 	*mimeprep(),		/* preprocess inputted data */
11660: 	*dbltoa(), *reformat=NULL; /* reformat numerical input */
11661: /* -------------------------------------------------------------------------
11662: obtain [tag]{filename} argument
11663: -------------------------------------------------------------------------- */
11664: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
11665: if ( *(*expression) == '[' )		/* check for []-enclosed value */
11666:   { char argfld[MAXTOKNSZ+1];		/* optional argument field */
11667:     *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
11668:     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
11669:       {	format = 1;			/* signal dtoa()/dbltoa() format */
11670: 	if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
11671: 	  npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
11672:     if ( format == 0 ) {		/* reformat not requested */
11673:       strninit(tag,argfld,1020); } }	/* so interpret arg as tag */
11674: /* --- parse for {filename} arg, and bump expression past it --- */
11675: *expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
11676: /* --- check for alternate filename:tag --- */
11677: if ( !isempty(filename)			/* got filename */
11678: /*&& isempty(tag)*/ )			/* but no [tag] */
11679:  { char	*delim = strchr(filename,':');	/* look for : in filename:tag */
11680:    if ( delim != (char *)NULL )		/* found it */
11681:     { *delim = '\000';			/* null-terminate filename at : */
11682:       strninit(tag,delim+1,1020); } }	/* and stuff after : is tag */
11683: /* --- check filename for an inputpath valid for all users --- */
11684: if ( !isinput				/* if this user can't \input{} */
11685: &&   !isempty(filename)			/* and we got a filename */
11686: &&   !isempty(inputpath) )		/* and an inputpath */
11687:   if ( isstrstr(filename,inputpath,0) )	/* filename has allowed inputpath */
11688:     isinput = 1;			/* okay to \input{} this filename */
11689: /* --- guard against recursive runaway (e.g., file \input's itself) --- */
11690: if ( ++ninputcmds > 8 )			/* max \input's per expression */
11691:   isinput = 0;				/* flip flag off after the max */
11692: /* --------------------------------------------------------------------------
11693: Read file (and convert to numeric if [dtoa] option was given)
11694: -------------------------------------------------------------------------- */
11695: if ( isinput ) {			/* user permitted to use \input{} */
11696:   status = rastreadfile(filename,0,tag,subexpr); /* read file */
11697:   if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
11698:   /* --- rasterize input subexpression  --- */
11699:   mimeprep(subexpr);			/* preprocess subexpression */
11700:   if ( format == 1 ) {			/* dtoa()/dbltoa() */
11701:     double d = strtod(subexpr,NULL);	/* interpret subexpr as double */
11702:     if ( d != 0.0 )			/* conversion to double successful */
11703:       if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
11704:         strcpy(subexpr,reformat); }	/*replace subexpr with reformatted*/
11705:   } /* --- end-of-if(isinput) --- */
11706: /* --------------------------------------------------------------------------
11707: emit error message for unauthorized users trying to use \input{}
11708: -------------------------------------------------------------------------- */
11709: else {					/* inputseclevel > seclevel */
11710:   sprintf(subexpr,
11711:   "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
11712:   (isempty(filename)?"???":filename));
11713:   } /* --- end-of-if/else(isinput) --- */
11714: /* --------------------------------------------------------------------------
11715: Rasterize constructed subexpression
11716: -------------------------------------------------------------------------- */
11717: inputsp = rasterize(subexpr,size);	/* rasterize subexpression */
11718: /* --- return input image to caller --- */
11719: end_of_job:
11720:   return ( inputsp );			/* return input image to caller */
11721: } /* --- end-of-function rastinput() --- */
11722: 
11723: 
11724: /* ==========================================================================
11725:  * Function:	rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
11726:  * Purpose:	\counter[value]{filename} handler, returns subraster
11727:  *		containing image of counter value read from filename
11728:  *		(or optional [value]), and increments counter
11729:  * --------------------------------------------------------------------------
11730:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11731:  *				string immediately following \counter to be
11732:  *				rasterized, and returning ptr immediately
11733:  *				following last character processed.
11734:  *		size (I)	int containing 0-7 default font size
11735:  *		basesp (I)	subraster *  to character (or subexpression)
11736:  *				immediately preceding \counter
11737:  *				(unused, but passed for consistency)
11738:  *		arg1 (I)	int unused
11739:  *		arg2 (I)	int unused
11740:  *		arg3 (I)	int unused
11741:  * --------------------------------------------------------------------------
11742:  * Returns:	( subraster * )	ptr to subraster corresponding to \counter
11743:  *				requested, or NULL for any parsing error
11744:  * --------------------------------------------------------------------------
11745:  * Notes:     o	Summary of syntax...
11746:  *		  \counter[value][logfile]{filename:tag}
11747:  *	      o	:tag is optional
11748:  * ======================================================================= */
11749: /* --- entry point --- */
11750: subraster *rastcounter ( char **expression, int size, subraster *basesp,
11751: 			int arg1, int arg2, int arg3 )
11752: {
11753: /* -------------------------------------------------------------------------
11754: Allocations and Declarations
11755: -------------------------------------------------------------------------- */
11756: char	*texsubexpr(), filename[1024]="\000", /* counter file */
11757: 	logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
11758: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
11759: FILE	/* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
11760: int	status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
11761: 	iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
11762: 	isstrict = 1;		/* true to only write to existing files */
11763: char	text[MAXFILESZ] = "1_",	/* only line in counter file without tags */
11764: 	*delim = NULL,		/* delimiter in text */
11765: 	utext[128] = "1_",	/* default delimiter */
11766: 	*udelim = utext+1;	/* underscore delimiter */
11767: char	*rasteditfilename(),	/* edit log file name */
11768: 	*timestamp(),		/* timestamp for logging */
11769: 	*dbltoa();		/* double to comma-separated ascii */
11770: int	counter = 1,		/* atoi(text) (after _ removed, if present) */
11771: 	value = 1,		/* optional [value] argument */
11772: 	gotvalue = 0,		/* set true if [value] supplied */
11773: 	isdelta = 0,		/* set true if [+value] or [-value] is delta*/
11774: 	ordindex = (-1);	/* ordinal[] index to append ordinal suffix */
11775: /*--- ordinal suffixes based on units digit of counter ---*/
11776: static	char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
11777: static	char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
11778: static	int  commentvar = 1;	/* logvars[commentvar] replaced by comment */
11779: /* -------------------------------------------------------------------------
11780: first obtain optional [value][logfile] args immediately following \counter
11781: -------------------------------------------------------------------------- */
11782: /* --- first check for optional \counter[value] --- */
11783: if ( *(*expression) == '[' )		/* check for []-enclosed value */
11784:   { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
11785:     if ( *text != '\000' )		/* got counter value (or logfile) */
11786:      if ( strlen(text) >= 1 ) {		/* and it's not an empty string */
11787:       if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
11788: 	gotvalue = 1;			/* signal we got optional value */
11789:       else				/* not +-digit, so must be logfile */
11790: 	strcpy(logfile,text); }		/* so just copy it */
11791:   } /* --- end-of-if(**expression=='[') --- */
11792: /* --- next check for optional \counter[][logfile] --- */
11793: if ( *(*expression) == '[' )		/* check for []-enclosed logfile */
11794:   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
11795:     if ( *filename != '\000' )		/* got logfile (or counter value) */
11796:      if ( strlen(filename) >= 1 ) {	/* and it's not an empty string */
11797:       if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
11798:       ||   gotvalue )			/* or we already got counter value */
11799: 	strcpy(logfile,filename);	/* so just copy it */
11800:       else				/* leading +-digit must be value */
11801: 	{ strcpy(text,filename);	/* copy value to text line */
11802: 	  gotvalue = 1; } }		/* and signal we got optional value*/
11803:   } /* --- end-of-if(**expression=='[') --- */
11804: /* --- evaluate [value] if present --- */
11805: if ( gotvalue ) {			/*leading +-digit should be in text*/
11806:  if ( *text == '+' ) isdelta = (+1);	/* signal adding */
11807:  if ( *text == '-' ) isdelta = (-1);	/* signal subtracting */
11808:  value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
11809:  if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
11810:  counter = value;			/* re-init counter */
11811:  } /* --- end-of-if(gotvalue) --- */
11812: /* -------------------------------------------------------------------------
11813: obtain counter {filename} argument
11814: -------------------------------------------------------------------------- */
11815: /* --- parse for {filename} arg, and bump expression past it --- */
11816: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
11817: /* --- check for counter filename:tag --- */
11818: if ( *filename != '\000' )		/* got filename */
11819:  if ( (delim=strchr(filename,':'))	/* look for : in filename:tag */
11820:  !=   (char *)NULL )			/* found it */
11821:   { *delim = '\000';			/* null-terminate filename at : */
11822:     strcpy(tag,delim+1); }		/* and stuff after : is tag */
11823: /* --------------------------------------------------------------------------
11824: emit error message for unauthorized users trying to use \counter{}
11825: -------------------------------------------------------------------------- */
11826: if ( !iscounter ) {			/* counterseclevel > seclevel */
11827:  sprintf(text,
11828:  "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
11829:  (isempty(filename)?"???":filename));
11830:  goto rasterize_counter;		/* rasterize error message */
11831:  } /* --- end-of-if(!iscounter) --- */
11832: /* --------------------------------------------------------------------------
11833: Read and parse file, increment and rewrite counter (with optional underscore)
11834: -------------------------------------------------------------------------- */
11835: if ( strlen(filename) > 1 )		/* make sure we got {filename} arg */
11836:   {
11837:   /* --- read and interpret first (and only) line from counter file --- */
11838:   if ( !gotvalue || (isdelta!=0) )	/*if no [count] arg or if delta arg*/
11839:    if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
11840:     { char *vdelim = NULL;		/* underscore delim from file */
11841:       double fileval  = strtod(text,&vdelim); /* value and delim from file */
11842:       counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
11843:       counter += value;			/* bump count by 1 or add/sub delta*/
11844:       if ( !gotvalue ) udelim=vdelim; }	/* default to file's current delim */
11845:   /* --- check for ordinal suffix --- */
11846:   if ( udelim != (char *)NULL )		/* have some delim after value */
11847:    if ( *udelim == '_' )		/* underscore signals ordinal */
11848:     { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
11849:       ordindex = abscount%10;		/* least significant digit */
11850:       if ( abscount >= 10 )		/* counter is 10 or greater */
11851:        if ( (abscount/10)%10 == 1 )	/* and the last two are 10-19 */
11852: 	ordindex = 0; }		/* use th for 11,12,13 rather than st,nd,rd */
11853:   /* --- rewrite counter file --- */
11854:   if ( status >= 0 )			/* file was read okay */
11855:    { sprintf(text,"%d",counter);	/*build image of incremented counter*/
11856:      if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
11857:      if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
11858:      status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
11859:   } /* --- end-of-if(strlen(filename)>1) --- */
11860: /* --------------------------------------------------------------------------
11861: log counter request
11862: -------------------------------------------------------------------------- */
11863: if ( strlen(logfile) > 1 )		/* optional [logfile] given */
11864:  {
11865:  char	comment[1024] = "\000",		/* embedded comment, logfile:comment*/
11866: 	*commptr = strchr(logfile,':');	/* check for : signalling comment */
11867:  int	islogokay = 1;			/* logfile must exist if isstrict */
11868:  if ( commptr != NULL )			/* have embedded comment */
11869:   { strcpy(comment,commptr+1);		/* comment follows : */
11870:     *commptr = '\000'; }		/* null-terminate actual logfile */
11871:  strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
11872:  if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
11873:  else if ( isstrict ) {			/*okay, but only write if it exists*/
11874:   if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
11875:     islogokay = 0;			/* so don't write log file */
11876:   else fclose(logfp); }			/* close file opened for test read */
11877:  if ( islogokay )			/* okay to write logfile */
11878:   if ( (logfp = fopen(logfile,"a"))	/* open logfile */
11879:   != (FILE *)NULL ) {			/* opened successfully for append */
11880:    int	ilog=0;				/* logvars[] index */
11881:    fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
11882:    if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
11883:    else fprintf(logfp,"<%s>",tag);	/* or tag if we have one */
11884:    fprintf(logfp,"=%d",counter);	/* emit counter value */
11885:    if ( status < 1 )			/* read or re-write failed */
11886:     fprintf(logfp,"(%s %d)","error status",status); /* emit error */
11887:    for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
11888:     if ( ilog == commentvar		/* replace with comment... */
11889:     &&   commptr != NULL )		/* ...if available */  
11890:      fprintf(logfp,"  %.256s",comment); /* log embedded comment */
11891:     else
11892:      { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
11893:        fprintf(logfp,"  %.64s",		/* log variable */
11894: 	(logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
11895:    fprintf(logfp,"\n");			/* terminating newline */
11896:    fclose(logfp);			/* close logfile */
11897:    } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
11898:  } /* --- end-of-if(strlen(logfile)>1) --- */
11899: /* --------------------------------------------------------------------------
11900: construct counter expression and rasterize it
11901: -------------------------------------------------------------------------- */
11902: /* --- construct expression --- */
11903: /*sprintf(text,"%d",counter);*/		/* start with counter */
11904: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
11905: if ( ordindex >= 0 )			/* need to tack on ordinal suffix */
11906:   { strcat(text,"^{\\underline{\\rm~");	/* start with ^ and {\underline{\rm */
11907:     strcat(text,ordinal[ordindex]);	/* then st,nd,rd, or th */
11908:     strcat(text,"}}"); }		/* finish with }} */
11909: /* --- rasterize it --- */
11910: rasterize_counter:
11911:   countersp = rasterize(text,size);	/* rasterize counter subexpression */
11912: /* --- return counter image to caller --- */
11913: /*end_of_job:*/
11914:   return ( countersp );			/* return counter image to caller */
11915: } /* --- end-of-function rastcounter() --- */
11916: 
11917: 
11918: /* ==========================================================================
11919:  * Function:	rasteval ( expression, size, basesp, arg1, arg2, arg3 )
11920:  * Purpose:	handle \eval
11921:  * --------------------------------------------------------------------------
11922:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11923:  *				string immediately following \eval,
11924:  *				and returning ptr immediately
11925:  *				following last character processed.
11926:  *		size (I)	int containing 0-7 default font size
11927:  *		basesp (I)	subraster *  to character (or subexpression)
11928:  *				immediately preceding \eval
11929:  *				(unused, but passed for consistency)
11930:  *		arg1 (I)	int unused
11931:  *		arg2 (I)	int unused
11932:  *		arg3 (I)	int unused
11933:  * --------------------------------------------------------------------------
11934:  * Returns:	( subraster * )	subraster ptr to date stamp
11935:  * --------------------------------------------------------------------------
11936:  * Notes:     o
11937:  * ======================================================================= */
11938: /* --- entry point --- */
11939: subraster *rasteval ( char **expression, int size, subraster *basesp,
11940: 			int arg1, int arg2, int arg3 )
11941: {
11942: /* -------------------------------------------------------------------------
11943: Allocations and Declarations
11944: -------------------------------------------------------------------------- */
11945: char	*texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
11946: subraster *rasterize(), *evalsp=NULL;	/* rasterize evaluated expression */
11947: int	evalterm(), value=0;		/* evaluate expression */
11948: /* -------------------------------------------------------------------------
11949: Parse for subexpr to be \eval-uated, and bump expression past it
11950: -------------------------------------------------------------------------- */
11951: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11952: if ( *subexpr == '\000' )		/* couldn't get subexpression */
11953:   goto end_of_job;			/* nothing to do, so quit */
11954: /* -------------------------------------------------------------------------
11955: Evaluate expression, ascii-ize integer result, rasterize it
11956: -------------------------------------------------------------------------- */
11957: /* --- evaluate expression --- */
11958: value = evalterm(mimestore,subexpr);	/* evaluate expression */
11959: /* --- ascii-ize it --- */
11960: sprintf(subexpr,"%d",value);		/* ascii version of value */
11961: /* --- rasterize ascii-ized expression value --- */
11962: evalsp = rasterize(subexpr,size);	/* rasterize evaluated expression */
11963: /* --- return evaluated expression raster to caller --- */
11964: end_of_job:
11965:   return ( evalsp );			/* return evaluated expr to caller */
11966: } /* --- end-of-function rasteval() --- */
11967: 
11968: 
11969: /* ==========================================================================
11970:  * Function:	rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
11971:  * Purpose:	handle \today
11972:  * --------------------------------------------------------------------------
11973:  * Arguments:	expression (I/O) char **  to first char of null-terminated
11974:  *				string immediately following \today,
11975:  *				and returning ptr immediately
11976:  *				following last character processed.
11977:  *		size (I)	int containing 0-7 default font size
11978:  *		basesp (I)	subraster *  to character (or subexpression)
11979:  *				immediately preceding \today
11980:  *				(unused, but passed for consistency)
11981:  *		arg1 (I)	int unused
11982:  *		arg2 (I)	int unused
11983:  *		arg3 (I)	int unused
11984:  * --------------------------------------------------------------------------
11985:  * Returns:	( subraster * )	subraster ptr to date stamp
11986:  * --------------------------------------------------------------------------
11987:  * Notes:     o
11988:  * ======================================================================= */
11989: /* --- entry point --- */
11990: subraster *rasttoday ( char **expression, int size, subraster *basesp,
11991: 			int arg1, int arg2, int arg3 )
11992: {
11993: /* -------------------------------------------------------------------------
11994: Allocations and Declarations
11995: -------------------------------------------------------------------------- */
11996: char	*texsubexpr(), optarg[2050];	/* optional [+/-tzdelta,ifmt] args */
11997: char	*timestamp(), *today=optarg;	/* timestamp to be rasterized */
11998: subraster *rasterize(), *todaysp=NULL;	/* rasterize timestamp */
11999: int	ifmt=1, tzdelta=0;		/* default timestamp() args */
12000: /* -------------------------------------------------------------------------
12001: Get optional args \today[+/-tzdelta,ifmt]
12002: -------------------------------------------------------------------------- */
12003: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
12004: if ( *(*expression) == '[' )		/* check for []-enclosed value */
12005:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
12006:     if ( *optarg != '\000' )		/* got optional arg */
12007:      { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
12008:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
12009:        if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
12010:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
12011: 	{ char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
12012: 	  if ( isthischar(*arg,"+-") )	/* leading +/- signals tzdelta */
12013: 	    tzdelta = atoi(arg);	/* so interpret arg as tzdelta */
12014: 	  else ifmt = atoi(arg); }	/* else interpret args as ifmt */
12015:      } /* --- end-of-if(*optarg!='\0') --- */
12016:   } /* --- end-of-if(**expression=='[') --- */
12017: /* -------------------------------------------------------------------------
12018: Get timestamp and rasterize it
12019: -------------------------------------------------------------------------- */
12020: strcpy(today,"\\text{");		/* rasterize timestamp as text */
12021: strcat(today,timestamp(tzdelta,ifmt));	/* get timestamp */
12022: strcat(today,"}");			/* terminate \text{} braces */
12023: todaysp = rasterize(today,size);	/* rasterize timestamp */
12024: /* --- return timestamp raster to caller --- */
12025: /*end_of_job:*/
12026:   return ( todaysp );			/* return timestamp to caller */
12027: } /* --- end-of-function rasttoday() --- */
12028: 
12029: 
12030: /* ==========================================================================
12031:  * Function:	rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
12032:  * Purpose:	handle \calendar
12033:  * --------------------------------------------------------------------------
12034:  * Arguments:	expression (I/O) char **  to first char of null-terminated
12035:  *				string immediately following \calendar
12036:  *				and returning ptr immediately
12037:  *				following last character processed.
12038:  *		size (I)	int containing 0-7 default font size
12039:  *		basesp (I)	subraster *  to character (or subexpression)
12040:  *				immediately preceding \calendar
12041:  *				(unused, but passed for consistency)
12042:  *		arg1 (I)	int unused
12043:  *		arg2 (I)	int unused
12044:  *		arg3 (I)	int unused
12045:  * --------------------------------------------------------------------------
12046:  * Returns:	( subraster * )	subraster ptr to rendered one-month calendar
12047:  * --------------------------------------------------------------------------
12048:  * Notes:     o
12049:  * ======================================================================= */
12050: /* --- entry point --- */
12051: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
12052: 			int arg1, int arg2, int arg3 )
12053: {
12054: /* -------------------------------------------------------------------------
12055: Allocations and Declarations
12056: -------------------------------------------------------------------------- */
12057: char	*texsubexpr(), optarg[2050];	/* optional [year,month] args */
12058: char	*calendar(), *calstr=NULL;	/* calendar to be rasterized */
12059: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
12060: int	year=0,month=0,day=0, argval=0;	/* default calendar() args */
12061: /* -------------------------------------------------------------------------
12062: Get optional args \today[+/-tzdelta,ifmt]
12063: -------------------------------------------------------------------------- */
12064: /* --- check for optional \calendar[year,month] --- */
12065: if ( *(*expression) == '[' )		/* check for []-enclosed value */
12066:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
12067:     if ( *optarg != '\000' )		/* got optional arg */
12068:      { char *comma = strchr(optarg,','), /* comma between year,month */
12069:        *comma2 = NULL;			/* second comma before day */
12070:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
12071:        if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
12072: 	if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
12073: 	 { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
12074:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
12075: 	{ char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
12076: 	  argval = atoi(arg);		/* interpret arg as integer */
12077: 	  if ( iarg < 3 )		/* first two args are month,year */
12078: 	   {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
12079: 	    else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
12080: 	  else				/* only 3rd arg can be day */
12081: 	   if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
12082:      } /* --- end-of-if(*optarg!='\0') --- */
12083:   } /* --- end-of-if(**expression=='[') --- */
12084: /* -------------------------------------------------------------------------
12085: Get calendar string and rasterize it
12086: -------------------------------------------------------------------------- */
12087: if ( msgfp!= NULL && msglevel>=9 )
12088:   fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
12089:   year,month,day);
12090: calstr = calendar(year,month,day);		/* get calendar string */
12091: calendarsp = rasterize(calstr,size);	/* rasterize calendar string */
12092: /* --- return calendar raster to caller --- */
12093: /*end_of_job:*/
12094:   return ( calendarsp );		/* return calendar to caller */
12095: } /* --- end-of-function rastcalendar() --- */
12096: 
12097: 
12098: /* ==========================================================================
12099:  * Function:	rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
12100:  * Purpose:	handle \environment
12101:  * --------------------------------------------------------------------------
12102:  * Arguments:	expression (I/O) char **  to first char of null-terminated
12103:  *				string immediately following \environment
12104:  *				and returning ptr immediately
12105:  *				following last character processed (in this
12106:  *				case, \environment takes no arguments, so
12107:  *				expression is returned unchanged).
12108:  *		size (I)	int containing 0-7 default font size
12109:  *		basesp (I)	subraster *  to character (or subexpression)
12110:  *				immediately preceding \environment
12111:  *				(unused, but passed for consistency)
12112:  *		arg1 (I)	int unused
12113:  *		arg2 (I)	int unused
12114:  *		arg3 (I)	int unused
12115:  * --------------------------------------------------------------------------
12116:  * Returns:	( subraster * )	subraster ptr to rendered environment image
12117:  * --------------------------------------------------------------------------
12118:  * Notes:     o
12119:  * ======================================================================= */
12120: /* --- entry point --- */
12121: subraster *rastenviron ( char **expression, int size, subraster *basesp,
12122: 			int arg1, int arg2, int arg3 )
12123: {
12124: /* -------------------------------------------------------------------------
12125: Allocations and Declarations
12126: -------------------------------------------------------------------------- */
12127: char	*texsubexpr(), optarg[255];	/* optional [...] args (for future)*/
12128: char	environstr[8192] = "\000",	/* string for all environment vars */
12129: 	environvar[1024] = "\000";	/* one environment variable */
12130: char	*strwrap(),			/* wrap long lines */
12131: 	*strdetex(),			/* removes/replaces any math chars */
12132: 	*environptr = NULL;		/* ptr to preprocessed environvar */
12133: char	*mimeprep();			/* preprocess environvar string */
12134: int	unescape_url();			/* convert all %xx's to chars */
12135: int	isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
12136: int	maxvarlen = 512,		/* max chars in environment var */
12137: 	maxenvlen = 6400,		/* max chars in entire string */
12138: 	wraplen = 48;			/* strwrap() wrap lines at 48 chars*/
12139: int	ienv = 0;			/* environ[] index */
12140: subraster *rasterize(), *environsp=NULL; /* rasterize environment string */
12141: /* -------------------------------------------------------------------------
12142: Get args 
12143: -------------------------------------------------------------------------- */
12144: /* --- check for optional \environment args --- */
12145: if ( 1 )				/* there aren't any args (yet) */
12146:  if ( *(*expression) == '[' ) {		/* check for []-enclosed value */
12147:    *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
12148:    if ( *optarg != '\000' ) { ;		/* got optional arg, so process it */
12149:      wraplen = atoi(optarg);		/* interpret \environment[wraplen] */
12150:      if ( wraplen < 1 ) wraplen = 8;	/* set minimum */
12151:      } /* --- end-of-if(*optarg!='\0') --- */
12152:    } /* --- end-of-if(**expression=='[') --- */
12153: /* --------------------------------------------------------------------------
12154: emit error message for unauthorized users trying to use \environ
12155: -------------------------------------------------------------------------- */
12156: if ( !isenviron ) {			/* environseclevel > seclevel */
12157:   sprintf(environstr,
12158:   "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
12159:   goto rasterize_environ;		/* rasterize error message */
12160:   } /* --- end-of-if(!isenviron) --- */
12161: /* -------------------------------------------------------------------------
12162: Accumulate environment variables and rasterize string containing them
12163: -------------------------------------------------------------------------- */
12164: *environstr = '\000';			/* reset environment string */
12165: strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
12166: for ( ienv=0; ; ienv++ ) {		/* loop over environ[] strings */
12167:   if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
12168:   if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
12169:   strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
12170:   if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
12171:     strcat(environvar,"...");		/* so add an ellipsis */
12172:   unescape_url(environvar,0);		/* convert all %xx's to chars */
12173:   environptr = strdetex(environvar,1);	/* remove/replace any math chars */
12174:   strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
12175:   environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
12176:   strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
12177:   mimeprep(environvar);			/* preprocess environvar string */
12178:   if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
12179:   sprintf(environstr+strlen(environstr), /* display environment string */
12180:   " %2d. %s\\\\\n", ienv+1,environvar);
12181:   if ( msgfp!= NULL && msglevel>=9 )
12182:     fprintf(msgfp,"rastenviron> %2d. %.256s\n",
12183:     ienv+1,/*environ[ienv]*/environvar);
12184:   if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
12185:   } /* --- end-of-for(ienv) --- */
12186: strcat(environstr,"}}");		/* end {\text{...}} mode */
12187: rasterize_environ:
12188:   environsp = rasterize(environstr,size); /* rasterize environment string */
12189: /* --- return environment raster to caller --- */
12190: /*end_of_job:*/
12191:   return ( environsp );			/* return environment to caller */
12192: } /* --- end-of-function rastenviron() --- */
12193: 
12194: 
12195: /* ==========================================================================
12196:  * Function:	rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
12197:  * Purpose:	handle \message
12198:  * --------------------------------------------------------------------------
12199:  * Arguments:	expression (I/O) char **  to first char of null-terminated
12200:  *				string immediately following \message
12201:  *				and returning ptr immediately
12202:  *				following last character processed.
12203:  *		size (I)	int containing 0-7 default font size
12204:  *		basesp (I)	subraster *  to character (or subexpression)
12205:  *				immediately preceding \mesasge
12206:  *				(unused, but passed for consistency)
12207:  *		arg1 (I)	int unused
12208:  *		arg2 (I)	int unused
12209:  *		arg3 (I)	int unused
12210:  * --------------------------------------------------------------------------
12211:  * Returns:	( subraster * )	subraster ptr to rendered message image
12212:  * --------------------------------------------------------------------------
12213:  * Notes:     o
12214:  * ======================================================================= */
12215: /* --- entry point --- */
12216: subraster *rastmessage ( char **expression, int size, subraster *basesp,
12217: 			int arg1, int arg2, int arg3 )
12218: {
12219: /* -------------------------------------------------------------------------
12220: Allocations and Declarations
12221: -------------------------------------------------------------------------- */
12222: char	*texsubexpr(), amsg[256]="\000"; /* message number text */
12223: int	imsg = 0;			/* default message number */
12224: char	msg[4096];
12225: subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */
12226: int	strreplace();			/*replace SERVER_NAME in refmsgnum*/
12227: int	reflevels = REFLEVELS;		/* #topmost levels to match */
12228: char	*urlprune();			/*prune referer_match in refmsgnum*/
12229: char	*strdetex();			/* remove math chars from messages */
12230: char	*http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
12231: 	*server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
12232: 	*referer_match = (!isempty(http_host)?http_host: /*match http_host*/
12233: 	  (!isempty(server_name)?server_name:(NULL))); /* or server_name */
12234: /* -------------------------------------------------------------------------
12235: obtain message {amsg} argument
12236: -------------------------------------------------------------------------- */
12237: /* --- parse for {amsg} arg, and bump expression past it --- */
12238: *expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
12239: /* --- interpret argument --- */
12240: if ( *amsg != '\000' ) {		/* got amsg arg */
12241:   imsg = atoi(amsg);			/* interpret as an int */
12242:   if ( imsg < 0				/* if too small */
12243:   ||   imsg > maxmsgnum )		/* or too big */
12244:     imsg = 0; }				/* default to first message */
12245: /* --- retrieve requested message --- */
12246: strninit(msg,msgtable[imsg],4095);	/* local copy of message */
12247: /* --- process as necessary --- */
12248: if ( imsg == refmsgnum) {		/* urlncmp() failed to validate */
12249:   if ( reflevels > 0 )			/* have #levels to validate */
12250:    strreplace(msg,"SERVER_NAME",	/* replace SERVER_NAME */
12251:     strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
12252:   } /* --- end-of-switch(imsg) --- */
12253: /* --- rasterize requested message --- */
12254: messagesp = rasterize(msg,size);	/* rasterize message string */
12255: /* --- return message raster to caller --- */
12256: /*end_of_job:*/
12257:   return ( messagesp );			/* return message to caller */
12258: } /* --- end-of-function rastmessage() --- */
12259: 
12260: 
12261: /* ==========================================================================
12262:  * Function:	rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
12263:  * Purpose:	no op -- flush \escape without error
12264:  * --------------------------------------------------------------------------
12265:  * Arguments:	expression (I/O) char **  to first char of null-terminated
12266:  *				string immediately following \escape to be
12267:  *				flushed, and returning ptr immediately
12268:  *				following last character processed.
12269:  *		size (I)	int containing 0-5 default font size
12270:  *		basesp (I)	subraster *  to character (or subexpression)
12271:  *				immediately preceding \escape
12272:  *				(unused, but passed for consistency)
12273:  *		nargs (I)	int containing number of {}-args after
12274:  *				\escape to be flushed along with it
12275:  *		arg2 (I)	int unused
12276:  *		arg3 (I)	int unused
12277:  * --------------------------------------------------------------------------
12278:  * Returns:	( subraster * )	NULL subraster ptr
12279:  * --------------------------------------------------------------------------
12280:  * Notes:     o
12281:  * ======================================================================= */
12282: /* --- entry point --- */
12283: subraster *rastnoop ( char **expression, int size, subraster *basesp,
12284: 			int nargs, int arg2, int arg3 )
12285: {
12286: /* -------------------------------------------------------------------------
12287: Allocations and Declarations
12288: -------------------------------------------------------------------------- */
12289: char	*texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
12290: subraster *rasterize(), *noopsp=NULL;	/* rasterize subexpr */
12291: /* --- flush accompanying args if necessary --- */
12292: if ( nargs != NOVALUE			/* not unspecified */
12293: &&   nargs > 0 )			/* and args to be flushed */
12294:   while ( --nargs >= 0 )		/* count down */
12295:     *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
12296: /* --- return null ptr to caller --- */
12297: /*end_of_job:*/
12298:   return ( noopsp );			/* return NULL ptr to caller */
12299: } /* --- end-of-function rastnoop() --- */
12300: 
12301: 
12302: /* ==========================================================================
12303:  * Function:	rastopenfile ( filename, mode )
12304:  * Purpose:	Opens filename[.tex] in mode, returning FILE *
12305:  * --------------------------------------------------------------------------
12306:  * Arguments:	filename (I/O)	char * to null-terminated string containing
12307:  *				name of file to open (preceded by path
12308:  *				relative to mimetex executable)
12309:  *				If fopen() fails, .tex appeneded,
12310:  *				and returned if that fopen() succeeds
12311:  *		mode (I)	char * to null-terminated string containing
12312:  *				fopen() mode
12313:  * --------------------------------------------------------------------------
12314:  * Returns:	( FILE * )	pointer to opened file, or NULL if error
12315:  * --------------------------------------------------------------------------
12316:  * Notes:     o
12317:  * ======================================================================= */
12318: /* --- entry point --- */
12319: FILE	*rastopenfile ( char *filename, char *mode )
12320: {
12321: /* -------------------------------------------------------------------------
12322: Allocations and Declarations
12323: -------------------------------------------------------------------------- */
12324: FILE	*fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
12325: char	texfile[2050] = "\000",		/* local, edited copy of filename */
12326: 	*rasteditfilename(),		/* prepend pathprefix if necessary */
12327: 	amode[512] = "r";		/* test open mode if arg mode=NULL */
12328: int	ismode = 0;			/* true of mode!=NULL */
12329: /* --------------------------------------------------------------------------
12330: Check mode and open file
12331: -------------------------------------------------------------------------- */
12332: /* --- edit filename --- */
12333: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
12334: texfile[2047] = '\000';			/* make sure it's null terminated */
12335: /* --- check mode --- */
12336: if ( mode != (char *)NULL )		/* caller passed mode arg */
12337:  if ( *mode != '\000' )			/* and it's not an empty string */
12338:   { ismode = 1;				/* so flip mode flag true */
12339:     strncpy(amode,mode,254);		/* and replace "r" with caller's */
12340:     amode[254] = '\000';		/* make sure it's null terminated */
12341:     compress(amode,' '); }		/* remove embedded blanks */
12342: /* --- open filename or filename.tex --- */
12343: if ( strlen(texfile) > 1 )		/* make sure we got actual filename*/
12344:   if ( (fp = fopen(texfile,amode))	/* try opening given filename */
12345:   ==   NULL )				/* failed to open given filename */
12346:   { strcpy(filename,texfile);		/* signal possible filename error */
12347:     strcat(texfile,".tex");		/* but first try adding .tex */
12348:     if ( (fp = fopen(texfile,amode))	/* now try opening filename.tex */
12349:     !=   NULL )				/* filename.tex succeeded */
12350:       strcpy(filename,texfile); }	/* replace caller's filename */
12351: /* --- close file if only opened to check name --- */
12352: if ( !ismode && fp!=NULL )		/* no mode, so just checking */
12353:   fclose(fp);				/* close file, fp signals success */
12354: /* --- return fp or NULL to caller --- */
12355: /*end_of_job:*/
12356:   if ( msglevel>=9 && msgfp!=NULL )	/* debuging */
12357:     { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
12358:       filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
12359:   return ( fp );			/* return fp or NULL to caller */
12360: } /* --- end-of-function rastopenfile() --- */
12361: 
12362: 
12363: /* ==========================================================================
12364:  * Function:	rasteditfilename ( filename )
12365:  * Purpose:	edits filename to remove security problems,
12366:  *		e.g., removes all ../'s and ..\'s.
12367:  * --------------------------------------------------------------------------
12368:  * Arguments:	filename (I)	char * to null-terminated string containing
12369:  *				name of file to be edited
12370:  * --------------------------------------------------------------------------
12371:  * Returns:	( char * )	pointer to edited filename,
12372:  *				or empty string "\000" if any problem
12373:  * --------------------------------------------------------------------------
12374:  * Notes:     o
12375:  * ======================================================================= */
12376: /* --- entry point --- */
12377: char	*rasteditfilename ( char *filename )
12378: {
12379: /* -------------------------------------------------------------------------
12380: Allocations and Declarations
12381: -------------------------------------------------------------------------- */
12382: static	char editname[2050];		/*edited filename returned to caller*/
12383: char	*strchange();			/* prepend pathprefix if necessary */
12384: int	strreplace(),			/* remove ../'s and ..\'s */
12385: 	isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
12386: /* --------------------------------------------------------------------------
12387: edit filename
12388: -------------------------------------------------------------------------- */
12389: /* --- first check filename arg --- */
12390: *editname = '\000';			/* init edited name as empty string*/
12391: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
12392: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
12393: /* --- init edited filename --- */
12394: strcpy(editname,filename);		/* init edited name as input name */
12395: compress(editname,' ');			/* remove embedded blanks */
12396: /* --- remove leading or embedded ....'s --- */
12397: while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
12398: /* --- remove leading / and \ and dots (and blanks) --- */
12399: if ( *editname != '\000' )		/* still have chars in filename */
12400:  while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
12401:    {strsqueeze(editname,1);}		/* so flush leading / or \ (or ' ')*/
12402: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
12403: /* --- remove leading or embedded ../'s and ..\'s --- */
12404: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
12405: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
12406: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
12407: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
12408: if ( isprefix && *editname!='\000' )	/* filename is preceded by prefix */
12409:   strchange(0,editname,pathprefix);	/* so prepend prefix */
12410: end_of_job:
12411:   return ( editname );			/* back with edited filename */
12412: } /* --- end-of-function rasteditfilename() --- */
12413: 
12414: 
12415: /* ==========================================================================
12416:  * Function:	rastreadfile ( filename, islock, tag, value )
12417:  * Purpose:	Read filename, returning value as string
12418:  *		between <tag>...</tag> or entire file if tag=NULL passed.
12419:  * --------------------------------------------------------------------------
12420:  * Arguments:	filename (I)	char * to null-terminated string containing
12421:  *				name of file to read (preceded by path
12422:  *				relative to mimetex executable)
12423:  *		islock (I)	int containing 1 to lock file while reading
12424:  *				(hopefully done by opening in "r+" mode)
12425:  *		tag (I)		char * to null-terminated string containing
12426:  *				html-like tagname.  File contents between
12427:  *				<tag> and </tag> will be returned, or
12428:  *				entire file if tag=NULL passed.
12429:  *		value (O)	char * returning value between <tag>...</tag>
12430:  *				or entire file if tag=NULL.
12431:  * --------------------------------------------------------------------------
12432:  * Returns:	( int )		1=okay, 0=some error
12433:  * --------------------------------------------------------------------------
12434:  * Notes:     o
12435:  * ======================================================================= */
12436: /* --- entry point --- */
12437: int	rastreadfile ( char *filename, int islock, char *tag, char *value )
12438: {
12439: /* -------------------------------------------------------------------------
12440: Allocations and Declarations
12441: -------------------------------------------------------------------------- */
12442: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
12443: char	texfile[1024] = "\000",		/* local copy of input filename */
12444: 	text[MAXLINESZ+1];		/* line from input file */
12445: char	*tagp, tag1[1024], tag2[1024];	/* left <tag> and right <tag/> */
12446: int	vallen=0, maxvallen=MAXFILESZ;	/* #chars in value, max allowed */
12447: int	status = (-1);			/* status returned, 1=okay */
12448: int	tagnum = 0;			/* tag we're looking for */
12449: /*int	islock = 1;*/			/* true to lock file */
12450: /* --------------------------------------------------------------------------
12451: Open file
12452: -------------------------------------------------------------------------- */
12453: /* --- first check output arg --- */
12454: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
12455: *value = '\000';			/* init buffer with empty string */
12456: /* --- open filename or filename.tex --- */
12457: if ( filename != (char *)NULL )		/* make sure we got filename arg */
12458:   { strncpy(texfile,filename,1023);	/* local copy of filename */
12459:     texfile[1023] = '\000';		/* make sure it's null terminated */
12460:     fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
12461: /* --- check that file opened --- */
12462: if ( fp == (FILE *)NULL )		/* failed to open file */
12463:   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
12464:     goto end_of_job; }			/* return error message to caller */
12465: status = 0;				/* file opened successfully */
12466: if ( islock ) rewind(fp);		/* start at beginning of file */
12467: /* --------------------------------------------------------------------------
12468: construct <tag>'s
12469: -------------------------------------------------------------------------- */
12470: if ( tag != (char *)NULL )		/* caller passed tag arg */
12471:  if ( *tag != '\000' )			/* and it's not an empty string */
12472:   { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
12473:     strcat(tag1,tag); strcat(tag2,tag);	/* followed by caller's tag */
12474:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
12475:     compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
12476:     tagnum = 1; }			/* signal that we have tag */
12477: /* --------------------------------------------------------------------------
12478: Read file, concatnate lines
12479: -------------------------------------------------------------------------- */
12480: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
12481:   switch ( tagnum ) {			/* look for left- or right-tag */
12482:     case 0: status = 1; break;		/* no tag to look for */
12483:     case 1:				/* looking for opening left <tag> */
12484:       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
12485:       tagp += strlen(tag1);		/* first char past tag */
12486:       strsqueezep(text,tagp);		/*shift out preceding text and tag*/
12487:       tagnum = 2;			/*now looking for closing right tag*/
12488:     case 2:				/* looking for closing right </tag> */
12489:       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
12490:       *tagp = '\000';			/* terminate line at tag */
12491:       tagnum = 3;			/* done after this line */
12492:       status = 1;			/* successfully read tag */
12493:       break;
12494:     } /* ---end-of-switch(tagnum) --- */
12495:   if ( tagnum != 1 ) {			/* no tag or left tag already found*/
12496:     int	textlen = strlen(text);		/* #chars in current line */
12497:     if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
12498:     strcat(value,text);			/* concat line to end of value */
12499:     vallen += textlen;			/* bump length */
12500:     if ( tagnum > 2 ) break; }		/* found right tag, so we're done */
12501:   } /* --- end-of-while(fgets()!=NULL) --- */
12502: if ( tagnum<1 || tagnum>2 ) status=1;	/* okay if no tag or we found tag */
12503: fclose ( fp );				/* close input file after reading */
12504: /* --- return value and status to caller --- */
12505: end_of_job:
12506:   return ( status );			/* return status to caller */
12507: } /* --- end-of-function rastreadfile() --- */
12508: 
12509: 
12510: /* ==========================================================================
12511:  * Function:	rastwritefile ( filename, tag, value, isstrict )
12512:  * Purpose:	Re/writes filename, replacing string between <tag>...</tag>
12513:  *		with value, or writing entire file as value if tag=NULL.
12514:  * --------------------------------------------------------------------------
12515:  * Arguments:	filename (I)	char * to null-terminated string containing
12516:  *				name of file to write (preceded by path
12517:  *				relative to mimetex executable)
12518:  *		tag (I)		char * to null-terminated string containing
12519:  *				html-like tagname.  File contents between
12520:  *				<tag> and </tag> will be replaced, or
12521:  *				entire file written if tag=NULL passed.
12522:  *		value (I)	char * containing string replacing value
12523:  *				between <tag>...</tag> or replacing entire
12524:  *				file if tag=NULL.
12525:  *		isstrict (I)	int containing 1 to only rewrite existing
12526:  *				files, or 0 to create new file if necessary.
12527:  * --------------------------------------------------------------------------
12528:  * Returns:	( int )		1=okay, 0=some error
12529:  * --------------------------------------------------------------------------
12530:  * Notes:     o
12531:  * ======================================================================= */
12532: /* --- entry point --- */
12533: int	rastwritefile( char *filename, char *tag, char *value, int isstrict )
12534: {
12535: /* -------------------------------------------------------------------------
12536: Allocations and Declarations
12537: -------------------------------------------------------------------------- */
12538: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
12539: char	texfile[1024] = "\000",		/* local copy of input filename */
12540: 	filebuff[MAXFILESZ+1] = "\000",	/* entire contents of file */
12541: 	tag1[1024], tag2[1024],		/* left <tag> and right <tag/> */
12542: 	*strchange(),			/* put value between <tag>...</tag>*/
12543: 	*timestamp();			/* log modification time */
12544: int	istag=0, rastreadfile(),	/* read file if tag!=NULL */
12545: 	/*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
12546: 	isnewfile = 0,			/* true if writing new file */
12547: 	status = 0;			/* status returned, 1=okay */
12548: int	istimestamp = 0;		/* true to update <timestamp> tag */
12549: /* --------------------------------------------------------------------------
12550: check args
12551: -------------------------------------------------------------------------- */
12552: /* --- check filename and value --- */
12553: if ( filename == (char *)NULL		/* quit if no filename arg supplied*/
12554: ||   value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
12555: if ( strlen(filename) < 2		/* quit if unreasonable filename */
12556: ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
12557: /* --- establish filename[.tex] --- */
12558: strncpy(texfile,filename,1023);		/* local copy of input filename */
12559: texfile[1023] = '\000';			/* make sure it's null terminated */
12560: if ( rastopenfile(texfile,NULL)		/* unchanged or .tex appended */
12561: ==   (FILE *)NULL )			/* can't open, so write new file */
12562:   { if ( isstrict ) goto end_of_job;	/* fail if new files not permitted */
12563:     isnewfile = 1; }			/* signal we're writing new file */
12564: /* --- check whether tag supplied by caller --- */
12565: if ( tag != (char *)NULL )		/* caller passed tag argument */
12566:  if ( *tag != '\000' )			/* and it's not an empty string */
12567:   { istag = 1;				/* so flip tag flag true */
12568:     strcpy(tag1,"<"); strcpy(tag2,"</");  /* begin tags with < and </ */
12569:     strcat(tag1,tag); strcat(tag2,tag);   /* followed by caller's tag */
12570:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
12571:     compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
12572: /* --------------------------------------------------------------------------
12573: read existing file if just rewriting a single tag
12574: -------------------------------------------------------------------------- */
12575: /* --- read original file if only replacing a tag within it --- */
12576: *filebuff = '\000';			/* init as empty file */
12577: if ( !isnewfile )			/* if file already exists */
12578:  if ( istag )				/* and just rewriting one tag */
12579:   if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
12580:   <=   0 ) goto end_of_job;		/* signal error if failed to read */
12581: /* --------------------------------------------------------------------------
12582: construct new file data if needed (entire file replaced by value if no tag)
12583: -------------------------------------------------------------------------- */
12584: if ( istag )				/* only replacing tag in file */
12585:  {
12586:  /* --- find <tag> and </tag> in file --- */
12587:  int	tlen1=strlen(tag1),  tlen2=strlen(tag2), flen;  /*tag,buff lengths*/
12588:  char	*tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
12589: 	*tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
12590:  /* --- if adding new <tag> just concatanate at end of file --- */
12591:  if ( tagp1 == (char *)NULL )		/* add new tag to file */
12592:   {
12593:   /* --- preprocess filebuff --- */
12594:   if ( tagp2 != (char *)NULL )		/* apparently have ...</tag> */
12595:     {strsqueezep(filebuff,tagp2+tlen2);} /* remove ...</tag> */
12596:   if ( (flen = strlen(filebuff))	/* #chars currently in buffer */
12597:   > 0 )					/* we have non-empty buffer */
12598:    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
12599:      if(0)strcat(filebuff,"\n");	/* so add one before new tag */
12600:   /* --- add new tag --- */
12601:   strcat(filebuff,tag1);		/* add opening <tag> */
12602:   strcat(filebuff,value);		/* then value */
12603:   strcat(filebuff,tag2);		/* finally closing </tag> */
12604:   strcat(filebuff,"\n");		/* newline at end of file */
12605:   } /* --- end-of-if(tagp1==NULL) --- */
12606:  else					/* found existing opening <tag> */
12607:   {
12608:   if ( tagp2 == NULL )			/* apparently have <tag>... */
12609:     { *(tagp1+tlen1) = '\000';		/* so get rid of trailing ... */
12610:       strcat(filebuff,value);		/* then concatanate value */
12611:       strcat(filebuff,tag2); }		/* and finally closing </tag> */
12612:   else					/* else have <tag>...<tag/> */
12613:    if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
12614:    >=   0 )				/* usually <tag> precedes </tag> */
12615:     strchange(flen,tagp1+tlen1,value);	/* change ...'s to value */
12616:    else					/* weirdly, </tag> precedes <tag> */
12617:     { char fbuff[4096];			/* field buff for <tag>value</tag> */
12618:       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
12619:       <=   0 ) goto end_of_job;		/* must be internal error */
12620:       strcpy(fbuff,tag1);		/* set opening <tag> */
12621:       strcat(fbuff,value);		/* then value */
12622:       strcat(fbuff,tag2);		/* finally closing </tag> */
12623:       strchange(flen,tagp2,fbuff); }	/* replace original </tag>...<tag> */
12624:   } /* --- end-of-if/else(tagp1==NULL) --- */
12625:  } /* --- end-of-if(istag) --- */
12626: /* --------------------------------------------------------------------------
12627: rewrite file and return to caller
12628: -------------------------------------------------------------------------- */
12629: /* --- first open file for write --- */
12630: if ( (fp=rastopenfile(texfile,"w"))	/* open for write */
12631: ==   (FILE *)NULL ) goto end_of_job;	/* signal error if can't open */
12632: /* --- rewrite and close file --- */
12633: if ( fputs((istag?filebuff:value),fp)	/* write filebuff or value */
12634: !=  EOF ) status = 1;			/* signal success if succeeded */
12635: fclose ( fp );				/* close output file after writing */
12636: /* --- modify timestamp --- */
12637: if ( status > 0 )			/*forget timestamp if write failed*/
12638:  if ( istimestamp )			/* if we're updating timestamp */
12639:   if ( istag )				/* only log time in tagged file */
12640:    if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
12641:     { char fbuff[2048];			/* field buff <timestamp> value */
12642:       strcpy(fbuff,tag);		/* tag modified */
12643:       strcat(fbuff," modified at ");	/* spacer */
12644:       strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
12645:       status = rastwritefile(filename,"timestamp",fbuff,1); }
12646: /* --- return status to caller --- */
12647: end_of_job:
12648:   return ( status );			/* return status to caller */
12649: } /* --- end-of-function rastwritefile() --- */
12650: 
12651: 
12652: /* ==========================================================================
12653:  * Function:	calendar ( year, month, day )
12654:  * Purpose:	returns null-terminated character string containing
12655:  *		\begin{array}...\end{array} for the one-month calendar
12656:  *		specified by year=1973...2099 and month=1...12.
12657:  *		If either arg out-of-range, today's value is used.
12658:  * --------------------------------------------------------------------------
12659:  * Arguments:	year (I)	int containing 1973...2099 or 0 for current
12660:  *				year
12661:  *		month (I)	int containing 1...12 or 0 for current month
12662:  *		day (I)		int containing day to emphasize or 0
12663:  * --------------------------------------------------------------------------
12664:  * Returns:	( char * )	char ptr to null-terminated buffer
12665:  *				containing \begin{array}...\end{array}
12666:  *				string that will render calendar for
12667:  *				requested month, or NULL for any error.
12668:  * --------------------------------------------------------------------------
12669:  * Notes:     o
12670:  * ======================================================================= */
12671: /* --- entry point --- */
12672: char	*calendar( int year, int month, int day )
12673: {
12674: /* -------------------------------------------------------------------------
12675: Allocations and Declarations
12676: -------------------------------------------------------------------------- */
12677: static char calbuff[4096];		/* calendar returned to caller */
12678: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
12679: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
12680: int	yy=0, mm=0, dd=0;		/* today (emphasize today's dd) */
12681: int	idd=1, iday=0, daynumber();	/* day-of-week for idd=1...31 */
12682: char	aval[64];			/* ascii day or 4-digit year */
12683: /* --- calendar data --- */
12684: static	char *monthnames[] = { "?", "January", "February", "March", "April",
12685: 	 "May", "June", "July", "August", "September", "October",
12686: 	"November", "December", "?" } ;
12687: static	int modays[] =
12688: 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
12689: /* -------------------------------------------------------------------------
12690: initialization
12691: -------------------------------------------------------------------------- */
12692: /* --- get current date/time --- */
12693: time((time_t *)(&time_val));		/* get date and time */
12694: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
12695: yy  =  1900 + (int)(tmstruct->tm_year);	/* current four-digit year */
12696: mm  =  1 + (int)(tmstruct->tm_mon);	/* current month, 1-12 */
12697: dd  =  (int)(tmstruct->tm_mday);	/* current day, 1-31 */
12698: /* --- check args --- */
12699: if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
12700: if ( month<1 || month>12 ) month = mm;	/* current month if out-of-bounds */
12701: if ( month==mm && year==yy && day==0 )	/* current month and default day */
12702:   day = dd;				/* emphasize current day */
12703: modays[2] = (year%4==0?29:28);		/* Feb has 29 days in leap years */
12704: /* --- initialize calendar string --- */
12705: strcpy(calbuff,"{\\begin{gather}");	/* center `month year` above cal */
12706: strcat(calbuff,"\\small\\text{");	/* month set in roman */
12707: strcat(calbuff,monthnames[month]);	/* insert month name */
12708: strcat(calbuff," }");			/* add a space */
12709: sprintf(aval,"%d",year);		/* convert year to ascii */
12710: strcat(calbuff,aval);			/* add year */
12711: strcat(calbuff,"\\\\");			/* end top row */
12712: strcat(calbuff,				/* now begin calendar arrayr */
12713: 	"\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
12714: 	"\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
12715: 	"\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
12716: 	"\\tiny\\text{Sat} \\\\ \\hline " );
12717: /* -------------------------------------------------------------------------
12718: generate calendar
12719: -------------------------------------------------------------------------- */
12720: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
12721:   {
12722:   /* --- get day-of-week for this day --- */
12723:   iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
12724:   if ( iday == 7 ) iday = 0;		/* now 0=Sunday...6=Saturday */
12725:   /* --- may need empty cells at beginning of month --- */
12726:   if ( idd == 1 )			/* first day of month */
12727:    if ( iday > 0 )			/* need to skip cells */
12728:     { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
12729:       aval[3*iday] = '\000';		/*skip cells preceding 1st of month*/
12730:       strcat(calbuff,aval); }		/* add skip string to buffer */
12731:   /* --- add idd to current cell --- */
12732:   sprintf(aval,"%d",idd);		/* convert idd to ascii */
12733:   if ( idd == day			/* emphasize today's date */
12734:   /*&&   month==mm && year==yy*/ )	/* only if this month's calendar */
12735:    { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
12736:      strcat(calbuff,aval);		/* put in idd */
12737:      strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
12738:   else					/* not today's date */
12739:     strcat(calbuff,aval);		/* so just put in idd */
12740:   /* --- terminate cell --- */
12741:   if ( idd < modays[month] ) {		/* not yet end-of-month */
12742:    if ( iday < 6 )			/* still have days left in week */
12743:     strcat(calbuff,"&");		/* new cell in same week */
12744:    else					/* reached end-of-week */
12745:     strcat(calbuff,"\\\\ \\hline"); }	/* so start new week */
12746:   } /* --- end-of-for(idd) --- */
12747: strcat(calbuff,"\\\\ \\hline");		/* final underline at end-of-month */
12748: /* --- return calendar to caller --- */
12749: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
12750: return ( calbuff );			/* back to caller with calendar */
12751: } /* --- end-of-function calendar() --- */
12752: 
12753: 
12754: /* ==========================================================================
12755:  * Function:	timestamp ( tzdelta, ifmt )
12756:  * Purpose:	returns null-terminated character string containing
12757:  *		current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
12758:  * --------------------------------------------------------------------------
12759:  * Arguments:	tzdelta (I)	integer, positive or negative, containing
12760:  *				containing number of hours to be added or
12761:  *				subtracted from system time (to accommodate
12762:  *				your desired time zone).
12763:  *		ifmt (I)	integer containing 0 for default format
12764:  * --------------------------------------------------------------------------
12765:  * Returns:	( char * )	ptr to null-terminated buffer
12766:  *				containing current date:time stamp
12767:  * --------------------------------------------------------------------------
12768:  * Notes:     o
12769:  * ======================================================================= */
12770: /* --- entry point --- */
12771: char	*timestamp( int tzdelta, int ifmt )
12772: {
12773: /* -------------------------------------------------------------------------
12774: Allocations and Declarations
12775: -------------------------------------------------------------------------- */
12776: static	char timebuff[256];		/* date:time buffer back to caller */
12777: /*long	time_val = 0L;*/		/* binary value returned by time() */
12778: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
12779: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
12780: int	year=0, hour=0,ispm=1,		/* adjust year, and set am/pm hour */
12781: 	month=0, day=0,			/* adjust day and month for delta  */
12782: 	minute=0,second=0;		/* minute and second not adjusted  */
12783: int	tzadjust();			/* time zone adjustment function */
12784: int	daynumber();			/* #days since Jan 1, 1973 */
12785: static	char *daynames[] = { "Monday", "Tuesday", "Wednesday",
12786: 	 "Thursday", "Friday", "Saturday", "Sunday" } ;
12787: static	char *monthnames[] = { "?", "January", "February", "March", "April",
12788: 	 "May", "June", "July", "August", "September", "October",
12789: 	"November", "December", "?" } ;
12790: /* -------------------------------------------------------------------------
12791: get current date:time, adjust values, and and format stamp
12792: -------------------------------------------------------------------------- */
12793: /* --- first init returned timebuff in case of any error --- */
12794: *timebuff = '\000';
12795: /* --- get current date:time --- */
12796: time((time_t *)(&time_val));		/* get date and time */
12797: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
12798: /* --- extract fields --- */
12799: year  = (int)(tmstruct->tm_year);	/* local copy of year,  0=1900 */
12800: month = (int)(tmstruct->tm_mon) + 1;	/* local copy of month, 1-12 */
12801: day   = (int)(tmstruct->tm_mday);	/* local copy of day,   1-31 */
12802: hour  = (int)(tmstruct->tm_hour);	/* local copy of hour,  0-23 */
12803: minute= (int)(tmstruct->tm_min);	/* local copy of minute,0-59 */
12804: second= (int)(tmstruct->tm_sec);	/* local copy of second,0-59 */
12805: /* --- adjust year --- */
12806: year += 1900;				/* set century in year */
12807: /* --- adjust for timezone --- */
12808: tzadjust(tzdelta,&year,&month,&day,&hour);
12809: /* --- check params --- */
12810: if ( hour<0  || hour>23
12811: ||   day<1   || day>31
12812: ||   month<1 || month>12
12813: ||   year<1973 ) goto end_of_job;
12814: /* --- adjust hour for am/pm --- */
12815: switch ( ifmt )
12816:   {
12817:   default:
12818:   case 0:
12819:     if ( hour < 12 )			/* am check */
12820:      { ispm=0;				/* reset pm flag */
12821:        if ( hour == 0 ) hour = 12; }	/* set 00hrs = 12am */
12822:     if ( hour > 12 ) hour -= 12;	/* pm check sets 13hrs to 1pm, etc */
12823:     break;
12824:   } /* --- end-of-switch(ifmt) --- */
12825: /* --- format date:time stamp --- */
12826: switch ( ifmt )
12827:   {
12828:   default:
12829:   case 0:  /* --- 2005-03-05:11:49:59am --- */
12830:     sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
12831:     hour,minute,second,((ispm)?"pm":"am"));
12832:     break;
12833:   case 1:  /* --- Saturday, March 5, 2005 --- */
12834:     sprintf(timebuff,"%s, %s %d, %d",
12835:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
12836:     break;
12837:   case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
12838:     sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
12839:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
12840:     hour,minute,second,((ispm)?"pm":"am"));
12841:     break;
12842:   case 3: /* --- 11:49:59am --- */
12843:     sprintf(timebuff,"%d:%02d:%02d%s",
12844:     hour,minute,second,((ispm)?"pm":"am"));
12845:     break;
12846:   case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
12847:     sprintf(timebuff,"%d%02d%02d%02d%02d",
12848:     month,day,hour,minute,second);
12849:     break;
12850:   } /* --- end-of-switch(ifmt) --- */
12851: end_of_job:
12852:   return ( timebuff );			/* return stamp to caller */
12853: } /* --- end-of-function timestamp() --- */
12854: 
12855: 
12856: /* ==========================================================================
12857:  * Function:	tzadjust ( tzdelta, year, month, day, hour )
12858:  * Purpose:	Adjusts hour, and day,month,year if necessary,
12859:  *		by delta increment to accommodate your time zone.
12860:  * --------------------------------------------------------------------------
12861:  * Arguments:	tzdelta (I)	integer, positive or negative, containing
12862:  *				containing number of hours to be added or
12863:  *				subtracted from given time (to accommodate
12864:  *				your desired time zone).
12865:  *		year (I)	addr of int containing        4-digit year
12866:  *		month (I)	addr of int containing month  1=Jan - 12=Dec.
12867:  *		day (I)		addr of int containing day    1-31 for Jan.
12868:  *		hour (I)	addr of int containing hour   0-23
12869:  * Returns:	( int )		1 for success, or 0 for error
12870:  * --------------------------------------------------------------------------
12871:  * Notes:     o
12872:  * ======================================================================= */
12873: /* --- entry point --- */
12874: int	tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
12875: {
12876: /* --------------------------------------------------------------------------
12877: Allocations and Declarations
12878: -------------------------------------------------------------------------- */
12879: int	yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
12880: /* --- calendar data --- */
12881: static	int modays[] =
12882: 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
12883: /* --------------------------------------------------------------------------
12884: check args
12885: -------------------------------------------------------------------------- */
12886: if ( mm<1 || mm>12 ) return(-1);	/* bad month */
12887: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
12888: if ( hh<0 || hh>23 ) return(-1);	/* bad hour */
12889: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
12890: /* --------------------------------------------------------------------------
12891: make adjustments
12892: -------------------------------------------------------------------------- */
12893: /* --- adjust hour --- */
12894: hh += tzdelta;				/* apply caller's delta */
12895: /* --- adjust for feb 29 --- */
12896: modays[2] = (yy%4==0?29:28);		/* Feb has 29 days in leap years */
12897: /* --- adjust day --- */
12898: if ( hh < 0 )				/* went to preceding day */
12899:   { dd--;  hh += 24; }
12900: if ( hh > 23 )				/* went to next day */
12901:   { dd++;  hh -= 24; }
12902: /* --- adjust month --- */
12903: if ( dd < 1 )				/* went to preceding month */
12904:   { mm--;  dd = modays[mm]; }
12905: if ( dd > modays[mm] )			/* went to next month */
12906:   { mm++;  dd = 1; }
12907: /* --- adjust year --- */
12908: if ( mm < 1 )				/* went to preceding year */
12909:   { yy--;  mm = 12;  dd = modays[mm]; }
12910: if ( mm > 12 )				/* went to next year */
12911:   { yy++;  mm = 1;   dd = 1; }
12912: /* --- back to caller --- */
12913: *year=yy; *month=mm; *day=dd; *hour=hh;	/* reset adjusted args */
12914: return ( 1 );
12915: } /* --- end-of-function tzadjust() --- */
12916: 
12917: 
12918: /* ==========================================================================
12919:  * Function:	daynumber ( year, month, day )
12920:  * Purpose:	Returns number of actual calendar days from Jan 1, 1973
12921:  *		to the given date (e.g., bvdaynumber(1974,1,1)=365).
12922:  * --------------------------------------------------------------------------
12923:  * Arguments:	year (I)	int containing year -- may be either 1995 or
12924:  *				95, or may be either 2010 or 110 for those
12925:  *				years.
12926:  *		month (I)	int containing month, 1=Jan thru 12=Dec.
12927:  *		day (I)		int containing day of month, 1-31 for Jan, etc.
12928:  * Returns:	( int )		Number of days from Jan 1, 1973 to given date,
12929:  *				or -1 for error (e.g., year<1973).
12930:  * --------------------------------------------------------------------------
12931:  * Notes:     o
12932:  * ======================================================================= */
12933: /* --- entry point --- */
12934: int	daynumber ( int year, int month, int day )
12935: {
12936: /* --------------------------------------------------------------------------
12937: Allocations and Declarations
12938: -------------------------------------------------------------------------- */
12939: /* --- returned value (note: returned as a default "int") --- */
12940: int	ndays;				/* #days since jan 1, year0 */
12941: /* --- initial conditions --- */
12942: static	int year0 = 73, 		/* jan 1 was a monday, 72 was a leap */
12943: 	days4yrs = 1461,		/* #days in 4 yrs = 365*4 + 1 */
12944: 	days1yr  = 365;
12945: /* --- table of accumulated days per month (last index not used) --- */
12946: static	int modays[] =
12947: 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
12948: /* --- variables for #days since day0 --- */
12949: int	nyears, nfouryrs;		/*#years, #4-yr periods since year0*/
12950: /* --------------------------------------------------------------------------
12951: Check input
12952: -------------------------------------------------------------------------- */
12953: if ( month < 1 || month > 12 )		/*month used as index, so must be ok*/
12954: 	return ( -1 );			/* otherwise, forget it */
12955: if ( year >= 1900 ) year -= 1900;	/*use two-digit years (3 after 2000)*/
12956: /* --------------------------------------------------------------------------
12957: Find #days since jan 1, 1973
12958: -------------------------------------------------------------------------- */
12959: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
12960: nyears = year - year0;			/* #years since year0 */
12961: if ( nyears < 0 ) return ( -1 );	/* we're not working backwards */
12962: nfouryrs = nyears/4;			/* #complete four-year periods */
12963: nyears -= (4*nfouryrs); 		/* remainder excluding current year*/
12964: /* --- #days from jan 1, year0 till jan 1, this year --- */
12965: ndays = (days4yrs*nfouryrs)		/* #days in 4-yr periods */
12966:       +  (days1yr*nyears);		/* +remaining days */
12967: /*if ( year > 100 ) ndays--;*/		/* subtract leap year for 2000AD */
12968: /* --- add #days within current year --- */
12969: ndays += (modays[month-1] + (day-1));
12970: /* --- may need an extra day if current year is a leap year --- */
12971: if ( nyears == 3 )			/*three preceding yrs so this is 4th*/
12972:     { if ( month > 2 )			/* past feb so need an extra day */
12973: 	/*if ( year != 100 )*/		/* unless it's 2000AD */
12974: 	  ndays++; }			/* so add it in */
12975: return ( (int)(ndays) );		/* #days back to caller */
12976: } /* --- end-of-function daynumber() --- */
12977: 
12978: 
12979: /* ==========================================================================
12980:  * Function:	strwrap ( s, linelen, tablen )
12981:  * Purpose:	Inserts \n's and spaces in (a copy of) s to wrap lines
12982:  *		at linelen and indent them by tablen.
12983:  * --------------------------------------------------------------------------
12984:  * Arguments:	s (I)		char * to null-terminated string
12985:  *				to be wrapped.
12986:  *		linelen (I)	int containing maximum linelen
12987:  *				between \\'s.
12988:  *		tablen (I)	int containing number of spaces to indent
12989:  *				lines.  0=no indent.  Positive means
12990:  *				only indent first line and not others.
12991:  *				Negative means indent all lines except first.
12992:  * --------------------------------------------------------------------------
12993:  * Returns:	( char * )	ptr to "line-wrapped" copy of s
12994:  *				or "" (empty string) for any error.
12995:  * --------------------------------------------------------------------------
12996:  * Notes:     o	The returned copy of s has embedded \\'s as necessary
12997:  *		to wrap lines at linelen.  Any \\'s in the input copy
12998:  *		are removed first.  If (and only if) the input s contains
12999:  *		a terminating \\ then so does the returned copy.
13000:  *	      o	The returned pointer addresses a static buffer,
13001:  *		so don't call strwrap() again until you're finished
13002:  *		with output from the preceding call.
13003:  *	      o	Modified for mimetex from original version written
13004:  *		for mathtex (where \n in verbatim mode instead of \\
13005:  *		produced linebreaks).
13006:  * ======================================================================= */
13007: /* --- entry point --- */
13008: char	*strwrap ( char *s, int linelen, int tablen )
13009: {
13010: /* -------------------------------------------------------------------------
13011: Allocations and Declarations
13012: -------------------------------------------------------------------------- */
13013: static	char sbuff[4096];		/* line-wrapped copy of s */
13014: char	*sol = sbuff;			/* ptr to start of current line*/
13015: char	tab[32] = "                 ";	/* tab string */
13016: int	strreplace();			/* remove \n's */
13017: char	*strchange();			/* add \n's and indent space */
13018: int	finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
13019: int	istab = (tablen>0?1:0),		/* init true to indent first line */
13020: 	iswhite = 0;			/* true if line break on whitespace*/
13021: int	rhslen  = 0,			/* remaining right hand side length*/
13022: 	thislen = 0,			/* length of current line segment */
13023: 	thistab = 0,			/* length of tab on current line */
13024: 	wordlen = 0;			/* length to next whitespace char */
13025: /* -------------------------------------------------------------------------
13026: Make a clean copy of s
13027: -------------------------------------------------------------------------- */
13028: /* --- check input --- */
13029: *sbuff = '\000';			/* initialize in case of error */
13030: if ( isempty(s) ) goto end_of_job;	/* no input */
13031: if ( tablen < 0 ) tablen = (-tablen);	/* set positive tablen */
13032: if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
13033: tab[min2(tablen,16)] = '\000';		/* null-terminate tab string */
13034: tablen = strlen(tab);			/* reset to actual tab length */
13035: finalnewline = 0;			/* turned off for mimetex version */
13036: /* --- start with copy of s --- */
13037: strninit(sbuff,s,3000);			/* leave room for \n's and tabs */
13038: if ( linelen < 1 ) goto end_of_job;	/* can't do anything */
13039: trimwhite(sbuff);			/*remove leading/trailing whitespace*/
13040: strreplace(sbuff,"\n"," ",0);		/* remove any original \n's */
13041: strreplace(sbuff,"\r"," ",0);		/* remove any original \r's */
13042: strreplace(sbuff,"\t"," ",0);		/* remove any original \t's */
13043: strreplace(sbuff,"\f"," ",0);		/* remove any original \f's */
13044: strreplace(sbuff,"\v"," ",0);		/* remove any original \v's */
13045: strreplace(sbuff,"\\\\"," ",0);		/* remove any original \\'s */
13046: /* -------------------------------------------------------------------------
13047: Insert \\'s and spaces as needed
13048: -------------------------------------------------------------------------- */
13049: while ( 1 ) {				/* till end-of-line */
13050:   /* --- init --- */
13051:   trimwhite(sol);			/*remove leading/trailing whitespace*/
13052:   thislen = thistab = 0;		/* no chars in current line yet */
13053:   if ( istab && tablen>0 ) {		/* need to indent this line */
13054:     strchange(0,sol,tab);		/* insert indent at start of line */
13055:     thistab = tablen; }			/* line starts with whitespace tab */
13056:   if ( sol == sbuff ) istab = 1-istab;	/* flip tab flag after first line */
13057:   sol += thistab;			/* skip tab */
13058:   rhslen = strlen(sol);			/* remaining right hand side chars */
13059:   if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
13060:   if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
13061:     fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
13062:     fflush(msgfp); }
13063:   /* --- look for last whitespace preceding linelen --- */
13064:   while ( 1 ) {				/* till we exceed linelen */
13065:     wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/
13066:     if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
13067:       goto end_of_job;			/* so nothing more we can do */
13068:     if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
13069:       if ( thislen > 0 ) break;		/* but make sure line has one word */
13070:     thislen += (wordlen+1); }		/* ptr past next whitespace char */
13071:   if ( thislen < 1 ) break;		/* line will have one too-long word*/
13072:   /*sol[thislen-1] = '\n';*/		/* replace last space with newline */
13073:   /*sol += thislen;*/			/* next line starts after newline */
13074:   iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
13075:   strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
13076:   sol += (thislen+2-iswhite);		/* next line starts after \\ */
13077:   } /* --- end-of-while(1) --- */
13078: end_of_job:
13079:   if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
13080:   return ( sbuff );			/* back with clean copy of s */
13081: } /* --- end-of-function strwrap() --- */
13082: 
13083: 
13084: /* ==========================================================================
13085:  * Function:	strnlower ( s, n )
13086:  * Purpose:	lowercase the first n chars of string s
13087:  * --------------------------------------------------------------------------
13088:  * Arguments:	s (I/O)		(char *)pointer to null-terminated string
13089:  *				whose chars are to be lowercased
13090:  *		n (I)		int containing max number of chars to be
13091:  *				lowercased (less than n will be lowercased
13092:  *				if terminating '\000' found first)
13093:  *				If n<=0 (or n>=strlen(s)) then the entire
13094:  *				string s will be lowercased
13095:  * --------------------------------------------------------------------------
13096:  * Returns:	( char * )	s (always same as input)
13097:  * --------------------------------------------------------------------------
13098:  * Notes:     o
13099:  * ======================================================================= */
13100: /* --- entry point --- */
13101: char	*strnlower ( char *s, int n )
13102: {
13103: /* -------------------------------------------------------------------------
13104: lowercase s
13105: -------------------------------------------------------------------------- */
13106: char	*p = s;				/* save s for return to caller */
13107: if ( !isempty(s) )			/* check for valid input */
13108:   while ( *p != '\000' ) {		/* lowercase each char till end */
13109:     *p = tolower(*p);			/* lowercase this char */
13110:     if ( n > 0 )			/* only lowercase first n chars */
13111:       if ( --n < 1 ) break;		/* quit when we're done */
13112:     p++; }				/* proceed to next char */
13113: return ( s );				/* back to caller with s */
13114: } /* --- end-of-function strnlower() --- */
13115: 
13116: 
13117: /* ==========================================================================
13118:  * Function:	urlprune ( url, n )
13119:  * Purpose:	Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
13120:  *		(if n=2 only ghi.com is returned, or if n=-1 only "ghi")
13121:  * --------------------------------------------------------------------------
13122:  * Arguments:	url (I)		char * to null-terminated string
13123:  *				containing url to be pruned
13124:  *		n (i)		int containing number of levels retained
13125:  *				in pruned url.  If n<0 its abs() is used,
13126:  *				but the topmost level (usually .com, .org,
13127:  *				etc) is omitted.  That is, if n=2 would
13128:  *				return "ghi.com" then n=-1 returns "ghi".
13129:  *				n=0 retains all levels.
13130:  * --------------------------------------------------------------------------
13131:  * Returns:	( char * )	pointer to (static) null-terminated string
13132:  *				containing pruned url with the first n
13133:  *				top-level domain, e.g., for n=2,
13134:  *				http://abc.def.ghi.com/etc returns ghi.com,
13135:  *				or an empty string "\000" for any error
13136:  * --------------------------------------------------------------------------
13137:  * Notes:     o
13138:  * ======================================================================= */
13139: /* --- entry point --- */
13140: char	*urlprune ( char *url, int n )
13141: {
13142: /* -------------------------------------------------------------------------
13143: Allocations and Declarations
13144: -------------------------------------------------------------------------- */
13145: static	char pruned[2048];		/* pruned url returned to caller */
13146: char	*purl = /*NULL*/pruned;		/* ptr to pruned, init for error */
13147: char	*delim = NULL;			/* delimiter separating components */
13148: char	*strnlower();			/* lowercase a string */
13149: int	istruncate = (n<0?1:0);		/*true to truncate .com from pruned*/
13150: int	ndots = 0;			/* number of dots found in url */
13151: /* -------------------------------------------------------------------------
13152: prune the url
13153: -------------------------------------------------------------------------- */
13154: /* --- first check input --- */
13155: *pruned = '\000';			/* init for error */
13156: if ( isempty(url) ) goto end_of_job;	/* missing input, so return NULL */
13157: if ( n < 0 ) n = (-n);			/* flip n positive */
13158: if ( n == 0 ) n = 999;			/* retain all levels of url */
13159: /* --- preprocess url --- */
13160: strninit(pruned,url,2032);		/* copy url to our static buffer */
13161: strlower(pruned);			/* lowercase it and... */
13162: trimwhite(pruned);			/*remove leading/trailing whitespace*/
13163: /* --- first remove leading http:// --- */
13164: if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
13165:   if ( ((int)(delim-pruned)) <= 8 ) {	/* make sure it's a prefix */
13166:     strsqueezep(pruned,delim+3);	/* squeeze out leading http:// */
13167:     trimwhite(pruned); }		/*remove leading/trailing whitespace*/
13168: /* --- next remove leading www. --- */
13169: if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
13170:   if ( ((int)(delim-pruned)) == 0 ) {	/* make sure it's the leading chars*/
13171:     strsqueezep(pruned,delim+4);	/* squeeze out leading www. */
13172:     trimwhite(pruned); }		/*remove leading/trailing whitespace*/
13173: /* --- finally remove leading / and everything following it --- */
13174: if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
13175:   *delim = '\000';			/* null-terminate url at first / */
13176: if ( isempty(pruned) ) goto end_of_job;	/* nothing left in url */
13177: /* --- count dots from back of url --- */
13178: delim = pruned + strlen(pruned);	/*ptr to '\000' terminating pruned*/
13179: while ( ((int)(delim-pruned)) > 0 ) {	/* don't back up before first char */
13180:   delim--;				/* ptr to preceding character */
13181:   if ( *delim != '.' ) continue;	/* not a dot, so keep looking */
13182:   ndots++;				/* count another dot found */
13183:   if ( istruncate ) {			/* remove trailing .com */
13184:     istruncate = 0;			/* don't truncate any more dots */
13185:     *delim = '\000';			/* truncate pruned url */
13186:     ndots = 0; }			/* and reset dot count */
13187:   if ( ndots >= n ) {			/* have all requested levels */
13188:     strsqueezep(pruned,delim+1);	/* squeeze out leading levels */
13189:     break; }				/* and we're done */
13190:   } /* --- end-of-while() --- */
13191: purl = pruned;				/*completed okay, return pruned url*/
13192: end_of_job:
13193:   return ( purl );			/* back with pruned url */
13194: } /* --- end-of-function urlprune() --- */
13195: 
13196: 
13197: /* ==========================================================================
13198:  * Function:	urlncmp ( url1, url2, n )
13199:  * Purpose:	Compares the n topmost levels of two urls
13200:  * --------------------------------------------------------------------------
13201:  * Arguments:	url1 (I)	char * to null-terminated string
13202:  *				containing url to be compared with url2
13203:  *		url2 (I)	char * to null-terminated string
13204:  *				containing url to be compared with url1
13205:  *		n (I)		int containing number of top levels
13206:  *				to compare, or 0 to compare them all.
13207:  *				n<0 compares that many top levels excluding
13208:  *				the last, i.e., for n=-1, xxx.com and xxx.org
13209:  *				would be considered a match
13210:  * --------------------------------------------------------------------------
13211:  * Returns:	( int )		1 if url's match, or
13212:  *				0 if not.
13213:  * --------------------------------------------------------------------------
13214:  * Notes:     o
13215:  * ======================================================================= */
13216: /* --- entry point --- */
13217: int	urlncmp ( char *url1, char *url2, int n )
13218: {
13219: /* -------------------------------------------------------------------------
13220: Allocations and Declarations
13221: -------------------------------------------------------------------------- */
13222: char	*urlprune(), *prune=NULL,	/* prune url's */
13223: 	prune1[4096], prune2[4096];	/* pruned copies of url1,url2 */
13224: int	ismatch = 0;			/* true if url's match */
13225: /* -------------------------------------------------------------------------
13226: prune url's and compare the pruned results
13227: -------------------------------------------------------------------------- */
13228: /* --- check input --- */
13229: if ( isempty(url1)			/*make sure both url1,url2 supplied*/
13230: ||   isempty(url2) ) goto end_of_job;	/* missing input, so return 0 */
13231: /* --- prune url's --- */
13232: prune = urlprune(url1,n);		/* ptr to pruned version of url1 */
13233: if ( isempty(prune) ) goto end_of_job;	/* some problem with url1 */
13234: strninit(prune1,prune,4064);		/* local copy of pruned url1 */
13235: prune = urlprune(url2,n);		/* ptr to pruned version of url2 */
13236: if ( isempty(prune) ) goto end_of_job;	/* some problem with url2 */
13237: strninit(prune2,prune,4064);		/* local copy of pruned url2 */
13238: /* --- compare pruned url's --- */
13239: if ( strcmp(prune1,prune2) == 0 )	/* pruned url's are identical */
13240:   ismatch = 1;				/* signal match to caller */
13241: end_of_job:
13242:   return ( ismatch );			/*back with #matching url components*/
13243: } /* --- end-of-function urlncmp() --- */
13244: 
13245: 
13246: /* ==========================================================================
13247:  * Function:	dbltoa ( dblval, npts )
13248:  * Purpose:	Converts double to ascii, in financial format
13249:  *		(e.g., comma-separated and negatives enclosed in ()'s).
13250:  * -------------------------------------------------------------------------
13251:  * Arguments:	dblval (I)	double containing value to be converted.
13252:  *		npts (I)	int containing #places after decimal point
13253:  *				to be displayed in returned string.
13254:  * Returns:	( char * )	null-terminated string containing
13255:  *				double converted to financial format.
13256:  * -------------------------------------------------------------------------
13257:  * Notes:     o
13258:  * ======================================================================= */
13259: /* --- entry point --- */
13260: char	*dbltoa ( double dblval, int npts )
13261: /* double dblval;
13262:    int	npts; */
13263: {
13264: /* -------------------------------------------------------------------------
13265: Allocations and Declarations
13266: ------------------------------------------------------------------------- */
13267: static	char finval[256];		/* buffer returned to caller */
13268: static	char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
13269: char	*finptr = finval;		/* ptr to next char being converted*/
13270: double	floor();			/* integer which is glb(double) */
13271: double	dbldigit;			/* to shift out digits from dblval */
13272: int	digit;				/* one digit from dblval */
13273: int	isneg = 0;			/* reset true if dblval negative */
13274: int	ifrac = 0;			/* npts fractional digits of dblval*/
13275: char	digits[64]; int ndigits=0;	/* all the digits [0]=least signif */
13276: /* -------------------------------------------------------------------------
13277: Check sign
13278: ------------------------------------------------------------------------- */
13279: if ( dblval < 0.0 )			/* got a negative value to convert */
13280:     { isneg=1; dblval=(-dblval); }	/* set flag and make it positive */
13281: /* -------------------------------------------------------------------------
13282: Get fractional part of dblval if required
13283: ------------------------------------------------------------------------- */
13284: if ( npts > 0 )
13285:     { int ipts = npts;			/* loop index */
13286:       dbldigit = dblval-floor(dblval);	/* fractional part as double */
13287:       digit = 1;			/* check if rounded frac > 1 */
13288:       while ( --ipts >= 0 )		/* count down */
13289: 	{ dbldigit *= 10.0;		/* shift left one digit at a time */
13290: 	  digit *= 10; }		/* and keep max up-to-date */
13291:       ifrac = (int)(dbldigit + 0.5);	/* store fractional part as integer*/
13292:       if ( ifrac >= digit )		/* round to next whole number */
13293: 	{ dblval++; ifrac=0; }		/* bump val, reset frac to zero */
13294:     } /* --- end-of-if(npts>0) --- */
13295: else dblval += 0.5;			/* no frac, round to nearest whole */
13296: /* -------------------------------------------------------------------------
13297: Get whole digits
13298: ------------------------------------------------------------------------- */
13299: dblval = floor(dblval);			/* get rid of fractional part */
13300: while ( dblval > 0.0 )			/* still have data digits remaining*/
13301:     { dbldigit = floor(dblval/10.0);	/* shift out next digit */
13302:       digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
13303:       if ( digit<0 || digit>9 ) digit=10; /* index check */
13304:       digits[ndigits++] = digittbl[digit]; /* store ascii digit */
13305:       dblval = dbldigit; }		/* ready for next digit */
13306: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
13307: /* -------------------------------------------------------------------------
13308: Format whole part from digits[] array
13309: ------------------------------------------------------------------------- */
13310: if ( isneg ) *finptr++ = '(';		/* leading paren for negative value*/
13311: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
13312:     { *finptr++ = digits[digit];	/* store digit */
13313:       if ( digit>0 && digit%3==0 )	/* need a comma */
13314: 	*finptr++ = ','; }		/* put in separating comma */
13315: /* -------------------------------------------------------------------------
13316: Format fractional part using ifrac
13317: ------------------------------------------------------------------------- */
13318: if ( npts > 0 )
13319:     { *finptr++ = '.';			/* start with decimal point */
13320:       sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
13321:       finptr += npts; }			/* bump ptr past fractional digits */
13322: /* -------------------------------------------------------------------------
13323: End-of-Job
13324: ------------------------------------------------------------------------- */
13325: if ( isneg ) *finptr++ = ')';		/*trailing paren for negative value*/
13326: *finptr = '\000';			/* null-terminate converted double */
13327: return ( finval );			/* converted double back to caller */
13328: } /* --- end-of-function dbltoa() --- */
13329: 
13330: 
13331: /* ==========================================================================
13332:  * Function:	aalowpass ( rp, bytemap, grayscale )
13333:  * Purpose:	calculates a lowpass anti-aliased bytemap
13334:  *		for rp->bitmap, with each byte 0...grayscale-1
13335:  * --------------------------------------------------------------------------
13336:  * Arguments:	rp (I)		raster *  to raster whose bitmap
13337:  *				is to be anti-aliased
13338:  *		bytemap (O)	intbyte * to bytemap, calculated
13339:  *				by applying lowpass filter to rp->bitmap,
13340:  *				and returned (as you'd expect) in 1-to-1
13341:  *				addressing correspondence with rp->bitmap
13342:  *		grayscale (I)	int containing number of grayscales
13343:  *				to be calculated, 0...grayscale-1
13344:  *				(should typically be given as 256)
13345:  * --------------------------------------------------------------------------
13346:  * Returns:	( int )		1=success, 0=any error
13347:  * --------------------------------------------------------------------------
13348:  * Notes:     o	If the center point of the box being averaged is black,
13349:  *		then the entire "average" is forced black (grayscale-1)
13350:  *		regardless of the surrounding points.  This is my attempt
13351:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
13352:  *		lines, which would otherwise turn from black to a gray shade.
13353:  *	     o	Also, while the weights for neighbor points are fixed,
13354:  *		you may adjust the center point weight on the compile line.
13355:  *		A higher weight sharpens the resulting anti-aliased image;
13356:  *		lower weights blur it out more (but keep the "center" black
13357:  *		as per the preceding note).
13358:  * ======================================================================= */
13359: /* --- entry point --- */
13360: int	aalowpass (raster *rp, intbyte *bytemap, int grayscale)
13361: {
13362: /* -------------------------------------------------------------------------
13363: Allocations and Declarations
13364: -------------------------------------------------------------------------- */
13365: int	status = 1;			/* 1=success, 0=failure to caller */
13366: pixbyte	*bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
13367: int	irow=0, icol=0;			/* rp->height, rp->width indexes */
13368: int	weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
13369: int	adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
13370: int	totwts = 0;			/* sum of all weights in matrix */
13371: int	isforceavg = 1,			/*force avg black if center black?*/
13372: 	isminmaxwts = 1,		/*use wts or #pts for min/max test */
13373: 	blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
13374: /* -------------------------------------------------------------------------
13375: Initialization
13376: -------------------------------------------------------------------------- */
13377: /* --- calculate total weights --- */
13378: weights[4]= centerwt;			/* weight for center point */
13379: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
13380: totwts = centerwt + 4*(1+adjacentwt);	/* tot is center plus neighbors */
13381: /* -------------------------------------------------------------------------
13382: Calculate bytemap as 9-point weighted average over bitmap
13383: -------------------------------------------------------------------------- */
13384: for ( irow=0; irow<rp->height; irow++ )
13385:  for ( icol=0; icol<rp->width; icol++ )
13386:   {
13387:   int	ipixel = icol + irow*(rp->width); /* center pixel index */
13388:   int	jrow=0, jcol=0,			/* box around ipixel */
13389: 	bitval = 0,			/* value of bit/pixel at jrow,jcol */
13390: 	iscenter = 0,			/* set true if center pixel black */
13391: 	nadjacent=0, wadjacent=0,	/* #adjacent black pixels, their wts*/
13392: 	ngaps = 0,			/* #gaps in 8 pixels around center */
13393: 	iwt=(-1), sumwts=0;		/* weights index, sum in-bound wts */
13394:   char	adjmatrix[8];			/* adjacency "matrix" */
13395:   memset(adjmatrix,0,8);		/* zero out adjacency matrix */
13396:   bytemap[ipixel] = 0;			/* init pixel white */
13397:   /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
13398:   for ( jrow=irow-1; jrow<=irow+1; jrow++ )  /* jrow = irow-1...irow+1 */
13399:    for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
13400:     {
13401:     int	jpixel = jcol + jrow*(rp->width); /* averaging index */
13402:     iwt++;				/*always bump weight index*/
13403:     if ( jrow<0 || jrow>=rp->height	/* if row out pf bounds */
13404:     ||   jcol<0 || jcol>=rp->width )	/* or col out of bounds */
13405: 	continue;			/* ignore this point */
13406:     bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
13407:     if ( bitval )			/* this is a black pixel */
13408:       {	if ( jrow==irow && jcol==icol )	/* and this is center point */
13409: 	  iscenter = 1;			/* set flag for center point black */
13410: 	else				/* adjacent point black */
13411: 	  { nadjacent++;		/* bump adjacent black count */
13412: 	    adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
13413: 	wadjacent += weights[iwt]; }	/* sum weights for black pixels */
13414:     sumwts += weights[iwt];		/* and sum weights for all pixels */
13415:     } /* --- end-of-for(jrow,jcol) --- */
13416:   /* --- count gaps --- */
13417:   ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
13418:   for ( iwt=0; iwt<7; iwt++ )		/* clockwise around adjacency */
13419:     if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
13420:   ngaps /= 2;				/*each gap has 2 black/white flips*/
13421:   /* --- anti-alias pixel, but leave it black if it was already black --- */
13422:   if ( isforceavg && iscenter )		/* force avg if center point black */
13423:       bytemap[ipixel] = grayscale-1;	/* so force grayscale-1=black */
13424:   else					/* center point not black */
13425:    if ( ngaps <= 2 )			/*don't darken checkerboarded pixel*/
13426:     { bytemap[ipixel] =			/* 0=white ... grayscale-1=black */
13427: 	((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
13428:       if ( blackscale > 0		/* blackscale kludge turned on */
13429:       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
13430: 	bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
13431:   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
13432:   if ( !iscenter ) {			/* apply min/maxadjacent test */
13433:    if ( isminmaxwts )			/* min/max refer to adjacent weights*/
13434:     { if ( wadjacent < minadjacent	/* wts of adjacent points too low */
13435:       ||   wadjacent > maxadjacent )	/* or too high */
13436: 	bytemap[ipixel] = 0; }		/* so leave point white */
13437:    else					/* min/max refer to #adjacent points*/
13438:     { if ( nadjacent < minadjacent	/* too few adjacent points black */
13439:       ||   nadjacent > maxadjacent )	/* or too many */
13440: 	bytemap[ipixel] = 0; } }	/* so leave point white */
13441:   } /* --- end-of-for(irow,icol) --- */
13442: /* -------------------------------------------------------------------------
13443: Back to caller with gray-scale anti-aliased bytemap
13444: -------------------------------------------------------------------------- */
13445: /*end_of_job:*/
13446:   return ( status );
13447: } /* --- end-of-function aalowpass() --- */
13448: 
13449: 
13450: /* ==========================================================================
13451:  * Function:	aapnm ( rp, bytemap, grayscale )
13452:  * Purpose:	calculates a lowpass anti-aliased bytemap
13453:  *		for rp->bitmap, with each byte 0...grayscale-1,
13454:  *		based on the pnmalias.c algorithm
13455:  * --------------------------------------------------------------------------
13456:  * Arguments:	rp (I)		raster *  to raster whose bitmap
13457:  *				is to be anti-aliased
13458:  *		bytemap (O)	intbyte * to bytemap, calculated
13459:  *				by applying pnm-based filter to rp->bitmap,
13460:  *				and returned (as you'd expect) in 1-to-1
13461:  *				addressing correspondence with rp->bitmap
13462:  *		grayscale (I)	int containing number of grayscales
13463:  *				to be calculated, 0...grayscale-1
13464:  *				(should typically be given as 256)
13465:  * --------------------------------------------------------------------------
13466:  * Returns:	( int )		1=success, 0=any error
13467:  * --------------------------------------------------------------------------
13468:  * Notes:    o	Based on the pnmalias.c algorithm in the netpbm package
13469:  *		on sourceforge.
13470:  * ======================================================================= */
13471: /* --- entry point --- */
13472: int	aapnm (raster *rp, intbyte *bytemap, int grayscale)
13473: {
13474: /* -------------------------------------------------------------------------
13475: Allocations and Declarations
13476: -------------------------------------------------------------------------- */
13477: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
13478: int	width=rp->width, height=rp->height, /* width, height of raster */
13479: 	icol = 0,        irow = 0,	/* width, height indexes */
13480: 	imap = (-1);			/* pixel index = icol + irow*width */
13481: int	bgbitval=0, fgbitval=1;		/* background, foreground bitval */
13482: int	isfirstaa = 1;			/*debugging switch signals 1st pixel*/
13483: #if 0
13484: int	totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
13485: int	totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
13486: #endif
13487: int	totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
13488: int	isresetparams = 1,		/* true to set antialiasing params */
13489: 	isfgalias  = 1,			/* true to antialias fg bits */
13490: 	isfgonly   = 0,			/* true to only antialias fg bits */
13491: 	isbgalias  = 0,			/* true to antialias bg bits */
13492: 	isbgonly   = 0;			/* true to only antialias bg bits */
13493: /* -------------------------------------------------------------------------
13494: Initialization
13495: -------------------------------------------------------------------------- */
13496: /* --- check for bold light --- */
13497: if ( 0 )
13498:  { if ( weightnum > 2 ) { isbgalias=1; }	/* simulate bold */
13499:    if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
13500: /* --- reset wts[], etc, and calculate total weights --- */
13501: if ( isresetparams )			/* wts[], etc taken from params */
13502:   { int	iwt=0;				/* wts[iwt] index */
13503:     wts[4]= centerwt;			/* weight for center point */
13504:     wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
13505:     wts[0]=wts[2]=wts[6]=wts[8] = cornerwt;   /* and corner points */
13506:     for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
13507:     isfgalias = fgalias;		/* set isfgalias */
13508:     isfgonly = fgonly;			/* set isfgonly */
13509:     isbgalias = bgalias;		/* set isbgalias */
13510:     isbgonly = bgonly; }		/* set isbgonly */
13511: /* -------------------------------------------------------------------------
13512: Calculate bytemap as 9-point weighted average over bitmap
13513: -------------------------------------------------------------------------- */
13514: for ( irow=0; irow<height; irow++ )
13515:  for ( icol=0; icol<width; icol++ )
13516:   {
13517:   /* --- local allocations and declarations --- */
13518:   int	bitval=0,			/* value of rp bit at irow,icol */
13519: 	nnbitval=0, nebitval=0, eebitval=0, sebitval=0,	/*adjacent vals*/
13520: 	ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0;	/*compass pt names*/
13521:   int	isbgedge=0, isfgedge=0;		/*does pixel border a bg or fg edge*/
13522:   int	aabyteval=0;			/* antialiased (or unchanged) value*/
13523:   /* --- bump imap index and get center bit value --- */
13524:   imap++;				/* imap = icol + irow*width */
13525:   bitval = getlongbit(bitmap,imap);	/* value of rp input bit at imap */
13526:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
13527:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
13528:   /* --- check if we're antialiasing this pixel --- */
13529:   if ( (isbgonly && bitval==fgbitval)	/* only antialias background bit */
13530:   ||   (isfgonly && bitval==bgbitval) )	/* only antialias foreground bit */
13531:     continue;				/* leave default and do next bit */
13532:   /* --- get surrounding bits --- */
13533:   if ( irow > 0 )			/* nn (north) bit available */
13534:      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
13535:   if ( irow < height-1 )		/* ss (south) bit available */
13536:      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
13537:   if ( icol > 0 )			/* ww (west) bit available */
13538:    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
13539:      if ( irow > 0 )			/* nw bit available */
13540:        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
13541:      if ( irow < height-1 )		/* sw bit available */
13542:        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
13543:   if ( icol < width-1 )			/* ee (east) bit available */
13544:    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
13545:      if ( irow > 0 )			/* ne bit available */
13546:        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
13547:      if ( irow < height-1 )		/* se bit available */
13548:        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
13549:   /* --- check for edges --- */
13550:   isbgedge =				/* current pixel borders a bg edge */
13551: 	(nnbitval==bgbitval && eebitval==bgbitval) ||	/*upper-right edge*/
13552: 	(eebitval==bgbitval && ssbitval==bgbitval) ||	/*lower-right edge*/
13553: 	(ssbitval==bgbitval && wwbitval==bgbitval) ||	/*lower-left  edge*/
13554: 	(wwbitval==bgbitval && nnbitval==bgbitval) ;	/*upper-left  edge*/
13555:   isfgedge =				/* current pixel borders an fg edge*/
13556: 	(nnbitval==fgbitval && eebitval==fgbitval) ||	/*upper-right edge*/
13557: 	(eebitval==fgbitval && ssbitval==fgbitval) ||	/*lower-right edge*/
13558: 	(ssbitval==fgbitval && wwbitval==fgbitval) ||	/*lower-left  edge*/
13559: 	(wwbitval==fgbitval && nnbitval==fgbitval) ;	/*upper-left  edge*/
13560:   /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
13561:   if ( 1 ) {				/* true to perform test */
13562:     int	isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
13563:     isbghorz =				/* top or bottom edge is all bg */
13564: 	(nwbitval+nnbitval+nebitval == 3*bgbitval) ||	/* top edge bg */
13565: 	(swbitval+ssbitval+sebitval == 3*bgbitval) ;	/* bottom edge bg */
13566:     isfghorz =				/* top or bottom edge is all fg */
13567: 	(nwbitval+nnbitval+nebitval == 3*fgbitval) ||	/* top edge fg */
13568: 	(swbitval+ssbitval+sebitval == 3*fgbitval) ;	/* bottom edge fg */
13569:     isbgvert =				/* left or right edge is all bg */
13570: 	(nwbitval+wwbitval+swbitval == 3*bgbitval) ||	/* left edge bg */
13571: 	(nebitval+eebitval+sebitval == 3*bgbitval) ;	/* right edge bg */
13572:     isfgvert =				/* left or right edge is all bg */
13573: 	(nwbitval+wwbitval+swbitval == 3*fgbitval) ||	/* left edge fg */
13574: 	(nebitval+eebitval+sebitval == 3*fgbitval) ;	/* right edge fg */
13575:     if ( (isbghorz && isbgvert && (bitval==fgbitval))	/* we're at an...*/
13576:     ||   (isfghorz && isfgvert && (bitval==bgbitval)) )	/*...inside corner */
13577: 	continue;					/* don't antialias */
13578:     } /* --- end-of-if(1) --- */
13579:   /* --- check #gaps for checkerboard (added by j.forkosh) --- */
13580:   if ( 0 ) {				/* true to perform test */
13581:     int	ngaps=0, mingaps=1,maxgaps=2;	/* count #fg/bg flips (max=4 noop) */
13582:     if ( nwbitval!=nnbitval ) ngaps++;	/* upper-left =? upper */
13583:     if ( nnbitval!=nebitval ) ngaps++;	/* upper =? upper-right */
13584:     if ( nebitval!=eebitval ) ngaps++;	/* upper-right =? right */
13585:     if ( eebitval!=sebitval ) ngaps++;	/* right =? lower-right */
13586:     if ( sebitval!=ssbitval ) ngaps++;	/* lower-right =? lower */
13587:     if ( ssbitval!=swbitval ) ngaps++;	/* lower =? lower-left */
13588:     if ( swbitval!=wwbitval ) ngaps++;	/* lower-left =? left */
13589:     if ( wwbitval!=nwbitval ) ngaps++;	/* left =? upper-left */
13590:     if ( ngaps > 0 ) ngaps /= 2;	/* each gap has 2 bg/fg flips */
13591:     if ( ngaps<mingaps || ngaps>maxgaps ) continue;
13592:     } /* --- end-of-if(1) --- */
13593:   /* --- antialias if necessary --- */
13594:   if ( (isbgalias && isbgedge)		/* alias pixel surrounding bg */
13595:   ||   (isfgalias && isfgedge)		/* alias pixel surrounding fg */
13596:   ||   (isbgedge  && isfgedge) )	/* neighboring fg and bg pixel */
13597:     {
13598:     int	aasumval =			/* sum wts[]*bitmap[] */
13599: 	wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
13600: 	wts[3]*wwbitval +  wts[4]*bitval  + wts[5]*eebitval +
13601: 	wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
13602:     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
13603:     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
13604:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
13605:     if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
13606:       "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
13607:       (isfirstaa?"aapnm algorithm":"aapnm"),
13608:       irow,icol,imap, aawtval,aabyteval);
13609:       isfirstaa = 0; }
13610:     } /* --- end-of-if(isedge) --- */
13611:   } /* --- end-of-for(irow,icol) --- */
13612: /* -------------------------------------------------------------------------
13613: Back to caller with gray-scale anti-aliased bytemap
13614: -------------------------------------------------------------------------- */
13615: /*end_of_job:*/
13616:   return ( 1 );
13617: } /* --- end-of-function aapnm() --- */
13618: 
13619: 
13620: /* ==========================================================================
13621:  * Function:	aapnmlookup ( rp, bytemap, grayscale )
13622:  * Purpose:	calculates a lowpass anti-aliased bytemap
13623:  *		for rp->bitmap, with each byte 0...grayscale-1,
13624:  *		based on the pnmalias.c algorithm.
13625:  *		This version uses aagridnum() and aapatternnum() lookups
13626:  *		to interpret 3x3 lowpass pixel grids.
13627:  * --------------------------------------------------------------------------
13628:  * Arguments:	rp (I)		raster *  to raster whose bitmap
13629:  *				is to be anti-aliased
13630:  *		bytemap (O)	intbyte * to bytemap, calculated
13631:  *				by applying pnm-based filter to rp->bitmap,
13632:  *				and returned (as you'd expect) in 1-to-1
13633:  *				addressing correspondence with rp->bitmap
13634:  *		grayscale (I)	int containing number of grayscales
13635:  *				to be calculated, 0...grayscale-1
13636:  *				(should typically be given as 256)
13637:  * --------------------------------------------------------------------------
13638:  * Returns:	( int )		1=success, 0=any error
13639:  * --------------------------------------------------------------------------
13640:  * Notes:    o	Based on the pnmalias.c algorithm in the netpbm package
13641:  *		on sourceforge.
13642:  *	     o	This version uses aagridnum() and aapatternnum() lookups
13643:  *		to interpret 3x3 lowpass pixel grids.
13644:  * ======================================================================= */
13645: /* --- entry point --- */
13646: int	aapnmlookup (raster *rp, intbyte *bytemap, int grayscale)
13647: {
13648: /* -------------------------------------------------------------------------
13649: Allocations and Declarations
13650: -------------------------------------------------------------------------- */
13651: int	width=rp->width, height=rp->height, /* width, height of raster */
13652: 	icol = 0,        irow = 0,	/* width, height indexes */
13653: 	imap = (-1);			/* pixel index = icol + irow*width */
13654: int	bgbitval=0, fgbitval=1;		/* background, foreground bitval */
13655: int	isfirstaa = 1;			/*debugging switch signals 1st pixel*/
13656: int	aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt,
13657: 	totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/
13658: int	isfgalias  = fgalias,		/*(1) true to antialias fg bits */
13659: 	isfgonly   = fgonly,		/*(0) true to only antialias fg bits*/
13660: 	isbgalias  = bgalias,		/*(0) true to antialias bg bits */
13661: 	isbgonly   = bgonly;		/*(0) true to only antialias bg bits*/
13662: int	gridnum=(-1), aagridnum(),	/* grid# for 3x3 grid at irow,icol */
13663: 	patternum=(-1), aapatternnum();	/*pattern#, 1-51, for input gridnum*/
13664: int	aapatterns();			/* to antialias special patterns */
13665: /* ---
13666:  * pattern number data
13667:  * ------------------- */
13668: /* --- number of adjacent fg pixels set in pattern --- */
13669: static	int nadjacents[] = { -1,	/* #adjacent fg pixels for pattern */
13670:    0,  4,  0,  1,  4,  3,  1,  0,  1,  0,	/*  1-10 */
13671:    2,  2,  3,  4,  3,  4,  2,  2,  1,  2,	/* 11-20 */
13672:    1,  2,  1,  2,  0,  1,  3,  2,  3,  2,	/* 21-30 */
13673:    3,  2,  3,  2,  4,  3,  1,  2,  2,  2,	/* 31-40 */
13674:    2,  1,  2,  2,  3,  0,  3,  2,  2,  1,  4,	/* 41-51 */
13675:    -1 } ; /* --- end-of-nadjacents[] --- */
13676: /* --- number of corner fg pixels set in pattern --- */
13677: static	int ncorners[] = { -1,		/* #corner fg pixels for pattern */
13678:    0,  4,  1,  0,  3,  4,  1,  2,  1,  2,	/*  1-10 */
13679:    0,  0,  3,  2,  3,  2,  4,  4,  2,  1,	/* 11-20 */
13680:    2,  1,  2,  1,  3,  2,  0,  1,  2,  3,	/* 21-30 */
13681:    2,  3,  2,  3,  1,  2,  4,  3,  2,  2,	/* 31-40 */
13682:    2,  3,  2,  2,  1,  4,  1,  2,  2,  3,  0,	/* 41-51 */
13683:    -1 } ; /* --- end-of-ncorners[] --- */
13684: /* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */
13685: static	int horzedges[] = { -1,		/* 0,1,2 = horz bg,fg,both edge */
13686:    0,  1,  0,  0,  1,  1,  0,  0,  0, -1,	/*  1-10 */
13687:    0, -1,  1,  1,  1, -1,  1, -1,  2,  0,	/* 11-20 */
13688:   -1, -1, -1,  0, -1, -1, -1, -1,  2,  1,	/* 21-30 */
13689:   -1, -1, -1,  1, -1, -1, -1, -1,  2, -1,	/* 31-40 */
13690:   -1,  1,  1, -1, -1, -1,  0,  0, -1, -1, -1,	/* 41-51 */
13691:    -1 } ; /* --- end-of-horzedges[] --- */
13692: /* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */
13693: static	int vertedges[] = { -1,		/* 0,1,2 = vert bg,fg,both edge */
13694:    0,  1,  0,  0,  1,  1,  0, -1, -1, -1,	/*  1-10 */
13695:    0,  0,  1, -1, -1, -1,  1,  1, -1, -1,	/* 11-20 */
13696:   -1,  0,  0,  0, -1, -1,  0, -1, -1, -1,	/* 21-30 */
13697:   -1,  1,  1,  1, -1, -1,  1, -1, -1, -1,	/* 31-40 */
13698:   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	/* 41-51 */
13699:    -1 } ; /* --- end-of-vertedges[] --- */
13700: /* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */
13701: static	int diagedges[] = { -1,		/* 0,1,2 = diag bg,fg,both edge */
13702:    0,  1,  0,  0,  1,  1,  0,  0,  0,  0,	/*  1-10 */
13703:    2, -1,  1,  1,  1,  1,  1, -1,  0,  2,	/* 11-20 */
13704:    0, -1,  0,  2,  0, -1,  1,  1,  1,  1,	/* 21-30 */
13705:    1, -1,  1,  2,  1,  1, -1,  1,  2, -1,	/* 31-40 */
13706:    1,  0, -1,  2,  1,  0,  1, -1,  1, -1,  1,	/* 41-51 */
13707:    -1 } ; /* --- end-of-diagedges[] --- */
13708: /* -------------------------------------------------------------------------
13709: Calculate bytemap as 9-point weighted average over bitmap
13710: -------------------------------------------------------------------------- */
13711: for ( irow=0; irow<height; irow++ )
13712:  for ( icol=0; icol<width; icol++ )
13713:   {
13714:   /* --- local allocations and declarations --- */
13715:   int	bitval=0,			/* value of rp bit at irow,icol */
13716: 	isbgdiag=0, isfgdiag=0,		/*does pixel border a bg or fg edge*/
13717: 	aabyteval=0;			/* antialiased (or unchanged) value*/
13718:   /* --- get gridnum and center bit value, init aabyteval --- */
13719:   imap++;				/* first set imap=icol + irow*width*/
13720:   gridnum = aagridnum(rp,irow,icol);	/*grid# coding 3x3 grid at irow,icol*/
13721:   bitval = (gridnum&1);			/* center bit set if gridnum odd */
13722:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
13723:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
13724:   if ( gridnum<0 || gridnum>511 ) continue; /* gridnum out of bounds*/
13725:   /* --- check if we're antialiasing this pixel --- */
13726:   if ( (isbgonly && bitval==fgbitval)	/* only antialias background bit */
13727:   ||   (isfgonly && bitval==bgbitval) )	/* only antialias foreground bit */
13728:     continue;				/* leave default and do next bit */
13729:   /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
13730:   patternum = aapatternnum(gridnum);	/* look up pattern number */
13731:   if ( patternum<1 || patternum>51 ) continue; /* some internal error */
13732:   /* --- special pattern number processing --- */
13733:   if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale))
13734:   >=   0 ) {				/* special processing for pattern */
13735:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
13736:     continue; }				/* and continue with next pixel */
13737:   /* --- check for diagonal edges --- */
13738:   isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/
13739:                diagedges[patternum]==0 );
13740:   isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/
13741:                diagedges[patternum]==1 );
13742:   /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
13743:   if ( 1 ) {				/* true to perform test */
13744:     int	isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */
13745: 	horzedge=horzedges[patternum], vertedge=vertedges[patternum];
13746:     isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */
13747:     isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */
13748:     isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */
13749:     isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */
13750:     if ( (isbghorz && isbgvert && (bitval==fgbitval))	/* we're at an...*/
13751:     ||   (isfghorz && isfgvert && (bitval==bgbitval)) )	/*...inside corner */
13752: 	continue;					/* don't antialias */
13753:     } /* --- end-of-if(1) --- */
13754: #if 0
13755:   /* --- check #gaps for checkerboard (added by j.forkosh) --- */
13756:   if ( 0 ) {				/* true to perform test */
13757:     int	ngaps=0, mingaps=1,maxgaps=2;	/* count #fg/bg flips (max=4 noop) */
13758:     if ( nwbitval!=nnbitval ) ngaps++;	/* upper-left =? upper */
13759:     if ( nnbitval!=nebitval ) ngaps++;	/* upper =? upper-right */
13760:     if ( nebitval!=eebitval ) ngaps++;	/* upper-right =? right */
13761:     if ( eebitval!=sebitval ) ngaps++;	/* right =? lower-right */
13762:     if ( sebitval!=ssbitval ) ngaps++;	/* lower-right =? lower */
13763:     if ( ssbitval!=swbitval ) ngaps++;	/* lower =? lower-left */
13764:     if ( swbitval!=wwbitval ) ngaps++;	/* lower-left =? left */
13765:     if ( wwbitval!=nwbitval ) ngaps++;	/* left =? upper-left */
13766:     if ( ngaps > 0 ) ngaps /= 2;	/* each gap has 2 bg/fg flips */
13767:     if ( ngaps<mingaps || ngaps>maxgaps ) continue;
13768:     } /* --- end-of-if(1) --- */
13769: #endif
13770:   /* --- antialias if necessary --- */
13771:   if ( (isbgalias && isbgdiag)		/* alias pixel surrounding bg */
13772:   ||   (isfgalias && isfgdiag)		/* alias pixel surrounding fg */
13773:   ||   (isbgdiag  && isfgdiag) )	/* neighboring fg and bg pixel */
13774:     {
13775:     int	aasumval =			/* sum wts[]*bitmap[] */
13776: 	aacenterwt*bitval +		/* apply centerwt to center pixel */
13777: 	aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */
13778: 	aacornerwt*ncorners[patternum];	/* and corners */
13779:     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
13780:     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
13781:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
13782:     if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
13783:       "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d",
13784:       (isfirstaa?"aapnmlookup algorithm":"aapnm"),
13785:       irow,icol,imap, aawtval,aabyteval);
13786:       if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */
13787:       else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum);
13788:       isfirstaa = 0; }
13789:     } /* --- end-of-if(isedge) --- */
13790:   } /* --- end-of-for(irow,icol) --- */
13791: /* -------------------------------------------------------------------------
13792: Back to caller with gray-scale anti-aliased bytemap
13793: -------------------------------------------------------------------------- */
13794: /*end_of_job:*/
13795:   return ( 1 );
13796: } /* --- end-of-function aapnmlookup() --- */
13797: 
13798: 
13799: /* ==========================================================================
13800:  * Function:	aapatterns ( rp, irow, icol, gridnum, patternum, grayscale )
13801:  * Purpose:	For patterns requireing special processing,
13802:  *		calculates anti-aliased value for pixel at irow,icol,
13803:  *		whose surrounding 3x3 pixel grid is coded by gridnum
13804:  *		(which must correspond to a pattern requiring special
13805:  *		processing).
13806:  * --------------------------------------------------------------------------
13807:  * Arguments:	rp (I)		raster *  to raster whose bitmap
13808:  *				is to be anti-aliased
13809:  *		irow (I)	int containing row, 0...height-1,
13810:  *				of pixel to be antialiased
13811:  *		icol (I)	int containing col, 0...width-1,
13812:  *				of pixel to be antialiased
13813:  *		gridnum (I)	int containing 0...511 corresponding to
13814:  *				3x3 pixel grid surrounding irow,icol
13815:  *		patternum (I)	int containing 1...51 pattern# of
13816:  *				the 3x3 grid surrounding irow,icol
13817:  *		grayscale (I)	int containing number of grayscales
13818:  *				to be calculated, 0...grayscale-1
13819:  *				(should typically be given as 256)
13820:  * --------------------------------------------------------------------------
13821:  * Returns:	( int )		0...grayscale-1 for success,
13822:  *				-1 = error, or no special processing required
13823:  * --------------------------------------------------------------------------
13824:  * Notes:    o
13825:  * ======================================================================= */
13826: /* --- entry point --- */
13827: int	aapatterns (raster *rp, int irow, int icol,
13828: 	int gridnum, int patternum, int grayscale)
13829: {
13830: /* -------------------------------------------------------------------------
13831: Allocations and Declarations
13832: -------------------------------------------------------------------------- */
13833: int	aaval = (-1);			/* antialiased value returned */
13834: int	iscenter = (gridnum&1);		/* true if center pixel set/black */
13835: int	aapatternnum(),			/* if patternum not supplied */
13836: 	aapattern1124(),		/* routine for patterns #11,24 */
13837: 	aapattern19(),			/* special routine for pattern #19 */
13838: 	aapattern20(),			/* special routine for pattern #20 */
13839: 	aapattern39();			/* special routine for pattern #39 */
13840: /* -------------------------------------------------------------------------
13841: special pattern number processing
13842: -------------------------------------------------------------------------- */
13843: if ( 1 ) {
13844:   if ( patternum < 1 )			/* pattern# not supplied by caller */
13845:     patternum = aapatternnum(gridnum);	/* so look it up ourselves */
13846:   switch ( patternum ) {
13847:     default: break;			/* no special processing */
13848:     case 11:
13849:     case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break;
13850:     case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break;
13851:     case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break;
13852:     case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break;
13853:     /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */
13854:     case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */
13855:     } /* --- end-of-switch(patternum) --- */
13856:   } /* --- end-of-if() --- */
13857: return ( aaval );			/* return antialiased val to caller*/
13858: } /* --- end-of-function aapatterns() --- */
13859: 
13860: 
13861: /* ==========================================================================
13862:  * Function:	aapattern1124 ( rp, irow, icol, gridnum, grayscale )
13863:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
13864:  *		whose surrounding 3x3 pixel grid is coded by gridnum
13865:  *		(which must correspond to pattern #11 or #24).
13866:  * --------------------------------------------------------------------------
13867:  * Arguments:	rp (I)		raster *  to raster whose bitmap
13868:  *				is to be anti-aliased
13869:  *		irow (I)	int containing row, 0...height-1,
13870:  *				of pixel to be antialiased
13871:  *		icol (I)	int containing col, 0...width-1,
13872:  *				of pixel to be antialiased
13873:  *		gridnum (I)	int containing 0...511 corresponding to
13874:  *				3x3 pixel grid surrounding irow,icol
13875:  *		grayscale (I)	int containing number of grayscales
13876:  *				to be calculated, 0...grayscale-1
13877:  *				(should typically be given as 256)
13878:  * --------------------------------------------------------------------------
13879:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
13880:  * --------------------------------------------------------------------------
13881:  * Notes:    o	Handles the eight gridnum's
13882:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
13883:  *		  ---        ---         -*-          -*-
13884:  *		  --* = 10   *-- = 18    --* = 72     *-- = 80  (pattern$11)
13885:  *		  -*-        -*-         ---          ---
13886:  *
13887:  *		  ---        ---         -**          **-
13888:  *		  --* = 11   *-- = 22    --* = 104    *-- = 208 (pattern$24)
13889:  *		  -**        **-         ---          ---
13890:  *	     o	For black * center pixel, using grid#10 as an example,
13891:  *		pixel stays ---      antialiased  ---*
13892:  *		black if    -***     if part of	  -**
13893:  *		part of a   -*-      a diagonal	  -*- 
13894:  *		corner, eg,  *       line, eg,	  *
13895:  * ======================================================================= */
13896: /* --- entry point --- */
13897: int	aapattern1124 (raster *rp, int irow, int icol,
13898: 	int gridnum, int grayscale)
13899: {
13900: /* -------------------------------------------------------------------------
13901: Allocations and Declarations
13902: -------------------------------------------------------------------------- */
13903: int	aaval = (-1);			/* antialiased value returned */
13904: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
13905: int	patternum = 24;			/* init for pattern#24 default */
13906: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
13907: int	width=rp->width, height=rp->height; /* width, height of raster */
13908: int	jrow=irow, jcol=icol;		/* corner or diagonal row,col */
13909: int	vertcornval=0, horzcornval=0,	/* vertical, horizontal corner bits*/
13910: 	topdiagval=0,  botdiagval=0,	/* upper,lower diagonal pixel bits */
13911: 	cornval=0, diagval=0;		/* vert+horzcorn, top+botdiag */
13912: int	hdirection=99, vdirection=99,	/* horz,vert corner direction */
13913: 	hturn=99,vturn=99, aafollowline(); /* follow corner till turns */
13914: /* -------------------------------------------------------------------------
13915: Check corner and diagonal pixels
13916: -------------------------------------------------------------------------- */
13917: if ( 0 ) goto end_of_job;		/* true to turn off pattern1124 */
13918: switch ( gridnum/2 ) {			/* check pattern#11,24 corner, diag*/
13919:   default: goto end_of_job;		/* not a pattern#11,24 gridnum */
13920:   case 10: patternum=11; case 11:
13921:     hdirection = 2;  vdirection = -1;	/* directions to follow corner */
13922:     if ( (jrow=irow+2) < height ) {	/* vert corner below center pixel */
13923:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
13924:       if ( (icol-1) >= 0 )		/* lower diag left of center */
13925:         botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
13926:     if ( (jcol=icol+2) < width ) {	/* horz corner right of center */
13927:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
13928:       if ( (irow-1) >= 0 )		/* upper diag above center */
13929:         topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
13930:     break;
13931:   case 18: patternum=11; case 22:
13932:     hdirection = -2;  vdirection = -1;	/* directions to follow corner */
13933:     if ( (jrow=irow+2) < height ) {	/* vert corner below center pixel */
13934:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
13935:       if ( (icol+1) < width )		/* lower diag right of center */
13936:         botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
13937:     if ( (jcol=icol-2) >= 0 ) {		/* horz corner left of center */
13938:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
13939:       if ( (irow-1) >= 0 )		/* upper diag above center */
13940:         topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
13941:     break;
13942:   case 72: patternum=11; case 104:
13943:     hdirection = 2;  vdirection = 1;	/* directions to follow corner */
13944:     if ( (jrow=irow-2) >= 0 ) {		/* vert corner above center pixel */
13945:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
13946:       if ( (icol-1) >= 0 )		/* upper diag left of center */
13947:         topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
13948:     if ( (jcol=icol+2) < width ) {	/* horz corner right of center */
13949:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
13950:       if ( (irow+1) < height )		/* lower diag below center */
13951:         botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
13952:     break;
13953:   case 80: patternum=11; case 208:
13954:     hdirection = -2;  vdirection = 1;	/* directions to follow corner */
13955:     if ( (jrow=irow-2) >= 0 ) {		/* vert corner above center pixel */
13956:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
13957:       if ( (icol+1) < width )		/* upper diag right of center */
13958:         topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
13959:     if ( (jcol=icol-2) >= 0 ) {		/* horz corner left of center */
13960:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
13961:       if ( (irow+1) < height )		/* lower diag below center */
13962:         botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
13963:     break;
13964:   } /* --- end-of-switch(gridnum/2) --- */
13965: cornval = vertcornval+horzcornval;	/* 0=no corner bits, 1, 2=both */
13966: diagval = topdiagval+botdiagval;	/* 0=no diag bits, 1, 2=both */
13967: /* -------------------------------------------------------------------------
13968: Handle white center
13969: -------------------------------------------------------------------------- */
13970: if ( 1 && !iscenter ) { aaval = (patternum==11?51:64);  goto end_of_job; }
13971: /* -------------------------------------------------------------------------
13972: Handle black center
13973: -------------------------------------------------------------------------- */
13974: if ( diagval > 1 ) aaval = ( patternum==24? 255:191 );
13975: else {
13976:   hturn = aafollowline(rp,irow,icol,hdirection);
13977:   vturn = aafollowline(rp,irow,icol,vdirection);
13978:   if ( vturn*hdirection < 0  && hturn*vdirection < 0 )
13979:        aaval = ( patternum==24? 255:191 );
13980:   else aaval = grayscale-1; }		/* actual corner */
13981: /* -------------------------------------------------------------------------
13982: Back to caller with grayscale antialiased value for pixel at irow,icol
13983: -------------------------------------------------------------------------- */
13984: end_of_job:
13985:   if ( aaval >= 0 )			/* have antialiasing result */
13986:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
13987:     "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, "
13988:     "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n",
13989:     irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval,
13990:     vdirection,hdirection, vturn,hturn, aaval);
13991:   return ( aaval );			/* back with antialiased value */
13992: } /* --- end-of-function aapattern1124() --- */
13993: 
13994: 
13995: /* ==========================================================================
13996:  * Function:	aapattern19 ( rp, irow, icol, gridnum, grayscale )
13997:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
13998:  *		whose surrounding 3x3 pixel grid is coded by gridnum
13999:  *		(which must correspond to pattern #19).
14000:  * --------------------------------------------------------------------------
14001:  * Arguments:	rp (I)		raster *  to raster whose bitmap
14002:  *				is to be anti-aliased
14003:  *		irow (I)	int containing row, 0...height-1,
14004:  *				of pixel to be antialiased
14005:  *		icol (I)	int containing col, 0...width-1,
14006:  *				of pixel to be antialiased
14007:  *		gridnum (I)	int containing 0...511 corresponding to
14008:  *				3x3 pixel grid surrounding irow,icol
14009:  *		grayscale (I)	int containing number of grayscales
14010:  *				to be calculated, 0...grayscale-1
14011:  *				(should typically be given as 256)
14012:  * --------------------------------------------------------------------------
14013:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
14014:  * --------------------------------------------------------------------------
14015:  * Notes:    o	Handles the four gridnum's
14016:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
14017:  *		  ---        --*         *--          ***
14018:  *		  --- = 7    --* = 41    *-- = 148    --- = 224
14019:  *		  ***        --*         *--          ---
14020:  * ======================================================================= */
14021: /* --- entry point --- */
14022: int	aapattern19 (raster *rp, int irow, int icol,
14023: 	int gridnum, int grayscale)
14024: {
14025: /* -------------------------------------------------------------------------
14026: Allocations and Declarations
14027: -------------------------------------------------------------------------- */
14028: int	aaval = (-1);			/* antialiased value returned */
14029: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
14030: int	orientation = 1,		/* 1=vertical, 2=horizontal */
14031: 	jrow=irow, jcol=icol;		/* middle pixel of *** line */
14032: int	turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */
14033: /* -------------------------------------------------------------------------
14034: Initialization and calculation of antialiased value
14035: -------------------------------------------------------------------------- */
14036: /* --- check input -- */
14037: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
14038: /* --- set params --- */
14039: switch ( gridnum/2 ) {			/* check pattern#19 orientation */
14040:   default: goto end_of_job;		/* not a pattern#19 gridnum */
14041:   case 7:   orientation=2; jrow++; break;
14042:   case 41:  orientation=1; jcol++; break;
14043:   case 148: orientation=1; jcol--; break;
14044:   case 224: orientation=2; jrow--; break;
14045:   } /* --- end-of-switch(gridnum/2) --- */
14046: /* --- get turns in both directions --- */
14047: if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job;
14048: if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job;
14049: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14050: /* --- weight pixel --- */
14051: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14052: /* -------------------------------------------------------------------------
14053: Back to caller with grayscale antialiased value for pixel at irow,icol
14054: -------------------------------------------------------------------------- */
14055: end_of_job:
14056:   if ( aaval >= 0 )			/* have antialiasing result */
14057:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14058:     "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n",
14059:     irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval);
14060:   return ( aaval );			/* back with antialiased value */
14061: } /* --- end-of-function aapattern19() --- */
14062: 
14063: 
14064: /* ==========================================================================
14065:  * Function:	aapattern20 ( rp, irow, icol, gridnum, grayscale )
14066:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
14067:  *		whose surrounding 3x3 pixel grid is coded by gridnum
14068:  *		(which must correspond to pattern #20).
14069:  * --------------------------------------------------------------------------
14070:  * Arguments:	rp (I)		raster *  to raster whose bitmap
14071:  *				is to be anti-aliased
14072:  *		irow (I)	int containing row, 0...height-1,
14073:  *				of pixel to be antialiased
14074:  *		icol (I)	int containing col, 0...width-1,
14075:  *				of pixel to be antialiased
14076:  *		gridnum (I)	int containing 0...511 corresponding to
14077:  *				3x3 pixel grid surrounding irow,icol
14078:  *		grayscale (I)	int containing number of grayscales
14079:  *				to be calculated, 0...grayscale-1
14080:  *				(should typically be given as 256)
14081:  * --------------------------------------------------------------------------
14082:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
14083:  * --------------------------------------------------------------------------
14084:  * Notes:    o	Handles the eight gridnum's
14085:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
14086:  *		  ---        ---         --*          -*-      
14087:  *		  --* = 14   *-- = 19    --* = 42     --* = 73
14088:  *		  **-        -**         -*-          --*     
14089:  *
14090:  *		  -*-        -**         *--          **-      
14091:  *		  *-- = 84   *-- = 112   *-- = 146    --* = 200
14092:  *		  *--        ---         -*-          ---     
14093:  * ======================================================================= */
14094: /* --- entry point --- */
14095: int	aapattern20 (raster *rp, int irow, int icol,
14096: 	int gridnum, int grayscale)
14097: {
14098: /* -------------------------------------------------------------------------
14099: Allocations and Declarations
14100: -------------------------------------------------------------------------- */
14101: int	aaval = (-1);			/* antialiased value returned */
14102: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
14103: int	direction = 1,			/* direction to follow ** line */
14104: 	jrow1=irow, jcol1=icol,		/* coords of * */
14105: 	jrow2=irow, jcol2=icol;		/* coords of adjacent ** pixel */
14106: int	turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
14107: /* -------------------------------------------------------------------------
14108: Initialization and calculation of antialiased value
14109: -------------------------------------------------------------------------- */
14110: /* --- check input -- */
14111: if ( 1 ) goto end_of_job;		/* don't want this one */
14112: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
14113: /* --- set params --- */
14114: switch ( gridnum/2 ) {			/* check pattern#20 orientation */
14115:   default: goto end_of_job;		/* not a pattern#20 gridnum */
14116:   case 14:  direction=-2; jcol1++; jrow2++; break;
14117:   case 19:  direction=2;  jcol1--; jrow2++; break;
14118:   case 42:  direction=1;  jrow1++; jcol2++; break;
14119:   case 73:  direction=-1; jrow1--; jcol2++; break;
14120:   case 84:  direction=-1; jrow1--; jcol2--; break;
14121:   case 112: direction=2;  jcol1--; jrow2--; break;
14122:   case 146: direction=1;  jrow1++; jcol2--; break;
14123:   case 200: direction=-2; jcol1++; jrow2--; break;
14124:   } /* --- end-of-switch(gridnum/2) --- */
14125: /* --- get turns in both directions --- */
14126: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job;
14127: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
14128: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14129: /* --- weight pixel --- */
14130: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14131: /* -------------------------------------------------------------------------
14132: Back to caller with grayscale antialiased value for pixel at irow,icol
14133: -------------------------------------------------------------------------- */
14134: end_of_job:
14135:   if ( aaval >= 0 )			/* have antialiasing result */
14136:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14137:     "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
14138:     irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
14139:   return ( aaval );			/* back with antialiased value */
14140: } /* --- end-of-function aapattern20() --- */
14141: 
14142: 
14143: /* ==========================================================================
14144:  * Function:	aapattern39 ( rp, irow, icol, gridnum, grayscale )
14145:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
14146:  *		whose surrounding 3x3 pixel grid is coded by gridnum
14147:  *		(which must correspond to pattern #39).
14148:  * --------------------------------------------------------------------------
14149:  * Arguments:	rp (I)		raster *  to raster whose bitmap
14150:  *				is to be anti-aliased
14151:  *		irow (I)	int containing row, 0...height-1,
14152:  *				of pixel to be antialiased
14153:  *		icol (I)	int containing col, 0...width-1,
14154:  *				of pixel to be antialiased
14155:  *		gridnum (I)	int containing 0...511 corresponding to
14156:  *				3x3 pixel grid surrounding irow,icol
14157:  *		grayscale (I)	int containing number of grayscales
14158:  *				to be calculated, 0...grayscale-1
14159:  *				(should typically be given as 256)
14160:  * --------------------------------------------------------------------------
14161:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
14162:  * --------------------------------------------------------------------------
14163:  * Notes:    o	Handles the eight gridnum's
14164:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
14165:  *		  ---        ---         --*          -**      
14166:  *		  --* = 15   *-- = 23    --* = 43     --* = 105
14167:  *		  ***        ***         -**          --*     
14168:  *
14169:  *		  **-        ***         *--          ***      
14170:  *		  *-- = 212  *-- = 240   *-- = 150    --* = 232
14171:  *		  *--        ---         **-          ---     
14172:  * ======================================================================= */
14173: /* --- entry point --- */
14174: int	aapattern39 (raster *rp, int irow, int icol,
14175: 	int gridnum, int grayscale)
14176: {
14177: /* -------------------------------------------------------------------------
14178: Allocations and Declarations
14179: -------------------------------------------------------------------------- */
14180: int	aaval = (-1);			/* antialiased value returned */
14181: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
14182: int	direction = 1,			/* direction to follow ** line */
14183: 	jrow1=irow, jcol1=icol,		/* coords of * */
14184: 	jrow2=irow, jcol2=icol;		/* coords of adjacent ** pixel */
14185: int	turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
14186: /* -------------------------------------------------------------------------
14187: Initialization and calculation of antialiased value
14188: -------------------------------------------------------------------------- */
14189: /* --- check input -- */
14190: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
14191: /* --- set params --- */
14192: switch ( gridnum/2 ) {			/* check pattern#39 orientation */
14193:   default: goto end_of_job;		/* not a pattern#39 gridnum */
14194:   case 15:  direction=-2; jcol1++; jrow2++; break;
14195:   case 23:  direction=2;  jcol1--; jrow2++; break;
14196:   case 43:  direction=1;  jrow1++; jcol2++; break;
14197:   case 105: direction=-1; jrow1--; jcol2++; break;
14198:   case 212: direction=-1; jrow1--; jcol2--; break;
14199:   case 240: direction=2;  jcol1--; jrow2--; break;
14200:   case 150: direction=1;  jrow1++; jcol2--; break;
14201:   case 232: direction=-2; jcol1++; jrow2--; break;
14202:   } /* --- end-of-switch(gridnum/2) --- */
14203: /* --- get turns directions (tunr1==1 signals inside corner) --- */
14204: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 )
14205:   { aaval=0; goto end_of_job; }
14206: if ( 1 ) goto end_of_job;  /* stop here for now */
14207: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
14208: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14209: /* --- weight pixel --- */
14210: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14211: /* -------------------------------------------------------------------------
14212: Back to caller with grayscale antialiased value for pixel at irow,icol
14213: -------------------------------------------------------------------------- */
14214: end_of_job:
14215:   if ( aaval >= 0 )			/* have antialiasing result */
14216:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14217:     "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
14218:     irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
14219:   return ( aaval );			/* back with antialiased value */
14220: } /* --- end-of-function aapattern39() --- */
14221: 
14222: 
14223: /* ==========================================================================
14224:  * Function:	aafollowline ( rp, irow, icol, direction )
14225:  * Purpose:	starting with pixel at irow,icol, moves in
14226:  *		specified direction looking for a "turn"
14227:  * --------------------------------------------------------------------------
14228:  * Arguments:	rp (I)		raster *  to raster containing pixel image
14229:  *		irow (I)	int containing row, 0...height-1,
14230:  *				of first pixel
14231:  *		icol (I)	int containing col, 0...width-1,
14232:  *				of first pixel
14233:  *		direction (I)	int containing +1 to follow line up/north
14234:  *				(decreasing irow), -1 to follow line
14235:  *				down/south (increasing irow), +2 to follow
14236:  *				line right/east (increasing icol),
14237:  *				-2 to follow line left/west (decreasing icol)
14238:  * --------------------------------------------------------------------------
14239:  * Returns:	( int )		#rows or #cols traversed prior to turn,
14240:  *				or 0 if no turn detected (or for any error).
14241:  *				Sign is + if turn direction is right/east or
14242:  *				up/north, or is - if left/west or down/south.
14243:  * --------------------------------------------------------------------------
14244:  * Notes:     o	Here are some examples illustrating turn detection in
14245:  *		+2 (right/east) direction.  Turns in other directions
14246:  *		are detected similarly/symmetrically.  * denotes black
14247:  *		bits (usually fg), - denotes white bits (usually bg),
14248:  *		and ? denotes "don't care" bit (won't affect outcome).
14249:  *		Arrow --> points to start pixel denoted by irow,icol.
14250:  *
14251:  *		   *???         -???	turn=0 (no turn) is returned
14252:  *		-->*???   or -->-???	because the start pixel isn't
14253:  *		   *???         -???	on an edge to begin with
14254:  *
14255:  *		   ----         **--	turn=0 returned because the
14256:  *		-->***-   or -->***-	line ends abruptly without
14257:  *		   ----	        ----	turning (even the second case)
14258:  *
14259:  *		   ---*         ---*	turn=0 returned because the
14260:  *		-->***-   or -->****	line forms a Y or T rather
14261:  *		   ---*	        ---*	than turning
14262:  *
14263:  *		   ***-	        ****	turn=+3 returned
14264:  *		-->***-   or -->***-	(outside corner)
14265:  *		   ----	        ----
14266:  *
14267:  *		   *****        ****-	turn=-4 returned
14268:  *		-->*****  or -->****-	(inside corner)
14269:  *		   ----*        ----*
14270:  *
14271:  *		   ----*        ----*	turn=+4 returned
14272:  *		-->****-  or -->*****	(outside or inside corner)
14273:  *		   -----        -----
14274:  * ======================================================================= */
14275: /* --- entry point --- */
14276: int	aafollowline (raster *rp, int irow, int icol, int direction)
14277: {
14278: /* -------------------------------------------------------------------------
14279: Allocations and Declarations
14280: -------------------------------------------------------------------------- */
14281: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
14282: int	width=rp->width, height=rp->height; /* width, height of raster */
14283: int	drow=0, dcol=0,			/* delta row,col to follow line */
14284: 	jrow=irow, jcol=icol;		/* current row,col following line */
14285: int	bitval=1,			/* value of rp bit at irow,icol */
14286: 	fgval=1, bgval=0,		/* "fg" is whatever bitval is */
14287: 	bitminus=0, bitplus=0;		/* value of left/down, right/up bit*/
14288: int	isline=1, isedge=0;		/*isline signals one-pixel wide line*/
14289: int	turn = 0,			/* detected turn back to caller */
14290: 	maxturn = maxfollow;		/* don't follow more than max pixels*/
14291: /* -------------------------------------------------------------------------
14292: Initialization
14293: -------------------------------------------------------------------------- */
14294: /* --- check input --- */
14295: if ( irow<0 || irow>=height		/* irow out-of-bounds */
14296: ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
14297: /* --- adjust maxturn for magstep --- */
14298: if ( 1 )				/* guard */
14299:   if ( magstep > 1 && magstep <= 10 )	/* sanity check */
14300:     maxturn *= magstep;			/* factor in magnification */
14301: /* --- starting bit -- see if we're following a fg (usual), or bg line --- */
14302: bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
14303: fgval = bitval;  bgval = (1-bitval);	/* define "fg" as whatever bitval is*/
14304: /* --- set drow,dcol corresponding to desired direction --- */
14305: switch ( direction ) {			/* determine drow,dcol for direction*/
14306:   default: goto end_of_job;		/* unrecognized direction arg */
14307:   case  1: drow = (-1); break;		/* follow line up/north */
14308:   case -1: drow =   1;  break;		/* down/south */
14309:   case  2: dcol =   1;  break;		/* right/east */
14310:   case -2: dcol = (-1); break; }	/* left/west */
14311: /* --- set bitminus and bitplus --- */
14312: if ( drow == 0 ) {			/* we're following line right/left */
14313:   if ( irow < height )			/* there's a pixel below current */
14314:     bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */
14315:   if ( irow > 0 )			/* there's a pixel above current */
14316:     bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */
14317: if ( dcol == 0 ) {			/* we're following line up/down */
14318:   if ( icol < width )			/* there's a pixel to the right */
14319:     bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */
14320:   if ( icol > 0 )			/* there's a pixel to the left */
14321:     bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */
14322: /* --- check for lack of line to follow --- */
14323: if ( bitval == bitplus			/* starting pixel same as above */
14324: &&   bitval == bitminus )		/* and below (or right and left) */
14325:   goto end_of_job;			/* so there's no line to follow */
14326: /* --- set isline and isedge (already initted for isline) --- */
14327: if ( bitval == bitplus )		/* starting pixel same as above */
14328:   { isedge = (-1);  isline = 0; }	/* so we're at an edge below */
14329: if (  bitval == bitminus )		/* starting pixel same as below */
14330:   { isedge = 1;  isline = 0; }		/* so we're at an edge above */
14331: /* -------------------------------------------------------------------------
14332: follow line
14333: -------------------------------------------------------------------------- */
14334: while ( 1 ) {				/* until turn found (or max) */
14335:   /* --- local allocations and declarations --- */
14336:   int	dbitval=0,			/* value of bit at jrow,jcol */
14337: 	dbitminus=0, dbitplus=0;	/* value of left/down, right/up bit*/
14338:   /* --- bump pixel count and indexes; check for max or end-of-raster --- */
14339:   turn++;				/* bump #pixels followed */
14340:   jrow += drow;  jcol += dcol;		/* indexes of next pixel to check */
14341:   if ( turn > maxturn			/* already followed max #pixels */
14342:   ||   jrow<0 || jrow>=height		/* or jrow past end-of-raster */
14343:   ||   jcol<0 || jcol>=width )		/* or jcol past end-of-raster */
14344:     { turn = 0;  goto end_of_job; }	/* so quit without finding a turn */
14345:   /* --- set current bit (dbitval) --- */
14346:   dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/
14347:   /* --- set dbitminus and dbitplus --- */
14348:   if ( drow == 0 ) {			/* we're following line right/left */
14349:     if ( irow < height )		/* there's a pixel below current */
14350:       dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */
14351:     if ( irow > 0 )			/* there's a pixel above current */
14352:       dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */
14353:   if ( dcol == 0 ) {			/* we're following line up/down */
14354:     if ( icol < width )			/* there's a pixel to the right */
14355:       dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */
14356:     if ( icol > 0 )			/* there's a pixel to the left */
14357:       dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */
14358:   /* --- first check for abrupt end-of-line, or for T or Y --- */
14359:   if ( isline != 0 )			/* abrupt end or T,Y must be a line*/
14360:     if ( ( bgval == dbitval		/* end-of-line if pixel flips to bg*/
14361:            && bgval == dbitplus		/* and bg same as above pixel */
14362:            && bgval == dbitminus )	/* and below (or right and left) */
14363:     ||   ( fgval == dbitplus		/* T or Y if fg same as above pixel*/
14364:            && fgval == dbitminus ) )	/* and below (or right and left) */
14365:       { turn = 0;  goto end_of_job; }	/* so we're at a T or Y */
14366:   /* --- check for turning line --- */
14367:   if ( isline != 0 ) {			/* turning line must be a line */
14368:     if ( fgval == dbitminus )		/* turning down */
14369:       { turn = -turn;  goto end_of_job; } /* so return negative turn */
14370:     else if ( fgval == dbitplus )	/* turning up */
14371:       goto end_of_job; }		/* so return positive turn */
14372:   /* --- check for inside corner at edge --- */
14373:   if ( isedge != 0 ) {			/* inside corner must be a edge */
14374:     if ( isedge < 0 && fgval == bitminus ) /* corner below */
14375:       { turn = -turn;  goto end_of_job; } /* so return negative turn */
14376:     if ( isedge > 0 && fgval == bitplus ) /* corner above */
14377:       goto end_of_job; }		/* so return positive turn */
14378:   /* --- check for abrupt end at edge --- */
14379:   if ( isedge != 0			/* abrupt edge end must be an edge */
14380:   &&   fgval == dbitval )		/* and line must not end */
14381:     if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */
14382:     ||   (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */
14383:       { turn = 0;  goto end_of_job; }	/* so edge ended abruptly */
14384:   /* --- check for outside corner at edge --- */
14385:   if ( isedge != 0			/* outside corner must be a edge */
14386:   &&   bgval == dbitval ) {		/* and line must end */
14387:     if ( isedge > 0 ) turn = -turn;	/* outside turn down from edge above*/
14388:     goto end_of_job; }
14389:   } /* --- end-of-while(1) --- */
14390: /* -------------------------------------------------------------------------
14391: Back to caller with #rows or #cols traversed, and direction of detected turn
14392: -------------------------------------------------------------------------- */
14393: end_of_job:
14394:   if ( msglevel>=99 && msgfp!=NULL )	/* debugging/diagnostic output */
14395:     fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n",
14396:     irow,icol,direction,turn);
14397:   return ( turn );
14398: } /* --- end-of-function aafollowline() --- */
14399: 
14400: 
14401: /* ==========================================================================
14402:  * Function:	aagridnum ( rp, irow, icol )
14403:  * Purpose:	calculates gridnum, 0-511 (see Notes below),
14404:  *		for 3x3 grid centered at irow,icol
14405:  * --------------------------------------------------------------------------
14406:  * Arguments:	rp (I)		raster *  to raster containing
14407:  *				bitmap image (to be anti-aliased)
14408:  *		irow (I)	int containing row, 0...height-1,
14409:  *				at center of 3x3 grid
14410:  *		icol (I)	int containing col, 0...width-1,
14411:  *				at center of 3x3 grid
14412:  * --------------------------------------------------------------------------
14413:  * Returns:	( int )		0-511 grid number, or -1=any error
14414:  * --------------------------------------------------------------------------
14415:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14416:  *		whose bit positions (and corresponding values) in gridnum are
14417:  *		  876     256 128  64
14418:  *		  504  =   32   1  16
14419:  *		  321       8   4   2
14420:  *		Thus, for example (*=pixel set/black, -=pixel not set/white),
14421:  *		  *--         *--	  -**         (note that 209 is the
14422:  *		  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
14423:  *		  --*         ***         ---          of 302)
14424:  *	      o	A set pixel is considered black, an unset pixel considered
14425:  *		white.
14426:  * ======================================================================= */
14427: /* --- entry point --- */
14428: int	aagridnum (raster *rp, int irow, int icol)
14429: {
14430: /* -------------------------------------------------------------------------
14431: Allocations and Declarations
14432: -------------------------------------------------------------------------- */
14433: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
14434: int	width=rp->width, height=rp->height, /* width, height of raster */
14435: 	imap = icol + irow*width;	/* pixel index = icol + irow*width */
14436: int	bitval=0,			/* value of rp bit at irow,icol */
14437: 	nnbitval=0, nebitval=0, eebitval=0, sebitval=0,	/*adjacent vals*/
14438: 	ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0,	/*compass pt names*/
14439: 	gridnum = (-1);			/* grid# 0-511 for above 9 bits */
14440: /* -------------------------------------------------------------------------
14441: check input
14442: -------------------------------------------------------------------------- */
14443: if ( irow<0 || irow>=height		/* irow out-of-bounds */
14444: ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
14445: /* -------------------------------------------------------------------------
14446: get the 9 bits comprising the 3x3 grid centered at irow,icol
14447: -------------------------------------------------------------------------- */
14448: /* --- get center bit --- */
14449: bitval = getlongbit(bitmap,imap);	/* value of rp input bit at imap */
14450: /* --- get 8 surrounding bits --- */
14451: if ( irow > 0 )				/* nn (north) bit available */
14452:    nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
14453: if ( irow < height-1 )			/* ss (south) bit available */
14454:    ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
14455: if ( icol > 0 )				/* ww (west) bit available */
14456:  { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
14457:    if ( irow > 0 )			/* nw bit available */
14458:      nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
14459:    if ( irow < height-1 )		/* sw bit available */
14460:      swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
14461: if ( icol < width-1 )			/* ee (east) bit available */
14462:  { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
14463:    if ( irow > 0 )			/* ne bit available */
14464:      nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
14465:    if ( irow < height-1 )		/* se bit available */
14466:      sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
14467: /* --- set gridnum --- */
14468: gridnum = 0;				/* clear all bits */
14469: if (   bitval ) gridnum = 1;		/* set1bit(gridnum,0); */
14470: if ( nwbitval ) gridnum += 256;		/* set1bit(gridnum,8); */
14471: if ( nnbitval ) gridnum += 128;		/* set1bit(gridnum,7); */
14472: if ( nebitval ) gridnum += 64;		/* set1bit(gridnum,6); */
14473: if ( wwbitval ) gridnum += 32;		/* set1bit(gridnum,5); */
14474: if ( eebitval ) gridnum += 16;		/* set1bit(gridnum,4); */
14475: if ( swbitval ) gridnum += 8;		/* set1bit(gridnum,3); */
14476: if ( ssbitval ) gridnum += 4;		/* set1bit(gridnum,2); */
14477: if ( sebitval ) gridnum += 2;		/* set1bit(gridnum,1); */
14478: /* -------------------------------------------------------------------------
14479: Back to caller with gridnum coding 3x3 grid centered at irow,icol
14480: -------------------------------------------------------------------------- */
14481: end_of_job:
14482:   return ( gridnum );
14483: } /* --- end-of-function aagridnum() --- */
14484: 
14485: 
14486: /* ==========================================================================
14487:  * Function:	aapatternnum ( gridnum )
14488:  * Purpose:	Looks up the pattern number 1...51
14489:  *		corresponding to the 3x3 pixel grid coded by gridnum 0=no
14490:  *		pixels set (white) to 511=all pixels set (black).
14491:  * --------------------------------------------------------------------------
14492:  * Arguments:	gridnum (I)	int containing 0-511 coding a 3x3 pixel grid
14493:  *				(see Notes below)
14494:  * --------------------------------------------------------------------------
14495:  * Returns:	( int )		1 to 51, or -1=error
14496:  * --------------------------------------------------------------------------
14497:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14498:  *		whose bit positions (and corresponding values) in gridnum are
14499:  *		  876     256 128  64
14500:  *		  504  =   32   1  16
14501:  *		  321       8   4   2
14502:  *		Thus, for example (*=pixel set/black, -=pixel not set/white),
14503:  *		  *--         *--	  -**         (note that 209 is the
14504:  *		  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
14505:  *		  --*         ***         ---          of 302)
14506:  *	      o	A set pixel is considered black, an unset pixel considered
14507:  *		white.
14508:  *	      o	Ignoring whether the center pixel is set or unset, and
14509:  *		taking rotation, reflection and inversion (set<-->unset)
14510:  *		symmetries into account, there are 32 unique pixel patterns.
14511:  *		If inversions are listed separately, there are 51 patterns.
14512:  *	      o	Here are the 51 unique patterns, with ? always denoting the
14513:  *		undetermined center pixel.  At the upper-left corner of each
14514:  *		pattern is the "pattern index number" assigned to it in this
14515:  *		function. At the upper-right is the pattern's multiplicity,
14516:  *		i.e., the number of different patterns obtained by rotations
14517:  *		and reflection of the illustrated one.  Inverse patters are
14518:  *		illustrated immediately beneath the original (the first three
14519:  *		four-pixel patterns have identical inverses).
14520:  *		-------------------------------------------------------------
14521:  *		No pixels set:
14522:  *		 #1 1 (in this case, 1 signifies that rotation
14523:  *		  ---  and reflection give no different grids)
14524:  *		  -?-
14525:  *		  ---
14526:  *		Inverse, all eight pixels set
14527:  *		 #2 1 (the inverse multiplicity is always the same)
14528:  *		  ***
14529:  *		  *?*
14530:  *		  ***
14531:  *		-------------------------------------------------------------
14532:  *		One pixel set:
14533:  *		 #3 4  #4 4
14534:  *		  *--   -*-
14535:  *		  -?-   -?-
14536:  *		  ---   ---
14537:  *		Inverse, seven pixels set:
14538:  *		 #5 4  #6 4
14539:  *		  -**   *-*
14540:  *		  *?*   *?*
14541:  *		  ***   ***
14542:  *		-------------------------------------------------------------
14543:  *		Two pixels set:
14544:  *		 #7 8  #8 4  #9 8  10 2  11 4  12 2
14545:  *		  **-   *-*   *--   *--   -*-   -*-
14546:  *		  -?-   -?-   -?*   -?-   -?*   -?-
14547:  *		  ---   ---   ---   --*   ---   -*-
14548:  *		Inverse, six pixels set:
14549:  *		#13 8  14 4  15 8  16 2  17 4  18 2
14550:  *		  --*   -*-   -**   -**   *-*   *-*
14551:  *		  *?*   *?*   *?-   *?*   *?-   *?*
14552:  *		  ***   ***   ***   **-   ***   *-*
14553:  *		-------------------------------------------------------------
14554:  *		Three pixels set:
14555:  *		#19 4  20 8  21 8  22 8  23 8  24 4  25 4  26 4  27 4  28 4
14556:  *		  ***   **-   **-   **-   **-   **-   *-*   *-*   -*-   -*-
14557:  *		  -?-   -?*   -?-   -?-   -?-   *?-   -?-   -?-   -?*   -?*
14558:  *		  ---   ---   --*   -*-   *--   ---   --*   -*-   -*-   *--
14559:  *		Inverse, five pixels set:
14560:  *		#29 4  30 8  31 8  32 8  33 8  34 4  35 4  36 4  37 4  38 4
14561:  *		  ---   --*   --*   --*   --*   --*   -*-   -*-   *-*   *-*
14562:  *		  *?*   *?-   *?*   *?*   *?*   -?*   *?*   *?*   *?-   *?-
14563:  *		  ***   ***   **-   *-*   -**   ***   **-   *-*   *-*   -**
14564:  *		-------------------------------------------------------------
14565:  *		Four pixels set (including inverses):
14566:  *		#39 8  40 4  41 8  42 8  43 4  44 4  45 8  46 1
14567:  *		  ***   **-   **-   ***   ***   **-   **-   *-*
14568:  *		  -?*   -?-   -?*   -?-   -?-   -?*   -?*   -?-
14569:  *		  ---   -**   *--   --*   -*-   --*   -*-   *-*
14570:  *
14571:  *		                  #47 8  48 4  49 4  50 8  51 1
14572:  *		                    ---   ---   --*   --*   -*-
14573:  *		                    *?*   *?*   *?-   *?-   *?*
14574:  *		                    **-   *-*   **-   *-*   -*-
14575:  * ======================================================================= */
14576: /* --- entry point --- */
14577: int	aapatternnum ( int gridnum )
14578: {
14579: /* -------------------------------------------------------------------------
14580: Allocations and Declarations
14581: -------------------------------------------------------------------------- */
14582: int	pattern = (-1);			/*pattern#, 1-51, for input gridnum*/
14583: /* ---
14584:  * pattern number corresponding to input gridnum/2 code
14585:  * ( gridnum/2 strips off gridnum's low bit because it's
14586:  * the same pattern whether or not center pixel is set )
14587:  * --- */
14588: static int patternnum[] = {
14589:     1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39,  /*   0- 15 */
14590:     4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29,  /*  16- 31 */
14591:     3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34,  /*  32- 47 */
14592:     9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13,  /*  48- 63 */
14593:     4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33,  /*  64- 79 */
14594:    11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14,  /*  80- 95 */
14595:     7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13,  /*  96-111 */
14596:    20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5,  /* 112-127 */
14597:     3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30,  /* 128-143 */
14598:     7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13,  /* 144-159 */
14599:     8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17,  /* 160-175 */
14600:    23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6,  /* 176-191 */
14601:     7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15,  /* 192-207 */
14602:    24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5,  /* 208-223 */
14603:    19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6,  /* 224-239 */
14604:    39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2,  /* 240-255 */
14605:    -1 } ; /* --- end-of-patternnum[] --- */
14606: /* -------------------------------------------------------------------------
14607: look up pattern number for gridnum
14608: -------------------------------------------------------------------------- */
14609: /* --- first check input --- */
14610: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
14611: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
14612: pattern = patternnum[gridnum/2];	/* /2 strips off gridnum's low bit */
14613: if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */
14614: end_of_job:
14615:   return ( pattern );			/* back to caller with pattern# */
14616: } /* --- end-of-function aapatternnum() --- */
14617: 
14618: 
14619: /* ==========================================================================
14620:  * Function:	aalookup ( gridnum )
14621:  * Purpose:	Looks up the grayscale value 0=white to 255=black
14622:  *		corresponding to the 3x3 pixel grid coded by gridnum 0=no
14623:  *		pixels set (white) to 511=all pixels set (black).
14624:  * --------------------------------------------------------------------------
14625:  * Arguments:	gridnum (I)	int containing 0-511 coding a 3x3 pixel grid
14626:  * --------------------------------------------------------------------------
14627:  * Returns:	( int )		0=white to 255=black, or -1=error
14628:  * --------------------------------------------------------------------------
14629:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14630:  *	      o	A set pixel is considered black, an unset pixel considered
14631:  *		white.  Likewise, the returned grayscale is 255 for black,
14632:  *		0 for white.  You'd more typically want to use 255-grayscale
14633:  *		so that 255 is white and 0 is black.
14634:  *	      o	The returned number is the (lowpass) antialiased grayscale
14635:  *		for the center pixel (gridnum bit 0) of the grid.
14636:  * ======================================================================= */
14637: /* --- entry point --- */
14638: int	aalookup ( int gridnum )
14639: {
14640: /* -------------------------------------------------------------------------
14641: Allocations and Declarations
14642: -------------------------------------------------------------------------- */
14643: int	grayscale = (-1);		/*returned grayscale, init for error*/
14644: int	pattern = (-1), aapatternnum();	/*pattern#, 1-51, for input gridnum*/
14645: int	iscenter = gridnum&1;		/*low-order bit set for center pixel*/
14646: /* --- gray scales --- */
14647: #define	WHT 0
14648: #define	LGT 64
14649: #define	GRY 128
14650: #define	DRK 192
14651: #define	BLK 255
14652: #if 1
14653: /* ---
14654:  * modified aapnm() grayscales (second try)
14655:  * --- */
14656: /* --- grayscale for each pattern when center pixel set/black --- */
14657: static int grayscale1[] = { -1,		/* [0] index not used */
14658:    BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160,	/*  1-10 */
14659: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */	/*  1-10 */
14660:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
14661:    BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191,	/* 21-30 */
14662:    204,BLK,204,191,217,204,BLK,191,178,BLK,	/* 31-40 */
14663:    178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204,	/* 41-51 */
14664:    -1 } ; /* --- end-of-grayscale1[] --- */
14665: /* --- grayscale for each pattern when center pixel not set/white --- */
14666: static int grayscale0[] = { -1,		/* [0] index not used */
14667:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
14668:     64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64,	/* 11-20 */
14669:  /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */	/* 11-20 */
14670:    WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
14671:    102,WHT,102,WHT,115,102,WHT, 89, 76,WHT,	/* 31-40 */
14672:     76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
14673:    -1 } ; /* --- end-of-grayscale0[] --- */
14674: #endif
14675: #if 0
14676: /* ---
14677:  * modified aapnm() grayscales (first try)
14678:  * --- */
14679: /* --- grayscale for each pattern when center pixel set/black --- */
14680: static int grayscale1[] = { -1,		/* [0] index not used */
14681:    BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK,	/*  1-10 */
14682: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */	/*  1-10 */
14683:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
14684:    BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191,	/* 21-30 */
14685: /* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */	/* 21-30 */
14686:    204,BLK,204,BLK,217,204,BLK,191,GRY,BLK,	/* 31-40 */
14687: /* 204,BLK,204,191,217,204,BLK,191,178,BLK, */	/* 31-40 */
14688:    178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204,	/* 41-51 */
14689: /* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */
14690:    -1 } ; /* --- end-of-grayscale1[] --- */
14691: /* --- grayscale for each pattern when center pixel not set/white --- */
14692: static int grayscale0[] = { -1,		/* [0] index not used */
14693:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
14694:    GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY,	/* 11-20 */
14695: /*  51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */	/* 11-20 */
14696:    WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
14697: /* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */	/* 21-30 */
14698:    102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT,	/* 31-40 */
14699: /* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */	/* 31-40 */
14700:     76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
14701: /*  76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */
14702:    -1 } ; /* --- end-of-grayscale0[] --- */
14703: #endif
14704: #if 0
14705: /* ---
14706:  * these grayscales _exactly_ correspond to the aapnm() algorithm
14707:  * --- */
14708: /* --- grayscale for each pattern when center pixel set/black --- */
14709: static int grayscale1[] = { -1,		/* [0] index not used */
14710:    BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK,	/*  1-10 */
14711:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
14712:    BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191,	/* 21-30 */
14713:    204,BLK,204,191,217,204,BLK,191,178,BLK,	/* 31-40 */
14714:    178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204,	/* 41-51 */
14715:    -1 } ; /* --- end-of-grayscale1[] --- */
14716: /* --- grayscale for each pattern when center pixel not set/white --- */
14717: static int grayscale0[] = { -1,		/* [0] index not used */
14718:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
14719:     51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64,	/* 11-20 */
14720:    WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
14721:    102,WHT,102,WHT,115,102,WHT, 89, 76,WHT,	/* 31-40 */
14722:     76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
14723:    -1 } ; /* --- end-of-grayscale0[] --- */
14724: #endif
14725: /* -------------------------------------------------------------------------
14726: look up grayscale for gridnum
14727: -------------------------------------------------------------------------- */
14728: /* --- first check input --- */
14729: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
14730: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
14731: pattern = aapatternnum(gridnum);	/* look up pattern number */
14732: if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */
14733: if ( ispatternnumcount ) {		/* counts being accumulated */
14734:   if (iscenter)	patternnumcount1[pattern] += 1;	/* bump diagnostic count */
14735:   else		patternnumcount0[pattern] += 1; }
14736: /* --- look up grayscale for this pattern --- */
14737: grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] );
14738: end_of_job:
14739:   return ( grayscale );			/* back to caller with grayscale */
14740: } /* --- end-of-function aalookup() --- */
14741: 
14742: 
14743: /* ==========================================================================
14744:  * Function:	aalowpasslookup ( rp, bytemap, grayscale )
14745:  * Purpose:	calls aalookup() for each pixel in rp->bitmap
14746:  *		to create anti-aliased bytemap
14747:  * --------------------------------------------------------------------------
14748:  * Arguments:	rp (I)		raster *  to raster whose bitmap
14749:  *				is to be anti-aliased
14750:  *		bytemap (O)	intbyte * to bytemap, calculated
14751:  *				by calling aalookup() for each pixel
14752:  *				in rp->bitmap
14753:  *		grayscale (I)	int containing number of grayscales
14754:  *				to be calculated, 0...grayscale-1
14755:  *				(should typically be given as 256)
14756:  * --------------------------------------------------------------------------
14757:  * Returns:	( int )		1=success, 0=any error
14758:  * --------------------------------------------------------------------------
14759:  * Notes:    o
14760:  * ======================================================================= */
14761: /* --- entry point --- */
14762: int	aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale)
14763: {
14764: /* -------------------------------------------------------------------------
14765: Allocations and Declarations
14766: -------------------------------------------------------------------------- */
14767: int	width=rp->width, height=rp->height, /* width, height of raster */
14768: 	icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */
14769: int	bgbitval=0 /*, fgbitval=1*/;	/* background, foreground bitval */
14770: int	bitval=0,			/* value of rp bit at irow,icol */
14771: 	aabyteval=0;			/* antialiased (or unchanged) value*/
14772: int	gridnum=0, aagridnum(),		/* grid# for 3x3 grid at irow,icol */
14773: 	aalookup();			/* table look up  antialiased value*/
14774: /* -------------------------------------------------------------------------
14775: generate bytemap by table lookup for each pixel of bitmap
14776: -------------------------------------------------------------------------- */
14777: for ( irow=0; irow<height; irow++ )
14778:  for ( icol=0; icol<width; icol++ )
14779:   {
14780:   /* --- get gridnum and center bit value, init aabyteval --- */
14781:   gridnum = aagridnum(rp,irow,icol);	/*grid# coding 3x3 grid at irow,icol*/
14782:   bitval = (gridnum&1);			/* center bit set if gridnum odd */
14783:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
14784:   imap++;				/* first bump bitmap[] index */  
14785:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
14786:   /* --- look up antialiased value for this grid --- */
14787:   aabyteval = aalookup(gridnum);	/* look up on grid# */
14788:   if ( aabyteval>=0 && aabyteval<=255 )	/* check for success */
14789:     bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
14790:   } /* --- end-of-for(irow,icol) --- */
14791: ispatternnumcount = 0;			/* accumulate counts only once */
14792: /* -------------------------------------------------------------------------
14793: Back to caller with gray-scale anti-aliased bytemap
14794: -------------------------------------------------------------------------- */
14795: /*end_of_job:*/
14796:   return ( 1 );
14797: } /* --- end-of-function aalowpasslookup() --- */
14798: 
14799: 
14800: /* ==========================================================================
14801:  * Function:	aasupsamp ( rp, aa, sf, grayscale )
14802:  * Purpose:	calculates a supersampled anti-aliased bytemap
14803:  *		for rp->bitmap, with each byte 0...grayscale-1
14804:  * --------------------------------------------------------------------------
14805:  * Arguments:	rp (I)		raster *  to raster whose bitmap
14806:  *				is to be anti-aliased
14807:  *		aa (O)		address of raster * to supersampled bytemap,
14808:  *				calculated by supersampling rp->bitmap
14809:  *		sf (I)		int containing supersampling shrinkfactor
14810:  *		grayscale (I)	int containing number of grayscales
14811:  *				to be calculated, 0...grayscale-1
14812:  *				(should typically be given as 256)
14813:  * --------------------------------------------------------------------------
14814:  * Returns:	( int )		1=success, 0=any error
14815:  * --------------------------------------------------------------------------
14816:  * Notes:     o	If the center point of the box being averaged is black,
14817:  *		then the entire "average" is forced black (grayscale-1)
14818:  *		regardless of the surrounding points.  This is my attempt
14819:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
14820:  *		lines, which would otherwise turn from black to a gray shade.
14821:  * ======================================================================= */
14822: /* --- entry point --- */
14823: int	aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
14824: {
14825: /* -------------------------------------------------------------------------
14826: Allocations and Declarations
14827: -------------------------------------------------------------------------- */
14828: int	status = 0;			/* 1=success, 0=failure to caller */
14829: int	rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
14830: 	heightrem=0, widthrem=0,	/* rp+rem is a multiple of shrinkf */
14831: 	aaheight=0,  aawidth=0,		/* supersampled dimensions */
14832: 	aapixsz=8;			/* output pixels are 8-bit bytes */
14833: int	maxaaval=(-9999),		/* max grayscale val set in matrix */
14834: 	isrescalemax=1;			/* 1=rescale maxaaval to grayscale */
14835: int	irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
14836: raster	*aap=NULL, *new_raster();	/* raster for supersampled image */
14837: raster	*aaweights();			/* get weight matrix applied to rp */
14838: static	raster *aawts = NULL;		/* aaweights() resultant matrix */
14839: static	int prevshrink = NOVALUE,	/* shrinkfactor from previous call */
14840: 	sumwts = 0;			/* sum of weights */
14841: static	int blackfrac = 40,		/* force black if this many pts are */
14842: 	/*grayfrac = 20,*/
14843: 	maxwt = 10,			/* max weight in weight matrix */
14844: 	minwtfrac=10, maxwtfrac=70;	/* force light pts white, dark black*/
14845: int	type_raster(), type_bytemap();	/* debugging display routines */
14846: int	delete_raster();		/* delete old rasters */
14847: /* -------------------------------------------------------------------------
14848: Initialization
14849: -------------------------------------------------------------------------- */
14850: /* --- check args --- */
14851: if ( aa == NULL ) goto end_of_job;	/* no ptr for return output arg */
14852: *aa = NULL;				/* init null ptr for error return */
14853: if ( rp == NULL				/* no ptr to input arg */
14854: ||   sf < 1				/* invalid shrink factor */
14855: ||   grayscale < 2 ) goto end_of_job;	/* invalid grayscale */
14856: /* --- get weight matrix (or use current one) --- */
14857: if ( sf != prevshrink )			/* have new shrink factor */
14858:   { if ( aawts != NULL )		/* have unneeded weight matrix */
14859:       delete_raster(aawts);		/* so free it */
14860:     sumwts = 0;				/* reinitialize sum of weights */
14861:     aawts = aaweights(sf,sf);		/* get new weight matrix */
14862:     if ( aawts != NULL )		/* got weight matrix okay*/
14863:       for ( jwt=0; jwt<sf; jwt++ )	/* for each row */
14864:        for ( iwt=0; iwt<sf; iwt++ )	/* and each column */
14865: 	{ int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
14866: 	  if ( wt > maxwt )		/* don't overweight center pts */
14867: 	    { wt = maxwt;		/* scale it back */
14868: 	      setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
14869: 	  sumwts += wt; }		/* add weight to sum */
14870:     prevshrink = sf; }			/* save new shrink factor */
14871: if ( msgfp!=NULL && msglevel>=999 )
14872:   { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
14873:     type_bytemap((intbyte *)aawts->pixmap,grayscale,
14874:     aawts->width,aawts->height,msgfp); }
14875: /* --- calculate supersampled height,width and allocate output raster */
14876: heightrem = rpheight%sf;		/* remainder after division... */
14877: widthrem  = rpwidth%sf;			/* ...by shrinkfactor */
14878: aaheight  = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
14879: aawidth   = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
14880: if ( msgfp!=NULL && msglevel>=999 )
14881:  { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
14882:    rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
14883:    fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
14884:    type_raster(rp,msgfp); }		/* ascii image of rp raster */
14885: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
14886: ==   NULL ) goto end_of_job;		/* quit if alloc fails */
14887: /* -------------------------------------------------------------------------
14888: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
14889: -------------------------------------------------------------------------- */
14890: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
14891:  {
14892:  for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
14893:   {
14894:   int aaval=0;				/* weighted rpvals */
14895:   int nrp=0, mrp=0;			/* #rp bits set, #within matrix */
14896:   for ( jwt=0; jwt<sf; jwt++ )
14897:    for ( iwt=0; iwt<sf; iwt++ )
14898:     {
14899:     int i=irp+iwt, j=jrp+jwt;		/* rp->pixmap point */
14900:     int rpval = 0;			/* rp->pixmap value at i,j */
14901:     if ( i>=0 && i<rpwidth		/* i within actual pixmap */
14902:     &&   j>=0 && j<rpheight )		/* ditto j */
14903:       {	mrp++;				/* count another bit within matrix */
14904: 	rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
14905:     if ( rpval != 0 )
14906:       {	nrp++;				/* count another bit set */
14907: 	aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
14908:     } /* --- end-of-for(iwt,jwt) --- */
14909:   if ( aaval > 0 )			/*normalize and rescale non-zero val*/
14910:     { int aafrac = (100*aaval)/sumwts;	/* weighted percent of black points */
14911:       /*if((100*nrp)/mrp >=blackfrac)*/	/* many black interior pts */
14912:       if( aafrac >= maxwtfrac )		/* high weight of sampledblack pts */
14913: 	aaval = grayscale-1;		/* so set supersampled pt black */
14914:       else if( aafrac <= minwtfrac )	/* low weight of sampledblack pts */
14915: 	aaval = 0;			/* so set supersampled pt white */
14916:       else				/* rescale calculated weight */
14917: 	aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
14918:   maxaaval = max2(maxaaval,aaval);	/* largest aaval so far */
14919:   if ( msgfp!=NULL && msglevel>=999 )
14920:     fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
14921:     " mrp,nrp=%d,%d aaval=%d\n",
14922:     jrp,irp, jaa,iaa, mrp,nrp, aaval);
14923:   if ( jaa<aaheight && iaa<aawidth )	/* bounds check */
14924:     setpixel(aap,jaa,iaa,aaval);	/*weighted val in supersamp raster*/
14925:   else if( msgfp!=NULL && msglevel>=9 )	/* emit error if out-of-bounds */
14926:     fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
14927:     aaheight,aawidth, jaa,iaa);
14928:   iaa++;				/* bump aa col index */
14929:   } /* --- end-of-for(irp) --- */
14930:  jaa++;					/* bump aa row index */
14931:  } /* --- end-of-for(jrp) --- */
14932: /* --- rescale supersampled image so darkest points become black --- */
14933: if ( isrescalemax )			/* flag set to rescale maxaaval */
14934:   {
14935:   double scalef = ((double)(grayscale-1))/((double)maxaaval);
14936:   for ( jaa=0; jaa<aaheight; jaa++ )	/* height */
14937:    for ( iaa=0; iaa<aawidth; iaa++ )	/* width */
14938:     { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
14939:       aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
14940:       aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
14941:       if( aafrac >= blackfrac )		/* high weight of sampledblack pts */
14942: 	aaval = grayscale-1;		/* so set supersampled pt black */
14943:       else if( 0&&aafrac <= minwtfrac )	/* low weight of sampledblack pts */
14944: 	aaval = 0;			/* so set supersampled pt white */
14945:       setpixel(aap,jaa,iaa,aaval); }	/* replace rescaled val in raster */
14946:   } /* --- end-of-if(isrescalemax) --- */
14947: *aa = aap;				/* return supersampled image*/
14948: status = 1;				/* set successful status */
14949: if ( msgfp!=NULL && msglevel>=999 )
14950:   { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
14951:     type_bytemap((intbyte *)aap->pixmap,grayscale,
14952:     aap->width,aap->height,msgfp);  fflush(msgfp); }
14953: /* -------------------------------------------------------------------------
14954: Back to caller with gray-scale anti-aliased bytemap
14955: -------------------------------------------------------------------------- */
14956: end_of_job:
14957:   return ( status );
14958: } /* --- end-of-function aasupsamp() --- */
14959: 
14960: 
14961: /* ==========================================================================
14962:  * Function:	aacolormap ( bytemap, nbytes, colors, colormap )
14963:  * Purpose:	searches bytemap, returning a list of its discrete values
14964:  *		in ascending order in colors[], and returning an "image"
14965:  *		of bytemap (where values are replaced by colors[]
14966:  *		indexes) in colormap[].
14967:  * --------------------------------------------------------------------------
14968:  * Arguments:	bytemap (I)	intbyte *  to bytemap containing
14969:  *				grayscale values (usually 0=white
14970:  *				through 255=black) for which colors[]
14971:  *				and colormap[] will be constructed.
14972:  *		nbytes (I)	int containing #bytes in bytemap
14973:  *				(usually just #rows * #cols)
14974:  *		colors (O)	intbyte *  (to be interpreted as ints)
14975:  *				returning a list of the discrete/different
14976:  *				values in bytemap, in ascending value order,
14977:  *				and with gamma correction applied
14978:  *		colormap (O)	intbyte *  returning a bytemap "image",
14979:  *				i.e., in one-to-one pixel correspondence
14980:  *				with bytemap, but where the values have been
14981:  *				replaced with corresponding colors[] indexes.
14982:  * --------------------------------------------------------------------------
14983:  * Returns:	( int )		#colors in colors[], or 0 for any error
14984:  * --------------------------------------------------------------------------
14985:  * Notes:     o
14986:  * ======================================================================= */
14987: /* --- entry point --- */
14988: int	aacolormap ( intbyte *bytemap, int nbytes,
14989: 			intbyte *colors, intbyte *colormap )
14990: {
14991: /* -------------------------------------------------------------------------
14992: Allocations and Declarations
14993: -------------------------------------------------------------------------- */
14994: int	ncolors = 0,			/* #different values in bytemap */
14995: 	igray, grayscale = 256;		/* bytemap contains intbyte's */
14996: intbyte	*bytevalues = NULL;		/* 1's where bytemap contains value*/
14997: int	ibyte;				/* bytemap/colormap index */
14998: int	isscale = 0,			/* true to scale largest val to 255*/
14999: 	isgamma = 1;			/* true to apply gamma correction */
15000: int	maxcolors = 0;			/* maximum ncolors */
15001: /* -------------------------------------------------------------------------
15002: Accumulate colors[] from values occurring in bytemap
15003: -------------------------------------------------------------------------- */
15004: /* --- initialization --- */
15005: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
15006: ==   NULL ) goto end_of_job;		/* signal error if malloc() failed */
15007: memset(bytevalues,0,grayscale);		/* zero out bytevalues */
15008: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
15009: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
15010:   bytevalues[(int)bytemap[ibyte]] = 1;	/*use its value to index bytevalues*/
15011: /* --- collect the 1's indexes in colors[] --- */
15012: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
15013:   if ( (int)bytevalues[igray] )		/*bytemap contains igray somewheres*/
15014:     { colors[ncolors] = (intbyte)igray;	/* so store igray in colors */
15015:       bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
15016:       if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
15017:         bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
15018:       ncolors++; }			/* and bump #colors */
15019: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
15020: if ( isscale )				/* only rescale if requested */
15021:  if ( ncolors > 1 )			/* and if not a "blank" raster */
15022:   if ( colors[ncolors-1] > 0 )		/*and at least one pixel non-white*/
15023:    {
15024:    /* --- multiply each colors[] by factor that scales largest to 255 --- */
15025:    double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
15026:    for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
15027:     { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
15028:       if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
15029:    } /* --- end-of-if(isscale) --- */
15030: /* --- apply gamma correction --- */
15031: if ( isgamma				/* only gamma correct if requested */
15032: &&   gammacorrection > 0.0001 )		/* and if we have gamma correction */
15033:  if ( ncolors > 1 )			/* and if not a "blank" raster */
15034:   if ( colors[ncolors-1] > 0 )		/*and at least one pixel non-white*/
15035:    {
15036:    for ( igray=1; igray<ncolors; igray++ ) { /*gamma correct each colors[]*/
15037:     int	grayval = colors[igray],	/* original 0=white to 255=black */
15038: 	gmax = grayscale-1;		/* should be 255 */
15039:     double dgray=((double)(gmax-grayval))/((double)gmax); /*0=black 1=white*/
15040:     dgray = pow(dgray,(1.0/gammacorrection)); /* apply gamma correction */
15041:     grayval = (int)( gmax*(1.0-dgray) + 0.5 ); /* convert back to grayval */
15042:     colors[igray] = grayval; }		/* store back in colors[] */
15043:    } /* --- end-of-if(isgamma) --- */
15044: /* -------------------------------------------------------------------------
15045: Construct colormap
15046: -------------------------------------------------------------------------- */
15047: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
15048:   colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
15049: /* -------------------------------------------------------------------------
15050: back to caller with #colors, or 0 for any error
15051: -------------------------------------------------------------------------- */
15052: end_of_job:
15053:   if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
15054:   if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
15055:     ncolors = maxcolors;		/* return maximum to caller */
15056:   return ( ncolors );			/* back with #colors, or 0=error */
15057: } /* --- end-of-function aacolormap() --- */
15058: 
15059: 
15060: /* ==========================================================================
15061:  * Function:	aaweights ( width, height )
15062:  *		Builds "canonical" weight matrix, width x height, in a raster
15063:  *		(see Notes below for discussion).
15064:  * --------------------------------------------------------------------------
15065:  * Arguments:	width (I)	int containing width (#cols) of returned
15066:  *				raster/matrix of weights
15067:  *		height (I)	int containing height (#rows) of returned
15068:  *				raster/matrix of weights
15069:  * --------------------------------------------------------------------------
15070:  * Returns:	( raster * )	ptr to raster containing width x height
15071:  *				weight matrix, or NULL for any error
15072:  * --------------------------------------------------------------------------
15073:  * Notes:     o For example, given width=7, height=5, builds the matrix
15074:  *			1 2 3  4 3 2 1
15075:  *			2 4 6  8 6 4 2
15076:  *			3 6 9 12 9 6 3
15077:  *			2 4 6  8 6 4 2
15078:  *			1 2 3  4 3 2 1
15079:  *		If an even dimension given, the two center numbers stay
15080:  *		the same, e.g., 123321 for the top row if width=6.
15081:  *	      o	For an odd square n x n matrix, the sum of all n^2
15082:  *		weights will be ((n+1)/2)^4.
15083:  *	      o	The largest weight (in the allocated pixsz=8 raster) is 255,
15084:  *		so the largest square matrix is 31 x 31.  Any weight that
15085:  *		tries to grow beyond 255 is held constant at 255.
15086:  *	      o	A new_raster(), pixsz=8, is allocated for the caller.
15087:  *		To avoid memory leaks, be sure to delete_raster() when done.
15088:  * ======================================================================= */
15089: /* --- entry point --- */
15090: raster	*aaweights ( int width, int height )
15091: {
15092: /* -------------------------------------------------------------------------
15093: Allocations and Declarations
15094: -------------------------------------------------------------------------- */
15095: raster	*new_raster(), *weights=NULL;	/* raster of weights returned */
15096: int	irow=0, icol=0,			/* height, width indexes */
15097: 	weight = 0;			/*running weight, as per Notes above*/
15098: /* -------------------------------------------------------------------------
15099: Initialization
15100: -------------------------------------------------------------------------- */
15101: /* --- allocate raster for weights --- */
15102: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
15103: ==  NULL ) goto end_of_job;		/* return NULL error if failed */
15104: /* -------------------------------------------------------------------------
15105: Fill weight matrix, as per Notes above
15106: -------------------------------------------------------------------------- */
15107: for ( irow=0; irow<height; irow++ )	/* outer loop over rows */
15108:   for ( icol=0; icol<width; icol++ )	/* inner loop over cols */
15109:     {
15110:     int	jrow = height-irow-1,		/* backwards irow, height-1,...,0 */
15111: 	jcol =  width-icol-1;		/* backwards icol,  width-1,...,0 */
15112:     weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
15113:     if ( aaalgorithm == 1 ) weight=1;	/* force equal weights */
15114:     setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
15115:     } /* --- end-of-for(irow,icol) --- */
15116: end_of_job:
15117:   return ( weights );			/* back with weights or NULL=error */
15118: } /* --- end-of-function aaweights() --- */
15119: 
15120: 
15121: /* ==========================================================================
15122:  * Function:	aawtpixel ( image, ipixel, weights, rotate )
15123:  * Purpose:	Applies matrix of weights to the pixels
15124:  *		surrounding ipixel in image, rotated clockwise
15125:  *		by rotate degrees (typically 0 or 30).
15126:  * --------------------------------------------------------------------------
15127:  * Arguments:	image (I)	raster * to bitmap (though it can be bytemap)
15128:  *				containing image with pixels to be averaged.
15129:  *		ipixel (I)	int containing index (irow*width+icol) of
15130:  *				center pixel of image for weighted average.
15131:  *		weights (I)	raster * to bytemap of relative weights
15132:  *				(0-255), whose dimensions (usually odd width
15133:  *				and odd height) determine the "subgrid" of
15134:  *				image surrounding ipixel to be averaged.
15135:  *		rotate (I)	int containing degrees clockwise rotation
15136:  *				(typically 0 or 30), i.e., imagine weights
15137:  *				rotated clockwise and then averaging the
15138:  *				image pixels "underneath" it now.
15139:  * --------------------------------------------------------------------------
15140:  * Returns:	( int )		0-255 weighted average, or -1 for any error
15141:  * --------------------------------------------------------------------------
15142:  * Notes:     o	The rotation matrix used (when requested) is
15143:  *		    / x' \     / cos(theta)  sin(theta)/a \  / x \
15144:  *		    |    |  =  |                          |  |   |
15145:  *                  \ y' /     \ -a*sin(theta) cos(theta) /  \ y /
15146:  *		where a=1 (current default) is the pixel (not screen)
15147:  *		aspect ratio width:height, and theta is rotate (converted
15148:  *		from degrees to radians).  Then x=col,y=row are the integer
15149:  *		pixel coords relative to the input center ipixel, and
15150:  *		x',y' are rotated coords which aren't necessarily integer.
15151:  *		The actual pixel used is the one nearest x',y'.
15152:  * ======================================================================= */
15153: /* --- entry point --- */
15154: int	aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
15155: {
15156: /* -------------------------------------------------------------------------
15157: Allocations and Declarations
15158: -------------------------------------------------------------------------- */
15159: int	aaimgval = 0,			/* weighted avg returned to caller */
15160: 	totwts=0, sumwts=0;		/* total of all wts, sum wts used */
15161: int	pixsz = image->pixsz,		/* #bits per image pixel */
15162: 	black1=1, black8=255,		/* black for 1-bit, 8-bit pixels */
15163: 	black = (pixsz==1? black1:black8), /* black value for our image */
15164: 	scalefactor = (black1+black8-black), /* only scale 1-bit images */
15165: 	iscenter = 0;			/* set true if center pixel black */
15166: /* --- grid dimensions and indexes --- */
15167: int	wtheight  = weights->height,	/* #rows in weight matrix */
15168: 	wtwidth   = weights->width,	/* #cols in weight matrix */
15169: 	imgheight =   image->height,	/* #rows in image */
15170: 	imgwidth  =   image->width;	/* #cols in image */
15171: int	wtrow,  wtrow0 = wtheight/2,	/* center row index for weights */
15172: 	wtcol,  wtcol0 = wtwidth/2,	/* center col index for weights */
15173: 	imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
15174: 	imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
15175: /* --- rotated grid variables --- */
15176: static	int prevrotate = 0;		/* rotate from previous call */
15177: static	double costheta = 1.0,		/* cosine for previous rotate */
15178: 	sintheta = 0.0;			/* and sine for previous rotate */
15179: double	a = 1.0;			/* default aspect ratio */
15180: /* -------------------------------------------------------------------------
15181: Initialization
15182: -------------------------------------------------------------------------- */
15183: /* --- refresh trig functions for rotate when it changes --- */
15184: if ( rotate != prevrotate )		/* need new sine/cosine */
15185:   { costheta = cos(((double)rotate)/57.29578);	/*cos of rotate in radians*/
15186:     sintheta = sin(((double)rotate)/57.29578);	/*sin of rotate in radians*/
15187:     prevrotate = rotate; }		/* save current rotate as prev */
15188: /* -------------------------------------------------------------------------
15189: Calculate aapixel as weighted average over image points around ipixel
15190: -------------------------------------------------------------------------- */
15191: for ( wtrow=0; wtrow<wtheight; wtrow++ )
15192:  for ( wtcol=0; wtcol<wtwidth; wtcol++ )
15193:   {
15194:   /* --- allocations and declarations --- */
15195:   int	wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
15196:   int	drow = wtrow - wtrow0,		/* delta row offset from center */
15197: 	dcol = wtcol - wtcol0;		/* delta col offset from center */
15198:   int	iscenter = 0;			/* set true if center point black */
15199:   /* --- initialization --- */
15200:   totwts += wt;				/* sum all weights */
15201:   /* --- rotate (if requested) --- */
15202:   if ( rotate != 0 )			/* non-zero rotation */
15203:     {
15204:     /* --- apply rotation matrix to (x=dcol,y=drow) --- */
15205:     double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
15206:     dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
15207:     dy = -a*dx*sintheta + dy*costheta;	/* dy becomes new dy' */
15208:     dx = dtemp;				/* just for notational convenience */
15209:     /* --- replace original (drow,dcol) with nearest rotated point --- */
15210:     drow = (int)(dy+0.5);		/* round dy for nearest row */
15211:     dcol = (int)(dx+0.5);		/* round dx for nearest col */
15212:     } /* --- end-of-if(rotate!=0) --- */
15213:   /* --- select image pixel to be weighted --- */
15214:   imgrow = imgrow0 + drow;		/*apply displacement to center row*/
15215:   imgcol = imgcol0 + dcol;		/*apply displacement to center col*/
15216:   /* --- if pixel in bounds, accumulate weighted average --- */
15217:   if ( imgrow>=0 && imgrow<imgheight )	/* row is in bounds */
15218:    if ( imgcol>=0 && imgcol<imgwidth )	/* and col is in bounds */
15219:     {
15220:     /* --- accumulate weighted average --- */
15221:     int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
15222:     aaimgval += wt*imgval;		/* weighted sum of image values */
15223:     sumwts += wt;			/* and also sum weights used */
15224:     /* --- check if center image pixel black --- */
15225:     if ( drow==0 && dcol==0 )		/* this is center ipixel */
15226:       if ( imgval==black )		/* and it's black */
15227: 	iscenter = 1;			/* so set black center flag true */
15228:     } /* --- end-of-if(bounds checks ok) --- */
15229:   } /* --- end-of-for(irow,icol) --- */
15230: if ( 0 && iscenter )			/* center point is black */
15231:   aaimgval = black8;			/* so force average black */
15232: else					/* center point not black */
15233:   aaimgval =				/* 0=white ... black */
15234:       ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
15235: /*end_of_job:*/
15236:   return ( aaimgval );
15237: } /* --- end-of-function aawtpixel() --- */
15238: 
15239: 
15240: /* ==========================================================================
15241:  * Function:	mimetexsetmsg ( newmsglevel, newmsgfp )
15242:  * Purpose:	Sets msglevel and msgfp, usually called from
15243:  *		an external driver (i.e., DRIVER not defined
15244:  *		in this module).
15245:  * --------------------------------------------------------------------------
15246:  * Arguments:	newmsglevel (I)	int containing new msglevel
15247:  *				(unchanged if newmsglevel<0)
15248:  *		newmsgfp (I)	FILE * containing new msgfp
15249:  *				(unchanged if newmsgfp=NULL)
15250:  * --------------------------------------------------------------------------
15251:  * Returns:	( int )		always 1
15252:  * --------------------------------------------------------------------------
15253:  * Notes:     o
15254:  * ======================================================================= */
15255: /* --- entry point --- */
15256: int	mimetexsetmsg ( int newmsglevel, FILE *newmsgfp )
15257: {
15258: /* -------------------------------------------------------------------------
15259: set msglevel and msgfp
15260: -------------------------------------------------------------------------- */
15261: if ( newmsglevel >= 0 ) msglevel = newmsglevel;
15262: if ( newmsgfp != NULL ) msgfp = newmsgfp;
15263: return ( 1 );
15264: } /* --- end-of-function mimetexsetmsg() --- */
15265: #endif /* PART3 */
15266: 
15267: /* ---
15268:  * PART1
15269:  * ------ */
15270: #if !defined(PARTS) || defined(PART1)
15271: #ifdef DRIVER
15272: /* ==========================================================================
15273:  * Function:	main() driver for mimetex.c
15274:  * Purpose:	emits a mime xbitmap or gif image of a LaTeX math expression
15275:  *		entered either as
15276:  *		    (1)	html query string from a browser (most typical), or
15277:  *		    (2)	a query string from an html <form method="get">
15278:  *			whose <input name="formdata"> (mostly for demo), or
15279:  *		    (3)	command-line arguments (mostly to test).
15280:  *		If no input supplied, expression defaults to "f(x)=x^2",
15281:  *		treated as test (input method 3).
15282:  *		   If args entered on command-line (or if no input supplied),
15283:  *		output is (usually) human-viewable ascii raster images on
15284:  *		stdout rather than the usual mime xbitmaps or gif images.
15285:  * --------------------------------------------------------------------------
15286:  * Command-Line Arguments:
15287:  *		When running mimeTeX from the command-line, rather than
15288:  *		from a browser, syntax is
15289:  *		     ./mimetex	[-d ]		dump gif to stdout
15290:  *				[expression	expression, e.g., x^2+y^2,
15291:  *				|-f input_file]	or read expression from file
15292:  *				[-m msglevel]	verbosity of debugging output
15293:  *				[-s fontsize]	default fontsize, 0-5
15294:  *		-d   Rather than ascii debugging output, mimeTeX dumps the
15295:  *		     actual gif (or xbitmap) to stdout, e.g.,
15296:  *			./mimetex  -d  x^2+y^2  > expression.gif
15297:  *		     creates a gif file containing an image of x^2+y^2
15298:  *		-f   Reads expression from input_file, and automatically
15299:  *		     assumes -d switch.  The input_file may contain the
15300:  *		     expression on one line or spread out over many lines.
15301:  *		     MimeTeX will concatanate all lines from input_file
15302:  *		     to construct one long expression.  Blanks, tabs, and
15303:  *		     newlines will just be ignored.
15304:  *		-m   0-99, controls verbosity level for debugging output
15305:  *		     (usually used only while testing code).
15306:  *		-s   Font size, 0-5.  As usual, the font size can
15307:  *		     also be specified in the expression by a leading
15308:  *		     preamble terminated by $, e.g., 3$f(x)=x^2 displays
15309:  *		     f(x)=x^2 at font size 3.  Default font size is 2.
15310:  * --------------------------------------------------------------------------
15311:  * Exits:	0=success, 1=some error
15312:  * --------------------------------------------------------------------------
15313:  * Notes:     o For an executable that emits mime xbitmaps, compile as
15314:  *		     cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
15315:  *		or, alternatively, for an executable that emits gif images
15316:  *		     cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
15317:  *		or for gif images with anti-aliasing
15318:  *		     cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
15319:  *		See Notes at top of file for other compile-line -D options.
15320:  *	      o	Move executable to your cgi-bin directory and either
15321:  *		point your browser to it directly in the form
15322:  *		     http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
15323:  *		or put a tag in your html document of the form
15324:  *		     <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
15325:  *		       border=0 align=absmiddle>
15326:  *		where f(x)=x^2 (or any other expression) will be displayed
15327:  *		either as a mime xbitmap or gif image (as per -D flag).
15328:  * ======================================================================= */
15329: 
15330: /* -------------------------------------------------------------------------
15331: header files and other data
15332: -------------------------------------------------------------------------- */
15333: /* --- (additional) standard headers --- */
15334: /* --- other data --- */
15335: #ifdef DUMPENVIRON
15336:  extern	char **environ;			/* environment information */
15337: #endif
15338: 
15339: /* -------------------------------------------------------------------------
15340: globals for gif and png callback functions
15341: -------------------------------------------------------------------------- */
15342: GLOBAL(raster,*bitmap_raster,NULL);	/* use 0/1 bitmap image or */
15343: GLOBAL(intbyte,*colormap_raster,NULL);	/* anti-aliased color indexes */
15344: GLOBAL(int,raster_width,0);		/* width of final/displayed image */
15345: GLOBAL(int,raster_height,0);		/* height of final/displayed image */
15346: GLOBAL(int,raster_baseline,0);		/* baseline of final/displayed image*/
15347: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
15348: #ifdef AA				/* if anti-aliasing requested */
15349:   #define ISAAVALUE 1			/* turn flag on */
15350: #else
15351:   #define ISAAVALUE 0			/* else turn flag off */
15352: #endif
15353: GLOBAL(int,isaa,ISAAVALUE);		/* set anti-aliasing flag */
15354: 
15355: /* -------------------------------------------------------------------------
15356: logging data structure, and default data to be logged
15357: -------------------------------------------------------------------------- */
15358: /* --- logging data structure --- */
15359: #define	logdata	struct logdata_struct	/* "typedef" for logdata_struct*/
15360: logdata
15361:   {
15362:   /* -----------------------------------------------------------------------
15363:   environment variable name, max #chars to display, min msglevel to display
15364:   ------------------------------------------------------------------------ */
15365:   char	*name;				/* environment variable name */
15366:   int	maxlen;				/* max #chars to display */
15367:   int	msglevel;			/* min msglevel to display data */
15368:   } ; /* --- end-of-logdata_struct --- */
15369: /* --- data logged by mimeTeX --- */
15370: STATIC logdata mimelog[]
15371: #ifdef INITVALS
15372:   =
15373:   {
15374:   /* ------ variable ------ maxlen msglevel ----- */
15375:     { "QUERY_STRING",         999,    4 },
15376:     { "REMOTE_ADDR",          999,    3 },
15377:     { "HTTP_REFERER",         999,    3 },
15378:     { "REQUEST_URI",          999,    5 },
15379:     { "HTTP_USER_AGENT",      999,    3 },
15380:     { "HTTP_X_FORWARDED_FOR", 999,    3 },
15381:     { NULL, -1, -1 }			/* trailer record */
15382:   } /* --- end-of-mimelog[] --- */
15383: #endif
15384:   ;
15385: 
15386: 
15387: /* --- entry point --- */
15388: int	main ( int argc, char *argv[]
15389: 	  #ifdef DUMPENVP
15390: 	    , char *envp[]
15391: 	  #endif
15392: 	)
15393: {
15394: /* -------------------------------------------------------------------------
15395: Allocations and Declarations
15396: -------------------------------------------------------------------------- */
15397: /* --- expression to be emitted --- */
15398: static	char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */
15399: char	*expression = exprbuffer;	/* ptr to expression */
15400: int	size = NORMALSIZE;		/* default font size */
15401: char	*query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
15402: char	*mimeprep();			/* preprocess expression */
15403: int	unescape_url();			/* convert %xx's to ascii chars */
15404: int	emitcache();			/* emit cached image if it exists */
15405: int	isquery = 0,			/* true if input from QUERY_STRING */
15406: 	isqempty = 0,			/* true if query string empty */
15407: 	isqforce = 0,			/* true to force query emulation */
15408: 	isqlogging = 0,			/* true if logging in query mode */
15409: 	isformdata = 0,			/* true if input from html form */
15410: 	isinmemory = 1,			/* true to generate image in memory*/
15411: 	isdumpimage = 0,		/* true to dump image on stdout */
15412: 	isdumpbuffer = 0;		/* true to dump to memory buffer */
15413: /* --- rasterization --- */
15414: subraster *rasterize(), *sp=NULL;	/* rasterize expression */
15415: raster	*border_raster(), *bp=NULL;	/* put a border around raster */
15416: int	delete_subraster();		/* for clean-up at end-of-job */
15417: int	type_raster(), type_bytemap(),	/* screen dump function prototypes */
15418: 	xbitmap_raster();		/* mime xbitmap output function */
15419: /* --- http_referer --- */
15420: char	*referer = REFERER;		/* http_referer must contain this */
15421: char	*inputreferer = INPUTREFERER;	/*http_referer's permitted to \input*/
15422: int	reflevels = REFLEVELS, urlncmp(); /* cmp http_referer,server_name */
15423: int	strreplace();			/* replace SERVER_NAME in errmsg */
15424: char	*urlprune();			/* prune referer_match */
15425: struct	{ char *referer; int msgnum; }	/* http_referer can't contain this */
15426: 	denyreferer[] = {		/* referer table to deny access to */
15427: 	#ifdef DENYREFERER
15428: 	  #include DENYREFERER		/* e.g.,  {"",1},  for no referer */
15429: 	#endif
15430: 	{ NULL, -999 } };		/* trailer */
15431: char	*http_referer = getenv("HTTP_REFERER"), /* referer using mimeTeX */
15432: 	*http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
15433: 	*server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
15434: 	*referer_match = (!isempty(http_host)?http_host: /*match http_host*/
15435: 	  (!isempty(server_name)?server_name:(NULL))); /* or server_name */
15436: int	ishttpreferer = (isempty(http_referer)?0:1);
15437: int	isstrstr();			/* search http_referer for referer */
15438: int	isinvalidreferer = 0;		/* true for inavlid referer */
15439: int	norefmaxlen = NOREFMAXLEN;	/*max query_string len if no referer*/
15440: /* --- gif --- */
15441: #if defined(GIF)
15442:   int	GetPixel();			/* feed pixels to gifsave library */
15443:   int	GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
15444:   void	GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
15445: #endif
15446: char	*gif_outfile = (char *)NULL,	/* gif output defaults to stdout */
15447: 	gif_buffer[MAXGIFSZ] = "\000",	/* or gif written in memory buffer */
15448: 	cachefile[256] = "\000",	/* full path and name to cache file*/
15449: 	*md5str();			/* md5 has of expression */
15450: int	maxage = 7200;			/* max-age is two hours */
15451: int	valign = (-9999);		/*Vertical-Align:baseline-(height-1)*/
15452: /* --- pbm/pgm (-g switch) --- */
15453: int	ispbmpgm = 0;			/* true to write pbm/pgm file */
15454: int	type_pbmpgm(), ptype=0;		/* entry point, graphic format */
15455: char	*pbm_outfile = (char *)NULL;	/* output file defaults to stdout */
15456: /* --- anti-aliasing --- */
15457: intbyte	*bytemap_raster = NULL,		/* anti-aliased bitmap */
15458: 	colors[256];			/* grayscale vals in bytemap */
15459: int	aalowpass(), aapnm(),		/*lowpass filters for anti-aliasing*/
15460: 	grayscale = 256;		/* 0-255 grayscales in 8-bit bytes */
15461: int	ncolors=2,			/* #colors (2=b&w) */
15462: 	aacolormap();			/* build colormap from bytemap */
15463: int	ipattern;			/*patternnumcount[] index diagnostic*/
15464: /* --- advertisement preprocessing --- */
15465: int	advertisement(), crc16();	/*wrap expression in advertisement*/
15466: char	*adtemplate = NULL;		/* usually use default message */
15467: char	*host_showad = HOST_SHOWAD;	/* show ads only on this host */
15468: /* --- messages --- */
15469: char	logfile[256] = LOGFILE,		/*log queries if msglevel>=LOGLEVEL*/
15470: 	cachelog[256] = CACHELOG;	/* cached image log in cachepath/ */
15471: char	*timestamp();			/* time stamp for logged messages */
15472: char	*strdetex();			/* remove math chars from messages */
15473: int	logger();			/* logs environ variables */
15474: int	ismonth();			/* check argv[0] for current month */
15475: char	*progname = (argc>0?argv[0]:"noname"); /* name program executed as */
15476: char	*dashes =			/* separates logfile entries */
15477:  "--------------------------------------------------------------------------";
15478: char	*invalid_referer_msg = msgtable[invmsgnum]; /*msg to invalid referer*/
15479: char	*invalid_referer_match = msgtable[refmsgnum]; /*referer isn't host*/
15480: /* -------------------------------------------------------------------------
15481: initialization
15482: -------------------------------------------------------------------------- */
15483: /* --- run optional system command string --- */
15484: #ifdef SYSTEM
15485:   system(SYSTEM);
15486: #endif
15487: /* --- set global variables --- */
15488: daemonlevel++;				/* signal other funcs to reset */
15489: msgfp = stdout;				/* for comamnd-line mode output */
15490: isss = issupersampling;			/* set supersampling flag */
15491: isemitcontenttype = 1;			/* true to emit mime content-type */
15492: iscachecontenttype = 0;			/* true to cache mime content-type */
15493: *contenttype = '\000';			/* reset content-type:, etc. cache */
15494: isnomath = 0;				/* true to inhibit math mode */
15495: seclevel = SECURITY;			/* overall security level */
15496: inputseclevel = INPUTSECURITY;		/* security level for \input{} */
15497: counterseclevel = COUNTERSECURITY;	/* security level for \counter{} */
15498: environseclevel = ENVIRONSECURITY;	/* security level for \environ */
15499: ninputcmds = 0;				/* reset count of \input commands */
15500: exitstatus=0; errorstatus=ERRORSTATUS;	/* reset exit/error status */
15501: iscaching = ISCACHING;			/* true if caching images */
15502: if ( iscaching ) {			/* images are being cached */
15503:   strcpy(cachepath,CACHEPATH);		/* relative path to cached files */
15504:   if ( *cachepath == '%' ) {		/* leading % signals cache headers */
15505:     iscachecontenttype = 1;		/* signal caching mime content-type*/
15506:     strsqueeze(cachepath,1); } }	/* and squeeze out leading % char */
15507: gifSize = 0;				/* signal that image not in memory */
15508: fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */
15509: bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */
15510: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
15511: for ( ipattern=1; ipattern<=51; ipattern++ )
15512:  patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
15513: /* ---
15514:  * check QUERY_STRING query for expression overriding command-line arg
15515:  * ------------------------------------------------------------------- */
15516: if ( query != NULL )			/* check query string from environ */
15517:   if ( strlen(query) >= 1 ) {		/* caller gave us a query string */
15518:     strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
15519:     expression[MAXEXPRSZ] = '\000';	/* make sure it's null terminated */
15520:     if ( 0 )				/*true to remove leading whitespace*/
15521:       while ( isspace(*expression) && *expression!='\000' )
15522:         {strsqueeze(expression,1);}	/* squeeze out white space */
15523:     isquery = 1; }			/* and set isquery flag */
15524: if ( !isquery ) {			/* empty query string */
15525:   char *host = getenv("HTTP_HOST"),	/* additional getenv("") results */
15526:   *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
15527:   if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */
15528:     isquery = 1;			/* set flag to signal query */
15529:     if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */
15530:     strcpy(expression,			/* and give user an error message */
15531:     "\\red\\small\\rm\\fbox{\\begin{gather}\\LaTeX~expression~not~supplied"
15532:     "\\\\i.e.,~no~?query\\_string~given~to~mimetex.cgi\\end{gather}}"); }
15533:   isqempty = 1;				/* signal empty query string */
15534:   } /* --- end-of-if(!isquery) --- */
15535: /* ---
15536:  * process command-line input args (if not a query)
15537:  * ------------------------------------------------ */
15538: if ( !isquery				/* don't have an html query string */
15539: ||   ( /*isqempty &&*/ argc>1) )	/* or have command-line args */
15540:  {
15541:  char	*argsignal = ARGSIGNAL,		/* signals start of mimeTeX args */
15542: 	stopsignal[32] = "--";		/* default Unix end-of-args signal */
15543:  int	iarg=0, argnum=0,		/*argv[] index for command-line args*/
15544: 	exprarg = 0,			/* argv[] index for expression */
15545: 	infilearg = 0,			/* argv[] index for infile */
15546: 	nswitches = 0,			/* number of -switches */
15547: 	isstopsignal = 0,		/* true after stopsignal found */
15548: 	isstrict = 1/*iswindows*/,	/* true for strict arg checking */
15549: 					/*nb, windows has apache "x -3" bug*/
15550: 	nargs=0, nbadargs=0,		/* number of arguments, bad ones */
15551: 	maxbadargs = (isstrict?0:1),	/*assume query if too many bad args*/
15552: 	isgoodargs = 0;			/* true to accept command-line args*/
15553:  if ( argsignal != NULL )		/* if compiled with -DARGSIGNAL */
15554:   while ( argc > ++iarg )		/* check each argv[] for argsignal */
15555:     if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
15556:      { argnum = iarg;			/* got it, start parsing next arg */
15557:        break; }				/* stop looking for argsignal */
15558:  while ( argc > ++argnum )		/* check for switches and values, */
15559:     {
15560:     nargs++;				/* count another command-line arg */
15561:     if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
15562:       {	isstopsignal = 1;		/* so set stopsignal flag */
15563: 	continue; }			/* and get expression after it */
15564:     if ( !isstopsignal			/* haven't seen stopsignal switch */
15565:     &&   *argv[argnum] == '-' )		/* and have some '-' switch */
15566:       {
15567:       char *field = argv[argnum] + 1;	/* ptr to char(s) following - */
15568:       char flag = tolower(*field);	/* single char following '-' */
15569:       int  arglen = strlen(field);	/* #chars following - */
15570:       argnum++;		/* arg following flag/switch is usually its value */
15571:       nswitches++;			/* another switch on command line */
15572:       if ( isstrict &&			/* if strict checking then... */
15573:       !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
15574: 	{ nbadargs++; argnum--; }	/* so ignore longer -xxx switch */
15575:       else				/* process single-char -x switch */
15576:        switch ( flag ) {		/* see what user wants to tell us */
15577: 	/* --- ignore uninterpreted flag --- */
15578: 	default:  nbadargs++;                              argnum--;  break;
15579: 	/* --- adjustable program parameters (not checking input) --- */
15580: 	case 'b': isdumpimage++; isdumpbuffer++;           argnum--;  break;
15581: 	case 'd': isdumpimage++;                           argnum--;  break;
15582: 	case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;
15583: 	case 'f': isdumpimage++;                   infilearg=argnum;  break;
15584: 	case 'g': ispbmpgm++;
15585: 	     if ( arglen > 1 ) ptype = atoi(field+1);	/* -g2 ==> ptype=2 */
15586: 	     if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
15587: 	     else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
15588: 	case 'm': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break;
15589: 	case 'o': istransparent = (istransparent?0:1);     argnum--;  break;
15590: 	case 'q': isqforce = 1;                            argnum--;  break;
15591: 	case 's': if ( argnum < argc ) size = atoi(argv[argnum]);     break;
15592: 	} /* --- end-of-switch(flag) --- */
15593:       } /* --- end-of-if(*argv[argnum]=='-') --- */
15594:     else				/* expression if arg not a -flag */
15595:       if ( infilearg == 0 )		/* no infile arg yet */
15596: 	{ if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
15597: 	  exprarg = argnum;		/* but take last expression */
15598: 	  /*infilearg = (-1);*/ }	/* and set infilearg */
15599:       else nbadargs++;			/* infile and expression invalid */
15600:     } /* --- end-of-while(argc>++argnum) --- */
15601:  if ( msglevel>=999 && msgfp!=NULL )	/* display command-line info */
15602:   { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
15603:     argc,progname,nargs,nbadargs);
15604:     fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
15605:     cachepath,pathprefix); }
15606:  /* ---
15607:   * decide whether command-line input overrides query_string
15608:   * -------------------------------------------------------- */
15609:  if ( isdumpimage > 2 ) nbadargs++;	/* duplicate/conflicting -switch */
15610:  isgoodargs =  ( !isstrict		/*good if not doing strict checking*/
15611:   || !isquery				/* or if no query, must use args */
15612:   || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
15613:  /* ---
15614:   * take expression from command-line args
15615:   * -------------------------------------- */
15616:  if ( isgoodargs && exprarg > 0		/* good expression on command line */
15617:  &&   infilearg <= 0 )			/* and not given in input file */
15618:   if ( !isquery				/* no conflict if no query_string */
15619:   ||   nswitches > 0 )			/* explicit -switch(es) also given */
15620:    { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/
15621:      expression[MAXEXPRSZ] = '\000';	/* make sure it's null terminated */
15622:      isquery = 0; }			/* and not from a query_string */
15623:  /* ---
15624:   * or read expression from input file
15625:   * ---------------------------------- */
15626:  if ( isgoodargs && infilearg > 0 )	/* have a good -f arg */
15627:   {
15628:   FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
15629:   if ( infile != (FILE *)NULL )		/* opened input file successfully */
15630:    { char instring[MAXLINESZ+1];	/* line from file */
15631:      int  exprsz = 0;			/* total #bytes read from file */
15632:      isquery = 0;			/* file input, not a query_string */
15633:      *expression = '\000';		/* start expresion as empty string */
15634:      while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/
15635:       if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */
15636: 	strcat(expression,instring);	/* concat line to end of expression*/
15637: 	exprsz += strlen(instring); }	/* update expression buffer length */
15638:      fclose ( infile ); }	/*close input file after reading expression*/
15639:   } /* --- end-of-if(infilearg>0) --- */
15640:  /* ---
15641:   * xlate +++'s to blanks only if query
15642:   * ----------------------------------- */
15643:  if ( !isquery ) isplusblank = 0;	/* don't xlate +++'s to blanks */
15644:  /* ---
15645:   * check if emulating query (for testing)
15646:   * -------------------------------------- */
15647:  if ( isqforce ) isquery = 1;		/* emulate query string processing */
15648:  /* ---
15649:   * check if emitting pbm/pgm graphic
15650:   * --------------------------------- */
15651:  if ( isgoodargs && ispbmpgm > 0 )	/* have a good -g arg */
15652:   if ( 1 && gif_outfile != NULL )	/* had an -e switch with file */
15653:    if ( *gif_outfile != '\000' )	/* make sure string isn't empty */
15654:      { pbm_outfile = gif_outfile;	/* use -e switch file for pbm/pgm */
15655:        gif_outfile = (char *)NULL;	/* reset gif output file */
15656:        /*isdumpimage--;*/ }		/* and decrement -e count */
15657:  } /* --- end-of-if(!isquery) --- */
15658: /* ---
15659:  * check for <form> input
15660:  * ---------------------- */
15661: if ( isquery ) {				/* must be <form method="get"> */
15662:  if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
15663:   { char *delim=strchr(expression,'=');	/* find equal following formdata */
15664:     if ( delim != (char *)NULL )	/* found unescaped equal sign */
15665:       {strsqueezep(expression,delim+1);} /* so shift name= out */
15666:     while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
15667:       *delim = ' ';			/* is "shorthand" for blank space */
15668:     /*unescape_url(expression,1);*/	/* convert unescaped %xx's to chars */
15669:     unescape_url(expression,0);		/* convert all %xx's to chars */
15670:     unescape_url(expression,0);		/* repeat */
15671:     if(0) msglevel = FORMLEVEL;		/* msglevel for forms */
15672:     isformdata = 1; }			/* set flag to signal form data */
15673:  else /* --- query, but not <form> input --- */
15674:     unescape_url(expression,0); }	/* convert _all_ %xx's to chars */
15675: /* ---
15676:  * check queries for prefixes/suffixes/embedded that might cause problems
15677:  * ---------------------------------------------------------------------- */
15678: /* --- expression whose last char is \ --- */
15679: if ( lastchar(expression) == '\\' )	/* last char is backslash */
15680:   strcat(expression," ");		/* assume "\ " lost the final space*/
15681: /* ---
15682:  * check queries for embedded prefixes signalling special processing
15683:  * ----------------------------------------------------------------- */
15684: if ( isquery )				/* only check queries */
15685:  {
15686:  /* --- check for msglevel=###$ prefix --- */
15687:  if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
15688:    { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
15689:      if ( delim != (char *)NULL )	/* check that we found delim */
15690:       {	*delim = '\000';		/* replace delim with null */
15691: 	if ( seclevel <= 9 )		/* permit msglevel specification */
15692: 	  msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
15693: 	strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
15694:  /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
15695:  if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
15696:    { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
15697:      if ( delim != (char *)NULL )	/* check that we found delim */
15698:       {	*delim = '\000';		/* replace delim with null */
15699: 	if ( seclevel <= 3 )		/* permit logfile specification */
15700: 	  strcpy(logfile,expression+8);	/* interpret xxx in logfile=xxx$ */
15701: 	strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
15702:  } /* --- end-of-if(isquery) --- */
15703: /* ---
15704:  * log query (e.g., for debugging)
15705:  * ------------------------------- */
15706: if ( isquery )				/* only log query_string's */
15707:  if ( msglevel >= LOGLEVEL		/* check if logging */
15708:  &&   seclevel <= 5 )			/* and if logging permitted */
15709:   if ( logfile != NULL )		/* if a logfile is given */
15710:    if ( *logfile != '\000' ) {		/*and if it's not an empty string*/
15711:     if ( (msgfp=fopen(logfile,"a"))	/* open logfile for append */
15712:     !=   NULL )				/* ignore logging if can't open */
15713:      {
15714:      /* --- default logging --- */
15715:      logger(msgfp,msglevel,expression,mimelog); /* log query */
15716:      /* --- additional debug logging (argv and environment) --- */
15717:      if ( msglevel >= 9 )		/* log environment */
15718:       { int i;  /*char name[999],*value;*/
15719: 	fprintf(msgfp,"Command-line arguments...\n");
15720: 	if ( argc < 1 )			/* no command-line args */
15721: 	 fprintf(msgfp,"  ...argc=%d, no argv[] variables\n",argc);
15722: 	else
15723: 	 for ( i=0; i<argc; i++ )	/* display all argv[]'s */
15724: 	  fprintf(msgfp,"  argv[%d] = \"%s\"\n",i,argv[i]);
15725: 	#ifdef DUMPENVP			/* char *envp[] available for dump */
15726: 	fprintf(msgfp,"Environment variables (using envp[])...\n");
15727: 	if ( envp == (char **)NULL )	/* envp not provided */
15728: 	 fprintf(msgfp,"  ...envp[] environment variables not available\n");
15729: 	else
15730: 	 for ( i=0; ; i++ )		/* display all envp[]'s */
15731: 	  if ( envp[i] == (char *)NULL ) break;
15732: 	  else fprintf(msgfp,"  envp[%d] = \"%s\"\n",i,envp[i]);
15733: 	#endif /* --- DUMPENVP ---*/
15734: 	#ifdef DUMPENVIRON	/* skip what should be redundant output */
15735: 	fprintf(msgfp,"Environment variables (using environ)...\n");
15736: 	if ( environ == (char **)NULL )	/* environ not provided */
15737: 	 fprintf(msgfp,"  ...extern environ variables not available\n");
15738: 	else
15739: 	 for ( i=0; ; i++ )		/*display environ[] and getenv()'s*/
15740: 	  if ( environ[i] == (char *)NULL ) break;
15741: 	  else {
15742: 	    strcpy(name,environ[i]);	/* set up name for getenv() arg */
15743: 	    if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
15744: 	      {	*value = '\000';	/* got it, so null-terminate name */
15745: 		value = getenv(name); }	/* and look up name using getenv() */
15746: 	    else strcpy(name,"NULL");	/* missing = delim in environ[i] */
15747: 	    fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
15748: 	    i,environ[i],name,(value==NULL?"NULL":value));
15749: 	    } /* --- end-of-if/else --- */
15750: 	#endif /* --- DUMPENVIRON ---*/
15751:       } /* --- end-of-if(msglevel>=9) --- */
15752:      /* --- close log file if no longer needed --- */
15753:      if ( msglevel < DBGLEVEL )		/* logging, but not debugging */
15754:       {	fprintf(msgfp,"%s\n",dashes);	/* so log separator line, */
15755: 	fclose(msgfp);			/* close logfile immediately, */
15756: 	msgfp = NULL; }			/* and reset msgfp pointer */
15757:      else
15758: 	isqlogging = 1;			/* set query logging flag */
15759:      } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
15760:     else				/* couldn't open logfile */
15761:      msglevel = 0; }			/* can't emit messages */
15762: /* ---
15763:  * prepend prefix to submitted expression
15764:  * -------------------------------------- */
15765: if ( 1 || isquery )			/* queries or command-line */
15766:  if ( *exprprefix != '\000' )		/* we have a prefix string */
15767:   { int npref = strlen(exprprefix);	/* #chars in prefix */
15768:     memmove(expression+npref+1,expression,strlen(expression)+1);/*make room*/
15769:     memcpy(expression,exprprefix,npref); /* copy prefix into expression */
15770:     expression[npref] = '{';		/* followed by { */
15771:     strcat(expression,"}"); }		/* and terminating } to balance { */
15772: /* ---
15773:  * check if http_referer is allowed to use this image and to use \input{}
15774:  * ---------------------------------------------------------------------- */
15775: if ( isquery ) {			/* not relevant if "interactive" */
15776:  /* --- check -DREFERER=\"comma,separated,list\" of valid referers --- */
15777:  if ( referer != NULL ) {		/* compiled with -DREFERER=\"...\" */
15778:   if ( strcmp(referer,"month") != 0 )	/* but it's *only* "month" signal */
15779:    if ( ishttpreferer )			/* or called "standalone" */
15780:     if ( !isstrstr(http_referer,referer,0) ) { /* invalid http_referer */
15781:       expression = invalid_referer_msg; /* so give user error message */
15782:       isinvalidreferer = 1; } }		/* and signal invalid referer */
15783:  else					/* compiled without -DREFERER= */
15784:   if ( reflevels > 0 ) {		/*match referer unless -DREFLEVELS=0*/
15785:    /* --- check topmost levels of http_referer against http_host --- */
15786:    if ( ishttpreferer			/* have http_referer */
15787:    &&   !isempty(referer_match) )	/* and something to match it with */
15788:     if ( !urlncmp(http_referer,referer_match,reflevels) ) { /*match failed*/
15789:      strcpy(exprbuffer,invalid_referer_match); /* init error message */
15790:      strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
15791:        strdetex(urlprune(referer_match,reflevels),1),0);/*with referer_match*/
15792:      isinvalidreferer = 1; }		/* and signal invalid referer */
15793:    } /* --- end-of-if(reflevels>0) --- */
15794:  /* --- check -DINPUTREFERER=\"comma,separated,list\" of \input users --- */
15795:  inputseclevel = INPUTSECURITY;		/* set default input security */
15796:  if ( inputreferer != NULL ) {		/* compiled with -DINPUTREFERER= */
15797:   if ( http_referer == NULL )		/* but no http_referer given */
15798:    inputseclevel = (-1);		/* unknown user can't \input{} */
15799:   else					/*have inputreferer and http_referer*/
15800:    if ( !isstrstr(http_referer,inputreferer,0) ) /*http_referer can't \input*/
15801:     inputseclevel = (-1);		/* this known user can't \input{} */
15802:   } /* --- end-of-if(inputreferer!=NULL) --- */
15803:  } /* --- end-of-if(isquery) --- */
15804: /* ---
15805:  * check if referer contains "month" signal
15806:  * ---------------------------------------- */
15807: if ( isquery )				/* not relevant if "interactive" */
15808:  if ( referer != NULL )			/* nor if compiled w/o -DREFERER= */
15809:   if ( !isinvalidreferer )		/* nor if already invalid referer */
15810:    if ( strstr(referer,"month") != NULL ) /* month check requested */
15811:     if ( !ismonth(progname) )		/* not executed as mimetexJan-Dec */
15812:      { expression = invalid_referer_msg; /* so give user error message */
15813:        isinvalidreferer = 1; }		/* and signal invalid referer */
15814: /* ---
15815:  * check if http_referer is to be denied access
15816:  * -------------------------------------------- */
15817: if ( isquery )				/* not relevant if "interactive" */
15818:  if ( !isinvalidreferer )		/* nor if already invalid referer */
15819:   { int	iref=0, msgnum=(-999);		/* denyreferer index, message# */
15820:     for ( iref=0; msgnum<0; iref++ ) {	/* run through denyreferer[] table */
15821:       char *deny = denyreferer[iref].referer; /* referer to be denied */
15822:       if ( deny == NULL ) break;	/* null signals end-of-table */
15823:       if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
15824: 	{fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
15825: 	 iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
15826:       if ( *deny == '\000' )		/* signal to check for no referer */
15827: 	{ if ( http_referer == NULL )	/* http_referer not supplied */
15828: 	   msgnum = denyreferer[iref].msgnum; } /* so set message# */
15829:       else				/* have referer to check for */
15830:        if ( http_referer != NULL )	/* and have referer to be checked */
15831: 	if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
15832: 	 msgnum = denyreferer[iref].msgnum; /* so set message# */
15833:       } /* --- end-of-for(iref) --- */
15834:     if ( msgnum >= 0 )			/* deny access to this referer */
15835:      { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
15836:        expression = msgtable[msgnum];	/* set user error message */
15837:        isinvalidreferer = 1; }		/* and signal invalid referer */
15838:   } /* --- end-of-if(!isinvalidreferer) --- */
15839: /* --- also check maximum query_string length if no http_referer given --- */
15840: if ( isquery )				/* not relevant if "interactive" */
15841:  if ( !isinvalidreferer )		/* nor if already invalid referer */
15842:   if ( !ishttpreferer )			/* no http_referer supplied */
15843:    if ( strlen(expression) > norefmaxlen ) { /* query_string too long */
15844:     if ( isempty(referer_match) )	/* no referer_match to display */
15845:      expression = invalid_referer_msg;	/* set invalid http_referer message*/
15846:     else {				/* error with referer_match display*/
15847:      strcpy(exprbuffer,invalid_referer_match); /* init error message */
15848:      strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
15849:        strdetex(urlprune(referer_match,reflevels),1),0); } /*with host_http*/
15850:      isinvalidreferer = 1; }		/* and signal invalid referer */
15851: /* ---
15852:  * check for "advertisement"
15853:  * ------------------------- */
15854: /* --- check if advertisement messages only for one particular host --- */
15855: if ( !isempty(host_showad) )		/* messages only for this referer */
15856:  if ( !isempty(referer_match) )		/* have HTTP_HOST or SERVER_NAME */
15857:    if ( strstr(referer_match,host_showad) /* see if this host sees ad */
15858:    == NULL )				/* not mimetex host for ad */
15859:      adfrequency = 0;			/* turn off advertisements */
15860: /* --- check for advertisement directive (\advertisement) --- */
15861: if ( strreplace(expression,"\\advertisement","",0) /*remove \advertisement*/
15862: >=   1 ) adfrequency = 1;		/* force advertisement display */
15863: if ( adfrequency > 0 ) {		/* advertising enabled */
15864:   int	npump = crc16(expression)%16;	/* #times, 0-15, to pump rand() */
15865:   srand(atoi(timestamp(TZDELTA,4)));	/* init rand() with mmddhhmmss */
15866:   while ( npump-- >= 0 ) rand();	/* pre-pump rand() before use */
15867:   if ( (1+rand())%adfrequency == 0 ) {	/* once every adfrequency calls */
15868:     advertisement(expression,adtemplate); } } /*wrap expression in advert*/
15869: /* ---
15870:  * check for image caching (and whether or not caching content type)
15871:  * ----------------------------------------------------------------- */
15872: if ( strstr(expression,"\\counter")  != NULL /* can't cache \counter{} */
15873: ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */
15874: ||   strstr(expression,"\\today")    != NULL /* can't cache \today */
15875: ||   strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
15876: ||   strstr(expression,"\\nocach")   != NULL /* no caching requested */
15877: ||   isformdata				/* don't cache user form input */
15878:  ) { iscaching = 0;			/* so turn caching off */
15879:      maxage = 5; }			/* and set max-age to 5 seconds */
15880: if ( strstr(expression,"\\depth")    != NULL ) /* cache content-type lines */
15881:      iscachecontenttype = 1;		/* set flag to cache content-type */
15882: if ( strstr(expression,"\\nodepth")  != NULL ) /* don't cache content-type */
15883:      iscachecontenttype = 0;		/*set flag to not cache content-type*/
15884: if ( isquery )				/* don't cache command-line images */
15885:  if ( iscaching )			/* image caching enabled */
15886:   {
15887:   /* --- set up path to cached image file --- */
15888:   char *md5hash = md5str(expression);	/* md5 hash of expression */
15889:   if ( md5hash == NULL )		/* failed for some reason */
15890:     iscaching = 0;			/* so turn off caching */
15891:   else
15892:    {
15893:    strcpy(cachefile,cachepath);		/* start with (relative) path */
15894:    strcat(cachefile,md5hash);		/* add md5 hash of expression */
15895:    strcat(cachefile,".gif");		/* finish with .gif extension */
15896:    gif_outfile = cachefile;		/* signal GIF_Create() to cache */
15897:    /* --- emit mime content-type line --- */
15898:    if ( 0 && isemitcontenttype )	/* now done in emitcache() */
15899:     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
15900:       if ( abs(valign) < 999 )		/* have vertical align */
15901:         fprintf( stdout, "Vertical-Align: %d\n",valign );
15902:       fprintf( stdout, "Content-type: image/gif\n\n" ); }
15903:    /* --- emit cached image if it already exists --- */
15904:    if ( emitcache(cachefile,maxage,valign,0) > 0 ) /* cached image emitted */
15905:     goto end_of_job;			/* so nothing else to do */
15906:    /* --- log caching request --- */
15907:    if ( msglevel >= 1			/* check if logging */
15908:    /*&&   seclevel <= 5*/ )		/* and if logging permitted */
15909:     if ( cachelog != NULL )		/* if a logfile is given */
15910:      if ( *cachelog != '\000' )		/*and if it's not an empty string*/
15911:       { char filename[256];		/* construct cachepath/cachelog */
15912:         FILE *filefp=NULL;		/* fopen(filename) */
15913:         strcpy(filename,cachepath);	/* start with (relative) path */
15914:         strcat(filename,cachelog);	/* add cache log filename */
15915:         if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
15916:         !=   NULL )			/* ignore logging if can't open */
15917: 	 { int isreflogged = 0;		/* set true if http_referer logged */
15918: 	   fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */
15919: 	    timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
15920: 	   fprintf(filefp,"%s\n",expression); /* expression in filename */
15921: 	   if ( http_referer != NULL )	/* show referer if we have one */
15922: 	    if ( *http_referer != '\000' )    /* and if not an empty string*/
15923: 	      {	int loglen = strlen(dashes);  /* #chars on line in log file*/
15924: 		char *refp = http_referer;    /* line to be printed */
15925: 		isreflogged = 1;	      /* signal http_referer logged*/
15926: 		while ( 1 ) {		      /* printed in parts if needed*/
15927: 		  fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
15928: 		  if ( strlen(refp) <= loglen ) break;  /* no more parts */
15929: 		  refp += loglen; } }	      /* bump ptr to next part */
15930: 	   if ( !isreflogged )		      /* http_referer not logged */
15931: 	     fprintf(filefp,"http://none\n"); /* so log dummy referer line */
15932: 	   fprintf(filefp,"%s\n",dashes);     /* separator line */
15933: 	   fclose(filefp); }		     /* close logfile immediately */
15934:       } /* --- end-of-if(cachelog!=NULL) --- */
15935:    } /* --- end-of-if/else(md5hash==NULL) --- */
15936:   } /* --- end-of-if(iscaching) --- */
15937: /* ---
15938:  * emit copyright, gnu/gpl notice (if "interactive")
15939:  * ------------------------------------------------- */
15940: if ( !isdumpimage )			/* don't mix ascii with image dump */
15941:  if ( (!isquery||isqlogging) && msgfp!=NULL ) { /* called from command line */
15942:    fprintf(msgfp,"%s\n%s\n",copyright1,copyright2); /* display copyright */
15943:    fprintf(msgfp,"Most recent revision: %s\n",REVISIONDATE); /*revision date*/
15944:    } /* --- end-of-if(!isquery...) --- */
15945: /* -------------------------------------------------------------------------
15946: rasterize expression and put a border around it
15947: -------------------------------------------------------------------------- */
15948: /* --- preprocess expression, converting LaTeX constructs for mimeTeX  --- */
15949: if ( expression != NULL ) {		/* have expression to rasterize */
15950:   expression = mimeprep(expression); }	/* preprocess expression */
15951: /* --- double-check that we actually have an expression to rasterize --- */
15952: if ( expression == NULL ) {		/* nothing to rasterize */
15953:   if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
15954:   if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
15955:     if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
15956:     fprintf(msgfp,"No LaTeX expression to rasterize\n"); }
15957:   goto end_of_job; }			/* and then quit */
15958: /* --- rasterize expression --- */
15959: if ( (sp = rasterize(expression,size)) == NULL ) { /* failed to rasterize */
15960:   if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
15961:   if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
15962:     if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
15963:     fprintf(msgfp,"Failed to rasterize %.2048s\n",expression); }
15964:   if ( isquery ) {			/* try to display failed expression*/
15965:     char errormsg[4096];		/* buffer for failed expression */
15966:     strcpy(errormsg,			/* init error message */
15967:     "\\red\\fbox{\\begin{gather}"
15968:     "{\\rm~mi\\underline{meTeX~failed~to~render~your~expressi}on}\\\\[5]");
15969:     strcat(errormsg,"{\\rm\\hspace{10}{"); /*render expression as \rm*/
15970:     strcat(errormsg,strdetex(expression,0));/*add detexed expression to msg*/
15971:     strcat(errormsg,"}\\hspace{10}}\\end{gather}}"); /* finish up */
15972:     if ( (sp = rasterize(errormsg,1)) == NULL ) /*couldn't rasterize errmsg*/
15973:       sp = rasterize(			/* so rasterize generic error */
15974:       "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1); }
15975:   if ( sp ==  NULL ) goto end_of_job;	/* re-check for err message failure*/
15976:   magstep = 1;				/* don't magstep error msgs */
15977:   } /* --- end-of-if((sp=rasterize())==NULL) --- */
15978: /* --- magnify entire image here if we need >>bit<<map for pbm output --- */
15979: if ( !isaa || (ispbmpgm && ptype<2) ) {	/*or use bytemapmag() below instead*/
15980:  if ( magstep > 1 && magstep <= 10 ) {	/* magnify entire bitmap image */
15981:   raster *rastmag(), *magrp=NULL;	/* bitmap magnify function */
15982:   int baseline = sp->baseline;		/* original image baseline */
15983:   magrp = rastmag(sp->image,magstep);	/* magnify raster image */
15984:   if ( magrp != NULL ) {		/* succeeded to magnify image */
15985:     delete_raster(sp->image);		/* free original raster image */
15986:     sp->image = magrp;			/*and replace it with magnified one*/
15987:     /* --- adjust parameters --- */
15988:     baseline *= magstep;		/* scale baseline */
15989:     if ( baseline > 0 ) baseline += 1;	/* adjust for no descenders */
15990:     sp->baseline = baseline; }		/*reset baseline of magnified image*/
15991:   magstep = (-1);			/*done, don't also use bytemapmag()*/
15992:   } /* --- end-of-if(magstep) --- */
15993:  } /* --- end-of-if(1||(ispbmpgm&&ptype<2)) --- */
15994: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
15995: if ( issupersampling )			/* no border needed for gifs */
15996:   bp = sp->image;			/* so just extract pixel map */
15997: else					/* for mime xbitmaps must have... */
15998:   bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
15999: sp->image = bitmap_raster = bp;		/* global copy for gif,png output */
16000: raster_width = bp->width; raster_height = bp->height; /* global copy */
16001: raster_baseline = sp->baseline;		/* global copy (not needed) */
16002: if ( sp!=NULL && bp!=NULL ) {		/* have raster */
16003:   valign= raster_baseline -(raster_height -1);/*#pixels for Vertical-Align:*/
16004:   if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */
16005: if ( ispbmpgm && ptype<2 )		/* -g switch or -g1 switch */
16006:   type_pbmpgm(bp,ptype,pbm_outfile);	/* emit b/w pbm file */
16007: /* -------------------------------------------------------------------------
16008: generate anti-aliased bytemap from (bordered) bitmap
16009: -------------------------------------------------------------------------- */
16010: if ( isaa )				/* we want anti-aliased bitmap */
16011:   {
16012:   /* ---
16013:    * allocate bytemap and colormap as per width*height of bitmap
16014:    * ----------------------------------------------------------- */
16015:   int	nbytes = (raster_width)*(raster_height); /*#bytes for byte,colormap*/
16016:   if ( isss )				/* anti-aliasing by supersampling */
16017:     bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
16018:   else					/* need to allocate bytemap */
16019:     if ( aaalgorithm == 0 )		/* anti-aliasing not wanted */
16020:       isaa = 0;				/* so signal no anti-aliasing */
16021:     else				/* anti-aliasing wanted */
16022:       if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
16023:       ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
16024:   /* ---
16025:    * now generate anti-aliased bytemap and colormap from bitmap
16026:    * ---------------------------------------------------------- */
16027:   if ( isaa )				/*re-check that we're anti-aliasing*/
16028:     {
16029:     /* ---
16030:      * select anti-aliasing algorithm
16031:      * ------------------------------ */
16032:     if ( !isss )			/* generate bytemap for lowpass */
16033:      switch ( aaalgorithm ) {		/* choose antialiasing algorithm */
16034:        default: isaa = 0; break;	/* unrecognized algorithm */
16035:        case 1:				/* 1 for aalowpass() */
16036: 	if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/
16037: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
16038: 	break;
16039:        case 2:				/*2 for netpbm pnmalias.c algorithm*/
16040: 	if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
16041: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
16042: 	break;
16043:        case 3:				/*3 for aapnm() based on aagridnum()*/
16044: 	if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
16045: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
16046: 	break;
16047:        case 4:				/* 4 for aalookup() table lookup */
16048: 	if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */
16049: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
16050: 	break;
16051:        } /* --- end-of-switch(aaalgorithm) --- */
16052:     /* ---
16053:      * emit aalookup() pattern# counts/percents diagnostics
16054:      * ---------------------------------------------------- */
16055:     if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/
16056:      int pcount0=0, pcount1=0;		/* init total w,b center counts */
16057:      for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
16058:       if ( ipattern > 1 )		/* ignore all-white squares */
16059:        pcount0 += patternnumcount0[ipattern];  /* bump total white centers */
16060:       pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */
16061:      if ( pcount0+pcount1 > 0 )		/* have pcounts (using aalookup) */
16062:       fprintf(msgfp, "  aalookup() patterns excluding#1 white"
16063:       " (%%'s are in tenths of a percent)...\n");
16064:      for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
16065:       int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern];
16066:       if ( tot > 0 )			/* this pattern occurs in image */
16067:        fprintf(msgfp,
16068:        "  pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n",
16069:        ipattern, patternnumcount0[ipattern],  (ipattern<=1? 999.99:
16070:        1000.*((double)patternnumcount0[ipattern])/((double)pcount0)),
16071:        patternnumcount1[ipattern],
16072:        1000.*((double)patternnumcount1[ipattern])/((double)pcount1),
16073:        tot,  (ipattern<=1? 999.99:
16074:        1000.*((double)tot)/((double)(pcount0+pcount1))) ); }
16075:       if ( pcount0+pcount1 > 0 )	/* true when using aalookup() */
16076:        fprintf(msgfp,
16077:        "all patterns: %7d          +%7d          =%7d  total pixels\n",
16078:        pcount0,pcount1,pcount0+pcount1); }
16079:     /* ---
16080:      * apply magstep if requested and not already done to bitmap above
16081:      * --------------------------------------------------------------- */
16082:       if ( 1 ) {			/* or use rastmag() above instead */
16083:        if ( magstep > 1 && magstep <= 10 ) { /*magnify entire bytemap image*/
16084:         intbyte *bytemapmag(), *magmap=NULL; /* bytemap magnify function */
16085:         magmap=bytemapmag(bytemap_raster,raster_width,raster_height,magstep);
16086:         if ( magmap != NULL ) {		/* succeeded to magnify image */
16087:           free(bytemap_raster);		/* free original bytemap image */
16088:           bytemap_raster = magmap;	/*and replace it with magnified one*/
16089:           /* --- adjust parameters --- */
16090:           raster_width *= magstep; raster_height *= magstep; /*scale raster*/
16091:           nbytes *= (magstep*magstep);	/* scale total image size */
16092:           if ( abs(valign) < 255 ) {	/* valign okay */
16093:             valign *= magstep;		/* scale by magstep */
16094:             if ( abs(valign) > 512 ) valign = (-9999); } /* sanity check */
16095:           } /* --- end-of-if(magmap!=NULL) --- */
16096:         magstep = (-1);			/*done, don't also use bytemapmag()*/
16097:         } /* --- end-of-if(magstep) --- */
16098:        } /* --- end-of-if(1) --- */
16099:     /* ---
16100:      * finally, generate colors and colormap
16101:      * ------------------------------------- */
16102:     if ( isaa )				/* have bytemap, so... */
16103:       if ( (colormap_raster = (intbyte *)malloc(nbytes)) /*malloc colormap*/
16104:       ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
16105:     if ( isaa ) {			/* we have byte/colormap_raster */
16106:       ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
16107:       if ( ncolors < 2 )		/* failed */
16108: 	{ isaa = 0;			/* so turn off anti-aliasing */
16109: 	  ncolors = 2; }		/* and reset for black&white */
16110:       } /* --- end-of-if(isaa) --- */
16111:      if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */
16112:       raster pbm_raster;		/*construct arg for write_pbmpgm()*/
16113:       pbm_raster.width  = raster_width;  pbm_raster.height = raster_height;
16114:       pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;
16115:       type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
16116:     } /* --- end-of-if(isaa) --- */
16117:   } /* --- end-of-if(isaa) --- */
16118: /* -------------------------------------------------------------------------
16119: display results on msgfp if called from command line (usually for testing)
16120: -------------------------------------------------------------------------- */
16121: if ( (!isquery||isqlogging) || msglevel >= 99 )	/*command line or debuging*/
16122:  if ( !isdumpimage )			/* don't mix ascii with image dump */
16123:   {
16124:   /* ---
16125:    * display ascii image of rasterize()'s rasterized bitmap
16126:    * ------------------------------------------------------ */
16127:   if ( !isss )				/* no bitmap for supersampling */
16128:     { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
16129:       type_raster(bp,msgfp); }		/* emit ascii image of raster */
16130:   /* ---
16131:    * display anti-aliasing results applied to rasterized bitmap
16132:    * ---------------------------------------------------------- */
16133:   if ( isaa )				/* if anti-aliasing applied */
16134:     {
16135:     int	igray;				/* colors[] index */
16136:     /* --- anti-aliased bytemap image --- */
16137:     if ( msgfp!=NULL && msglevel>=9 )	/* don't usually emit raw bytemap */
16138:       {	fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
16139: 	"asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
16140: 	type_bytemap(bytemap_raster,grayscale,
16141:         raster_width,raster_height,msgfp); }
16142:     /* --- colormap image --- */
16143:     fprintf(msgfp,"\nHex dump of colormap indexes, "  /* emit colormap */
16144:       "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
16145:     type_bytemap(colormap_raster,ncolors,
16146:     raster_width,raster_height,msgfp);
16147:     /* --- rgb values corresponding to colormap indexes */
16148:     fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
16149:     for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
16150:       fprintf(msgfp,"%s%2x-->%3d", (igray%5?"   ":"\n"),
16151: 	igray,(int)(colors[ncolors-1]-colors[igray]));
16152:     fprintf(msgfp,"\n");		/* always needs a final newline */
16153:     } /* --- end-of-if(isaa) --- */
16154:   } /* --- end-of-if(!isquery||msglevel>=9) --- */
16155: /* -------------------------------------------------------------------------
16156: emit xbitmap or gif image, and exit
16157: -------------------------------------------------------------------------- */
16158: if (  isquery				/* called from browser (usual) */
16159: ||    (isdumpimage && !ispbmpgm)	/* or to emit gif dump of image */
16160: ||    msglevel >= 99 )			/* or for debugging */
16161:  {
16162:  int  igray = 0;			/* grayscale index */
16163:  #if defined(GIF)			/* compiled to emit gif */
16164:  /* ------------------------------------------------------------------------
16165:  emit GIF image
16166:  ------------------------------------------------------------------------- */
16167:   /* --- don't use memory buffer if outout file given --- */
16168:   if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
16169:   /* --- construct contenttype[] buffer containing mime headers --- */
16170:   if ( 1 ) {				/* always construct buffer */
16171:     sprintf( contenttype, "Cache-Control: max-age=%d\n", maxage );
16172:     /*sprintf(contenttype+strlen(contenttype),
16173:        "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
16174:     /*sprintf(contenttype+strlen(contenttype),
16175:        "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n");*/
16176:     if ( abs(valign) < 999 )		/* have Vertical-Align: header info*/
16177:       sprintf( contenttype+strlen(contenttype),
16178:        "Vertical-Align: %d\n", valign );
16179:     sprintf( contenttype+strlen(contenttype),
16180:       "Content-type: image/gif\n\n" ); }
16181:   /* --- emit mime content-type line --- */
16182:   if ( isemitcontenttype		/* content-type lines wanted */
16183:   &&   !isdumpimage			/* don't mix ascii with image dump */
16184:   &&   !isinmemory			/* done below if in memory */
16185:   &&   !iscaching )			/* done by emitcache() if caching */
16186:     { fputs(contenttype,stdout); }	/* emit content-type: header buffer*/
16187:   /* --- write output to memory buffer, possibly for testing --- */
16188:   if ( isinmemory			/* want gif written to memory */
16189:   ||   isdumpbuffer )			/*or dump memory buffer for testing*/
16190:    if ( gif_outfile == NULL )		/* and don't already have a file */
16191:     { *gif_buffer = '\000';		/* init buffer as empty string */
16192:       memset(gif_buffer,0,MAXGIFSZ);	/* zero out buffer */
16193:       gif_outfile = gif_buffer;		/* and point outfile to buffer */
16194:       if ( isdumpbuffer )		/* buffer dump test requested */
16195: 	isdumpbuffer = 999; }		/* so signal dumping to buffer */
16196:   /* --- initialize gifsave library and colors --- */
16197:   if ( msgfp!=NULL && msglevel>=999 )
16198:     { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
16199:       raster_width,raster_height,ncolors); fflush(msgfp); }
16200:   while ( 1 )		/* init gifsave lib, and retry if caching fails */
16201:     { int status = GIF_Create(gif_outfile,
16202:         raster_width,raster_height, ncolors, 8);
16203:       if ( status == 0 ) break;		/* continue if succeeded */
16204:       if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
16205:       iscaching = 0;			/* retry without cache file */
16206:       isdumpbuffer = 0;			/* reset isdumpbuffer signal */
16207:       if ( isquery ) isinmemory = 1;	/* force in-memory image generation*/
16208:       if ( isinmemory ) {		/* using memory buffer */
16209: 	gif_outfile = gif_buffer;	/* emit images to memory buffer */
16210: 	*gif_outfile = '\000'; }	/* empty string signals buffer */
16211:       else {				/* or */
16212: 	gif_outfile = (char *)NULL;	/* emit images to stdout */
16213: 	if ( isemitcontenttype ) {	/* content-type lines wanted */
16214: 	  fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
16215: 	  fprintf( stdout, "Content-type: image/gif\n\n" ); } }
16216:     } /* --- end-of-while(1) --- */
16217:   GIF_SetColor(0,bgred,bggreen,bgblue);	/* background white if all 255 */
16218:   if ( !isaa )				/* just b&w if not anti-aliased */
16219:     { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
16220:       colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
16221:   else					/* set grayscales for anti-aliasing */
16222:     /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
16223:     for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
16224:       {
16225:       /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
16226:       double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
16227:       /* --- r,g,b components go from background to foreground color --- */
16228:       int red  = iround(((double)bgred)  +gfrac*((double)(fgred-bgred))),
16229: 	  green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
16230: 	  blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
16231:       /* --- set color index number igray to rgb values gray,gray,gray --- */
16232:       GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
16233:       } /* --- end-of-for(igray) --- */
16234:   /* --- set gif color#0 (background) transparent --- */
16235:   if ( istransparent )			/* transparent background wanted */
16236:     GIF_SetTransparent(0);		/* set transparent background */
16237:   if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
16238:   /* --- emit compressed gif image (to stdout or cache file) --- */
16239:   GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
16240:   GIF_Close();				/* close file */
16241:   if ( msgfp!=NULL && msglevel>=9 )
16242:     { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
16243:       fflush(msgfp); }
16244:   /* --- may need to emit image from cached file or from memory --- */
16245:   if ( isquery				/* have an actual query string */
16246:   ||   isdumpimage			/* or dumping image */
16247:   ||   msglevel >= 99 ) {		/* or debugging */
16248:   int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
16249:    if ( iscaching )			/* caching enabled */
16250:      emitcache(cachefile,maxage2,valign,0); /*emit cached image (hopefully)*/
16251:    else if ( isinmemory )		/* or emit image from memory buffer*/
16252:      emitcache(gif_buffer,maxage2,valign,1); } /*emitted from memory buffer*/
16253:   /* --- for testing, may need to write image buffer to file --- */
16254:   if ( isdumpbuffer > 99 )		/* gif image in memory buffer */
16255:    if ( gifSize > 0 )			/* and it's not an empty buffer */
16256:     { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
16257:       if ( dumpfp != NULL )		/* file opened successfully */
16258: 	{ fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
16259: 	  fclose(dumpfp); }		/* and close file */
16260:     } /* --- end-of-if(isdumpbuffer>99) --- */
16261:  #else
16262:  /* ------------------------------------------------------------------------
16263:  emit mime XBITMAP image
16264:  ------------------------------------------------------------------------- */
16265:   xbitmap_raster(bp,stdout);		/* default emits mime xbitmap */
16266:  #endif
16267:  } /* --- end-of-if(isquery) --- */
16268: /* --- exit --- */
16269: end_of_job:
16270:   if ( !isss )				/*bytemap raster in sp for supersamp*/
16271:    if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
16272:   if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
16273:   if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
16274:   if ( 1 && sp != NULL ) delete_subraster(sp);	/* and free expression */
16275:   if ( msgfp != NULL			/* have message/log file open */
16276:   &&   msgfp != stdout )		/* and it's not stdout */
16277:    { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
16278:        timestamp(TZDELTA,0));
16279:      fprintf(msgfp,"%s\n",dashes);	/* so log separator line */
16280:      fclose(msgfp); }			/* and close logfile */
16281:   /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
16282:   #if defined(_CRTDBG_MAP_ALLOC)
16283:     _CrtDumpMemoryLeaks();
16284:   #endif
16285:   /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
16286:   #if !defined(_USRDLL)
16287:     if ( errorstatus == 0 )		/*user doesn't want errors signalled*/
16288:       exitstatus = 0;			/* so reset error status */
16289:     exit ( exitstatus );
16290:   #endif
16291: } /* --- end-of-function main() --- */
16292: 
16293: /* ==========================================================================
16294:  * Function:	CreateGifFromEq ( expression, gifFileName )
16295:  * Purpose:	shortcut method to create GIF file for expression,
16296:  *		with antialising and all other capabilities
16297:  * --------------------------------------------------------------------------
16298:  * Arguments:	expression (I)	char *ptr to null-terminated string
16299:  *				containing LaTeX expression to be rendred
16300:  *		gifFileName (I)	char *ptr to null-terminated string
16301:  *				containing name of output gif file
16302:  * --------------------------------------------------------------------------
16303:  * Returns:	( int )		exit value from main (0 if successful)
16304:  * --------------------------------------------------------------------------
16305:  * Notes:     o	This function is the entry point when mimeTeX is built
16306:  *		as a Win32 DLL rather then a standalone app or CGI
16307:  *	      o	Contributed to mimeTeX by Shital Shah.  See his homepage
16308:  *		  http://www.shitalshah.com
16309:  *	      o	Shital discusses the mimeTeX Win32 DLL project at
16310:  *		  http://www.codeproject.com/dotnet/Eq2Img.asp
16311:  *		and you can download his latest code from
16312:  *		  http://www.shitalshah.com/dev/eq2img_all.zip
16313:  * ======================================================================= */
16314: /* --- include function to expose Win32 DLL to outside world --- */
16315: #if defined(_USRDLL)
16316:   extern _declspec(dllexport)int _cdecl
16317: 	CreateGifFromEq ( char *expression, char *gifFileName );
16318: #endif
16319: /* --- entry point --- */
16320: int	CreateGifFromEq ( char *expression, char *gifFileName )
16321: {
16322: /* -------------------------------------------------------------------------
16323: Allocations and Declarations
16324: -------------------------------------------------------------------------- */
16325: int	main();			/* main() akways returns an int */
16326: /* --- set constants --- */
16327: int	argc = 4;		/* count of args supplied to main() */
16328: char	*argv[5] =		/* command line args to run with -e option */
16329: 	  { "MimeTeXWin32DLL", "-e", /* constant args */
16330: 	    /*gifFileName, expression,*/ NULL, NULL, NULL };
16331: /* --- set argv[]'s not computable at load time --- */
16332: argv[2] = gifFileName;		/* args are -e gifFileName */
16333: argv[3] = expression;		/* and now  -e gifFileName expression */
16334: /* -------------------------------------------------------------------------
16335: Run mimeTeX in command-line mode with -e (export) option, and then return
16336: -------------------------------------------------------------------------- */
16337: return	main ( argc, argv
16338: 	  #ifdef DUMPENVP
16339: 	    , NULL
16340: 	  #endif
16341: 	) ;
16342: } /* --- end-of-function CreateGifFromEq() --- */
16343: 
16344: 
16345: /* ==========================================================================
16346:  * Function:	ismonth ( char *month )
16347:  * Purpose:	returns 1 if month contains current month "jan"..."dec".
16348:  * --------------------------------------------------------------------------
16349:  * Arguments:	month (I)	char * containing null-terminated string
16350:  *				in which "jan"..."dec" is (putatively)
16351:  *				contained as a substring.
16352:  * --------------------------------------------------------------------------
16353:  * Returns:	( int )		1 if month contains current month,
16354:  *				0 otherwise
16355:  * --------------------------------------------------------------------------
16356:  * Notes:     o	There's a three day "grace period", e.g., Dec 3 mtaches Nov.
16357:  * ======================================================================= */
16358: /* --- entry point --- */
16359: int	ismonth ( char *month )
16360: {
16361: /* -------------------------------------------------------------------------
16362: Allocations and Declarations
16363: -------------------------------------------------------------------------- */
16364: int	isokay = 0;			/*1 if month contains current month*/
16365: /*long	time_val = 0L;*/		/* binary value returned by time() */
16366: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
16367: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
16368: int	imonth, mday;			/* current month 1-12 and day 1-31 */
16369: int	ngrace = 3;			/* grace period */
16370: char	lcmonth[128]="\000"; int i=0;	/* lowercase month */
16371: static	char *months[] =		/* month must contain current one */
16372:    {"dec","jan","feb","mar","apr","may","jun",
16373:     "jul","aug","sep","oct","nov","dec","jan"};
16374: /* -------------------------------------------------------------------------
16375: get current date:time info, and check month
16376: -------------------------------------------------------------------------- */
16377: /* --- lowercase input month --- */
16378: if ( month != NULL )			/* check that we got input */
16379:   for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
16380:     lcmonth[i] = tolower(*month);	/* lowerase each char in month */
16381: if ( i < 2 ) goto end_of_job;		/* must be invalid input */
16382: lcmonth[i] = '\000';			/* null-terminate lcmonth[] */
16383: /* --- get current date:time --- */
16384: time((time_t *)(&time_val));		/* get date and time */
16385: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
16386: /* --- month and day  --- */
16387: imonth = 1 + (int)(tmstruct->tm_mon);	/* 1=jan ... 12=dec */
16388: mday = (int)(tmstruct->tm_mday);	/* 1-31 */
16389: if ( imonth<1 || imonth>12		/* quit if month out-of-range */
16390: ||   mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
16391: /* --- check input month against current date --- */
16392: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
16393: if ( mday <= ngrace )			/* 1-3 within grace period */
16394:  if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
16395: if ( mday >= 31-ngrace )		/* 28-31 within grace period */
16396:  if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
16397: end_of_job:
16398:   return ( isokay );			/*1 if month contains current month*/
16399: } /* --- end-of-function ismonth() --- */
16400: 
16401: 
16402: /* ==========================================================================
16403:  * Function:	logger ( fp, msglevel, message, logvars )
16404:  * Purpose:	Logs the environment variables specified in logvars
16405:  *		to fp if their msglevel is >= the passed msglevel.
16406:  * --------------------------------------------------------------------------
16407:  * Arguments:	fp (I)		FILE * to file containing log
16408:  *		msglevel (I)	int containing logging message level
16409:  *		message (I)	char * to optional message, or NULL
16410:  *		logvars (I)	logdata * to array of environment variables
16411:  *				to be logged
16412:  * --------------------------------------------------------------------------
16413:  * Returns:	( int )		number of variables from logvars
16414:  *				that were actually logged
16415:  * --------------------------------------------------------------------------
16416:  * Notes:     o
16417:  * ======================================================================= */
16418: /* --- entry point --- */
16419: int	logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
16420: {
16421: /* -------------------------------------------------------------------------
16422: Allocations and Declarations
16423: -------------------------------------------------------------------------- */
16424: int	ilog=0, nlogged=0;		/* logvars[] index, #vars logged */
16425: char	*timestamp();			/* timestamp logged */
16426: char	*value = NULL;			/* getenv(name) to be logged */
16427: /* -------------------------------------------------------------------------
16428: Log each variable
16429: -------------------------------------------------------------------------- */
16430: fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
16431: if ( message != NULL )			/* optional message supplied */
16432:  fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */
16433: if ( logvars != (logdata *)NULL )	/* have logvars */
16434:  for ( ilog=0; logvars[ilog].name != NULL; ilog++ )  /* till end-of-table */
16435:   if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
16436:    if ( (value=getenv(logvars[ilog].name))  /* getenv(name) to be logged */
16437:    != NULL )				/* check that name exists */
16438:     {
16439:     fprintf(fp,"  %s = %.*s\n",		/* emit variable name = value */
16440:      logvars[ilog].name,logvars[ilog].maxlen,value);
16441:     nlogged++;				/* bump #vars logged */
16442:     } /* --- end-of-for(ilog) --- */
16443: return ( nlogged );			/* back to caller */
16444: } /* --- end-of-function logger() --- */
16445: 
16446: 
16447: /* ==========================================================================
16448:  * Function:	emitcache ( cachefile, maxage, valign, isbuffer )
16449:  * Purpose:	dumps bytes from cachefile to stdout
16450:  * --------------------------------------------------------------------------
16451:  * Arguments:	cachefile (I)	pointer to null-terminated char string
16452:  *				containing full path to file to be dumped,
16453:  *				or contains buffer of bytes to be dumped
16454:  *		maxage (I)	int containing maxage, in seconds, for
16455:  *				http header, or -1 to not emit headers
16456:  *		valign (I)	int containing Vertical-Align:, in pixels,
16457:  *				for http header, or <= -999 to not emit
16458:  *		isbuffer (I)	1 if cachefile is buffer of bytes to be
16459:  *				dumped
16460:  * --------------------------------------------------------------------------
16461:  * Returns:	( int )		#bytes dumped (0 signals error)
16462:  * --------------------------------------------------------------------------
16463:  * Notes:     o
16464:  * ======================================================================= */
16465: /* --- entry point --- */
16466: int	emitcache ( char *cachefile, int maxage, int valign, int isbuffer )
16467: {
16468: /* -------------------------------------------------------------------------
16469: Allocations and Declarations
16470: -------------------------------------------------------------------------- */
16471: int	nbytes=gifSize, readcachefile(); /* read cache file */
16472: FILE	*emitptr = stdout;		/* emit cachefile to stdout */
16473: unsigned char buffer[MAXGIFSZ+1];	/* bytes from cachefile */
16474: unsigned char *buffptr = buffer;	/* ptr to buffer */
16475: int	isvalign = (abs(valign)<999?1:0); /* true to emit Vertical-Align: */
16476: int	iscontenttypecached = iscachecontenttype; /*true if headers cached*/
16477: /* -------------------------------------------------------------------------
16478: initialization
16479: -------------------------------------------------------------------------- */
16480: /* --- check that files opened okay --- */
16481: if ( emitptr == (FILE *)NULL )		/* failed to open emit file */
16482:   goto end_of_job;			/* so return 0 bytes to caller */
16483: /* --- read the file if necessary --- */
16484: if ( isbuffer ) {			/* cachefile is buffer */
16485:   buffptr = (unsigned char *)cachefile;	/* so reset buffer pointer */
16486:   iscontenttypecached = 0; }		/* and iscontenttypecached flag */
16487: else {					/* cachefile is file name */
16488:   if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
16489:   < 1 ) goto end_of_job; }		/* quit if file not read */
16490: /* --- first emit http headers if requested --- */
16491: if ( isemitcontenttype			/* content-type lines enabled */
16492: &&   !iscontenttypecached		/* and not in cached image */
16493: &&   maxage >= 0 )			/* caller wants http headers */
16494:  { /* --- emit mime content-type line --- */
16495:    fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
16496:    fprintf( emitptr, "Content-Length: %d\n",nbytes );
16497:    if ( isvalign )			/* Vertical-Align: header wanted */
16498:      fprintf( emitptr, "Vertical-Align: %d\n",valign );
16499:    fprintf( emitptr, "Content-type: image/gif\n\n" ); }
16500: /* -------------------------------------------------------------------------
16501: set stdout to binary mode (for Windows)
16502: -------------------------------------------------------------------------- */
16503: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */
16504: #ifdef WINDOWS				/* so instead... */
16505:   #ifdef HAVE_SETMODE			/* prefer (non-portable) setmode() */
16506:     if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
16507:     == -1 ) ; /* handle error */	/* sets stdout to binary mode */
16508:   #else					/* setmode() not available */
16509:     #if 1
16510:       freopen ("CON", "wb", stdout);	/* freopen() stdout binary */
16511:     #else
16512:       stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
16513:     #endif
16514:   #endif
16515: #endif
16516: /* -------------------------------------------------------------------------
16517: emit bytes from cachefile
16518: -------------------------------------------------------------------------- */
16519: /* --- write bytes to stdout --- */
16520: if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
16521: <    nbytes )				/* failed to write all bytes */
16522:   nbytes = 0;				/* reset total count to 0 */
16523: end_of_job:
16524:   return ( nbytes );			/* back with #bytes emitted */
16525: } /* --- end-of-function emitcache() --- */
16526: 
16527: 
16528: /* ==========================================================================
16529:  * Function:	readcachefile ( cachefile, buffer )
16530:  * Purpose:	read cachefile into buffer
16531:  * --------------------------------------------------------------------------
16532:  * Arguments:	cachefile (I)	pointer to null-terminated char string
16533:  *				containing full path to file to be read
16534:  *		buffer (O)	pointer to unsigned char string
16535:  *				returning contents of cachefile
16536:  *				(max 64000 bytes)
16537:  * --------------------------------------------------------------------------
16538:  * Returns:	( int )		#bytes read (0 signals error)
16539:  * --------------------------------------------------------------------------
16540:  * Notes:     o
16541:  * ======================================================================= */
16542: /* --- entry point --- */
16543: int	readcachefile ( char *cachefile, unsigned char *buffer )
16544: {
16545: /* -------------------------------------------------------------------------
16546: Allocations and Declarations
16547: -------------------------------------------------------------------------- */
16548: FILE	*cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
16549: unsigned char cachebuff[64];		/* bytes from cachefile */
16550: int	buflen = 32,			/* #bytes we try to read from file */
16551: 	nread = 0,			/* #bytes actually read from file */
16552: 	maxbytes = MAXGIFSZ,		/* max #bytes returned in buffer */
16553: 	nbytes = 0;			/* total #bytes read */
16554: /* -------------------------------------------------------------------------
16555: initialization
16556: -------------------------------------------------------------------------- */
16557: /* --- check that files opened okay --- */
16558: if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
16559: /* --- check that output buffer provided --- */
16560: if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
16561: /* -------------------------------------------------------------------------
16562: read bytes from cachefile
16563: -------------------------------------------------------------------------- */
16564: while ( 1 )
16565:   {
16566:   /* --- read bytes from cachefile --- */
16567:   nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
16568:   if ( nbytes + nread > maxbytes )	/* block too big for buffer */
16569:     nread = maxbytes - nbytes;		/* so truncate it */
16570:   if ( nread < 1 ) break;		/* no bytes left in cachefile */
16571:   /* --- store bytes in buffer --- */
16572:   memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
16573:   /* --- ready to read next block --- */
16574:   nbytes += nread;			/* bump total #bytes emitted */
16575:   if ( nread < buflen ) break;		/* no bytes left in cachefile */
16576:   if ( nbytes >= maxbytes ) break;	/* avoid buffer overflow */
16577:   } /* --- end-of-while(1) --- */
16578: end_of_job:
16579:   if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
16580:   return ( nbytes );			/* back with #bytes emitted */
16581: } /* --- end-of-function readcachefile() --- */
16582: 
16583: 
16584: /* ==========================================================================
16585:  * Function:	advertisement ( expression, message )
16586:  * Purpose:	wrap expression in advertisement message
16587:  * --------------------------------------------------------------------------
16588:  * Arguments:	expression (I/O) pointer to null-terminated char string
16589:  *				containing expression to be "wrapped",
16590:  *				and returning wrapped expression
16591:  *		message (I)	pointer to null-terminated char string
16592:  *				containing template for advertisement
16593:  *				message, or NULL to use default message
16594:  * --------------------------------------------------------------------------
16595:  * Returns:	( int )		1 if successful, 0=error
16596:  * --------------------------------------------------------------------------
16597:  * Notes:     o
16598:  * ======================================================================= */
16599: /* --- entry point --- */
16600: int	advertisement ( char *expression, char *message )
16601: {
16602: /* -------------------------------------------------------------------------
16603: Allocations and Declarations
16604: -------------------------------------------------------------------------- */
16605: /* --- advertisement template --- */
16606: char  *adtemplate =
16607: 	#if defined(ADVERTISEMENT)	/* cc -DADVERTISEMENT=\"filename\" */
16608: 	  #include ADVERTISEMENT	/* filename with advertisement */
16609: 	#else				/* formatted as illustrated below */
16610: 	"\\begin{gather} {\\small\\text \\fbox{\\begin{gather}"
16611: 	"mime\\TeX rendering courtesy of\\\\"
16612: 	"\\homepagetext \\end{gather}}}\\\\"
16613: 	" %%beginmath%% %%expression%% %%endmath%% \\end{gather}"
16614: 	#endif
16615: 	;				/* terminating semicolon */
16616: /* --- other variables --- */
16617: char	adbuffer[MAXEXPRSZ+2048];	/*construct wrapped expression here*/
16618: char	*beginmath = " ",		/* start math mode */
16619: 	*endmath =   " ";		/* end math mode */
16620: int	strreplace();			/* replace %%keywords%% with values*/
16621: /* -------------------------------------------------------------------------
16622: wrap expression in advertisement
16623: -------------------------------------------------------------------------- */
16624: /* --- start with template --- */
16625: if ( isempty(message) )			/* caller didn't supply message */
16626:   message = adtemplate;			/* so use default message */
16627: strcpy(adbuffer,message);		/* copy message template to buffer */
16628: /* --- replace %%beginmath%%...%%endmath%% --- */
16629:   strreplace(adbuffer,"%%beginmath%%",beginmath,0);
16630:   strreplace(adbuffer,"%%endmath%%",endmath,0);
16631: /* --- replace %%expression%% in template with expression --- */
16632:   strreplace(adbuffer,"%%expression%%",expression,0);
16633: /* --- replace original expression --- */
16634: strcpy(expression,adbuffer);		/* expression now wrapped in ad */
16635: return ( 1 );				/* always just return 1 */
16636: } /* --- end-of-function advertisement() --- */
16637: 
16638: 
16639: /* ==========================================================================
16640:  * Function:	crc16 ( s )
16641:  * Purpose:	16-bit crc of string s
16642:  * --------------------------------------------------------------------------
16643:  * Arguments:	s (I)		pointer to null-terminated char string
16644:  *				whose crc is desired
16645:  * --------------------------------------------------------------------------
16646:  * Returns:	( int )		16-bit crc of s
16647:  * --------------------------------------------------------------------------
16648:  * Notes:     o	From Numerical Recipes in C, 2nd ed, page 900.
16649:  * ======================================================================= */
16650: /* --- entry point --- */
16651: int	crc16 ( char *s )
16652: {
16653: /* -------------------------------------------------------------------------
16654: Compute the crc
16655: -------------------------------------------------------------------------- */
16656: unsigned short crc = 0;			/* returned crc */
16657: int	ibit;				/* for(ibit) eight one-bit shifts */
16658: while ( !isempty(s) ) {			/* while there are still more chars*/
16659:   crc = (crc ^ (*s)<<8);		/* add next char */
16660:   for ( ibit=0; ibit<8; ibit++ )	/* generator polynomial */
16661:     if ( crc & 0x8000 ) { crc<<=1; crc=crc^4129; }
16662:     else crc <<= 1;
16663:   s++;					/* next xhar */
16664:   } /* --- end-of-while(!isempty(s)) --- */
16665: return ( (int)crc );			/* back to caller with crc */
16666: } /* --- end-of-function crc16() --- */
16667: 
16668: 
16669: /* ==========================================================================
16670:  * Function:	md5str ( instr )
16671:  * Purpose:	returns null-terminated character string containing
16672:  *		md5 hash of instr (input string)
16673:  * --------------------------------------------------------------------------
16674:  * Arguments:	instr (I)	pointer to null-terminated char string
16675:  *				containing input string whose md5 hash
16676:  *				is desired
16677:  * --------------------------------------------------------------------------
16678:  * Returns:	( char * )	ptr to null-terminated 32-character
16679:  *				md5 hash of instr
16680:  * --------------------------------------------------------------------------
16681:  * Notes:     o	Other md5 library functions are included below.
16682:  *		They're all taken from Christophe Devine's code,
16683:  *		which (as of 04-Aug-2004) is available from
16684:  *		     http://www.cr0.net:8040/code/crypto/md5/
16685:  *	      o	The P,F,S macros in the original code are replaced
16686:  *		by four functions P1()...P4() to accommodate a problem
16687:  *		with Compaq's vax/vms C compiler.
16688:  * ======================================================================= */
16689: /* --- #include "md5.h" --- */
16690: #ifndef uint8
16691:   #define uint8  unsigned char
16692: #endif
16693: #ifndef uint32
16694:   #define uint32 unsigned long int
16695: #endif
16696: typedef struct
16697:   { uint32 total[2];
16698:     uint32 state[4];
16699:     uint8 buffer[64];
16700:   } md5_context;
16701: void md5_starts( md5_context *ctx );
16702: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
16703: void md5_finish( md5_context *ctx, uint8 digest[16] );
16704: /* --- md5.h --- */
16705: #define GET_UINT32(n,b,i)                       \
16706:   { (n) = ( (uint32) (b)[(i)    ]       )       \
16707:         | ( (uint32) (b)[(i) + 1] <<  8 )       \
16708:         | ( (uint32) (b)[(i) + 2] << 16 )       \
16709:         | ( (uint32) (b)[(i) + 3] << 24 ); }
16710: #define PUT_UINT32(n,b,i)                       \
16711:   { (b)[(i)    ] = (uint8) ( (n)       );       \
16712:     (b)[(i) + 1] = (uint8) ( (n) >>  8 );       \
16713:     (b)[(i) + 2] = (uint8) ( (n) >> 16 );       \
16714:     (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
16715: /* --- P,S,F macros defined as functions --- */
16716: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16717:   { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
16718:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16719:     return; }
16720: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16721:   { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
16722:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16723:     return; }
16724: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16725:   { *a += (uint32)(b ^ c ^ d) + X[k] + t;
16726:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16727:     return; }
16728: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16729:   { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
16730:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16731:     return; }
16732: 
16733: /* --- entry point (this one little stub written by me)--- */
16734: char *md5str( char *instr )
16735:   { static char outstr[64];
16736:     unsigned char md5sum[16];
16737:     md5_context ctx;
16738:     int j;
16739:     md5_starts( &ctx );
16740:     md5_update( &ctx, (uint8 *)instr, strlen(instr) );
16741:     md5_finish( &ctx, md5sum );
16742:     for( j=0; j<16; j++ )
16743:       sprintf( outstr + j*2, "%02x", md5sum[j] );
16744:     outstr[32] = '\000';
16745:     return ( outstr ); }
16746: 
16747: /* --- entry point (all md5 functions below by Christophe Devine) --- */
16748: void md5_starts( md5_context *ctx )
16749:   { ctx->total[0] = 0;
16750:     ctx->total[1] = 0;
16751:     ctx->state[0] = 0x67452301;
16752:     ctx->state[1] = 0xEFCDAB89;
16753:     ctx->state[2] = 0x98BADCFE;
16754:     ctx->state[3] = 0x10325476; }
16755: 
16756: void md5_process( md5_context *ctx, uint8 data[64] )
16757:   { uint32 X[16], A, B, C, D;
16758:     GET_UINT32( X[0],  data,  0 );
16759:     GET_UINT32( X[1],  data,  4 );
16760:     GET_UINT32( X[2],  data,  8 );
16761:     GET_UINT32( X[3],  data, 12 );
16762:     GET_UINT32( X[4],  data, 16 );
16763:     GET_UINT32( X[5],  data, 20 );
16764:     GET_UINT32( X[6],  data, 24 );
16765:     GET_UINT32( X[7],  data, 28 );
16766:     GET_UINT32( X[8],  data, 32 );
16767:     GET_UINT32( X[9],  data, 36 );
16768:     GET_UINT32( X[10], data, 40 );
16769:     GET_UINT32( X[11], data, 44 );
16770:     GET_UINT32( X[12], data, 48 );
16771:     GET_UINT32( X[13], data, 52 );
16772:     GET_UINT32( X[14], data, 56 );
16773:     GET_UINT32( X[15], data, 60 );
16774:     A = ctx->state[0];
16775:     B = ctx->state[1];
16776:     C = ctx->state[2];
16777:     D = ctx->state[3];
16778:     P1( X, &A, B, C, D,  0,  7, 0xD76AA478 );
16779:     P1( X, &D, A, B, C,  1, 12, 0xE8C7B756 );
16780:     P1( X, &C, D, A, B,  2, 17, 0x242070DB );
16781:     P1( X, &B, C, D, A,  3, 22, 0xC1BDCEEE );
16782:     P1( X, &A, B, C, D,  4,  7, 0xF57C0FAF );
16783:     P1( X, &D, A, B, C,  5, 12, 0x4787C62A );
16784:     P1( X, &C, D, A, B,  6, 17, 0xA8304613 );
16785:     P1( X, &B, C, D, A,  7, 22, 0xFD469501 );
16786:     P1( X, &A, B, C, D,  8,  7, 0x698098D8 );
16787:     P1( X, &D, A, B, C,  9, 12, 0x8B44F7AF );
16788:     P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
16789:     P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
16790:     P1( X, &A, B, C, D, 12,  7, 0x6B901122 );
16791:     P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
16792:     P1( X, &C, D, A, B, 14, 17, 0xA679438E );
16793:     P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
16794:     P2( X, &A, B, C, D,  1,  5, 0xF61E2562 );
16795:     P2( X, &D, A, B, C,  6,  9, 0xC040B340 );
16796:     P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
16797:     P2( X, &B, C, D, A,  0, 20, 0xE9B6C7AA );
16798:     P2( X, &A, B, C, D,  5,  5, 0xD62F105D );
16799:     P2( X, &D, A, B, C, 10,  9, 0x02441453 );
16800:     P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
16801:     P2( X, &B, C, D, A,  4, 20, 0xE7D3FBC8 );
16802:     P2( X, &A, B, C, D,  9,  5, 0x21E1CDE6 );
16803:     P2( X, &D, A, B, C, 14,  9, 0xC33707D6 );
16804:     P2( X, &C, D, A, B,  3, 14, 0xF4D50D87 );
16805:     P2( X, &B, C, D, A,  8, 20, 0x455A14ED );
16806:     P2( X, &A, B, C, D, 13,  5, 0xA9E3E905 );
16807:     P2( X, &D, A, B, C,  2,  9, 0xFCEFA3F8 );
16808:     P2( X, &C, D, A, B,  7, 14, 0x676F02D9 );
16809:     P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
16810:     P3( X, &A, B, C, D,  5,  4, 0xFFFA3942 );
16811:     P3( X, &D, A, B, C,  8, 11, 0x8771F681 );
16812:     P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
16813:     P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
16814:     P3( X, &A, B, C, D,  1,  4, 0xA4BEEA44 );
16815:     P3( X, &D, A, B, C,  4, 11, 0x4BDECFA9 );
16816:     P3( X, &C, D, A, B,  7, 16, 0xF6BB4B60 );
16817:     P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
16818:     P3( X, &A, B, C, D, 13,  4, 0x289B7EC6 );
16819:     P3( X, &D, A, B, C,  0, 11, 0xEAA127FA );
16820:     P3( X, &C, D, A, B,  3, 16, 0xD4EF3085 );
16821:     P3( X, &B, C, D, A,  6, 23, 0x04881D05 );
16822:     P3( X, &A, B, C, D,  9,  4, 0xD9D4D039 );
16823:     P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
16824:     P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
16825:     P3( X, &B, C, D, A,  2, 23, 0xC4AC5665 );
16826:     P4( X, &A, B, C, D,  0,  6, 0xF4292244 );
16827:     P4( X, &D, A, B, C,  7, 10, 0x432AFF97 );
16828:     P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
16829:     P4( X, &B, C, D, A,  5, 21, 0xFC93A039 );
16830:     P4( X, &A, B, C, D, 12,  6, 0x655B59C3 );
16831:     P4( X, &D, A, B, C,  3, 10, 0x8F0CCC92 );
16832:     P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
16833:     P4( X, &B, C, D, A,  1, 21, 0x85845DD1 );
16834:     P4( X, &A, B, C, D,  8,  6, 0x6FA87E4F );
16835:     P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
16836:     P4( X, &C, D, A, B,  6, 15, 0xA3014314 );
16837:     P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
16838:     P4( X, &A, B, C, D,  4,  6, 0xF7537E82 );
16839:     P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
16840:     P4( X, &C, D, A, B,  2, 15, 0x2AD7D2BB );
16841:     P4( X, &B, C, D, A,  9, 21, 0xEB86D391 );
16842:     ctx->state[0] += A;
16843:     ctx->state[1] += B;
16844:     ctx->state[2] += C;
16845:     ctx->state[3] += D; }
16846: 
16847: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
16848:   { uint32 left, fill;
16849:     if( length < 1 ) return;
16850:     left = ctx->total[0] & 0x3F;
16851:     fill = 64 - left;
16852:     ctx->total[0] += length;
16853:     ctx->total[0] &= 0xFFFFFFFF;
16854:     if( ctx->total[0] < length )
16855:         ctx->total[1]++;
16856:     if( left && length >= fill )
16857:       { memcpy( (void *) (ctx->buffer + left),
16858:                 (void *) input, fill );
16859:         md5_process( ctx, ctx->buffer );
16860:         length -= fill;
16861:         input  += fill;
16862:         left = 0; }
16863:     while( length >= 64 )
16864:       { md5_process( ctx, input );
16865:         length -= 64;
16866:         input  += 64; }
16867:     if( length >= 1 )
16868:       memcpy( (void *) (ctx->buffer + left),
16869:               (void *) input, length ); }
16870: 
16871: void md5_finish( md5_context *ctx, uint8 digest[16] )
16872:   { static uint8 md5_padding[64] =
16873:      { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16874:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16875:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16876:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
16877:     uint32 last, padn;
16878:     uint32 high, low;
16879:     uint8 msglen[8];
16880:     high = ( ctx->total[0] >> 29 )
16881:          | ( ctx->total[1] <<  3 );
16882:     low  = ( ctx->total[0] <<  3 );
16883:     PUT_UINT32( low,  msglen, 0 );
16884:     PUT_UINT32( high, msglen, 4 );
16885:     last = ctx->total[0] & 0x3F;
16886:     padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
16887:     md5_update( ctx, md5_padding, padn );
16888:     md5_update( ctx, msglen, 8 );
16889:     PUT_UINT32( ctx->state[0], digest,  0 );
16890:     PUT_UINT32( ctx->state[1], digest,  4 );
16891:     PUT_UINT32( ctx->state[2], digest,  8 );
16892:     PUT_UINT32( ctx->state[3], digest, 12 ); }
16893: /* --- end-of-function md5str() and "friends" --- */
16894: 
16895: 
16896: #if defined(GIF)
16897: /* ==========================================================================
16898:  * Function:	GetPixel ( int x, int y )
16899:  * Purpose:	callback for GIF_CompressImage() returning the
16900:  *		pixel at column x, row y
16901:  * --------------------------------------------------------------------------
16902:  * Arguments:	x (I)		int containing column=0...width-1
16903:  *				of desired pixel
16904:  *		y (I)		int containing row=0...height-1
16905:  *				of desired pixel
16906:  * --------------------------------------------------------------------------
16907:  * Returns:	( int )		0 or 1, if pixel at x,y is off or on
16908:  * --------------------------------------------------------------------------
16909:  * Notes:     o
16910:  * ======================================================================= */
16911: /* --- entry point --- */
16912: int	GetPixel ( int x, int y )
16913: {
16914: int	ipixel = y*raster_width + x;	/* pixel index for x,y-coords*/
16915: int	pixval =0;			/* value of pixel */
16916: if ( !isaa )				/* use bitmap if not anti-aliased */
16917:   pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
16918: else					/* else use anti-aliased grayscale*/
16919:   pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
16920: if ( msgfp!=NULL && msglevel>=9999 )	/* dump pixel */
16921:   { fprintf(msgfp,"GetPixel> x=%d, y=%d  pixel=%d\n",x,y,pixval);
16922:     fflush(msgfp); }
16923: return pixval;
16924: } /* --- end-of-function GetPixel() --- */
16925: #endif /* gif */
16926: #endif /* driver */
16927: #endif /* PART1 */
16928: /* ======================= END-OF-FILE MIMETEX.C ========================= */
16929: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>