/**************************************************************************** * * Copyright(c) 2002-2004, John Forkosh Associates, Inc. All rights reserved. * -------------------------------------------------------------------------- * This file is part of mimeTeX, which is free software. You may redistribute * and/or modify it under the terms of the GNU General Public License, * version 2 or later, as published by the Free Software Foundation. * MimeTeX is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY. * See the GNU General Public License for specific details. * By using mimeTeX, you warrant that you have read, understood and * agreed to these terms and conditions, and that you are at least 18 years * of age and possess the legal right and ability to enter into this * agreement and to use mimeTeX in accordance with it. * Your mimeTeX distribution should contain a copy of the GNU General * Public License. If not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, * or point your browser to http://www.gnu.org/licenses/gpl.html * -------------------------------------------------------------------------- * * Purpose: o Parses a LaTeX math expression and emits a gif image or mime * xbitmap of it, rather than the usual TeX dvi. MimeTeX images * can therefore be directly inserted into html documents using * the standard html tag, e.g., * * without intermediate dvi-to-gif conversion, and without * storing lots of resulting little gif image files, one file * for each converted expression. This makes your site and * html documents more readable and easily maintained. * o MimeTeX isn't primarily meant for latex2html-like tasks * where you're maintaining native (La)TeX documents that are * later redistributed in several formats, including html. * Rather, mimeTeX is primarily meant to help maintain native * html documents containing math. In this sense it's a kind * of "lightweight" alternative to MathML, with the advantage * that mimeTeX preserves LaTeX syntax, and works with any * browser and server. * * Functions: ===================== Raster Functions ====================== * --- raster constructor functions --- * new_raster(width,height,pixsz) allocation (and constructor) * new_subraster(width,height,pixsz)allocation (and constructor) * new_chardef() allocate chardef struct * delete_raster(rp) deallocate raster (rp = raster ptr) * delete_subraster(sp) deallocate subraster (sp=subraster ptr) * delete_chardef(cp) deallocate chardef (cp = chardef ptr) * --- primitive (sub)raster functions --- * rastcpy(rp) allocate new copy of rp * subrastcpy(sp) allocate new copy of sp * rastrot(rp) new raster rotated right 90 degrees to rp * rastput(target,source,top,left,isopaque) overlay src on trgt * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1 * rastcat(sp1,sp2,isfree) concatanate sp1||sp2 * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1 * rastile(tiles,ntiles) create composite raster from tiles * --- raster "drawing" functions --- * accent_subraster(accent,width,height) draw \hat\vec\etc * arrow_subraster(width,height,drctn,isBig) left/right arrow * uparrow_subraster(width,height,drctn,isBig) up/down arrow * rule_raster(rp,top,left,width,height,type) draw rule in rp * line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp * line_recurse(rp,row0,col0,row1,col1,thickness) recurse line * circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse * bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively * border_raster(rp,ntop,nbot,isline,isfree)put border around rp * --- raster (and chardef) output functions --- * type_raster(rp,fp) emit ascii dump of rp on file ptr fp * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp * xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp * cstruct_chardef(cp,fp,col1) emit C struct of cp on fp * cstruct_raster(rp,fp,col1) emit C struct of rp on fp * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp * --- ancillary output functions --- * emit_string(fp,col1,string,comment) emit string and C comment * ====================== Font Functions ======================= * --- font lookup functions --- * get_symdef(symbol) returns mathchardef for symbol * get_chardef(symdef,size) returns chardef for symdef,size * get_charsubraster(symdef,size) wrap subraster around chardef * --- ancillary font functions --- * get_baseline(gfdata) determine baseline (in our coords) * get_delim(symbol,height,family) delim just larger than height * make_delim(symbol,height) construct delim exactly height size * ================= Tokenize/Parse Functions ================== * texchar(expression,chartoken) retruns next char or \sequence * texsubexpr(expression,subexpr,left,right,isescape,isdelim) * texscripts(expression,subscript,superscript,which)get scripts * --- ancillary parse functions --- * isbrace(expression,braces,isescape) check for leading brace * preamble(expression,size,subexpr) parse preamble * mimeprep(expression) preprocessor converts \left( to \(, etc. * strchange(nfirst,from,to) change nfirst chars of from to to * findbraces(expression,command) find opening { or closing } * =========== Rasterize an Expression (recursively) =========== * --- here's the primary entry point for all of mimeTeX --- * rasterize(expression,size) parse and rasterize expression * --- explicitly called handlers that rasterize... --- * rastparen(subexpr,size,basesp) parenthesized subexpr * rastlimits(expression,size,basesp) dispatch super/sub call * rastscripts(expression,size,basesp) super/subscripted exprssn * rastdispmath(expression,size,sp) scripts for displaymath * --- table-driven handlers that rasterize... --- * rastflags(expression,size,basesp,flag,value,arg3) set flag * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\; * rastnewline(expression,size,basesp,arg1,arg2,arg3) \\ * rastarrow(expression,size,basesp,width,height,drctn) \longarr * rastuparrow(expression,size,basesp,width,height,drctn)up/down * rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not * rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop * rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel * rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc * rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt * rastaccent(expression,size,basesp,accent,isabove,isscript) * rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{} * rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{} * rastarray(expression,size,basesp,arg1,arg2,arg3) \array * rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture * rastline(expression,size,basesp,arg1,arg2,arg3) \line * rastcircle(expression,size,basesp,arg1,arg2,arg3) \circle * rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier * rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox * rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox * rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape * ================ Anti-alias completed raster ================ * aalowpass(rp,bytemap,grayscale) make grayscale bytemap * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap * --- the following two functions tested but not used yet --- * aaweights(width,height) builds "canonical" weight matrix * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel * ========================== Driver =========================== * main(argc,argv) parses math expression and emits mime xbitmap * isstrstr(string,snippets,iscase) are any snippets in string? * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded * logger(fp,msglevel,logvars) logs environment variables * timestamp( ) formats timestamp string (used by logger) * GetPixel(x,y) callback function for gifsave library * * Source: mimetex.c (needs mimetex.h and texfonts.h to compile, * and also needs gifsave.c if compiled with -DGIF switch) * * -------------------------------------------------------------------------- * Notes o See bottom of file for main() driver, and compile as * cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi * to produce an executable that emits gif images with * anti-aliasing (see Notes below). You may also compile * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi * to produce an executable that emits gif images without * anti-aliasing. Alternatively, compile mimeTeX as * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi * to produce an executable that just emits mime xbitmaps. * In either case you'll need mimetex.h and texfonts.h, * and with -DGIF you'll also need gifsave.c * o For gif images, the gifsave.c library by Sverre H. Huseby * , slightly modified by me to allow * (a)sending output to stdout and (b)specifying a transparent * background color index, is included with mimeTeX, * and documented in mimetex.html. * o Optional compile-line -D defined variables include... * -DAA * Turns on gif anti-aliasing with default values * (CENTERWT=12, MINADJACENT=4, MAXADJACENT=7) * for anti-aliasing parameters below... * -DCENTERWT=n * At this time, mimeTeX only provides a lowpass filtering * algorithm for anti-aliasing (see the Notes section of * function aalowpass() below), which is applied to the * existing set of fonts, using weights * 1 3 1 * 3 x 3 * 1 3 1 * for adjacent pixels, and weight x=CENTERWT * (which defaults to 10) for the center pixel. * If you modify the weights[] array for adjacent * pixels in aalowpass(), make sure to also modify * neighborwts which must be their sum (16 as shown). * I've modified the "canonical" lowpass algorithm, * so that if the center pixel is black, then the * anti-aliased value remains completely black * regardless of adjacent pixels/weights. * The intent is to prevent thin lines, * typical for text, from getting washed out. * As a result, anti-aliased lines can only become * heavier than they started. A low CENTERWT value * will blur/spread out lines without washing out their * centers. A high CENTERWT value will sharpen lines. * Experimentation is recommended to determine * what value works best for you. * -DMINADJACENT=n * Besides CENTERWT, mimeTeX's lowpass algorithm differs * from the "canonical" one in that the weighted grayscale * value calculated for a pixel is applied to it _only_ * if the summed weights of adjacent black pixels is * greater than or equal to MINADJACENT (default 6); * or, as above, originally black pixels remain black. * That is, an originally white pixel remains white * if the summed weights of adjacent black pixels is * less than MINADJACENT. * -DMAXADJACENT=n * As above, an originally white pixel remains white if * the summed weights of adjacent black pixels is greater * than MAXADJACENT (default 8). Note that default * MINADJACENT=6 and MAXADJACENT=8 apply lowpass * anti-aliasing only to originally white pixels * surrounded by adjacent black pixels whose summed * weights are between 6 and 8, inclusive. * Experimentation with these two parameters is also * recommended. * -NORMALSIZE=n * mimeTeX has six font sizes numbered 0-5, and starts * in NORMALSIZE (default 2). * -DREFERER=\"domain\" -or- * -DREFERER=\"domain1,domain2,etc\" * If REFERER is defined, mimeTeX checks for the environment * variable HTTP_REFERER and, if it exists, performs a * case-insensitive test to make sure it contains 'domain' * as a substring. If given several 'domain's (second form) * then HTTP_REFERER must contain either 'domain1' or * 'domain2', etc, as a (case-insensitive) substring. * If HTTP_REFERER fails to contain a substring matching * any of these domain(s), mimeTeX emits an error message * image corresponding to the expression specified by * the invalid_referer_msg string defined in main(). * Note: if HTTP_REFERER is not an environment variable, * mimeTeX correctly generates the requested expression * (i.e., no referer error). * -DWARNINGS=n -or- * -DNOWARNINGS * If an expression submitted to mimeTeX contains an * unrecognzied escape sequence, e.g., "y=x+\abc+1", then * mimeTeX generates a gif image containing an embedded * warning in the form "y=x+[\abc?]+1". If you want these * warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells * mimeTeX to ignore unrecognized symbols, and the rendered * image is "y=x++1" instead. * o See individual function entry points for further comments. * o The font information in texfonts.h was produced by multiple * runs of gfuntype, one run per struct (i.e., one run per font * family at a particular size). See gfuntype.c, and also * mimetex.html#fonts, for details. * o mimetex.c contains library functions implementing a raster * datatype, functions to manipulate rasterized .mf fonts * (see gfuntype.c which rasterizes .mf fonts), functions * to parse LaTeX expressions, etc. A complete list of * mimetex.c functions is above. See their individual entry * points below for further comments. * All these functions eventually belong in several * different modules, possibly along the lines suggested * by the divisions above. But until the best decomposition * becomes clear, it seems better to keep mimetex.c * neatly together, avoiding a bad decomposition that * becomes permanent by default. * o The "main" reusable function is rasterize(), * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt" * and returns a (sub)raster representing it as a bit or bytemap. * Your application can do anything it likes with this pixel map. * MimeTeX just outputs it, either as a mime xbitmap or as a gif. * -------------------------------------------------------------------------- * Revision History: * 09/18/02 J.Forkosh Installation. * 12/11/02 J.Forkosh Version 1.00 released. * 07/04/03 J.Forkosh Version 1.01 released. * 10/17/03 J.Forkosh Version 1.20 released. * 12/21/03 J.Forkosh Version 1.30 released. * 02/01/04 J.Forkosh Version 1.40 released. * ****************************************************************************/ /* ------------------------------------------------------------------------- header files -------------------------------------------------------------------------- */ /* --- standard headers --- */ #include #include /*#include */ #include #include #include /* --- application headers --- */ #define TEXFONTS /* to include texfonts.h */ #include "mimetex.h" /* ------------------------------------------------------------------------- driver conditionally compiled if output format (xbitmap,gif,png) specified -------------------------------------------------------------------------- */ /* --- explicitly set gif if any gif options specified --- */ #if defined(AA) || defined(CENTERWT) || defined(ADJACENTWT) \ || defined(MINADJACENT) || defined(MAXADJACENT) #ifndef GIF /* gif not explicitly requested */ #define GIF /* so define it ourselves */ #endif #endif /* --- check whether xbitmap (default), gif or png output desired --- */ #if defined(XBITMAP) || defined(GIF) || defined(PNG) #define DRIVER /* driver will be compiled */ /* --- check whether or not to perform http_referer check --- */ #ifndef REFERER /* all http_referer's allowed */ #define REFERER NULL #endif /* --- check whether or not to apply anti-aliasing --- */ #if defined(CENTERWT) || defined(ADJACENTWT) \ || defined(MINADJACENT) || defined(MAXADJACENT) #ifndef AA /* but not explicitly requested */ #define AA /* so define it ourselves */ #endif #endif /* --- resolve inconsistencies --- */ #if defined(XBITMAP) /* xbitmap supercedes gif */ #ifdef GIF #undef GIF #endif #endif #endif /* ------------------------------------------------------------------------- adjustable default values -------------------------------------------------------------------------- */ /* --- anti-aliasing parameters --- */ #ifndef CENTERWT #define CENTERWT 10 /* anti-aliasing centerwt default */ #endif #ifndef ADJACENTWT #define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/ #endif #ifndef MINADJACENT #define MINADJACENT 6 /*anti-aliasing minadjacent default*/ #endif #ifndef MAXADJACENT #define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/ #endif /* --- variables for anti-aliasing parameters --- */ static int centerwt = CENTERWT, /*lowpass matrix center pixel weight*/ adjacentwt = ADJACENTWT, /*lowpass matrix adjacent pixel wt*/ minadjacent = MINADJACENT, /* darken if>=adjacent pts black*/ maxadjacent = MAXADJACENT; /* darken if<=adjacent pts black */ static int weightnum=1, maxaaparams=4; /* font wt, #entries in table */ /* --- parameter values by font weight --- */ static struct { int centerwt; /*lowpass matrix center pixel weight*/ int adjacentwt; /* lowpass matrix adjacent pixel wt*/ int minadjacent; /* darken if >= adjacent pts black */ int maxadjacent; } /* darken if <= adjacent pts black */ aaparams[] = /* set params by weight */ { /* ------------------------------------------------ centerwt adjacentwt minadj. maxadjacent ------------------------------------------------ */ { 64, 3, 6, 8 }, /* 0 = light */ { CENTERWT,ADJACENTWT,MINADJACENT,MAXADJACENT },/* 1 = regular */ { 12, 2, 5, 8 }, /* 2 = semibold */ { 8, 2, 4, 9 } /* 3 = bold */ } ; /* --- end-of-aaparams[] --- */ /* ------------------------------------------------------------------------- other variables -------------------------------------------------------------------------- */ /* --- black on white background (default), or white on black --- */ #ifdef WHITE #define ISBLACKONWHITE 0 /* white on black background */ #else #define ISBLACKONWHITE 1 /* black on white background */ #endif /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */ #ifdef NOARGSIGNAL #define ARGSIGNAL NULL #endif #ifndef ARGSIGNAL #define ARGSIGNAL "++" #endif /* ------------------------------------------------------------------------- debugging and logging / error reporting -------------------------------------------------------------------------- */ /* --- debugging and error reporting --- */ #ifndef MSGLEVEL #define MSGLEVEL 1 #endif #define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */ #define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */ #ifndef FORMLEVEL #define FORMLEVEL MSGLEVEL /*msglevel if called from html form*/ #endif static int msglevel = MSGLEVEL; /* message level for verbose/debug */ static FILE *msgfp; /* output in command-line mode */ /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */ static int warninglevel = /* warning level */ #ifdef WARNINGS WARNINGS ; #else #ifdef NOWARNINGS 0; #else 1; #endif #endif /* ------------------------------------------------------------------------- control flags and values -------------------------------------------------------------------------- */ static int recurlevel = 0, /* inc/decremented in rasterize() */ scriptlevel = 0; /* inc/decremented in rastlimits() */ static int istext = 0, /* text mode when true, italics=2 */ isdisplaymath = 1, /* displaymath mode (forced if 2) */ displaysize = NORMALSIZE; /* current size */ static double unitlength = 1.0; /* #pixels per unit (may be <1.0) */ static int istransparent = 1, /*true to set background transparent*/ fgcolor = (ISBLACKONWHITE?0:255), /* default fg black */ bgcolor = (ISBLACKONWHITE?255:0), /* default bg white */ isblackonwhite=ISBLACKONWHITE; /*1=black on white,0=reverse*/ static int *workingparam = (int *)NULL; /* working parameter */ static subraster *workingbox = (subraster *)NULL; /*working subraster box*/ static int isreplaceleft = 0; /* true to replace leftexpression */ static subraster *leftexpression = (subraster *)NULL; /*rasterized so far*/ static mathchardef *leftsymdef = NULL; /* mathchardef for preceding symbol*/ /* ------------------------------------------------------------------------- miscellaneous macros -------------------------------------------------------------------------- */ #define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */ #define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */ #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */ #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */ #define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */ #define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */ /* ========================================================================== * Function: new_raster ( width, height, pixsz ) * Purpose: Allocation and constructor for raster. * mallocs and initializes memory for width*height pixels, * and returns raster struct ptr to caller. * -------------------------------------------------------------------------- * Arguments: width (I) int containing width, in bits, * of raster pixmap to be allocated * height (I) int containing height, in bits/scans, * of raster pixmap to be allocated * pixsz (I) int containing #bits per pixel, 1 or 8 * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to allocated and initialized * raster struct, or NULL for any error. * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ raster *new_raster ( int width, int height, int pixsz ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ raster *rp = (raster *)NULL; /* raster ptr returned to caller */ pixbyte *pixmap = NULL; /* raster pixel map to be malloced */ int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */ int delete_raster(); /* in case pixmap malloc() fails */ /* ------------------------------------------------------------------------- allocate and initialize raster struct and embedded bitmap -------------------------------------------------------------------------- */ /* --- allocate and initialize raster struct --- */ rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */ if ( rp == (raster *)NULL ) /* malloc failed */ goto end_of_job; /* return error to caller */ rp->width = width; /* store width in raster struct */ rp->height = height; /* and store height */ rp->pixsz = pixsz; /* store #bits per pixel */ rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */ /* --- allocate and initialize bitmap array --- */ if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/ pixmap = (pixbyte *)malloc(nbytes); /*bytes for width*height bits*/ if ( pixmap == (pixbyte *)NULL ) /* malloc failed */ { delete_raster(rp); /* so free everything */ rp = (raster *)NULL; /* reset pointer */ goto end_of_job; } /* and return error to caller */ memset((void *)pixmap,0,nbytes); /* init bytes to binary 0's */ rp->pixmap = pixmap; /* store ptr to malloced memory */ /* ------------------------------------------------------------------------- Back to caller with address of raster struct, or NULL ptr for any error. -------------------------------------------------------------------------- */ end_of_job: return ( rp ); /* back to caller with raster */ } /* --- end-of-function new_raster() --- */ /* ========================================================================== * Function: new_subraster ( width, height, int pixsz ) * Purpose: Allocate a new subraster along with * an embedded raster of width x height. * -------------------------------------------------------------------------- * Arguments: width (I) int containing width of embedded raster * height (I) int containing height of embedded raster * pixsz (I) int containing #bits per pixel, 1 or 8 * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to newly-allocated subraster, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o if width or height <=0, embedded raster not allocated * ======================================================================= */ /* --- entry point --- */ subraster *new_subraster ( int width, int height, int pixsz ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *sp=NULL; /* subraster returned to caller */ raster *new_raster(), *rp=NULL; /* image raster embedded in sp */ int delete_subraster(); /* in case new_raster() fails */ int size = NORMALSIZE, /* default size */ baseline = height-1; /* and baseline */ /* ------------------------------------------------------------------------- allocate and initialize subraster struct -------------------------------------------------------------------------- */ /* --- allocate subraster struct --- */ sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */ if ( sp == (subraster *)NULL ) /* malloc failed */ goto end_of_job; /* return error to caller */ /* --- initialize subraster struct --- */ sp->type = (-1); /* charcter or image raster */ sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */ sp->baseline = baseline; /*0 if image is entirely descending*/ sp->size = size; /* font size 0-4 */ sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */ sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/ /* ------------------------------------------------------------------------- allocate raster and embed it in subraster, and return to caller -------------------------------------------------------------------------- */ /* --- allocate raster struct if desired --- */ if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */ if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */ != NULL ) /* if allocate succeeded */ sp->image = rp; /* embed raster in subraster */ else /* or if allocate failed */ { delete_subraster(sp); /* free non-unneeded subraster */ sp = NULL; } /* signal error */ /* --- back to caller with new subraster or NULL --- */ end_of_job: return ( sp ); } /* --- end-of-function new_subraster() --- */ /* ========================================================================== * Function: new_chardef ( ) * Purpose: Allocates and initializes a chardef struct, * but _not_ the embedded raster struct. * -------------------------------------------------------------------------- * Arguments: none * -------------------------------------------------------------------------- * Returns: ( chardef * ) ptr to allocated and initialized * chardef struct, or NULL for any error. * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ chardef *new_chardef ( ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */ /* ------------------------------------------------------------------------- allocate and initialize chardef struct -------------------------------------------------------------------------- */ cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */ if ( cp == (chardef *)NULL ) /* malloc failed */ goto end_of_job; /* return error to caller */ cp->charnum = cp->location = 0; /* init character description */ cp->toprow = cp->topleftcol = 0; /* init upper-left corner */ cp->botrow = cp->botleftcol = 0; /* init lower-left corner */ cp->image.width = cp->image.height = 0; /* init raster dimensions */ cp->image.pixsz = 0; /* and #bits per pixel */ cp->image.pixmap = NULL; /* init raster pixmap as null */ /* ------------------------------------------------------------------------- Back to caller with address of chardef struct, or NULL ptr for any error. -------------------------------------------------------------------------- */ end_of_job: return ( cp ); } /* --- end-of-function new_chardef() --- */ /* ========================================================================== * Function: delete_raster ( rp ) * Purpose: Destructor for raster. * Frees memory for raster bitmap and struct. * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct to be deleted. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int delete_raster ( raster *rp ) { /* ------------------------------------------------------------------------- free raster bitmap and struct -------------------------------------------------------------------------- */ if ( rp != (raster *)NULL ) /* can't free null ptr */ { if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */ free((void *)rp->pixmap); /* free pixmap within raster */ free((void *)rp); /* lastly, free raster struct */ } /* --- end-of-if(rp!=NULL) --- */ return ( 1 ); /* back to caller, 1=okay 0=failed */ } /* --- end-of-function delete_raster() --- */ /* ========================================================================== * Function: delete_subraster ( sp ) * Purpose: Deallocates a subraster (and embedded raster) * -------------------------------------------------------------------------- * Arguments: sp (I) ptr to subraster struct to be deleted. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int delete_subraster ( subraster *sp ) { /* ------------------------------------------------------------------------- free subraster struct -------------------------------------------------------------------------- */ int delete_raster(); /* to delete embedded raster */ if ( sp != (subraster *)NULL ) /* can't free null ptr */ { if ( sp->type != CHARASTER ) /* not static character data */ if ( sp->image != NULL ) /*raster allocated within subraster*/ delete_raster(sp->image); /* so free embedded raster */ free((void *)sp); /* and free subraster struct itself*/ } /* --- end-of-if(sp!=NULL) --- */ return ( 1 ); /* back to caller, 1=okay 0=failed */ } /* --- end-of-function delete_subraster() --- */ /* ========================================================================== * Function: delete_chardef ( cp ) * Purpose: Deallocates a chardef (and bitmap of embedded raster) * -------------------------------------------------------------------------- * Arguments: cp (I) ptr to chardef struct to be deleted. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int delete_chardef ( chardef *cp ) { /* ------------------------------------------------------------------------- free chardef struct -------------------------------------------------------------------------- */ if ( cp != (chardef *)NULL ) /* can't free null ptr */ { if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */ free((void *)cp->image.pixmap); /* so free embedded pixmap */ free((void *)cp); /* and free chardef struct itself */ } /* --- end-of-if(cp!=NULL) --- */ /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ return ( 1 ); } /* --- end-of-function delete_chardef() --- */ /* ========================================================================== * Function: rastcpy ( rp ) * Purpose: makes duplicate copy of rp * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct to be copied * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to new copy rp, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ raster *rastcpy ( raster *rp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/ int height= (rp==NULL?0:rp->height), /* original and copied height */ width = (rp==NULL?0:rp->width), /* original and copied width */ pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */ nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */ /* ------------------------------------------------------------------------- allocate rotated raster and fill it -------------------------------------------------------------------------- */ /* --- allocate copied raster with same width,height, and copy bitmap --- */ if ( rp != NULL ) /* nothing to copy if ptr null */ if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/ != NULL ) /* check that allocate succeeded */ memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */ return ( newrp ); /* return copied raster to caller */ } /* --- end-of-function rastcpy() --- */ /* ========================================================================== * Function: subrastcpy ( sp ) * Purpose: makes duplicate copy of sp * -------------------------------------------------------------------------- * Arguments: sp (I) ptr to subraster struct to be copied * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to new copy sp, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *subrastcpy ( subraster *sp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */ raster *rastcpy(), *newrp=NULL; /* and new raster image within it */ int delete_subraster(); /* dealloc newsp if rastcpy() fails*/ /* ------------------------------------------------------------------------- make copy, and return it to caller -------------------------------------------------------------------------- */ if ( sp == NULL ) goto end_of_job; /* nothing to copy */ /* --- allocate new subraster "envelope" for copy --- */ if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */ == NULL ) goto end_of_job; /* and quit if we fail to allocate */ /* --- transparently copy original envelope to new one --- */ memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */ /* --- make a copy of the rasterized image itself, if there is one --- */ if ( sp->image != NULL ) /* there's an image embedded in sp */ if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */ == NULL ) /* failed to copy successfully */ { delete_subraster(newsp); /* won't need newsp any more */ newsp = NULL; /* because we're returning error */ goto end_of_job; } /* back to caller with error signal*/ /* --- set new params in new envelope --- */ newsp->image = newrp; /* new raster image we just copied */ switch ( sp->type ) /* set new raster image type */ { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break; case IMAGERASTER: default: newsp->type = IMAGERASTER; break; } /* --- return copy of sp to caller --- */ end_of_job: return ( newsp ); /* copy back to caller */ } /* --- end-of-function subrastcpy() --- */ /* ========================================================================== * Function: rastrot ( rp ) * Purpose: rotates rp image 90 degrees right/clockwise * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct to be rotated * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to new raster rotated ralative to rp, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o An underbrace is } rotated 90 degrees clockwise, * a hat is <, etc. * ======================================================================= */ /* --- entry point --- */ raster *rastrot ( raster *rp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/ int height = rp->height, irow, /* original height, row index */ width = rp->width, icol, /* original width, column index */ pixsz = rp->pixsz; /* #bits per pixel */ /* ------------------------------------------------------------------------- allocate rotated raster and fill it -------------------------------------------------------------------------- */ /* --- allocate rotated raster with flipped width<-->height --- */ if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */ != NULL ) /* check that allocation succeeded */ /* --- fill rotated raster --- */ for ( irow=0; irowheight - 1 * left (I) int containing 0 ... target->width - 1 * isopaque (I) int containing false (zero) to allow * original 1-bits of target to "show through" * 0-bits of source. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int rastput ( raster *target, raster *source, int top, int left, int isopaque ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int irow, icol, /* indexes over source raster */ twidth=target->width, theight=target->height, /*target width,height*/ tpix, ntpix = twidth*theight; /* #pixels in target */ int isfatal = 0, /* true to abend on out-of-bounds error */ isstrict = 1, /* true for strict bounds check - no "wrap"*/ isokay = 1; /* true if no pixels out-of-bounds */ /* ------------------------------------------------------------------------- superimpose source onto target, one bit at a time -------------------------------------------------------------------------- */ if ( isstrict && (top<0 || left<0) ) /* args fail strict test */ isokay = 0; /* so just return error */ else for ( irow=0; irowheight; irow++ ) /* for each scan line */ { tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/ for ( icol=0; icolwidth; icol++ ) /* each pixel in scan line */ { int svalue = getpixel(source,irow,icol); /* source pixel value */ if ( ++tpix >= ntpix /* bounds check failed */ || (isstrict && (irow+top>=theight || icol+left>=twidth)) ) { isokay = 0; /* reset okay flag */ if ( isfatal ) goto end_of_job; /* abort if error is fatal */ else break; } /*or just go on to next row*/ if ( tpix >= 0 ) /* bounds check okay */ if ( svalue!=0 || isopaque ) /*got dark or opaque source*/ setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/ } /* --- end-of-for(icol) --- */ } /* --- end-of-for(irow) --- */ /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ end_of_job: return ( isokay /*isfatal? (tpixbaseline, /*baseline for underlying subraster*/ height1 = (sp1->image)->height, /* height for underlying subraster */ width1 = (sp1->image)->width, /* width for underlying subraster */ pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */ base2 = sp2->baseline, /*baseline for overlaid subraster */ height2 = (sp2->image)->height, /* height for overlaid subraster */ width2 = (sp2->image)->width, /* width for overlaid subraster */ pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */ int height=0, width=0, pixsz=0, base=0; /* overlaid composite */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ /* --- determine height, width and baseline of composite raster --- */ if ( isalign ) /* baselines of sp1,sp2 aligned */ { height = max2(base1+1,base2+1) /* max height above baseline */ + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/ base = max2(base1,base2); } /* max space above baseline */ else /* baselines not aligned */ { height = max2(height1,height2); /* max height */ base = base1 + (height-height1)/2; } /* baseline for sp1 */ width = max2(width1,width2+abs(offset2)); /* max width */ pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */ /* ------------------------------------------------------------------------- allocate concatted composite subraster -------------------------------------------------------------------------- */ /* --- allocate returned subraster (and then initialize it) --- */ if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == (subraster *)NULL ) goto end_of_job; /* failed, so quit */ /* --- initialize subraster parameters --- */ sp->type = IMAGERASTER; /* image */ sp->baseline = base; /* composite baseline */ sp->size = sp1->size; /* underlying char is sp1 */ /* --- extract raster from subraster --- */ rp = sp->image; /* raster allocated in subraster */ /* ------------------------------------------------------------------------- overlay sp1 and sp2 in new composite raster -------------------------------------------------------------------------- */ if ( isalign ) { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/ rastput (rp, sp2->image, base-base2, /*overlaid*/ (width-width2)/2+offset2, 0); } else { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/ rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/ (width-width2)/2+offset2, 0); } /* ------------------------------------------------------------------------- free input if requested -------------------------------------------------------------------------- */ if ( isfree > 0 ) /* caller wants input freed */ { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */ if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */ /* ------------------------------------------------------------------------- Back to caller with pointer to concatted subraster or with null for error -------------------------------------------------------------------------- */ end_of_job: return ( sp ); /* back with subraster or null ptr */ } /* --- end-of-function rastcompose() --- */ /* ========================================================================== * Function: rastcat ( sp1, sp2, isfree ) * Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged * and returning a newly-allocated subraster. * Frees/deletes input sp1 and/or sp2 depending on value * of isfree (0=none, 1=sp1, 2=sp2, 3=both). * -------------------------------------------------------------------------- * Arguments: sp1 (I) subraster * to left-hand subraster * sp2 (I) subraster * to right-hand subraster * isfree (I) int containing 1=free sp1 before return, * 2=free sp2, 3=free both, 0=free none. * -------------------------------------------------------------------------- * Returns: ( subraster * ) pointer to constructed subraster sp1||sp2 * or NULL for any error * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */ raster *rp=(raster *)NULL; /* new concatted raster */ int delete_subraster(); /* in case isfree non-zero */ int rastput(); /*place sp1,sp2 in concatted raster*/ int base1 = sp1->baseline, /*baseline for left-hand subraster*/ height1 = (sp1->image)->height, /* height for left-hand subraster */ width1 = (sp1->image)->width, /* width for left-hand subraster */ pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */ base2 = sp2->baseline, /*baseline for right-hand subraster*/ height2 = (sp2->image)->height, /* height for right-hand subraster */ width2 = (sp2->image)->width, /* width for right-hand subraster */ pixsz2 = (sp2->image)->pixsz; /* pixsz for right-hand subraster */ int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/ mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/ *symdef2 = sp2->symdef; /* mathchardef of right-hand char */ int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */ class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */ space = displaysize/2+1; /* #cols between sp1 and sp2 */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ /* --- determine inter-character space from character class --- */ space = max2(2,(symspace[class1][class2] + displaysize-4)); /* space */ /* --- determine height, width and baseline of composite raster --- */ height = max2(base1+1,base2+1) /* max height above baseline */ + max2(height1-base1-1,height2-base2-1); /*plus max descending below*/ width = width1 + width2 + space; /* just add widths and space */ pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */ base = max2(base1,base2); /* max space above baseline */ /* ------------------------------------------------------------------------- allocate concatted composite subraster -------------------------------------------------------------------------- */ /* --- allocate returned subraster (and then initialize it) --- */ if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == (subraster *)NULL ) goto end_of_job; /* failed, so quit */ /* --- initialize subraster parameters --- */ sp->type = STRINGRASTER; /* concatted string */ sp->symdef = symdef2; /* rightmost char is sp2 */ sp->baseline = base; /* composite baseline */ sp->size = sp2->size; /* rightmost char is sp2 */ /* --- extract raster from subraster --- */ rp = sp->image; /* raster allocated in subraster */ /* ------------------------------------------------------------------------- overlay sp1 and sp2 in new composite raster -------------------------------------------------------------------------- */ rastput (rp, sp1->image, base-base1, 0, 1); /* overlay left-hand */ rastput (rp, sp2->image, base-base2, width1+space, 1); /*and right*/ /* ------------------------------------------------------------------------- free input if requested -------------------------------------------------------------------------- */ if ( isfree > 0 ) /* caller wants input freed */ { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */ if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */ /* ------------------------------------------------------------------------- Back to caller with pointer to concatted subraster or with null for error -------------------------------------------------------------------------- */ end_of_job: return ( sp ); /* back with subraster or null ptr */ } /* --- end-of-function rastcat() --- */ /* ========================================================================== * Function: rastack ( sp1, sp2, base, space, iscenter, isfree ) * Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged * and returning a newly-allocated subraster, * whose baseline is sp1's if base=1, or sp2's if base=2. * Frees/deletes input sp1 and/or sp2 depending on value * of isfree (0=none, 1=sp1, 2=sp2, 3=both). * -------------------------------------------------------------------------- * Arguments: sp1 (I) subraster * to lower subraster * sp2 (I) subraster * to upper subraster * base (I) int containing 1 if sp1 is baseline, * or 2 if sp2 is baseline. * space (I) int containing #rows blank space inserted * between sp1's image and sp2's image. * iscenter (I) int containing 1 to center both sp1 and sp2 * in stacked array, 0 to left-justify both * isfree (I) int containing 1=free sp1 before return, * 2=free sp2, 3=free both, 0=free none. * -------------------------------------------------------------------------- * Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1 * or NULL for any error * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ subraster *rastack ( subraster *sp1, subraster *sp2, int base, int space, int iscenter, int isfree ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */ raster *rp=(raster *)NULL; /* new stacked raster in sp */ int delete_subraster(); /* in case isfree non-zero */ int rastput(); /* place sp1,sp2 in stacked raster */ int base1 = sp1->baseline, /* baseline for lower subraster */ height1 = (sp1->image)->height, /* height for lower subraster */ width1 = (sp1->image)->width, /* width for lower subraster */ pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */ base2 = sp2->baseline, /* baseline for upper subraster */ height2 = (sp2->image)->height, /* height for upper subraster */ width2 = (sp2->image)->width, /* width for upper subraster */ pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */ int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/ mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */ *symdef2 = sp2->symdef; /* mathchardef of right upper char */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ /* --- determine height, width and baseline of composite raster --- */ height = height1 + space + height2; /* sum of heights plus space */ width = max2(width1,width2); /* max width is overall width */ pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */ baseline = (base==1? height2+space+base1 : (base==2? base2 : 0)); /* ------------------------------------------------------------------------- allocate stacked composite subraster (with embedded raster) -------------------------------------------------------------------------- */ /* --- allocate returned subraster (and then initialize it) --- */ if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == (subraster *)NULL ) goto end_of_job; /* failed, so quit */ /* --- initialize subraster parameters --- */ sp->type = IMAGERASTER; /* stacked rasters */ sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */ sp->baseline = baseline; /* composite baseline */ sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/ /* --- extract raster from subraster --- */ rp = sp->image; /* raster embedded in subraster */ /* ------------------------------------------------------------------------- overlay sp1 and sp2 in new composite raster -------------------------------------------------------------------------- */ if ( iscenter == 1 ) /* center both sp1 and sp2 */ { rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */ rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/ else /* left-justify both sp1 and sp2 */ { rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */ rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/ /* ------------------------------------------------------------------------- free input if requested -------------------------------------------------------------------------- */ if ( isfree > 0 ) /* caller wants input freed */ { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */ if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */ /* ------------------------------------------------------------------------- Back to caller with pointer to stacked subraster or with null for error -------------------------------------------------------------------------- */ end_of_job: return ( sp ); /* back with subraster or null ptr */ } /* --- end-of-function rastack() --- */ /* ========================================================================== * Function: rastile ( tiles, ntiles ) * Purpose: Allocate and build up a composite raster * from the ntiles components/characters supplied in tiles. * -------------------------------------------------------------------------- * Arguments: tiles (I) subraster * to array of subraster structs * describing the components and their locations * ntiles (I) int containing number of subrasters in tiles[] * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to composite raster, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o The top,left corner of a raster is row=0,col=0 * with row# increasing as you move down, * and col# increasing as you move right. * Metafont numbers rows with the baseline=0, * so the top row is a positive number that * decreases as you move down. * o rastile() is no longer used. * It was used by an earlier rasterize() algorithm, * and I've left it in place should it be needed again. * But recent changes haven't been tested/exercised. * ======================================================================= */ /* --- entry point --- */ raster *rastile ( subraster *tiles, int ntiles ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/ int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/ toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */ botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */ int itile; /* tiles[] index */ int rastput(); /* overlay each tile in composite raster */ /* ------------------------------------------------------------------------- run through tiles[] to determine dimensions for composite raster -------------------------------------------------------------------------- */ /* --- determine row and column bounds of composite raster --- */ for ( itile=0; itiletoprow); leftcol = min2(leftcol, tile->leftcol); /* --- lower-right corner of composite --- */ botrow = max2(botrow, tile->toprow + (tile->image)->height - 1); rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1); /* --- pixsz of composite --- */ pixsz = max2(pixsz,(tile->image)->pixsz); } /* --- end-of-for(itile) --- */ /* --- calculate width and height from bounds --- */ width = rightcol - leftcol + 1; height = botrow - toprow + 1; /* --- sanity check (quit if bad dimensions) --- */ if ( width<1 || height<1 ) goto end_of_job; /* ------------------------------------------------------------------------- allocate composite raster, and embed tiles[] within it -------------------------------------------------------------------------- */ /* --- allocate composite raster --- */ if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/ == (raster *)NULL ) goto end_of_job; /* and quit if failed */ /* --- embed tiles[] in composite --- */ for ( itile=0; itileimage, /* overlay tile image at...*/ tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/ /* ------------------------------------------------------------------------- Back to caller with composite raster (or null for any error) -------------------------------------------------------------------------- */ end_of_job: return ( composite ); /* back with composite or null ptr */ } /* --- end-of-function rastile() --- */ /* ========================================================================== * Function: accent_subraster ( accent, width, height, pixsz ) * Purpose: Allocate a new subraster of width x height * (or maybe different dimensions, depending on accent), * and draw an accent (\hat or \vec or \etc) that fills it * -------------------------------------------------------------------------- * Arguments: accent (I) int containing either HATACCENT or VECACCENT, * etc, indicating the type of accent desired * width (I) int containing desired width of accent (#cols) * height (I) int containing desired height of accent(#rows) * pixsz (I) int containing 1 for bitmap, 8 for bytemap * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to newly-allocated subraster with accent, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o Some accents have internally-determined dimensions, * and caller should check dimensions in returned subraster * ======================================================================= */ /* --- entry point --- */ subraster *accent_subraster ( int accent, int width, int height, int pixsz ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ /* --- general info --- */ raster *new_raster(), *rp=NULL; /*raster containing desired accent*/ subraster *new_subraster(), *sp=NULL; /* subraster returning accent */ int delete_raster(), delete_subraster(); /*free allocated raster on err*/ int line_raster(), /* draws lines */ thickness = 1; /* line thickness */ int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */ /* --- other working info --- */ int col0, col1, /* cols for line */ row0, row1; /* rows for line */ subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/ /* --- info for under/overbraces, tildes, etc --- */ char brace[16]; /*"{" for over, "}" for under, etc*/ raster *rastrot(), /* rotate { for overbrace, etc */ *rastcpy(); /* may need copy of original */ subraster *arrow_subraster(); /* rightarrow for vec */ subraster *rastack(); /* stack accent atop extra space */ /* ------------------------------------------------------------------------- outer switch() traps accents that may change caller's height,width -------------------------------------------------------------------------- */ switch ( accent ) { default: /* ----------------------------------------------------------------------- inner switch() first allocates fixed-size raster for accents that don't ------------------------------------------------------------------------ */ if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */ != NULL ) /* and if we succeeded... */ switch ( accent ) /* ...draw requested accent in it */ { /* --- unrecognized request --- */ default: delete_raster(rp); /* unrecognized accent requested */ rp = NULL; break; /* so free raster and signal error */ /* --- bar request --- */ case UNDERBARACCENT: case BARACCENT: thickness = height-1; /* adjust thickness */ if ( accent == BARACCENT ) /* bar is above expression */ line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/ else /* underbar is below expression */ line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/ break; /* --- dot request --- */ case DOTACCENT: thickness = height-1; /* adjust thickness */ line_raster(rp,0,width/2,0,width/2,thickness); /* centered dot */ break; /* --- ddot request --- */ case DDOTACCENT: thickness = height-1; /* adjust thickness */ col0 = max2(width/3-(thickness-1),0); /* one-third of width */ col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/ line_raster(rp,0,col0,0,col0,thickness); /* set a dot at 1st third */ line_raster(rp,0,col1,0,col1,thickness); /* and another at 2nd */ break; /* --- hat request --- */ case HATACCENT: thickness = (width<=12? 2 : 3); /* adjust thickness */ line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/ line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/ break; /* --- sqrt request --- */ case SQRTACCENT: col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */ col0 = (col1+2)/3; /* midpoint col of sqrt */ row0 = (height+1)/2; /* midpoint row of sqrt */ row1 = height-1; /* bottom row of sqrt */ line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */ line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */ line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/ break; } /* --- end-of-inner-switch(accent) --- */ break; /* break from outer accent switch */ /* --- underbrace, overbrace request --- */ case UNDERBRACE: case OVERBRACE: if ( accent == UNDERBRACE ) strncpy(brace,"}",16); /* start with } brace */ if ( accent == OVERBRACE ) strncpy(brace,"{",16); /* start with { brace */ if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */ != NULL ) /* found desired brace */ { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */ delete_subraster(accsp); } /* and free subraster "envelope" */ break; /* --- hat request --- */ case HATACCENT: if ( accent == HATACCENT ) strncpy(brace,"<",16); /* start with < */ if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */ != NULL ) /* found desired brace */ { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */ delete_subraster(accsp); } /* and free subraster "envelope" */ break; /* --- vec request --- */ case VECACCENT: height = 2*(height/2) + 1; /* force height odd */ if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/ != NULL ) /* succeeded */ { rp = accsp->image; /* "extract" raster with bitmap */ free((void *)accsp); } /* and free subraster "envelope" */ break; /* --- tilde request --- */ case TILDEACCENT: if ( (accsp=get_delim("~",-width,CMEX10)) /* width search for tilde */ != NULL ) /* found desired tilde */ if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/ != NULL ) /* have tilde with space below it */ { rp = sp->image; /* "extract" raster with bitmap */ free((void *)sp); } /* and free subraster "envelope" */ break; } /* --- end-of-outer-switch(accent) --- */ /* ------------------------------------------------------------------------- if we constructed accent raster okay, embed it in a subraster and return it -------------------------------------------------------------------------- */ /* --- if all okay, allocate subraster to contain constructed raster --- */ if ( rp != NULL ) /* accent raster constructed okay */ if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */ == NULL ) /* and if we fail to allocate */ delete_raster(rp); /* free now-unneeded raster */ else /* subraster allocated okay */ { /* --- init subraster parameters, embedding raster in it --- */ sp->type = IMAGERASTER; /* constructed image */ sp->image = rp; /* raster we just constructed */ sp->size = (-1); /* can't set font size here */ sp->baseline = 0; } /* can't set baseline here */ /* --- return subraster containing desired accent to caller --- */ return ( sp ); /* return accent or NULL to caller */ } /* --- end-of-function accent_subraster() --- */ /* ========================================================================== * Function: arrow_subraster ( width, height, pixsz, drctn, isBig ) * Purpose: Allocate a raster/subraster and draw left/right arrow in it * -------------------------------------------------------------------------- * Arguments: width (I) int containing number of cols for arrow * height (I) int containing number of rows for arrow * pixsz (I) int containing 1 for bitmap, 8 for bytemap * drctn (I) int containing +1 for right arrow, * or -1 for left, 0 for leftright * isBig (I) int containing 1/true for \Long arrows, * or false for \long arrows, i.e., * true for ===> or false for --->. * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to constructed left/right arrow * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *arrow_subraster ( int width, int height, int pixsz, int drctn, int isBig ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */ int rule_raster(); /* draw arrow line */ int irow, midrow=height/2; /* index, midrow is arrowhead apex */ int icol, thickness=(height>15?2:1); /* arrowhead thickness and index */ int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */ int ipix, /* raster pixmap[] index */ npix = width*height; /* #pixels malloced in pixmap[] */ /* ------------------------------------------------------------------------- allocate raster/subraster and draw arrow line -------------------------------------------------------------------------- */ if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */ if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */ == NULL ) goto end_of_job; /* and quit if failed */ if ( !isBig ) /* single line */ rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/ else { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1); rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0); rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); } /* ------------------------------------------------------------------------- construct arrowhead(s) -------------------------------------------------------------------------- */ for ( irow=0; irow= 0 ) /* right arrowhead wanted */ for ( icol=0; icol= 0 ) /* bounds check */ if ( pixsz == 1 ) /* have a bitmap */ setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/ else /* should have a bytemap */ if ( pixsz == 8 ) /* check pixsz for bytemap */ ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/ /* --- left arrowhead (same as right except for ipix calculation) --- */ if ( drctn <= 0 ) /* left arrowhead wanted */ for ( icol=0; icolimage)->pixmap,ipix);/*turn on arrowhead bit*/ else /* should have a bytemap */ if ( pixsz == 8 ) /* check pixsz for bytemap */ ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/ } /* --- end-of-for(irow) --- */ end_of_job: return ( arrowsp ); /*back to caller with arrow or NULL*/ } /* --- end-of-function arrow_subraster() --- */ /* ========================================================================== * Function: uparrow_subraster ( width, height, pixsz, drctn, isBig ) * Purpose: Allocate a raster/subraster and draw up/down arrow in it * -------------------------------------------------------------------------- * Arguments: width (I) int containing number of cols for arrow * height (I) int containing number of rows for arrow * pixsz (I) int containing 1 for bitmap, 8 for bytemap * drctn (I) int containing +1 for up arrow, * or -1 for down, or 0 for updown * isBig (I) int containing 1/true for \Long arrows, * or false for \long arrows, i.e., * true for ===> or false for --->. * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to constructed up/down arrow * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *uparrow_subraster ( int width, int height, int pixsz, int drctn, int isBig ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */ int rule_raster(); /* draw arrow line */ int icol, midcol=width/2; /* index, midcol is arrowhead apex */ int irow, thickness=(width>15?2:1); /* arrowhead thickness and index */ int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */ int ipix, /* raster pixmap[] index */ npix = width*height; /* #pixels malloced in pixmap[] */ /* ------------------------------------------------------------------------- allocate raster/subraster and draw arrow line -------------------------------------------------------------------------- */ if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */ if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */ == NULL ) goto end_of_job; /* and quit if failed */ if ( !isBig ) /* single line */ rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/ else { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1); rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0); rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); } /* ------------------------------------------------------------------------- construct arrowhead(s) -------------------------------------------------------------------------- */ for ( icol=0; icol= 0 ) /* up arrowhead wanted */ for ( irow=0; irowimage)->pixmap,ipix);/*turn on arrowhead bit*/ else /* should have a bytemap */ if ( pixsz == 8 ) /* check pixsz for bytemap */ ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/ /* --- down arrowhead (same as up except for ipix calculation) --- */ if ( drctn <= 0 ) /* down arrowhead wanted */ for ( irow=0; irow 0 ) /* bounds check */ if ( pixsz == 1 ) /* have a bitmap */ setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/ else /* should have a bytemap */ if ( pixsz == 8 ) /* check pixsz for bytemap */ ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/ } /* --- end-of-for(irow) --- */ end_of_job: return ( arrowsp ); /*back to caller with arrow or NULL*/ } /* --- end-of-function uparrow_subraster() --- */ /* ========================================================================== * Function: rule_raster ( rp, top, left, width, height, type ) * Purpose: Draw a solid or dashed line (or box) in existing raster rp, * starting at top,left with dimensions width,height. * -------------------------------------------------------------------------- * Arguments: rp (I) raster * to raster in which rule * will be drawn * top (I) int containing row at which top-left corner * of rule starts (0 is topmost) * left (I) int containing col at which top-left corner * of rule starts (0 is leftmost) * width (I) int containing number of cols for rule * height (I) int containing number of rows for rule * type (I) int containing 0 for solid rule, * 1 for horizontal dashes, 2 for vertical * -------------------------------------------------------------------------- * Returns: ( int ) 1 if rule drawn okay, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o Rule line is implicitly "horizontal" or "vertical" depending * on relative width,height dimensions. It's a box if they're * more or less comparable. * ======================================================================= */ /* --- entry point --- */ int rule_raster ( raster *rp, int top, int left, int width, int height, int type ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int irow, icol; /* indexes over rp raster */ int ipix, /* raster pixmap[] index */ npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */ int isfatal = 0; /* true to abend on out-of-bounds error */ int hdash=1, vdash=2; /* type for horizontal, vertical dashes */ int dashlen=3, spacelen=2, /* #pixels for dash followed by space */ isdraw=1; /* true when drawing dash (init for solid) */ /* ------------------------------------------------------------------------- Check args -------------------------------------------------------------------------- */ if ( rp == (raster *)NULL ) /* no raster arg supplied */ if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */ rp = workingbox->image; /* use workingbox if possible */ else return ( 0 ); /* otherwise signal error to caller */ /* ------------------------------------------------------------------------- Fill line/box -------------------------------------------------------------------------- */ for ( irow=top; irowwidth + left - 1; /*first pixel preceding icol*/ for ( icol=left; icol= npix ) /* bounds check failed */ if ( isfatal ) goto end_of_job; /* abort if error is fatal */ else break; /*or just go on to next row*/ else /*ibit is within rp bounds*/ if ( isdraw ) /*and we're drawing this bit*/ if ( rp->pixsz == 1 ) /* have a bitmap */ setlongbit(rp->pixmap,ipix); /* so turn on bit in line */ else /* should have a bytemap */ if ( rp->pixsz == 8 ) /* check pixsz for bytemap */ (rp->pixmap)[ipix] = 255; /* set black byte in line */ } /* --- end-of-for(icol) --- */ } /* --- end-of-for(irow) --- */ end_of_job: return ( isfatal? (ipixheight-1 is bottom-most) * col1 (I) int containing col at which * line will end (rp->width-1 is rightmost) * thickness (I) int containing number of pixels/bits * thick the line will be * -------------------------------------------------------------------------- * Returns: ( int ) 1 if line drawn okay, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o if row0==row1, a horizontal line is drawn * between col0 and col1, with row0(==row1) the top row * and row0+(thickness-1) the bottom row * o if col0==col1, a vertical bar is drawn * between row0 and row1, with col0(==col1) the left col * and col0+(thickness-1) the right col * o if both the above, you get a square thickness x thickness * whose top-left corner is row0,col0. * ======================================================================= */ /* --- entry point --- */ int line_raster ( raster *rp, int row0, int col0, int row1, int col1, int thickness ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int irow, icol, /* indexes over rp raster */ locol=col0, hicol=col1, /* col limits at irow */ lorow=row0, hirow=row1; /* row limits at icol */ int ipix, /* raster pixmap[] index */ npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */ int isfatal = 0; /* true to abend on out-of-bounds error */ int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/ double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */ dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */ a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */ xcol, xrow; /* calculated col at irow, or row at icol */ double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */ xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/ ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)), xheight = 1.0; int line_recurse(), isrecurse=1; /* true to draw line recursively */ /* ------------------------------------------------------------------------- Check args -------------------------------------------------------------------------- */ if ( rp == (raster *)NULL ) /* no raster arg supplied */ if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */ rp = workingbox->image; /* use workingbox if possible */ else return ( 0 ); /* otherwise signal error to caller */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n" "\t dy,dx=%3.1lf,%3.1lf, a=%4.3lf, xwidth=%4.3lf\n", row0,col0, row1,col1, thickness, dy,dx, a, xwidth); /* --- check for recursive line drawing --- */ if ( isrecurse ) /* drawing lines recursively */ { line_recurse(rp,(double)row0,(double)col0, (double)row1,(double)col1,thickness); return ( 1 ); } /* --- set params for horizontal line or vertical bar --- */ if ( isline ) /*interpret row as top row*/ row1 = row0 + (thickness-1); /* set bottom row for line */ if ( 0&&isbar ) /*interpret col as left col*/ hicol = col0 + (thickness-1); /* set right col for bar */ /* ------------------------------------------------------------------------- draw line one row at a time -------------------------------------------------------------------------- */ for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/ { if ( !isbar && !isline ) /* neither vert nor horiz */ { xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */ locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */ hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"\t irow=%d, xcol=%4.2lf, lo,hicol=%d,%d\n", irow,xcol,locol,hicol); ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/ for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/ if ( ++ipix >= npix ) /* bounds check failed */ if ( isfatal ) goto end_of_job; /* abort if error is fatal */ else break; /*or just go on to next row*/ else /* turn on pixel in line */ if ( rp->pixsz == 1 ) /* have a pixel bitmap */ setlongbit(rp->pixmap,ipix); /* so turn on bit in line */ else /* should have a bytemap */ if ( rp->pixsz == 8 ) /* check pixsz for bytemap */ (rp->pixmap)[ipix] = 255; /* set black byte in line */ } /* --- end-of-for(irow) --- */ /* ------------------------------------------------------------------------- now _redraw_ line one col at a time to avoid "gaps" -------------------------------------------------------------------------- */ if ( 1 ) for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/ { if ( !isbar && !isline ) /* neither vert nor horiz */ { xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */ lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */ hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"\t icol=%d, xrow=%4.2lf, lo,hirow=%d,%d\n", icol,xrow,lorow,hirow); ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/ for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/ if ( irow<0 || irow>=rp->height || icol<0 || icol>=rp->width ) /* bounds check */ if ( isfatal ) goto end_of_job; /* abort if error is fatal */ else continue; /*or just go on to next row*/ else setpixel(rp,irow,icol,255); /* set pixel at irow,icol */ } /* --- end-of-for(irow) --- */ /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ end_of_job: return ( isfatal? (ipixheight-1 is bottom-most) * col1 (I) double containing col at which * line will end (rp->width-1 is rightmost) * thickness (I) int containing number of pixels/bits * thick the line will be * -------------------------------------------------------------------------- * Returns: ( int ) 1 if line drawn okay, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o Recurses, drawing left- and right-halves of line * until a horizontal or vertical segment is found * ======================================================================= */ /* --- entry point --- */ int line_recurse ( raster *rp, double row0, double col0, double row1, double col1, int thickness ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ double delrow = fabs(row1-row0), /* 0 if line horizontal */ delcol = fabs(col1-col0), /* 0 if line vertical */ tolerance = 0.5; /* draw line when it goes to point */ double midrow = 0.5*(row0+row1), /* midpoint row */ midcol = 0.5*(col0+col1); /* midpoint col */ /* ------------------------------------------------------------------------- recurse if either delta > tolerance -------------------------------------------------------------------------- */ if ( delrow > tolerance /* row hasn't converged */ || delcol > tolerance ) /* col hasn't converged */ { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */ line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */ return ( 1 ); } /* ------------------------------------------------------------------------- draw converged point -------------------------------------------------------------------------- */ setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/ return ( 1 ); } /* --- end-of-function line_recurse() --- */ /* ========================================================================== * Function: circle_raster ( rp, row0, col0, row1, col1, * thickness, quads ) * Purpose: Draw quad(rant)s of an ellipse in box determined by * diagonally opposite corner points (row0,col0) and * (row1,col1), of thickness pixels in existing raster rp. * -------------------------------------------------------------------------- * Arguments: rp (I) raster * to raster in which an ellipse * will be drawn * row0 (I) int containing 1st corner row bounding ellipse * (0 is topmost) * col0 (I) int containing 1st corner col bounding ellipse * (0 is leftmost) * row1 (I) int containing 2nd corner row bounding ellipse * (rp->height-1 is bottom-most) * col1 (I) int containing 2nd corner col bounding ellipse * (rp->width-1 is rightmost) * thickness (I) int containing number of pixels/bits * thick the ellipse arc line will be * quads (I) char * to null-terminated string containing * any subset/combination of "1234" specifying * which quadrant(s) of ellipse to draw. * NULL ptr draws all four quadrants; * otherwise 1=upper-right quadrant, * 2=uper-left, 3=lower-left, 4=lower-right, * i.e., counterclockwise from 1=positive quad. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if ellipse drawn okay, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o row0==row1 or col0==col1 are errors * o using ellipse equation x^2/a^2 + y^2/b^2 = 1 * ======================================================================= */ /* --- entry point --- */ int circle_raster ( raster *rp, int row0, int col0, int row1, int col1, int thickness, char *quads ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ /* --- lower-left and upper-right bounding points (in our coords) --- */ int lorow = min2(row0,row1), /* lower bounding row (top of box) */ locol = min2(col0,col1), /* lower bounding col (left of box)*/ hirow = max2(row0,row1), /* upper bounding row (bot of box) */ hicol = max2(col0,col1); /* upper bounding col (right of box)*/ /* --- a and b ellipse params --- */ int width = hicol-locol+1, /* width of bounding box */ height= hirow-lorow+1, /* height of bounding box */ islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/ double a = ((double)width)/2.0, /* x=a when y=0 */ b = ((double)height)/2.0, /* y=b when x=0 */ abmajor = (islandscape? a : b), /* max2(a,b) */ abminor = (islandscape? b : a), /* min2(a,b) */ abmajor2 = abmajor*abmajor, /* abmajor^2 */ abminor2 = abminor*abminor; /* abminor^2 */ /* --- other stuff --- */ int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/ iminor=0, nminor=min2(width,height); /* solved index on minor axis */ int irow, icol, /* raster indexes at circumference */ rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/ double midrow= ((double)(row0+row1))/2.0, /* center row */ midcol= ((double)(col0+col1))/2.0; /* center col */ double xy, xy2, /* major axis ellipse coord */ yx2, yx; /* solved minor ellipse coord */ int isokay = 1; /* true if no pixels out-of-bounds */ char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */ /* ------------------------------------------------------------------------- pixel-by-pixel along positive major axis, quit when it goes negative -------------------------------------------------------------------------- */ if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n", width,height,quads); if ( nmajor < 1 ) isokay = 0; /* problem with input args */ else { for ( imajor=(nmajor+1)/2; ; imajor-- ) { /* --- xy is coord along major axis, yx is "solved" along minor axis --- */ xy = ((double)imajor); /* xy = abmajor ... 0 */ if ( xy < 0.0 ) break; /* negative side symmetrical */ yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */ yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */ iminor = iround(yx); /* nearest integer */ /* --- set pixels for each requested quadrant --- */ for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */ { rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */ switch ( *qptr ) /* check for quadrant 1,2,3,4 */ { default: break; /* unrecognized, assume quadrant 1 */ case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */ case '3': rsign = 1; /* row pos, col neg in quadrant 3 */ case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */ irow = iround(midrow + (double)rsign*(islandscape?yx:xy)); irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */ icol = iround(midcol + (double)csign*(islandscape?xy:yx)); icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n", imajor,iminor,*qptr,irow,icol); if ( irow<0 || irow>=rp->height /* row outside raster */ || icol<0 || icol>=rp->width ) /* col outside raster */ { isokay = 0; /* signal out-of-bounds pixel */ continue; } /* but still try remaining points */ setpixel(rp,irow,icol,255); /* set pixel at irow,icol */ } /* --- end-of-for(qptr) --- */ } /* --- end-of-for(imajor) --- */ /* ------------------------------------------------------------------------ now do it _again_ along minor axis to avoid "gaps" ------------------------------------------------------------------------- */ if ( 1 && iminor>0 ) for ( iminor=(nminor+1)/2; ; iminor-- ) { /* --- yx is coord along minor axis, xy is "solved" along major axis --- */ yx = ((double)iminor); /* yx = abminor ... 0 */ if ( yx < 0.0 ) break; /* negative side symmetrical */ xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */ xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */ imajor = iround(xy); /* nearest integer */ /* --- set pixels for each requested quadrant --- */ for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */ { rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */ switch ( *qptr ) /* check for quadrant 1,2,3,4 */ { default: break; /* unrecognized, assume quadrant 1 */ case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */ case '3': rsign = 1; /* row pos, col neg in quadrant 3 */ case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */ irow = iround(midrow + (double)rsign*(islandscape?yx:xy)); irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */ icol = iround(midcol + (double)csign*(islandscape?xy:yx)); icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n", iminor,imajor,*qptr,irow,icol); if ( irow<0 || irow>=rp->height /* row outside raster */ || icol<0 || icol>=rp->width ) /* col outside raster */ { isokay = 0; /* signal out-of-bounds pixel */ continue; } /* but still try remaining points */ setpixel(rp,irow,icol,255); /* set pixel at irow,icol */ } /* --- end-of-for(qptr) --- */ } /* --- end-of-for(iminor) --- */ } /* --- end-of-if/else(nmajor<1) --- */ return ( isokay ); } /* --- end-of-function circle_raster() --- */ /* ========================================================================== * Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct ) * Purpose: Recursively draw bezier from r0,c0 to r1,c1 * (with tangent point rt,ct) in existing raster rp. * -------------------------------------------------------------------------- * Arguments: rp (I) raster * to raster in which a line * will be drawn * r0 (I) double containing row at which * bezier will start (0 is topmost) * c0 (I) double containing col at which * bezier will start (0 is leftmost) * r1 (I) double containing row at which * bezier will end (rp->height-1 is bottom-most) * c1 (I) double containing col at which * bezier will end (rp->width-1 is rightmost) * rt (I) double containing row for tangent point * ct (I) double containing col for tangent point * -------------------------------------------------------------------------- * Returns: ( int ) 1 if line drawn okay, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o Recurses, drawing left- and right-halves of bezier curve * until a point is found * ======================================================================= */ /* --- entry point --- */ int bezier_raster ( raster *rp, double r0, double c0, double r1, double c1, double rt, double ct ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ double delrow = fabs(r1-r0), /* 0 if same row */ delcol = fabs(c1-c0), /* 0 if same col */ tolerance = 0.5; /* draw curve when it goes to point*/ double midrow = 0.5*(r0+r1), /* midpoint row */ midcol = 0.5*(c0+c1); /* midpoint col */ int irow=0, icol=0; /* point to be drawn */ int status = 1; /* return status */ /* ------------------------------------------------------------------------- recurse if either delta > tolerance -------------------------------------------------------------------------- */ if ( delrow > tolerance /* row hasn't converged */ || delcol > tolerance ) /* col hasn't converged */ { bezier_raster(rp, r0,c0, /* left half */ 0.5*(rt+midrow), 0.5*(ct+midcol), 0.5*(r0+rt), 0.5*(c0+ct) ); bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */ r1,c1, 0.5*(r1+rt), 0.5*(c1+ct) ); return ( 1 ); } /* ------------------------------------------------------------------------- draw converged point -------------------------------------------------------------------------- */ /* --- get integer point --- */ irow = iround(midrow); /* row pixel coord */ icol = iround(midcol); /* col pixel coord */ /* --- bounds check --- */ if ( irow>=0 && irowheight /* row in bounds */ && icol>=0 && icolwidth ) /* col in bounds */ setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/ else status = 0; /* bad status if out-of-bounds */ return ( status ); } /* --- end-of-function bezier_raster() --- */ /* ========================================================================== * Function: border_raster ( rp, ntop, nbot, isline, isfree ) * Purpose: Allocate a new raster containing a copy of input rp, * along with ntop extra rows at top and nbot at bottom, * and whose width is either adjusted correspondingly, * or is automatically enlarged to a multiple of 8 * with original bitmap centered * -------------------------------------------------------------------------- * Arguments: rp (I) raster * to raster on which a border * is to be placed * ntop (I) int containing number extra rows at top. * if negative, abs(ntop) used, and same * number of extra cols added at left. * nbot (I) int containing number extra rows at bottom. * if negative, abs(nbot) used, and same * number of extra cols added at right. * isline (I) int containing 0 to leave border pixels clear * or >0 to draw a line around border of width * isline. * isfree (I) int containing true to free rp before return * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to bordered raster, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ raster *border_raster ( raster *rp, int ntop, int nbot, int isline, int isfree ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/ int rastput(); /* overlay rp in new bordered raster */ int width = (rp==NULL?0:rp->width), /* height of raster */ height = (rp==NULL?0:rp->height), /* width of raster */ istopneg=0, isbotneg=0, /* true if ntop or nbot negative */ leftmargin = 0; /* adjust width to whole number of bytes */ int delete_raster(); /* to free input rp if isdelete is true */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ if ( rp == NULL ) goto end_of_job; /* no input raster provided */ /* --- check for negative args --- */ if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/ if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/ /* --- adjust height for ntop and nbot margins --- */ height += (ntop+nbot); /* adjust height for margins */ /* --- adjust width for left and right margins --- */ if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/ { /* --- adjust width (and leftmargin) as requested by caller -- */ if ( istopneg ) { width += ntop; leftmargin = ntop; } if ( isbotneg ) width += nbot; } else { /* --- or adjust width (and leftmargin) to whole number of bytes --- */ leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/ width += leftmargin; /* width now multiple of 8 */ leftmargin /= 2; } /* center original raster */ /* ------------------------------------------------------------------------- allocate bordered raster, and embed rp within it -------------------------------------------------------------------------- */ /* --- allocate bordered raster --- */ if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/ == (raster *)NULL ) goto end_of_job; /* and quit if failed */ /* --- embed rp in it --- */ rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */ /* ------------------------------------------------------------------------- draw border if requested -------------------------------------------------------------------------- */ if ( isline ) { int irow, icol, nthick=isline; /*height,width index, line thickness*/ /* --- draw left- and right-borders --- */ for ( irow=0; irowwidth) -------------------------------------------------------------------------- */ while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/ { /* --- set hicol for this pass (locol set above) --- */ hicol += display_width; /* show as much as display allows */ if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/ scan_width = hicol-locol+1; /* #chars in this scan */ if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/ /* ------------------------------------------------------------------------ display all scan lines for this local...hicol segment range ------------------------------------------------------------------------ */ for ( irow=0; irowheight; irow++ ) /* all scan lines for col range */ { /* --- allocations and declarations --- */ int ipix, /* pixmap[] index for this scan */ lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/ /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */ for ( ipix=0; ipixpixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/ scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.'); else /* should have a bytemap */ if ( rp->pixsz == 8 ) /* double-check pixsz for bytemap */ { int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/ ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */ scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/ /* --- display completed scan line --- */ fprintf(fp,"%.*s\n",scan_width,scanline); } /* --- end-of-for(irow) --- */ } /* --- end-of-while(hicolwidth) --- */ /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ return ( 1 ); } /* --- end-of-function type_raster() --- */ /* ========================================================================== * Function: type_bytemap ( bp, grayscale, width, height, fp ) * Purpose: Emit an ascii dump representing bp, on fp. * -------------------------------------------------------------------------- * Arguments: bp (I) intbyte * to bytemap for which an * ascii dump is to be constructed. * grayscale (I) int containing #gray shades, 256 for 8-bit * width (I) int containing #cols in bytemap * height (I) int containing #rows in bytemap * fp (I) File ptr to output device (defaults to * stdout if passed as NULL). * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int type_bytemap ( intbyte *bp, int grayscale, int width, int height, FILE *fp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ static int display_width = 72; /* max columns for display */ int byte_width = 3, /* cols to display byte (ff+space) */ maxbyte = 0; /* if maxbyte<16, set byte_width=2 */ int white_byte = 0, /* show dots for white_byte's */ black_byte = grayscale-1; /* show stars for black_byte's */ char scanline[133]; /* ascii image for one scan line */ int scan_width, /* #chars in scan (<=display_width)*/ scan_cols; /* #cols in scan (hicol-locol+1) */ int ibyte, /* bp[] index */ irow, locol,hicol=(-1); /* height index, width indexes */ /* -------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- redirect null fp --- */ if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */ /* --- see if we can get away with byte_width=1 --- */ for ( ibyte=0; ibytewidth) -------------------------------------------------------------------------- */ while ( (locol=hicol+1) < width ) /*start where prev segment left off*/ { /* --- set hicol for this pass (locol set above) --- */ hicol += display_width/byte_width; /* show as much as display allows */ if (hicol >= width) hicol = width - 1; /* but not more than bytemap */ scan_cols = hicol-locol+1; /* #cols in this scan */ scan_width = byte_width*scan_cols; /* #chars in this scan */ if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/ /* ------------------------------------------------------------------------ display all scan lines for this local...hicol segment range ------------------------------------------------------------------------ */ for ( irow=0; irow 1 ) /* don't blank out single char */ scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */ if ( byteval != white_byte /* format bytes that are non-white */ && byteval != black_byte ) /* and that are non-black */ sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/ memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/ /* --- display completed scan line --- */ fprintf(fp,"%.*s\n",scan_width,scanline); } /* --- end-of-for(irow) --- */ } /* --- end-of-while(hicolbitmap image -------------------------------------------------------------------------- */ /* --- first redirect null fp --- */ if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */ /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */ fprintf( fp, "Content-type: image/x-xbitmap\n\n" ); fprintf( fp, "#define %s_width %d\n#define %s_height %d\n", title,rp->width, title,rp->height ); fprintf( fp, "static char %s_bits[] = {\n", title ); hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */ fprintf (fp,"};\n"); /* ending with "};" for C array */ /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ return ( 1 ); } /* --- end-of-function xbitmap_raster() --- */ /* ========================================================================== * Function: cstruct_chardef ( cp, fp, col1 ) * Purpose: Emit a C struct of cp on fp, starting in col1. * -------------------------------------------------------------------------- * Arguments: cp (I) ptr to chardef struct for which * a C struct is to be generated. * fp (I) File ptr to output device (defaults to * stdout if passed as NULL). * col1 (I) int containing 0...65; output lines * are preceded by col1 blanks. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int cstruct_chardef ( chardef *cp, FILE *fp, int col1 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char field[64]; /* field within output line */ int cstruct_raster(), /* emit a raster */ emit_string(); /* emit a string and comment */ /* ------------------------------------------------------------------------- emit charnum, location, name / hirow, hicol, lorow, locol -------------------------------------------------------------------------- */ /* --- charnum, location, name --- */ sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/ emit_string ( fp, col1, field, "character number, location"); /* --- toprow, topleftcol, botrow, botleftcol --- */ sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */ cp->toprow,cp->topleftcol, /* toprow, topleftcol, */ cp->botrow,cp->botleftcol); /* and botrow, botleftcol */ emit_string ( fp, col1, field, "topleft row,col, and botleft row,col"); /* ------------------------------------------------------------------------- emit raster and chardef's closing brace, and then return to caller -------------------------------------------------------------------------- */ cstruct_raster(&cp->image,fp,col1+4); /* emit raster */ emit_string ( fp, 0, " }", NULL); /* emit closing brace */ return ( 1 ); /* back to caller with 1=okay, 0=failed */ } /* --- end-of-function cstruct_chardef() --- */ /* ========================================================================== * Function: cstruct_raster ( rp, fp, col1 ) * Purpose: Emit a C struct of rp on fp, starting in col1. * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct for which * a C struct is to be generated. * fp (I) File ptr to output device (defaults to * stdout if passed as NULL). * col1 (I) int containing 0...65; output lines * are preceded by col1 blanks. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int cstruct_raster ( raster *rp, FILE *fp, int col1 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char field[64]; /* field within output line */ int hex_bitmap(); /* to emit raster bitmap */ int emit_string(); /* emit a string and comment */ /* ------------------------------------------------------------------------- emit width and height -------------------------------------------------------------------------- */ sprintf(field,"{ %2d, %3d,%2d,\n", /* format width,height,pixsz */ rp->width,rp->height,rp->pixsz); emit_string ( fp, col1, field, "width x height, pixsz, bitmap..."); /* ------------------------------------------------------------------------- emit bitmap and closing brace, and return to caller -------------------------------------------------------------------------- */ hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */ emit_string ( fp, 0, " }", NULL); /* emit closing brace */ return ( 1 ); /* back to caller with 1=okay, 0=failed */ } /* --- end-of-function cstruct_raster() --- */ /* ========================================================================== * Function: hex_bitmap ( rp, fp, col1, isstr ) * Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1. * If isstr (is string) is true, the dump is of the form * "\x01\x02\x03\x04\x05..." * Otherwise, if isstr is false, the dump is of the form * 0x01,0x02,0x03,0x04,0x05... * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct for which * a hex dump is to be constructed. * fp (I) File ptr to output device (defaults to * stdout if passed as NULL). * col1 (I) int containing 0...65; output lines * are preceded by col1 blanks. * isstr (I) int specifying dump format as described above * -------------------------------------------------------------------------- * Returns: ( int ) 1 if completed successfully, * or 0 otherwise (for any error). * -------------------------------------------------------------------------- * Notes: * ======================================================================= */ /* --- entry point --- */ int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */ char stub[64]=" ";/* col1 leading blanks */ int linewidth = 64, /* (roughly) rightmost column */ colwidth = (isstr? 4:5); /* #cols required for each byte */ int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */ /* -------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- redirect null fp --- */ if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */ /* --- emit initial stub if wanted --- */ if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */ /* -------------------------------------------------------------------------- emit hex dump of rp->bitmap image -------------------------------------------------------------------------- */ if ( isstr ) fprintf(fp,"\""); /* opening " before first line */ for ( ibyte=0; ibytepixmap)[ibyte]); /*print byte as hex char*/ else /* comma-separated format wanted */ fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/ /* --- add a separator and newline, etc, as necessary --- */ if ( ibyte < nbytes-1) /* not the last byte yet */ { if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */ if ( (ibyte+1)%ncols==0 ) /* need new line after every ncols */ if ( !isstr ) /* for hex numbers format ... */ fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */ else /* for string format... */ fprintf(fp,"\"\n%.*s\"",col1,stub); /* ...need closing, opening "s */ } /* --- end-of-if(ibyte 6 ) /* can fit all or part of comment */ sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */ '*', fieldlen-6,comment, '*'); col1 = linelen; } /* indicate line filled */ /* --- line completed --- */ line[col1] = '\000'; /* null-terminate completed line */ /* ------------------------------------------------------------------------- emit line, then back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ /* --- first redirect null fp --- */ if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */ /* --- emit line (and optional newline) --- */ fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */ if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/ return ( 1 ); } /* --- end-of-function emit_string() --- */ /* ========================================================================== * Function: get_symdef ( symbol ) * Purpose: returns mathchardef struct for symbol * -------------------------------------------------------------------------- * Arguments: symbol (I) char * containing symbol * whose corresponding mathchardef is wanted * -------------------------------------------------------------------------- * Returns: ( mathchardef * ) pointer to struct defining symbol, * or NULL for any error * -------------------------------------------------------------------------- * Notes: o Input symbol need only contain a leading substring to match, * e.g., \gam passed in symbol will match \gamma in the table. * If the table contains two or more possible matches, * the shortest is returned, e.g., input \e will return with * data for \eta rather than \epsilon. To get \epsilon, * you must pass a leading substring long enough to eliminate * shorter table matches, i.e., in this case \ep * ======================================================================= */ /* --- entry point --- */ mathchardef *get_symdef ( char *symbol ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ mathchardef *symdefs = symtable; /* table of mathchardefs */ int idef = 0, /* symdefs[] index */ bestdef = (-9999); /*index of shortest matching symdef*/ int symlen = strlen(symbol), /* length of input symbol */ deflen, minlen=9999; /*length of shortest matching symdef*/ int alphasym = (symlen==1 && isalpha(*symbol)); /* symbol is an alpha */ /* ------------------------------------------------------------------------- search symdefs[] in order for first occurrence of symbol -------------------------------------------------------------------------- */ for ( idef=0; ;idef++ ) /* until trailer record found */ if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */ else /* check against caller's symbol */ if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */ if ( !istext || !alphasym /* mathmode or (textmode&&!alpha) */ || (istext==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/ || (istext==2 && symdefs[idef].family==CMMI10) ) /*textmode && it text*/ if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/ { bestdef = idef; /* save index of new best match */ if ( (minlen = deflen) /* and save its len for next test */ == symlen ) break; } /*perfect match, so return with it*/ return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/ } /* --- end-of-function get_symdef() --- */ /* ========================================================================== * Function: get_chardef ( symdef, size ) * Purpose: returns chardef ptr containing data for symdef at given size * -------------------------------------------------------------------------- * Arguments: symdef (I) mathchardef * corresponding to symbol * whose corresponding chardef is wanted * size (I) int containing 0-4 for desired size * -------------------------------------------------------------------------- * Returns: ( chardef * ) pointer to struct defining symbol at size, * or NULL for any error * -------------------------------------------------------------------------- * Notes: o if size unavailable, the next-closer-to-normalsize * is returned instead. * ======================================================================= */ /* --- entry point --- */ chardef *get_chardef ( mathchardef *symdef, int size ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ fontfamily *fonts = fonttable; /* table of font families */ chardef **fontdef, /*tables for desired font, by size*/ *gfdata = (chardef *)NULL; /* chardef for symdef,size */ int ifont; /* fonts[] index */ int family, charnum; /* indexes retrieved from symdef */ int sizeinc = 0; /*+1 or -1 to get closer to normal*/ int isBig = 0; /*true if symbol's 1st char is upper*/ char *symptr = NULL; /* look for 1st alpha of symbol */ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- check symdef --- */ if ( symdef == NULL ) return ( NULL ); /* get_symdef() probably failed */ /* --- get local copy of indexes from symdef --- */ family = symdef->family; /* font family containing symbol */ charnum = symdef->charnum; /* char# of symbol within font */ /* --- check requested size, and set size increment --- */ if ( size < 0 ) size = 0; /* size was definitely too small */ if ( size > LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */ if ( size < NORMALSIZE ) sizeinc = (+1); /*use next larger if size too small*/ if ( size > NORMALSIZE ) sizeinc = (-1); /*or next smaller if size too large*/ /* --- check for really big symbol (1st char of symbol name uppercase) --- */ for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/ if ( isalpha(*symptr) ) /* found leading alpha char */ { isBig = isupper(*symptr); /* is 1st char of name uppercase? */ break; } /* don't check beyond 1st char */ /* ------------------------------------------------------------------------- find font family in table of fonts[] -------------------------------------------------------------------------- */ /* --- look up font family --- */ for ( ifont=0; ;ifont++ ) /* until trailer record found */ if ( fonts[ifont].family < 0 ) return ( NULL ); /* error, no such family */ else if ( fonts[ifont].family == family ) break; /* found font family */ /* --- get local copy of table for this family by size --- */ fontdef = fonts[ifont].fontdef; /* font by size */ /* ------------------------------------------------------------------------- get font in desired size, or closest available size, and return symbol -------------------------------------------------------------------------- */ /* --- get font in desired size --- */ while ( 1 ) /* find size or closest available */ if ( fontdef[size] != NULL ) break; /* found available size */ else /* adjust size closer to normal */ if ( size == NORMALSIZE ) return ( NULL ); /* already normal, no sizes */ else size += sizeinc; /* see if adjusted size available */ /* --- ptr to chardef struct --- */ gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/ /* ------------------------------------------------------------------------- kludge to tweak CMEX10 (which appears to have incorrect descenders) -------------------------------------------------------------------------- */ if ( family == CMEX10 ) /* cmex10 needs tweak */ { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/ gfdata->botrow = (isBig? (-height/3) : (-height/4)); gfdata->toprow = gfdata->botrow + gfdata->image.height; } /* ------------------------------------------------------------------------- return subraster containing chardef data for symbol in requested size -------------------------------------------------------------------------- */ return ( gfdata ); /*ptr to chardef for symbol in size*/ } /* --- end-of-function get_chardef() --- */ /* ========================================================================== * Function: get_charsubraster ( symdef, size ) * Purpose: returns new subraster ptr containing * data for symdef at given size * -------------------------------------------------------------------------- * Arguments: symdef (I) mathchardef * corresponding to symbol * whose corresponding chardef is wanted * size (I) int containing 0-4 for desired size * -------------------------------------------------------------------------- * Returns: ( subraster * ) pointer to struct defining symbol at size, * or NULL for any error * -------------------------------------------------------------------------- * Notes: o just wraps a subraster envelope around get_chardef() * ======================================================================= */ /* --- entry point --- */ subraster *get_charsubraster ( mathchardef *symdef, int size ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */ int get_baseline(); /* baseline of gfdata */ subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */ /* ------------------------------------------------------------------------- look up chardef for symdef at size, and embed data (gfdata) in subraster -------------------------------------------------------------------------- */ if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */ != NULL ) /* and check that we found it */ if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */ != NULL ) /* and check that we succeeded */ { sp->type = CHARASTER; /* static character raster */ sp->symdef = symdef; /* replace NULL with caller's arg */ sp->size = size; /*replace default with caller's size*/ sp->baseline = get_baseline(gfdata); /* get baseline of character */ sp->image = &(gfdata->image); /* store ptr to its bitmap */ } /* --- end-of-if(sp!=NULL) --- */ return ( sp ); /* back to caller */ } /* --- end-of-function get_charsubraster() --- */ /* ========================================================================== * Function: get_baseline ( gfdata ) * Purpose: returns baseline for a chardef struct * -------------------------------------------------------------------------- * Arguments: gfdata (I) chardef * containing chardef for symbol * whose baseline is wanted * -------------------------------------------------------------------------- * Returns: ( int ) baseline for symdef, * or -1 for any error * -------------------------------------------------------------------------- * Notes: o Unlike TeX, the top-left corners of our rasters are (0,0), * with (row,col) increasing as you move down and right. * Baselines are calculated with respect to this scheme, * so 0 would mean the very top row is on the baseline * and everything else descends below the baseline. * ======================================================================= */ /* --- entry point --- */ int get_baseline ( chardef *gfdata ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int toprow = gfdata->toprow, /*TeX top row from .gf file info*/ botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/ height = gfdata->image.height; /* #rows comprising symbol */ /* ------------------------------------------------------------------------- give caller baseline -------------------------------------------------------------------------- */ return ( (height-1) + botrow ); /* note: descenders have botrow<0 */ } /* --- end-of-function get_baseline() --- */ /* ========================================================================== * Function: get_delim ( char *symbol, int height, int family ) * Purpose: returns subraster corresponding to the samllest * character containing symbol, but at least as large as height, * and in caller's family (if specified). * If no symbol character as large as height is available, * then the largest availabale character is returned instead. * -------------------------------------------------------------------------- * Arguments: symbol (I) char * containing (substring of) desired * symbol, e.g., if symbol="(", then any * mathchardef like "(" or "\\(", etc, match. * height (I) int containing minimum acceptable height * for returned character * family (I) int containing -1 to consider all families, * or, e.g., CMEX10 for only that family * -------------------------------------------------------------------------- * Returns: ( subraster * ) best matching character available, * or NULL for any error * -------------------------------------------------------------------------- * Notes: o If height is passed as negative, its absolute value is used * but the best-fit width is searched for (rather than height) * ======================================================================= */ /* --- entry point --- */ subraster *get_delim ( char *symbol, int height, int family ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ mathchardef *symdefs = symtable; /* table of mathchardefs */ subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */ subraster *make_delim(); /* construct delim if can't find it*/ chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */ char lcsymbol[99], *symptr; /* lowercase symbol for comparison */ int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/ deflen = 0; /* length of symdef (aka lcsymbol) */ int idef = 0, /* symdefs[] index */ bestdef = (-9999), /* index of best fit symdef */ bigdef = (-9999); /*index of biggest (in case no best)*/ int size = 0, /* size index 0...LARGESTSIZE */ bestsize = (-9999), /* index of best fit size */ bigsize = (-9999); /*index of biggest (in case no best)*/ int defheight, bestheight=9999, /* height of best fit symdef */ bigheight = (-9999); /*height of biggest(in case no best)*/ int iswidth = 0; /* true if best-fit width desired */ /* ------------------------------------------------------------------------- determine if searching height or width, and search symdefs[] for best-fit -------------------------------------------------------------------------- */ /* --- determine whether searching for best-fit height or width --- */ if ( height < 0 ) /* negative signals width search */ { height = (-height); /* flip "height" positive */ iswidth = 1; } /* set flag for width search */ /* --- search symdefs[] for best-fit height (or width) --- */ for ( idef=0; ;idef++ ) /* until trailer record found */ if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */ else /* check against caller's symbol */ if ( family<0 || symdefs[idef].family == family ) /*if in caller's family*/ { strncpy(lcsymbol,symdefs[idef].symbol,99); /*local copy of symdefs[] symbol*/ if ( 0 ) /* don't ignore case */ for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/ if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/ deflen = strlen(lcsymbol); /* #chars in symbol we're checking */ if ( (symptr=strstr(lcsymbol,symbol)) != NULL ) /*caller's sym embedded*/ if ( symptr == lcsymbol /* caller's sym is a prefix */ || symptr == lcsymbol+deflen-symlen ) /* or a suffix */ for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */ if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/ { defheight = gfdata->image.height; /* height of this character */ if ( iswidth ) /* width search wanted instead... */ defheight = gfdata->image.width; /* ...so substitute width */ if ( defheight>=height && defheight= bigheight ) /* new biggest character */ { bigdef=idef; bigsize=size; /* save indexes of biggest */ bigheight = defheight; } /* and save new big height */ } /* --- end-of-if(gfdata!=NULL) --- */ } /* --- end-of-if(family) --- */ /* ------------------------------------------------------------------------- construct subraster for best fit character, and return it to caller -------------------------------------------------------------------------- */ if ( bestdef >= 0 ) /* found a best fit for caller */ sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */ if ( sp==NULL && height-bigheight>5 ) /* try to construct delim */ sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */ if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */ sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */ return ( sp ); } /* --- end-of-function get_delim() --- */ /* ========================================================================== * Function: make_delim ( char *symbol, int height ) * Purpose: constructs subraster corresponding to symbol * exactly as large as height, * -------------------------------------------------------------------------- * Arguments: symbol (I) char * containing, e.g., if symbol="(" * for desired delimiter * height (I) int containing height * for returned character * -------------------------------------------------------------------------- * Returns: ( subraster * ) constructed delimiter * or NULL for any error * -------------------------------------------------------------------------- * Notes: o If height is passed as negative, its absolute value is used * and interpreted as width (rather than height) * ======================================================================= */ /* --- entry point --- */ subraster *make_delim ( char *symbol, int height ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *sp = (subraster *)NULL, /* subraster returned to caller */ *new_subraster(); /* allocate subraster */ raster *rasp = (raster *)NULL; /* sp->image */ int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */ int pixsz = 1; /* pixels are one bit each */ int thickness = 1; /* drawn lines are one pixel thick */ int aspectratio = 8; /* default height/width for parens */ int iswidth = 0, /*true if width specified by height*/ width = height; /* #pixels width (e.g., of ellipse)*/ char *lp=NULL, *rp=NULL, *strchr(), /* check symbol for left or right */ *lp2=NULL, *rp2=NULL; /* synonym for lp,rp */ int circle_raster(), /* ellipse for ()'s in sp->image */ rule_rsater(), /* horizontal or vertical lines */ line_raster(); /* line between two points */ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- determine whether constructing height or width --- */ if ( height < 0 ) /* negative "height" signals width */ { width = height = (-height); /* flip height positive */ iswidth = 1; } /* set flag for width */ /* --- set default width (or height) accordingly --- */ if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio; else width = (height+(aspectratio+1)/2)/aspectratio; if ( strchr(symbol,'=') != NULL ) /* left or right || bracket wanted */ width = max2(width,5); /* need space between two |'s */ /* --- allocate and initialize subraster for constructed delimiter --- */ if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize delimiter subraster parameters --- */ sp->type = IMAGERASTER; /* image */ sp->symdef = NULL; /* not applicable for image */ sp->baseline = height/2 + 2; /* is a little above center good? */ sp->size = NORMALSIZE; /* size (probably unneeded) */ rasp = sp->image; /* pointer to image in subraster */ /* ------------------------------------------------------------------------- ( ) parens -------------------------------------------------------------------------- */ if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */ || (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */ { circle_raster ( rasp, /* embedded raster image */ 0, 0, /* row0,col0 are upper-left corner */ height-1, width-1, /* row1,col1 are lower-right */ thickness, /* line thickness is 1 pixel */ (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */ isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-() paren wanted) --- */ /* ------------------------------------------------------------------------- [ ] brackets -------------------------------------------------------------------------- */ else if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */ || (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */ { /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */ thickness = 2; /* set lines two pixels thick */ rule_raster(rasp, 0,0, width,thickness, 0); /* top horizontal bar */ rule_raster(rasp, height-thickness,0, width,thickness, 0); /* bottom */ if ( lp != NULL ) /* left [ bracket wanted */ rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */ if ( rp != NULL ) /* right ] bracket wanted */ rule_raster(rasp, 0,width-thickness, thickness,height, 0); /* right */ isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-[] bracket wanted) --- */ /* ------------------------------------------------------------------------- < > brackets -------------------------------------------------------------------------- */ else if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */ || (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */ { /* --- line_raster ( rasp, row0, col0, row1, col1, thickness ) --- */ thickness = 1; /* set line pixel thickness */ if ( lp != NULL ) /* left < bracket wanted */ { line_raster(rasp,height/2,0,0,width-1,thickness); line_raster(rasp,height/2,0,height-1,width-1,thickness); } if ( rp != NULL ) /* right > bracket wanted */ { line_raster(rasp,height/2,width-1,0,0,thickness); line_raster(rasp,height/2,width-1,height-1,0,thickness); } isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-<> bracket wanted) --- */ /* ------------------------------------------------------------------------- \- for | | brackets or \= for || || brackets -------------------------------------------------------------------------- */ else if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */ || (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */ || (rp=strchr(symbol,'=')) != NULL ) /* left or right || bracket wanted */ { /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */ int midcol = width/2; /* middle col, left of mid if even */ if ( lp != NULL /* left or right | bracket wanted */ || lp2 != NULL ) /* ditto for synomym */ { thickness = 1; /* set | two pixels thick */ rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/ if ( rp != NULL ) /* right ] bracket wanted */ { thickness = 1; /* each | of || one pixel thick */ rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */ rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); } isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-[] bracket wanted) --- */ /* ------------------------------------------------------------------------- back to caller -------------------------------------------------------------------------- */ end_of_job: if ( !isokay ) /* don't have requested delimiter */ { delete_subraster(sp); /* so free unneeded structure */ sp = NULL; } /* and signal error to caller */ return ( sp ); /*back to caller with delim or NULL*/ } /* --- end-of-function make_delim() --- */ /* ========================================================================== * Function: texchar ( expression, chartoken ) * Purpose: scans expression, returning either its first character, * or the next \sequence if that first char is \, * and a pointer to the first expression char past that. * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing valid LaTeX expression * to be scanned * chartoken (O) char * to null-terminated string returning * either the first (non-whitespace) character * of expression if that char isn't \, or else * the \ and everything following it up to * the next non-alphabetic character (but at * least one char following the \ even if * it's non-alpha) * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to the first char of expression * past returned chartoken, * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o Does *not* skip leading whitespace, but simply * returns any whitespace character as the next character. * o \left( is returned as \(, and \right) is returned as \). * And similarly for [] and <> and {} delimiters. * Actually, and non-alpha character, say ?, following * either \left or \right is returned as \? . * ======================================================================= */ /* --- entry point --- */ char *texchar ( char *expression, char *chartoken ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int esclen = 0; /*length of escape sequence*/ char *chartoken0 = chartoken; /* save start of chartoken */ int iprefix = 0; /* prefix index */ static char *prefixes[] = /*e.g., \big followed by ( */ { "\\big", "\\Big", "\\bigg", "\\Bigg", "\\left", "\\right", NULL }; /* ------------------------------------------------------------------------- just return the next char if it's not \ -------------------------------------------------------------------------- */ /* --- error check for end-of-string --- */ *chartoken = '\000'; /* init in case of error */ if ( expression == NULL ) return(NULL); /* nothing to scan */ if ( *expression == '\000' ) return(NULL); /* nothing to scan */ /* --- always returning first character (either \ or some other char) --- */ *chartoken++ = *expression++; /* here's first character */ /* --- if first char isn't \, then just return it to caller --- */ if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */ { *chartoken = '\000'; /* add a null terminator */ goto end_of_job; } /* ptr past returned char */ if ( *expression == '\000' ) /* \ is very last char */ { *chartoken0 = '\000'; /* flush bad \ */ return(NULL); } /* and signal end-of-job */ /* ------------------------------------------------------------------------- we have an escape sequence, so return all alpha chars following \ -------------------------------------------------------------------------- */ /* --- accumulate chars until first non-alpha char found --- */ for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */ *chartoken++ = *expression++; /*copy alpha char, bump ptrs*/ /* --- if we have a prefix, append next (non-alpha) char, e.g., \big( --- */ *chartoken = '\000'; /* set null for comparison */ for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */ if ( strcmp(chartoken0,prefixes[iprefix]) == 0 ) /* have an exact match */ { *chartoken++ = *expression++; /* so append next char */ esclen++; /* bump escape length */ break; } /* stop checking prefixes */ /* --- every \ must be followed by at least one char, e.g., \[ --- */ if ( esclen < 1 ) /* \ followed by non-alpha */ *chartoken++ = *expression++; /*copy non-alpha, bump ptrs*/ else /* normal alpha \sequence */ { /* --- respect spaces in text mode, except first space after \escape --- */ if ( istext>0 ) /* in \rm or \it text mode */ if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */ expression++; /* so flush delim */ } *chartoken = '\000'; /* null-terminate token */ /* ------------------------------------------------------------------------- Translate \leftX or \rightX to just \X for X in ()[]<>{} -------------------------------------------------------------------------- */ if ( 0 ) /* --- not yet implemented properly --- */ if ( strcmp(chartoken0,"\\left") == 0 /* have \left */ || strcmp(chartoken0,"\\right") == 0 ) /* or have \right */ { skipwhite(expression); /* skip leading whitespace */ chartoken0[1] = *expression++; /*and return only next char*/ chartoken0[2] = '\000'; } /* null-terminated */ /* --- back to caller --- */ end_of_job: if ( msgfp!=NULL && msglevel>=999 ) fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken0); return ( expression ); /*ptr to 1st non-alpha char*/ } /* --- end-of-function texchar() --- */ /* ========================================================================== * Function: texsubexpr (expression,subexpr,left,right,isescape,isdelim) * Purpose: scans expression, returning everything between a balanced * left{...right} subexpression if the first non-whitespace * char of expression is an (escaped or unescaped) left{, * or just the next texchar() otherwise, * and a pointer to the first expression char past that. * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing valid LaTeX expression * to be scanned * subexpr (O) char * to null-terminated string returning * either everything between a balanced {...} * subexpression if the first char is {, * or the next texchar() otherwise. * left (I) char * specifying allowable left delimiters * that begin subexpression, e.g., "{[(<" * right (I) char * specifying matching right delimiters * in the same order as left, e.g., "}])>" * isescape (I) int controlling whether escaped and/or * unescaped left,right are matched; * see isbrace() comments below for details. * isdelim (I) int containing true (non-zero) to return * the leading left and trailing right delims * (if any were found) along with subexpr, * or containing false=0 to return subexpr * without its delimiters * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to the first char of expression * past returned subexpr (see Notes), * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o If subexpr is of the form left{...right}, * the outer {}'s are returned as part of subexpr * if isdelim is true; if isdelim is false the {}'s aren't * returned. In either case the returned pointer is * *always* bumped past the closing right}, even if * that closing right} isn't returned in subexpr. * o If subexpr is not of the form left{...right}, * the returned pointer is on the character immediately * following the last character returned in subexpr * o \. acts as LaTeX \right. and matches any \left( * And it also acts as a LaTeX \left. and matches any \right) * ======================================================================= */ /* --- entry point --- */ char *texsubexpr ( char *expression, char *subexpr, char *left, char *right, int isescape, int isdelim ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texchar(); /*next char (or \sequence) from expression*/ char *leftptr, leftdelim[32] = "(\000", /* left( found in expression */ rightdelim[32] = ")\000"; /* and matching right) */ int gotescape = 0, /* true if leading char of expression is \ */ prevescape = 0; /* while parsing, true if preceding char \ */ int isbrace(); /* check for left,right braces */ int isleftdot = 0; /* true if left brace is a \. */ int nestlevel = 1; /* current # of nested braces */ /* ------------------------------------------------------------------------- skip leading whitespace and just return the next char if it's not { -------------------------------------------------------------------------- */ /* --- skip leading whitespace and error check for end-of-string --- */ *subexpr = '\000'; /* init in case of error */ if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/ skipwhite(expression); /* leading whitespace gone */ if ( *expression == '\000' ) return(NULL); /* nothing left to scan */ /* --- check for escape --- */ if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */ gotescape = 1; /* so set flag accordingly */ /* --- if first char isn't left{ or script, just return it to caller --- */ if ( !isbrace(expression,left,isescape) ) /* not a left{ */ if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */ return ( texchar(expression,subexpr) ); /* next char to caller */ else /* --- kludge for super/subscripts to accommodate texscripts() --- */ { *subexpr++ = *expression; /* signal script */ *subexpr = '\000'; /* null-terminate subexpr */ return ( expression ); } /* leave script in stream */ /* --- extract left and find matching right delimiter --- */ *leftdelim = *(expression+gotescape); /* the left( in expression */ if ( gotescape && *leftdelim == '.' ) /* we have a left \. */ { isleftdot = 1; /* so just set flag */ *leftdelim = '\000'; } /* and reset leftdelim */ else /* find matching \right */ if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */ *rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */ else /* can't happen -- pgm bug */ return ( NULL ); /*just signal eoj to caller*/ /* ------------------------------------------------------------------------- accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0 -------------------------------------------------------------------------- */ /* --- first initialize by bumping past left{ or \{ --- */ if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/ else expression++; /* always bump past left{ */ if ( gotescape ) /*need to bump another char*/ if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */ else expression++; /* else just bump past it */ /* --- search for matching right} --- */ while ( 1 ) /*until balanced right} */ { /* --- error check for end-of-string --- */ if ( *expression == '\000' ) /* premature end-of-string */ { if ( isdelim ) /* generate fake right */ if ( gotescape ) /* need escaped right */ { *subexpr++ = '\\'; /* set escape char */ *subexpr++ = '.'; } /* and fake \right. */ else /* escape not wanted */ *subexpr++ = *rightdelim; /* so fake actual right */ *subexpr = '\000'; /* null-terminate subexpr */ return ( expression ); } /* back with final token */ /* --- check preceding char for escape --- */ if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */ prevescape = 1-prevescape; /* so flip escape flag */ else prevescape = 0; /* or turn flag off */ /* --- check for { and } (un/escaped as per leading left) --- */ if ( gotescape == prevescape ) /* escaped iff leading is */ { /* --- check for (closing) right delim and see if we're done --- */ if ( isthischar(*expression,rightdelim) /* found a right} */ || (isleftdot && isthischar(*expression,right)) /*\left. matches all*/ || (prevescape && isthischar(*expression,".")) ) /*or found \right. */ if ( --nestlevel < 1 ) /*\right balances 1st \left*/ { if ( isdelim ) /*caller wants } in subexpr*/ *subexpr++ = *expression; /* so end subexpr with } */ else /*check for \ before right}*/ if ( prevescape ) /* have unwanted \ */ *(subexpr-1) = '\000'; /* so replace it with null */ *subexpr = '\000'; /* null-terminate subexpr */ return ( expression+1 ); } /* back with char after } */ /* --- check for (another) left{ --- */ if ( isthischar(*expression,leftdelim) /* found another left{ */ || (isleftdot && isthischar(*expression,left)) ) /* any left{ */ nestlevel++; } /* --- end-of-if(gotescape==prevescape) --- */ /* --- not done, so copy char to subexpr and continue with next char --- */ *subexpr++ = *expression++; /* copy char and bump ptrs */ } /* --- end-of-while(1) --- */ } /* --- end-of-function texsubexpr() --- */ /* ========================================================================== * Function: texscripts ( expression, subscript, superscript, which ) * Purpose: scans expression, returning subscript and/or superscript * if expression is of the form _x^y or ^{x}_{y}, * or any (valid LaTeX) permutation of the above, * and a pointer to the first expression char past "scripts" * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing valid LaTeX expression * to be scanned * subscript (O) char * to null-terminated string returning * subscript (without _), if found, or "\000" * superscript (O) char * to null-terminated string returning * superscript (without ^), if found, or "\000" * which (I) int containing 1 for subscript only, * 2 for superscript only, >=3 for either/both * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to the first char of expression * past returned "scripts" (unchanged * except for skipped whitespace if * neither subscript nor superscript found), * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o an input expression like ^a^b_c will return superscript="b", * i.e., totally ignoring all but the last "script" encountered * ======================================================================= */ /* --- entry point --- */ char *texscripts ( char *expression, char *subscript, char *superscript, int which ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(); /* next subexpression from expression */ int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */ /* ------------------------------------------------------------------------- init "scripts" and skip leading whitespace -------------------------------------------------------------------------- */ /* --- skip leading whitespace and check for end-of-string --- */ *subscript = *superscript = '\000'; /* init in case no scripts */ skipwhite(expression); /* leading whitespace gone */ if ( *expression == '\000' ) return(expression); /* nothing left to scan */ /* ------------------------------------------------------------------------- get subscript and/or superscript from expression -------------------------------------------------------------------------- */ while ( expression != NULL ) if ( isthischar(*expression,SUBSCRIPT) /* found _ */ && (which==1 || which>2 ) ) /* and caller wants it */ { if ( gotsub /* found 2nd subscript */ || subscript == NULL ) break; /* or no subscript buffer */ gotsub = 1; /* set subscript flag */ expression = texsubexpr(expression+1,subscript,"{","}",0,0); } else /* no _, check for ^ */ if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */ && which>=2 ) /* and caller wants it */ { if ( gotsup /* found 2nd superscript */ || superscript == NULL ) break; /* or no superscript buffer*/ gotsup = 1; /* set superscript flag */ expression = texsubexpr(expression+1,superscript,"{","}",0,0); } else /* neither _ nor ^ */ return ( expression ); /*return ptr past "scripts"*/ return ( expression ); } /* --- end-of-function texscripts() --- */ /* ========================================================================== * Function: isbrace ( expression, braces, isescape ) * Purpose: checks leading char(s) of expression for a brace, * either escaped or unescaped depending on isescape, * except that { and } are always matched, if they're * in braces, regardless of isescape. * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing a valid LaTeX expression * whose leading char(s) are checked for braces * that begin subexpression, e.g., "{[(<" * braces (I) char * specifying matching brace delimiters * to be checked for, e.g., "{[(<" or "}])>" * isescape (I) int containing 0 to match only unescaped * braces, e.g., (...) or {...}, etc, * or containing 1 to match only escaped * braces, e.g., \(...\) or \[...\], etc, * or containing 2 to match either. * But note: if {,} are in braces * then they're *always* matched whether * escaped or not, regardless of isescape. * -------------------------------------------------------------------------- * Returns: ( int ) 1 if the leading char(s) of expression * is a brace, or 0 if not. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ int isbrace ( char *expression, char *braces, int isescape ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int gotescape = 0, /* true if leading char is an escape */ gotbrace = 0; /*true if first non-escape char is a brace*/ /* ------------------------------------------------------------------------- check for brace -------------------------------------------------------------------------- */ /* --- first check for end-of-string --- */ if ( *expression == '\000' ) return(0); /* nothing to check */ /* --- check leading char for escape --- */ if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */ { gotescape = 1; /* so set flag accordingly */ expression++; } /* and bump past escape */ /* --- check (maybe next char) for brace --- */ if ( isthischar(*expression,braces) ) /* expression is braced */ gotbrace = 1; /* so set flag accordingly */ if ( gotescape && *expression == '.' ) /* \. matches any brace */ gotbrace = 1; /* set flag */ /* --- check for TeX brace { or } --- */ if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/ if ( isescape ) isescape = 2; /* reset escape flag */ /* ------------------------------------------------------------------------- back to caller -------------------------------------------------------------------------- */ if ( gotbrace && /* found a brace */ ( isescape==2 || /* escape irrelevant */ gotescape==isescape ) /* un/escaped as requested */ ) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */ } /* --- end-of-function isbrace() --- */ /* ========================================================================== * Function: preamble ( expression, size, subexpr ) * Purpose: parses $-terminated preamble, if present, at beginning * of expression, re-setting size if necessary, and * returning any other parameters besides size in subexpr. * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing LaTeX expression possibly * preceded by $-terminated preamble * size (I/O) int * containing 0-4 default font size, * and returning size modified by first * preamble parameter (or unchanged) * subexpr(O) char * returning any remaining preamble * parameters past size * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to first char past preamble in expression * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o size can be any number >=0. If preceded by + or -, it's * interpreted as an increment to input size; otherwise * it's interpreted as the size. * o if subexpr is passed as NULL ptr, then returned expression * ptr will have "flushed" and preamble parameters after size * ======================================================================= */ /* --- entry point --- */ char *preamble ( char *expression, int *size, char *subexpr ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char pretext[512], *prep=expression, /*pream from expression, ptr into it*/ *dollar, *comma; /* preamble delimiters */ int prelen = 0, /* preamble length */ sizevalue = 0, /* value of size parameter */ issize = 0, /* true if leading size present */ isdelta = 0; /*true to increment passed size arg*/ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ if ( subexpr != NULL ) /* caller passed us an address */ *subexpr = '\000'; /* so init assuming no preamble */ /* ------------------------------------------------------------------------- process preamble if present -------------------------------------------------------------------------- */ if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */ != NULL ) /* found embedded $ */ if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/ > 0 ) /* must have preamble preceding $ */ { memcpy(pretext,expression,prelen); /* local copy of preamble */ pretext[prelen] = '\000'; /* null-terminated */ if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/ && strchr(pretext,'{') == NULL ) /*shouldn't be a left{ in preamble*/ { /* --- skip any leading whitespace --- */ prep = pretext; /* start at beginning of preamble */ skipwhite(prep); /* skip any leading white space */ /* --- check for embedded , or leading +/- (either signalling size) --- */ if ( isthischar(*prep,"+-") ) /* have leading + or - */ isdelta = 1; /* so use size value as increment */ comma = strchr(pretext,','); /* , signals leading size param */ /* --- process leading size parameter if present --- */ if ( comma != NULL /* size param explicitly signalled */ || isdelta || isdigit(*prep) ) /* or inferred implicitly */ { /* --- parse size parameter and reset size accordingly --- */ if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/ sizevalue = atoi(prep); /* convert size string to integer */ if ( size != NULL ) /* caller passed address for size */ *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */ /* --- finally, set flag and shift size parameter out of preamble --- */ issize = 1; /* set flag showing size present */ if ( comma != NULL ) strncpy(pretext,comma+1,512);/*leading size param gone*/ } /* --- end-of-if(comma!=NULL||etc) --- */ /* --- copy any preamble params following size to caller's subexpr --- */ if ( comma != NULL || !issize ) /*preamb contains params past size*/ if ( subexpr != NULL ) /* caller passed us an address */ strcpy(subexpr,pretext); /*so return extra params to caller*/ /* --- finally, set prep to shift preamble out of expression --- */ prep = expression + prelen+1; /* set prep past $ in expression */ } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */ } /* --- end-of-if(dollar!=NULL)/if(prelen>0) --- */ /* ------------------------------------------------------------------------- back to caller -------------------------------------------------------------------------- */ return ( prep ); /*expression, or ptr past preamble*/ } /* --- end-of-function preamble() --- */ /* ========================================================================== * Function: mimeprep ( expression ) * Purpose: preprocessor for mimeTeX input, e.g., * (a) removes comments, * (b) converts \left( to \( and \right) to \), * (c) xlates &html; special chars to equivalent latex * Should only be called once (after unescape_url()) * -------------------------------------------------------------------------- * Arguments: expression (I/O) char * to first char of null-terminated * string containing mimeTeX/LaTeX expression, * and returning preprocessed string * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to input expression, * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ char *mimeprep ( char *expression ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *expptr=expression, /* ptr within expression */ *tokptr=NULL; /*ptr to token found in expression*/ char *strchange(); /* change leading chars of string */ char *findbraces(); /*find left { and right } for \atop*/ int idelim=0, /* left- or right-index */ isymbol=0; /*symbols[],rightcomment[],etc index*/ /* --- * comments * -------- */ char *leftptr=NULL; /* find leftcomment in expression */ static char *leftcomment = "%%", /* open comment */ *rightcomment[] = {"\n", "%%", NULL}; /* close comments */ /* --- * special long (more than 1-char) \left and \right delimiters * ----------------------------------------------------------- */ static char *leftfrom[] = /* xlate any \left suffix... */ { "\\|", /* \left\| */ "\\{", /* \left\{ */ "\\langle", /* \left\langle */ NULL } ; /* --- end-of-leftfrom[] --- */ static char *leftto[] = /* ...to this instead */ { "=", /* = */ "{", /* { */ "<", /* < */ NULL } ; /* --- end-of-leftto[] --- */ static char *rightfrom[] = /* xlate any \right suffix... */ { "\\|", /* \right\| */ "\\}", /* \right\} */ "\\rangle", /* \right\rangle */ NULL } ; /* --- end-of-rightfrom[] --- */ static char *rightto[] = /* ...to this instead */ { "=", /* = */ "}", /* } */ ">", /* > */ NULL } ; /* --- end-of-rightto[] --- */ /* --- * { \atop }-like commands * ----------------------- */ char *atopsym=NULL; /* atopcommands[isymbol] */ static char *atopcommands[] = /* list of {a+b\command c+d}'s */ { "\\over", /* plain tex for \frac */ #ifndef NOATOP /*noatop preserves old mimeTeX rule*/ "\\atop", #endif NULL } ; /* --- end-of-atopcommands[] --- */ /* --- * html special/escape chars converted to latex equivalents * -------------------------------------------------------- */ char *htmlsym=NULL; /* symbols[isymbol].html */ static struct { char *html; char *term; char *latex; } symbols[] = { /* --------------------------------------- html termchar LaTeX equivalent... --------------------------------------- */ #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */ #include NEWCOMMANDS #endif { """, ";", "\"" }, /* " is first, " */ { "&", ";", "&" }, { "<", ";", "<" }, { ">", ";", ">" }, { " ", ";", "~" }, { "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" }, { "¦", ";", "|" }, { "±", ";", "\\pm" }, { "²", ";", "{{}^2}" }, { "³", ";", "{{}^3}" }, { "µ", ";", "\\mu" }, { "¹", ";", "{{}^1}" }, { "¼", ";", "{\\frac14}" }, { "½", ";", "{\\frac12}" }, { "¾", ";", "{\\frac34}" }, { "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" }, { "Â", ";", "{\\rm~\\hat~A}" }, { "Ã", ";", "{\\rm~\\tilde~A}" }, { "Ä", ";", "{\\rm~\\ddot~A}" }, { "Å", ";", "{\\rm~A\\limits^{-1$o}}" }, { "ã", ";", "{\\rm~\\tilde~a}" }, { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */ /* --------------------------------------- LaTeX termchar mimeTeX equivalent... --------------------------------------- */ { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" }, { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" }, { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" }, { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" }, { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" }, { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"}, { "\\notin", NULL, "{\\not\\in}" }, { "\\neq", NULL, "{\\not=}" }, { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" }, { "\\cr", NULL, "\\\\" }, { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" }, { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" }, /* --------------------------------------- LaTeX Constant termchar value... --------------------------------------- */ { "\\thinspace", NULL, "2" }, { "\\thinmathspace", NULL, "2" }, { NULL, NULL, NULL } } ; /* --- end-of-symbols[] --- */ /* ------------------------------------------------------------------------- first remove comments -------------------------------------------------------------------------- */ expptr = expression; /* start search at beginning */ while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/ { char *rightsym=NULL; /* rightcomment[isymbol] */ expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */ /* --- check for any closing rightcomment, in given precedent order --- */ if ( *expptr != '\000' ) /*have chars after this leftcomment*/ for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++) if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/ { tokptr += strlen(rightsym); /* first char after rightcomment */ if ( *tokptr == '\000' ) /*nothing after this rightcomment*/ { *leftptr = '\000'; /*so terminate expr at leftcomment*/ break; } /* and stop looking for comments */ *leftptr = '~'; /* replace entire comment by ~ */ strcpy(leftptr+1,tokptr); /* and squeeze out comment */ goto next_comment; } /* stop looking for rightcomment */ /* --- no rightcomment after opening leftcomment --- */ *leftptr = '\000'; /* so terminate expression */ /* --- resume search past squeezed-out comment --- */ next_comment: if ( *leftptr == '\000' ) break; /* reached end of expression */ expptr = leftptr+1; /*resume search after this comment*/ } /* --- end-of-while(leftptr!=NULL) --- */ /* ------------------------------------------------------------------------- convert \left( to \( and \right) to \), etc. -------------------------------------------------------------------------- */ for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left and 1 for \right */ { char *lrstr = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */ int lrlen = (idelim==0?5:6); /* strlen() of \left or \right */ char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{*/ **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */ **lrto = (idelim==0?leftto:rightto), /* xlated to 1-char like = */ *lrsym = NULL; /* lrfrom[isymbol] */ expptr = expression; /* start search at beginning */ while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */ { if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/ { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/ expptr = tokptr+2; } /* and resume search past brace */ else /* may be a "long" brace like \| */ { expptr = tokptr+lrlen; /*init to resume search past\left\rt*/ for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++) { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */ if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/ { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */ *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/ expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/ break; } /* no need to check more lrsym's */ } /* --- end-of-for(isymbol) --- */ } /* --- end-of-if/else(isthischar()) --- */ } /* --- end-of-while(tokptr!=NULL) --- */ } /* --- end-of-for(idelim) --- */ /* ------------------------------------------------------------------------- run thru table, converting all occurrences of each html escape to latex equiv -------------------------------------------------------------------------- */ for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++) { int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */ char *htmlterm = symbols[isymbol].term, /* optional htmlsym terminators */ *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/ expptr = expression; /* re-start search at beginning */ while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */ { char termchar = *(tokptr+htmllen); /* char terminating html sequence */ int escapelen = htmllen; /* total length of escape sequence */ if ( htmlterm != NULL ) /* sequence may have terminator */ escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/ if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/ { expptr = tokptr+htmllen; /* just resume search after prefix */ continue; } /* but don't replace it */ if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */ && !isthischar(*htmlsym,"&") ) /* and not an &html; special char */ if ( tokptr != expression ) /* then if we're past beginning */ if ( isthischar(*(tokptr-1),ESCAPE) ) /*and if inline symbol escaped*/ { expptr = tokptr+escapelen; /*just resume search after literal*/ continue; } /* but don't replace it */ strchange(escapelen,tokptr,latexsym); /* replace html symbol */ expptr = tokptr + strlen(latexsym); } /* and resume search after it */ } /* --- end-of-for(isymbol) --- */ /* ------------------------------------------------------------------------- run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d} -------------------------------------------------------------------------- */ for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++) { int atoplen = strlen(atopsym); /* #chars in \atop */ expptr = expression; /* re-start search at beginning */ while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */ { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/ char termchar = *(tokptr+atoplen); /* \atop followed by terminator */ if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/ { expptr = tokptr+atoplen; /* just resume search after prefix */ continue; } /* but don't process it */ if ( (leftbrace=findbraces(expression,tokptr))==NULL /*find left {*/ || (rightbrace=findbraces(NULL,tokptr+atoplen-1))==NULL ) /*right }*/ expptr += atoplen; /* skip command if didn't find */ else /* we have bracketed { \atop } */ { int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */ rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/ totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/ char arg[8192], command[8192]; /* left/right args, new \atop{}{} */ strcpy(command,atopsym); /* start command with \atop */ memcpy(arg,leftbrace,leftlen+1); /* extract left-hand arg with { */ arg[leftlen+1] = '\000'; /* and null terminate it */ strcat(command,arg); /* concatanate {left-arg to \atop */ strcat(command,"}{"); /* close left-arg, open right-arg */ memcpy(arg,tokptr+atoplen,rightlen+1); /* right-hand arg with } */ arg[rightlen+1] = '\000'; /* and null terminate it */ if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */ strcpy(arg,arg+1); /* so squeeze it out */ strcat(command,arg); /* concatanate right-arg} */ strchange(totlen,leftbrace,command); /* { \atop } --> \atop{}{} */ expptr = leftbrace + totlen; /* resume search after \atop{}{} */ } } /* --- end-of-while(tokptr!=NULL) --- */ } /* --- end-of-for(isymbol) --- */ /* ------------------------------------------------------------------------- back to caller with preprocessed expression -------------------------------------------------------------------------- */ if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */ fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression); return ( expression ); } /* --- end-of-function mimeprep() --- */ /* ========================================================================== * Function: strchange ( int nfirst, char *from, char *to ) * Purpose: Changes the nfirst leading chars of `from` to `to`. * For example, to change char x[99]="12345678" to "123ABC5678" * call strchange(1,x+3,"ABC") * -------------------------------------------------------------------------- * Arguments: nfirst (I) int containing #leading chars of `from` * that will be replace by `to` * from (I/O) char * to null-terminated string whose nfirst * leading chars will be replaced by `to` * to (I) char * to null-terminated string that will * replace the nfirst leading chars of `from` * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to first char of input `from` * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o If strlen(to)>nfirst, from must have memory past its null * (i.e., we don't do a realloc) * ======================================================================= */ /* --- entry point --- */ char *strchange ( int nfirst, char *from, char *to ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */ nshift = abs(tolen-nfirst); /*need to shift from left or right*/ /* ------------------------------------------------------------------------- shift from left or right to accommodate replacement of its nfirst chars by to -------------------------------------------------------------------------- */ if ( tolen < nfirst ) /* shift left is easy */ strcpy(from,from+nshift); /* because memory doesn't overlap */ if ( tolen > nfirst ) /* need more room at start of from */ { char *pfrom = from+strlen(from); /* ptr to null terminating from */ for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */ *(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */ /* ------------------------------------------------------------------------- from has exactly the right number of free leading chars, so just put to there -------------------------------------------------------------------------- */ if ( tolen != 0 ) /* make sure to not empty or null */ memcpy(from,to,tolen); /* chars moved into place */ return ( from ); /* changed string back to caller */ } /* --- end-of-function strchange() --- */ /* ========================================================================== * Function: findbraces ( char *expression, char *command ) * Purpose: If expression!=NULL, finds opening left { preceding command; * if expression==NULL, finds closing right } after command. * For example, to parse out {a+b\over c+d} call findbraces() * twice. * -------------------------------------------------------------------------- * Arguments: expression (I) NULL to find closing right } after command, * or char * to null-terminated string to find * left opening { preceding command. * command (I) char * to null-terminated string whose * first character is usually the \ of \command * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to either opening { or closing }, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ char *findbraces ( char *expression, char *command ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int isopen = (expression==NULL?0:1); /* true to find left opening { */ char *left="{", *right="}", /* delims bracketing {x\command y} */ *delim = (isopen?left:right), /* delim we want, { if isopen */ *match = (isopen?right:left), /* matching delim, } if isopen */ *brace = NULL; /* ptr to delim returned to caller */ int inc = (isopen?-1:+1); /* pointer increment */ int level = 1; /* nesting level, for {{}\command} */ char *ptr = command; /* start search here */ /* ------------------------------------------------------------------------- search for left opening { before command, or right closing } after command -------------------------------------------------------------------------- */ while ( 1 ) /* search for brace, or until end */ { /* --- next char to check for delim --- */ ptr += inc; /* bump ptr left or right */ /* --- check for beginning or end of expression --- */ if ( isopen ) /* going left, check for beginning */ if ( ptr < expression ) break; /* went before start of string */ else if ( *ptr == '\000' ) break; /* went past end of string */ /* --- don't check this char if it's escaped --- */ if ( !isopen || ptr>expression ) /* very first char can't be escaped*/ if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */ continue; /* so don't check this char */ /* --- check for delim --- */ if ( isthischar(*ptr,delim) ) /* found delim */ if ( --level == 0 ) /* and it's not "internally" nested*/ { brace = ptr; /* set ptr to brace */ goto end_of_job; } /* and return it to caller */ /* --- check for matching delim --- */ if ( isthischar(*ptr,match) ) /* found matching delim */ level++; /* so bump nesting level */ } /* --- end-of-while(1) --- */ end_of_job: return ( brace ); /*back to caller with delim or NULL*/ } /* --- end-of-function findbraces() --- */ /* ========================================================================== * Function: rasterize ( expression, size ) * Purpose: returns subraster corresponding to (a valid LaTeX) expression * at font size * -------------------------------------------------------------------------- * Arguments: expression (I) char * to first char of null-terminated * string containing valid LaTeX expression * to be rasterized * size (I) int containing 0-4 default font size * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to expression, * or NULL for any parsing error. * -------------------------------------------------------------------------- * Notes: o This is mimeTeX's "main" reusable entry point. Easy to use: * just call it with a LaTeX expression, and get back a bitmap * of that expression. Then do what you want with the bitmap. * ======================================================================= */ /* --- entry point --- */ subraster *rasterize ( char *expression, int size ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *preamble(), pretext[256]; /* process preamble, if present */ char chartoken[8192], *texsubexpr(), /*get subexpression from expression*/ *subexpr = chartoken; /* token may be parenthesized expr */ int isbrace(); /* check subexpr for braces */ mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/ int natoms=0; /* #atoms/tokens processed so far */ subraster *rasterize(), /* recurse */ *rastparen(), /* handle parenthesized subexpr's */ *rastlimits(); /* handle sub/superscripted expr's */ subraster *rastcat(), /* concatanate atom subrasters */ *subrastcpy(); /* copy final result if a charaster*/ subraster *get_charsubraster(), /* character subraster */ *sp=NULL, *prevsp=NULL, /* raster for current, prev char */ *expraster = (subraster *)NULL; /* raster returned to caller */ int delete_subraster(); /* free everything before returning*/ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* --- global values saved/restored at each recursive iteration --- */ int wastext = istext, /* initial istext mode flag */ wasdisplaymath = isdisplaymath, /* initial displaymath mode flag */ olddisplaysize = displaysize, /* initial displaysize */ *oldworkingparam = workingparam; /* initial working parameter */ subraster *oldworkingbox = workingbox, /* initial working box */ *oldleftexpression = leftexpression; /*left half rasterized so far*/ double oldunitlength = unitlength; /* initial unitlength */ mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ recurlevel++; /* wind up one more recursion level*/ leftexpression = NULL; /* no leading left half yet */ isreplaceleft = 0; /* reset replaceleft flag */ if ( msgfp!=NULL && msglevel >= 29 ) /*display expression for debugging*/ fprintf(msgfp,"rasterize> recursion level=%d, size=%d.\nexpression=\"%s\"\n", recurlevel,size,(expression==NULL?"null":expression)); if ( expression == NULL ) goto end_of_job; /* nothing given to do */ /* ------------------------------------------------------------------------- preocess optional $-terminated preamble preceding expression -------------------------------------------------------------------------- */ expression = preamble(expression,&size,pretext); /* size may be modified */ if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */ displaysize = size; /* start at requested size */ /* ------------------------------------------------------------------------- build up raster one character (or subexpression) at a time -------------------------------------------------------------------------- */ while ( 1 ) { /* --- get next character/token or subexpression --- */ expression = texsubexpr(expression,chartoken,LEFTBRACES,RIGHTBRACES,1,1); subexpr = chartoken; /* "local" copy of chartoken ptr */ leftsymdef = NULL; /* no character identified yet */ sp = NULL; /* no subraster yet */ size = displaysize; /* in case reset by \tiny, etc */ /* --- debugging output --- */ if ( msgfp!=NULL && msglevel >= 999 ) /* display chartoken for debugging */ fprintf(msgfp,"rasterize> recursion level=%d, atom#%d = \"%s\"\n", recurlevel,natoms+1,chartoken); if ( expression == NULL /* no more tokens */ && *subexpr == '\000' ) break; /* and this token empty */ if ( *subexpr == '\000' ) break; /* enough if just this token empty */ /* --- check for parenthesized subexpression --- */ if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */ { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */ == NULL ) continue; } /* flush it if failed to rasterize */ else /* --- single-character atomic token --- */ if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */ { /* --- first look up mathchardef for atomic token in table --- */ if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/ == NULL ) /* lookup failed */ { char literal[512] = "[?]"; /*display for unrecognized literal*/ if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */ fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n", chartoken); sp = (subraster *)NULL; /* init to signal failure */ if ( warninglevel < 1 ) continue; /* warnings not wanted */ if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */ { /* --- so display literal {\rm~[\backslash~chartoken?]} --- */ strcpy(literal,"{\\rm~[\\backslash~"); /* init token */ strcat(literal,chartoken+1); /* add chars following leading \ */ strcat(literal,"?]}"); } /* add closing brace */ if ( (sp = rasterize(literal,size-1)) /* rasterize literal token */ == (subraster *)NULL ) continue; } /*flush token if rasterize fails*/ else /* --- check if we have special handler to process this token --- */ if ( symdef->handler != NULL ) /* have a handler for this token */ { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class; if ( (sp = (subraster *) /* returned void* is subraster* */ (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))== NULL ) continue; } /* flush token if handler failed */ else /* --- no handler, so just get subraster for this character --- */ if ( (sp=get_charsubraster(symdef,size)) /* get subraster */ == NULL ) continue; /* flush token if failed */ } /* --- end-of-if/else ... if/else --- */ /* --- handle any super/subscripts following symbol or subexpression --- */ sp = rastlimits(&expression,size,sp); /* --- debugging output --- */ if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */ { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d%s\n", recurlevel,natoms+1,(sp==NULL?" = null":"...")); if(sp!=NULL) type_raster(sp->image,msgfp); } /* display raster */ /* --- accumulate atom or parenthesized subexpression --- */ if ( natoms < 1 /* nothing previous to concat */ || isreplaceleft ) /* or we're replacing previous */ { expraster = subrastcpy(sp); /* so just copy static CHARASTER */ isreplaceleft = 0; } /* reset replacement flag */ else /*we've already built up atoms so...*/ if ( sp != NULL ) /* ...if we have a new component */ expraster = rastcat(expraster,sp,1); /* concat new one, free previous */ delete_subraster(prevsp); /* free prev (if not a CHARASTER) */ prevsp = sp; /* current becomes previous */ leftexpression = expraster; /* left half rasterized so far */ /* --- bump count --- */ natoms++; /* bump #atoms count */ } /* --- end-of-while(expression!=NULL) --- */ /* ------------------------------------------------------------------------- back to caller with rasterized expression -------------------------------------------------------------------------- */ end_of_job: delete_subraster(prevsp); /* free last (if not a CHARASTER) */ /* --- debugging output --- */ if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */ { fprintf(msgfp,"rasterize> final recursion level=%d, atom#%d...\n", recurlevel,natoms); type_raster(expraster->image,msgfp); } /* display completed raster */ /* --- restore flags/values to original saved values --- */ istext = wastext; /* text mode reset */ isdisplaymath = wasdisplaymath; /* displaymath mode reset */ displaysize = olddisplaysize; /* displaysize reset */ workingparam = oldworkingparam; /* working parameter reset */ workingbox = oldworkingbox; /* working box reset */ leftexpression = oldleftexpression; /* leftexpression reset */ leftsymdef = oldleftsymdef; /* leftsymdef reset */ unitlength = oldunitlength; /* unitlength reset */ recurlevel--; /* unwind one recursion level */ /* --- return final subraster to caller --- */ return ( expraster ); } /* --- end-of-function rasterize() --- */ /* ========================================================================== * Function: rastparen ( subexpr, size, basesp ) * Purpose: parentheses handler, returns a subraster corresponding to * parenthesized subexpression at font size * -------------------------------------------------------------------------- * Arguments: subexpr (I) char ** to first char of null-terminated * string beginning with a LEFTBRACES * to be rasterized * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding leading left{ * (unused, but passed for consistency) * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to subexpr, * or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o This "handler" isn't in the mathchardef symbol table, * but is called directly from rasterize(), as necessary. * o Though subexpr is returned unchanged, it's passed as char ** * for consistency with other handlers. Ditto, basesp is unused * but passed for consistency * ======================================================================= */ /* --- entry point --- */ subraster *rastparen ( char **subexpr, int size, subraster *basesp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *expression = *subexpr; /* dereference subexpr to get char* */ int explen = strlen(expression); /* total #chars, including parens */ int isescape = 0, /* true if parens \escaped */ isrightdot = 0, /* true if right paren is \right. */ isleftdot = 0; /* true if left paren is \left. */ char left[16], right[16]; /* parens enclosing expresion */ char noparens[8192]; /* get subexpr without parens */ subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */ int isheight = 1; /*true=full height, false=baseline*/ int height, /* height of rasterized noparens[] */ baseline; /* and its baseline */ int family = CMEX10; /* family for paren chars */ subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */ subraster *rastcat(); /* concatanate subrasters */ int delete_subraster(); /*in case of error after allocation*/ /* ------------------------------------------------------------------------- rasterize "interior" of expression, i.e., without enclosing parens -------------------------------------------------------------------------- */ /* --- first see if enclosing parens are \escaped --- */ if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */ isescape = 1; /* so set flag accordingly */ /* --- get expression *without* enclosing parens --- */ strcpy(noparens,expression); /* get local copy of expression */ noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */ strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */ /* --- rasterize it --- */ if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/ == NULL ) goto end_of_job; /* quit if failed */ /* --- no need to add parentheses for unescaped { --- */ if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */ goto end_of_job; /* just return sp to caller */ /* ------------------------------------------------------------------------- obtain paren characters to enclose noparens[] raster with -------------------------------------------------------------------------- */ /* --- first get left and right parens from expression --- */ memset(left,0,16); memset(right,0,16); /* init parens with nulls */ left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */ right[0] = *(expression+explen-1); /* right} is always last char */ isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */ isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */ /* --- need height of noparens[] raster as minimum parens height --- */ height = (sp->image)->height; /* height of noparens[] raster */ baseline = sp->baseline; /* baseline of noparens[] raster */ if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */ /* --- get best-fit parentheses characters --- */ if ( !isleftdot ) /* if not \left. */ lp = get_delim(left,height+1,family); /* get left paren char */ if ( !isrightdot ) /* and if not \right. */ rp = get_delim(right,height+1,family); /* get right paren char */ if ( (lp==NULL && !isleftdot) /* check that we got left( */ || (rp==NULL && !isrightdot) ) /* and right) if needed */ { delete_subraster(sp); /* if failed, free subraster */ if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/ if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/ sp = (subraster *)NULL; /* signal error to caller */ goto end_of_job; } /* and quit */ /* ------------------------------------------------------------------------- set paren baselines to center on noparens[] raster, and concat components -------------------------------------------------------------------------- */ /* --- set baselines to center paren chars on raster --- */ if ( lp != NULL ) /* ignore for \left. */ lp->baseline = baseline + ((lp->image)->height - height)/2; if ( rp != NULL ) /* ignore for \right. */ rp->baseline = baseline + ((rp->image)->height - height)/2; /* --- concat lp||sp||rp to obtain final result --- */ if ( lp != NULL ) /* ignore \left. */ sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */ if ( sp != NULL ) /* succeeded or ignored \left. */ if ( rp != NULL ) /* ignore \right. */ sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */ /* --- back to caller --- */ end_of_job: return ( sp ); } /* --- end-of-function rastparen() --- */ /* ========================================================================== * Function: rastlimits ( expression, size, basesp ) * Purpose: \limits, \nolimts, _ and ^ handler, * dispatches call to rastscripts() or to rastdispmath() * as necessary, to handle sub/superscripts following symbol * -------------------------------------------------------------------------- * Arguments: expression (I) char ** to first char of null-terminated * LaTeX expression (unused/unchanged) * size (I) int containing base font size (not used, * just stored in subraster) * basesp (I) subraster * to current character (or * subexpression) immediately preceding script * indicator * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster returned by rastscripts() * or rastdispmath(), or NULL for any error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastlimits ( char **expression, int size, subraster *basesp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/ *rastcat(), /* may need to concat scripts */ *scriptsp = basesp; /* and this will become the result */ int isdisplay = (-1); /*set 1 for displaymath, 0 otherwise*/ /* --- to check for \limits or \nolimits preceding scripts --- */ char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/ int toklen=0; /* strlen(limtoken) */ mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */ int class = (leftsymdef==NULL?(-1):leftsymdef->class); /*base sym class*/ /* ------------------------------------------------------------------------- determine whether or not to use displaymath -------------------------------------------------------------------------- */ scriptlevel++; /* first, increment subscript level*/ *limtoken = '\000'; /* no token yet */ /* --- check for \limits or \nolimits --- */ skipwhite(exprptr); /* skip white space before \limits */ if ( exprptr != NULL ) /* expression ptr supplied */ if ( *exprptr != '\000' ) /* something in expression */ exprptr = texchar(exprptr,limtoken); /* retrieve next token */ if ( *limtoken != '\000' ) /* have token */ if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */ if ( memcmp("\\limits",limtoken,toklen) == 0 /* may be \limits */ || memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */ if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */ != NULL ) /* found token in table */ if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */ isdisplay = 1; /* so explicitly set displaymath */ else /* wasn't \limits */ if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */ isdisplay = 0; /* so explicitly reset displaymath */ /* --- see if we found \[no]limits --- */ if ( isdisplay != (-1) ) /* explicit directive found */ *expression = exprptr; /* so bump expression past it */ else /* noexplicit directive */ { isdisplay = 0; /* init displaymath flag off */ if ( isdisplaymath ) /* we're in displaymath mode */ if ( isdisplaymath >= 2 ) /* and mode forced true */ { if ( class!=VARIABLE && class!=ORDINARY ) /*don't force variables*/ isdisplay = 1; } /* so set flag */ else /* determine mode from base symbol */ if ( class == DISPOPER ) /*it's a displaymath operator*/ isdisplay = 1; } /* so set flag */ /* ------------------------------------------------------------------------- dispatch call to create sub/superscripts -------------------------------------------------------------------------- */ if ( isdisplay ) /* scripts above/below base symbol */ scriptsp = rastdispmath(expression,size,basesp); /* everything all done */ else /* scripts alongside base symbol */ if ( (scriptsp=rastscripts(expression,size,basesp)) == NULL ) /*no scripts*/ scriptsp = basesp; /* so just return unscripted symbol*/ else /* symbols followed by scripts */ if ( basesp != NULL ) /* have base symbol */ scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base symbol*/ end_of_job: if ( msgfp!=NULL && msglevel>=99 ) { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n", scriptlevel,(scriptsp==NULL?"null":"...")); if ( scriptsp != NULL ) /* have a constructed raster */ type_raster(scriptsp->image,msgfp); } /*display constructed raster*/ scriptlevel--; /*lastly, decrement subscript level*/ return ( scriptsp ); } /* --- end-of-function rastlimits() --- */ /* ========================================================================== * Function: rastscripts ( expression, size, basesp ) * Purpose: super/subscript handler, returns subraster for the leading * scripts in expression, whose base symbol is at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string beginning with a super/subscript, * and returning ptr immediately following * last script character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding leading script * (scripts will be placed relative to base) * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to scripts, * or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o This "handler" isn't in the mathchardef symbol table, * but is called directly from rasterize(), as necessary. * ======================================================================= */ /* --- entry point --- */ subraster *rastscripts ( char **expression, int size, subraster *basesp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texscripts(), /* parse expression for scripts */ subscript[512], supscript[512]; /* scripts parsed from expression */ subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */ subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */ *rastack(); /*sets scripts in displaymath mode*/ raster *rp=NULL; /* image raster embedded in sp */ int height=0, width=0, baseline=0, /* height,width,baseline of sp */ subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */ supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */ baseht=0, baseln=0; /* height,baseline of base */ int bdescend=0, sdescend=0; /* descender of base, subscript */ int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */ isbase=0; /* true if we have base symbol */ int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */ vbetween = 2, /* vertical space between scripts */ vabove = szval+1, /*sup's top/bot above base's top/bot*/ vbelow = szval+1, /*sub's top/bot below base's top/bot*/ vbottom = szval+1, /*sup's bot above (sub's below) bsln*/ istweak = 1; /* true to tweak script positioning */ int rastput(); /*put scripts in constructed raster*/ int delete_subraster(); /* free work areas */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* ------------------------------------------------------------------------- Obtain subscript and/or superscript expressions, and rasterize them/it -------------------------------------------------------------------------- */ /* --- parse for sub,superscript(s), and bump expression past it(them) --- */ if ( expression == NULL ) goto end_of_job; /* no *ptr given */ if ( *expression == NULL ) goto end_of_job; /* no expression given */ if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */ *expression = texscripts(*expression,subscript,supscript,3); /* --- rasterize scripts --- */ if ( *subscript != '\000' ) /* have a subscript */ subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */ if ( *supscript != '\000' ) /* have a superscript */ supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */ /* --- set flags for convenience --- */ issub = (subsp != (subraster *)NULL); /* true if we have subscript */ issup = (supsp != (subraster *)NULL); /* true if we have superscript */ isboth = (issub && issup); /* true if we have both */ if (!issub && !issup) goto end_of_job; /* quit if we have neither */ /* ------------------------------------------------------------------------- get height, width, baseline of scripts, and height, baseline of base symbol -------------------------------------------------------------------------- */ /* --- get height and width of components --- */ if ( issub ) /* we have a subscript */ { subht = (subsp->image)->height; /* so get its height */ subwidth = (subsp->image)->width; /* and width */ subln = subsp->baseline; } /* and baseline */ if ( issup ) /* we have a superscript */ { supht = (supsp->image)->height; /* so get its height */ supwidth = (supsp->image)->width; /* and width */ supln = supsp->baseline; } /* and baseline */ /* --- get height and baseline of base, and descender of base and sub --- */ if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */ basesp = leftexpression; /* try using left side thus far */ if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */ { baseht = (basesp->image)->height; /* height of base symbol */ baseln = basesp->baseline; /* and its baseline */ bdescend = baseht-(baseln+1); /* and base symbol descender */ sdescend = bdescend + vbelow; /*sub must descend by at least this*/ if ( baseht > 0 ) isbase = 1; } /* set flag */ /* ------------------------------------------------------------------------- determine width of constructed raster -------------------------------------------------------------------------- */ width = max2(subwidth,supwidth); /*widest component is overall width*/ /* ------------------------------------------------------------------------- determine height and baseline of constructed raster -------------------------------------------------------------------------- */ /* --- both super/subscript --- */ if ( isboth ) /*we have subscript and superscript*/ { height = max2(subht+vbetween+supht, /* script heights + space bewteen */ vbelow+baseht+vabove); /*sub below base bot, sup above top*/ baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/ /* --- superscript only --- */ if ( !issub ) /* we only have a superscript */ { height = max3(baseln+1+vabove, /* sup's top above base symbol top */ supht+vbottom, /* sup's bot above baseln */ supht+vabove-bdescend); /* sup's bot above base symbol bot */ baseline = height-1; } /*sup's baseline at bottom of raster*/ /* --- subscript only --- */ if ( !issup ) /* we only have a subscript */ if ( subht > sdescend ) /*sub can descend below base bot...*/ { height = subht; /* ...without extra space on top */ baseline = height-(sdescend+1); /* sub's bot below base symbol bot */ baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/ else /* sub's top will be below baseln */ { height = sdescend+1; /* sub's bot below base symbol bot */ baseline = 0; } /* sub's baseline at top of raster */ /* ------------------------------------------------------------------------- construct raster with superscript over subscript -------------------------------------------------------------------------- */ /* --- allocate subraster containing constructed raster --- */ if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/ == NULL ) /* and if we fail to allocate */ goto end_of_job; /* quit */ /* --- initialize subraster parameters --- */ sp->type = IMAGERASTER; /* set type as constructed image */ sp->size = size; /* set given size */ sp->baseline = baseline; /* composite scripts baseline */ rp = sp->image; /* raster embedded in subraster */ /* --- place super/subscripts in new raster --- */ if ( issup ) /* we have a superscript */ rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */ if ( issub ) /* we have a subscript */ rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/ /* ------------------------------------------------------------------------- free unneeded component subrasters and return final result to caller -------------------------------------------------------------------------- */ end_of_job: if ( issub ) delete_subraster(subsp); /* free unneeded subscript */ if ( issup ) delete_subraster(supsp); /* and superscript */ return ( sp ); } /* --- end-of-function rastscripts() --- */ /* ========================================================================== * Function: rastdispmath ( expression, size, sp ) * Purpose: displaymath handler, returns sp along with * its immediately following super/subscripts * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following sp to be * rasterized along with its super/subscripts, * and returning ptr immediately following last * character processed. * size (I) int containing 0-4 default font size * sp (I) subraster * to display math operator * to which super/subscripts will be added * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to sp * plus its scripts, or NULL for any error * -------------------------------------------------------------------------- * Notes: o sp returned unchanged if no super/subscript(s) follow it. * ======================================================================= */ /* --- entry point --- */ subraster *rastdispmath ( char **expression, int size, subraster *sp ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texscripts(), /* parse expression for scripts */ subscript[512], supscript[512]; /* scripts parsed from expression */ int issub=0, issup=0; /* true if we have sub,sup */ subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */ *rastack(), /* stack operator with scripts */ *new_subraster(); /* for dummy base sp, if needed */ int vspace = 1; /* vertical space between scripts */ /* ------------------------------------------------------------------------- Obtain subscript and/or superscript expressions, and rasterize them/it -------------------------------------------------------------------------- */ /* --- parse for sub,superscript(s), and bump expression past it(them) --- */ *expression = texscripts(*expression,subscript,supscript,3); /* --- rasterize scripts --- */ if ( *subscript != '\000' ) /* have a subscript */ subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */ if ( *supscript != '\000' ) /* have a superscript */ supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */ /* --- set flags for convenience --- */ issub = (subsp != (subraster *)NULL); /* true if we have subscript */ issup = (supsp != (subraster *)NULL); /* true if we have superscript */ if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/ /* ------------------------------------------------------------------------- stack operator and its script(s) -------------------------------------------------------------------------- */ /* --- stack superscript atop operator --- */ if ( issup ) /* we have a superscript */ if ( sp == NULL ) /* but no base expression */ sp = supsp; /* so just use superscript */ else /* have base and superscript */ if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */ == NULL ) goto end_of_job; /* and quit if failed */ /* --- stack operator+superscript atop subscript --- */ if ( issub ) /* we have a subscript */ if ( sp == NULL ) /* but no base expression */ sp = subsp; /* so just use subscript */ else /* have base and subscript */ if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */ == NULL ) goto end_of_job; /* and quit if failed (done anyway)*/ /* ------------------------------------------------------------------------- free unneeded component subrasters and return final result to caller -------------------------------------------------------------------------- */ end_of_job: return ( sp ); } /* --- end-of-function rastdispmath() --- */ /* ========================================================================== * Function: rastflags ( expression, size, basesp, flag, value, arg3 ) * Purpose: sets an internal flag, e.g., for \rm, or sets an internal * value, e.g., for \unitlength=, and returns NULL * so nothing is displayed * -------------------------------------------------------------------------- * Arguments: expression (I) char ** to first char of null-terminated * LaTeX expression (unused/unchanged) * size (I) int containing base font size (not used, * just stored in subraster) * basesp (I) subraster * to character (or subexpression) * immediately preceding space, whose baseline * and height params are transferred to space * flag (I) int containing #define'd symbol specifying * internal flag to be set * value (I) int containing new value of flag * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) NULL so nothing is displayed * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastflags ( char **expression, int size, subraster *basesp, int flag, int value, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), /* parse expression for... */ valuearg[256]; /* value from expression, if needed */ int argvalue=0, /* atoi(valuearg) */ isdelta=0; /* true if + or - precedes valuearg */ double strtod(); /*convert ascii {valuearg} to double*/ /* ------------------------------------------------------------------------- set flag or value -------------------------------------------------------------------------- */ switch ( flag ) { default: break; /* unrecognized flag */ case ISTEXT: if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */ (*expression)++; /* skip leading ~ after \rm */ istext=value; break; /* set text mode */ case ISDISPLAY: isdisplaymath=value; break; /* set displaymath mode */ case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */ case ISSIZE: /* set displaysize */ case ISWEIGHT: /* set font weight */ if ( value != NOVALUE ) /* passed a fixed value to be set */ argvalue = value; /* set given fixed value */ else /* get value from expression */ { *expression = texsubexpr(*expression,valuearg,"{","}",0,0); if ( *valuearg != '\000' ) /* guard against empty string */ { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */ argvalue = atoi(valuearg); } } /* convert to int */ switch ( flag ) { default: break; case ISSIZE: /* set display size */ displaysize = (isdelta? displaysize+argvalue : argvalue); break; case ISWEIGHT: /* set font weight number */ value = (isdelta? weightnum+argvalue : argvalue); if ( value>=0 && value=2 && widthval<=600 ) /* sanity check */ width = widthval; } /* replace deafault width */ /* ------------------------------------------------------------------------- see if width is "absolute" or fill width -------------------------------------------------------------------------- */ if ( isfill /* called as \hfill{} */ && !isheight ) /* parameter conflict */ { if ( leftexpression != NULL ) /* if we have left half */ width -= (leftexpression->image)->width; /*reduce left width from total*/ if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */ != NULL ) /* succeeded */ width -= (rightsp->image)->width; } /* reduce right width from total */ /* ------------------------------------------------------------------------- construct blank subraster, and return it to caller -------------------------------------------------------------------------- */ /* --- get parameters from base symbol --- */ if ( basesp != (subraster *)NULL ) /* we have base symbol for space */ { baseht = (basesp->image)->height; /* height of base symbol */ baseln = basesp->baseline; } /* and its baseline */ /* --- flip params for height --- */ if ( isheight ) /* width is actually height */ { baseht = width; /* use given width as height */ width = 1; } /* and set default width */ /* --- generate and init space subraster --- */ if ( width > 0 ) /*make sure we have positive width*/ if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/ != NULL ) /* and if we succeed... */ { /* --- ...re-init subraster parameters --- */ spacesp->size = size; /*propagate base font size forward*/ spacesp->baseline = baseln; } /* ditto baseline */ /* ------------------------------------------------------------------------- concat right half if \hfill-ing -------------------------------------------------------------------------- */ if ( rightsp != NULL ) /* we have a right half after fill */ { spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/ rastcat(spacesp,rightsp,3)); /* or cat right half after space */ *expression += strlen((*expression)); } /* push expression to its null */ return ( spacesp ); } /* --- end-of-function rastspace() --- */ /* ========================================================================== * Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \\ handler, returns subraster corresponding to * left-hand expression preceding \\ above right-hand expression * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \\ to be * rasterized, and returning ptr immediately * to terminating null. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to expression, * or NULL for any parsing error * (expression ptr unchanged if error occurs) * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastnewline ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *rastack(), *newlsp=NULL; /* subraster for both lines */ subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/ char *texsubexpr(), spacexpr[129], *xptr=spacexpr; /*optional \\[vspace]*/ double strtod(); /* convert ascii param to double */ int vspace = size+2; /* #pixels between lines */ /* ------------------------------------------------------------------------- obtain optional [vspace] argument immediately following \\ command -------------------------------------------------------------------------- */ /* --- check if [vspace] given --- */ if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/ { /* ---parse [vspace] and bump expression past it, interpret as double--- */ *expression = texsubexpr(*expression,spacexpr,"[","]",0,0); if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */ vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */ } /* --- end-of-if(*(*expression)=='[') --- */ if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */ /* ------------------------------------------------------------------------- rasterize right half of expression and stack left half above it -------------------------------------------------------------------------- */ /* --- rasterize right half --- */ if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */ == NULL ) goto end_of_job; /* quit if failed */ /* --- stack left half above it --- */ newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/ /* --- back to caller --- */ end_of_job: if ( newlsp != NULL ) /* returning entire expression */ { int newht = (newlsp->image)->height; /* height of returned subraster */ newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */ isreplaceleft = 1; /* so set flag to replace left half*/ *expression += strlen(*expression); } /* and push to terminating null*/ return ( newlsp ); /* 1st line over 2nd, or null=error*/ } /* --- end-of-function rastnewline() --- */ /* ========================================================================== * Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 ) * Purpose: returns left/right arrow subraster (e.g., for \longrightarrow) * -------------------------------------------------------------------------- * Arguments: expression (I) char ** to first char of null-terminated * LaTeX expression (unused/unchanged) * size (I) int containing base font size (not used, * just stored in subraster) * basesp (I) subraster * to character (or subexpression) * immediately preceding space, whose baseline * and height params are transferred to space * drctn (I) int containing +1 for right, -1 for left, * or 0 for leftright * isBig (I) int containing 0 for ---> or 1 for ===> * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to left/right arrow subraster * or NULL for any error * -------------------------------------------------------------------------- * Notes: o An optional argument [width] may *immediately* follow * the \longxxx to explicitly set the arrow's width in pixels. * For example, \longrightarrow calculates a default width * (as usual in LaTeX), whereas \longrightarrow[50] explicitly * draws a 50-pixel long arrow. This can be used, e.g., * to draw commutative diagrams in conjunction with * \array (and maybe with \stackrel and/or \relstack, too). * o In case you really want to render, say, [f]---->[g], just * use an intervening space, i.e., [f]\longrightarrow~[g]. * In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}. * ======================================================================= */ /* --- entry point --- */ subraster *rastarrow ( char **expression, int size, subraster *basesp, int drctn, int isBig, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */ char *texsubexpr(), widtharg[256]; /* parse for optional [width] */ char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/ subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/ subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/ int delete_subraster(); /*free work areas in case of error*/ double strtod(); /* convert ascii [width] to value */ int width = 10 + 8*size, height; /* width, height for \longxxxarrow */ int islimits = 1; /*true to handle limits internally*/ int limsize = size-1; /* font size for limits */ int vspace = 1; /* #empty rows below arrow */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* ------------------------------------------------------------------------- construct longleft/rightarrow subraster, with limits, and return it to caller -------------------------------------------------------------------------- */ /* --- check for optional width arg and replace default width --- */ if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ { int widthval; /* test [width] before using it */ *expression = texsubexpr(*expression,widtharg,"[","]",0,0); widthval = /* convert [width] to integer */ (int)((unitlength*strtod(widtharg,NULL))+0.5); if ( widthval>=2 && widthval<=600 ) /* sanity check */ width = widthval; } /* replace deafault width */ /* --- now parse for limits, and bump expression past it(them) --- */ if ( islimits ) /* handling limits internally */ { *expression = texscripts(*expression,sub,super,3); /* parse for limits */ if ( *sub != '\000' ) /*have a subscript following arrow*/ subsp = rasterize(sub,limsize); /* so try to rasterize subscript */ if ( *super != '\000' ) /*have superscript following arrow*/ supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/ /* --- set height based on width --- */ height = min2(17,max2(9,(width+2)/6)); /* height based on width */ height = 1 + (height/2)*2; /* always force odd height */ /* --- generate arrow subraster --- */ if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/ == NULL ) goto end_of_job; /* and quit if we failed */ /* --- add space below arrow --- */ if ( vspace > 0 ) /* if we have space below arrow */ if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/ != NULL ) /* and if we succeeded */ if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */ == NULL ) goto end_of_job; /* and quit if we failed */ /* --- init arrow subraster parameters --- */ arrowsp->size = size; /*propagate base font size forward*/ arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */ /* --- add limits above/below arrow, as necessary --- */ if ( subsp != NULL ) /* stack subscript below arrow */ if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */ == NULL ) goto end_of_job; /* quit if failed */ if ( supsp != NULL ) /* stack superscript above arrow */ if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/ == NULL ) goto end_of_job; /* quit if failed */ /* --- return arrow (or NULL) to caller --- */ end_of_job: return ( arrowsp ); } /* --- end-of-function rastarrow() --- */ /* ========================================================================== * Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 ) * Purpose: returns an up/down arrow subraster (e.g., for \longuparrow) * -------------------------------------------------------------------------- * Arguments: expression (I) char ** to first char of null-terminated * LaTeX expression (unused/unchanged) * size (I) int containing base font size (not used, * just stored in subraster) * basesp (I) subraster * to character (or subexpression) * immediately preceding space, whose baseline * and height params are transferred to space * drctn (I) int containing +1 for up, -1 for down, * or 0 for updown * isBig (I) int containing 0 for ---> or 1 for ===> * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to up/down arrow subraster * or NULL for any error * -------------------------------------------------------------------------- * Notes: o An optional argument [height] may *immediately* follow * the \longxxx to explicitly set the arrow's height in pixels. * For example, \longuparrow calculates a default height * (as usual in LaTeX), whereas \longuparrow[25] explicitly * draws a 25-pixel high arrow. This can be used, e.g., * to draw commutative diagrams in conjunction with * \array (and maybe with \stackrel and/or \relstack, too). * o In case you really want to render, say, [f]---->[g], just * use an intervening space, i.e., [f]\longuparrow~[g]. * In text use two spaces {\rm~[f]\longuparrow~~[g]}. * ======================================================================= */ /* --- entry point --- */ subraster *rastuparrow ( char **expression, int size, subraster *basesp, int drctn, int isBig, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */ char *texsubexpr(), heightarg[256]; /* parse for optional [height] */ char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/ subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/ subraster *rastcat(); /* cat superscript left, sub right */ double strtod(); /* convert ascii [height] to value */ int height = 8 + 2*size, width; /* height, width for \longxxxarrow */ int islimits = 1; /*true to handle limits internally*/ int limsize = size-1; /* font size for limits */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* ------------------------------------------------------------------------- construct blank subraster, and return it to caller -------------------------------------------------------------------------- */ /* --- check for optional height arg and replace default height --- */ if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ { int heightval; /* test height before using it */ *expression = texsubexpr(*expression,heightarg,"[","]",0,0); heightval = /* convert [height] to integer */ (int)((unitlength*strtod(heightarg,NULL))+0.5); if ( heightval>=2 && heightval<=600 ) /* sanity check */ height = heightval; } /* replace deafault height */ /* --- now parse for limits, and bump expression past it(them) --- */ if ( islimits ) /* handling limits internally */ { *expression = texscripts(*expression,sub,super,3); /* parse for limits */ if ( *sub != '\000' ) /*have a subscript following arrow*/ subsp = rasterize(sub,limsize); /* so try to rasterize subscript */ if ( *super != '\000' ) /*have superscript following arrow*/ supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/ /* --- set width based on height --- */ width = min2(17,max2(9,(height+2)/4)); /* width based on height */ width = 1 + (width/2)*2; /* always force odd width */ /* --- generate arrow subraster --- */ if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/ == NULL ) goto end_of_job; /* and quit if we failed */ /* --- init arrow subraster parameters --- */ arrowsp->size = size; /*propagate base font size forward*/ arrowsp->baseline = height-1; /* set baseline at bottom of arrow */ /* --- add limits above/below arrow, as necessary --- */ if ( supsp != NULL ) /* cat superscript to left of arrow*/ { int supht = (supsp->image)->height, /* superscript height */ deltab = (1+abs(height-supht))/2; /* baseline difference to center */ supsp->baseline = supht-1; /* force script baseline to bottom */ if ( supht <= height ) /* arrow usually taller than script*/ arrowsp->baseline -= deltab; /* so bottom of script goes here */ else supsp->baseline -= deltab; /* else bottom of arrow goes here */ if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */ == NULL ) goto end_of_job; } /* quit if failed */ if ( subsp != NULL ) /* cat subscript to right of arrow */ { int subht = (subsp->image)->height, /* subscript height */ deltab = (1+abs(height-subht))/2; /* baseline difference to center */ arrowsp->baseline = height-1; /* reset arrow baseline to bottom */ subsp->baseline = subht-1; /* force script baseline to bottom */ if ( subht <= height ) /* arrow usually taller than script*/ arrowsp->baseline -= deltab; /* so bottom of script goes here */ else subsp->baseline -= deltab; /* else bottom of arrow goes here */ if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */ == NULL ) goto end_of_job; } /* quit if failed */ /* --- return arrow (or NULL) to caller --- */ end_of_job: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */ return ( arrowsp ); } /* --- end-of-function rastuparrow() --- */ /* ========================================================================== * Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3) * Purpose: overlays one raster on another * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following overlay \cmd to * be rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding overlay \cmd * (unused, but passed for consistency) * overlay (I) int containing 1 to overlay / (e.g., \not) * or NOVALUE to pick up 2nd arg from expression * offset2 (I) int containing #pixels to horizontally offset * overlay relative to underlying symbol, * positive(right) or negative or 0, * or NOVALUE to pick up optional [offset] arg * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to composite, * or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastoverlay ( char **expression, int size, subraster *basesp, int overlay, int offset2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), /*parse expression for base,overlay*/ expr1[512], expr2[512]; /* base, overlay */ subraster *rasterize(), *sp1=NULL, *sp2=NULL; /*rasterize 1=base, 2=overlay*/ subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/ /* ------------------------------------------------------------------------- Obtain base, and maybe overlay, and rasterize them -------------------------------------------------------------------------- */ /* --- check for optional offset2 arg --- */ if ( offset2 == NOVALUE ) /* only if not explicitly specified*/ if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ { int offsetval; /* test before using it */ *expression = texsubexpr(*expression,expr2,"[","]",0,0); offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */ if ( abs(offsetval) <= 25 ) /* sanity check */ offset2 = offsetval; } /* replace deafault */ if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */ /* --- parse for base, bump expression past it, and rasterize it --- */ *expression = texsubexpr(*expression,expr1,"{","}",0,0); if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */ if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */ == NULL ) goto end_of_job; /* quit if failed to rasterize */ overlaysp = sp1; /*in case we return with no overlay*/ /* --- get overlay expression, and rasterize it --- */ if ( overlay == NOVALUE ) /* get overlay from input stream */ { *expression = texsubexpr(*expression,expr2,"{","}",0,0); if ( *expr2 != '\000' ) /* have an overlay */ sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */ else /* specific overlay */ switch ( overlay ) { default: break; case 1: /* e.g., \not overlays slash */ sp2 = rasterize("/",size+1); /* rasterize overlay expression */ offset2 = max2(1,size-3); /* push / right a bit */ offset2 = 0; break; } /* --- end-of-switch(overlay) --- */ if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/ /* ------------------------------------------------------------------------- construct composite overlay -------------------------------------------------------------------------- */ overlaysp = rastcompose(sp1,sp2,offset2,0,3); end_of_job: return ( overlaysp ); } /* --- end-of-function rastoverlay() --- */ /* ========================================================================== * Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 ) * Purpose: \frac,\atop handler, returns a subraster corresponding to * expression (immediately following \frac,\atop) at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \frac to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \frac * (unused, but passed for consistency) * isfrac (I) int containing true to draw horizontal line * between numerator and denominator, * or false not to draw it (for \atop). * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to fraction, * or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastfrac ( char **expression, int size, subraster *basesp, int isfrac, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), /*parse expression for numer,denom*/ numer[8192], denom[8192]; /*numer,denom parsed from expression*/ subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/ subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */ subraster *new_subraster(), *spacesp=NULL; /*may use space for num or den*/ int width=0, /* width of constructed raster */ numheight=0; /* height of numerator */ int baseht=0, baseln=0; /* height,baseline of base symbol */ int istweak = 1; /*true to tweak baseline alignment*/ int rule_raster(), /* draw horizontal line for frac */ lineheight = 1; /* thickness of fraction line */ int vspace = 1; /*vertical space between components*/ int delete_subraster(); /*free work areas in case of error*/ /* ------------------------------------------------------------------------- Obtain numerator and denominator, and rasterize them -------------------------------------------------------------------------- */ /* --- parse for numerator,denominator and bump expression past them --- */ *expression = texsubexpr(*expression,numer,"{","}",0,0); *expression = texsubexpr(*expression,denom,"{","}",0,0); if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */ goto end_of_job; /* nothing to do, so quit */ /* --- rasterize numerator, denominator --- */ if ( *numer != '\000' ) /* have a numerator */ if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */ == NULL ) goto end_of_job; /* and quit if failed */ if ( *denom != '\000' ) /* have a denominator */ if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */ == NULL ) /* failed */ { if ( numsp != NULL ) /* already rasterized numerator */ delete_subraster(numsp); /* so free now-unneeded numerator */ goto end_of_job; } /* and quit */ /* --- if one componenet missing, use a blank space for it --- */ if ( numsp == NULL ) /* no numerator given */ numsp = rasterize("[?]",size-1); /* missing numerator */ if ( densp == NULL ) /* no denominator given */ densp = rasterize("[?]",size-1); /* missing denominator */ /* --- check that we got both components --- */ if ( numsp==NULL || densp==NULL ) /* some problem */ { delete_subraster(numsp); /*delete numerator (if it existed)*/ delete_subraster(densp); /*delete denominator (if it existed)*/ goto end_of_job; } /* and quit */ /* --- get height of numerator (to determine where line belongs) --- */ numheight = (numsp->image)->height; /* get numerator's height */ /* ------------------------------------------------------------------------- construct raster with numerator stacked over denominator -------------------------------------------------------------------------- */ /* --- construct raster with numer/denom --- */ if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/ == NULL ) /* failed to construct numer/denom */ { delete_subraster(numsp); /* so free now-unneeded numerator */ delete_subraster(densp); /* and now-unneeded denominator */ goto end_of_job; } /* and then quit */ /* --- determine width of constructed raster --- */ width = (fracsp->image)->width; /*just get width of embedded image*/ /* --- initialize subraster parameters --- */ fracsp->size = size; /* propagate font size forward */ fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/ if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */ { baseht = (basesp->image)->height; /* height of base symbol */ baseln = basesp->baseline; /* and its baseline */ } /* --- end-of-if(basesp!=NULL) --- */ /* ------------------------------------------------------------------------- draw horizontal line between numerator and denominator -------------------------------------------------------------------------- */ if ( isfrac ) /*line for \frac, but not for \atop*/ rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0); /* ------------------------------------------------------------------------- return final result to caller -------------------------------------------------------------------------- */ end_of_job: if ( msgfp!=NULL && msglevel>=99 ) { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"...")); if ( fracsp != NULL ) /* have a constructed raster */ type_raster(fracsp->image,msgfp); } /* display constructed raster */ return ( fracsp ); } /* --- end-of-function rastfrac() --- */ /* ========================================================================== * Function: rastackrel ( expression, size, basesp, base, arg2, arg3 ) * Purpose: \stackrel handler, returns a subraster corresponding to * stacked relation * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \stackrel to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \stackrel * (unused, but passed for consistency) * base (I) int containing 1 if upper/first subexpression * is base relation, or 2 if lower/second is * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to stacked * relation, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastackrel ( char **expression, int size, subraster *basesp, int base, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), /*parse expression for numer,denom*/ upper[8192], lower[8192]; /*upper,lower parsed from expression*/ subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */ subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */ int upsize = (base==1? size:size-1), /* font size for upper component */ lowsize = (base==2? size:size-1); /* font size for lower component */ int vspace = 1; /*vertical space between components*/ int delete_subraster(); /*free work areas in case of error*/ /* ------------------------------------------------------------------------- Obtain numerator and denominator, and rasterize them -------------------------------------------------------------------------- */ /* --- parse for numerator,denominator and bump expression past them --- */ *expression = texsubexpr(*expression,upper,"{","}",0,0); *expression = texsubexpr(*expression,lower,"{","}",0,0); if ( *upper=='\000' || *lower=='\000' ) /* missing either component */ goto end_of_job; /* nothing to do, so quit */ /* --- rasterize upper, lower --- */ if ( *upper != '\000' ) /* have upper component */ if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */ == NULL ) goto end_of_job; /* and quit if failed */ if ( *lower != '\000' ) /* have lower component */ if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */ == NULL ) /* failed */ { if ( upsp != NULL ) /* already rasterized upper */ delete_subraster(upsp); /* so free now-unneeded upper */ goto end_of_job; } /* and quit */ /* ------------------------------------------------------------------------- construct stacked relation raster -------------------------------------------------------------------------- */ /* --- construct stacked relation --- */ if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize subraster parameters --- */ relsp->size = size; /* propagate font size forward */ /* ------------------------------------------------------------------------- return final result to caller -------------------------------------------------------------------------- */ end_of_job: return ( relsp ); } /* --- end-of-function rastackrel() --- */ /* ========================================================================== * Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 ) * Purpose: \log, \lim, etc handler, returns a subraster corresponding * to math functions * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \mathfunc to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \mathfunc * (unused, but passed for consistency) * mathfunc (I) int containing 1=arccos, 2=arcsin, etc. * islimits (I) int containing 1 if function may have * limits underneath, e.g., \lim_{n\to\infty} * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to mathfunc, * or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastmathfunc ( char **expression, int size, subraster *basesp, int mathfunc, int islimits, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texscripts(), /* parse expression for _limits */ func[128], limits[8192]; /* func as {\rm func}, limits */ subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/ subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */ int limsize = size-1; /* font size for limits */ int vspace = 1; /*vertical space between components*/ int delete_subraster(); /*free work areas in case of error*/ /* --- table of function names by mathfunc number --- */ static int numnames = 33; /* number of names in table */ static char *funcnames[] = { "error", /* 0 index is illegal/error bucket*/ "arccos", "arcsin", "arctan", /* 1 - 3 */ "arg", "cos", "cosh", /* 4 - 6 */ "cot", "coth", "csc", /* 7 - 9 */ "deg", "det", "dim", /* 10 - 12 */ "exp", "gcd", "hom", /* 13 - 15 */ "inf", "ker", "lg", /* 16 - 18 */ "lim", "liminf", "limsup", /* 19 - 21 */ "ln", "log", "max", /* 22 - 24 */ "min", "Pr", "sec", /* 25 - 27 */ "sin", "sinh", "sup", /* 28 - 30 */ "tan", "tanh", /* 31 - 32 */ /* --- extra mimetex funcnames --- */ "tr" /* 33 */ } ; /* ------------------------------------------------------------------------- set up and rasterize function name in \rm -------------------------------------------------------------------------- */ if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */ strcpy(func,"{\\rm~"); /* init string with {\rm~ */ strcat(func,funcnames[mathfunc]); /* concat function name */ strcat(func,"}"); /* and add terminating } */ if ( (funcsp = rasterize(func,size)) /* rasterize function name */ == NULL ) goto end_of_job; /* and quit if failed */ mathfuncsp = funcsp; /* just return funcsp if no limits */ if ( !islimits ) goto end_of_job; /* treat any subscript normally */ /* ------------------------------------------------------------------------- Obtain limits, if permitted and if provided, and rasterize them -------------------------------------------------------------------------- */ /* --- parse for subscript limits, and bump expression past it(them) --- */ *expression = texscripts(*expression,limits,limits,1); if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */ /* --- rasterize limits --- */ if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */ == NULL ) goto end_of_job; /* and quit if failed */ /* ------------------------------------------------------------------------- construct func atop limits -------------------------------------------------------------------------- */ /* --- construct func atop limits --- */ if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize subraster parameters --- */ mathfuncsp->size = size; /* propagate font size forward */ /* ------------------------------------------------------------------------- return final result to caller -------------------------------------------------------------------------- */ end_of_job: return ( mathfuncsp ); } /* --- end-of-function rastmathfunc() --- */ /* ========================================================================== * Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \sqrt handler, returns a subraster corresponding to * expression (immediately following \sqrt) at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \sqrt to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to expression, * or NULL for any parsing error * (expression ptr unchanged if error occurs) * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastsqrt ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192], /* parse subexpr to be sqrt-ed */ rootarg[8192]; /* optional \sqrt[rootarg]{...} */ subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */ subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */ *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/ int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */ rootheight=0, rootwidth=0, /* height,width of rootarg raster */ subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */ int rastput(); /* put subexpr in constructed sqrt */ int delete_subraster(); /* free work areas */ /* ------------------------------------------------------------------------- Obtain subexpression to be sqrt-ed, and rasterize it -------------------------------------------------------------------------- */ /* --- first check for optional \sqrt[rootarg]{...} --- */ if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ { *expression = texsubexpr(*expression,rootarg,"[","]",0,0); if ( *rootarg != '\000' ) /* got rootarg */ if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/ != NULL ) /* rasterized successfully */ { rootheight = (rootsp->image)->height; /* get height of rootarg */ rootwidth = (rootsp->image)->width; } /* and its width */ } /* --- end-of-if(**expression=='[') --- */ /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); if ( *subexpr == '\000' ) /* couldn't get subexpression */ goto end_of_job; /* nothing to do, so quit */ /* --- rasterize subexpression to be accented --- */ if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/ == NULL ) goto end_of_job; /* quit if failed */ /* ------------------------------------------------------------------------- determine height and width of sqrt raster to be constructed -------------------------------------------------------------------------- */ /* --- first get height and width of subexpr --- */ subheight = (subsp->image)->height; /* height of subexpr */ subwidth = (subsp->image)->width; /* and its width */ pixsz = (subsp->image)->pixsz; /* pixsz remains constant */ /* --- determine height and width of sqrt to contain subexpr --- */ sqrtheight = subheight + 2; /* subexpr + blank line + overbar */ surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */ sqrtwidth = subwidth + surdwidth; /* ------------------------------------------------------------------------- construct sqrt (with room to move in subexpr) and embed subexpr in it -------------------------------------------------------------------------- */ /* --- construct sqrt --- */ if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz)) == NULL ) goto end_of_job; /* quit if failed to build sqrt */ /* --- embed subexpr in sqrt at lower-right corner--- */ rastput(sqrtsp->image,subsp->image,2,sqrtwidth-subwidth,1); sqrtsp->baseline = subsp->baseline + 2; /* adjust baseline */ /* --- "embed" rootarg at upper-left --- */ if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/ { /* --- allocate full raster to contain sqrtsp and rootsp --- */ int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,surdwidth-2), fullheight = sqrtheight+rootheight - min2(rootheight,2+size); subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz); if ( fullsp != NULL ) /* allocated successfully */ { /* --- embed sqrtsp exactly at lower-right corner --- */ rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/ fullheight-sqrtheight,fullwidth-sqrtwidth,1); /* --- embed rootsp near upper-left, nestled above leading surd --- */ rastput(fullsp->image,rootsp->image, 0,max2(0,surdwidth-rootwidth-2-size),0); /* --- replace sqrtsp with fullsp --- */ delete_subraster(sqrtsp); /* free original sqrtsp */ sqrtsp = fullsp; /* and repoint it to fullsp instead*/ sqrtsp->baseline = fullheight - (subheight - subsp->baseline); } } /* --- end-of-if(rootsp!=NULL) --- */ /* --- initialize subraster parameters --- */ sqrtsp->size = size; /* propagate font size forward */ /* ------------------------------------------------------------------------- free unneeded component subrasters and return final result to caller -------------------------------------------------------------------------- */ end_of_job: if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */ return ( sqrtsp ); } /* --- end-of-function rastsqrt() --- */ /* ========================================================================== * Function: rastaccent (expression,size,basesp,accent,isabove,isscript) * Purpose: \hat, \vec, \etc handler, returns a subraster corresponding * to expression (immediately following \accent) at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \accent to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) * accent (I) int containing HATACCENT or VECACCENT, etc, * between numerator and denominator, * or false not to draw it (for \over). * isabove (I) int containing true if accent is above * expression to be accented, or false * if accent is below (e.g., underbrace) * isscript (I) int containing true if sub/superscripts * allowed (for under/overbrace), or 0 if not. * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to expression, * or NULL for any parsing error * (expression ptr unchanged if error occurs) * -------------------------------------------------------------------------- * Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way * of isabove and isscript args. * ======================================================================= */ /* --- entry point --- */ subraster *rastaccent ( char **expression, int size, subraster *basesp, int accent, int isabove, int isscript ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192]; /* parse subexpr to be accented */ char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */ subscript[512], supscript[512]; /* scripts parsed from expression */ subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/ subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */ subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/ int accheight=0, accwidth=0, /* height, width of accent */ subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */ int delete_subraster(); /*free work areas in case of error*/ int vspace = 0; /*vertical space between accent,sub*/ /* ------------------------------------------------------------------------- Obtain subexpression to be accented, and rasterize it -------------------------------------------------------------------------- */ /* --- parse for subexpr to be accented, and bump expression past it --- */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); if ( *subexpr=='\000' ) /* couldn't get subexpression */ goto end_of_job; /* nothing to do, so quit */ /* --- rasterize subexpression to be accented --- */ if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/ == NULL ) goto end_of_job; /* quit if failed */ /* ------------------------------------------------------------------------- determine desired accent width and height -------------------------------------------------------------------------- */ /* --- first get height and width of subexpr --- */ subheight = (subsp->image)->height; /* height of subexpr */ subwidth = (subsp->image)->width; /* and its width is overall width */ pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */ /* --- determine desired width, height of accent --- */ accwidth = subwidth; /* same width as subexpr */ accheight = 3; /* default for bars */ switch ( accent ) { default: break; /* default okay */ case DOTACCENT: case DDOTACCENT: accheight = (size<3? 3:4); /* default for dots */ break; case HATACCENT: case VECACCENT: accheight = 7; /* default */ if ( subwidth < 10 ) accheight = 5; /* unless small width */ else if ( subwidth > 25 ) accheight = 9; /* or large */ break; } /* --- end-of-switch(accent) --- */ accheight = min2(accheight,subheight); /*never higher than accented subexpr*/ /* ------------------------------------------------------------------------- construct accent, and construct subraster with accent over (or under) subexpr -------------------------------------------------------------------------- */ /* --- first construct accent --- */ if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */ == NULL ) goto end_of_job; /* quit if failed to build accent */ /* --- now stack accent above (or below) subexpr, and free both args --- */ accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/ : rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/ if ( accsubsp == NULL ) /* failed to stack accent */ { delete_subraster(subsp); /* free unneeded subsp */ delete_subraster(accsp); /* and unneeded accsp */ goto end_of_job; } /* and quit */ /* ------------------------------------------------------------------------- look for super/subscript (annotation for over/underbrace) -------------------------------------------------------------------------- */ /* --- first check whether accent permits accompanying annotations --- */ if ( !isscript ) goto end_of_job; /* no annotations for this accent */ /* --- now get scripts if there actually are any --- */ *expression = texscripts(*expression,subscript,supscript,(isabove?2:1)); script = (isabove? supscript : subscript); /*select above^ or below_ script*/ if ( *script == '\000' ) goto end_of_job; /* no accompanying script */ /* --- rasterize script annotation at size-2 --- */ if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */ == NULL ) goto end_of_job; /* quit if failed */ /* --- stack annotation above (or below) accent, and free both args --- */ accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */ : rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */ /* ------------------------------------------------------------------------- return final result to caller -------------------------------------------------------------------------- */ end_of_job: if ( accsubsp != NULL ) /* initialize subraster parameters */ accsubsp->size = size; /* propagate font size forward */ return ( accsubsp ); } /* --- end-of-function rastaccent() --- */ /* ========================================================================== * Function: rastfont (expression,size,basesp,font,arg2,arg3) * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding * to char(s) within {}'s rendered at size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \font to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) * font (I) int containing 1 for \cal{}, 2 for \scr{} * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to chars * between {}'s, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastfont ( char **expression, int size, subraster *basesp, int font, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/ subexpr[8192]; /* turn \cal{AB} into \calA\calB */ char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/ char *name = NULL; /* fonts[font].name */ int class = 0; /* fonts[font].class */ subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */ *rastflags(); /* or just set flag to switch font */ /* --- fonts recognized by rastfont --- */ static int nfonts = 4; /* legal font #'s are 1...nfonts */ static struct {char *name; int class;} fonts[] = { /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */ { "\\badfont", 0 }, { "\\cal", 1 }, /*(1) calligraphic, uppercase */ { "\\scr", 1 }, /*(2) rsfs/script, uppercase */ { "\\rm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */ { "\\it", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */ { NULL, 0 } } ; /* --- end-of-fonts[] --- */ /* ------------------------------------------------------------------------- first get font name and class to determine type of conversion desired -------------------------------------------------------------------------- */ if ( font<=0 || font>nfonts ) font=0; /* set error if out-of-bounds */ name = fonts[font].name; /* font name */ class = fonts[font].class; /* font class */ /* ------------------------------------------------------------------------- now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC -------------------------------------------------------------------------- */ if ( class < 0 ) /* not character-by-character */ { /* --- if \font not immediately followed by { then it has no arg, so just set flag ------------------------------------------------------------------------ */ if ( *(*expression) != '{' ) /* no \font arg, so just set flag */ { if ( msgfp!=NULL && msglevel>=99 ) fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font); switch ( font ) /* set flag by our internal font# */ { case 3: /* \rm, \text sets flag istext=1 */ fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break; case 4: /* \it, \text sets flag istext=2 */ fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break; default: break; } /* unrecognized, set no flags */ goto end_of_job; } /* --- end-of-if(*(*expression)!='{') --- */ /* --- convert \font{abc} --> {\font~abc} ---------------------------------- */ /* --- parse for {fontchars} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,fontchars,"{","}",0,0); if ( msgfp!=NULL && msglevel>=99 ) fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars); /* --- convert all fontchars at the same time --- */ strcpy(subexpr,"{"); /* start off with opening { */ strcat(subexpr,name); /* followed by font name */ strcat(subexpr,"~"); /* followed by whitespace */ strcat(subexpr,fontchars); /* followed by all the chars */ strcat(subexpr,"}"); /* terminate with closing } */ } /* --- end-of-if(class<0) --- */ else /* character-by-character */ { /* --- convert ABC to \calA\calB\calC ------------------------------ */ int isprevchar=0; /* true if prev char converted */ /* --- parse for {fontchars} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,fontchars,"{","}",0,0); if ( msgfp!=NULL && msglevel>=99 ) fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars); /* --- convert fontchars one at a time --- */ strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */ strcpy(subexpr,"{"); /* nope, just start off with { */ for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ ) { if ( isthischar(fchar,WHITEMATH) ) /* some whitespace in text mode */ strcat(subexpr,"\\;"); /* so respect whitespace */ else /* char to be displayed in font */ { int exprlen = 0; /* #chars in subexpr before fchar */ int isinclass = 0; /* set true if fchar in font class */ switch ( class ) /* check if fchar is in font class */ { default: break; /* no chars in unrecognized class */ case 1: if ( isupper((int)fchar) ) isinclass=1; break; case 2: if ( isalpha((int)fchar) ) isinclass=1; break; case 3: if ( isalnum((int)fchar) ) isinclass=1; break; case 4: if ( islower((int)fchar) ) isinclass=1; break; case 5: if ( isdigit((int)fchar) ) isinclass=1; break; case 9: isinclass=1; break; } if ( isinclass ) /* convert current char to \font */ { strcat(subexpr,name); /* by prefixing it with font name */ isprevchar = 1; } /* and set flag to signal separator*/ else /* current char not in \font */ { if ( isprevchar ) /* extra separator only after \font*/ if ( isalpha(fchar) ) /* separator only before alpha */ strcat(subexpr,"~"); /* need separator after \font */ isprevchar = 0; } /* reset flag for next char */ exprlen = strlen(subexpr); /* #chars so far */ subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/ subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */ } /* --- end-of-for(pfchars) --- */ strcat(subexpr,"}"); /* add closing } */ } /* --- end-of-if/else(class<0) --- */ /* ------------------------------------------------------------------------- rasterize subexpression containing chars to be rendered at font -------------------------------------------------------------------------- */ if ( msgfp!=NULL && msglevel>=99 ) fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr); if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */ == NULL ) goto end_of_job; /* and quit if failed */ /* ------------------------------------------------------------------------- back to caller with chars rendered in font -------------------------------------------------------------------------- */ end_of_job: return ( fontsp ); /* chars rendered in font */ } /* --- end-of-function rastfont() --- */ /* ========================================================================== * Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \begin{}...\end{} handler, returns a subraster corresponding * to array expression within environment, i.e., rewrites * \begin{}...\end{} as mimeTeX equivalent, and rasterizes that. * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \begin to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \begin * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to array * expression, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastbegin ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192], /* \begin{} environment paramaters */ *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */ char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/ subraster *rasterize(), *sp=NULL; /* rasterize environment */ int ienviron = 0; /* environs[] index */ static char *environs[] = { /* types of environments we process*/ "eqnarray", /* eqnarray environment */ "array", /* array environment */ "matrix", /* array environment */ "tabular", /* array environment */ NULL }; /* trailer */ /* ------------------------------------------------------------------------- determine type of environment we're beginning -------------------------------------------------------------------------- */ /* --- \begin must be followed by {type_of_environment} --- */ exprptr = texsubexpr(*expression,subexpr,"{","}",0,0); if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */ /* --- look up environment in our table --- */ for ( ienviron=0; ;ienviron++ ) /* search table till NULL */ if ( environs[ienviron] == NULL ) /* found NULL before match */ goto end_of_job; /* so quit */ else /* see if we have an exact match */ if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/ break; /* leave loop with ienviron index */ /* --- accumulate any additional params for this environment --- */ *subexpr = '\000'; /* reset subexpr to empty string */ switch ( ienviron ) { default: goto end_of_job; /* environ not implemented yet */ case 0: /* \begin{eqnarray} */ strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */ break; case 1: case 2: case 3: /* \begin{array} followed by {lcr} */ strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/ skipwhite(exprptr); /* bump to next non-white char */ if ( *exprptr == '{' ) /* assume we have {lcr} argument */ { exprptr = texsubexpr(exprptr,subexpr+7,"{","}",0,0); /* add on lcr */ if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */ strcat(subexpr,"$"); } /* add terminating $ to lcr */ break; } /* --- end-of-switch(ienviron) --- */ /* ------------------------------------------------------------------------- locate matching \end{...} -------------------------------------------------------------------------- */ /* --- first \end following \begin --- */ if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */ == NULL ) goto end_of_job; /* and quit if no \end found */ /* --- find matching endptr by pushing past any nested \begin's --- */ begptr = exprptr; /* start after first \begin{...} */ while ( 1 ) /*break when we find matching \end*/ { /* --- first, set ptr to closing } terminating current \end{...} --- */ if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */ == NULL ) goto end_of_job; /* and quit if no } found */ /* -- locate next nested \begin --- */ if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */ == NULL ) break; /*no more, so we have matching \end*/ begptr += strlen(begtoken); /* push ptr past token */ if ( begptr >= endptr ) break; /* past endptr, so not nested */ /* --- have nested \begin, so push forward to next \end --- */ if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */ == NULL ) goto end_of_job; /* and quit if none found */ } /* --- end-of-while(1) --- */ /* ------------------------------------------------------------------------- add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{} -------------------------------------------------------------------------- */ /* --- add on everything, completing subexpr for \begin{}...\end{} --- */ *endptr = '\000'; /*replace \ in \end by terminating 0*/ strcat(subexpr,exprptr); /* add on everything preceding \end*/ strcat(subexpr,"}"); /* followed by terminating } */ /* --- push expression past closing } of \end{} --- */ *expression = braceptr+1; /* resume processing after } */ /* --- debugging output --- */ if ( msgfp!=NULL && msglevel>=99 ) fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr); /* ------------------------------------------------------------------------- return rasterized mimeTeX equivalent of \begin{}...\end{} environment -------------------------------------------------------------------------- */ sp = rasterize(subexpr,size); /* rasterize subexpr */ end_of_job: return ( sp ); /* back to caller with sp or NULL */ } /* --- end-of-function rastbegin() --- */ /* ========================================================================== * Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \array handler, returns a subraster corresponding to array * expression (immediately following \array) at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \array to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \array * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to array * expression, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \array{3,lcrBC$a&b&c\\d&e&f\\etc} * o The 3,lcrBC$ part is an optional "preamble". The lcr means * what you think, i.e., "horizontal" left,center,right * justification down corresponding column. The new BC means * "vertical" baseline,center justification across corresponding * row. The leading 3 specifies the font size 0-4 to be used. * You may also specify +1,-1,+2,-2, etc, which is used as an * increment to the current font size, e.g., -1,lcr$ uses * one font size smaller than current. Without a leading * + or -, the font size is "absolute". * o The preamble can also be just lcrBC$ without a leading * size-part, or just 3$ without a trailing lcrBC-part. * The default size is whatever is current, and the * default justification is c(entered) and B(aseline). * ======================================================================= */ /* --- entry point --- */ subraster *rastarray ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8193], *exprptr, /* parse array subexpr */ subtok[4096], *subptr=subtok, /* & or \\ inside { }'s not a delim*/ token[4096], *tokptr=token, /* token from subexpr to rasterize */ *preamble(), *preptr=token; /*process optional size,lcr preamble*/ char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */ int maxarraysz = 32; /* max #rows, cols */ int justify[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, hline[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, vline[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, colwidth[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, rowheight[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, rowbaseln[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, rowcenter[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int irow,nrows=0, icol,ncols[33], /*#rows in array, #cols in each row*/ maxcols=0; /* max# cols in any single row */ int itoken, ntokens=0, /* index, total #tokens in array */ istokwhite=1, /* true if token all whitespace */ nnonwhite=0; /* #non-white tokens */ int isescape=0, wasescape=0; /* is current,previous char escape */ subraster *rasterize(), *toksp[1025], /* rasterize tokens */ *new_subraster(), *arraysp=NULL; /* subraster for entire array */ raster *arrayrp=NULL; /* raster for entire array */ int delete_subraster(); /* free toksp[] workspace at eoj */ int rowspace=2, colspace=4, /* blank space between rows, cols */ hspace=1, vspace=1; /*space to accommodate hline,vline*/ int width=0, height=0, /* width,height of array */ leftcol=0, toprow=0; /*upper-left corner for cell in it*/ int rastput(); /* embed tokens/cells in array */ int rule_raster(); /* draw hlines and vlines in array */ char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */ char *texchar(), hltoken[1025]; /* extract \hline from token */ int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* ------------------------------------------------------------------------- Macros to determine extra raster space required for vline/hline -------------------------------------------------------------------------- */ #define vlinespace(icol) \ ( vline[icol] == 0? 0 : /* no vline so no space needed */ \ ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) ) #define hlinespace(irow) \ ( hline[irow] == 0? 0 : /* no hline so no space needed */ \ ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) ) /* ------------------------------------------------------------------------- Obtain array subexpression -------------------------------------------------------------------------- */ /* --- parse for array subexpression, and bump expression past it --- */ *subexpr = ' '; /* set leading blank */ *expression = texsubexpr(*expression,subexpr+1,"{","}",0,0); if ( *(subexpr+1)=='\000' ) /* couldn't get subexpression */ goto end_of_job; /* nothing to do, so quit */ /* ------------------------------------------------------------------------- process optional size,lcr preamble if present -------------------------------------------------------------------------- */ /* --- reset size, get lcr's, and push exprptr past preamble --- */ exprptr = preamble(subexpr+1,&size,preptr); /* reset size and get lcr's */ /* --- process lcr's, etc in preamble --- */ irow = icol = 0; /* init lcr counts */ while ( *preptr != '\000' ) /* check preamble text for lcr */ { if ( irow= minhltoklen ) /*token must be at least \hl or \hd*/ if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */ hline[nrows] += 1; /* bump \hline count for row */ else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/ hline[nrows] = (-1); /* set \hdash flag for row */ if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */ { skipwhite(tokptr); /* flush whitespace after \hline */ if ( *tokptr == '\000' /* end-of-expression after \hline */ || isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */ istokwhite = ishonly = 1; /* so token contains \hline only */ else /* token contains more than \hline */ strcpy(token,tokptr); } /* so flush \hline from token */ } /* --- end-of-if(ncols[nrows]==0) --- */ /* --- rasterize completed token --- */ toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */ rasterize(token,size)); /* rasterize non-empty token */ if ( toksp[ntokens] != NULL ) /* have a rasterized token */ nnonwhite++; /* bump rasterized token count */ /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */ if ( toksp[ntokens] != NULL ) /* we have a rasterized token */ { /* --- update max token "height" in current row, and baseline --- */ int twidth = ((toksp[ntokens])->image)->width, /* width of token */ theight = ((toksp[ntokens])->image)->height, /* height of token */ tbaseln = (toksp[ntokens])->baseline, /* baseline of token */ rheight = rowheight[nrows], /* current max height for row */ rbaseln = rowbaseln[nrows]; /* current baseline for max height */ rowheight[nrows] = max2(rbaseln+1,tbaseln+1) /*max hght above baseln*/ + max2(rheight-rbaseln-1,theight-tbaseln-1); /* plus max below */ rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/ /* --- update max token width in current column --- */ icol = ncols[nrows]; /* current column index */ colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/ } /* --- end-of-if(toksp[]!=NULL) --- */ /* --- bump counters --- */ if ( !ishonly ) /* don't count only an \hline */ { ntokens++; /* bump total token count */ ncols[nrows] += 1; } /* and bump #cols in current row */ /* --- get ready for next token --- */ tokptr = token; /* reset ptr for next token */ istokwhite = 1; /* next token starts all white */ } /* --- end-of-if(iseoc) --- */ /* ----------------------------------------------------------------------- bump row as necessary ------------------------------------------------------------------------ */ if ( iseor ) /* we have a completed row */ { maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */ if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/ nrows++; /* bump row count */ ncols[nrows] = 0; /* no cols in this row yet */ if ( !iseox ) /* don't have a null yet */ { exprptr++; /* bump past extra \ in \\ delim */ iseox = (*exprptr == '\000'); } /* recheck for pathological \null */ } /* --- end-of-if(iseor) --- */ /* ----------------------------------------------------------------------- quit when done, or accumulate char in token and proceed to next char ------------------------------------------------------------------------ */ /* --- quit when done --- */ if ( iseox ) break; /* null terminator signalled done */ /* --- accumulate chars in token --- */ if ( !iseoc ) /* don't accumulate delimiters */ { *tokptr++ = *exprptr; /* accumulate non-delim char */ if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */ istokwhite = 0; } /* so reset flag to rasterize it */ /* --- ready for next char --- */ exprptr++; /* bump ptr */ } /* --- end-of-while(*exprptr!='\000') --- */ /* --- make sure we got something to do --- */ if ( nnonwhite < 1 ) /* completely empty array */ goto end_of_job; /* NULL back to caller */ /* ------------------------------------------------------------------------- determine dimensions of array raster and allocate it -------------------------------------------------------------------------- */ /* --- adjust colspace --- */ colspace = 2 + 2*size; /* temp kludge */ /* --- determine width of array raster --- */ width = colspace*(maxcols-1); /* empty space between cols */ for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */ { width += colwidth[icol]; /*width of this col (0 for maxcols)*/ width += vlinespace(icol); } /*plus space for vline, if present*/ /* --- determine height of array raster --- */ height = rowspace*(nrows-1); /* empty space between rows */ for ( irow=0; irow<=nrows; irow++ ) /* and for each row */ { height += rowheight[irow]; /*height of this row (0 for nrows)*/ height += hlinespace(irow); } /*plus space for hline, if present*/ /* --- allocate subraster and raster for array --- */ if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize subraster parameters --- */ arraysp->type = IMAGERASTER; /* image */ arraysp->symdef = NULL; /* not applicable for image */ arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/ arraysp->size = size; /* size (probably unneeded) */ arrayrp = arraysp->image; /* raster embedded in subraster */ /* ------------------------------------------------------------------------- embed tokens/cells in array -------------------------------------------------------------------------- */ itoken = 0; /* start with first token */ toprow = 0; /* start at top row of array */ for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/ { /* --- initialization for row --- */ int baseline = rowbaseln[irow]; /* baseline for this row */ if ( hline[irow] != 0 ) /* need hline above this row */ { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */ if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */ rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */ if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/ toprow += hlinespace(irow); /* space for hline above irow */ leftcol = 0; /* start at leftmost column */ for ( icol=0; icolimage)->width, /* token width */ theight= (tsp->image)->height, /* token height */ tokencol = 0, /*H offset (init for left justify)*/ tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/ /* --- adjust leftcol for vline to left of icol, if present ---- */ leftcol += vlinespace(icol); /* space for vline to left of col */ /* --- reset justification (if not left-justified) --- */ if ( justify[icol] == 0 ) /* but user wants it centered */ tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */ else if ( justify[icol] == 1 ) /* or user wants right-justify */ tokencol = cwidth-twidth; /* so put entire margin at left */ /* --- reset vertical centering (if not baseline-aligned) --- */ if ( rowcenter[irow] ) /* center cells in row vertically */ tokenrow = (rowheight[irow]-theight)/2; /* center row */ /* --- embed token raster at appropriate place in array raster --- */ rastput(arrayrp,tsp->image, /* overlay cell token in array */ toprow+ tokenrow, /*with aligned baseline or centered*/ leftcol+tokencol, 1); /* and justified as requested */ } /* --- end-of-if(tsp!=NULL) --- */ itoken++; /* bump index for next cell */ leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/ } /* --- end-of-for(icol) --- */ toprow += rowheight[irow] + rowspace; /* move toprow down for next row */ } /* --- end-of-for(irow) --- */ /* ------------------------------------------------------------------------- draw vlines as necessary -------------------------------------------------------------------------- */ leftcol = 0; /* start at leftmost column */ for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */ { if ( vline[icol] != 0 ) /* need vline to left of this col */ { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */ if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/ rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */ leftcol += vlinespace(icol); /* space for vline to left of col */ if ( icol < maxcols ) /* don't address past end of array */ leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/ } /* --- end-of-for(icol) --- */ /* ------------------------------------------------------------------------- free workspace and return final result to caller -------------------------------------------------------------------------- */ end_of_job: /* --- free workspace --- */ if ( ntokens > 0 ) /* if we have workspace to free */ while ( --ntokens >= 0 ) /* free each token subraster */ if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */ delete_subraster(toksp[ntokens]); /* then free it */ /* --- return final result to caller --- */ return ( arraysp ); } /* --- end-of-function rastarray() --- */ /* ========================================================================== * Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \picture handler, returns subraster corresponding to picture * expression (immediately following \picture) at font size * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \picture to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \picture * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to picture * expression, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc} * o * ======================================================================= */ /* --- entry point --- */ subraster *rastpicture ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */ putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/ pream[64], *preptr, /* optional put preamble */ picelem[1025]; /* picture element following put */ subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */ *new_subraster(), *picturesp=NULL, /* subraster for entire picture */ *oldworkingbox = workingbox; /* save working box on entry */ raster *picturerp=NULL; /* raster for entire picture */ int delete_subraster(); /* free picelemsp[] workspace */ int pixsz = 1; /* pixels are one bit each */ double strtod(), /* convert ascii params to doubles */ x=0.0,y=0.0, /* x,y-coords for put,multiput*/ xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/ int width=0, height=0, /* #pixels width,height of picture */ ewidth=0, eheight=0, /* pic element width,height */ ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */ num=1, inum; /* number reps, index of element */ int iscenter=0; /* center or lowerleft put position*/ int *oldworkingparam = workingparam, /* save working param on entry */ origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */ int rastput(); /* embed elements in picture */ /* ------------------------------------------------------------------------- First obtain (width,height) arguments immediately following \picture command -------------------------------------------------------------------------- */ /* --- parse for (width,height) arguments, and bump expression past it --- */ *expression = texsubexpr(*expression,putexpr,"(",")",0,0); if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */ /* --- now interpret width,height returned in putexpr --- */ if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/ *putptr = '\000'; /* found it, so replace ',' by '\0'*/ width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/ if ( putptr != NULL ) /* 2nd arg, if present, is height */ height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/ /* ------------------------------------------------------------------------- Then obtain entire picture {...} subexpression following (width,height) -------------------------------------------------------------------------- */ /* --- parse for picture subexpression, and bump expression past it --- */ *expression = texsubexpr(*expression,picexpr,"{","}",0,0); if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */ /* ------------------------------------------------------------------------- allocate subraster and raster for complete picture -------------------------------------------------------------------------- */ /* --- sanity check on width,height args --- */ if ( width < 2 || width > 600 || height < 2 || height > 600 ) goto end_of_job; /* --- allocate and initialize subraster for constructed picture --- */ if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/ == NULL ) goto end_of_job; /* quit if failed */ workingbox = picturesp; /* set workingbox to our picture */ /* --- initialize picture subraster parameters --- */ picturesp->type = IMAGERASTER; /* image */ picturesp->symdef = NULL; /* not applicable for image */ picturesp->baseline = height/2 + 2; /* is a little above center good? */ picturesp->size = size; /* size (probably unneeded) */ picturerp = picturesp->image; /* raster embedded in subraster */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"picture> width,height=%d,%d\n",width,height); /* ------------------------------------------------------------------------- parse out each picture element, rasterize it, and place it in picture -------------------------------------------------------------------------- */ while ( *picptr != '\000' ) /* until we run out of pic_elems */ { /* ----------------------------------------------------------------------- first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem ------------------------------------------------------------------------ */ /* --- init default values in case not explicitly supplied in args --- */ x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */ iscenter = origin = 0; /* center, origin */ /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */ picptr = texsubexpr(picptr,putexpr,"(",")",0,0); if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */ /* --- first look for $-terminated or for any non-digit preamble --- */ *pream = '\000'; /* init preamble as empty string */ if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/ { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */ strncpy(pream,putexpr,64); } /* copy leading preamble from put */ else /* look for any non-digit preamble */ { for ( preptr=pream,putptr=putexpr; ; putptr++ ) if ( *putptr == '\000' /* end-of-putdata signalled */ || !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */ else *preptr++ = *putptr; /* copy alpha char to preamble */ *preptr = '\000'; } /* null-terminate preamble */ /* --- interpret preamble --- */ for ( preptr=pream; ; preptr++ ) /* examine each preamble char */ if ( *preptr == '\000' ) break; /* end-of-preamble signalled */ else switch ( tolower(*preptr) ) /* check lowercase preamble char */ { default: break; /* unrecognized flag */ case 'c': iscenter=1; break; /* center pic_elem at x,y coords */ } /* --- end-of-switch --- */ /* --- interpret x,y;xinc,yinc;num following preamble --- */ if ( *putptr != '\000' ) /*check for put data after preamble*/ { /* --- first squeeze preamble out of put expression --- */ if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */ /* --- interpret x,y --- */ if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/ *multptr = '\000'; /* replace semicolon by '\0' */ if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */ *putptr = '\000'; /* replace comma by '\0' */ if ( *putexpr != '\000' ) /* leading , may be placeholder */ x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/ if ( putptr != NULL ) /* 2nd arg, if present, is y coord */ y = unitlength*strtod(putptr+1,NULL); /* in pixels */ /* --- interpret xinc,yinc,num if we have a multiput --- */ if ( multptr != NULL ) /* found ';' signalling multiput */ { if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/ *preptr = '\000'; /* replace ';' by '\0' */ if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/ *putptr = '\000'; /* replace ',' by '\0' */ if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */ xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */ if ( putptr != NULL ) /* 2nd arg, if present, is yinc */ yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */ num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/ } /* --- end-of-if(multptr!=NULL) --- */ } /* --- end-of-if(*preptr!='\000') --- */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp, "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2lf,%.2lf;%.2lf,%.2lf;%d\n", pream,x,y,xinc,yinc,num); /* ----------------------------------------------------------------------- now obtain {...} picture element following [multi]put, and rasterize it ------------------------------------------------------------------------ */ /* --- parse for {...} picture element and bump picptr past it --- */ picptr = texsubexpr(picptr,picelem,"{","}",0,0); if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem); /* --- rasterize picture element --- */ origin = 0; /* init origin as working param */ workingparam = &origin; /* and point working param to it */ picelemsp = rasterize(picelem,size); /* rasterize picture element */ if ( picelemsp == NULL ) continue; /* failed to rasterize, skip elem */ ewidth = (picelemsp->image)->width; /* width of element, in pixels */ eheight = (picelemsp->image)->height; /* height of element, in pixels */ if ( origin == 55 ) iscenter = 1; /* origin set to (.5,.5) for center*/ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n", ewidth,eheight,origin,num); if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); } /* ----------------------------------------------------------------------- embed element in picture (once, or multiple times if requested) ------------------------------------------------------------------------ */ for ( inum=0; inum=29 ) /* debugging */ fprintf(msgfp, "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2lf,%.2lf,%d,%d,%d,%d\n", inum,x,y,ix,iy,xpos,ypos); /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */ if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break; /* --- apply increment --- */ if ( xinc==0. && yinc==0. ) break; /* quit if both increments zero */ x += xinc; y += yinc; /* increment coords for next iter */ } /* --- end-of-for(inum) --- */ /* --- free picture element subraster after embedding it in picture --- */ delete_subraster(picelemsp); /* done with subraster, so free it */ } /* --- end-of-while(*picptr!=0) --- */ /* ------------------------------------------------------------------------- return picture constructed from pic_elements to caller -------------------------------------------------------------------------- */ end_of_job: workingbox = oldworkingbox; /* restore original working box */ workingparam = oldworkingparam; /* restore original working param */ return ( picturesp ); /* return our picture to caller */ } /* --- end-of-function rastpicture() --- */ /* ========================================================================== * Function: rastline ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \line handler, returns subraster corresponding to line * parameters (xinc,yinc){xlen} * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \line to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \line * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to line * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \line(xinc,yinc){xlen} * o if {xlen} not given, then it's assumed xlen = |xinc| * ======================================================================= */ /* --- entry point --- */ subraster *rastline ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), linexpr[129], *xptr=linexpr; /*line(xinc,yinc){xlen}*/ subraster *new_subraster(), *linesp=NULL; /* subraster for line */ int pixsz = 1; /* pixels are one bit each */ double strtod(), /* convert ascii params to doubles */ xinc=0.0, yinc=0.0, /* x,y-increments for line, */ xlen=0.0, ylen=0.0; /* x,y lengths for line */ int width=0, height=0; /* #pixels width,height of line */ int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */ origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */ int line_raster(); /* draw line in linesp->image */ /* ------------------------------------------------------------------------- obtain (xinc,yinc) arguments immediately following \line command -------------------------------------------------------------------------- */ /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */ *expression = texsubexpr(*expression,linexpr,"(",")",0,0); if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */ /* --- now interpret xinc,yinc returned in linexpr --- */ if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */ *xptr = '\000'; /* found it, so replace ',' by '\0'*/ if ( *linexpr != '\000' ) /* check against missing 1st arg */ xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */ if ( xptr != NULL ) /* 2nd arg, if present, is yinc */ yinc = ylen = strtod(xptr+1,NULL); /* in user units */ /* ------------------------------------------------------------------------- obtain optional {xlen} following (xinc,yinc), and calculate ylen -------------------------------------------------------------------------- */ /* --- check if {xlen} given --- */ if ( *(*expression) == '{' ) /*have {xlen} if leading char is { */ { /* --- parse {xlen} and bump expression past it, interpret as double --- */ *expression = texsubexpr(*expression,linexpr,"{","}",0,0); if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */ xlen = strtod(linexpr,NULL); /* xlen in user units */ /* --- set other values accordingly --- */ if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/ if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/ else xlen = 0.0; /* can't have xlen if xinc=0 */ } /* --- end-of-if(*(*expression)=='{') --- */ /* ------------------------------------------------------------------------- calculate width,height, etc, based on xlen,ylen, etc -------------------------------------------------------------------------- */ /* --- force lengths positive --- */ xlen = absval(xlen); /* force xlen positive */ ylen = absval(ylen); /* force ylen positive */ /* --- calculate corresponding lengths in pixels --- */ width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/ height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */ /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */ if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/ if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/ origin = isright*10 + istop; /* interpret 0=(0,0), 11=(1,1), etc*/ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n", width,height,origin,xinc,yinc); /* ------------------------------------------------------------------------- allocate subraster and raster for complete picture -------------------------------------------------------------------------- */ /* --- sanity check on width,height args --- */ if ( width < 1 || width > 600 || height < 1 || height > 600 ) goto end_of_job; /* --- allocate and initialize subraster for constructed line --- */ if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize line subraster parameters --- */ linesp->type = IMAGERASTER; /* image */ linesp->symdef = NULL; /* not applicable for image */ linesp->baseline = height/2 + 2; /* is a little above center good? */ linesp->size = size; /* size (probably unneeded) */ /* ------------------------------------------------------------------------- draw the line -------------------------------------------------------------------------- */ line_raster ( linesp->image, /* embedded raster image */ (istop? 0 : height-1), /* row0, from bottom or top */ (isright? width-1 : 0), /* col0, from left or right */ (istop? height-1 : 0), /* row1, to top or bottom */ (isright? 0 : width-1), /* col1, to right or left */ 1 ); /* line thickness is 1 pixel */ /* ------------------------------------------------------------------------- return constructed line to caller -------------------------------------------------------------------------- */ end_of_job: if ( workingparam != NULL ) /* caller wants origin */ *workingparam = origin; /* return origin corner to caller */ return ( linesp ); /* return line to caller */ } /* --- end-of-function rastline() --- */ /* ========================================================================== * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \circle handler, returns subraster corresponding to ellipse * parameters (xdiam[,ydiam]) * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \circle to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-4 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \circle * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to ellipse * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \circle(xdiam[,ydiam]) * o * ======================================================================= */ /* --- entry point --- */ subraster *rastcircle ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), circexpr[129],*xptr=circexpr; /*circle(xdiam[,ydiam])*/ char *qptr=NULL, quads[99]="1234"; /* default to draw all quadrants */ subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */ int pixsz = 1; /* pixels are one bit each */ double strtod(), /* convert ascii params to doubles */ xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */ int width=0, height=0; /* #pixels width,height of ellipse */ int thickness = 1; /* drawn lines are one pixel thick */ int origin = 55; /* force origin centered */ int circle_raster(); /* draw ellipse in circsp->image */ /* ------------------------------------------------------------------------- obtain (xdiam[,ydiam]) arguments immediately following \circle command -------------------------------------------------------------------------- */ /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */ *expression = texsubexpr(*expression,circexpr,"(",")",0,0); if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/ /* --- now interpret xdiam[,ydiam] returned in circexpr --- */ if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */ { *qptr = '\000'; /* replace semicolon by '\0' */ strcpy(quads,qptr+1); /* save user-requested quads */ qptr = quads; } /* set qptr arg for circle_raster()*/ if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/ *xptr = '\000'; /* found it, so replace ',' by '\0'*/ xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */ if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */ ydiam = strtod(xptr+1,NULL); /* in user units */ /* ------------------------------------------------------------------------- calculate width,height, etc -------------------------------------------------------------------------- */ /* --- calculate width,height in pixels --- */ width = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/ height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n", width,height,(qptr==NULL?"default":qptr)); /* ------------------------------------------------------------------------- allocate subraster and raster for complete picture -------------------------------------------------------------------------- */ /* --- sanity check on width,height args --- */ if ( width < 1 || width > 600 || height < 1 || height > 600 ) goto end_of_job; /* --- allocate and initialize subraster for constructed ellipse --- */ if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize ellipse subraster parameters --- */ circsp->type = IMAGERASTER; /* image */ circsp->symdef = NULL; /* not applicable for image */ circsp->baseline = height/2 + 2; /* is a little above center good? */ circsp->size = size; /* size (probably unneeded) */ /* ------------------------------------------------------------------------- draw the ellipse -------------------------------------------------------------------------- */ circle_raster ( circsp->image, /* embedded raster image */ 0, 0, /* row0,col0 are upper-left corner */ height-1, width-1, /* row1,col1 are lower-right */ thickness, /* line thickness is 1 pixel */ qptr ); /* "1234" quadrants to be drawn */ /* ------------------------------------------------------------------------- return constructed ellipse to caller -------------------------------------------------------------------------- */ end_of_job: if ( workingparam != NULL ) /* caller wants origin */ *workingparam = origin; /* return center origin to caller */ return ( circsp ); /* return ellipse to caller */ } /* --- end-of-function rastcircle() --- */ /* ========================================================================== * Function: rastbezier ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \bezier handler, returns subraster corresponding to bezier * parameters (col0,row0)(col1,row1)(colt,rowt) * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \bezier to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \bezier * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to bezier * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \bezier(col1,row1)(colt,rowt) * o col0=0,row0=0 assumed, i.e., given by * \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~} * ======================================================================= */ /* --- entry point --- */ subraster *rastbezier ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */ char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/ double strtod(); /* convert ascii params to doubles */ double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */ rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/ rmin=0.0, cmin=0.0, /* minimum r,c */ rmax=0.0, cmax=0.0, /* maximum r,c */ rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */ r=0.0, c=0.0; /* some point */ int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */ int width=0, height=0; /* dimensions of bezier raster */ int pixsz = 1; /* pixels are one bit each */ int thickness = 1; /* drawn lines are one pixel thick */ int origin = 0; /*c's,r's reset to lower-left origin*/ int bezier_raster(); /* draw bezier in bezsp->image */ /* ------------------------------------------------------------------------- obtain (c1,r1)(ct,rt) args immediately following \bezier command -------------------------------------------------------------------------- */ for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0,r0 1=c1,r1 2=ct,rt */ { /* --- parse for (r,c) args, and bump expression past them all --- */ *expression = texsubexpr(*expression,bezexpr,"(",")",0,0); if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/ /* --- now interpret (r,c) returned in bezexpr --- */ c = r = 0.0; /* init x-coord=col, y-coord=row */ if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */ { *xptr = '\000'; /* found it, so replace ',' by '\0'*/ r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */ c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */ /* --- store r,c --- */ switch ( iarg ) { case 0: r0=r; c0=c; break; case 1: r1=r; c1=c; break; case 2: rt=r; ct=c; break; } } /* --- end-of-for(iarg) --- */ /* --- determine midpoint and maximum,minimum points --- */ rmid = 0.5*(rt + 0.5*(r0+r1)); /* y-coord at middle of bezier */ cmid = 0.5*(ct + 0.5*(c0+c1)); /* x-coord at middle of bezier */ rmin = min3(r0,r1,rmid); /* lowest row */ cmin = min3(c0,c1,cmid); /* leftmost col */ rmax = max3(r0,r1,rmid); /* highest row */ cmax = max3(c0,c1,cmid); /* rightmost col */ rdelta = rmax-rmin; /* height */ cdelta = cmax-cmin; /* width */ /* --- rescale coords so we start at 0,0 --- */ r0 -= rmin; c0 -= cmin; /* rescale r0,c0 */ r1 -= rmin; c1 -= cmin; /* rescale r1,c1 */ rt -= rmin; ct -= cmin; /* rescale rt,ct */ /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */ r0 = rdelta - r0 + 1; /* map 0-->height-1, height-1-->0 */ r1 = rdelta - r1 + 1; rt = rdelta - rt + 1; /* --- determine width,height of raster needed for bezier --- */ width = (int)(cdelta + 0.9999) + 1; /* round width up */ height = (int)(rdelta + 0.9999) + 1; /* round height up */ if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; " "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n", width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax); /* ------------------------------------------------------------------------- allocate raster -------------------------------------------------------------------------- */ /* --- sanity check on width,height args --- */ if ( width < 1 || width > 600 || height < 1 || height > 600 ) goto end_of_job; /* --- allocate and initialize subraster for constructed bezier --- */ if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */ == NULL ) goto end_of_job; /* quit if failed */ /* --- initialize bezier subraster parameters --- */ bezsp->type = IMAGERASTER; /* image */ bezsp->symdef = NULL; /* not applicable for image */ bezsp->baseline = height/2 + 2; /* is a little above center good? */ bezsp->size = size; /* size (probably unneeded) */ /* ------------------------------------------------------------------------- draw the bezier -------------------------------------------------------------------------- */ bezier_raster ( bezsp->image, /* embedded raster image */ r0, c0, /* row0,col0 are lower-left corner */ r1, c1, /* row1,col1 are upper-right */ rt, ct ); /* bezier tangent point */ /* ------------------------------------------------------------------------- return constructed bezier to caller -------------------------------------------------------------------------- */ end_of_job: if ( workingparam != NULL ) /* caller wants origin */ *workingparam = origin; /* return center origin to caller */ return ( bezsp ); /* return bezier to caller */ } /* --- end-of-function rastbezier() --- */ /* ========================================================================== * Function: rastraise ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \raisebox{lift}{subexpression} handler, returns subraster * containing subexpression with its baseline "lifted" by lift * pixels, scaled by \unitlength, or "lowered" if lift arg * negative * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \raisebox to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \rotatebox * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to \raisebox * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \raisebox{lift}{subexpression} * o * ======================================================================= */ /* --- entry point --- */ subraster *rastraise ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192], *liftexpr=subexpr; /* args */ subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */ int lift=0; /* amount to raise/lower baseline */ /* ------------------------------------------------------------------------- obtain {lift} argument immediately following \raisebox command -------------------------------------------------------------------------- */ /* --- parse for {lift} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,liftexpr,"{","}",0,0); if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */ lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/ if ( abs(lift) > 200 ) lift=0; /* sanity check */ /* ------------------------------------------------------------------------- obtain {subexpr} argument after {lift}, and rasterize it -------------------------------------------------------------------------- */ /* --- parse for {subexpr} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); /* --- rasterize subexpression to be raised/lowered --- */ if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */ == NULL ) goto end_of_job; /* and quit if failed */ /* ------------------------------------------------------------------------- raise/lower baseline and return it to caller -------------------------------------------------------------------------- */ /* --- raise/lower baseline --- */ raisesp->baseline += lift; /* new baseline (no height checks) */ /* --- return raised subexpr to caller --- */ end_of_job: return ( raisesp ); /* return raised subexpr to caller */ } /* --- end-of-function rastraise() --- */ /* ========================================================================== * Function: rastrotate ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \rotatebox{degrees}{subexpression} handler, returns subraster * containing subexpression rotated by degrees (counterclockwise * if degrees positive) * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \rotatebox to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \rotatebox * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to \rotatebox * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \rotatebox{degrees}{subexpression} * o * ======================================================================= */ /* --- entry point --- */ subraster *rastrotate ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */ subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */ raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */ int delete_raster(); /* delete intermediate rasters */ int baseline=0; /* baseline of rasterized image */ double strtod(), /* convert ascii params to doubles */ degrees=0.0, ipart,fpart; /* degrees to be rotated */ int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */ int n90=0, isn90=1; /* degrees is n90 multiples of 90 */ /* ------------------------------------------------------------------------- obtain {degrees} argument immediately following \rotatebox command -------------------------------------------------------------------------- */ /* --- parse for {degrees} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,degexpr,"{","}",0,0); if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */ degrees = strtod(degexpr,NULL); /* degrees to be rotated */ if ( degrees < 0.0 ) /* clockwise rotation desired */ { degrees = -degrees; /* flip sign so degrees positive */ isneg = 1; } /* and set flag to indicate flip */ fpart = modf(degrees,&ipart); /* integer and fractional parts */ ipart = (double)(((int)degrees)%360); /* degrees mod 360 */ degrees = ipart + fpart; /* restore fractional part */ if ( isneg ) /* if clockwise rotation requested */ degrees = 360.0 - degrees; /* do equivalent counterclockwise */ idegrees = (int)(degrees+0.5); /* integer degrees */ n90 = idegrees/90; /* degrees is n90 multiples of 90 */ isn90 = (90*n90==idegrees); /*true if degrees is multiple of 90*/ isn90 = 1; /* forced true for time being */ /* ------------------------------------------------------------------------- obtain {subexpr} argument after {degrees}, and rasterize it -------------------------------------------------------------------------- */ /* --- parse for {subexpr} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); /* --- rasterize subexpression to be rotated --- */ if ( (rotsp = rasterize(subexpr,size)) /* rasterize subexpression */ == NULL ) goto end_of_job; /* and quit if failed */ /* --- return unmodified image if no rotation requested --- */ if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */ /* --- extract params for image to be rotated --- */ rotrp = rotsp->image; /* unrotated rasterized image */ baseline = rotsp->baseline; /* and baseline of that image */ /* ------------------------------------------------------------------------- rotate by multiples of 90 degrees -------------------------------------------------------------------------- */ if ( isn90 ) /* rotation by multiples of 90 */ if ( n90 > 0 ) /* do nothing for 0 degrees */ { n90 = 4-n90; /* rasrot() rotates clockwise */ while ( n90 > 0 ) /* still have remaining rotations */ { raster *nextrp = rastrot(rotrp); /* rotate raster image */ if ( nextrp == NULL ) break; /* something's terribly wrong */ delete_raster(rotrp); /* free previous raster image */ rotrp = nextrp; /* and replace it with rotated one */ n90--; } /* decrement remaining count */ } /* --- end-of-if(isn90) --- */ /* ------------------------------------------------------------------------- requested rotation not multiple of 90 degrees -------------------------------------------------------------------------- */ if ( !isn90 ) /* explicitly construct rotation */ { ; } /* not yet implemented */ /* ------------------------------------------------------------------------- re-populate subraster envelope with rotated image -------------------------------------------------------------------------- */ /* --- re-init various subraster parameters, embedding raster in it --- */ if ( rotrp != NULL ) /* rotated raster constructed okay */ { rotsp->type = IMAGERASTER; /* signal constructed image */ rotsp->image = rotrp; /* raster we just constructed */ /* --- now try to guess pleasing baseline --- */ if ( idegrees > 2 ) /* leave unchanged if unrotated */ if ( strlen(subexpr) < 3 /* we rotated a short expression */ || abs(idegrees-180) < 3 ) /* or just turned it upside-down */ baseline = rotrp->height - 1; /* so set with nothing descending */ else /* rotated a long expression */ baseline = (65*(rotrp->height-1))/100; /* roughly center long expr */ rotsp->baseline = baseline; } /* set baseline as calculated above*/ /* --- return rotated subexpr to caller --- */ end_of_job: return ( rotsp ); /*return rotated subexpr to caller*/ } /* --- end-of-function rastrotate() --- */ /* ========================================================================== * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \fbox{subexpression} handler, returns subraster * containing subexpression with frame box drawn around it * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \fbox to be * rasterized, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \fbox * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to subraster corresponding to \fbox * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... * \fbox{subexpression} * o * ======================================================================= */ /* --- entry point --- */ subraster *rastfbox ( char **expression, int size, subraster *basesp, int arg1, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192], widtharg[512]; /* args */ subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */ raster *border_raster(), *bp=NULL; /* framed image raster */ double strtod(); /* interpret [width][height] */ int fwidth=6, fthick=1; /*extra frame width, line thickness*/ int width=(-1), height=(-1), /* optional [width][height] args */ iscompose = 0; /* set true if optional args given */ /* ------------------------------------------------------------------------- obtain optional [width][height] arguments immediately following \fbox -------------------------------------------------------------------------- */ /* --- first check for optional \fbox[width] --- */ if ( *(*expression) == '[' ) /* check for []-enclosed width arg */ { *expression = texsubexpr(*expression,widtharg,"[","]",0,0); if ( *widtharg != '\000' ) /* got widtharg */ { width = max2(1,iround(unitlength*strtod(widtharg,NULL))); height = 1; fwidth = 2; iscompose = 1; } } /* --- end-of-if(**expression=='[') --- */ if ( width > 0 ) /* found leading [width], so... */ if ( *(*expression) == '[' ) /* check for []-enclosed height arg */ { *expression = texsubexpr(*expression,widtharg,"[","]",0,0); if ( *widtharg != '\000' ) /* got widtharg */ { height = max2(1,iround(unitlength*strtod(widtharg,NULL))); fwidth = 0; } /* no extra border */ } /* --- end-of-if(**expression=='[') --- */ /* ------------------------------------------------------------------------- obtain {subexpr} argument -------------------------------------------------------------------------- */ /* --- parse for {subexpr} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); /* --- rasterize subexpression to be framed --- */ if ( width<0 || height<0 ) /* no explicit dimensions given */ { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */ == NULL ) goto end_of_job; } /* and quit if failed */ else { char composexpr[8192]; /* compose subexpr with empty box */ sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}", width,height,subexpr); if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */ == NULL ) goto end_of_job; } /* and quit if failed */ /* ------------------------------------------------------------------------- draw frame, reset params, and return it to caller -------------------------------------------------------------------------- */ /* --- draw border --- */ if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1)) == NULL ) goto end_of_job; /* draw border and quit if failed */ /* --- replace original image and raise baseline to accommodate frame --- */ framesp->image = bp; /* replace image with framed one */ if ( !iscompose ) /* simple border around subexpr */ framesp->baseline += fwidth; /* so just raise baseline */ else framesp->baseline = (framesp->image)->height - 1; /* set at bottom */ /* --- return framed subexpr to caller --- */ end_of_job: return ( framesp ); /* return framed subexpr to caller */ } /* --- end-of-function rastfbox() --- */ /* ========================================================================== * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 ) * Purpose: no op -- flush \escape without error * -------------------------------------------------------------------------- * Arguments: expression (I/O) char ** to first char of null-terminated * string immediately following \escape to be * flushed, and returning ptr immediately * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \fbox * (unused, but passed for consistency) * nargs (I) int containing number of {}-args after * \escape to be flushed along with it * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- * Returns: ( subraster * ) NULL subraster ptr * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ subraster *rastnoop ( char **expression, int size, subraster *basesp, int nargs, int arg2, int arg3 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), subexpr[8192]; /* flush dummy args eaten by \escape*/ subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */ /* --- flush accompanying args if necessary --- */ if ( nargs != NOVALUE /* not unspecified */ && nargs > 0 ) /* and args to be flushed */ while ( --nargs >= 0 ) /* count down */ *expression = texsubexpr(*expression,subexpr,"{","}",0,0); /*flush arg*/ /* --- return null ptr to caller --- */ end_of_job: return ( noopsp ); /* return NULL ptr to caller */ } /* --- end-of-function rastnoop() --- */ /* ========================================================================== * Function: aalowpass ( rp, bytemap, grayscale ) * Purpose: calculates a lowpass anti-aliased bytemap * for rp->bitmap, with each byte 0...grayscale-1 * -------------------------------------------------------------------------- * Arguments: rp (I) raster * to raster whose bitmap * is to be anti-aliased * bytemap (O) intbyte * to bytemap, calculated * by applying lowpass filter to rp->bitmap, * and returned (as you'd expect) in 1-to-1 * addressing correspondence with rp->bitmap * grayscale (I) int containing number of grayscales * to be calculated, 0...grayscale-1 * (should typically be given as 256) * -------------------------------------------------------------------------- * Returns: ( int ) 1=success, 0=any error * -------------------------------------------------------------------------- * Notes: o If the center point of the box being averaged is black, * then the entire "average" is forced black (grayscale-1) * regardless of the surrounding points. This is my attempt * to avoid a "washed-out" appearance of thin (one-pixel-wide) * lines, which would otherwise turn from black to a gray shade. * o Also, while the weights for neighbor points are fixed, * you may adjust the center point weight on the compile line. * A higher weight sharpens the resulting anti-aliased image; * lower weights blur it out more (but keep the "center" black * as per the preceding note). * ======================================================================= */ /* --- entry point --- */ int aalowpass (raster *rp, intbyte *bytemap, int grayscale) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int status = 1; /* 1=success, 0=failure to caller */ pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/ int irow=0, icol=0; /* rp->height, rp->width indexes */ int weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */ int adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/ int totwts = 0; /* sum of all weights in matrix */ int isforceavg = 1, /*force avg black if center black?*/ isminmaxwts = 1, /*use wts or #pts for min/max test */ blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ /* --- calculate total weights --- */ weights[4]= centerwt; /* weight for center point */ weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/ totwts = centerwt + 4*(1+adjacentwt); /* tot is center plus neighbors */ /* ------------------------------------------------------------------------- Calculate bytemap as 9-point weighted average over bitmap -------------------------------------------------------------------------- */ for ( irow=0; irowheight; irow++ ) for ( icol=0; icolwidth; icol++ ) { int ipixel = icol + irow*(rp->width); /* center pixel index */ int jrow=0, jcol=0, /* box around ipixel */ bitval = 0, /* value of bit/pixel at jrow,jcol */ iscenter = 0, /* set true if center pixel black */ nadjacent=0, wadjacent=0, /* #adjacent black pixels, their wts*/ ngaps = 0, /* #gaps in 8 pixels around center */ iwt=(-1), sumwts=0; /* weights index, sum in-bound wts */ char adjmatrix[8]; /* adjacency "matrix" */ memset(adjmatrix,0,8); /* zero out adjacency matrix */ bytemap[ipixel] = 0; /* init pixel white */ /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/ for ( jrow=irow-1; jrow<=irow+1; jrow++ ) /* jrow = irow-1...irow+1 */ for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */ { int jpixel = jcol + jrow*(rp->width); /* averaging index */ iwt++; /*always bump weight index*/ if ( jrow<0 || jrow>=rp->height /* if row out pf bounds */ || jcol<0 || jcol>=rp->width ) /* or col out of bounds */ continue; /* ignore this point */ bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */ if ( bitval ) /* this is a black pixel */ { if ( jrow==irow && jcol==icol ) /* and this is center point */ iscenter = 1; /* set flag for center point black */ else /* adjacent point black */ { nadjacent++; /* bump adjacent black count */ adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/ wadjacent += weights[iwt]; } /* sum weights for black pixels */ sumwts += weights[iwt]; /* and sum weights for all pixels */ } /* --- end-of-for(jrow,jcol) --- */ /* --- count gaps --- */ ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */ for ( iwt=0; iwt<7; iwt++ ) /* clockwise around adjacency */ if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */ ngaps /= 2; /*each gap has 2 black/white flips*/ /* --- anti-alias pixel, but leave it black if it was already black --- */ if ( isforceavg && iscenter ) /* force avg if center point black */ bytemap[ipixel] = grayscale-1; /* so force grayscale-1=black */ else /* center point not black */ if ( ngaps <= 2 ) /*don't darken checkerboarded pixel*/ { bytemap[ipixel] = /* 0=white ... grayscale-1=black */ ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */ if ( blackscale > 0 /* blackscale kludge turned on */ && bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */ bytemap[ipixel] = grayscale-1; } /* so force it entirely black */ /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/ if ( !iscenter ) /* apply min/maxadjacent test */ if ( isminmaxwts ) /* min/max refer to adjacent weights*/ { if ( wadjacent < minadjacent /* wts of adjacent points too low */ || wadjacent > maxadjacent ) /* or too high */ bytemap[ipixel] = 0; } /* so leave point white */ else /* min/max refer to #adjacent points*/ { if ( nadjacent < minadjacent /* too few adjacent points black */ || nadjacent > maxadjacent ) /* or too many */ bytemap[ipixel] = 0; } /* so leave point white */ } /* --- end-of-for(irow,icol) --- */ /* ------------------------------------------------------------------------- Back to caller with gray-scale anti-aliased bytemap -------------------------------------------------------------------------- */ end_of_job: return ( status ); } /* --- end-of-function aalowpass() --- */ /* ========================================================================== * Function: aacolormap ( bytemap, nbytes, colors, colormap ) * Purpose: searches bytemap, returning a list of its discrete values * in ascending order in colors[], and returning an "image" * of bytemap (where vales are replaced by colors[] * indexes) in colormap[]. * -------------------------------------------------------------------------- * Arguments: bytemap (I) intbyte * to bytemap containing * grayscale values (usually 0=white * through 255=black) for which colors[] * and colormap[] will be constructed. * nbytes (I) int containing #bytes in bytemap * (usually just #rows * #cols) * colors (O) intbyte * (to be interpreted as ints) * returning a list of the discrete/different * values in bytemap, in ascending value order * colormap (O) intbyte * returning a bytemap "image", * i.e., in one-to-one pixel correspondence * with bytemap, but where the values have been * replaced with corresponding colors[] indexes. * -------------------------------------------------------------------------- * Returns: ( int ) #colors in colors[], or 0 for any error * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ int aacolormap ( intbyte *bytemap, int nbytes, intbyte *colors, intbyte *colormap ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int ncolors = 0, /* #different values in bytemap */ igray, grayscale = 256; /* bytemap contains intbyte's */ intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/ int ibyte; /* bytemap/colormap index */ int isscale = 0; /* true to scale largest val to 255*/ /* ------------------------------------------------------------------------- Accumulate colors[] from values occurring in bytemap -------------------------------------------------------------------------- */ /* --- initialization --- */ if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/ == NULL ) goto end_of_job; /* signal error if malloc() failed */ memset(bytevalues,0,grayscale); /* zero out bytevalues */ /* --- now set 1's at offsets corresponding to values found in bytemap --- */ for ( ibyte=0; ibyte 1 ) /* and if not a "blank" raster */ if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/ { /* --- multiply each colors[] by factor that scales largest to 255 --- */ double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]); for ( igray=1; igray5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); } } /* --- end-of-if(isscale) --- */ /* ------------------------------------------------------------------------- Construct colormap -------------------------------------------------------------------------- */ for ( ibyte=0; ibytepixsz, /* #bits per image pixel */ black1=1, black8=255, /* black for 1-bit, 8-bit pixels */ black = (pixsz==1? black1:black8), /* black value for our image */ scalefactor = (black1+black8-black), /* only scale 1-bit images */ iscenter = 0; /* set true if center pixel black */ /* --- grid dimensions and indexes --- */ int wtheight = weights->height, /* #rows in weight matrix */ wtwidth = weights->width, /* #cols in weight matrix */ imgheight = image->height, /* #rows in image */ imgwidth = image->width; /* #cols in image */ int wtrow, wtrow0 = wtheight/2, /* center row index for weights */ wtcol, wtcol0 = wtwidth/2, /* center col index for weights */ imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */ imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/ /* --- rotated grid variables --- */ static int prevrotate = 0; /* rotate from previous call */ static double costheta = 1.0, /* cosine for previous rotate */ sintheta = 0.0; /* and sine for previous rotate */ double a = 1.0; /* default aspect ratio */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ /* --- refresh trig functions for rotate when it changes --- */ if ( rotate != prevrotate ) /* need new sine/cosine */ { costheta = cos(((double)rotate)/57.29578); /*cos of rotate in radians*/ sintheta = sin(((double)rotate)/57.29578); /*sin of rotate in radians*/ prevrotate = rotate; } /* save current rotate as prev */ /* ------------------------------------------------------------------------- Calculate aapixel as weighted average over image points around ipixel -------------------------------------------------------------------------- */ for ( wtrow=0; wtrow=0 && imgrow=0 && imgcol * whose (mostly for demo), or * (3) command-line arguments (mostly to test). * If no input supplied, expression defaults to "f(x)=x^2", * treated as test (input method 3). * If args entered on command-line (or if no input supplied), * output is (usually) human-viewable ascii raster images on * stdout rather than the usual mime xbitmaps or gif images. * -------------------------------------------------------------------------- * Command-Line Arguments: * When running mimeTeX from the command-line, rather than * from a browser, syntax is * ./mimetex [-d ] dump gif to stdout * [expression expression, e.g., x^2+y^2, * |-f input_file] or read expression from file * [-m msglevel] verbosity of debugging output * [-s fontsize] default fontsize, 0-5 * -d Rather than ascii debugging output, mimeTeX dumps the * actual gif (or xbitmap) to stdout, e.g., * ./mimetex -d x^2+y^2 > expression.gif * creates a gif file containing an image of x^2+y^2 * -f Reads expression from input_file, and automatically * assumes -d switch. The input_file may contain the * expression on one line or spread out over many lines. * MimeTeX will concatanate all lines from input_file * to construct one long expression. Blanks, tabs, and * newlines will just be ignored. * -m 0-99, controls verbosity level for debugging output * (usually used only while testing code). * -s Font size, 0-5. As usual, the font size can * also be specified in the expression by a leading * preamble terminated by $, e.g., 3$f(x)=x^2 displays * f(x)=x^2 at font size 3. Default font size is 2. * -------------------------------------------------------------------------- * Exits: 0=success, 1=some error * -------------------------------------------------------------------------- * Notes: o For an executable that emits mime xbitmaps, compile as * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi * or, alternatively, for an executable that emits gif images * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi * or for gif images with anti-aliasing * cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi * See Notes at top of file for other compile-line -D options. * o Move executable to your cgi-bin directory and either * point your browser to it directly in the form * http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2 * or put a tag in your html document of the form * * where f(x)=x^2 (or any other expression) will be displayed * either as a mime xbitmap or gif image (as per -D flag). * ======================================================================= */ /* ------------------------------------------------------------------------- header files -------------------------------------------------------------------------- */ /* --- standard headers --- */ #include /* ------------------------------------------------------------------------- globals for gif and png callback functions -------------------------------------------------------------------------- */ static raster *bitmap_raster=NULL; /* use 0/1 bitmap image or */ static intbyte *colormap_raster=NULL; /* anti-aliased color indexes */ /* --- anti-aliasing flag (needed by GetPixel() as well as main()) --- */ static int isaa = /* set anti-aliasing flag */ #ifdef AA /* if anti-aliasing requested */ 1; /* turn flag on */ #else 0; /* else turn flag off */ #endif /* ------------------------------------------------------------------------- logging data structure, and default data to be logged -------------------------------------------------------------------------- */ /* --- logging data structure --- */ #define logdata struct logdata_struct /* "typedef" for logdata_struct*/ logdata { /* ----------------------------------------------------------------------- environment variable name, max #chars to display, min msglevel to display ------------------------------------------------------------------------ */ char *name; /* environment variable name */ int maxlen; /* max #chars to display */ int msglevel; /* min msglevel to display data */ } ; /* --- end-of-logdata_struct --- */ /* --- data logged by mimeTeX --- */ static logdata mimelog[] = { /* ------ variable ------ maxlen msglevel ----- */ { "QUERY_STRING", 999, 4 }, { "REMOTE_ADDR", 999, 3 }, { "HTTP_REFERER", 999, 3 }, { "REQUEST_URI", 999, 3 }, { "HTTP_USER_AGENT", 999, 3 }, { "HTTP_X_FORWARDED_FOR", 999, 3 }, { NULL, -1, -1 } /* trailer record */ } ; /* --- end-of-mimelog[] --- */ /* --- entry point --- */ int main ( int argc, char *argv[] ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ /* --- expression to be emitted --- */ static char exprbuffer[16385] = "f(x)=x^2"; /* expression to be processed */ char *expression = exprbuffer; /* ptr to expression */ int size = NORMALSIZE; /* default font size */ char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */ char *mimeprep(); /* preprocess expression */ char *argsignal = ARGSIGNAL; /* signals start of mimeTeX args */ int iarg=0, argnum=0, /*argv[] index for command-line args*/ nswitches = 0, /* number of -switches */ exprarg = 0, /* argv[] index for expression */ infilearg = 0; /* argv[] index for infile */ int isquery = 0, /* true if input from QUERY_STRING */ isformdata = 0, /* true if input from html form */ isdumpimage = 0; /* true to dump image on stdout */ /* --- rasterization --- */ subraster *rasterize(), *sp; /* rasterize expression */ raster *border_raster(), *bp; /* put a border around raster */ int type_raster(), type_bytemap(), /* screen dump function prototypes */ xbitmap_raster(); /* mime xbitmap output function */ /* --- http_referer --- */ char *referer = REFERER, /* http_referer must contain this */ *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */ int isstrstr(); /* search http_referer for referer */ /* --- gif --- */ #if defined(GIF) int GetPixel(); /* feed pixels to gifsave library */ #endif char *gif_outfile = (char *)NULL; /* gif output defaults to stdout */ /* --- anti-aliasing --- */ intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */ colors[256]; /* grayscale vals in bytemap */ int aalowpass(), /*lowpass filter for anti-aliasing*/ grayscale = 256; /* 0-255 grayscales in 8-bit bytes */ int ncolors=2, /* #colors (2=b&w) */ aacolormap(); /* build colormap from bytemap */ /* --- messages --- */ char logfile[256] = "mimetex.log"; /*log queries if msglevel>=LOGLEVEL*/ int logger(); /* logs environ variables */ char *dashes = /* separates logfile entries */ "--------------------------------------------------------------------------"; char *copyright = /* copyright, gnu/gpl notice */ "+-----------------------------------------------------------------------+\n" "|mimeTeX vers 1.40, Copyright(c) 2002-2004, John Forkosh Associates, Inc|\n" "+-----------------------------------------------------------------------+\n" "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n" "| and comes with absolutely no warranty whatsoever. |\n" "+-----------------------------------------------------------------------+"; char *invalid_referer_msg = /* message to invalid http_referer */ "1$\\rm\\array{Please~read~www.forkosh.com/mimetex.html\\\\" "and~install~your~own~mimetex.cgi\\\\Thank~you,~John~Forkosh}"; /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- set global variables --- */ msgfp = stdout; /* for comamnd-line mode output */ /* --- * check QUERY_STRING query for expression overriding command-line arg * ------------------------------------------------------------------- */ if ( query != NULL ) /* check query string from environ */ if ( strlen(query) >= 1 ) /* caller gave us a query string */ { strncpy(expression,query,16385); /* so use it as expression */ isquery = 1; } /* and set isquery flag */ /* --- * process command-line input args (if not a query) * ------------------------------------------------ */ if ( !isquery /* don't have an html query string */ || argc > 1 ) /* or have command-line args, too */ { if ( argsignal != NULL ) /* if compiled with -DARGSIGNAL */ while ( argc > ++iarg ) /* check each argv[] for argsignal */ if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */ { argnum = iarg; /* got it, start parsing next arg */ break; } /* stop looking for argsignal */ while ( argc > ++argnum ) /* check for switches and values, */ if ( *argv[argnum] == '-' ) /* got some '-' switch */ { char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */ argnum++; /* arg following flag/switch is usually its value */ nswitches++; /* another switch on command line */ switch ( flag ) /* see what user wants to tell us */ { /* --- ignore uninterpreted flag --- */ default: argnum--; break; /* unrecognized -flag */ /* --- adjustable program parameters (not checking input) --- */ case 'd': isdumpimage = 1; argnum--; break; case 'e': isdumpimage = 1; gif_outfile=argv[argnum]; break; case 'f': isdumpimage = 1; infilearg=argnum; break; case 'm': msglevel = atoi(argv[argnum]); break; case 'o': istransparent = 0; argnum--; break; case 's': size = atoi(argv[argnum]); break; } /* --- end-of-switch() --- */ } /* --- end-of-if(*argv[]=='-') --- */ else /* this arg not a -flag, so it must be... */ if ( infilearg == 0 ) /* no infile arg yet */ { exprarg = argnum; /* take expression from this arg */ infilearg = (-1); } /* and set infilearg */ else /* we already have an infile arg */ ; /* flush argument */ /* --- * decide whether command-line input overrides query_string * -------------------------------------------------------- */ if ( exprarg > 0 /* expression on command line */ && infilearg <= 0 ) /* and not given in input file */ if ( !isquery /* no conflict if no query_string */ || nswitches > 0 ) /* explicit -switch(es) also given */ { strncpy(expression,argv[exprarg],16385); /* expression from command-line */ isquery = 0; } /* and not from a query_string */ /* --- * read input file for expression * ------------------------------ */ if ( infilearg > 0 ) /* have an -f arg */ { FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */ if ( infile != (FILE *)NULL ) /* opened input file successfully */ { char instring[2049]; /* line from file */ isquery = 0; /* file input, not a query_string */ *expression = '\000'; /* start expresion as empty string */ while ( fgets(instring,2048,infile) != (char *)NULL ) /* read till eof*/ strcat(expression,instring); /* concat line to end of expression*/ fclose ( infile ); } /*close input file after reading expression*/ } /* --- end-of-if(infilearg>0) --- */ } /* --- end-of-if(!isquery) --- */ /* --- * check for
input * ---------------------- */ if ( isquery ) /* must be */ if ( !memcmp(expression,"formdata",8) ) /*must be */ { char *delim=strchr(expression,'='); /* find equal following formdata */ if ( delim != (char *)NULL ) /* found unescaped equal sign */ strncpy(expression,delim+1,16385); /* so shift name= out of expression*/ while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/ *delim = ' '; /* is "shorthand" for blank space */ unescape_url(expression,1); /* convert unescaped %xx's to chars */ msglevel = FORMLEVEL; /* msglevel for forms */ isformdata = 1; } /* set flag to signal form data */ else /* --- query, but not input --- */ unescape_url(expression,0); /* convert _all_ %xx's to chars */ /* --- * check queries for embedded prefixes signalling special processing * ----------------------------------------------------------------- */ if ( isquery ) /* only check queries */ { /* --- check for msglevel=###$ prefix --- */ if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */ { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/ if ( delim != (char *)NULL ) /* check that we found delim */ { *delim = '\000'; /* replace delim with null */ msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */ strncpy(expression,delim+1,16385); } } /* shift out prefix and delim */ /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */ if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */ { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/ if ( delim != (char *)NULL ) /* check that we found delim */ { *delim = '\000'; /* replace delim with null */ strncpy(logfile,expression+8,255); /* interpret xxx in logfile=xxx$ */ strncpy(expression,delim+1,16385); } } /* shift out prefix and delim */ } /* --- end-of-if(isquery) --- */ /* --- * log query (e.g., for debugging) * ------------------------------- */ // Commented out by Martin@Moodle, 10/3/2004 // if ( isquery ) /* only log query_string's */ // if ( msglevel >= LOGLEVEL ) /* check if logging */ // if ( logfile != NULL ) /* if a logfile is given */ // if ( *logfile != '\000' ) /*and if it's not an empty string*/ // if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */ // != NULL ) /* ignore logging if can't open */ // { // logger(msgfp,msglevel,expression,mimelog); /* log query */ // if ( msglevel < DBGLEVEL ) /* logging, but not debugging */ // { fprintf(msgfp,"%s\n",dashes); /* so log separator line, */ // fclose(msgfp); /* close logfile immediately, */ // msgfp = NULL; } /* and reset msgfp pointer */ // } /* --- end-of-if(msglevel>=LOGLEVEL) --- */ // else /* couldn't open logfile */ // msglevel = 0; /* can't emit messages */ /* --- * check if http_referer is allowed to use this image * -------------------------------------------------- */ if ( isquery ) /* not relevant if "interactive" */ if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */ if ( http_referer != NULL ) /* nor if called "standalone" */ if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */ expression = invalid_referer_msg; /* so give him error message */ /* --- * emit copyright, gnu/gpl notice (if "interactive") * ------------------------------------------------- */ if ( !isdumpimage ) /* don't mix ascii with image dump */ if ( !isquery && msgfp!=NULL ) /* called from command line */ fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */ /* ------------------------------------------------------------------------- rasterize expression and put a border around it -------------------------------------------------------------------------- */ /* --- preprocess expression, converting LaTeX constructs for mimeTeX --- */ expression = mimeprep(expression); /* preprocess expression */ /* --- double-check that we actually have an expression to rasterize --- */ if ( expression == NULL ) /* nothing to rasterize */ { if ( !isquery && msgfp!=NULL ) /* emit error if not a query */ fprintf(msgfp,"No expression to rasterize\n"); goto end_of_job; } /* and then quit */ /* --- rasterize expression --- */ if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */ { if ( !isquery && msgfp!=NULL ) /* emit error if not a query */ fprintf(msgfp,"Failed to rasterize %s\n",expression); if ( isquery ) /* or emit error raster if query */ sp = rasterize("\\rm~mimeTeX~failed~to~rasterize\\\\your~expression",1); if ( sp == NULL ) goto end_of_job; } /* re-check for failure */ /* ---no border requested, but this adjusts width to multiple of 8 bits--- */ bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */ sp->image = bitmap_raster = bp; /* global copy for gif,png output */ /* ------------------------------------------------------------------------- generate anti-aliased bytemap from (bordered) bitmap -------------------------------------------------------------------------- */ if ( isaa ) /* we want to anti-alias bitmap */ { /* --- * allocate bytemap and colormap as per width*height of bitmap * ----------------------------------------------------------- */ int nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/ if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */ == NULL ) isaa = 0; /* reset flag if malloc failed */ else /* bytemap malloc succeeded, so... */ if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */ == NULL ) isaa = 0; /* reset flag if malloc failed */ /* --- * now generate anti-aliased bytemap and colormap from bitmap * ---------------------------------------------------------- */ if ( isaa ) /*re-check that we're anti-aliasing*/ { /* --- * select anti-aliasing algorithm * ------------------------------ */ if ( 1 ) /* lowpass is the only algorithm we presently have */ if ( aalowpass(bp,bytemap_raster,grayscale) /* lowpass filter */ == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/ /* --- * finally, generate colors and colormap * ------------------------------------- */ if ( isaa ) { /* we have bytemap_raster */ ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster); if ( ncolors < 2 ) /* failed */ { isaa = 0; /* so turn off anti-aliasing */ ncolors = 2; } /* and reset for black&white */ } /* --- end-of-if(isaa) --- */ } /* --- end-of-if(isaa) --- */ } /* --- end-of-if(isaa) --- */ /* ------------------------------------------------------------------------- display results on msgfp if called from command line (usually for testing) -------------------------------------------------------------------------- */ if ( !isquery || msglevel >= 99 ) /*called from command line or debug*/ if ( !isdumpimage ) /* don't mix ascii with image dump */ { /* --- * display ascii image of rasterize()'s rasterized bitmap * ------------------------------------------------------ */ fprintf(msgfp,"\nAscii dump of bitmap image...\n"); type_raster(bp,msgfp); /* emit ascii image of raster */ /* --- * display anti-aliasing results applied to rasterized bitmap * ---------------------------------------------------------- */ if ( isaa ) /* if anti-aliasing applied */ { int igray; /* colors[] index */ /* --- anti-aliased bytemap image --- */ if ( msglevel >= 9 ) /* don't usually emit raw bytemap */ { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/ "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1); type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); } /* --- colormap image --- */ fprintf(msgfp,"\nHex dump of colormap indexes, " /* emit colormap */ "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1); type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp); /* --- rgb values corresponding to colormap indexes */ fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors); for ( igray=0; igray%3d", (igray%5?" ":"\n"), igray,(int)(colors[ncolors-1]-colors[igray])); fprintf(msgfp,"\n"); /* always needs a final newline */ } /* --- end-of-if(isaa) --- */ } /* --- end-of-if(!isquery||msglevel>=9) --- */ /* ------------------------------------------------------------------------- emit xbitmap or gif image, and exit -------------------------------------------------------------------------- */ if ( isquery /* called from browser (usual) */ || isdumpimage /* or to emit dump of image */ || msglevel >= 99 ) /* or for debugging */ { int igray = 0; /* grayscale index */ #if defined(GIF) /* compiled to emit gif */ /* ------------------------------------------------------------------------ emit GIF image ------------------------------------------------------------------------- */ /* --- emit mime content-type line --- */ if ( !isdumpimage ) /* don't mix ascii with image dump */ { fprintf( stdout, "Cache-Control: max-age=7200\n" ); /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/ /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/ fprintf( stdout, "Content-type: image/gif\n\n" ); } /* --- initialize gifsave library and colors --- */ GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8); /*init gifsave*/ GIF_SetColor(0,bgcolor,bgcolor,bgcolor); /* background white if 255 */ if ( !isaa ) /* just b&w if not anti-aliased */ { GIF_SetColor(1,fgcolor,fgcolor,fgcolor); /* foreground black if 0 */ colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */ else /* set grayscales for anti-aliasing */ /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */ for ( igray=1; igray= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } /* --- end-of-function x2c() --- */ /* ========================================================================== * Function: logger ( fp, msglevel, message, logvars ) * Purpose: Logs the environment variables specified in logvars * to fp if their msglevel is >= the passed msglevel. * -------------------------------------------------------------------------- * Arguments: fp (I) FILE * to file containing log * msglevel (I) int containing logging message level * message (I) char * to optional message, or NULL * logvars (I) logdata * to array of environment variables * to be logged * -------------------------------------------------------------------------- * Returns: ( int ) number of variables from logvars * that were actually logged * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ int logger ( FILE *fp, int msglevel, char *message, logdata *logvars ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ int ilog=0, nlogged=0; /* logvars[] index, #vars logged */ char *timestamp(); /* timestamp logged */ char *value = NULL; /* getenv(name) to be logged */ /* ------------------------------------------------------------------------- Log each variable -------------------------------------------------------------------------- */ fprintf(fp,"%s\n",timestamp()); /* emit timestamp before first var */ if ( message != NULL ) /* optional message supplied */ fprintf(fp," MESSAGE = %s\n",message); /* emit caller-supplied message */ for ( ilog=0; logvars[ilog].name != NULL; ilog++ ) /* till end-of-table */ if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */ if ( (value=getenv(logvars[ilog].name)) /* getenv(name) to be logged */ != NULL ) /* check that name exists */ { fprintf(fp," %s = %.*s\n", /* emit variable name = value */ logvars[ilog].name,logvars[ilog].maxlen,value); nlogged++; /* bump #vars logged */ } /* --- end-of-for(ilog) --- */ return ( nlogged ); /* back to caller */ } /* --- end-of-function logger() --- */ /* ========================================================================== * Function: timestamp ( ) * Purpose: returns null-terminated character string containing * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm} * -------------------------------------------------------------------------- * Arguments: ( none ) * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to null-terminated buffer * containing current date:time stamp * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ char *timestamp( ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ static char timebuff[64]; /* date:time buffer back to caller */ long time_val = 0L; /* binary value returned by time() */ struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */ int year=0, hour=0,ispm=1; /* adjust year, and set am/pm hour */ /* ------------------------------------------------------------------------- get current date:time, adjust values, and and format stamp -------------------------------------------------------------------------- */ /* --- get current date:time --- */ time(&time_val); /* get date and time */ tmstruct = localtime(&time_val); /* interpret time_val */ /* --- adjust year and hour as necessary --- */ year = (int)(tmstruct->tm_year); /* local copy of year */ hour = (int)(tmstruct->tm_hour); /* local copy of hour */ year += 1900; /* set century in year */ if ( hour < 12 ) /* am check */ { ispm=0; /* reset pm flag */ if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */ if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */ /* --- format date:time stamp --- */ sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,(int)((tmstruct->tm_mon)+1),(int)(tmstruct->tm_mday), hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am")); return ( timebuff ); /* return stamp to caller */ } /* --- end-of-function timestamp() --- */ #if defined(GIF) /* ========================================================================== * Function: GetPixel ( int x, int y ) * Purpose: callback for GIF_CompressImage() returning the * pixel at column x, row y * -------------------------------------------------------------------------- * Arguments: x (I) int containing column=0...width-1 * of desired pixel * y (I) int containing row=0...height-1 * of desired pixel * -------------------------------------------------------------------------- * Returns: ( int ) 0 or 1, if pixel at x,y is off or on * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ int GetPixel ( int x, int y ) { int ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/ if ( !isaa ) /* use bitmap if not anti-aliased */ return (int)getlongbit(bitmap_raster->pixmap,ipixel); /* pixel = 0 or 1 */ else /* else use anti-aliased grayscale*/ return (int)(colormap_raster[ipixel]); /* colors[] index number */ } /* --- end-of-function GetPixel() --- */ #endif /* gif */ #endif /* driver */ /* ======================= END-OF-FILE MIMETEX.C ========================= */