--- loncom/cgi/mimeTeX/mimetex.c 2005/02/28 19:08:11 1.1 +++ loncom/cgi/mimeTeX/mimetex.c 2012/06/09 00:58:11 1.5 @@ -1,10 +1,11 @@ /**************************************************************************** * - * Copyright(c) 2002-2005, John Forkosh Associates, Inc. All rights reserved. + * Copyright(c) 2002-2012, John Forkosh Associates, Inc. All rights reserved. + * http://www.forkosh.com mailto: john@forkosh.com * -------------------------------------------------------------------------- * 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. + * version 3 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. @@ -12,10 +13,11 @@ * agreed to these terms and conditions, and that you 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 + * Your mimetex.zip distribution file should contain the file COPYING, + * an ascii text copy of the GNU General Public License, version 3. + * If not, point your browser to http://www.gnu.org/licenses/ + * or write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * -------------------------------------------------------------------------- * * Purpose: o MimeTeX, licensed under the gpl, lets you easily embed @@ -25,23 +27,43 @@ * entirely separate little program that doesn't use TeX or * its fonts in any way. It's just one cgi that you put in * your site's cgi-bin/ directory, with no other dependencies. - * So mimeTeX is very easy to install. And it's equally easy - * to use. Just place an html tag in your document + * So mimeTeX is very easy to install. And it's equally + * easy to use. Just place an html tag in your document * wherever you want to see the corresponding LaTeX expression. * For example, * * immediately generates the corresponding gif image on-the-fly, * displaying the rendered expression wherever you put that - * tag. MimeTeX doesn't need intermediate dvi-to-gif - * conversion, and it doesn't clutter up your filesystem with - * separate little gif files for each converted expression. + * tag. + * MimeTeX doesn't need intermediate dvi-to-gif conversion, + * and it doesn't clutter up your filesystem with separate + * little gif files for each converted expression. + * But image caching is available by using mimeTeX's + * -DCACHEPATH=\"path/\" compile option (see below). * There's also no inherent need to repeatedly write the * cumbersome tag illustrated above. You can write * your own custom tags, or write a wrapper script around - * mimeTeX to simplify the necessary notation. + * mimeTeX to simplify the notation. + * Further discussion about mimeTeX's features and + * usage is available on its homepage, + * http://www.forkosh.com/mimetex.html + * and similarly in mimetex.html included with your mimetex.zip + * distribution file. (Note: http://www.forkosh.com/mimetex.html + * is a "quickstart" version of the the full mimetex.html manual + * included in your mimetex.zip distribution file.) * - * Functions: ===================== Raster Functions ====================== + * Functions: The following "table of contents" lists each function + * comprising mimeTeX in the order it appears in this file. + * See individual function entry points for specific comments + * about its purpose, calling sequence, side effects, etc. + * (All these functions eventually belong in several + * different modules, possibly along the lines suggested + * by the divisions below. 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.) + * ===================== Raster Functions ====================== * PART2 --- raster constructor functions --- * new_raster(width,height,pixsz) allocation (and constructor) * new_subraster(width,height,pixsz)allocation (and constructor) @@ -53,14 +75,18 @@ * rastcpy(rp) allocate new copy of rp * subrastcpy(sp) allocate new copy of sp * rastrot(rp) new raster rotated right 90 degrees to rp + * rastmag(rp,magstep) new raster magnified by "magstep" to rp + * bytemapmag(bytemap,width,height,magstep) magnify bytemap + * rastref(rp,axis) new raster reflected (axis 1=horz,2=vert) * 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 - * rastsquash(sp1,sp2,xmin,ymin) calc #squash pixels sp1||sp2 + * rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2 + * rastsmashcheck(term) check if term is "safe" to smash * --- raster "drawing" functions --- - * accent_subraster(accent,width,height) draw \hat\vec\etc + * accent_subraster(accent,width,height,direction,pixsz)\hat\vec * 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 @@ -70,20 +96,25 @@ * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1) * bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively * border_raster(rp,ntop,nbot,isline,isfree)put border around rp + * backspace_raster(rp,nback,pback,minspace,isfree) neg space * --- 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 + * type_pbmpgm(rp,ptype,file) pbm or pgm image of rp to file * 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 + * gftobitmap(rp) convert .gf-like pixmap to bitmap image * ====================== Font Functions ======================= * --- font lookup functions --- - * get_symdef(symbol) returns mathchardef for symbol - * get_chardef(symdef,size) returns chardef for symdef,size + * get_symdef(symbol) return mathchardef for symbol + * get_ligature(expr,family) return symtable index for ligature + * get_chardef(symdef,size) return chardef for symdef,size * get_charsubraster(symdef,size) wrap subraster around chardef + * get_symsubraster(symbol,size) returns subraster for symbol * --- ancillary font functions --- * get_baseline(gfdata) determine baseline (in our coords) * get_delim(symbol,height,family) delim just larger than height @@ -91,6 +122,7 @@ * ================= Tokenize/Parse Functions ================== * texchar(expression,chartoken) retruns next char or \sequence * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim) + * texleft(expr,subexpr,maxsubsz,ldelim,rdelim) \left...\right * texscripts(expression,subscript,superscript,which)get scripts * --- ancillary parse functions --- * isbrace(expression,braces,isescape) check for leading brace @@ -98,8 +130,16 @@ * mimeprep(expression) preprocessor converts \left( to \(, etc. * strchange(nfirst,from,to) change nfirst chars of from to to * strreplace(string,from,to,nreplace) change from to to in str + * strwstr(string,substr,white,sublen) find substr in string + * strdetex(s,mode) replace math chars like \^_{} for display * strtexchr(string,texchr) find texchr in string * findbraces(expression,command) find opening { or closing } + * strpspn(s,reject,segment) non-() chars of s not in reject + * isstrstr(string,snippets,iscase) are any snippets in string? + * isnumeric(s) determine if s is an integer + * evalterm(store,term) evaluate numeric value of expression + * getstore(store,identifier)return value corresponding to ident + * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded * PART3 =========== Rasterize an Expression (recursively) =========== * --- here's the primary entry point for all of mimeTeX --- * rasterize(expression,size) parse and rasterize expression @@ -110,6 +150,8 @@ * rastdispmath(expression,size,sp) scripts for displaymath * --- table-driven handlers that rasterize... --- * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right + * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right + * rastmiddle(expression,size,basesp,arg1,arg2,arg3) \middle * rastflags(expression,size,basesp,flag,value,arg3) set flag * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\; * rastnewline(expression,size,basesp,arg1,arg2,arg3) \\ @@ -126,42 +168,76 @@ * rastarray(expression,size,basesp,arg1,arg2,arg3) \array * rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture * rastline(expression,size,basesp,arg1,arg2,arg3) \line + * rastrule(expression,size,basesp,arg1,arg2,arg3) \rule * 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 + * rastmagnify(expression,size,basesp,arg1,arg2,arg3) \magnify + * rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox * rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox * rastinput(expression,size,basesp,arg1,arg2,arg3) \input * rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter + * rasteval(expression,size,basesp,arg1,arg2,arg3) \eval + * rasttoday(expression,size,basesp,arg1,arg2,arg3) \today + * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar + * rastenviron(expression,size,basesp,arg1,arg2,arg3) \environ + * rastmessage(expression,size,basesp,arg1,arg2,arg3) \message * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape * --- helper functions for handlers --- * rastopenfile(filename,mode) opens filename[.tex] in mode - * rastreadfile(filename,tag,value) read between ... + * rasteditfilename(filename) edit filename (for security) + * rastreadfile(filename,islock,tag,value) read ... * rastwritefile(filename,tag,value,isstrict)write... - * timestamp( ) formats timestamp string (used by rastcounter) - * dtoa(d,npts) double to comma-separated ascii + * calendar(year,month,day) formats one-month calendar string + * timestamp(tzdelta,ifmt) formats timestamp string + * tzadjust(tzdelta,year,month,day,hour) adjust date/time + * daynumber(year,month,day) #days since Monday, Jan 1, 1973 + * strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines + * strnlower(s,n) lowercase the first n chars of string s + * urlprune(url,n) http://abc.def.ghi.com/etc-->abc.def.ghi.com + * urlncmp(url1,url2,n) compares topmost n levels of two url's + * dbltoa(d,npts) double to comma-separated ascii * === Anti-alias completed raster (lowpass) or symbols (ss) === * aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap * aapnm(rp,bytemap,grayscale) lowpass based on pnmalias.c + * aapnmlookup(rp,bytemap,grayscale) aapnm based on aagridnum() + * aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19, + * aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn + * aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern + * aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern + * aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern + * aafollowline(rp,irow,icol,direction) looks for a "turn" + * aagridnum(rp,irow,icol) calculates gridnum, 0-511 + * aapatternnum(gridnum) looks up pattern#, 1-51, for gridnum + * aalookup(gridnum) table lookup for all possible 3x3 grids + * aalowpasslookup(rp,bytemap,grayscale) driver for aalookup() * aasupsamp(rp,aa,sf,grayscale) or by supersampling * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap * aaweights(width,height) builds "canonical" weight matrix * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel + * === miscellaneous === + * mimetexsetmsg(newmsglevel,newmsgfp) set msglevel and msgfp * PART1 ========================== Driver =========================== * main(argc,argv) parses math expression and emits mime xbitmap - * isstrstr(string,snippets,iscase) are any snippets in string? + * CreateGifFromEq(expression,gifFileName) entry pt for win dll * ismonth(month) is month current month ("jan"-"dec")? - * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded * logger(fp,msglevel,logvars) logs environment variables - * emitcache(cachefile) read cachefile and emit bytes to stdout + * emitcache(cachefile,maxage,valign,isbuffer) emit cachefile + * readcachefile(cachefile,buffer) read cachefile into buffer + * advertisement(expression,mode) wrap expression in ad message + * crc16(s) 16-bit crc of string s * md5str(instr) md5 hash library functions * 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 -DAA or -DGIF) + * and also needs gifsave.c when compiled with -DAA or -DGIF) * * -------------------------------------------------------------------------- - * Notes o See bottom of file for main() driver (and "friends"), + * Notes o See individual function entry points for specific comments + * about the purpose, calling sequence, side effects, etc + * of each mimeTeX function listed above. + * o See bottom of file for main() driver (and "friends"), * and compile as * cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi * to produce an executable that emits gif images with @@ -173,13 +249,39 @@ * to produce an executable that just emits mime xbitmaps. * In either case you'll need mimetex.h and texfonts.h, * and with -DAA or -DGIF you'll also need gifsave.c + * 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). Compile gfuntype as + * cc gfuntype.c mimetex.c -lm -o gfuntype + * See gfuntype.c, and also mimetex.html#fonts, for details. * 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 it's documented in mimetex.html#gifsave . + * (a)sending output to stdout or returning it in memory, + * and (b)specifying a transparent background color index, + * is included with mimeTeX, and it's documented in + * mimetex.html#gifsave + * o MimeTeX's principal 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. + * See mimetex.html#makeraster for further discussion + * and examples. + * o File mimetex.c also 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. As already mentioned, + * a complete list of mimetex.c functions is above. See their + * individual entry points below for further comments. + * As also mentioned, 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 Optional compile-line -D defined symbols are documented - * in mimetex.html#options . They include... + * in mimetex.html#options . They include (additional -D + * switches are discussed at mimetex.html#options)... * -DAA * Turns on gif anti-aliasing with default values * (CENTERWT=32, ADJACENTWT=3, CORNERWT=1) @@ -187,15 +289,18 @@ * -DCENTERWT=n * -DADJACENTWT=j * -DCORNERWT=k + * *** Note: Ignore these three switches because + * *** mimeTeX's current anti-aliasing algorithm + * *** no longer uses them (as of version 1.60). * MimeTeX currently provides a lowpass filtering * algorithm for anti-aliasing, which is applied to the * existing set of bitmap fonts. This lowpass filter * applies default weights - * 1 3 1 - * 3 32 3 - * 1 3 1 + * 1 2 1 + * 2 8 2 + * 1 2 1 * to neighboring pixels. The defaults weights are - * CENTERWT=32, ADJACENTWT=3 and CORNERWT=1, + * CENTERWT=8, ADJACENTWT=2 and CORNERWT=1, * which you can adjust to control anti-aliasing. * Lower CENTERWT values will blur/spread out lines * while higher values will tend to sharpen lines. @@ -216,6 +321,12 @@ * be writable by it. Files created under path/ are * named filename.gif, where filename is the 32-character * MD5 hash of the LaTeX expression. + * -DDEFAULTSIZE=n + * MimeTeX currently has eight font sizes numbered 0-7, + * and always starts in DEFAULTSIZE whose default value + * is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4 + * on the compile line if you prefer mimeTeX to start in + * larger default size 4 (corresponding to \Large), etc. * -DDISPLAYSIZE=n * By default, operator limits like \int_a^b are rendered * \textstyle at font sizes \normalsize and smaller, @@ -227,11 +338,15 @@ * \textstyle, \displaystyle, \limits or \nolimits * directives in an expression always override * the DISPLAYSIZE default. - * -NORMALSIZE=n - * MimeTeX currently has six font sizes numbered 0-5, - * and always starts in NORMALSIZE whose default value - * is 2. Specify -DNORMALSIZE=3 on the compile line if - * you prefer mimeTeX to start in default size 3, etc. + * -DERRORSTATUS=n + * The default, 0, means mimeTeX always exits with status 0, + * regardless of whether or not it detects error(s) while + * trying to render your expression. Specify any non-zero + * value (typically -1) if you write a script/plugin for + * mimeTeX that traps non-zero exit statuses. MimeTeX then + * exits with its own non-zero status when it detects an + * error it can identify, or with your ERRORSTATUS value + * for errors it can't specifically identify. * -DREFERER=\"domain\" -or- * -DREFERER=\"domain1,domain2,etc\" * Blocks mimeTeX requests from unauthorized domains that @@ -262,28 +377,6 @@ * MimeTeX usually renders black symbols on a white * background. This option renders white symbols on * a black background 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. @@ -294,10 +387,26 @@ * 02/01/04 J.Forkosh Version 1.40 released. * 10/02/04 J.Forkosh Version 1.50 released. * 11/30/04 J.Forkosh Version 1.60 released. + * 10/11/05 J.Forkosh Version 1.64 released. + * 11/30/06 J.Forkosh Version 1.65 released. + * 09/06/08 J.Forkosh Version 1.70 released. + * 03/23/09 J.Forkosh Version 1.71 released. + * 11/18/09 J.Forkosh Version 1.72 released. + * 11/15/11 J.Forkosh Version 1.73 released. + * 02/15/12 J.Forkosh Version 1.74 released. + * 03/31/12 J.Forkosh Most recent revision (also see REVISIONDATE) + * See http://www.forkosh.com/mimetexchangelog.html for further details. * ****************************************************************************/ /* ------------------------------------------------------------------------- +Program id +-------------------------------------------------------------------------- */ +#define VERSION "1.74" /* mimeTeX version number */ +#define REVISIONDATE "31 March 2012" /* date of most recent revision */ +#define COPYRIGHTTEXT "Copyright(c) 2002-2012, John Forkosh Associates, Inc" + +/* ------------------------------------------------------------------------- header files and macros -------------------------------------------------------------------------- */ /* --- standard headers --- */ @@ -308,11 +417,52 @@ header files and macros #include #include #include +extern char **environ; /* for \environment directive */ + +/* ------------------------------------------------------------------------- +messages (used mostly by main() and also by rastmessage()) +-------------------------------------------------------------------------- */ +static char *copyright1 = /* copyright, gnu/gpl notice */ + "+-----------------------------------------------------------------------+\n" + "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT "|\n" + "+-----------------------------------------------------------------------+\n" + "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n" + "| and comes with absolutely no warranty whatsoever. |", +*copyright2 = + "| See http://www.forkosh.com/mimetex.html for details. |\n" + "+-----------------------------------------------------------------------+"; +static int maxmsgnum = 3, /* maximum msgtable[] index */ + /* --- keep these message numbers updated if table changes --- */ + invmsgnum = 0, /* general invalid message */ + refmsgnum = 3; /* urlncmp() failed to validate */ +static char *msgtable[] = { /* messages referenced by [index] */ + "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */ + "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~" + "on~your~own~server.\\\\Thank~you,~John~Forkosh}}", + "\\red\\small\\rm\\fbox{\\array{" /* [1] */ + "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\" + "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\" + "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}", + "\\red\\small\\rm\\fbox{\\array{" /* [2] */ + "The~public~mimetex~server~is~for~testing.~~For~production,\\\\" + "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\" + "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}", + "\\red\\small\\rm\\fbox{\\array{" /* [3] */ + "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\" + "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\" + "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}", + NULL } ; /* trailer */ -/* --- windows-specific header info --- */ +/* ------------------------------------------------------------------------- +additional symbols +-------------------------------------------------------------------------- */ +/* --- + * windows-specific header info + * ---------------------------- */ #ifndef WINDOWS /* -DWINDOWS not supplied by user */ - #if defined(_WIN32) || defined(WIN32) \ - || defined(DJGPP) /* try to recognize windows */ + #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \ + || defined(DJGPP) /* try to recognize windows compilers */ \ + || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */ #define WINDOWS /* signal windows */ #endif #endif @@ -328,12 +478,19 @@ header files and macros #if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */ #define HAVE_SETMODE /* so we'll use setmode() */ #endif + #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */ + /* to show source file and line numbers where memory leaks occur... */ + #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */ + #include /* and this debug library */ + #endif #define ISWINDOWS 1 #else #define ISWINDOWS 0 #endif -/* --- check for supersampling or low-pass anti-aliasing --- */ +/* --- + * check for supersampling or low-pass anti-aliasing + * ------------------------------------------------- */ #ifdef SS #define ISSUPERSAMPLING 1 #ifndef AAALGORITHM @@ -348,11 +505,16 @@ header files and macros #else #define ISSUPERSAMPLING 0 #ifndef AAALGORITHM - #define AAALGORITHM 2 /* default lowpass algorithm */ + #define AAALGORITHM 3 /*2*/ /* default lowpass algorithm */ #endif #endif +#ifndef MAXFOLLOW + #define MAXFOLLOW 8 /* aafollowline() maxturn default */ +#endif -/* --- set aa (and default gif) if any anti-aliasing options specified --- */ +/* --- + * set aa (and default gif) if any anti-aliasing options specified + * --------------------------------------------------------------- */ #if defined(AA) || defined(GIF) || defined(PNG) \ || defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \ || defined(MINADJACENT) || defined(MAXADJACENT) @@ -376,40 +538,85 @@ header files and macros #endif #endif -/* --- decide whether to compile main() --- */ +/* --- + * decide whether or not to compile main() + * --------------------------------------- */ #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 - /* --- max query_string length if no http_referer supplied --- */ - #ifndef NOREFMAXLEN - #define NOREFMAXLEN 9999 /* default to any length query */ + /* --- yes, compile main() --- */ + #define DRIVER /* main() driver will be compiled */ +#else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */ + #ifndef TEXFONTS + #define NOTEXFONTS /* texfonts not required */ #endif -#else - #define NOTEXFONTS /* texfonts not required */ #endif -/* --- application headers --- */ +/* --- + * application headers + * ------------------- */ #if !defined(NOTEXFONTS) && !defined(TEXFONTS) #define TEXFONTS /* to include texfonts.h */ #endif #include "mimetex.h" +/* --- + * info needed when gif image returned in memory buffer + * ---------------------------------------------------- */ +#ifdef GIF /* compiling along with gifsave.c */ + extern int gifSize; + extern int maxgifSize; +#else /* or just set dummy values */ + static int gifSize=0, maxgifSize=0; +#endif +/* --- gamma correction --- */ +#ifndef GAMMA + #define GAMMA 1.25 /*1.75*/ /*2.2*/ +#endif +#ifndef REVERSEGAMMA + #define REVERSEGAMMA 0.5 /* for \reverse white-on-black */ +#endif +/* --- opaque background (default to transparent) --- */ +#ifndef OPAQUE + #define ISTRANSPARENT 1 +#else + #define ISTRANSPARENT 0 +#endif + +/* --- + * internal buffer sizes + * --------------------- */ +#if !defined(MAXEXPRSZ) + #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/ +#endif +#if !defined(MAXSUBXSZ) + #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/ +#endif +#if !defined(MAXTOKNSZ) + #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */ +#endif +#if !defined(MAXFILESZ) + #define MAXFILESZ (65536-1) /*max #bytes in input (output) file*/ +#endif +#if !defined(MAXLINESZ) + #define MAXLINESZ (4096-1) /* max #chars in line from file */ +#endif +#if !defined(MAXGIFSZ) + #define MAXGIFSZ 131072 /* max #bytes in output GIF image */ +#endif + /* ------------------------------------------------------------------------- adjustable default values -------------------------------------------------------------------------- */ -/* --- anti-aliasing parameters --- */ +/* --- + * anti-aliasing parameters + * ------------------------ */ #ifndef CENTERWT + /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */ /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */ - /*#define CENTERWT 6*/ /* anti-aliasing centerwt default */ - #define CENTERWT 32 /* anti-aliasing centerwt default */ + #define CENTERWT 8 /* anti-aliasing centerwt default */ #endif #ifndef ADJACENTWT /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/ - /*#define ADJACENTWT 2*/ /* anti-aliasing adjacentwt default*/ - #define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/ + #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/ #endif #ifndef CORNERWT #define CORNERWT 1 /* anti-aliasing cornerwt default*/ @@ -428,7 +635,7 @@ GLOBAL(int,minadjacent,MINADJACENT); /* GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */ GLOBAL(int,weightnum,1); /* font wt, */ GLOBAL(int,maxaaparams,4); /* #entries in table */ -/* --- parameter values by font weight --- */ +/* --- anti-aliasing parameter values by font weight --- */ #define aaparameters struct aaparameters_struct /* typedef */ aaparameters { int centerwt; /* lowpass matrix center pixel wt*/ @@ -450,6 +657,9 @@ STATIC aaparameters aaparams[] /* set p } /* --- end-of-aaparams[] --- */ #endif ; +/* --- anti-aliasing diagnostics (to help improve algorithm) --- */ +STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/ + ispatternnumcount = 1; /* true to accumulate counts */ /* ------------------------------------------------------------------------- other variables @@ -473,20 +683,31 @@ other variables #ifndef FGBLUE #define FGBLUE (ISBLACKONWHITE?0:255) #endif -/* --- "squash" margin (0 means no squashing) --- */ -#ifndef SQUASHMARGIN - #ifdef NOSQUASH - #define SQUASHMARGIN 0 +/* --- advertisement + one image in every ADFREQUENCY is wrapped in "advertisement" --- */ +#if !defined(ADFREQUENCY) + #define ADFREQUENCY 0 /* never show advertisement if 0 */ +#endif +#ifndef HOST_SHOWAD + #define HOST_SHOWAD "\000" /* show ads on all hosts */ +#endif +/* --- "smash" margin (0 means no smashing) --- */ +#ifndef SMASHMARGIN + #ifdef NOSMASH + #define SMASHMARGIN 0 #else - #define SQUASHMARGIN 3 + #define SMASHMARGIN 3 #endif #endif +#ifndef SMASHCHECK + #define SMASHCHECK 0 +#endif /* --- textwidth --- */ #ifndef TEXTWIDTH #define TEXTWIDTH (400) #endif /* --- font "combinations" --- */ -#define CMSYEX (107) /* select CMSY10 _or_ CMEX10 */ +#define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/ /* --- prefix prepended to all expressions --- */ #ifndef PREFIX #define PREFIX "\000" /* default no prepended prefix */ @@ -511,6 +732,72 @@ other variables #if !defined(NODUMPENVP) && !defined(DUMPENVP) #define DUMPENVP /* assume char *envp[] available */ #endif +/* --- max query_string length if no http_referer supplied --- */ +#ifndef NOREFMAXLEN + #define NOREFMAXLEN 9999 /* default to any length query */ +#endif +#ifndef NOREFSAFELEN + #define NOREFSAFELEN 24 /* too small for hack exploit */ +#endif +/* --- check whether or not to perform http_referer check --- */ +#ifdef REFERER /* only specified referers allowed */ + #undef NOREFMAXLEN + #define NOREFMAXLEN NOREFSAFELEN +#else /* all http_referer's allowed */ + #define REFERER NULL +#endif +/* --- check top levels of http_referer against server_name --- */ +#ifdef REFLEVELS /* #topmost levels to check */ + #undef NOREFMAXLEN + #define NOREFMAXLEN NOREFSAFELEN +#else + #ifdef NOREFCHECK + #define REFLEVELS 0 /* don't match host and referer */ + #else + #define REFLEVELS 3 /* default matches abc.def.com */ + #endif +#endif +/* --- check whether or not \input, \counter, \environment permitted --- */ +#ifdef DEFAULTSECURITY /* default security specified */ + #define EXPLICITDEFSECURITY /* don't override explicit default */ +#else /* defualt security not specified */ + #define DEFAULTSECURITY (8) /* so set default security level */ +#endif +#ifdef INPUTREFERER /*http_referer's permitted to \input*/ + #ifndef INPUTSECURITY /* so we need to permit \input{} */ + #define INPUTSECURITY (99999) /* make sure SECURITY=*/ GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */ +GLOBAL(int,rastlift,0); /* rastraise() lift parameter */ +GLOBAL(int,rastlift1,0); /* rastraise() lift for base exprssn*/ GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */ +GLOBAL(int,iunitlength,1); /* #pixels per unit as int for store*/ /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */ -GLOBAL(int,squashmargin,SQUASHMARGIN); /* minimum "squash" margin */ -GLOBAL(int,issquashdelta,1); /* true if squashmargin is a delta */ -GLOBAL(int,istransparent,1); /*true to set background transparent*/ +GLOBAL(int,adfrequency,ADFREQUENCY); /* advertisement frequency */ +GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/ +GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */ +GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/ +GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */ +GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */ +GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */ +GLOBAL(int,isnomath,0); /* true to inhibit math mode */ +GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/ +GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */ +GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/ +#define BLANKSIGNAL (-991234) /*rastsmash signal right-hand blank*/ +GLOBAL(int,blanksignal,BLANKSIGNAL); /*rastsmash signal right-hand blank*/ +GLOBAL(int,blanksymspace,0); /* extra (or too much) space wanted*/ +GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/ GLOBAL(int,fgred,FGRED); GLOBAL(int,fggreen,FGGREEN); GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */ GLOBAL(int,bgred,BGRED); GLOBAL(int,bggreen,BGGREEN); GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */ +GLOBAL(double,gammacorrection,GAMMA); /* gamma correction */ +GLOBAL(int,isplusblank,ISPLUSBLANK); /*interpret +'s in query as blanks?*/ GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/ GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */ GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */ +GLOBAL(int,maxfollow,MAXFOLLOW); /* aafollowline() maxturn parameter*/ GLOBAL(int,fgalias,1); GLOBAL(int,fgonly,0); GLOBAL(int,bgalias,0); @@ -587,14 +920,37 @@ GLOBAL(subraster,*workingbox,(subraster GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */ GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/ GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/ +GLOBAL(int,fraccenterline,NOVALUE); /* baseline for punct. after \frac */ +/*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/ GLOBAL(int,iscaching,ISCACHING); /* true if caching images */ GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */ +GLOBAL(int,isemitcontenttype,1); /* true to emit mime content-type */ +int iscachecontenttype = 0; /* true to cache mime content-type */ +char contenttype[2048] = "\000"; /* content-type:, etc buffer */ GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/ /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */ /* ------------------------------------------------------------------------- +store for evalterm() [n.b., these are stripped-down funcs from nutshell] +-------------------------------------------------------------------------- */ +#define STORE struct store_struct /* "typedef" for store struct */ +#define MAXSTORE 100 /* max 100 identifiers */ +STORE { + char *identifier; /* identifier */ + int *value; /* address of corresponding value */ + } ; /* --- end-of-store_struct --- */ +static STORE mimestore[MAXSTORE] = { + { "fontsize", &fontsize }, { "fs", &fontsize }, /* font size */ + { "fontnum", &fontnum }, { "fn", &fontnum }, /* font number */ + { "unitlength", &iunitlength }, /* unitlength */ + /*{ "mytestvar", &mytestvar },*/ + { NULL, NULL } /* end-of-store */ + } ; /* --- end-of-mimestore[] --- */ + +/* ------------------------------------------------------------------------- miscellaneous macros -------------------------------------------------------------------------- */ +#if 0 /* --- these are now #define'd in mimetex.h --- */ #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 */ @@ -602,10 +958,47 @@ miscellaneous macros #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 */ #define dmod(x,y) ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/ +#endif #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \ - { char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else + { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else #define slower(s) if ((s)!=NULL) /* lowercase all chars in s */ \ { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else +/*subraster *subrastcpy();*/ /* need global module declaration */ +/*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */ +/* sp->type=blanksignal */ +/* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */ +#define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg))))) +/* --- check if a string is empty --- */ +#define isempty(s) ((s)==NULL?1:(*(s)=='\000'?1:0)) +/* --- last char of a string --- */ +#define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1))) +/* --- lowercase a string --- */ +#define strlower(s) strnlower((s),0) /* lowercase an entire string */ +/* --- strip leading and trailing whitespace (including ~) --- */ +#define trimwhite(thisstr) if ( (thisstr) != NULL ) { \ + int thislen = strlen(thisstr); \ + while ( --thislen >= 0 ) \ + if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \ + (thisstr)[thislen] = '\000'; \ + else break; \ + if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \ + {strsqueeze((thisstr),thislen);} } else +/* --- strncpy() n bytes and make sure it's null-terminated --- */ +#define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \ + char *thissource = (source); \ + (target)[0] = '\000'; \ + if ( (n)>0 && thissource!=NULL ) { \ + strncpy((target),thissource,(n)); \ + (target)[(n)] = '\000'; } } +/* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */ +#define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \ + int thislen3=strlen(s); \ + if ((n) >= thislen3) *(s) = '\000'; \ + else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/ +/* --- strsqueeze(s,t) with two pointers --- */ +#define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \ + int sqlen=strlen((s))-strlen((t)); \ + if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else /* --- * PART2 @@ -655,6 +1048,7 @@ if ( rp == (raster *)NULL ) /* malloc f goto end_of_job; /* return error to caller */ rp->width = width; /* store width in raster struct */ rp->height = height; /* and store height */ +rp->format = 1; /* initialize as bitmap format */ rp->pixsz = pixsz; /* store #bits per pixel */ rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */ /* --- allocate and initialize bitmap array --- */ @@ -775,6 +1169,7 @@ cp->charnum = cp->location = 0; /* init 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.format = 0; /* init raster format */ cp->image.pixsz = 0; /* and #bits per pixel */ cp->image.pixmap = NULL; /* init raster pixmap as null */ /* ------------------------------------------------------------------------- @@ -947,6 +1342,8 @@ newsp->image = newrp; /* new raster im switch ( sp->type ) /* set new raster image type */ { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break; case ASCIISTRING: newsp->type = ASCIISTRING; break; + case FRACRASTER: newsp->type = FRACRASTER; break; + case BLANKSIGNAL: newsp->type = blanksignal; break; case IMAGERASTER: default: newsp->type = IMAGERASTER; break; } /* --- return copy of sp to caller --- */ end_of_job: @@ -960,7 +1357,7 @@ end_of_job: * -------------------------------------------------------------------------- * Arguments: rp (I) ptr to raster struct to be rotated * -------------------------------------------------------------------------- - * Returns: ( raster * ) ptr to new raster rotated ralative to rp, + * Returns: ( raster * ) ptr to new raster rotated relative to rp, * or NULL for any error. * -------------------------------------------------------------------------- * Notes: o An underbrace is } rotated 90 degrees clockwise, @@ -993,6 +1390,219 @@ return ( rotated ); /* return rotated /* ========================================================================== + * Function: rastmag ( rp, magstep ) + * Purpose: magnifies rp by integer magstep, + * e.g., double-height and double-width if magstep=2 + * -------------------------------------------------------------------------- + * Arguments: rp (I) ptr to raster struct to be "magnified" + * magstep (I) int containing magnification scale, + * e.g., 2 to double the width and height of rp + * -------------------------------------------------------------------------- + * Returns: ( raster * ) ptr to new raster magnified relative to rp, + * or NULL for any error. + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +raster *rastmag ( raster *rp, int magstep ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +raster *new_raster(), *magnified=NULL; /* magnified raster back to caller */ +int height = rp->height, irow, /* height, row index */ + width = rp->width, icol, /* width, column index */ + mrow = 0, mcol = 0, /* dup pixels magstep*magstep times*/ + pixsz = rp->pixsz; /* #bits per pixel */ +/* ------------------------------------------------------------------------- +check args +-------------------------------------------------------------------------- */ +if ( rp == NULL ) goto end_of_job; /* no input raster supplied */ +if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */ +/* ------------------------------------------------------------------------- +allocate magnified raster and fill it +-------------------------------------------------------------------------- */ +/* --- allocate magnified raster with magstep*width, magstep*height --- */ +if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/ +!= NULL ) /* check that allocation succeeded */ + /* --- fill reflected raster --- */ + for ( irow=0; irow100000 ) goto end_of_job; /* sanity check */ +if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */ +/* ------------------------------------------------------------------------- +allocate magnified bytemap and fill it +-------------------------------------------------------------------------- */ +/* --- allocate bytemap for magstep*width, magstep*height --- */ +if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/ +!= NULL ) /* check that allocation succeeded */ + /* --- fill reflected raster --- */ + for ( irow=0; irow0?(int)(bytemap[imap-1]):byteval), /*left of center*/ + icell[6]= (icol0?(int)(bytemap[imap-width]):byteval),/*above center*/ + icell[8]= (irow0&&icol>0?(int)(bytemap[imap-width-1]):byteval), + icell[3]= (irow>0&&icol0?(int)(bytemap[imap+width-1]):byteval), + icell[9]=(irowheight, irow, /* height, row index */ + width = rp->width, icol, /* width, column index */ + pixsz = rp->pixsz; /* #bits per pixel */ +/* ------------------------------------------------------------------------- +allocate reflected raster and fill it +-------------------------------------------------------------------------- */ +/* --- allocate reflected raster with same width, height --- */ +if ( axis==1 || axis==2 ) /* first validate axis arg */ + if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */ + != NULL ) /* check that allocation succeeded */ + /* --- fill reflected raster --- */ + for ( irow=0; irow= 0 ) /* bounds check okay */ - if ( svalue!=0 || isopaque ) /*got dark or opaque source*/ - setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/ + if ( svalue!=0 || isopaque ) { /*got dark or opaque source*/ + setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/ } /* --- end-of-for(icol) --- */ } /* --- end-of-for(irow) --- */ /* ------------------------------------------------------------------------- @@ -1072,14 +1682,50 @@ end_of_job: * to horizontally shift sp2 relative to sp1, * either positive (right) or negative * isalign (I) int containing 1 to align baselines, - * or 0 to vertically center sp2 over sp1 + * or 0 to vertically center sp2 over sp1. + * For isalign=2, images are vertically + * centered, but then adjusted by \raisebox + * lifts, using global variables rastlift1 + * for sp1 and rastlift for sp2. * isfree (I) int containing 1=free sp1 before return, * 2=free sp2, 3=free both, 0=free none. * -------------------------------------------------------------------------- * Returns: ( subraster * ) pointer to constructed subraster * or NULL for any error * -------------------------------------------------------------------------- - * Notes: + * Notes: o The top-left corner of each raster box has coords (0,0), + * down to (h-1,w-1) for a box of height h and width w. + * o A raster's baseline, b, is typically 0 <= b < h. + * But b can actually go out-of-bounds, b>=h or b<0, for + * an image additionally lifted (b>=h) or lowered (b<0) + * with respect to the surrounding expression. + * o Note that b=h-1 means no descenders and the bottom + * of the symbol rests exactly on the baseline, + * whereas b=0 means the top pixel of the symbol rests + * on the baseline, and all other pixels are descenders. + * o The composite raster is constructed as follows... + * The base image is labelled height h1 and baseline b1, + * the overlay h2 and b2, and the composite H and B. + * base overlay + * --- +------------------------+ --- For the overlay to be + * ^ | ^ +----------+| ^ vertically centered with + * | | | | || | respect to the base, + * | | |B-b1 | || | B - b1 = H-B -(h1-b1), so + * | | v | || | 2*B = H-h1 + 2*b1 + * | |+----------+| || | B = b1 + (H-h1)/2 + * B || ^ ^ || || | And when the base image is + * | || | | || || | bigger, H=h1 and B=b1 is + * | || b1 | || || | the obvious correct answer. + * | || | h1 || || H=h2 + * v || v | || || | + * ----------||-------|--|| ||--|-------- + * baseline || h1-b1 v || overlay || | + * for base |+----------+| baseline || | + * and com- | ^ | ignored || | + * posite | |H-B- |----------|| | + * | | (h1-b1)| || | + * | v +----------+| v + * +------------------------+ --- * ======================================================================= */ /* --- entry point --- */ subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2, @@ -1100,20 +1746,39 @@ int base1 = sp1->baseline, /*baseline 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 */ +int height = max2(height1,height2), /*composite height if sp2 centered*/ + base = base1 + (height-height1)/2, /* and composite baseline */ + tlc2 = (height-height2)/2, /* top-left corner for overlay */ + width=0, pixsz=0; /* other params for composite */ +int lift1 = rastlift1, /* vertical \raisebox lift for sp1 */ + lift2 = rastlift; /* vertical \raisebox lift for sp2 */ /* ------------------------------------------------------------------------- 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 */ +switch ( isalign ) { + default: + case 0: /* centered, baselines not aligned */ + height = max2(height1,height2); /* max height */ + base = base1 + (height-height1)/2; /* baseline for sp1 */ + break; + case 1: /* 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 */ + break; + case 2: /* centered +/- \raisebox lifts */ + base1 -= lift1; base2 -= lift2; /* reset to unlifted images */ + /* --- start with default for centered, unlifted images --- */ + height2 += 2*absval(lift2); /* "virtual" height of overlay */ + height = max2(height1,height2); /* max height */ + base = base1 + (height-height1)/2; /* baseline for sp1 */ + tlc2 = (height-height2)/2 /* top-left corner for overlay */ + + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */ + break; + } /* --- end-of-switch(isalign) --- */ +width = max2(width1,width2+abs(offset2)); /* max width */ +pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */ /* ------------------------------------------------------------------------- allocate concatted composite subraster -------------------------------------------------------------------------- */ @@ -1124,19 +1789,29 @@ if ( (sp=new_subraster(width,height,pixs sp->type = IMAGERASTER; /* image */ sp->baseline = base; /* composite baseline */ sp->size = sp1->size; /* underlying char is sp1 */ +if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */ /* --- 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*/ +switch ( isalign ) { + default: + case 0: /* centered, baselines not aligned */ + rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/ rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/ - (width-width2)/2+offset2, 0); } + (width-width2)/2+offset2, 0); + break; + case 1: /* baselines of sp1,sp2 aligned */ + rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/ + rastput (rp, sp2->image, base-base2, /*overlaid*/ + (width-width2)/2+offset2, 0); + break; + case 2: if(1){ /* centered +/- \raisebox lifts */ + rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); + rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); } + break; + } /* --- end-of-switch(isalign) --- */ /* ------------------------------------------------------------------------- free input if requested -------------------------------------------------------------------------- */ @@ -1190,11 +1865,12 @@ int base1 = sp1->baseline, /*baseline pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */ type2 = sp2->type; /* image type for right-hand */ int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/ -int issquash = (squashmargin!=0?1:0), /* true to "squash" sp1||sp2 */ - isopaque = (issquash?0:1), /* not oppaque if squashing */ - rastsquash(), isblank=0, nsquash=0, /* #cols to squash */ - oldsquashmargin = squashmargin; /* save original squashmargin */ -int blanksignal = (-991234); /*rastsquash signal right-hand blank*/ +int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */ + isopaque = (issmash?0:1), /* not oppaque if smashing */ + rastsmash(), isblank=0, nsmash=0, /* #cols to smash */ + oldsmashmargin = smashmargin, /* save original smashmargin */ + oldblanksymspace = blanksymspace, /* save original blanksymspace */ + oldnocatspace = isnocatspace; /* save original isnocatspace */ 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 */ @@ -1204,6 +1880,8 @@ int class1 = (symdef1==NULL?ORDINARY:sym smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE|| class2==OPENING||class2==CLOSING||class2==PUNCTION), space = fontsize/2+1; /* #cols between sp1 and sp2 */ +int isfrac = (type1 == FRACRASTER /* sp1 is a \frac */ + && class2 == PUNCTION); /* and sp2 is punctuation */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ @@ -1211,36 +1889,50 @@ Initialization if ( !isstring ) space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */ else space = 1; /* space for ascii string */ -/* --- determine squash --- */ -if ( !isstring ) /* don't squash strings */ - if ( issquash ) { /* raster squash wanted */ - int maxsquash = rastsquash(sp1,sp2), /* calculate max squash space */ - margin = squashmargin; /* init margin without delta */ +if ( isnocatspace > 0 ) { /* spacing explicitly turned off */ + space = 0; /* reset space */ + isnocatspace--; } /* and decrement isnocatspace flag */ +if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/ +if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */ + space = 0; /* no extra space between spaces */ +if ( sp2->type != BLANKSIGNAL ) /* not a blank space signal */ + if ( blanksymspace != 0 ) { /* and we have a space adjustment */ + space = max2(0,space+blanksymspace); /* adjust as much as possible */ + blanksymspace = 0; } /* and reset adjustment */ +if ( msgfp!=NULL && msglevel>=999 ) /* display space results */ + { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n", + space,oldblanksymspace,oldnocatspace); fflush(msgfp); } +/* --- determine smash --- */ +if ( !isstring && !isfrac ) /* don't smash strings or \frac's */ + if ( issmash ) { /* raster smash wanted */ + int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */ + margin = smashmargin; /* init margin without delta */ if ( (1 && smash1 && smash2) /* concatanating two chars */ - || (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) ) - /*maxsquash = 0;*/ /* turn off squash */ - margin = max2(space-1,0); /* force small squashmargin */ + || (1 && type1!=IMAGERASTER && type2!=IMAGERASTER + && type1!=FRACRASTER && type2!=FRACRASTER ) ) + /*maxsmash = 0;*/ /* turn off smash */ + margin = max2(space-1,0); /* force small smashmargin */ else /* adjust for delta if images */ - if ( issquashdelta ) /* squashmargin is a delta value */ + if ( issmashdelta ) /* smashmargin is a delta value */ margin += fontsize; /* add displaystyle base to margin */ - if ( maxsquash == blanksignal ) /* sp2 is intentional blank */ + if ( maxsmash == blanksignal ) /* sp2 is intentional blank */ isblank = 1; /* set blank flag signal */ else /* see how much extra space we have*/ - if ( maxsquash > margin ) /* enough space for adjustment */ - nsquash = maxsquash-margin; /* make adjustment */ - if ( msgfp!=NULL && msglevel>=99 ) /* display squash results */ - { fprintf(msgfp,"rastcat> maxsquash=%d, margin=%d, nsquash=%d\n", - maxsquash,margin,nsquash); + if ( maxsmash > margin ) /* enough space for adjustment */ + nsmash = maxsmash-margin; /* make adjustment */ + if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */ + { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n", + maxsmash,margin,nsmash); fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2, (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2)); fflush(msgfp); } - } /* --- end-of-if(issquash) --- */ + } /* --- end-of-if(issmash) --- */ /* --- determine height, width and baseline of composite raster --- */ if ( !isstring ) { height = max2(base1+1,base2+1) /* max height above baseline */ + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/ - width = width1+width2 + space-nsquash; /*add widths and space-squash*/ - width = max3(width,width1,width2); } /* don't "over-squash" composite */ + width = width1+width2 + space-nsmash; /*add widths and space-smash*/ + width = max3(width,width1,width2); } /* don't "over-smash" composite */ else /* ascii string */ { height = 1; /* default */ width = width1 + width2 + space - 1; } /* no need for two nulls */ @@ -1254,8 +1946,8 @@ if ( msgfp!=NULL && msglevel>=9999 ) /* height2,width2,pixsz2,base2); type_raster(sp2->image,msgfp); /* display right-hand raster */ fprintf(msgfp, - "rastcat> Composite ht,width,squash,pixsz,base = %d,%d,%d,%d,%d\n", - height,width,nsquash,pixsz,base); + "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n", + height,width,nsmash,pixsz,base); fflush(msgfp); } /* flush msgfp buffer */ /* ------------------------------------------------------------------------- allocate concatted composite subraster @@ -1273,7 +1965,10 @@ if ( (sp=new_subraster(width,height,pixs /* --- initialize subraster parameters --- */ /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/ if ( !isstring ) - sp->type = type2;/*(type1==type2?type2:IMAGERASTER);*/ /*string or image*/ + sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/ + (type2!=CHARASTER? type2 : + (type1!=CHARASTER&&type1!=BLANKSIGNAL + &&type1!=FRACRASTER?type1:IMAGERASTER)); else sp->type = ASCIISTRING; /* concatted ascii string */ sp->symdef = symdef2; /* rightmost char is sp2 */ @@ -1291,15 +1986,22 @@ if ( msgfp!=NULL && msglevel>=9999 ) fflush(msgfp); } /* flush msgfp buffer */ if ( !isstring ) rastput (rp, sp1->image, base-base1, /* overlay left-hand */ - max2(0,nsquash-width1), 1); /* plus any residual squash space */ + max2(0,nsmash-width1), 1); /* plus any residual smash space */ else memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1); /*init left string*/ if ( msgfp!=NULL && msglevel>=9999 ) { type_raster(sp->image,msgfp); /* display composite raster */ fflush(msgfp); } /* flush msgfp buffer */ if ( !isstring ) - rastput (rp, sp2->image, base-base2, /* overlay right-hand */ - max2(0,width1+space-nsquash), isopaque); /* minus any squashed space */ + { int fracbase = ( isfrac? /* baseline for punc after \frac */ + max2(fraccenterline,base2):base ); /*adjust baseline or use original*/ + rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */ + max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */ + if ( 1 && type1 == FRACRASTER /* we're done with \frac image */ + && type2 != FRACRASTER ) /* unless we have \frac\frac */ + fraccenterline = NOVALUE; /* so reset centerline signal */ + if ( fraccenterline != NOVALUE ) /* sp2 is a fraction */ + fraccenterline += (base-base2); } /* so adjust its centerline */ else { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap)); ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/ @@ -1316,7 +2018,7 @@ if ( isfree > 0 ) /* caller wants inpu Back to caller with pointer to concatted subraster or with null for error -------------------------------------------------------------------------- */ end_of_job: - squashmargin = oldsquashmargin; /* reset original squashmargin */ + smashmargin = oldsmashmargin; /* reset original smashmargin */ return ( sp ); /* back with subraster or null ptr */ } /* --- end-of-function rastcat() --- */ @@ -1487,26 +2189,26 @@ end_of_job: /* ========================================================================== - * Function: rastsquash ( sp1, sp2 ) + * Function: rastsmash ( sp1, sp2 ) * Purpose: When concatanating sp1||sp2, calculate #pixels - * we can "squash sp2 left" + * we can "smash sp2 left" * -------------------------------------------------------------------------- * Arguments: sp1 (I) subraster * to left-hand raster * sp2 (I) subraster * to right-hand raster * -------------------------------------------------------------------------- - * Returns: ( int ) max #pixels we can squash sp1||sp2, + * Returns: ( int ) max #pixels we can smash sp1||sp2, * or "blanksignal" if sp2 intentionally blank, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ -int rastsquash ( subraster *sp1, subraster *sp2 ) +int rastsmash ( subraster *sp1, subraster *sp2 ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -int nsquash = 0; /* #pixels to squash sp1||sp2 */ +int nsmash = 0; /* #pixels to smash sp1||sp2 */ 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 */ @@ -1520,7 +2222,6 @@ int base = max2(base1,base2), /* max asc int irow1=0,irow2=0, icol=0; /* row,col indexes */ int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/ firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/ -int blanksignal = (-991234); /*rastsquash signal right-hand blank*/ int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */ int type_raster(); /* display debugging output */ /* ------------------------------------------------------------------------- @@ -1528,9 +2229,10 @@ find right edge of sp1 and left edge of -------------------------------------------------------------------------- */ /* --- check args --- */ if ( isstring ) goto end_of_job; /* ignore string rasters */ -if ( height > 1023 ) goto end_of_job; /* don't try to squash huge image */ +if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */ +if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */ if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/ - goto end_of_job; /* don't squash intentional blank */ + goto end_of_job; /* don't smash intentional blank */ /* --- init firstcol1[], firstcol2[] --- */ for ( irow1=0; irow1type == blanksignal ) /*blanksignal was propagated to us*/ - goto end_of_job; /* don't squash intentional blank */ + goto end_of_job; /* don't smash intentional blank */ /* --- set firstcol1[] indicating right edge of sp1 --- */ for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */ for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */ @@ -1555,46 +2257,128 @@ for ( irow1=top1; irow1<=bot1; irow1++ ) nfirst1++; /* bump #rows containing set pixels*/ break; } /* and go on to next row */ if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/ - goto end_of_job; /* don't squash intentional blanks */ + goto end_of_job; /* don't smash intentional blanks */ /* ------------------------------------------------------------------------- find minimum separation -------------------------------------------------------------------------- */ for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */ int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */ - if ( margin2 != blanksignal ) /* irow2 not an empty/blank row */ + if ( margin2 != blanksignal ) { /* irow2 not an empty/blank row */ for ( irow1=max2(irow2-smin,top1); ; irow1++ ) if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */ else if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/ int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */ if ( ds >= smin ) continue; /* min unchanged */ - if ( dy>squashmargin && dxsmashmargin && dx= 99 ) /* display for debugging */ - { fprintf(msgfp,"rastsquash> nsquash=%d, squashmargin=%d\n", - nsquash,squashmargin); + { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n", + nsmash,smashmargin); if ( msglevel >= 999 ) /* also display rasters */ - { fprintf(msgfp,"rastsquash>left-hand image...\n"); + { fprintf(msgfp,"rastsmash>left-hand image...\n"); if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */ - fprintf(msgfp,"rastsquash>right-hand image...\n"); + fprintf(msgfp,"rastsmash>right-hand image...\n"); if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */ fflush(msgfp); } - return ( nsquash ); /* back with #squash pixels */ -} /* --- end-of-function rastsquash() --- */ + return ( nsmash ); /* back with #smash pixels */ +} /* --- end-of-function rastsmash() --- */ + + +/* ========================================================================== + * Function: rastsmashcheck ( term ) + * Purpose: Check an exponent term to see if its leading symbol + * would make smashing dangerous + * -------------------------------------------------------------------------- + * Arguments: term (I) char * to null-terminated string + * containing right-hand exponent term about to + * be smashed against existing left-hand. + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 if it's okay to smash term, or + * 0 if smash is dangerous. + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int rastsmashcheck ( char *term ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int isokay = 0; /* 1 to signal okay to caller */ +static char nosmashchars[64] = "-.,="; /* don't smash these leading chars */ +static char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */ +static char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize", + "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL }; +char *expression = term; /* local ptr to beginning of expression */ +char *token = NULL; int i; /* token = nosmashstrs[i] or grayspace[i] */ +/* ------------------------------------------------------------------------- +see if smash check enabled +-------------------------------------------------------------------------- */ +if ( smashcheck < 1 ) { /* no smash checking wanted */ + if ( smashcheck >= 0 ) /* -1 means check should always fail */ + isokay = 1; /* otherwise (if 0), signal okay to smash */ + goto end_of_job; } /* return to caller */ +/* ------------------------------------------------------------------------- +skip leading white and gray space +-------------------------------------------------------------------------- */ +/* --- first check input --- */ +if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */ +/* --- skip leading white space --- */ +skipwhite(term); /* skip leading white space */ +if ( *term == '\000' ) goto end_of_job; /* nothing but white space */ +/* --- skip leading gray space --- */ +skipgray: + for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */ + if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */ + term += strlen(token); /* skip past this grayspace token */ + skipwhite(term); /* and skip any subsequent white space */ + if ( *term == '\000' ) { /* nothing left so quit */ + if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */ + fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression); + goto end_of_job; } + goto skipgray; } /* restart grayspace check from beginning */ +/* ------------------------------------------------------------------------- +check for leading no-smash single char +-------------------------------------------------------------------------- */ +/* --- don't smash if term begins with a "nosmash" char --- */ +if ( (token=strchr(nosmashchars,*term)) != NULL ) { + if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */ + fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term); + goto end_of_job; } +/* ------------------------------------------------------------------------- +check for leading no-smash token +-------------------------------------------------------------------------- */ +for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */ + if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */ + if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */ + fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term); + goto end_of_job; } /* so don't smash term */ +/* ------------------------------------------------------------------------- +back to caller +-------------------------------------------------------------------------- */ +isokay = 1; /* no problem, so signal okay to smash */ +end_of_job: + if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */ + fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n", + isokay,(expression==NULL?"":expression)); + return ( isokay ); /* back to caller with 1 if okay to smash */ +} /* --- end-of-function rastsmashcheck() --- */ /* ========================================================================== - * Function: accent_subraster ( accent, width, height, pixsz ) + * Function: accent_subraster ( accent, width, height, direction, 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 @@ -1603,6 +2387,8 @@ end_of_job: * 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) + * direction (I) int containing desired direction of accent, + * +1=right, -1=left, 0=left/right * pixsz (I) int containing 1 for bitmap, 8 for bytemap * -------------------------------------------------------------------------- * Returns: ( subraster * ) ptr to newly-allocated subraster with accent, @@ -1612,7 +2398,8 @@ end_of_job: * and caller should check dimensions in returned subraster * ======================================================================= */ /* --- entry point --- */ -subraster *accent_subraster ( int accent, int width, int height, int pixsz ) +subraster *accent_subraster ( int accent, int width, int height, +int direction, int pixsz ) { /* ------------------------------------------------------------------------- Allocations and Declarations @@ -1622,6 +2409,7 @@ raster *new_raster(), *rp=NULL; /*raster subraster *new_subraster(), *sp=NULL; /* subraster returning accent */ int delete_raster(), delete_subraster(); /*free allocated raster on err*/ int line_raster(), /* draws lines */ + rule_raster(), /* draw solid boxes */ thickness = 1; /* line thickness */ /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/ /* --- other working info --- */ @@ -1634,6 +2422,13 @@ raster *rastrot(), /* rotate { for ove *rastcpy(); /* may need copy of original */ subraster *arrow_subraster(); /* rightarrow for vec */ subraster *rastack(); /* stack accent atop extra space */ +int iswidthneg = 0; /* set true if width<0 arg passed */ +int serifwidth=0; /* serif for surd */ +int isBig=0; /* true for ==>arrow, false for -->*/ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */ /* ------------------------------------------------------------------------- outer switch() traps accents that may change caller's height,width -------------------------------------------------------------------------- */ @@ -1653,38 +2448,52 @@ switch ( accent ) /* --- bar request --- */ case UNDERBARACCENT: case BARACCENT: - thickness = height-1; /* adjust thickness */ + thickness = 1; /*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*/ + { row0 = row1 = max2(height-3,0); /* row numbers for overbar */ + line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/ else /* underbar is below expression */ - line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/ + { row0 = row1 = min2(2,height-1); /* row numbers for underbar */ + line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/ break; /* --- dot request --- */ case DOTACCENT: thickness = height-1; /* adjust thickness */ - line_raster(rp,0,width/2,1,(width/2)+1,thickness); /* centered dot */ + /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/ + rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/ 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,1,col0+1,thickness); /* set a dot at 1st third*/ - line_raster(rp,0,col1,1,col1+1,thickness); /* and another at 2nd */ + col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */ + col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/ + if ( col0+thickness >= col1 ) /* dots overlap */ + { col0 = max2(col0-1,0); /* try moving left dot more left */ + col1 = min2(col1+1,width-thickness); } /* and right dot right */ + if ( col0+thickness >= col1 ) /* dots _still_ overlap */ + thickness = max2(thickness-1,1); /* so try reducing thickness */ + /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/ + /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/ + rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/ + rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/ break; /* --- hat request --- */ case HATACCENT: - thickness = (width<=12? 2 : 3); /* adjust thickness */ + thickness = 1; /*(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 */ + serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */ + col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/ + /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */ + col0 = (col1-serifwidth+1)/2; /* midpoint col of sqrt */ + row0 = max2(1,((height+1)/2)-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,row0,0,row1,col0,thickness);*/ /*descending portion*/ + line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness); + line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */ line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */ line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/ break; @@ -1711,7 +2520,10 @@ switch ( accent ) /* --- vec request --- */ case VECACCENT: height = 2*(height/2) + 1; /* force height odd */ - if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/ + if ( absval(direction) >= 9 ) { /* want ==> arrow rather than --> */ + isBig = 1; /* signal "Big" arrow */ + direction -= 10; } /* reset direction = +1, -1, or 0 */ + if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/ != NULL ) /* succeeded */ { rp = accsp->image; /* "extract" raster with bitmap */ free((void *)accsp); } /* and free subraster "envelope" */ @@ -1724,14 +2536,15 @@ switch ( accent ) 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" */ + free((void *)sp); /* and free subraster "envelope" */ + leftsymdef = NULL; } /* so \tilde{x}^2 works properly */ 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 ( 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 */ @@ -1741,6 +2554,7 @@ if ( rp != NULL ) /* accent raster con sp->image = rp; /* raster we just constructed */ sp->size = (-1); /* can't set font size here */ sp->baseline = 0; } /* can't set baseline here */ + } /* --- end-of-if(rp!=NULL) --- */ /* --- return subraster containing desired accent to caller --- */ return ( sp ); /* return accent or NULL to caller */ } /* --- end-of-function accent_subraster() --- */ @@ -1774,7 +2588,7 @@ 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 icol, thickness=(height>15?2:2); /* 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[] */ @@ -1800,22 +2614,22 @@ for ( irow=0; irow= 0 ) /* right arrowhead wanted */ for ( icol=0; icol= 0 ) /* bounds check */ + if ( ipix >= 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*/ + ((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*/ + ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/ } /* --- end-of-for(irow) --- */ end_of_job: return ( arrowsp ); /*back to caller with arrow or NULL*/ @@ -1850,7 +2664,7 @@ 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 irow, thickness=(width>15?2:2); /* 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[] */ @@ -1876,23 +2690,23 @@ 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*/ + ((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 ( ipix > 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) --- */ + ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/ + } /* --- end-of-for(icol) --- */ end_of_job: return ( arrowsp ); /*back to caller with arrow or NULL*/ } /* --- end-of-function uparrow_subraster() --- */ @@ -1913,6 +2727,8 @@ end_of_job: * height (I) int containing number of rows for rule * type (I) int containing 0 for solid rule, * 1 for horizontal dashes, 2 for vertical + * 3 for solid rule with corners removed (bevel) + * 4 for strut (nothing drawn) * -------------------------------------------------------------------------- * Returns: ( int ) 1 if rule drawn okay, * or 0 for any error. @@ -1928,42 +2744,53 @@ int rule_raster ( raster *rp, int top, i /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -int irow, icol; /* indexes over rp raster */ -int ipix, /* raster pixmap[] index */ +int irow=0, icol=0; /* indexes over rp raster */ +int ipix = 0, /* 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 hdash=1, vdash=2, /* type for horizontal, vertical dashes */ + bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */ 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 ( 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 */ + else return ( 0 ); } /* otherwise signal error to caller */ +if ( type == bevel ) /* remove corners of solid box */ + if ( width<3 || height<3 ) type=0; /* too small to remove corners */ /* ------------------------------------------------------------------------- Fill line/box -------------------------------------------------------------------------- */ -for ( irow=top; irow 0 ) /* zero width implies strut*/ + for ( irow=top; irowwidth + left - 1; /*first pixel preceding icol*/ for ( icol=left; icol=left+width-1) /* top-right corner */ + || (irow>=top+height-1 && icol==left) /* bottom-left corner */ + || (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */ + isdraw = 0; else isdraw = 1; } /*set isdraw to skip corner*/ if ( type == hdash ) /*set isdraw for horiz dash*/ isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen); 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 /*ibit is within rp bounds*/ - if ( isdraw ) /*and we're drawing this bit*/ + 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 */ - ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */ + ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */ } /* --- end-of-for(icol) --- */ } /* --- end-of-for(irow) --- */ end_of_job: @@ -2008,17 +2835,18 @@ int line_raster ( raster *rp, int row0, /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -int irow, icol, /* indexes over rp raster */ +int irow=0, icol=0, /* 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 width=rp->width, height=rp->height; /* dimensions of input raster */ +int ipix = 0, /* raster pixmap[] index */ + npix = width*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 */ + xcol=0, xrow=0; /* 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)), @@ -2027,22 +2855,28 @@ int line_recurse(), isrecurse=1; /* true /* ------------------------------------------------------------------------- Check args -------------------------------------------------------------------------- */ -if ( rp == (raster *)NULL ) /* no raster arg supplied */ +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 */ + else return ( 0 ); } /* otherwise signal error to caller */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ -if ( msgfp!=NULL && msglevel>=29 ) /* debugging */ +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.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n", - row0,col0, row1,col1, thickness, dy,dx, a, xwidth); + row0,col0, row1,col1, thickness, dy,dx, a, xwidth); fflush(msgfp); } /* --- check for recursive line drawing --- */ -if ( isrecurse ) /* drawing lines recursively */ - { line_recurse(rp,(double)row0,(double)col0, - (double)row1,(double)col1,thickness); - return ( 1 ); } +if ( isrecurse ) { /* drawing lines recursively */ + for ( irow=0; irow(-0.001) && xcol0>(-0.001) /*check line inside raster*/ + && xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) ) + line_recurse(rp,xrow0,xcol0,xrow1,xcol1,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 */ @@ -2481,14 +3315,22 @@ return ( status ); * 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. + * or >0 to draw a line around border of + * thickness isline pixels. See Notes below. * isfree (I) int containing true to free rp before return * -------------------------------------------------------------------------- * Returns: ( raster * ) ptr to bordered raster, * or NULL for any error. * -------------------------------------------------------------------------- - * Notes: o + * Notes: o The isline arg also controls which sides border lines + * are drawn for. To do this, isline is interpreted as + * thickness + 100*sides so that, e.g., passing isline=601 + * is interpreted as sides=6 and thickness=1. And + * sides is further interpreted as 1=left side, 2=top, + * 4=right and 8=bottom. For example, sides=6 where 6=2+4 + * draws the top and right borders only. 15 draws all four + * sides. And 0 (no sides value embedded in isline) + * draws all four sides, too. * ======================================================================= */ /* --- entry point --- */ raster *border_raster ( raster *rp, int ntop, int nbot, @@ -2499,11 +3341,12 @@ 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 */ +int width = (rp==NULL?0:rp->width), /* width of raster */ + height = (rp==NULL?0:rp->height), /* height 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 */ +int left=1, top=1, right=1, bot=1; /* frame sides to draw */ +int delete_raster(); /* free input rp if isfree is true */ /* ------------------------------------------------------------------------- Initialization -------------------------------------------------------------------------- */ @@ -2525,6 +3368,22 @@ else 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 */ +/* --- check which sides to draw --- */ +if ( isline > 100 ) { /* sides arg embedded in isline */ + int iside=0, sides=isline/100; /* index, sides=1-15 from 101-1599 */ + isline -= 100*sides; /* and remove sides from isline */ + for ( iside=1; iside<=4; iside++ ) { /* check left, top, right, bot */ + int shift = sides/2; /* shift sides left one bit */ + if ( sides == 2*shift ) /* low-order bit is >>not<< set */ + switch ( iside ) { /* don't draw corresponding side */ + default: break; /* internal error */ + case 1: left = 0; break; /* 1 = left side */ + case 2: top = 0; break; /* 2 = top side */ + case 3: right= 0; break; /* 4 = tight side */ + case 4: bot = 0; break; } /* 8 = bottom side */ + sides = shift; /* ready for next side */ + } /* --- end-of-for(iside) --- */ + } /* --- end-of-if(isline>100) --- */ /* ------------------------------------------------------------------------- allocate bordered raster, and embed rp within it -------------------------------------------------------------------------- */ @@ -2541,13 +3400,13 @@ if ( isline ) /* --- draw left- and right-borders --- */ for ( irow=0; irow=0 then (at least) that many columns + * of whitespace will be left in place, regardless of nback. + * If minspace<0 then existing black pixels will be deleted + * as required. + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster on which a border + * is to be placed + * nback (I) int containing number of columns to + * backspace (>=0) + * pback (O) ptr to int returning #pixels actually + * backspaced (or NULL to not use) + * minspace (I) int containing number of columns + * of whitespace to be left in place + * isfree (I) int containing true to free rp before return + * -------------------------------------------------------------------------- + * Returns: ( raster * ) ptr to backspaced raster, + * or NULL for any error. + * -------------------------------------------------------------------------- + * Notes: o For \! negative space, for \hspace{-10}, etc. + * ======================================================================= */ +/* --- entry point --- */ +raster *backspace_raster ( raster *rp, int nback, int *pback, int minspace, + int isfree ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +raster *new_raster(), *bp=(raster *)NULL; /* raster returned to caller */ +int delete_raster(); /* free input rp if isfree is true */ +int width = (rp==NULL?0:rp->width), /* original width of raster */ + height = (rp==NULL?0:rp->height), /* height of raster */ + mback = nback, /* nback adjusted for minspace */ + newwidth = width, /* adjusted width after backspace */ + icol=0, irow=0; /* col,row index */ +if ( rp == NULL ) goto end_of_job; /* no input given */ +/* ------------------------------------------------------------------------- +locate rightmost column of rp containing ink, and determine backspaced width +-------------------------------------------------------------------------- */ +/* --- locate rightmost column of rp containing ink --- */ +if ( minspace >= 0 ) /* only needed if given minspace */ + for ( icol=width-1; icol>=0; icol-- ) /* find first non-empty col in row */ + for ( irow=0; irow width ) mback = width; /* can't backspace before beginning*/ + newwidth = max2(1,width-mback); /* #cols in backspaced raster */ + if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */ +/* ------------------------------------------------------------------------- +allocate new raster and fill it with leftmost cols of rp +-------------------------------------------------------------------------- */ +/* --- allocate backspaced raster --- */ +if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/ +== (raster *)NULL ) goto end_of_job; /* and quit if failed */ +/* --- fill new raster --- */ +if ( 1 || width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/ + for ( icol=0; icol=999 ) { fprintf(msgfp, /* diagnostics */ + "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n", + nback,minspace,mback,width,newwidth); fflush(msgfp); } + if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */ + return ( bp ); /* back with backspaced or null ptr*/ +} /* --- end-of-function backspace_raster() --- */ + + +/* ========================================================================== * Function: type_raster ( rp, fp ) * Purpose: Emit an ascii dump representing rp, on fp. * -------------------------------------------------------------------------- @@ -2588,14 +3526,19 @@ static char display_chars[16] = /* displ char scanline[133]; /* ascii image for one scan line */ int scan_width; /* #chars in scan (<=display_width)*/ int irow, locol,hicol=(-1); /* height index, width indexes */ +raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */ +int delete_raster(); /*free bitmap converted for display*/ /* -------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- redirect null fp --- */ if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */ +if ( msglevel >= 999 ) { fprintf(fp, /* debugging diagnostics */ + "type_raster> width=%d height=%d ...\n", + rp->width,rp->height); fflush(fp); } /* --- check for ascii string --- */ if ( isstring /* pixmap has string, not raster */ -|| (1 && rp->height==1) ) /* infer input rp is a string */ +|| (0 && rp->height==1) ) /* infer input rp is a string */ { char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/ int width = strlen(string); /* #chars in ascii string */ @@ -2609,7 +3552,11 @@ if ( isstring /* pixmap has string, n /* -------------------------------------------------------------------------- display ascii dump of bitmap image (in segments if display_width < rp->width) -------------------------------------------------------------------------- */ -while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/ +if ( rp->format == 2 /* input is .gf-formatted */ +|| rp->format == 3 ) + bitmaprp = gftobitmap(rp); /* so convert it for display */ +if ( bitmaprp != NULL ) /* if we have image for display */ + 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 */ @@ -2626,11 +3573,11 @@ while ( (locol=hicol+1) < rp->width ) /* 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? '*':'.'); + if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/ + scanline[ipix]=(getlongbit(bitmaprp->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*/ + if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */ + { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*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 --- */ @@ -2640,6 +3587,10 @@ while ( (locol=hicol+1) < rp->width ) /* /* ------------------------------------------------------------------------- Back to caller with 1=okay, 0=failed. -------------------------------------------------------------------------- */ +if ( rp->format == 2 /* input was .gf-format */ +|| rp->format == 3 ) + if ( bitmaprp != NULL ) /* and we converted it for display */ + delete_raster(bitmaprp); /* no longer needed, so free it */ return ( 1 ); } /* --- end-of-function type_raster() --- */ @@ -2782,6 +3733,141 @@ return ( 1 ); /* ========================================================================== + * Function: type_pbmpgm ( rp, ptype, file ) + * Purpose: Write pbm or pgm image of rp to file + * -------------------------------------------------------------------------- + * Arguments: rp (I) ptr to raster struct for which + * a pbm/pgm file is to be written. + * ptype (I) int containing 1 for pbm, 2 for pgm, or + * 0 to determine ptype from values in rp + * file (I) ptr to null-terminated char string + * containing name of fuke to be written + * (see notes below). + * -------------------------------------------------------------------------- + * Returns: ( int ) total #bytes written, + * or 0 for any error. + * -------------------------------------------------------------------------- + * Notes: o (a) If file==NULL, output is written to stdout; + * (b) if *file=='\000' then file is taken as the + * address of an output buffer to which output + * is written (and is followed by a terminating + * '\0' which is not counted in #bytes returned); + * (c) otherwise file is the filename (opened and + * closed internally) to which output is written, + * except that any final .ext extension is replaced + * by .pbm or .pgm depending on ptype. + * ======================================================================= */ +/* --- entry point --- */ +int type_pbmpgm ( raster *rp, int ptype, char *file ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int isokay=0, nbytes=0; /* completion flag, total #bytes written */ +int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/ +int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */ + ngray = 0; /* #gray scale values */ +FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */ +char outline[1024], outfield[256], /* output line, field */ + cr[16] = "\n\000"; /* cr at end-of-line */ +int maxlinelen = 70; /* maximum allowed line length */ +int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */ +static char + *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */ + *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/ + *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */ +/* ------------------------------------------------------------------------- +check input, determine grayscale, and set up output file if necessary +-------------------------------------------------------------------------- */ +/* --- check input args --- */ +if ( rp == NULL ) goto end_of_job; /* no input raster provided */ +if ( ptype != 0 ) /* we'll determine ptype below */ + if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/ +/* --- determine largest (and smallest) value in pixmap --- */ +for ( irow=0; irowheight; irow++ ) /* for each row, top-to-bottom */ + for ( jcol=0; jcolwidth; jcol++ ) /* for each col, left-to-right */ + { int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */ + pixmin = min2(pixmin,pixval); /* new minimum */ + pixmax = max2(pixmax,pixval); } /* new maximum */ +ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */ +if ( ptype == 0 ) /* caller wants us to set ptype */ + ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */ +/* --- open output file if necessary --- */ +if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/ +else if ( *file != '\000' ) { /* explicit filename provided, so...*/ + char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/ + strncpy(fname,file,255); /* local copy of file name */ + fname[255] = '\000'; /* make sure it's null terminated */ + if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/ + strcat(fname,suffix[ptype]); /* so add extension */ + else /* we already have an extension */ + strcpy(pdot,suffix[ptype]); /* so replace original extension */ + if ( (fp = fopen(fname,mode[ptype])) /* open output file */ + == (FILE *)NULL ) goto end_of_job; /* quit if failed to open */ + } /* --- ens-of-if(*file!='\0') --- */ +/* ------------------------------------------------------------------------- +format and write header +-------------------------------------------------------------------------- */ +/* --- format header info --- */ +*outline = '\000'; /* initialize line buffer */ +strcat(outline,magic[ptype]); /* begin file with "magic number" */ +strcat(outline,cr); /* followed by cr to end line */ +sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */ +strcat(outline,outfield); /* add width and height to header */ +strcat(outline,cr); /* followed by cr to end line */ +if ( ptype == 2 ) /* need max grayscale value */ + { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */ + strcat(outline,outfield); /* add max value to header */ + strcat(outline,cr); } /* followed by cr to end line */ +/* --- write header to file or memory buffer --- */ +if ( fp == NULL ) /* if we have no open file... */ + strcat(file,outline); /* add header to caller's buffer */ +else /* or if we have an open file... */ + if ( fputs(outline,fp) /* try writing header to open file */ + == EOF ) goto end_of_job; /* return with error if failed */ +nbytes += strlen(outline); /* bump output byte count */ +/* ------------------------------------------------------------------------- +format and write pixels +-------------------------------------------------------------------------- */ +*outline = '\000'; /* initialize line buffer */ +for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */ + for ( jcol=0; jcolwidth; jcol++ ) { /* for each col, left-to-right */ + /* --- format value at irow,jcol--- */ + *outfield = '\000'; /* init empty field */ + if ( irow < rp->height ) { /* check row index */ + int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */ + if ( ptype == 1 ) /* pixval must be 1 or 0 */ + pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0); + sprintf(outfield,"%d ",pixval); } /* format pixel value */ + /* --- write line if this value won't fit on it (or last line) --- */ + if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/ + || irow >= rp->height ) { /* force writing last line */ + strcat(outline,cr); /* add cr to end current line */ + if ( fp == NULL ) /* if we have no open file... */ + strcat(file,outline); /* add header to caller's buffer */ + else /* or if we have an open file... */ + if ( fputs(outline,fp) /* try writing header to open file */ + == EOF ) goto end_of_job; /* return with error if failed */ + nbytes += strlen(outline); /* bump output byte count */ + *outline = '\000'; /* re-initialize line buffer */ + } /* --- end-of-if(strlen>=maxlinelen) --- */ + if ( irow >= rp->height ) break; /* done after writing last line */ + /* --- concatanate value to line -- */ + strcat(outline,outfield); /* concatanate value to line */ + } /* --- end-of-for(jcol,irow) --- */ +isokay = 1; /* signal successful completion */ +/* ------------------------------------------------------------------------- +Back to caller with total #bytes written, or 0=failed. +-------------------------------------------------------------------------- */ +end_of_job: + if ( fp != NULL /* output written to an open file */ + && fp != stdout ) /* and it's not just stdout */ + fclose(fp); /* so close file before returning */ + return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/ +} /* --- end-of-function type_pbmpgm() --- */ + + +/* ========================================================================== * Function: cstruct_chardef ( cp, fp, col1 ) * Purpose: Emit a C struct of cp on fp, starting in col1. * -------------------------------------------------------------------------- @@ -2813,7 +3899,7 @@ emit charnum, location, name / hirow 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... */ +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"); @@ -2855,9 +3941,9 @@ int emit_string(); /* emit a string and /* ------------------------------------------------------------------------- emit width and height -------------------------------------------------------------------------- */ -sprintf(field,"{ %2d, %3d,%2d, %s\n", /* format width,height,pixsz */ - rp->width,rp->height,rp->pixsz,typecast); -emit_string ( fp, col1, field, "widthxheight, pixsz,map..."); +sprintf(field,"{ %2d, %3d,%2d,%2d, %s\n", /* format width,height,pixsz */ + rp->width,rp->height,rp->format,rp->pixsz,typecast); +emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map..."); /* ------------------------------------------------------------------------- emit bitmap and closing brace, and return to caller -------------------------------------------------------------------------- */ @@ -2894,7 +3980,8 @@ int hex_bitmap ( raster *rp, FILE *fp, i /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */ +int ibyte, /* pixmap[ibyte] index */ + nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/ char stub[64]=" ";/* col1 leading blanks */ int linewidth = 64, /* (roughly) rightmost column */ colwidth = (isstr? 4:5); /* #cols required for each byte */ @@ -2921,11 +4008,11 @@ for ( ibyte=0; ibytewidth, gf->height, #bits */ +int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/ + ibit=0, bitval=0; /* bitmap index, bit value */ +int isrepeat = 1, /* true to process repeat counts */ + repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/ + nrepeats=0, irepeat=0, /* scan line repeat count,index */ + wbits = 0; /* count bits to width of scan line*/ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- check args --- */ +if ( gf == NULL ) goto end_of_job; /* input raster not provided */ +format = gf->format; /* 2 or 3 */ +if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */ +ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/ +/* --- allocate output raster with proper dimensions for bitmap --- */ +width=gf->width; height=gf->height; /* dimensions of raster */ +if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */ +== NULL ) goto end_of_job; /* quit if failed to allocate */ +totbits = width*height; /* total #bits in image */ +/* ------------------------------------------------------------------------- +fill bitmap +-------------------------------------------------------------------------- */ +for ( icount=0,bitval=0; icountpixmap,icount)); /*#bits to set*/ + if ( isrepeat /* we're proxessing repeat counts */ + && nbits == repeatcmds[format-2] ) { /* and repeat opcode found */ + if ( nrepeats == 0 ) /* recursive repeat is error */ + { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/ + nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/ + icount += 2; } /* bump byte/nibble count */ + else /* some internal error occurred */ + if ( msgfp!=NULL && msglevel>=1 ) /* report error */ + fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); } + if ( 0 ) + fprintf(stdout, + "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n", + icount,bitval,nbits,ibit,totbits); + for ( ; nbits>0; nbits-- ) /* count down */ + { if ( ibit >= totbits ) goto end_of_job; /* overflow check */ + for ( irepeat=0; irepeat<=nrepeats; irepeat++ ) + if ( bitval == 1 ) /* set pixel */ + { setlongbit(rp->pixmap,(ibit+irepeat*width)); } + else /* clear pixel */ + { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); } + if ( nrepeats > 0 ) wbits++; /* count another repeated bit */ + ibit++; } /* bump bit index */ + bitval = 1-bitval; /* flip bit value */ + if ( wbits >= width ) { /* completed repeats */ + ibit += nrepeats*width; /*bump bit count past repeated scans*/ + if ( wbits > width ) /* out-of alignment error */ + if ( msgfp!=NULL && msglevel>=1 ) /* report error */ + fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits); + wbits = nrepeats = 0; } /* reset repeat counts */ + } /* --- end-of-for(icount) --- */ +end_of_job: + return ( rp ); /* back to caller with image */ +} /* --- end-of-function gftobitmap() --- */ + + +/* ========================================================================== * Function: get_symdef ( symbol ) * Purpose: returns mathchardef struct for symbol * -------------------------------------------------------------------------- @@ -3024,30 +4190,47 @@ mathchardef *get_symdef ( char *symbol ) Allocations and Declarations -------------------------------------------------------------------------- */ mathchardef *symdefs = symtable; /* table of mathchardefs */ +int ligdef=0, get_ligature(); /* or we may have a ligature */ 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 /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/ - alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */ + alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */ + slashsym = (*symbol=='\\'); /* or \backslashed symbol */ +int family = fontinfo[fontnum].family; /* current font family */ static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/ + /* --- see table on page 536 in TLC2 --- */ {"\\int", "\\Bigint"}, {"\\oint", "\\Bigoint"}, {"\\sum", "\\Bigsum"}, {"\\prod", "\\Bigprod"}, {"\\coprod", "\\Bigcoprod"}, - {"\\cup", "\\Bigcup"}, - {"\\sqcup", "\\Bigsqcup"}, - {"\\cap", "\\Bigcap"}, - {"\\sqcap", "\\sqcap"}, /* don't have \Bigsqcap */ - {"\\odot", "\\Bigodot"}, - {"\\oplus", "\\Bigoplus"}, - {"\\otimes", "\\Bigotimes"}, - {"\\uplus", "\\Biguplus"}, - {"\\wedge", "\\Bigwedge"}, - {"\\vee", "\\Bigvee"}, + /* --- must be 'big' when related to similar binary operators --- */ + {"\\bigcup", "\\Bigcup"}, + {"\\bigsqcup", "\\Bigsqcup"}, + {"\\bigcap", "\\Bigcap"}, + /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */ + {"\\bigodot", "\\Bigodot"}, + {"\\bigoplus", "\\Bigoplus"}, + {"\\bigominus", "\\ominus"}, + {"\\bigotimes", "\\Bigotimes"}, + {"\\bigoslash", "\\oslash"}, + {"\\biguplus", "\\Biguplus"}, + {"\\bigwedge", "\\Bigwedge"}, + {"\\bigvee", "\\Bigvee"}, {NULL, NULL} }; /* ------------------------------------------------------------------------- +First check for ligature +-------------------------------------------------------------------------- */ +isligature = 0; /* init signal for no ligature */ +if ( family == CYR10 ) /*only check for cyrillic ligatures*/ + if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */ + >= 0 ) /* found a ligature */ + { bestdef = ligdef; /* set bestdef for ligature */ + isligature = 1; /* signal we found a ligature */ + goto end_of_job; } /* so just give it to caller */ +/* ------------------------------------------------------------------------- If in \displaystyle mode, first xlate int to Bigint, etc. -------------------------------------------------------------------------- */ if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */ @@ -3070,39 +4253,106 @@ for ( idef=0; ;idef++ ) /* until trail 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 ( symdefs[idef].handler != NULL /* mode irrelevant for directives */ - || istext==0 /* mathmode, so use first match */ - || (istext==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/ - || (istext==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/ - || (istext==3 && symdefs[idef].family==BBOLD10) /*textmode && bb text*/ - || (istext!=3 && !alphasym) ) /* not bb and not alpha */ + if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */ + || (1 && symdefs[idef].handler!=NULL) /* or check every directive */ + || (1 && istextmode && slashsym) /*text mode and \backslashed symbol*/ + || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */ + || symdefs[idef].handler!=NULL)) /* or text mode and directive */ + || (symdefs[idef].family==family /* have correct family */ + && symdefs[idef].handler==NULL) ) /* and not a handler collision */ +#if 0 + || (fontnum==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/ + || (fontnum==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/ + || (fontnum==3 && symdefs[idef].family==BBOLD10 /*textmode && bb text*/ + && symdefs[idef].handler==NULL) + || (fontnum==4 && symdefs[idef].family==CMMIB10 /*textmode && bf text*/ + && symdefs[idef].handler==NULL) ) +#endif 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*/ if ( bestdef < 0 ) /* failed to look up symbol */ - if ( istext != 0 ) /* we're in a restricted font mode */ - { int wastext = istext; /* save current mode */ - mathchardef *symdef = NULL; /* lookup result with istext=0 */ - istext = 0; /*try to look up symbol in any font*/ - symdef = get_symdef(symbol); /* repeat lookup with istext=0 */ - istext = wastext; /* reset font mode */ - return symdef; } /* caller gets istext=0 lookup */ -if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */ - { fprintf(msgfp,"get_symdefs> symbol=%s matches symtable[%d]=%s\n", - symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol)); + if ( fontnum != 0 ) /* we're in a restricted font mode */ + { int oldfontnum = fontnum; /* save current font family */ + mathchardef *symdef = NULL; /* lookup result with fontnum=0 */ + fontnum = 0; /*try to look up symbol in any font*/ + symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */ + fontnum = oldfontnum; /* reset font family */ + return symdef; } /* caller gets fontnum=0 lookup */ +end_of_job: + if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */ + { fprintf(msgfp, + "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n", + symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature); fflush(msgfp); } -return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/ + return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/ } /* --- end-of-function get_symdef() --- */ /* ========================================================================== + * Function: get_ligature ( expression, family ) + * Purpose: returns symtable[] index for ligature + * -------------------------------------------------------------------------- + * Arguments: expression (I) char * containing ligature + * whose corresponding mathchardef is wanted + * family (I) int containing NOVALUE for any family, + * or, e.g., CYR10 for cyrillic, etc. + * -------------------------------------------------------------------------- + * Returns: ( int ) symtable[] index defining ligature, + * or -9999 if no ligature found or for any error + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int get_ligature ( char *expression, int family ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +mathchardef *symdefs = symtable; /* table of mathchardefs */ +char *ligature = expression /*- 1*/, /* expression ptr */ + *symbol = NULL; /* symdefs[idef].symbol */ +int liglen = strlen(ligature); /* #chars remaining in expression */ +int iscyrfam = (family==CYR10); /* true for cyrillic families */ +int idef = 0, /* symdefs[] index */ + bestdef = (-9999), /*index of longest matching symdef*/ + maxlen=(-9999); /*length of longest matching symdef*/ +/* ------------------------------------------------------------------------- +search symdefs[] in order for first occurrence of symbol +-------------------------------------------------------------------------- */ +if ( !isstring ) { /* no ligatures in "string" mode */ + for ( idef=0; ;idef++ ) /* until trailer record found */ + if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */ + else { /* check against caller's ligature */ + int symlen = strlen(symbol); /* #chars in symbol */ + if ( ( symlen>1 || iscyrfam ) /*ligature >1 char long or cyrillic*/ + && symlen <= liglen /* and enough remaining chars */ + && ( *symbol!='\\' || iscyrfam ) /* not escaped or cyrillic */ + && symdefs[idef].handler == NULL ) /* and not a handler */ + if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */ + if ( family < 0 /* no family specifies */ + || symdefs[idef].family == family ) /* or have correct family */ + if ( symlen > maxlen ) /* new longest ligature */ + { bestdef = idef; /* save index of new best match */ + maxlen = symlen; } /* and save its len for next test */ + } /* --- end-of-if/else(symbol==NULL) --- */ + if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */ + { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n", + ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol)); + fflush(msgfp); } + } /* --- end-of-if(!isstring) --- */ +return ( bestdef ); /* -9999 or index of best symdef[] */ +} /* --- end-of-function get_ligature --- */ + + +/* ========================================================================== * 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 + * size (I) int containing 0-5 for desired size * -------------------------------------------------------------------------- * Returns: ( chardef * ) pointer to struct defining symbol at size, * or NULL for any error @@ -3129,7 +4379,7 @@ char *symptr = NULL; /* look for 1st a initialization -------------------------------------------------------------------------- */ /* --- check symdef --- */ -if ( symdef == NULL ) return ( NULL ); /* get_symdef() probably failed */ +if ( symdef == NULL ) goto end_of_job; /* 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 */ @@ -3161,7 +4411,11 @@ 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 */ + if ( fonts[ifont].family < 0 ) { /* error, no such family */ + if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */ + fprintf(msgfp,"get_chardef> failed to find font family %d\n", + family); fflush(msgfp); } + goto end_of_job; } /* quit if can't find font 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 */ @@ -3173,7 +4427,11 @@ while ( 1 ) /* find size or closest a if ( fontdef[size] != NULL ) break; /* found available size */ else /* adjust size closer to normal */ if ( size == NORMALSIZE /* already normal so no more sizes,*/ - || sizeinc == 0 ) return ( NULL); /* or must be supersampling */ + || sizeinc == 0 ) { /* or must be supersampling */ + if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */ + fprintf(msgfp,"get_chardef> failed to find font size %d\n", + size); fflush(msgfp); } + goto end_of_job; } /* quit if can't find desired size */ else /*bump size 1 closer to NORMALSIZE*/ size += sizeinc; /* see if adjusted size available */ /* --- ptr to chardef struct --- */ @@ -3188,7 +4446,14 @@ if ( family == CMEX10 ) /* cmex10 need /* ------------------------------------------------------------------------- return subraster containing chardef data for symbol in requested size -------------------------------------------------------------------------- */ -return ( gfdata ); /*ptr to chardef for symbol in size*/ +end_of_job: + if ( msgfp!=NULL && msglevel>=999 ) + { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n"); + else + fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d %s\n", + symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded")); + fflush(msgfp); } + return ( gfdata ); /*ptr to chardef for symbol in size*/ } /* --- end-of-function get_chardef() --- */ @@ -3197,9 +4462,9 @@ return ( gfdata ); /*ptr to chardef fo * 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 + * Arguments: symdef (I) mathchardef * corresponding to symbol whose + * corresponding chardef subraster is wanted + * size (I) int containing 0-5 for desired size * -------------------------------------------------------------------------- * Returns: ( subraster * ) pointer to struct defining symbol at size, * or NULL for any error @@ -3215,6 +4480,8 @@ 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 */ +raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */ +int delete_subraster(); /* in case gftobitmap() fails */ int aasupsamp(), /*antialias char with supersampling*/ grayscale=256; /* aasupersamp() parameters */ /* ------------------------------------------------------------------------- @@ -3225,11 +4492,23 @@ if ( (gfdata=get_chardef(symdef,size)) / if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */ != NULL ) /* and check that we succeeded */ { - sp->type = CHARASTER; /* static character raster */ + raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */ + int format = image->format; /* 1=bitmap, else .gf */ 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 */ + if ( format == 1 ) /* already a bitmap */ + { sp->type = CHARASTER; /* static char raster */ + sp->image = image; } /* store ptr to its bitmap */ + else /* need to convert .gf-to-bitmap */ + if ( (bitmaprp = gftobitmap(image)) /* convert */ + != (raster *)NULL ) /* successful */ + { sp->type = IMAGERASTER; /* allocated raster will be freed */ + sp->image = bitmaprp; } /* store ptr to converted bitmap */ + else /* conversion failed */ + { delete_subraster(sp); /* free unneeded subraster */ + sp = (subraster *)NULL; /* signal error to caller */ + goto end_of_job; } /* quit */ if ( issupersampling ) /* antialias character right here */ { raster *aa = NULL; /* antialiased char raster */ @@ -3245,14 +4524,53 @@ if ( (gfdata=get_chardef(symdef,size)) / sp->type = IMAGERASTER; } /* character is an image raster */ } /* --- end-of-if(issupersampling) --- */ } /* --- end-of-if(sp!=NULL) --- */ -if ( msgfp!=NULL && msglevel>=999 ) - { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n", - symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); } +end_of_job: + if ( msgfp!=NULL && msglevel>=999 ) + { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d" + " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline), + (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" ")); + fflush(msgfp); } return ( sp ); /* back to caller */ } /* --- end-of-function get_charsubraster() --- */ /* ========================================================================== + * Function: get_symsubraster ( symbol, size ) + * Purpose: returns new subraster ptr containing + * data for symbol at given size + * -------------------------------------------------------------------------- + * Arguments: symbol (I) char * corresponding to symbol + * whose corresponding subraster is wanted + * size (I) int containing 0-5 for desired size + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) pointer to struct defining symbol at size, + * or NULL for any error + * -------------------------------------------------------------------------- + * Notes: o just combines get_symdef() and get_charsubraster() + * ======================================================================= */ +/* --- entry point --- */ +subraster *get_symsubraster ( char *symbol, int size ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */ +mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */ +/* ------------------------------------------------------------------------- +look up mathchardef for symbol +-------------------------------------------------------------------------- */ +if ( symbol != NULL ) /* user supplied input symbol */ + symdef = get_symdef(symbol); /*look up corresponding mathchardef*/ +/* ------------------------------------------------------------------------- +look up chardef for mathchardef and wrap a subraster structure around data +-------------------------------------------------------------------------- */ +if ( symdef != NULL ) /* lookup succeeded */ + sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */ +return ( sp ); /* back to caller with sp or NULL */ +} /* --- end-of-function get_symsubraster() --- */ + + +/* ========================================================================== * Function: get_baseline ( gfdata ) * Purpose: returns baseline for a chardef struct * -------------------------------------------------------------------------- @@ -3331,6 +4649,7 @@ int defheight, bestheight=9999, /* heigh int iswidth = 0; /* true if best-fit width desired */ int isunesc = 0, /* true if leading escape removed */ issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */ +int iscurly = 0; /* true for StMary's curly symbols */ char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */ /* ------------------------------------------------------------------------- determine if searching height or width, and search symdefs[] for best-fit @@ -3338,6 +4657,7 @@ determine if searching height or width, /* --- arg checks --- */ if ( symlen < 1 ) return (sp); /* no input symbol suplied */ if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */ +if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */ /* --- ignore leading escapes for CMEX10 --- */ if ( 1 ) /* ignore leading escape */ if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */ @@ -3367,23 +4687,24 @@ for ( idef=0; ;idef++ ) /* until trail if ( defsym == NULL ) break; /* reached end-of-table */ else /* check against caller's symbol */ if ( family<0 || deffam == family /* if explicitly in caller's family*/ - || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10)) ) + || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) ) { strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */ if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */ - strcpy(lcsymbol,lcsymbol+1); /* so squeeze it out of lcsymbol too*/ + {strsqueeze(lcsymbol,1);} /*so squeeze it out of lcsymbol too*/ 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*/ + 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,unescsymbol)) != NULL) /*found caller's sym*/ if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/ && (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */ - if ( (deffam == CMSY10 ? /* CMSY10 or not CMSY10 */ + if ( ( deffam == CMSY10 ? /* CMSY10 or not CMSY10 */ symptr == lcsymbol /* caller's sym is a prefix */ && deflen == symlen: /* and same length */ - symptr == lcsymbol /* caller's sym is a prefix */ - || symptr == lcsymbol+deflen-symlen) ) /* or a suffix */ + (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/ + (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 */ @@ -3404,10 +4725,14 @@ construct subraster for best fit charact -------------------------------------------------------------------------- */ 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 */ +if ( (sp==NULL && height-bigheight>5) /* try to construct delim */ +|| bigdef < 0 ) /* delim not in font tables */ 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 */ +if ( msgfp!=NULL && msglevel>=99 ) + fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n", + (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success")); return ( sp ); } /* --- end-of-function get_delim() --- */ @@ -3436,18 +4761,30 @@ Allocations and Declarations -------------------------------------------------------------------------- */ subraster *sp = (subraster *)NULL, /* subraster returned to caller */ *new_subraster(); /* allocate subraster */ +subraster *get_symsubraster(), /* look up delim pieces in cmex10 */ + *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */ + *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */ + *rastack(), *rastcat(); /* stack pieces, concat filler */ +int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/ 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 pixsz = 1, /* pixels are one bit each */ + symsize = 0; /* size arg for get_symsubraster() */ 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 */ +char *lp=NULL, *rp=NULL, /* check symbol for left or right */ + *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */ + *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */ + *lp4=NULL, *rp4=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 */ +subraster *uparrow_subraster(); /* up/down arrows */ +int isprealloc = 1; /*pre-alloc subraster, except arrow*/ +int oldsmashmargin = smashmargin, /* save original smashmargin */ + wasnocatspace = isnocatspace; /* save original isnocatspace */ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ @@ -3460,51 +4797,196 @@ if ( height < 3 ) goto end_of_job; /* to if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio; else width = (height+(aspectratio+1)/2)/aspectratio; if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */ -|| strstr(symbol,"\\|") != NULL ) /* same || in standard tex notation*/ - width = max2(width,5); /* need space between two |'s */ +|| strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/ +|| strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */ + width = max2(width,6); /* need space between two |'s */ if ( width < 2 ) width=2; /* set min width */ if ( strchr(symbol,'(') != NULL /* if left ( */ || strchr(symbol,')') != NULL ) /* or right ) paren wanted */ - width = (3*width)/2; /* adjust width */ + { width = (3*width)/2; /* adjust width */ + if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */ +if ( strchr(symbol,'/') != NULL /* left / */ +|| strstr(symbol,"\\\\") != NULL /* or \\ for right \ */ +|| strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */ + width = max2(height/3,5); +if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */ + { width = min2(height/3,20); /* adjust width */ + isprealloc = 0; } /* don't preallocate subraster */ +if ( strchr(symbol,'{') != NULL /* if left { */ +|| strchr(symbol,'}') != NULL ) /* or right } brace wanted */ + { isprealloc = 0; } /* don't preallocate */ /* --- 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 */ +if ( isprealloc ) /* pre-allocation wanted */ + { 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 */ { - int mywidth = min2(width,20); /* max width for ()'s */ - circle_raster ( rasp, /* embedded raster image */ + if ( isdrawparen ) { /* draw the paren */ + int mywidth = min2(width,20); /* max width for ()'s */ + circle_raster ( rasp, /* embedded raster image */ 0, 0, /* row0,col0 are upper-left corner */ height-1, mywidth-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 */ + isokay = 1; } /* set flag */ + else { + int isleft = (lp!=NULL?1:0); /* true for left, false for right */ + char *parentop = (isleft?"\\leftparentop":"\\rightparentop"), + *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"), + *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar"); + int baseht=0, barht=0, /* height of base=top+bot, bar */ + ibar=0, nbars=0; /* bar index, #bars between top&bot*/ + int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */ + topfill=(isleft?0:0), botfill=(isleft?0:0), + barfill=(isleft?0:7); /* alignment fillers */ + /* --- get pieces at largest size smaller than total height --- */ + for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/ + { + /* --- get pieces at current test size --- */ + isokay = 1; /* check for all pieces */ + if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0; + if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0; + if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0; + /* --- check sum of pieces against total desired height --- */ + if ( isokay ) { /* all pieces retrieved */ + baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/ + barht = (symbar->image)->height; /* bar height */ + if ( baseht < height+5 ) break; /* largest base that's not too big */ + if ( symsize < 1 ) break; /* or smallest available base */ + } /* --- end-of-if(isokay) --- */ + /* --- free test pieces that were too big --- */ + if ( symtop != NULL ) delete_subraster(symtop); /* free top */ + if ( symbot != NULL ) delete_subraster(symbot); /* free bot */ + if ( symbar != NULL ) delete_subraster(symbar); /* free bar */ + isokay = 0; /* nothing available */ + if ( symsize < 1 ) break; /* leave isokay=0 after smallest */ + } /* --- end-of-for(symsize) --- */ + /* --- construct brace from pieces --- */ + if ( isokay ) { /* we have the pieces */ + /* --- add alignment fillers --- */ + smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/ + topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop); + botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot); + barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar); + smashmargin = oldsmashmargin; /* reset smashmargin */ + isnocatspace = wasnocatspace; /* reset isnocatspace */ + /* --- #bars needed between top and bot --- */ + nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */ + /* --- stack pieces --- */ + sp = topsym; /* start with top piece */ + if ( nbars > 0 ) /* need nbars between top and bot */ + for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2); + sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */ + delete_subraster(barsym); /* barsym no longer needed */ + } /* --- end-of-if(isokay) --- */ + } /* --- end-of-if/else(isdrawparen) --- */ } /* --- end-of-if(left- or right-() paren wanted) --- */ /* ------------------------------------------------------------------------- +{ } braces +-------------------------------------------------------------------------- */ +else + if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */ + || (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */ + { + int isleft = (lp!=NULL?1:0); /* true for left, false for right */ + char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"), + *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"), + *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"), + *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar"); + int baseht=0, barht=0, /* height of base=top+bot+mid, bar */ + ibar=0, nbars=0; /* bar index, #bars above,below mid*/ + int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */ + topfill=(isleft?4:0), botfill=(isleft?4:0), + midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */ + /* --- get pieces at largest size smaller than total height --- */ + for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/ + { + /* --- get pieces at current test size --- */ + isokay = 1; /* check for all pieces */ + if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0; + if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0; + if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0; + if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0; + /* --- check sum of pieces against total desired height --- */ + if ( isokay ) { /* all pieces retrieved */ + baseht = (symtop->image)->height + (symbot->image)->height + + (symmid->image)->height; /* top+bot+mid height */ + barht = (symbar->image)->height; /* bar height */ + if ( baseht < height+5 ) break; /* largest base that's not too big */ + if ( symsize < 1 ) break; /* or smallest available base */ + } /* --- end-of-if(isokay) --- */ + /* --- free test pieces that were too big --- */ + if ( symtop != NULL ) delete_subraster(symtop); /* free top */ + if ( symbot != NULL ) delete_subraster(symbot); /* free bot */ + if ( symmid != NULL ) delete_subraster(symmid); /* free mid */ + if ( symbar != NULL ) delete_subraster(symbar); /* free bar */ + isokay = 0; /* nothing available */ + if ( symsize < 1 ) break; /* leave isokay=0 after smallest */ + } /* --- end-of-for(symsize) --- */ + /* --- construct brace from pieces --- */ + if ( isokay ) { /* we have the pieces */ + /* --- add alignment fillers --- */ + smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/ + topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop); + botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot); + midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid); + barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar); + smashmargin = oldsmashmargin; /* reset smashmargin */ + isnocatspace = wasnocatspace; /* reset isnocatspace */ + /* --- #bars needed on each side of mid piece --- */ + nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/ + /* --- stack pieces --- */ + sp = topsym; /* start with top piece */ + if ( nbars > 0 ) /* need nbars above middle */ + for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2); + sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/ + if ( nbars > 0 ) /* need nbars below middle */ + for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2); + sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */ + delete_subraster(barsym); /* barsym no longer needed */ + } /* --- end-of-if(isokay) --- */ + } /* --- end-of-if(left- or right-{} brace wanted) --- */ +/* ------------------------------------------------------------------------- [ ] brackets -------------------------------------------------------------------------- */ else if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */ - || (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */ + || (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */ + || (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */ + || (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */ + || (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */ + || (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */ + || (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */ + || (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */ { - /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */ - int mywidth = min2(width,12); /* max width for horizontal bars */ - thickness = 1; /* set lines 1 or 2 pixels thick */ - rule_raster(rasp, 0,0, mywidth,thickness, 0); /* top horizontal bar */ - rule_raster(rasp, height-thickness,0, mywidth,thickness, 0); /* bottom */ - if ( lp != NULL ) /* left [ bracket wanted */ + /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */ + int mywidth = min2(width,12), /* max width for horizontal bars */ + wthick = 1; /* thickness of top.bottom bars */ + thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */ + if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/ + wthick = thickness; /* same thickness for top/bot bar */ + if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */ + rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */ + if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */ + rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */ + if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */ rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */ - if ( rp != NULL ) /* right ] bracket wanted */ + if ( lp4 != NULL ) /* 2nd left vertical bar needed */ + rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */ + if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */ rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */ + if ( rp4 != NULL ) /* 2nd right vertical bar needed */ + rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/ isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-[] bracket wanted) --- */ /* ------------------------------------------------------------------------- @@ -3514,18 +4996,69 @@ else if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */ || (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */ { - /* --- line_raster ( rasp, row0, col0, row1, col1, thickness ) --- */ - int mywidth = min2(width,12); /* max width for brackets */ - thickness = 1; /* set line pixel thickness */ + /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */ + int mywidth = min2(width,12), /* max width for brackets */ + mythick = 1; /* all lines one pixel thick */ + thickness = (height<25?1:2); /* set line pixel thickness */ if ( lp != NULL ) /* left < bracket wanted */ - { line_raster(rasp,height/2,0,0,mywidth-1,thickness); - line_raster(rasp,height/2,0,height-1,mywidth-1,thickness); } + { line_raster(rasp,height/2,0,0,mywidth-1,mythick); + if ( thickness>1 ) + line_raster(rasp,height/2,1,0,mywidth-1,mythick); + line_raster(rasp,height/2,0,height-1,mywidth-1,mythick); + if ( thickness>1 ) + line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); } if ( rp != NULL ) /* right > bracket wanted */ - { line_raster(rasp,height/2,mywidth-1,0,0,thickness); - line_raster(rasp,height/2,mywidth-1,height-1,0,thickness); } + { line_raster(rasp,height/2,mywidth-1,0,0,mythick); + if ( thickness>1 ) + line_raster(rasp,height/2,mywidth-2,0,0,mythick); + line_raster(rasp,height/2,mywidth-1,height-1,0,mythick); + if ( thickness>1 ) + line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); } isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-<> bracket wanted) --- */ /* ------------------------------------------------------------------------- +/ \ delimiters +-------------------------------------------------------------------------- */ +else + if ( (lp=strchr(symbol,'/')) != NULL /* left / wanted */ + || (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */ + || (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */ + { + /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */ + int mywidth = width; /* max width for / \ */ + thickness = 1; /* set line pixel thickness */ + if ( lp != NULL ) /* left / wanted */ + line_raster(rasp,0,mywidth-1,height-1,0,thickness); + if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */ + line_raster(rasp,0,0,height-1,mywidth-1,thickness); + isokay = 1; /* set flag */ + } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */ +/* ------------------------------------------------------------------------- +arrow delimiters +-------------------------------------------------------------------------- */ +else + if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */ + { + /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */ + int mywidth = width; /* max width for / \ */ + int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */ + || strstr(symbol,"Down")!=NULL); /* or a Down */ + int drctn = +1; /* init for uparrow */ + if ( strstr(symbol,"down")!=NULL /* down if we have down */ + || strstr(symbol,"Down")!=NULL ) /* or Down */ + { drctn = (-1); /* reset direction to down */ + if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */ + || strstr(symbol,"Up")!=NULL ) /* and down or Down */ + drctn = 0; } /* reset direction to updown */ + sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig); + if ( sp != NULL ) + { 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) */ + isokay = 1; } /* set flag */ + } /* --- end-of-if(arrow delimiter wanted) --- */ +/* ------------------------------------------------------------------------- \- for | | brackets or \= for || || brackets -------------------------------------------------------------------------- */ else @@ -3538,13 +5071,13 @@ else int midcol = width/2; /* middle col, left of mid if even */ if ( rp != NULL /* left or right || bracket wanted */ || rp2 != NULL ) /* or || in standard tex notation */ - { thickness = 1; /* each | of || one pixel thick */ + { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/ rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */ rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); } else /*nb, lp2 spuriously set if rp2 set*/ if ( lp != NULL /* left or right | bracket wanted */ || lp2 != NULL ) /* ditto for synomym */ - { thickness = 1; /* set | two pixels thick */ + { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */ rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/ isokay = 1; /* set flag */ } /* --- end-of-if(left- or right-[] bracket wanted) --- */ @@ -3552,8 +5085,11 @@ else back to caller -------------------------------------------------------------------------- */ end_of_job: + if ( msgfp!=NULL && msglevel>=99 ) + fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n", + (symbol==NULL?"null":symbol),isokay); if ( !isokay ) /* don't have requested delimiter */ - { delete_subraster(sp); /* so free unneeded structure */ + { if (sp!=NULL) 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() --- */ @@ -3598,6 +5134,8 @@ static char *prefixes[] = /*e.g., \big "\\big", "\\Big", "\\bigg", "\\Bigg", "\\bigl", "\\Bigl", "\\biggl", "\\Biggl", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL }; +static char *starred[] = /* may be followed by * */ + { "\\hspace", "\\!", NULL }; /* ------------------------------------------------------------------------- just return the next char if it's not \ -------------------------------------------------------------------------- */ @@ -3637,13 +5175,19 @@ for ( iprefix=0; prefixes[iprefix] != NU /* --- every \ must be followed by at least one char, e.g., \[ --- */ if ( esclen < 1 ) /* \ followed by non-alpha */ *ptoken++ = *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 ( istext != 3 ) /* but not \mathbb */ - if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */ - expression++; } /* so flush delim */ *ptoken = '\000'; /* null-terminate token */ +/* --- check for \hspace* or other starred commands --- */ +for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */ + if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */ + if ( *expression == '*' ) /* follows by a * */ + { *ptoken++ = *expression++; /* copy * and bump ptr */ + *ptoken = '\000'; /* null-terminate token */ + break; } /* stop checking */ +/* --- respect spaces in text mode, except first space after \escape --- */ +if ( esclen >= 1 ) { /*only for alpha \sequences*/ + if ( istextmode ) /* in \rm or \it text mode */ + if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */ + expression++; } /* so flush delim */ /* --- back to caller --- */ end_of_job: if ( msgfp!=NULL && msglevel>=999 ) @@ -3708,16 +5252,17 @@ char *texsubexpr ( char *expression, cha 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) */ +char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */ + rightdelim[256] = ")\000"; /* and matching right) */ char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/ +char *strtexchr(), *texleft(); /* check for \left, and get it */ 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 isanyright = 1; /* true matches any right with left, (...] */ int isleftdot = 0; /* true if left brace is a \. */ int nestlevel = 1; /* current # of nested braces */ -int subsz=0 /*, maxsubsz=8192*/; /* #chars in returned subexpr[] buffer*/ +int subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/ /* ------------------------------------------------------------------------- skip leading whitespace and just return the next char if it's not { -------------------------------------------------------------------------- */ @@ -3727,18 +5272,28 @@ if ( expression == NULL ) return(NULL); skipwhite(expression); /* leading whitespace gone */ if ( *expression == '\000' ) return(NULL); /* nothing left to scan */ /* --- set maxsubsz --- */ -if ( maxsubsz < 1 ) maxsubsz = 8192; /* input 0 means unlimited */ +if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2; /* input 0 means unlimited */ /* --- check for escape --- */ if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */ gotescape = 1; /* so set flag accordingly */ +/* --- check for \left...\right --- */ +if ( gotescape ) /* begins with \ */ + if ( memcmp(expression+1,"left",4) ) /* and followed by left */ + if ( strchr(left,'l') != NULL ) /* caller wants \left's */ + if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/ + { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/ + (isdelim?NULL:leftdelim),rightdelim); + if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */ + return ( pright ); /*back to caller past \right*/ + } /* --- end-of-if(expression=="\\left") --- */ /* --- if first char isn't left{ or script, just return it to caller --- */ -if ( !isbrace(expression,left,isescape) ) /* not a left{ */ +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 */ + 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 \. */ @@ -3756,9 +5311,9 @@ accumulate chars between balanced {}'s, /* --- 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 ( gotescape ) { /*need to bump another char*/ if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */ - else expression++; /* else just bump past it */ + else expression++; } /* else just bump past it */ /* --- set maximum size for numerical arguments --- */ if ( 0 ) /* check turned on or off? */ if ( !isescape && !isdelim ) /*looking for numerical arg*/ @@ -3771,12 +5326,12 @@ while ( 1 ) /*until balanced right} { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/ { expression = origexpression; /* so end-of-string is error*/ subexpr = origsubexpr; } /* so reset all ptrs */ - if ( isdelim ) /* generate fake right */ + 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++ = *rightdelim; } /* so fake actual right */ *subexpr = '\000'; /* null-terminate subexpr */ return ( expression ); } /* back with final token */ /* --- check preceding char for escape --- */ @@ -3811,6 +5366,112 @@ while ( 1 ) /*until balanced right} /* ========================================================================== + * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim) + * Purpose: scans expression, starting after opening \left, + * and returning ptr after matching closing \right. + * Everything between is returned in subexpr, if given. + * Likewise, if given, ldelim returns delimiter after \left + * and rdelim returns delimiter after \right. + * If ldelim is given, the returned subexpr doesn't include it. + * If rdelim is given, the returned pointer is after that delim. + * -------------------------------------------------------------------------- + * Arguments: expression (I) char * to first char of null-terminated + * string immediately following opening \left + * subexpr (O) char * to null-terminated string returning + * either everything between balanced + * \left ... \right. If leftdelim given, + * subexpr does _not_ contain that delimiter. + * maxsubsz (I) int containing max #bytes returned + * in subexpr buffer (0 means unlimited) + * ldelim (O) char * returning delimiter following + * opening \left + * rdelim (O) char * returning delimiter following + * closing \right + * -------------------------------------------------------------------------- + * Returns: ( char * ) ptr to the first char of expression + * past closing \right, or past closing + * right delimiter if rdelim!=NULL, + * or NULL for any error. + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +char *texleft ( char *expression, char *subexpr, int maxsubsz, + char *ldelim, char *rdelim ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texchar(), /* get delims after \left,\right */ + *strtexchr(), *pright=expression; /* locate matching \right */ +static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */ +int sublen = 0; /* #chars between \left...\right */ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- init output --- */ +if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */ +if ( ldelim != NULL ) *ldelim = '\000'; /* init ldelim, if given */ +if ( rdelim != NULL ) *rdelim = '\000'; /* init rdelim, if given */ +/* --- check args --- */ +if ( expression == NULL ) goto end_of_job; /* no input supplied */ +if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */ +/* --- determine left delimiter --- */ +if ( ldelim != NULL ) /* caller wants left delim */ + { skipwhite(expression); /* interpret \left ( as \left( */ + expression = texchar(expression,ldelim); } /*delim from expression*/ +/* ------------------------------------------------------------------------- +locate \right balancing opening \left +-------------------------------------------------------------------------- */ +/* --- first \right following \left --- */ +if ( (pright=strtexchr(expression,right)) /* look for \right after \left */ +!= NULL ) { /* found it */ + /* --- find matching \right by pushing past any nested \left's --- */ + char *pleft = expression; /* start after first \left( */ + while ( 1 ) { /*break when matching \right found*/ + /* -- locate next nested \left if there is one --- */ + if ( (pleft=strtexchr(pleft,left)) /* find next \left */ + == NULL ) break; /*no more, so matching \right found*/ + pleft += strlen(left); /* push ptr past \left token */ + if ( pleft >= pright ) break; /* not nested if \left after \right*/ + /* --- have nested \left, so push forward to next \right --- */ + if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */ + == NULL ) break; /* ran out of \right's */ + } /* --- end-of-while(1) --- */ + } /* --- end-of-if(pright!=NULL) --- */ +/* --- set subexpression length, push pright past \right --- */ +if ( pright != (char *)NULL ) /* found matching \right */ + { sublen = (int)(pright-expression); /* #chars between \left...\right */ + pright += strlen(right); } /* so push pright past \right */ +/* ------------------------------------------------------------------------- +get rightdelim and subexpr between \left...\right +-------------------------------------------------------------------------- */ +/* --- get delimiter following \right --- */ +if ( rdelim != NULL ) { /* caller wants right delim */ + if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/ + { strcpy(rdelim,"."); /* set default \right. */ + sublen = strlen(expression); /* use entire remaining expression */ + pright = expression + sublen; } /* and push pright to end-of-string*/ + else /* have explicit matching \right */ + { skipwhite(pright); /* interpret \right ) as \right) */ + pright = texchar(pright,rdelim); /* pull delim from expression */ + if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */ +/* --- get subexpression between \left...\right --- */ +if ( sublen > 0 ) /* have subexpr */ + if ( subexpr != NULL ) { /* and caller wants it */ + if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */ + memcpy(subexpr,expression,sublen); /* stuff between \left...\right */ + subexpr[sublen] = '\000'; } /* null-terminate subexpr */ +end_of_job: + if ( msglevel>=99 && msgfp!=NULL ) + { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n", + (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim), + (subexpr==NULL?"none":subexpr)); fflush(msgfp); } + return ( pright ); +} /* --- end-of-function texleft --- */ + + +/* ========================================================================== * 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}, @@ -3846,16 +5507,16 @@ 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 +init "scripts" -------------------------------------------------------------------------- */ -/* --- 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 */ +if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/ +if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/ /* ------------------------------------------------------------------------- get subscript and/or superscript from expression -------------------------------------------------------------------------- */ -while ( expression != NULL ) +while ( expression != NULL ) { + skipwhite(expression); /* leading whitespace gone */ + if ( *expression == '\000' ) return(expression); /* nothing left to scan */ if ( isthischar(*expression,SUBSCRIPT) /* found _ */ && (which==1 || which>2 ) ) /* and caller wants it */ { if ( gotsub /* found 2nd subscript */ @@ -3871,6 +5532,7 @@ while ( expression != NULL ) expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); } else /* neither _ nor ^ */ return ( expression ); /*return ptr past "scripts"*/ + } /* --- end-of-while(expression!=NULL) --- */ return ( expression ); } /* --- end-of-function texscripts() --- */ @@ -3913,8 +5575,9 @@ int gotescape = 0, /* true if leading c /* ------------------------------------------------------------------------- check for brace -------------------------------------------------------------------------- */ -/* --- first check for end-of-string --- */ -if ( *expression == '\000' ) return(0); /* nothing to check */ +/* --- first check for end-of-string or \= ligature --- */ +if ( *expression == '\000' /* nothing to check */ +|| isligature ) goto end_of_job; /* have a \= ligature */ /* --- check leading char for escape --- */ if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */ { gotescape = 1; /* so set flag accordingly */ @@ -3930,7 +5593,11 @@ if ( gotbrace && isthischar(*expression, /* ------------------------------------------------------------------------- back to caller -------------------------------------------------------------------------- */ -if ( gotbrace && /* found a brace */ +end_of_job: + if ( msglevel>=999 && msgfp!=NULL ) + { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n", + expression,gotbrace,isligature); fflush(msgfp); } + if ( gotbrace && /* found a brace */ ( isescape==2 || /* escape irrelevant */ gotescape==isescape ) /* un/escaped as requested */ ) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */ @@ -3985,7 +5652,7 @@ process preamble if present -------------------------------------------------------------------------- */ /*process_preamble:*/ if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */ -!= NULL ) /* found embedded $ */ +!= NULL ) { /* found embedded $ */ if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/ > 0 ) { /* must have preamble preceding $ */ if ( prelen < 65 ) { /* too long for a prefix */ @@ -4010,7 +5677,8 @@ if ( (dollar=strchr(expression,'$')) /* *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */ /* --- finally, set flag and shift size parameter out of preamble --- */ isfontsize = 1; /*set flag showing font size present*/ - if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/ + if ( comma != NULL ) /*2/15/12-isn't this superfluous???*/ + {strsqueezep(pretext,comma+1);} /* squeeze out leading size param */ } /* --- end-of-if(comma!=NULL||etc) --- */ /* --- copy any preamble params following size to caller's subexpr --- */ if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/ @@ -4039,6 +5707,7 @@ if ( (dollar=strchr(expression,'$')) /* isdisplaystyle = 2; /* so set \displaystyle */ /*goto process_preamble;*/ /*check for preamble after leading $*/ } /* --- end-of-if/else(prelen>0) --- */ + } /* --- end-of-if(dollar!=NULL) --- */ /* ------------------------------------------------------------------------- back to caller -------------------------------------------------------------------------- */ @@ -4074,6 +5743,8 @@ char *expptr=expression, /* ptr within *tokptr=NULL, /*ptr to token found in expression*/ *texsubexpr(), argval[8192]; /*parse for macro args after token*/ char *strchange(); /* change leading chars of string */ +int strreplace(); /* replace nnn with actual num, etc*/ +char *strwstr(); /*use strwstr() instead of strstr()*/ char *findbraces(); /*find left { and right } for \atop*/ int idelim=0, /* left- or right-index */ isymbol=0; /*symbols[],rightcomment[],etc index*/ @@ -4130,25 +5801,66 @@ static char *atopdelims[] = /* delims f * -------------------------------------------------------- */ char *htmlsym=NULL; /* symbols[isymbol].html */ static struct { char *html; char *args; char *latex; } symbols[] = - { /* --------------------------------------- + { /* -------------------------------------------- user-supplied newcommands - --------------------------------------- */ - #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */ - #include NEWCOMMANDS - #endif - /* ------------------------------------------ - LaTeX Macro #args,default template... - ------------------------------------------ */ + -------------------------------------------- */ + #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */ + #include NEWCOMMANDS + #endif + /* -------------------------------------------- + Specials termchar value... + -------------------------------------------- */ + { "\\version", NULL, "{\\small\\red\\text \\fbox{\\begin{gather}" + "mime\\TeX version \\versionnumber \\\\" + "last revised \\revisiondate \\\\ \\copyrighttext \\\\" + "see \\homepagetext for details \\end{gather}}}" }, + { "\\copyright", NULL, + "{\\small\\red\\text \\fbox{\\begin{gather}" + "mimeTeX \\copyrighttext \\\\" + "see \\homepagetext for details \\end{gather}}}" }, + { "\\versionnumber", NULL, "{\\text " VERSION "}" }, + { "\\revisiondate", NULL, "{\\text " REVISIONDATE "}" }, + { "\\copyrighttext", NULL, "{\\text " COPYRIGHTTEXT ".}" }, + { "\\homepagetext", NULL, + "{\\text http://www.forkosh.com/mimetex.html}" }, + /* -------------------------------------------- + Cyrillic termchar mimeTeX equivalent... + -------------------------------------------- */ + { "\\\'G", "embed\\","{\\acute{G}}" }, + { "\\\'g", "embed\\","{\\acute{g}}" }, + { "\\\'K", "embed\\","{\\acute{K}}" }, + { "\\\'k", "embed\\","{\\acute{k}}" }, + { "\\u U", "embed\\","{\\breve{U}}" }, + { "\\u u", "embed\\","{\\breve{u}}" }, + /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/ + /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/ + { "\\\"I", "embed\\","{\\ddot{\\=I}}" }, + { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" }, + /* -------------------------------------------- + LaTeX Macro #args,default template... + -------------------------------------------- */ { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" }, + { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */ + { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */ + { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */ + { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */ + { "\\buildrel","3", "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */ { "\\overset", NULL, "\\stackrel" }, /* just an alias */ { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */ - /* --------------------------------------- - html termchar LaTeX equivalent... - --------------------------------------- */ + { "\\dfrac", "2", "{\\frac{#1}{#2}}" }, + { "\\binom", "2", "{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" }, + { "\\aangle","26", "{\\boxaccent{#1}{#2}}" }, + { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/ + { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" }, + /* -------------------------------------------- + html char termchar LaTeX equivalent... + -------------------------------------------- */ { """, ";", "\"" }, /* " is first, " */ { "&", ";", "&" }, { "<", ";", "<" }, { ">", ";", ">" }, + /*{ "\", ";", "\\" },*/ /* backslash */ + { "&backslash",";", "\\" }, { " ", ";", "~" }, { "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" }, { "¦", ";", "|" }, @@ -4166,52 +5878,139 @@ static struct { char *html; char *args; { "Ä", ";", "{\\rm~\\ddot~A}" }, { "Å", ";", "{\\rm~A\\limits^{-1$o}}" }, { "ã", ";", "{\\rm~\\tilde~a}" }, - { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */ - /* --------------------------------------- + { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */ + { "&#", ";", "{[\\&\\#nnn?]}" }, /* all other explicit &#nnn's */ + /* -------------------------------------------- + html tag termchar LaTeX equivalent... + -------------------------------------------- */ + { "< br >", "embed\\i", "\\\\" }, + { "< br / >", "embed\\i", "\\\\" }, + { "< dd >", "embed\\i", " \000" }, + { "< / dd >", "embed\\i", " \000" }, + { "< dl >", "embed\\i", " \000" }, + { "< / dl >", "embed\\i", " \000" }, + { "< p >", "embed\\i", " \000" }, + { "< / p >", "embed\\i", " \000" }, + /* -------------------------------------------- + garbage termchar LaTeX equivalent... + -------------------------------------------- */ + { "< tex >", "embed\\i", " \000" }, + { "< / tex >", "embed\\i", " \000" }, + /* -------------------------------------------- LaTeX termchar mimeTeX equivalent... - --------------------------------------- */ + -------------------------------------------- */ { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" }, { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" }, { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" }, { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" }, + { "\\dots", NULL, "{\\cdots}" }, { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" }, { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" }, - { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"}, + { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4." + "\\hspace1\\raisebox0.}"}, { "\\notin", NULL, "{\\not\\in}" }, { "\\neq", NULL, "{\\not=}" }, + { "\\ne", NULL, "{\\not=}" }, + { "\\mapsto", NULL, "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" }, { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" }, { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"}, + { "\\textcelsius", NULL, "{\\textdegree C}"}, + { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"}, { "\\cr", NULL, "\\\\" }, + /*{ "\\colon", NULL, "{:}" },*/ + { "\\iiint", NULL, "{\\int\\int\\int}\\limits" }, { "\\iint", NULL, "{\\int\\int}\\limits" }, { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" }, + { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" }, + { "\\_", "embed","{\\underline{\\ }}" }, /* displayed underscore */ { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" }, { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" }, + { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */ + { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" }, + { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" }, + { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" }, + { "\'", "embed","^{\\fs{-1}\\prime}" }, { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" }, + { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" }, { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" }, { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" }, { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" }, { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" }, { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" }, + { "\\cancel",NULL, "\\Not" }, { "\\hhline",NULL, "\\Hline" }, { "\\Hline", NULL, "\\hline\\,\\\\\\hline" }, - /* --------------------------------------------------------- + /* ----------------------------------------------------------------------- + As per emails with Zbigniew Fiedorowicz "Algebra Syntax" termchar mimeTeX/LaTeX equivalent... - ------------------------------------------------------------ */ + ----------------------------------------------------------------------- */ { "sqrt", "1", "{\\sqrt{#1}}" }, { "sin", "1", "{\\sin{#1}}" }, { "cos", "1", "{\\cos{#1}}" }, { "asin", "1", "{\\sin^{-1}{#1}}" }, { "acos", "1", "{\\cos^{-1}{#1}}" }, - { "exp", "1", "{e^{#1}}" }, + { "exp", "1", "{{\\rm~e}^{#1}}" }, { "det", "1", "{\\left|{#1}\\right|}" }, - /* --------------------------------------- - LaTeX Constant termchar value... - --------------------------------------- */ - { "\\thinspace", NULL, "2" }, - { "\\thinmathspace", NULL, "2" }, + /* -------------------------------------------- + LaTeX Constant termchar value... + -------------------------------------------- */ + { "\\thinspace", NULL, "\\," }, + { "\\thinmathspace", NULL, "\\," }, { "\\textwidth", NULL, "400" }, + /* --- end-of-table indicator --- */ { NULL, NULL, NULL } } ; /* --- end-of-symbols[] --- */ +/* --- + * html &#nn chars converted to latex equivalents + * ---------------------------------------------- */ +int htmlnum=0; /* numbers[inum].html */ +static struct { int html; char *latex; } numbers[] = + { /* --------------------------------------- + html num LaTeX equivalent... + --------------------------------------- */ + { 9, " " }, /* horizontal tab */ + { 10, " " }, /* line feed */ + { 13, " " }, /* carriage return */ + { 32, " " }, /* space */ + { 33, "!" }, /* exclamation point */ + { 34, "\"" }, /* " */ + { 35, "#" }, /* hash mark */ + { 36, "$" }, /* dollar */ + { 37, "%" }, /* percent */ + { 38, "&" }, /* & */ + { 39, "\'" }, /* apostrophe (single quote) */ + { 40, ")" }, /* left parenthesis */ + { 41, ")" }, /* right parenthesis */ + { 42, "*" }, /* asterisk */ + { 43, "+" }, /* plus */ + { 44, "," }, /* comma */ + { 45, "-" }, /* hyphen (minus) */ + { 46, "." }, /* period */ + { 47, "/" }, /* slash */ + { 58, ":" }, /* colon */ + { 59, ";" }, /* semicolon */ + { 60, "<" }, /* < */ + { 61, "=" }, /* = */ + { 62, ">" }, /* > */ + { 63, "\?" }, /* question mark */ + { 64, "@" }, /* commercial at sign */ + { 91, "[" }, /* left square bracket */ + { 92, "\\" }, /* backslash */ + { 93, "]" }, /* right square bracket */ + { 94, "^" }, /* caret */ + { 95, "_" }, /* underscore */ + { 96, "`" }, /* grave accent */ + { 123, "{" }, /* left curly brace */ + { 124, "|" }, /* vertical bar */ + { 125, "}" }, /* right curly brace */ + { 126, "~" }, /* tilde */ + { 160, "~" }, /*   (use tilde for latex) */ + { 166, "|" }, /* ¦ (broken vertical bar) */ + { 173, "-" }, /* ­ (soft hyphen) */ + { 177, "{\\pm}" }, /* ± (plus or minus) */ + { 215, "{\\times}" }, /* × (plus or minus) */ + { -999, NULL } + } ; /* --- end-of-numbers[] --- */ /* ------------------------------------------------------------------------- first remove comments -------------------------------------------------------------------------- */ @@ -4229,7 +6028,7 @@ while ( (leftptr=strstr(expptr,leftcomme { *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 */ + strsqueezep(leftptr+1,tokptr); /* squeeze out comment */ goto next_comment; } /* stop looking for rightcomment */ /* --- no rightcomment after opening leftcomment --- */ *leftptr = '\000'; /* so terminate expression */ @@ -4239,75 +6038,115 @@ while ( (leftptr=strstr(expptr,leftcomme expptr = leftptr+1; /*resume search after this comment*/ } /* --- end-of-while(leftptr!=NULL) --- */ /* ------------------------------------------------------------------------- -convert \left( to \( and \right) to \), etc. --------------------------------------------------------------------------- */ -if ( xlateleft ) /* \left...\right xlation wanted */ - 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 macro to its expansion -------------------------------------------------------------------------- */ for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++) { int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */ int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */ + int isembedded = 0, /* true to xlate even if embedded */ + istag=0, isamp=0, /* true for , &char; symbols */ + isstrwstr = 0, /* true to use strwstr() */ + wstrlen = 0; /* length of strwstr() match */ char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/ + char embedkeywd[99] = "embed", /* keyword to signal embedded token*/ + embedterm = '\000'; /* char immediately after embed */ + int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */ char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */ *htmlterm = args, /*if *args nonumeric, then html term*/ - *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/ + *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/ + errorsym[256]; /*or latexsym may point to error msg*/ char abuff[8192]; int iarg,nargs=0; /* macro expansion params */ + char wstrwhite[99]; /* whitespace chars for strwstr() */ + skipwhite(htmlsym); /*skip any bogus leading whitespace*/ + htmllen = strlen(htmlsym); /* reset length of html token */ + istag = (isthischar(*htmlsym,"<")?1:0); /* html starts with < */ + isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */ if ( args != NULL ) /*we have args (or htmlterm) param*/ - if ( *args != '\000' ) /* and it's not an empty string */ + if ( *args != '\000' ) { /* and it's not an empty string */ if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */ { htmlterm = NULL; /* if so, then we have no htmlterm */ *abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */ nargs = atoi(abuff); } /* interpret #args to numeric */ + else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/ + { int arglen = strlen(args); /* length of "embed..." string */ + htmlterm = NULL; /* if so, then we have no htmlterm */ + isembedded = 1 ; /* turn on embedded flag */ + if ( arglen > embedlen ) /* have embed "allow escape" flag */ + embedterm = args[embedlen]; /* char immediately after "embed" */ + if (arglen > embedlen+1) { /* have embed,flag,white for strwstr*/ + isstrwstr = 1; /* turn on strwtsr flag */ + strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/ + } /* --- end-of-if(*args!='\000') --- */ 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 */ + while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */ + strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */ + != NULL ) { /* found another sym */ + int toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */ + char termchar = *(tokptr+toklen), /* char terminating html sequence */ + prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/ + int isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/ + int escapelen = toklen; /* total length of escape sequence */ + int isflush = 0; /* true to flush (don't xlate) */ + /* --- check odd/even backslashes preceding tokens --- */ + if ( isescaped ) { /* have one preceding backslash */ + char *p = tokptr-1; /* ptr to that preceding backslash */ + while ( p != expptr ) { /* and we may have more preceding */ + p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */ + isescaped = 1-isescaped; } } /* or flip isescaped flag if we do */ + /* --- init with "trivial" abuff,escapelen from symbols[] table --- */ *abuff = '\000'; /* default to empty string */ if ( latexsym != NULL ) /* table has .latex xlation */ if ( *latexsym != '\000' ) /* and it's not an empty string */ strcpy(abuff,latexsym); /* so get local copy */ - if ( htmlterm != NULL ) /* sequence may have terminator */ + if ( !isembedded ) /*embedded sequences not terminated*/ + 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 */ + /* --- don't xlate if we just found prefix of longer symbol, etc --- */ + if ( !isembedded ) { /* not embedded */ + if ( isescaped ) /* escaped */ + isflush = 1; /* set flag to flush escaped token */ + if ( !istag && isalpha((int)termchar) ) /* followed by alpha */ + isflush = 1; /* so just a prefix of longer symbol*/ + if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */ + if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/ + isflush = 1; } /* set flag to flush token */ + if ( isembedded ) /* for embedded token */ + if ( isescaped ) /* and embedded \token escaped */ + if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */ + isflush = 1; /* set flag to flush token */ + if ( isflush ) /* don't xlate this token */ + { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */ 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*/ - || (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */ - { expptr = tokptr+escapelen; /*just resume search after literal*/ - continue; } /* but don't replace it */ + /* --- check for &# prefix signalling &#nnn; --- */ + if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */ + /* --- accumulate chars comprising number following &# --- */ + char anum[32]; /* chars comprising number after &# */ + int inum = 0; /* no chars accumulated yet */ + while ( termchar != '\000' ) { /* don't go past end-of-string */ + if ( !isdigit((int)termchar) ) break; /* and don't go past digits */ + if ( inum > 10 ) break; /* some syntax error in expression */ + anum[inum] = termchar; /* accumulate this digit */ + inum++; toklen++; /* bump field length, token length */ + termchar = *(tokptr+toklen); } /* char terminating html sequence */ + anum[inum] = '\000'; /* null-terminate anum */ + escapelen = toklen; /* length of &#nnn; sequence */ + if ( htmlterm != NULL ) /* sequence may have terminator */ + escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/ + /* --- look up &#nnn in number[] table --- */ + htmlnum = atoi(anum); /* convert anum[] to an integer */ + strninit(errorsym,latexsym,128); /* init error message */ + latexsym = errorsym; /* init latexsym as error message */ + strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/ + for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */ + if ( htmlnum == numbers[inum].html ) { /* till we find a match */ + latexsym = numbers[inum].latex; /* latex replacement */ + break; } /* no need to look any further */ + if ( latexsym != NULL ) /* table has .latex xlation */ + if ( *latexsym != '\000' ) /* and it's not an empty string */ + strcpy(abuff,latexsym); /* so get local copy */ + } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */ + /* --- substitute macro arguments --- */ if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/ { char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */ @@ -4324,22 +6163,25 @@ for(isymbol=0; (htmlsym=symbols[isymbol] && !isalgebra ) /* but not in "algebra syntax" */ { strcpy(argval,optarg); /* init with default value */ if ( *expptr == '[' ) /* but user gave us [argval] */ - expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/ + expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/ else /* not optional, so get {argval} */ - if ( *expptr != '\000' ) /* check that some argval provided */ - if ( !isalgebra ) /* only { } delims for latex macro */ - expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/ - else /*any delim for algebra syntax macro*/ - { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1); + if ( *expptr != '\000' ) { /* check that some argval provided */ + if ( !isalgebra ) /* only { } delims for latex macro */ + expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/ + else { /*any delim for algebra syntax macro*/ + expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1); if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */ - if ( *argval != '{' ) /* and it's not { }-enclosed */ - { strchange(0,argval,"\\left"); /* insert opening \left, */ - strchange(0,argval+strlen(argval)-1,"\\right"); } /*\right*/ - } /* --- end-of-if/else(!isalgebra) --- */ + if ( *argval != '{' ) { /* and it's not { }-enclosed */ + strchange(0,argval,"\\left"); /* insert opening \left, */ + strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/ + } /* --- end-of-if(*expptr!='\000') --- */ + /* --- (recursively) call mimeprep() to prep the argument --- */ + if ( !isempty(argval) ) /* have an argument */ + mimeprep(argval); /* so (recursively) prep it */ /* --- replace #`iarg` in macro with argval --- */ sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */ while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */ - strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/ + {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */ while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */ strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/ } /* --- end-of-for(iarg) --- */ @@ -4347,9 +6189,41 @@ for(isymbol=0; (htmlsym=symbols[isymbol] } /* --- end-of-if(nargs>0) --- */ strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/ expptr = tokptr + strlen(abuff); /*resume search after macro / html*/ - } /* --- end-of-while(tokptr!=NULL) --- */ + } /* --- end-of-while(tokptr!=NULL) --- */ } /* --- end-of-for(isymbol) --- */ /* ------------------------------------------------------------------------- +convert \left( to \( and \right) to \), etc. +-------------------------------------------------------------------------- */ +if ( xlateleft ) /* \left...\right xlation wanted */ + 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*/ + { strsqueeze((tokptr+1),(lrlen-1));/*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*/ + { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*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 {a+b\atop c+d} to \atop{a+b}{c+d} -------------------------------------------------------------------------- */ for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++) @@ -4389,7 +6263,7 @@ for(isymbol=0; (atopsym=atopcommands[isy arg[rightlen] = '}'; /* add closing } */ 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 */ + {strsqueeze(arg,1);} /* so squeeze it out */ strcat(command,arg); /* concatanate right-arg} */ if (close!=NULL) strcat(command,close); /* add close delim if needed*/ strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */ @@ -4438,7 +6312,7 @@ int tolen = (to==NULL?0:strlen(to)), /* 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 */ + {strsqueeze(from,nshift);} /* memmove avoids overlap memory */ 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 */ @@ -4507,12 +6381,211 @@ return ( nreps ); /* #replacements bac /* ========================================================================== - * Function: strtexchr (char *string, char *texchr ) + * Function: strwstr (char *string, char *substr, char *white, int *sublen) + * Purpose: Find first substr in string, but wherever substr contains + * a whitespace char (in white), string may contain any number + * (including 0) of whitespace chars. If white contains I or i, + * then match is case-insensitive (and I,i _not_ whitespace). + * -------------------------------------------------------------------------- + * Arguments: string (I) char * to null-terminated string in which + * first occurrence of substr will be found + * substr (I) char * to null-terminated string containing + * "template" that will be searched for + * white (I) char * to null-terminated string containing + * whitespace chars. If NULL or empty, then + * "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used. + * If white contains I or i, then match is + * case-insensitive (and I,i _not_ considered + * whitespace). + * sublen (O) address of int returning "length" of substr + * found in string (which may be longer or + * shorter than substr itself). + * -------------------------------------------------------------------------- + * Returns: ( char * ) ptr to first char of substr in string + * or NULL if not found or for any error. + * -------------------------------------------------------------------------- + * Notes: o Wherever a single whitespace char appears in substr, + * the corresponding position in string may contain any + * number (including 0) of whitespace chars, e.g., + * string="abc def" and string="abcdef" both match + * substr="c d" at offset 2 of string. + * o If substr="c d" (two spaces between c and d), + * then string must have at least one space, so now "abcdef" + * doesn't match. In general, the minimum number of spaces + * in string is the number of spaces in substr minus 1 + * (so 1 space in substr permits 0 spaces in string). + * o Embedded spaces are counted in sublen, e.g., + * string="c d" (three spaces) matches substr="c d" + * with sublen=5 returned. But string="ab c d" will + * also match substr=" c d" returning sublen=5 and + * a ptr to the "c". That is, the mandatory preceding + * space is _not_ counted as part of the match. + * But all the embedded space is counted. + * (An inconsistent bug/feature is that mandatory + * terminating space is counted.) + * o Moreover, string="c d" matches substr=" c d", i.e., + * the very beginning of a string is assumed to be preceded + * by "virtual blanks". + * ======================================================================= */ +/* --- entry point --- */ +char *strwstr ( char *string, char *substr, char *white, int *sublen ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/ + *pfound = (char *)NULL; /*ptr to found substr back to caller*/ +char *pwhite=NULL, whitespace[256]; /* callers white whithout i,I */ +int iscase = (white==NULL?1: /* case-sensitive if i,I in white */ + strchr(white,'i')==NULL && strchr(white,'I')==NULL); +int foundlen = 0; /* length of substr found in string*/ +int nstrwhite=0, nsubwhite=0, /* #leading white chars in str,sub */ + nminwhite=0; /* #mandatory leading white in str */ +int nstrchars=0, nsubchars=0, /* #non-white chars to be matched */ + isncmp=0; /*strncmp() or strncasecmp() result*/ +/* ------------------------------------------------------------------------- +Initialization +-------------------------------------------------------------------------- */ +/* --- set up whitespace --- */ +strcpy(whitespace,WHITEMATH); /*default if no user input for white*/ +if ( white != NULL ) /*user provided ptr to white string*/ + if ( *white != '\000' ) { /*and it's not just an empty string*/ + strcpy(whitespace,white); /* so use caller's white spaces */ + while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */ + {strsqueeze(pwhite,1);} /* so squeeze it out */ + while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */ + {strsqueeze(pwhite,1);} /* so squeeze it out */ + if ( *whitespace == '\000' ) /* caller's white just had i,I */ + strcpy(whitespace,WHITEMATH); } /* so revert back to default */ +/* ------------------------------------------------------------------------- +Find first occurrence of substr in string +-------------------------------------------------------------------------- */ +if ( string != NULL ) /* caller passed us a string ptr */ + while ( *pstring != '\000' ) { /* break when string exhausted */ + char *pstrptr = pstring; /* (re)start at next char in string*/ + int leadingwhite = 0; /* leading whitespace */ + psubstr = substr; /* start at beginning of substr */ + foundlen = 0; /* reset length of found substr */ + if ( substr != NULL ) /* caller passed us a substr ptr */ + while ( *psubstr != '\000' ) { /*see if pstring begins with substr*/ + /* --- check for end-of-string before finding match --- */ + if ( *pstrptr == '\000' ) /* end-of-string without a match */ + goto nextstrchar; /* keep trying with next char */ + /* --- actual amount of whitespace in string and substr --- */ + nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */ + nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */ + nminwhite = max2(0,nsubwhite-1); /* #mandatory leading white in str */ + /* --- check for mandatory leading whitespace in string --- */ + if ( pstrptr != string ) /*not mandatory at start of string*/ + if ( nstrwhite < nminwhite ) /* too little leading white space */ + goto nextstrchar; /* keep trying with next char */ + /* ---hold on to #whitespace chars in string preceding substr match--- */ + if ( pstrptr == pstring ) /* whitespace at start of substr */ + leadingwhite = nstrwhite; /* save it as leadingwhite */ + /* --- check for optional whitespace --- */ + if ( psubstr != substr ) /* always okay at start of substr */ + if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */ + goto nextstrchar; /* keep trying with next char */ + /* --- skip any leading whitespace in substr and string --- */ + psubstr += nsubwhite; /* push past leading sub whitespace*/ + pstrptr += nstrwhite; /* push past leading str whitespace*/ + /* --- now get non-whitespace chars that we have to match --- */ + nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */ + nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */ + if ( nstrchars < nsubchars ) /* too few chars for match */ + goto nextstrchar; /* keep trying with next char */ + /* --- see if next nsubchars are a match --- */ + isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/ + strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/ + if ( isncmp != 0 ) /* no match */ + goto nextstrchar; /* keep trying with next char */ + /* --- push past matched chars --- */ + psubstr += nsubchars; pstrptr += nsubchars; /*nsubchars were matched*/ + } /* --- end-of-while(*psubstr!='\000') --- */ + pfound = pstring + leadingwhite; /* found match starting at pstring */ + foundlen = (int)(pstrptr-pfound); /* consisting of this many chars */ + goto end_of_job; /* back to caller */ + /* ---failed to find substr, continue trying with next char in string--- */ + nextstrchar: /* continue outer loop */ + pstring++; /* bump to next char in string */ + } /* --- end-of-while(*pstring!='\000') --- */ +/* ------------------------------------------------------------------------- +Back to caller with ptr to first occurrence of substr in string +-------------------------------------------------------------------------- */ +end_of_job: + if ( msglevel>=999 && msgfp!=NULL) { /* debugging/diagnostic output */ + fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n", + string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); } + if ( sublen != NULL ) /*caller wants length of found substr*/ + *sublen = foundlen; /* give it to him along with ptr */ + return ( pfound ); /*ptr to first found substr, or NULL*/ +} /* --- end-of-function strwstr() --- */ + + +/* ========================================================================== + * Function: strdetex ( s, mode ) + * Purpose: Removes/replaces any LaTeX math chars in s + * so that s can be displayed "verbatim", + * e.g., for error messages. + * -------------------------------------------------------------------------- + * Arguments: s (I) char * to null-terminated string + * whose math chars are to be removed/replaced + * mode (I) int containing 0 to _not_ use macros (i.e., + * mimeprep won't be called afterwards), + * or containing 1 to use macros that will + * be expanded by a subsequent call to mimeprep. + * -------------------------------------------------------------------------- + * Returns: ( char * ) ptr to "cleaned" copy of s + * or "" (empty string) for any error. + * -------------------------------------------------------------------------- + * Notes: o The returned pointer addresses a static buffer, + * so don't call strdetex() again until you're finished + * with output from the preceding call. + * ======================================================================= */ +/* --- entry point --- */ +char *strdetex ( char *s, int mode ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +static char sbuff[4096]; /* copy of s with no math chars */ +int strreplace(); /* replace _ with -, etc */ +/* ------------------------------------------------------------------------- +Make a clean copy of s +-------------------------------------------------------------------------- */ +/* --- check input --- */ +*sbuff = '\000'; /* initialize in case of error */ +if ( isempty(s) ) goto end_of_job; /* no input */ +/* --- start with copy of s --- */ +strninit(sbuff,s,2048); /* leave room for replacements */ +/* --- make some replacements -- we *must* replace \ { } first --- */ +strreplace(sbuff,"\\","\\backslash~\\!\\!",0); /*change all \'s to text*/ +strreplace(sbuff,"{", "\\lbrace~\\!\\!",0); /*change all {'s to \lbrace*/ +strreplace(sbuff,"}", "\\rbrace~\\!\\!",0); /*change all }'s to \rbrace*/ +/* --- now our further replacements may contain \directives{args} --- */ +if( mode >= 1 ) strreplace(sbuff,"_","\\_",0); /* change all _'s to \_ */ +else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/ +if(0)strreplace(sbuff,"<","\\textlangle ",0); /* change all <'s to text */ +if(0)strreplace(sbuff,">","\\textrangle ",0); /* change all >'s to text */ +if(0)strreplace(sbuff,"$","\\textdollar ",0); /* change all $'s to text */ +strreplace(sbuff,"$","\\$",0); /* change all $'s to \$ */ +strreplace(sbuff,"&","\\&",0); /* change all &'s to \& */ +strreplace(sbuff,"%","\\%",0); /* change all %'s to \% */ +strreplace(sbuff,"#","\\#",0); /* change all #'s to \# */ +/*strreplace(sbuff,"~","\\~",0);*/ /* change all ~'s to \~ */ +strreplace(sbuff,"^","{\\fs{+2}\\^}",0); /* change all ^'s to \^ */ +end_of_job: + return ( sbuff ); /* back with clean copy of s */ +} /* --- end-of-function strdetex() --- */ + + +/* ========================================================================== + * Function: strtexchr ( char *string, char *texchr ) * Purpose: Find first texchr in string, but texchr must be followed * by non-alpha * -------------------------------------------------------------------------- * Arguments: string (I) char * to null-terminated string in which - * firstoccurrence of delim will be found + * first occurrence of delim will be found * texchr (I) char * to null-terminated string that * will be searched for * -------------------------------------------------------------------------- @@ -4527,14 +6600,14 @@ char *strtexchr ( char *string, char *te /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *strstr(), delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/ +char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/ char *pstring = string; /* start or continue up search here*/ int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */ /* ------------------------------------------------------------------------- locate texchr in string -------------------------------------------------------------------------- */ if ( string != (char *)NULL /* check that we got input string */ -&& texchrlen > 0 ) /* and a texchr to search for */ +&& texchrlen > 0 ) { /* and a texchr to search for */ while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */ != (char *)NULL ) /* found it */ if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */ @@ -4544,7 +6617,7 @@ if ( string != (char *)NULL /* check th || 0 ) /* other tests to be determined */ pstring = ptexchr + texchrlen; /* continue search after texchr */ else /* passed all tests */ - break; /*so return ptr to texchr to caller*/ + break; } /*so return ptr to texchr to caller*/ return ( ptexchr ); /* ptr to texchar back to caller */ } /* --- end-of-function strtexchr() --- */ @@ -4612,6 +6685,471 @@ end_of_job: brace = ptr; /* { before expressn, } after cmmnd*/ return ( brace ); /*back to caller with delim or NULL*/ } /* --- end-of-function findbraces() --- */ + + +/* ========================================================================== + * Function: strpspn ( char *s, char *reject, char *segment ) + * Purpose: finds the initial segment of s containing no chars + * in reject that are outside (), [] and {} parens, e.g., + * strpspn("abc(---)def+++","+-",segment) returns + * segment="abc(---)def" and a pointer to the first '+' in s + * because the -'s are enclosed in () parens. + * -------------------------------------------------------------------------- + * Arguments: s (I) (char *)pointer to null-terminated string + * whose initial segment is desired + * reject (I) (char *)pointer to null-terminated string + * containing the "reject chars" + * segment (O) (char *)pointer returning null-terminated + * string comprising the initial segment of s + * that contains non-rejected chars outside + * (),[],{} parens, i.e., all the chars up to + * but not including the returned pointer. + * (That's the entire string if no non-rejected + * chars are found.) + * -------------------------------------------------------------------------- + * Returns: ( char * ) pointer to first reject-char found in s + * outside parens, or a pointer to the + * terminating '\000' of s if there are + * no reject chars in s outside all () parens. + * -------------------------------------------------------------------------- + * Notes: o the return value is _not_ like strcspn()'s + * o improperly nested (...[...)...] are not detected, + * but are considered "balanced" after the ] + * o if reject not found, segment returns the entire string s + * o leading/trailing whitespace is trimmed from returned segment + * ======================================================================= */ +/* --- entry point --- */ +char *strpspn ( char *s, char *reject, char *segment ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *ps = s; /* current pointer into s */ +int depth = 0; /* () paren nesting level */ +int seglen=0, maxseg=2047; /* segment length, max allowed */ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- check arguments --- */ +if ( isempty(s) ) /* no input string supplied */ + goto end_of_job; /* no reject chars supplied */ +/* ------------------------------------------------------------------------- +find first char from s outside () parens (and outside ""'s) and in reject +-------------------------------------------------------------------------- */ +while ( *ps != '\000' ) { /* search till end of input string */ + if ( isthischar(*ps,"([{") ) depth++; /* push another paren */ + if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */ + if ( depth < 1 ) { /* we're outside all parens */ + if ( isempty(reject) ) break; /* no reject so break immediately */ + if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */ + if ( segment != NULL ) /* caller gave us segment */ + if ( seglen < maxseg ) /* don't overflow segment buffer */ + memcpy(segment+seglen,ps,1); /* so copy non-reject char */ + seglen += 1; ps += 1; /* bump to next char */ + } /* --- end-of-while(*ps!=0) --- */ +end_of_job: + if ( segment != NULL ) { /* caller gave us segment */ + if ( isempty(reject) ) { /* no reject char */ + segment[min2(seglen,maxseg)] = *ps; seglen++; } /*closing )]} to seg*/ + segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */ + trimwhite(segment); } /* trim leading/trailing whitespace*/ + return ( ps ); /* back to caller */ +} /* --- end-of-function strpspn() --- */ + + +/* ========================================================================== + * Function: isstrstr ( char *string, char *snippets, int iscase ) + * Purpose: determine whether any substring of 'string' + * matches any of the comma-separated list of 'snippets', + * ignoring case if iscase=0. + * -------------------------------------------------------------------------- + * Arguments: string (I) char * containing null-terminated + * string that will be searched for + * any one of the specified snippets + * snippets (I) char * containing null-terminated, + * comma-separated list of snippets + * to be searched for in string + * iscase (I) int containing 0 for case-insensitive + * comparisons, or 1 for case-sensitive + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 if any snippet is a substring of + * string, 0 if not + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int isstrstr ( char *string, char *snippets, int iscase ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int status = 0; /*1 if any snippet found in string*/ +char snip[256], *snipptr = snippets, /* munge through each snippet */ + delim = ',', *delimptr = NULL; /* separated by delim's */ +char stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- arg check --- */ +if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */ +if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */ +/* --- copy string and lowercase it if case-insensitive --- */ +strninit(stringcp,string,4064); /* local copy of string */ +if ( !iscase ) /* want case-insensitive compares */ + for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */ + if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/ +/* ------------------------------------------------------------------------- +extract each snippet and see if it's a substring of string +-------------------------------------------------------------------------- */ +while ( snipptr != NULL ) /* while we still have snippets */ + { + /* --- extract next snippet --- */ + if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */ + == NULL ) /*not found following last snippet*/ + { strninit(snip,snipptr,255); /* local copy of last snippet */ + snipptr = NULL; } /* signal end-of-string */ + else /* snippet ends just before delim */ + { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */ + memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */ + snip[sniplen] = '\000'; /* null-terminated snippet */ + snipptr = delimptr + 1; } /* next snippet starts after delim */ + /* --- lowercase snippet if case-insensitive --- */ + if ( !iscase ) /* want case-insensitive compares */ + for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */ + if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/ + /* --- check if snippet in string --- */ + if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */ + { status = 1; /* so reset return status */ + break; } /* no need to check any further */ + } /* --- end-of-while(*snipptr!=0) --- */ +end_of_job: return ( status ); /*1 if snippet found in list, else 0*/ +} /* --- end-of-function isstrstr() --- */ + + +/* ========================================================================== + * Function: isnumeric ( s ) + * Purpose: determine if s is an integer + * -------------------------------------------------------------------------- + * Arguments: s (I) (char *)pointer to null-terminated string + * that's checked for a leading + or - + * followed by digits + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 if s is numeric, 0 if it is not + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int isnumeric ( char *s ) +{ +/* ------------------------------------------------------------------------- +determine whether s is an integer +-------------------------------------------------------------------------- */ +int status = 0; /* return 0 if not numeric, 1 if is*/ +char *p = s; /* pointer into s */ +if ( isempty(s) ) goto end_of_job; /* missing arg or empty string */ +skipwhite(p); /*check for leading +or- after space*/ +if ( *p=='+' || *p=='-' ) p++; /* skip leading + or - */ +for ( ; *p != '\000'; p++ ) { /* check rest of s for digits */ + if ( isdigit(*p) ) continue; /* still got uninterrupted digits */ + if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */ + skipwhite(p); /* skip all subsequent whitespace */ + if ( *p == '\000' ) break; /* trailing whitespace okay */ + goto end_of_job; /* embedded whitespace non-numeric */ + } /* --- end-of-for(*p) --- */ +status = 1; /* numeric after checks succeeded */ +end_of_job: + return ( status ); /*back to caller with 1=string, 0=no*/ +} /* --- end-of-function isnumeric() --- */ + + +/* ========================================================================== + * Function: evalterm ( STORE *store, char *term ) + * Purpose: evaluates a term + * -------------------------------------------------------------------------- + * Arguments: store (I/O) STORE * containing environment + * in which term is to be evaluated + * term (I) char * containing null-terminated string + * with a term like "3" or "a" or "a+3" + * whose value is to be determined + * -------------------------------------------------------------------------- + * Returns: ( int ) value of term, + * or NOVALUE for any error + * -------------------------------------------------------------------------- + * Notes: o Also evaluates index?a:b:c:etc, returning a if index<=0, + * b if index=1, etc, and the last value if index is too large. + * Each a:b:c:etc can be another expression, including another + * (index?a:b:c:etc) which must be enclosed in parentheses. + * ======================================================================= */ +/* --- entry point --- */ +int evalterm ( STORE *store, char *term ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int termval = 0; /* term value returned to caller */ +char token[2048] = "\000", /* copy term */ + *delim = NULL; /* delim '(' or '?' in token */ +/*int evalwff(),*/ /* recurse to evaluate terms */ +/* evalfunc();*/ /* evaluate function(arg1,arg2,...)*/ +char *strpspn(); /* span delims */ +int getstore(); /* lookup variables */ +int isnumeric(); /* numeric=constant, else variable */ +static int evaltermdepth = 0; /* recursion depth */ +int novalue = (-89123456); /* dummy (for now) error signal */ +/* ------------------------------------------------------------------------- +Initialization +-------------------------------------------------------------------------- */ +if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/ +if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/ +skipwhite(term); /* skip any leading whitespace */ +/* ------------------------------------------------------------------------- +First look for conditional of the form term?term:term:... +-------------------------------------------------------------------------- */ +/* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */ +delim = strpspn(term,"?",token); /* chars preceding ? outside () */ +if ( *delim != '\000' ) { /* found conditional expression */ + int ncolons = 0; /* #colons we've found so far */ + if ( *token != '\000' ) /* evaluate "index" value on left */ + if ( (termval=evalterm(store,token)) /* evaluate left-hand term */ + == novalue ) goto end_of_job; /* return error if failed */ + while ( *delim != '\000' ) { /* still have chars in term */ + delim++; *token='\000'; /* initialize for next "value:" */ + if ( *delim == '\000' ) break; /* no more values */ + delim = strpspn(delim,":",token); /* chars preceding : outside () */ + if ( ncolons++ >= termval ) break; /* have corresponding term */ + } /* --- end-of-while(*delim!='\000')) --- */ + if ( *token != '\000' ) /* have x:x:value:x:x on right */ + termval=evalterm(store,token); /* so evaluate it */ + goto end_of_job; /* return result to caller */ + } /* --- end-of-if(*delim!='\000')) --- */ +/* ------------------------------------------------------------------------- +evaluate a+b recursively +-------------------------------------------------------------------------- */ +/* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */ +term = strpspn(term,"/+-*%",token); /* chars preceding /+-*% outside ()*/ +/* --- evaluate a+b, a-b, etc --- */ +if ( *term != '\000' ) { /* found arithmetic operation */ + int leftval=0, rightval=0; /* init leftval for unary +a or -a */ + if ( *token != '\000' ) /* or eval for binary a+b or a-b */ + if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */ + == novalue ) goto end_of_job; /* return error if failed */ + if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */ + == novalue ) goto end_of_job; /* return error if failed */ + switch ( *term ) { /* perform requested arithmetic */ + default: break; /* internal error */ + case '+': termval = leftval+rightval; break; /* addition */ + case '-': termval = leftval-rightval; break; /* subtraction */ + case '*': termval = leftval*rightval; break; /* multiplication */ + case '/': if ( rightval != 0 ) /* guard against divide by zero */ + termval = leftval/rightval; break; /* integer division */ + case '%': if ( rightval != 0 ) /* guard against divide by zero */ + termval = leftval%rightval; break; /*left modulo right */ + } /* --- end-of-switch(*relation) --- */ + goto end_of_job; /* return result to caller */ + } /* --- end-of-if(*term!='\000')) --- */ +/* ------------------------------------------------------------------------- +check for parenthesized expression or term of the form function(arg1,arg2,...) +-------------------------------------------------------------------------- */ +if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */ + /* --- strip trailing paren (if there hopefully is one) --- */ + int toklen = strlen(token); /* total #chars in token */ + if ( token[toklen-1] == ')' ) /* found matching ) at end of token*/ + token[--toklen] = '\000'; /* remove trailing ) */ + /* --- handle parenthesized subexpression --- */ + if ( *token == '(' ) { /* have parenthesized expression */ + strsqueeze(token,1); /* so squeeze out leading ( */ + /* --- evaluate edited term --- */ + trimwhite(token); /* trim leading/trailing whitespace*/ + termval = evalterm(store,token); } /* evaluate token recursively */ + /* --- handle function(arg1,arg2,...) --- */ + else { /* have function(arg1,arg2,...) */ + *delim = '\000'; /* separate function name and args */ + /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */ + goto end_of_job; } /* return result to caller */ +/* ------------------------------------------------------------------------- +evaluate constants directly, or recursively evaluate variables, etc +-------------------------------------------------------------------------- */ +if ( *token != '\000' ) { /* empty string */ + if ( isnumeric(token) ) /* have a constant */ + termval = atoi(token); /* convert ascii-to-int */ + else { /* variable or "stored proposition"*/ + termval = getstore(store,token); } /* look up token */ + } /* --- end-of-if(*token!=0) --- */ +/* ------------------------------------------------------------------------- +back to caller with truth value of proposition +-------------------------------------------------------------------------- */ +end_of_job: + /* --- back to caller --- */ + if ( evaltermdepth > 0 ) evaltermdepth--; /* pop recursion depth */ + return ( termval ); /* back to caller with value */ +} /* --- end-of-function evalterm() --- */ + + +/* ========================================================================== + * Function: getstore ( store, identifier ) + * Purpose: finds identifier in store and returns corresponding value + * -------------------------------------------------------------------------- + * Arguments: store (I) (STORE *)pointer to store containing + * the desired identifier + * identifier (I) (char *)pointer to null-terminated string + * containing the identifier whose value + * is to be returned + * -------------------------------------------------------------------------- + * Returns: ( int ) identifier's corresponding value, + * or 0 if identifier not found (or any error) + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int getstore ( STORE *store, char *identifier ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int value = 0; /* store[istore].value for identifier */ +int istore=0; /* store[] index containing identifier */ +char seek[512], hide[512]; /* identifier arg, identifier in store */ +/* --- first check args --- */ +if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */ +strninit(seek,identifier,500); /* local copy of caller's identifier */ +trimwhite(seek); /* remove leading/trailing whitespace */ +/* --- loop over store --- */ +for ( istore=0; istorevalues[istore] or NULL */ +} /* --- end-of-function getstore() --- */ + + +/* ========================================================================== + * Functions: int unescape_url ( char *url, int isescape ) + * char x2c ( char *what ) + * Purpose: unescape_url replaces 3-character sequences %xx in url + * with the single character represented by hex xx. + * x2c returns the single character represented by hex xx + * passed as a 2-character sequence in what. + * -------------------------------------------------------------------------- + * Arguments: url (I) char * containing null-terminated + * string with embedded %xx sequences + * to be converted. + * isescape (I) int containing 1 to _not_ unescape + * \% sequences (0 would be NCSA default) + * what (I) char * whose first 2 characters are + * interpreted as ascii representations + * of hex digits. + * -------------------------------------------------------------------------- + * Returns: ( int ) unescape_url always returns 0. + * ( char ) x2c returns the single char + * corresponding to hex xx passed in what. + * -------------------------------------------------------------------------- + * Notes: o These two functions were taken verbatim from util.c in + * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z + * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03 + * so unescape_url() can be safely applied to input which may or + * may not have been url-encoded. (Note: currently, all calls + * to unescape_url() pass iescape=0, so it's not used.) + * o Added +++'s to blank xlation on 24-Sep-06 + * o Added ^M,^F,etc to blank xlation 0n 01-Oct-06 + * ======================================================================= */ +/* --- entry point --- */ +int unescape_url(char *url, int isescape) { + int x=0,y=0,prevescape=0,gotescape=0; + int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */ + int strreplace(); /* replace + with blank, if needed */ + char x2c(); + static char *hex="0123456789ABCDEFabcdef"; + /* --- + * xlate ctrl chars to blanks + * -------------------------- */ + if ( 1 ) { /* xlate ctrl chars to blanks */ + char *ctrlchars = "\n\t\v\b\r\f\a\015"; + int seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/ + int urllen = strlen(url); /* total length of url string */ + /* --- first, entirely remove ctrlchars from beginning and end --- */ + if ( seglen > 0 ) { /*have ctrlchars at start of string*/ + strsqueeze(url,seglen); /* squeeze out initial ctrlchars */ + urllen -= seglen; } /* string is now shorter */ + while ( --urllen >= 0 ) /* now remove ctrlchars from end */ + if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */ + url[urllen] = '\000'; /* re-terminate string before it */ + else break; /* or we're done */ + urllen++; /* length of url string */ + /* --- now, replace interior ctrlchars with ~ blanks --- */ + while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/ + url[seglen] = '~'; /* replace ctrlchar with ~ */ + } /* --- end-of-if(1) --- */ + /* --- + * xlate +'s to blanks if requested or if deemed necessary + * ------------------------------------------------------- */ + if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/ + char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++", + "+=+","+-+", NULL }; + int isearch = 0, /* searchfor[] index */ + nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/ + /* --- locate occurrences of searchfor[] strings in url --- */ + for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) { + char *psearch = url; /* start search at beginning */ + nfound[isearch] = 0; /* init #occurrences count */ + while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) { + nfound[isearch] += 1; /* count another occurrence */ + psearch += strlen(searchfor[isearch]); } /*resume search after it*/ + } /* --- end-of-for(isearch) --- */ + /* --- apply some common-sense logic --- */ + if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */ + isplusblank = xlateplus = 0; /* so +++'s aren't blanks */ + if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */ + if ( isplusblank != 0 ) /* and haven't disabled xlation */ + isplusblank = xlateplus = 1; /* so +++'s are blanks */ + else /* we have _both_ "%20" and "%2b" */ + xlateplus = 0; } /* tough call */ + if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */ + || nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */ + if ( isplusblank != 0 ) /* and haven't disabled xlation */ + xlateplus = 1; /* so xlate +++'s to blanks */ + } /* --- end-of-if(isplusblank==-1) --- */ + if ( xlateplus > 0 ) { /* want +'s xlated to blanks */ + char *xlateto[] = { ""," "," "," + "," "," "," "," "," " }; + while ( xlateplus > 0 ) { /* still have +++'s to xlate */ + char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */ + plusses[xlateplus] = '\000'; /* null-terminate +++'s */ + strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */ + xlateplus--; /* next shorter +++ string */ + } /* --- end-of-while(xlateplus>0) --- */ + } /* --- end-of-if(xlateplus) --- */ + isplusblank = 0; /* don't iterate this xlation */ + /* --- + * xlate %nn to corresponding char + * ------------------------------- */ + for(;url[y];++x,++y) { + gotescape = prevescape; + prevescape = (url[x]=='\\'); + if((url[x] = url[y]) == '%') + if(!isescape || !gotescape) + if(isthischar(url[y+1],hex) + && isthischar(url[y+2],hex)) + { url[x] = x2c(&url[y+1]); + y+=2; } + } + url[x] = '\0'; + return 0; +} /* --- end-of-function unescape_url() --- */ +/* --- entry point --- */ +char x2c(char *what) { + char digit; + digit = (what[0] >= '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() --- */ #endif /* PART2 */ /* --- @@ -4641,11 +7179,12 @@ subraster *rasterize ( char *expression, /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *preamble(), pretext[256]; /* process preamble, if present */ -char chartoken[8192], *texsubexpr(), /*get subexpression from expression*/ +char *preamble(), pretext[512]; /* process preamble, if present */ +char chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/ *subexpr = chartoken; /* token may be parenthesized expr */ int isbrace(); /* check subexpr for braces */ mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/ +int ligdef, get_ligature(); /*get symtable[] index for ligature*/ int natoms=0; /* #atoms/tokens processed so far */ int type_raster(); /* display debugging output */ subraster *rasterize(), /* recurse */ @@ -4658,16 +7197,23 @@ subraster *get_charsubraster(), /* char *sp=NULL, *prevsp=NULL, /* raster for current, prev char */ *expraster = (subraster *)NULL; /* raster returned to caller */ int delete_subraster(); /* free everything before returning*/ +int family = fontinfo[fontnum].family; /* current font family */ +int isleftscript = 0, /* true if left-hand term scripted */ + wasscripted = 0, /* true if preceding token scripted*/ + wasdelimscript = 0; /* true if preceding delim scripted*/ /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/ +char *strdetex(); /* detex token for error message */ /* --- global values saved/restored at each recursive iteration --- */ -int wastext = istext, /* initial istext mode flag */ - wasstring = isstring, /* initial isstring mode flag */ +int wasstring = isstring, /* initial isstring mode flag */ wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/ + oldfontnum = fontnum, /* initial font family */ oldfontsize = fontsize, /* initial fontsize */ olddisplaysize = displaysize, /* initial \displaystyle size */ oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */ - oldsquashmargin = squashmargin, /* initial squashmargin */ - oldissquashdelta = issquashdelta, /* initial issquashdelta */ + oldsmashmargin = smashmargin, /* initial smashmargin */ + oldissmashdelta = issmashdelta, /* initial issmashdelta */ + oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */ + oldisscripted = isscripted, /* initial isscripted */ *oldworkingparam = workingparam; /* initial working parameter */ subraster *oldworkingbox = workingbox, /* initial working box */ *oldleftexpression = leftexpression; /*left half rasterized so far*/ @@ -4679,12 +7225,14 @@ initialization recurlevel++; /* wind up one more recursion level*/ leftexpression = NULL; /* no leading left half yet */ isreplaceleft = 0; /* reset replaceleft flag */ +if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */ /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/ shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */ -if ( msgfp!=NULL && msglevel >= 29 ) /*display expression for debugging*/ - { fprintf(msgfp, - "rasterize> recursion level=%d, size=%d,\n\texpression=\"%s\"\n", - recurlevel,size,(expression==NULL?"null":expression)); fflush(msgfp); } +rastlift = 0; /* reset global rastraise() lift */ +if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/ + fprintf(msgfp, + "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n", + recurlevel,size,(expression==NULL?"":expression)); fflush(msgfp); } if ( expression == NULL ) goto end_of_job; /* nothing given to do */ /* ------------------------------------------------------------------------- preocess optional $-terminated preamble preceding expression @@ -4700,16 +7248,31 @@ build up raster one character (or subexp -------------------------------------------------------------------------- */ while ( 1 ) { + /* --- kludge for \= cyrillic ligature --- */ + isligature = 0; /* no ligature found yet */ + family = fontinfo[fontnum].family; /* current font family */ + if ( family == CYR10 ) /* may have cyrillic \= ligature */ + if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/ + >= 0 ) /* got some ligature */ + if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */ + isligature = 1; /* signal \= ligature */ /* --- get next character/token or subexpression --- */ + subexprptr = expression; /* ptr within expression to subexpr*/ expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1); subexpr = chartoken; /* "local" copy of chartoken ptr */ leftsymdef = NULL; /* no character identified yet */ sp = NULL; /* no subraster yet */ size = fontsize; /* in case reset by \tiny, etc */ + /*isleftscript = isdelimscript;*/ /*was preceding term scripted delim*/ + wasscripted = isscripted; /* true if preceding token scripted*/ + wasdelimscript = isdelimscript; /* preceding \right delim scripted */ + if(1)isscripted = 0; /* no subscripted expression yet */ + isdelimscript = 0; /* reset \right delim scripted flag*/ /* --- 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); fflush(msgfp); } + if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */ + fprintf(msgfp, + "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n", + recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); } if ( expression == NULL /* no more tokens */ && *subexpr == '\000' ) break; /* and this token empty */ if ( *subexpr == '\000' ) break; /* enough if just this token empty */ @@ -4720,36 +7283,60 @@ while ( 1 ) 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*/ - int wastext = istext; /* error display in default mode */ - if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */ + /* --- first check for opening $ in \text{ if $n-m$ even} --- */ + if ( istextmode /* we're in \text mode */ + && *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */ + char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/ + int exprlen = 0; /* length of $expression$ */ + int textfontnum = fontnum; /* current text font number */ + /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/ + if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */ + exprlen = (int)(endptr-expression); /* #chars preceding closing $ */ + else { /* no closing $ found */ + exprlen = strlen(expression); /* just assume entire expression */ + endptr = expression + (exprlen-1); } /*and push expression to '\000'*/ + exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */ + if ( exprlen > 0 ) { /* have something between $$ */ + memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/ + mathexpr[exprlen] = '\000'; /* null-terminate it */ + fontnum = 0; /* set math mode */ + sp = rasterize(mathexpr,size); /* and rasterize $expression$ */ + fontnum = textfontnum; } /* set back to text mode */ + expression = endptr+1; /* push expression past closing $ */ + } /* --- end-of-if(istextmode&&*subexpr=='$') --- */ + else + /* --- otherwise, 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*/ + int oldfontnum = fontnum; /* error display in default mode */ + if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/ { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n", chartoken); fflush(msgfp); } - sp = (subraster *)NULL; /* init to signal failure */ - if ( warninglevel < 1 ) continue; /* warnings not wanted */ - istext = 0; /* reset from \mathbb, etc */ - 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 */ - sp = rasterize(literal,size-1); /* rasterize literal token */ - istext = wastext; /* reset text mode */ - if ( sp == (subraster *)NULL ) continue; } /*flush 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 ( !isstring ) /* rasterizing */ - { if ( (sp=get_charsubraster(symdef,size)) /* get subraster */ + sp = (subraster *)NULL; /* init to signal failure */ + if ( warninglevel < 1 ) continue; /* warnings not wanted */ + fontnum = 0; /* reset from \mathbb, etc */ + if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/ + { /* --- so display literal {\rm~[\backslash~chartoken?]} --- */ + strcpy(literal,"{\\rm~["); /* init error message token */ + strcat(literal,strdetex(chartoken,0)); /* detex the token */ + strcat(literal,"?]}"); } /* add closing ? and brace */ + sp = rasterize(literal,size-1); /* rasterize literal token */ + fontnum = oldfontnum; /* reset font family */ + if ( sp == (subraster *)NULL ) continue; }/*flush 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 ( !isstring ) /* rasterizing */ + { if ( isligature ) /* found a ligature */ + expression = subexprptr + strlen(symdef->symbol); /*push past it*/ + if ( (sp=get_charsubraster(symdef,size)) /* get subraster */ == NULL ) continue; } /* flush token if failed */ - else /* constructing ascii string */ + else /* constructing ascii string */ { char *symbol = symdef->symbol; /* symbol for ascii string */ int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/ if ( symlen < 1 ) continue; /* no symbol for ascii string */ @@ -4760,24 +7347,36 @@ while ( 1 ) sp->baseline = 1; /* default (should be unused) */ strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */ /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/ - } /* --- end-of-if/else ... if/else --- */ + } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */ /* --- handle any super/subscripts following symbol or subexpression --- */ sp = rastlimits(&expression,size,sp); + isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/ /* --- 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 */ - fflush(msgfp); } /* flush msgfp buffer */ + if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */ + fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n", + recurlevel,natoms+1,(sp==NULL?" = ":"...")); + if ( msglevel >= 9 ) fprintf(msgfp, + " isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n", + isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript); + if ( msglevel >= 99 ) + if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */ + fflush(msgfp); } /* flush msgfp buffer */ /* --- accumulate atom or parenthesized subexpression --- */ if ( natoms < 1 /* nothing previous to concat */ || expraster == NULL /* or previous was complete error */ || isreplaceleft ) /* or we're replacing previous */ - { expraster = subrastcpy(sp); /* so just copy static CHARASTER */ + { if ( 1 && expraster!=NULL ) /* probably replacing left */ + delete_subraster(expraster); /* so first free original left */ + expraster = subrastcpy(sp); /* copy static CHARASTER or left */ 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 */ + if ( sp != NULL ) { /* ...if we have a new term */ + int prevsmashmargin = smashmargin; /* save current smash margin */ + if ( isleftscript ) { /* don't smash against scripts */ + isdelimscript = 0; /* reset \right delim scripted flag*/ + if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */ + expraster = rastcat(expraster,sp,1); /* concat new term, free previous */ + smashmargin = prevsmashmargin; } /* restore current smash margin */ delete_subraster(prevsp); /* free prev (if not a CHARASTER) */ prevsp = sp; /* current becomes previous */ leftexpression = expraster; /* left half rasterized so far */ @@ -4796,25 +7395,33 @@ end_of_job: if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */ type_raster(expraster->image,msgfp); /* display completed raster */ fflush(msgfp); } /* flush msgfp buffer */ + /* --- set final raster buffer --- */ + if ( 1 && expraster != (subraster *)NULL ) /* have an expression */ + { int type = expraster->type; /* type of constructed image */ + if ( type != FRACRASTER ) /* leave \frac alone */ + expraster->type = IMAGERASTER; /* set type to constructed image */ + if ( istextmode ) /* but in text mode */ + expraster->type = blanksignal; /* set type to avoid smash */ + expraster->size = fontsize; } /* set original input font size */ /* --- restore flags/values to original saved values --- */ - istext = wastext; /* text mode reset */ isstring = wasstring; /* string mode reset */ isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */ + fontnum = oldfontnum; /* font family reset */ fontsize = oldfontsize; /* fontsize reset */ displaysize = olddisplaysize; /* \displaystyle size reset */ shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */ - squashmargin = oldsquashmargin; /* squashmargin reset */ - issquashdelta = oldissquashdelta; /* issquashdelta reset */ + smashmargin = oldsmashmargin; /* smashmargin reset */ + issmashdelta = oldissmashdelta; /* issmashdelta reset */ + isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */ + isscripted = oldisscripted; /* isscripted reset */ workingparam = oldworkingparam; /* working parameter reset */ workingbox = oldworkingbox; /* working box reset */ leftexpression = oldleftexpression; /* leftexpression reset */ leftsymdef = oldleftsymdef; /* leftsymdef reset */ unitlength = oldunitlength; /* unitlength reset */ + iunitlength = (int)(unitlength+0.5); /* iunitlength reset */ recurlevel--; /* unwind one recursion level */ /* --- return final subraster to caller --- */ - if ( 1 && expraster != (subraster *)NULL ) /* have an expression */ - { expraster->type = IMAGERASTER; /* set type to constructed image */ - expraster->size = fontsize; } /* set original input font size */ return ( expraster ); } /* --- end-of-function rasterize() --- */ @@ -4827,7 +7434,7 @@ end_of_job: * Arguments: subexpr (I) char ** to first char of null-terminated * string beginning with a LEFTBRACES * to be rasterized - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding leading left{ * (unused, but passed for consistency) @@ -4852,13 +7459,13 @@ int explen = strlen(expression); /* tota 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 */ +char left[32], right[32]; /* parens enclosing expresion */ +char noparens[MAXSUBXSZ+1]; /* 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 */ +int family = /*CMSYEX*/ 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*/ @@ -4871,7 +7478,7 @@ if ( isthischar(*expression,ESCAPE) ) /* /* --- 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{ */ +strsqueeze(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 */ @@ -4950,10 +7557,14 @@ 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 */ + *rasterize(), /* may need to construct dummy base*/ + *scriptsp = basesp, /* and this will become the result */ + *dummybase = basesp; /* for {}_i construct a dummy base */ int isdisplay = (-1); /* set 1 for displaystyle, else 0 */ -int oldsquashmargin = squashmargin; /* save original squashmargin */ +int oldsmashmargin = smashmargin; /* save original smashmargin */ int type_raster(); /* display debugging output */ +int delete_subraster(); /* free dummybase, if necessary */ +int rastsmashcheck(); /* check if okay to smash scripts */ /* --- to check for \limits or \nolimits preceding scripts --- */ char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/ int toklen=0; /* strlen(limtoken) */ @@ -4964,32 +7575,32 @@ determine whether or not to use displaym -------------------------------------------------------------------------- */ scriptlevel++; /* first, increment subscript level*/ *limtoken = '\000'; /* no token yet */ +isscripted = 0; /* signal term not (text) scripted */ if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n", scriptlevel,(exprptr==NULL?"null":exprptr)); fflush(msgfp); } if ( isstring ) goto end_of_job; /* no scripts for ascii string */ /* --- check for \limits or \nolimits --- */ skipwhite(exprptr); /* skip white space before \limits */ -if ( exprptr != NULL ) /* expression ptr supplied */ - if ( *exprptr != '\000' ) /* something in expression */ +if ( !isempty(exprptr) ) /* non-empty expression supplied */ 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 */ + != 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 */ + 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 ( isdisplaystyle ) /* we're in displaystyle math mode */ + if ( isdisplaystyle ) { /* we're in displaystyle math mode */ if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */ { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/ isdisplay = 1; } /* set flag if mode forced true */ @@ -5002,23 +7613,39 @@ else /* noexplicit directive */ isdisplay = 1; } /* set flag if mode forced true */ else /* determine mode from base symbol */ if ( class == DISPOPER ) /* it's a displaystyle operator */ - isdisplay = 1; } /* so set flag */ + 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*/ +else { /* scripts alongside base symbol */ + if ( dummybase == NULL ) /* no base symbol preceding scripts*/ + dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/ + issmashokay = 1; /*haven't found a no-smash char yet*/ + if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/ scriptsp = basesp; /* so just return unscripted symbol*/ - else /* symbols followed by scripts */ + else { /* symbols followed by scripts */ + isscripted = 1; /*signal current term text-scripted*/ if ( basesp != NULL ) /* have base symbol */ - { squashmargin = 0; /* don't squash script */ - scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base sym*/ - scriptsp->type = IMAGERASTER; /* flip type of composite object */ - scriptsp->size = size; } /* and set font size */ + { /*if(0)smashmargin = 0;*/ /*don't smash script (doesn't work)*/ + /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/ + /* --- smash (or just concat) script raster against base symbol --- */ + if ( !issmashokay ) /* don't smash leading - */ + if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/ + scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/ + if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */ + /* --- smash (or just concat) scripted term to stuff to its left --- */ + issmashokay = 1; /* okay to smash base expression */ + if ( 0 && smashcheck > 1 ) /* smashcheck=2 to check base */ + /* note -- we _don't_ have base expression available to check */ + issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/ + if ( !issmashokay ) /* don't smash leading - */ + if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/ + scriptsp->size = size; } } } /* and set font size */ end_of_job: - squashmargin = oldsquashmargin; /* reset original squashmargin */ + smashmargin = oldsmashmargin; /* reset original smashmargin */ + if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/ if ( msgfp!=NULL && msglevel>=99 ) { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n", scriptlevel,(scriptsp==NULL?"null":"...")); @@ -5039,7 +7666,7 @@ end_of_job: * string beginning with a super/subscript, * and returning ptr immediately following * last script character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding leading script * (scripts will be placed relative to base) @@ -5077,6 +7704,7 @@ int szval = min2(max2(size,0),LARGESTSIZ /*int istweak = 1;*/ /* true to tweak script positioning */ int rastput(); /*put scripts in constructed raster*/ int delete_subraster(); /* free work areas */ +int rastsmashcheck(); /* check if okay to smash scripts */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ /* ------------------------------------------------------------------------- Obtain subscript and/or superscript expressions, and rasterize them/it @@ -5096,6 +7724,16 @@ issub = (subsp != (subraster *)NULL); / 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 */ +/* --- check for leading no-smash chars (if enabled) --- */ +issmashokay = 0; /* default, don't smash scripts */ +if ( smashcheck > 0 ) { /* smash checking wanted */ + issmashokay = 1; /*haven't found a no-smash char yet*/ + if ( issub ) /* got a subscript */ + issmashokay = rastsmashcheck(subscript); /* check if okay to smash */ + if ( issmashokay ) /* clean sub, so check sup */ + if ( issup ) /* got a superscript */ + issmashokay = rastsmashcheck(supscript); /* check if okay to smash */ + } /* --- end-of-if(smashcheck>0) --- */ /* ------------------------------------------------------------------------- get height, width, baseline of scripts, and height, baseline of base symbol -------------------------------------------------------------------------- */ @@ -5136,14 +7774,14 @@ if ( !issub ) /* we only have a super 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 ( !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 */ + baseline = 0; } } /* sub's baseline at top of raster */ /* ------------------------------------------------------------------------- construct raster with superscript over subscript -------------------------------------------------------------------------- */ @@ -5181,7 +7819,7 @@ end_of_job: * rasterized along with its super/subscripts, * and returning ptr immediately following last * character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * sp (I) subraster * to display math operator * to which super/subscripts will be added * -------------------------------------------------------------------------- @@ -5224,19 +7862,19 @@ if (!issub && !issup) goto end_of_job; / stack operator and its script(s) -------------------------------------------------------------------------- */ /* --- stack superscript atop operator --- */ -if ( issup ) /* we have a superscript */ +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 */ + == NULL ) goto end_of_job; } /* and quit if failed */ /* --- stack operator+superscript atop subscript --- */ -if ( issub ) /* we have a 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 */ + == NULL ) goto end_of_job; } /* and quit if failed */ sp->type = IMAGERASTER; /* flip type of composite object */ sp->size = size; /* and set font size */ /* ------------------------------------------------------------------------- @@ -5255,11 +7893,11 @@ end_of_job: * Arguments: expression (I) char ** to first char of null-terminated * string beginning with a \left * to be rasterized - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding leading left{ * (unused, but passed for consistency) - * ildelim (I) int containing ldelims index of + * ildelim (I) int containing ldelims[] index of * left delimiter * arg2 (I) int unused * arg3 (I) int unused @@ -5284,15 +7922,18 @@ int family=CMSYEX, /* get_delim() fami height=0, rheight=0, /* subexpr, right delim height */ margin=(size+1), /* delim height margin over subexpr*/ opmargin=(5); /* extra margin for \int,\sum,\etc */ -char subexpr[8192]; /* chars between \left...\right */ +char /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/ char *texchar(), /* get delims after \left,\right */ ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */ char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/ int isleftdot=0, isrightdot=0; /* true if \left. or \right. */ +int isleftscript=0, isrightscript=0; /* true if delims are scripted */ int sublen=0; /* strlen(subexpr) */ int idelim=0; /* 1=left,2=right */ +/* int gotldelim = 0; */ /* true if ildelim given by caller */ int delete_subraster(); /* free subraster if rastleft fails*/ int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */ +int istextleft=0, istextright=0; /* true for non-displaystyle delims*/ /* --- recognized delimiters --- */ static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */ static char *ldelims[] = { @@ -5329,6 +7970,15 @@ static char *xto[] = /* ...to this ins "<", /* \langle to < */ ">", /* \rangle to > */ NULL } ; /* --- end-of-xto[] --- */ +/* --- non-displaystyle delimiters --- */ +static char *textdelims[] = /* these delims _aren't_ display */ + { "|", "=", + "(", ")", + "[", "]", + "<", ">", + "{", "}", + "dbl", /* \lbrackdbl and \rbrackdbl */ + NULL } ; /* --- end-of-textdelims[] --- */ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ @@ -5336,10 +7986,15 @@ initialization if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */ /* --- determine left delimiter, and set default \right. delimiter --- */ if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */ - strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */ + { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */ + /* gotldelim = 1; */ } /* and set flag that we got it */ else /* trapped \left without delim */ { skipwhite(*expression); /* interpret \left ( as \left( */ - *expression = texchar(*expression,ldelim); } /*pull delim from expression*/ + if ( *(*expression) == '\000' ) /* end-of-string after \left */ + goto end_of_job; /* so return NULL */ + *expression = texchar(*expression,ldelim); /*pull delim from expression*/ + if ( *expression == NULL /* probably invalid end-of-string */ + || *ldelim == '\000' ) goto end_of_job; } /* no delimiter */ strcpy(rdelim,"."); /* init default \right. delim */ /* ------------------------------------------------------------------------- locate \right balancing our opening \left @@ -5364,7 +8019,10 @@ if ( (pright=strtexchr(*expression,right push past \left(_a^b sub/superscripts, if present -------------------------------------------------------------------------- */ pleft = *expression; /*reset pleft after opening \left( */ -/*lp=*/ rastlimits(expression,size,lp); /*dummy call push expression past b*/ +if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/ +!= NULL ) /* found actual _a^b scripts, too */ + { delete_subraster(lp); /* but we don't need them */ + lp = NULL; } /* reset pointer, too */ /* ------------------------------------------------------------------------- get \right delimiter and subexpression between \left...\right, xlate delims -------------------------------------------------------------------------- */ @@ -5384,14 +8042,17 @@ else { /* have explicit matching \ri /* --- get subexpression between \left...\right --- */ if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */ subexpr[sublen] = '\000'; /* and null-terminate it */ +/* --- adjust margin for expressions containing \middle's --- */ +if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */ + margin = 1; /* so don't "overwhelm" them */ /* --- check for operator delimiter --- */ for ( idelim=0; opdelims[idelim]!=NULL; idelim++ ) if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */ { margin += opmargin; /* extra height for operator */ if ( *ldelim == '\\' ) /* have leading escape */ - strcpy(ldelim,ldelim+1); /* squeeze it out */ + {strsqueeze(ldelim,1);} /* squeeze it out */ break; } /* no need to check rest of table */ -/* --- xlate delimiters --- */ +/* --- xlate delimiters and check for textstyle --- */ for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */ char *lrdelim = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */ int ix; char *xdelim; /* xfrom[] and xto[] index, delim */ @@ -5399,6 +8060,12 @@ for ( idelim=1; idelim<=2; idelim++ ) { if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */ { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/ break; } /* no need to check further */ + for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ ) + if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */ + { if ( idelim == 1 ) /* if it's the \left one */ + istextleft = 1; /* set left textstyle flag */ + else istextright = 1; /* else set right textstyle flag */ + break; } /* no need to check further */ } /* --- end-of-for(idelim) --- */ /* --- debugging --- */ if ( msgfp!=NULL && msglevel>=99 ) @@ -5419,7 +8086,7 @@ rasterize delimiters, reset baselines, a isleftdot = (strchr(ldelim,'.')!=NULL); /* true if \left. */ isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */ /* --- get rasters for best-fit delim characters, add sub/superscripts --- */ -isdisplaystyle = 9; /* force \displaystyle */ +isdisplaystyle = (istextleft?0:9); /* force \displaystyle */ if ( !isleftdot ) /* if not \left. */ { /* --- first get requested \left delimiter --- */ lp = get_delim(ldelim,rheight,family); /* get \left delim char */ @@ -5430,8 +8097,9 @@ if ( !isleftdot ) /* if not \left. */ if ( lheight > rheight ) /* got bigger delim than requested */ rheight = lheight-1; } /* make sure right delim matches */ /* --- then add on any sub/superscripts attached to \left( --- */ - lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/ -isdisplaystyle = 9; /* force \displaystyle */ + lp = rastlimits(&pleft,size,lp); /*\left(_a^b and push pleft past b*/ + isleftscript = isscripted; } /* check if left delim scripted */ +isdisplaystyle = (istextright?0:9); /* force \displaystyle */ if ( !isrightdot ) /* and if not \right. */ { /* --- first get requested \right delimiter --- */ rp = get_delim(rdelim,rheight,family); /* get \right delim char */ @@ -5439,7 +8107,8 @@ if ( !isrightdot ) /* and if not \righ if ( rp != NULL ) /* if get_delim() succeeded */ rp->baseline = sp->baseline + ((rp->image)->height - height)/2; /* --- then add on any sub/superscripts attached to \right) --- */ - rp = rastlimits(expression,size,rp); } /*\right)_c^d, expression past d*/ + rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/ + isrightscript = isscripted; } /* check if right delim scripted */ isdisplaystyle = wasdisplaystyle; /* original \displystyle default */ /* --- check that we got delimiters --- */ if ( 0 ) @@ -5461,11 +8130,168 @@ if ( sp != NULL ) /* succeeded or igno sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */ /* --- back to caller --- */ end_of_job: + isdelimscript = isrightscript; /* signal if right delim scripted */ return ( sp ); } /* --- end-of-function rastleft() --- */ /* ========================================================================== + * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 ) + * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right + * -------------------------------------------------------------------------- + * Arguments: expression (I) char ** to first char of null-terminated + * string beginning with a \right + * to be rasterized + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding leading left{ + * (unused, but passed for consistency) + * ildelim (I) int containing rdelims[] index of + * right delimiter + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) ptr to subraster corresponding to subexpr, + * or NULL for any parsing error + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastright ( char **expression, int size, subraster *basesp, + int ildelim, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/ + if ( sp != NULL ) /* returning entire expression */ + { + isreplaceleft = 1; /* set flag to replace left half*/ + } +return ( sp ); +} /* --- end-of-function rastright() --- */ + + +/* ========================================================================== + * Function: rastmiddle ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: \middle handler, returns subraster corresponding to + * entire expression with \middle delimiter(s) sized to fit. + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \middle to be + * rasterized, and returning ptr immediately + * to terminating null. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \middle + * (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 *rastmiddle ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/ +char *exprptr = *expression, /* local copy of ptr to expression */ + *texchar(), delim[32][132], /* delimiters following \middle's */ + *strtexchr(), /* locate \middle's */ + subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/ +int height=0, habove=0, hbelow=0; /* height, above & below baseline */ +int idelim, ndelims=0, /* \middle count (max 32) */ + family = CMSYEX; /* delims from CMSY10 or CMEX10 */ +subraster *subrastcpy(), /* copy subraster */ + *rastcat(), /* concatanate subraster */ + *get_delim(); /* get rasterized delimiter */ +int delete_subraster(); /* free work area subsp[]'s at eoj */ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +subsp[0] = leftexpression; /* expressn preceding 1st \middle */ +subsp[1] = NULL; /* set first null */ +/* ------------------------------------------------------------------------- +accumulate subrasters between consecutive \middle\delim...\middle\delim...'s +-------------------------------------------------------------------------- */ +while ( ndelims < 30 ) /* max of 31 \middle's */ + { + /* --- maintain max height above,below baseline --- */ + if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/ + { int baseline = (subsp[ndelims])->baseline; /* #rows above baseline */ + height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */ + habove = max2(habove,baseline); /* max #rows above baseline */ + hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */ + /* --- get delimter after \middle --- */ + skipwhite(exprptr); /*skip space betwn \middle & \delim*/ + exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */ + if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */ + break; /* ignore it and consider job done */ + ndelims++; /* count another \middle\delim */ + /* --- get subexpression between \delim and next \middle --- */ + subsp[ndelims] = NULL; /* no subexpresion yet */ + if ( *exprptr == '\000' ) /* end-of-expression after \delim */ + break; /* so we have all subexpressions */ + if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */ + == NULL ) /* no more \middle's */ + { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/ + subexpr[MAXSUBXSZ] = '\000'; /* make sure it's null-terminated */ + exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/ + else /* have another \middle */ + { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/ + memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */ + subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */ + exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/ + /* --- rasterize subexpression --- */ + subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */ + } /* --- end-of-while(1) --- */ +/* ------------------------------------------------------------------------- +construct \middle\delim's and concatanate them between subexpressions +-------------------------------------------------------------------------- */ +if ( ndelims < 1 /* no delims */ +|| (height=habove+hbelow) < 1 ) /* or no subexpressions? */ + goto end_of_job; /* just flush \middle directive */ +for ( idelim=0; idelim<=ndelims; idelim++ ) + { + /* --- first add on subexpression preceding delim --- */ + if ( subsp[idelim] != NULL ) { /* have subexpr preceding delim */ + if ( sp == NULL ) /* this is first piece */ + { sp = subsp[idelim]; /* so just use it */ + if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */ + else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */ + /* --- now construct delimiter --- */ + if ( *(delim[idelim]) != '\000' ) /* have delimter */ + { subraster *delimsp = get_delim(delim[idelim],height,family); + if ( delimsp != NULL ) /* rasterized delim */ + { delimsp->baseline = habove; /* set baseline */ + if ( sp == NULL ) /* this is first piece */ + sp = delimsp; /* so just use it */ + else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/ + } /* --- end-of-for(idelim) --- */ +/* --- back to caller --- */ +end_of_job: + if ( 0 ) /* now handled above */ + for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */ + if ( subsp[idelim] != NULL ) /* have allocated subraster */ + delete_subraster(subsp[idelim]); /* so free it */ + if ( sp != NULL ) /* returning entire expression */ + { int newht = (sp->image)->height; /* height of returned subraster */ + sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */ + isreplaceleft = 1; /* set flag to replace left half*/ + *expression += strlen(*expression); } /* and push to terminating null*/ + return ( sp ); +} /* --- end-of-function rastmiddle() --- */ + + +/* ========================================================================== * 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 @@ -5476,8 +8302,8 @@ end_of_job: * 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 + * immediately preceding "flags" directive + * (unused but passed for consistency) * flag (I) int containing #define'd symbol specifying * internal flag to be set * value (I) int containing new value of flag @@ -5499,7 +8325,7 @@ char *texsubexpr(), /* parse expressio int argvalue=NOVALUE, /* atoi(valuearg) */ isdelta=0, /* true if + or - precedes valuearg */ valuelen=0; /* strlen(valuearg) */ -double strtod(); /*convert ascii {valuearg} to double*/ +double dblvalue=(-99.), strtod(); /*convert ascii {valuearg} to double*/ static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */ /* ------------------------------------------------------------------------- set flag or value @@ -5507,10 +8333,10 @@ set flag or value switch ( flag ) { default: break; /* unrecognized flag */ - case ISTEXT: + case ISFONTFAM: if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */ (*expression)++; /* skip leading ~ after \rm */ - istext=value; /* set text mode */ + fontnum = value; /* set font family */ break; case ISSTRING: isstring=value; break; /* set string/image mode */ case ISDISPLAYSTYLE: /* set \displaystyle mode */ @@ -5524,6 +8350,8 @@ switch ( flag ) { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; } if ( value==2 || value==NOVALUE ) isblackonwhite = !isblackonwhite; + if ( gammacorrection > 0.0001 ) /* have gamma correction */ + gammacorrection = REVERSEGAMMA; /* use reverse video gamma instead */ break; case ISSUPER: /* set supersampling/lowpass flag */ #ifndef SSFONTS /* don't have ss fonts loaded */ @@ -5533,7 +8361,10 @@ switch ( flag ) fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */ break; case ISFONTSIZE: /* set fontsize */ + case ISMAGSTEP: /* set magstep */ case ISDISPLAYSIZE: /* set displaysize */ + case ISCONTENTTYPE: /*enable/disable content-type lines*/ + case ISCONTENTCACHED: /* write content-type to cache file*/ case ISSHRINK: /* set shrinkfactor */ case ISAAALGORITHM: /* set anti-aliasing algorithm */ case ISWEIGHT: /* set font weight */ @@ -5541,9 +8372,11 @@ switch ( flag ) case ISADJACENTWT: /* set lowpass adjacent weight */ case ISCORNERWT: /* set lowpass corner weight */ case ISCOLOR: /* set red(1),green(2),blue(3) */ - case ISSQUASH: /* set (minimum) "squash" margin */ + case ISSMASH: /* set (minimum) "smash" margin */ + case ISGAMMA: /* set gamma correction */ if ( value != NOVALUE ) /* passed a fixed value to be set */ - argvalue = value; /* set given fixed value */ + { argvalue = value; /* set given fixed int value */ + dblvalue = (double)value; } /* or maybe interpreted as double */ else /* get value from expression */ { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0); if ( *valuearg != '\000' ) /* guard against empty string */ @@ -5551,8 +8384,13 @@ switch ( flag ) if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/ { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */ if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/ - { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */ - argvalue = atoi(valuearg); } } /* convert to int */ + { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */ + switch ( flag ) { /* convert to double or int */ + default: argvalue = atoi(valuearg); break; /* convert to int */ + case ISGAMMA: + dblvalue = strtod(valuearg,NULL); break; } /* or to double */ + } /* --- end-of-if(*valuearg!='?') --- */ + } /* --- end-of-if(value==NOVALUE) --- */ switch ( flag ) { default: break; @@ -5578,10 +8416,14 @@ switch ( flag ) fontsize = (isdelta? fontsize+argvalue : argvalue); fontsize = max2(0,min2(fontsize,largestsize)); shrinkfactor = shrinkfactors[fontsize]; - if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/ + if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/ + || (1 && isdisplaystyle==2) /* displaystyle enabled and set */ + || (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/ if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/ - if ( !ispreambledollars ) /* respect $$...$$'s */ - isdisplaystyle = (fontsize>=displaysize? 2:1); /* forced */ + if ( !ispreambledollars ) { /* respect $$...$$'s */ + if ( fontsize >= displaysize ) + isdisplaystyle = 2; /* forced */ + else isdisplaystyle = 1; } /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */ else /* embed font size in expression */ { sprintf(valuearg,"%d",fontsize); /* convert size */ @@ -5590,16 +8432,31 @@ switch ( flag ) { *expression = (char *)(*expression-valuelen); /*back up buff*/ memcpy(*expression,valuearg,valuelen); } } /*and put in size*/ break; + case ISMAGSTEP: /* set magstep */ + if ( argvalue != NOVALUE ) { /* got a value */ + int largestmag = 10; + magstep = (isdelta? magstep+argvalue : argvalue); + magstep = max2(1,min2(magstep,largestmag)); } + break; case ISDISPLAYSIZE: /* set displaysize */ if ( argvalue != NOVALUE ) /* got a value */ displaysize = (isdelta? displaysize+argvalue : argvalue); break; - case ISSQUASH: /* set (minimum) "squash" margin */ + case ISCONTENTTYPE: /*enable/disable content-type lines*/ + if ( argvalue != NOVALUE ) /* got a value */ + isemitcontenttype = (argvalue>0?1:0); + break; + case ISCONTENTCACHED: /* write content-type to cache file*/ if ( argvalue != NOVALUE ) /* got a value */ - { squashmargin = argvalue; /* set value */ + iscachecontenttype = (argvalue>0?1:0); + break; + case ISSMASH: /* set (minimum) "smash" margin */ + if ( argvalue != NOVALUE ) /* got a value */ + { smashmargin = argvalue; /* set value */ if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */ - issquashdelta = (isdelta?1:0); } /* and set delta flag */ - squashmargin = max2((isdelta?-5:0),min2(squashmargin,32)); /*sanity*/ + issmashdelta = (isdelta?1:0); } /* and set delta flag */ + smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/ + isexplicitsmash = 1; /* signal explicit \smash directive*/ break; case ISSHRINK: /* set shrinkfactor */ if ( argvalue != NOVALUE ) /* got a value */ @@ -5607,9 +8464,11 @@ switch ( flag ) shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */ break; case ISAAALGORITHM: /* set anti-aliasing algorithm */ - if ( argvalue != NOVALUE ) /* got a value */ - aaalgorithm = argvalue; /* set algorithm number */ - aaalgorithm = max2(0,min2(aaalgorithm,3)); /* bounds check */ + if ( argvalue != NOVALUE ) { /* got a value */ + if ( argvalue >= 0 ) { /* non-negative to set algorithm */ + aaalgorithm = argvalue; /* set algorithm number */ + aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */ + else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */ break; case ISWEIGHT: /* set font weight number */ value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */ @@ -5638,6 +8497,10 @@ switch ( flag ) if ( argvalue != NOVALUE ) /* got a value */ cornerwt = argvalue; /* set lowpass corner weight */ break; + case ISGAMMA: /* set gamma correction */ + if ( dblvalue >= 0.0 ) /* got a value */ + gammacorrection = dblvalue; /* set gamma correction */ + break; } /* --- end-of-switch() --- */ break; case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/ @@ -5659,6 +8522,7 @@ switch ( flag ) { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0); if ( *valuearg != '\000' ) /* guard against empty string */ unitlength = strtod(valuearg,NULL); } /* convert to double */ + iunitlength = (int)(unitlength+0.5); /* iunitlength reset */ break; } /* --- end-of-switch(flag) --- */ return ( NULL ); /*just set value, nothing to display*/ @@ -5680,6 +8544,8 @@ return ( NULL ); /*just set value, not * width (I) int containing #bits/pixels for space width * isfill (I) int containing true to \hfill complete * expression out to width + * (Kludge: isfill=99 signals \hspace* + * for negative space) * isheight (I) int containing true (but not NOVALUE) * to treat width arg as height * -------------------------------------------------------------------------- @@ -5696,28 +8562,66 @@ subraster *rastspace ( char **expression Allocations and Declarations -------------------------------------------------------------------------- */ subraster *new_subraster(), *spacesp=NULL; /* subraster for space */ +raster *bp=NULL, *backspace_raster(); /* for negative space */ +int delete_subraster(); /* if fail, free unneeded subraster*/ int baseht=1, baseln=0; /* height,baseline of base symbol */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ +int isstar=0, minspace=0; /* defaults for negative hspace */ char *texsubexpr(), widtharg[256]; /* parse for optional {width} */ +int evalterm(), evalue=0; /* evaluate [args], {args} */ subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/ subraster *rastcat(); /* cat rightsp after \hfill */ -int blanksignal = (-991234); /*rastsquash signal right-hand blank*/ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ +if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */ if ( isfill == NOVALUE ) isfill=0; /* novalue means false */ if ( isheight == NOVALUE ) isheight=0; /* novalue means false */ +minspace = (isstar?(-1):0); /* reset default minspace */ /* ------------------------------------------------------------------------- determine width if not given (e.g., \hspace{width}, \hfill{width}) -------------------------------------------------------------------------- */ -if ( width <= 0 ) /* width specified in expression */ - { int widthval; /* test {width} before using it */ - width = 1; /* set default width */ - *expression = texsubexpr(*expression,widtharg,255,"{","}",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 */ +if ( width == 0 ) { /* width specified in expression */ + double dwidth; int widthval; /* test {width} before using it */ + int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */ + /* --- check if optional [minspace] given for negative \hspace --- */ + if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */ + /* ---parse [minspace], bump expression past it, evaluate expression--- */ + *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0); + if ( !isempty(widtharg) ) { /* got [minspace] */ + evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */ + minspace = iround(unitlength*((double)evalue)); } /* in pixels */ + } /* --- end-of-if(*(*expression)=='[') --- */ + width = 1; /* set default width */ + *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0); + dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */ + widthval = /* convert {width} to integer */ + (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) ); + if ( widthval>=minwidth && widthval<=600 ) /* sanity check */ + width = widthval; /* replace deafault width */ + } /* --- end-of-if(width==0) --- */ +/* ------------------------------------------------------------------------- +first check for negative space +-------------------------------------------------------------------------- */ +if ( width < 0 ) { /* have negative hspace */ + if ( leftexpression != (subraster *)NULL ) /* can't backspace */ + if ( (spacesp=new_subraster(0,0,0)) /* get new subraster for backspace */ + != NULL ) { /* and if we succeed... */ + int nback=(-width), pback; /*#pixels wanted,actually backspaced*/ + if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0)) + != NULL ) { /* and if backspace succeeds... */ + spacesp->image = bp; /* save backspaced image */ + /*spacesp->type = leftexpression->type;*/ /* copy original type */ + spacesp->type = blanksignal; /* need to propagate blanks */ + spacesp->size = leftexpression->size; /* copy original font size */ + spacesp->baseline = leftexpression->baseline; /* and baseline */ + blanksymspace += -(nback-pback); /* wanted more than we got */ + isreplaceleft = 1; } /*signal to replace entire expressn*/ + else { /* backspace failed */ + delete_subraster(spacesp); /* free unneeded envelope */ + spacesp = (subraster *)NULL; } } /* and signal failure */ + goto end_of_job; + } /* --- end-of-if(width<0) --- */ /* ------------------------------------------------------------------------- see if width is "absolute" or fill width -------------------------------------------------------------------------- */ @@ -5745,6 +8649,7 @@ if ( width > 0 ) /*make sure we have p != NULL ) /* and if we succeed... */ { /* --- ...re-init subraster parameters --- */ spacesp->size = size; /*propagate base font size forward*/ + if(1)spacesp->type = blanksignal; /* need to propagate blanks (???) */ spacesp->baseline = baseln; } /* ditto baseline */ /* ------------------------------------------------------------------------- concat right half if \hfill-ing @@ -5754,7 +8659,8 @@ if ( rightsp != NULL ) /* we have a ri rastcat(spacesp,rightsp,3)); /* or cat right half after space */ spacesp->type = blanksignal; /* need to propagate blanks */ *expression += strlen((*expression)); } /* push expression to its null */ -return ( spacesp ); +end_of_job: + return ( spacesp ); } /* --- end-of-function rastspace() --- */ @@ -5767,9 +8673,9 @@ return ( spacesp ); * string immediately following \\ to be * rasterized, and returning ptr immediately * to terminating null. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) - * immediately preceding \accent + * immediately preceding \\ * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused @@ -5791,7 +8697,7 @@ 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*/; /*for \\[vspace]*/ -double strtod(); /* convert ascii param to double */ +int evalterm(), evalue=0; /* evaluate [arg], {arg} */ int vspace = size+2; /* #pixels between lines */ /* ------------------------------------------------------------------------- obtain optional [vspace] argument immediately following \\ command @@ -5802,7 +8708,8 @@ if ( *(*expression) == '[' ) /*have [vs /* ---parse [vspace] and bump expression past it, interpret as double--- */ *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0); if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */ - vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */ + evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */ + vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */ } /* --- end-of-if(*(*expression)=='[') --- */ if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */ /* ------------------------------------------------------------------------- @@ -5812,7 +8719,8 @@ rasterize right half of expression and s 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*/ +/*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/ +newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/ /* --- back to caller --- */ end_of_job: if ( newlsp != NULL ) /* returning entire expression */ @@ -5867,7 +8775,7 @@ char *texscripts(), sub[1024],super[1024 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 evalterm(); /* evaluate [arg], {arg} */ 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 */ @@ -5881,7 +8789,7 @@ if ( *(*expression) == '[' ) /*check fo { int widthval; /* test [width] before using it */ *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0); widthval = /* convert [width] to integer */ - (int)((unitlength*strtod(widtharg,NULL))+0.5); + (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5); if ( widthval>=2 && widthval<=600 ) /* sanity check */ width = widthval; } /* replace deafault width */ /* --- now parse for limits, and bump expression past it(them) --- */ @@ -5961,7 +8869,7 @@ char *texsubexpr(), heightarg[256]; /* p 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 evalterm(); /* evaluate [arg], {arg} */ 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 */ @@ -5974,7 +8882,7 @@ if ( *(*expression) == '[' ) /*check fo { int heightval; /* test height before using it */ *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0); heightval = /* convert [height] to integer */ - (int)((unitlength*strtod(heightarg,NULL))+0.5); + (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5); if ( heightval>=2 && heightval<=600 ) /* sanity check */ height = heightval; } /* replace deafault height */ /* --- now parse for limits, and bump expression past it(them) --- */ @@ -6028,7 +8936,7 @@ end_of_job: * 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 + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding overlay \cmd * (unused, but passed for consistency) @@ -6057,7 +8965,9 @@ char *texsubexpr(), /*parse expression subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/ *new_subraster(); /*explicitly alloc sp2 if necessary*/ subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/ +int isalign = 0; /* true to align baselines */ int line_raster(); /* draw diagonal for \Not */ +int evalterm(); /* evaluate [arg], {arg} */ /* ------------------------------------------------------------------------- Obtain base, and maybe overlay, and rasterize them -------------------------------------------------------------------------- */ @@ -6066,32 +8976,41 @@ if ( offset2 == NOVALUE ) /* only if no if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ { int offsetval; /* test before using it */ *expression = texsubexpr(*expression,expr2,511,"[","]",0,0); - offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */ + offsetval = /* convert [offset2] to int */ + (int)(((double)evalterm(mimestore,expr2))+0.5); 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,511,"{","}",0,0); -if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */ +if ( isempty(expr1) ) goto end_of_job; /* nothing to overlay, so quit */ +rastlift1 = rastlift = 0; /* reset all raisebox() lifts */ +if ( strstr(expr1,"\\raise") != NULL ) /* have a \raisebox */ + isalign = 2; /* so align baselines */ 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*/ +rastlift1 = rastlift; /* save lift for base expression */ /* --- get overlay expression, and rasterize it --- */ if ( overlay == NOVALUE ) /* get overlay from input stream */ { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0); - if ( *expr2 != '\000' ) /* have an overlay */ - sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */ -else /* specific overlay */ + if ( !isempty(expr2) ) { /* have an overlay */ + if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */ + isalign = 2; /* so align baselines */ + sp2 = rasterize(expr2,size); } } /* rasterize overlay expression */ +else /* use specific built-in overlay */ switch ( overlay ) { default: break; case 1: /* e.g., \not overlays slash */ sp2 = rasterize("/",size+1); /* rasterize overlay expression */ + isalign = 0; /* automatically handled correctly */ offset2 = max2(1,size-3); /* push / right a bit */ offset2 = 0; break; case 2: /* e.g., \Not draws diagonal */ sp2 = NULL; /* no overlay required */ + isalign = 0; /* automatically handled correctly */ if ( overlaysp != NULL ) /* check that we have raster */ { raster *rp = overlaysp->image; /* raster to be \Not-ed */ int width=rp->width, height=rp->height; /* raster dimensions */ @@ -6106,9 +9025,9 @@ else /* specific overlay */ case 3: /* e.g., \sout for strikeout */ sp2 = NULL; /* no overlay required */ if ( overlaysp != NULL ) /* check that we have raster */ - { raster *rp = overlaysp->image; /* raster to be \Not-ed */ + { raster *rp = overlaysp->image; /* raster to be \sout-ed */ int width=rp->width, height=rp->height; /* raster dimensions */ - int baseline = overlaysp->baseline; /* we'll ignore descenders */ + int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/ int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2))); if ( 1 ) /* strikeout within bounding box */ line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/ @@ -6118,7 +9037,7 @@ if ( sp2 == NULL ) goto end_of_job; /*re /* ------------------------------------------------------------------------- construct composite overlay -------------------------------------------------------------------------- */ -overlaysp = rastcompose(sp1,sp2,offset2,0,3); +overlaysp = rastcompose(sp1,sp2,offset2,isalign,3); end_of_job: return ( overlaysp ); } /* --- end-of-function rastoverlay() --- */ @@ -6133,7 +9052,7 @@ end_of_job: * string immediately following \frac to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \frac * (unused, but passed for consistency) @@ -6156,7 +9075,7 @@ subraster *rastfrac ( char **expression, Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), /*parse expression for numer,denom*/ - numer[8192], denom[8192]; /*numer,denom parsed from expression*/ + numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */ subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/ subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */ subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */ @@ -6166,7 +9085,7 @@ int baseht=0, baseln=0; /* height,basel /*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 vspace = (size>2?2:1); /*vertical space between components*/ int delete_subraster(); /*free work areas in case of error*/ int type_raster(); /* display debugging output */ /* ------------------------------------------------------------------------- @@ -6213,6 +9132,7 @@ width = (fracsp->image)->width; /*just /* --- initialize subraster parameters --- */ fracsp->size = size; /* propagate font size forward */ fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/ +fracsp->type = FRACRASTER; /* signal \frac image */ if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */ { baseht = (basesp->image)->height; /* height of base symbol */ baseln = basesp->baseline; /* and its baseline */ @@ -6220,8 +9140,9 @@ if ( basesp != (subraster *)NULL ) /* we /* ------------------------------------------------------------------------- draw horizontal line between numerator and denominator -------------------------------------------------------------------------- */ +fraccenterline = numheight+vspace; /* signal that we have a \frac */ if ( isfrac ) /*line for \frac, but not for \atop*/ - rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0); + rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0); /* ------------------------------------------------------------------------- return final result to caller -------------------------------------------------------------------------- */ @@ -6243,7 +9164,7 @@ end_of_job: * string immediately following \stackrel to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \stackrel * (unused, but passed for consistency) @@ -6264,8 +9185,8 @@ subraster *rastackrel ( char **expressio /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), /*parse expression for numer,denom*/ - upper[8192], lower[8192]; /*upper,lower parsed from expression*/ +char *texsubexpr(), /*parse expression for upper,lower*/ + upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */ 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 */ @@ -6315,7 +9236,7 @@ end_of_job: * string immediately following \mathfunc to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \mathfunc * (unused, but passed for consistency) @@ -6337,9 +9258,9 @@ subraster *rastmathfunc ( char **express Allocations and Declarations -------------------------------------------------------------------------- */ char *texscripts(), /* parse expression for _limits */ - func[4096], limits[8192]; /* func as {\rm func}, limits */ + func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/ char *texsubexpr(), /* parse expression for arg */ - funcarg[2048]; /* optional func arg */ + funcarg[MAXTOKNSZ+1]; /* optional func arg */ 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 */ @@ -6422,7 +9343,7 @@ end_of_job: * string immediately following \sqrt to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) @@ -6443,8 +9364,8 @@ subraster *rastsqrt ( char **expression, /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192], /* parse subexpr to be sqrt-ed */ - rootarg[8192]; /* optional \sqrt[rootarg]{...} */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/ + rootarg[MAXSUBXSZ+1]; /* 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]*/ @@ -6482,13 +9403,14 @@ subwidth = (subsp->image)->width; /* an pixsz = (subsp->image)->pixsz; /* pixsz remains constant */ /* --- determine height and width of sqrt to contain subexpr --- */ sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */ -surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */ +surdwidth = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */ sqrtwidth = subwidth + surdwidth + 1; /* total width */ /* ------------------------------------------------------------------------- construct sqrt (with room to move in subexpr) and embed subexpr in it -------------------------------------------------------------------------- */ /* --- construct sqrt --- */ -if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz)) +if ( (sqrtsp=accent_subraster(SQRTACCENT, +(rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,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,overspace,sqrtwidth-subwidth,1); @@ -6532,7 +9454,7 @@ end_of_job: * string immediately following \accent to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \accent * (unused, but passed for consistency) @@ -6559,13 +9481,13 @@ subraster *rastaccent ( char **expressio /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192]; /* parse subexpr to be accented */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/ char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */ - subscript[512], supscript[512]; /* scripts parsed from expression */ + subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */ 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 */ +int accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/ 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*/ @@ -6588,13 +9510,17 @@ subwidth = (subsp->image)->width; /* an 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 */ +accheight = 4; /* default for bars */ switch ( accent ) { default: break; /* default okay */ case DOTACCENT: case DDOTACCENT: - accheight = (size<3? 3:4); /* default for dots */ + accheight = (size<4? 3:4); /* default for dots */ break; - case HATACCENT: case VECACCENT: + case VECACCENT: + vspace = 1; /* set 1-pixel vertical space */ + accdir = isscript; /* +1=right,-1=left,0=lr; +10for==>*/ + isscript = 0; /* >>don't<< signal sub/supscript */ + case HATACCENT: accheight = 7; /* default */ if ( subwidth < 10 ) accheight = 5; /* unless small width */ else if ( subwidth > 25 ) accheight = 9; /* or large */ @@ -6605,7 +9531,7 @@ accheight = min2(accheight,subheight); / construct accent, and construct subraster with accent over (or under) subexpr -------------------------------------------------------------------------- */ /* --- first construct accent --- */ -if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */ +if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz)) == 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*/ @@ -6640,7 +9566,7 @@ end_of_job: /* ========================================================================== - * Function: rastfont (expression,size,basesp,font,arg2,arg3) + * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3) * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding * to char(s) within {}'s rendered at size * -------------------------------------------------------------------------- @@ -6648,11 +9574,11 @@ end_of_job: * string immediately following \font to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 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{} + * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{} * arg2 (I) int unused * arg3 (I) int unused * -------------------------------------------------------------------------- @@ -6663,47 +9589,57 @@ end_of_job: * ======================================================================= */ /* --- entry point --- */ subraster *rastfont ( char **expression, int size, subraster *basesp, - int font, int arg2, int arg3 ) + int ifontnum, 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 *texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */ + subexpr[MAXSUBXSZ+1]; /* 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 */ - istext = 0; /* set true for text type */ +char *name = NULL; /* fontinfo[ifontnum].name */ +int family = 0, /* fontinfo[ifontnum].family */ + istext = 0, /* fontinfo[ifontnum].istext */ + class = 0; /* fontinfo[ifontnum].class */ subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */ *rastflags(); /* or just set flag to switch font */ -int oldsquashmargin = squashmargin; /* turn off squash in text mode */ -int blanksignal = (-991234); /*rastsquash signal right-hand blank*/ +int oldsmashmargin = smashmargin; /* turn off smash in text mode */ +#if 0 /* --- fonts recognized by rastfont --- */ -static int nfonts = 5; /* legal font #'s are 1...nfonts */ +static int nfonts = 11; /* 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} */ - { "\\bb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */ + { "\\math", 0 }, + { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */ + { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */ + { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */ + { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */ + { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */ + { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */ + { "\\mathrm", -1 }, /*(7) \mathrm */ + { "\\cyr", -1 }, /*(8) \cyr */ + { "\\textgreek",-1 }, /*(9) \textgreek */ + { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */ + { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */ { NULL, 0 } } ; /* --- end-of-fonts[] --- */ +#endif /* ------------------------------------------------------------------------- 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 */ -if ( font==3 || font==4 ) /* text (respect blanks) */ - { istext = 1; /* signal text mode */ - squashmargin = 0; } /* don't squash internal blanks */ +if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/ +name = fontinfo[ifontnum].name; /* font name */ +family = fontinfo[ifontnum].family; /* font family */ +istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/ +class = fontinfo[ifontnum].class; /* font class */ +if ( istext ) /* text (respect blanks) */ + { mathsmashmargin = smashmargin; /* needed for \text{if $n-m$ even} */ + smashmargin = 0; } /* don't smash internal blanks */ /* ------------------------------------------------------------------------- now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC -------------------------------------------------------------------------- */ -if ( class < 0 ) /* not character-by-character */ +if ( 1 || class<0 ) /* not character-by-character */ { /* --- if \font not immediately followed by { then it has no arg, so just set flag @@ -6711,15 +9647,8 @@ if ( class < 0 ) /* not character-by-c 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; - case 5: /* \bb, \mathbb sets flag istext=3 */ - fontsp = rastflags(expression,size,basesp,ISTEXT,3,arg3); break; - default: break; } /* unrecognized, set no flags */ + fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum); + fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3); goto end_of_job; } /* --- end-of-if(*(*expression)!='{') --- */ /* --- @@ -6752,11 +9681,12 @@ else /* character-by-character */ for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ ) { if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */ - { if ( 0 || istext ) /* and we're in a text mode */ + { if ( 0 || istext ) /* and we're in a text mode font */ 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 */ + /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */ 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; @@ -6790,9 +9720,10 @@ if ( (fontsp = rasterize(subexpr,size)) back to caller with chars rendered in font -------------------------------------------------------------------------- */ end_of_job: - squashmargin = oldsquashmargin; /* restore squash */ - if ( istext && fontsp!=NULL ) /* raster contains text */ - fontsp->type = blanksignal; /* signal nosquash */ + smashmargin = oldsmashmargin; /* restore smash */ + mathsmashmargin = SMASHMARGIN; /* this one probably not necessary */ + if ( istext && fontsp!=NULL ) /* raster contains text mode font */ + fontsp->type = blanksignal; /* signal nosmash */ return ( fontsp ); /* chars rendered in font */ } /* --- end-of-function rastfont() --- */ @@ -6807,7 +9738,7 @@ end_of_job: * string immediately following \begin to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \begin * (unused, but passed for consistency) @@ -6827,7 +9758,7 @@ subraster *rastbegin ( char **expression /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8210], /* \begin{} environment paramaters */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/ *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */ char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/ int strreplace(); /* replace substring in string */ @@ -6840,7 +9771,7 @@ int envlen=0, sublen=0; /* #chars in en static int blevel = 0; /* \begin...\end nesting level */ static char *mdelims[] = { NULL, NULL, NULL, NULL, "()","[]","{}","||","==", /* for pbBvVmatrix */ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char *environs[] = { /* types of environments we process*/ "eqnarray", /* 0 eqnarray environment */ @@ -6856,6 +9787,8 @@ static char *environs[] = { /* types of "align", /* 10 align environment */ "verbatim", /* 11 verbatim environment */ "picture", /* 12 picture environment */ + "cases", /* 13 cases environment */ + "equation", /* 14 for \begin{equation} */ NULL }; /* trailer */ /* ------------------------------------------------------------------------- determine type of environment we're beginning @@ -6866,7 +9799,7 @@ blevel++; /* count \begin...\begin... exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0); if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */ while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */ - strcpy(delims,delims+1); /* treat it as environment */ + {strsqueeze(delims,1);} /* treat it as environment */ /* --- look up environment in our table --- */ for ( ienviron=0; ;ienviron++ ) /* search table till NULL */ if ( environs[ienviron] == NULL ) /* found NULL before match */ @@ -6917,6 +9850,12 @@ switch ( ienviron ) if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */ strcat(subexpr,"{"); /* opening { after (width,height) */ break; + case 13: /* cases */ + strcat(subexpr,"\\array{ll$"); /* a&b \\ c&d etc */ + break; + case 14: /* \begin{equation} */ + strcat(subexpr,"{"); /* just enclose expression in {}'s */ + break; } /* --- end-of-switch(ienviron) --- */ /* ------------------------------------------------------------------------- locate matching \end{...} @@ -7030,7 +9969,7 @@ end_of_job: * string immediately following \array to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \array * (unused, but passed for consistency) @@ -7064,12 +10003,12 @@ subraster *rastarray ( char **expression /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8210], *exprptr, /* parse array subexpr */ - subtok[4096], *subptr=subtok, /* & or \\ inside { }'s not a delim*/ - token[4096], *tokptr=token, /* token from subexpr to rasterize */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/ + subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/ + token[MAXTOKNSZ+1], *tokptr=token, /* subexpr token to rasterize */ *preamble(), *preptr=token; /*process optional size,lcr preamble*/ char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */ -int maxarraysz = 64; /* max #rows, cols */ +int maxarraysz = 63; /* max #rows, cols */ int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -7094,6 +10033,9 @@ int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0 rowbaseln[65]={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,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, rowcenter[65]={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,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; @@ -7142,6 +10084,8 @@ char *texchar(), hltoken[1025]; /* extra int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/ int isnewrow=1; /* true for new row */ int pixsz = 1; /*default #bits per pixel, 1=bitmap*/ +int evalterm(), evalue=0; /* evaluate [arg], {arg} */ +static int mydaemonlevel = 0; /* check against global daemonlevel*/ /* ------------------------------------------------------------------------- Macros to determine extra raster space required for vline/hline -------------------------------------------------------------------------- */ @@ -7162,6 +10106,14 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */ goto end_of_job; /* nothing to do, so quit */ /* ------------------------------------------------------------------------- +reset static arrays if main re-entered as daemon (or dll) +-------------------------------------------------------------------------- */ +if ( mydaemonlevel != daemonlevel ) { /* main re-entered */ + for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */ + gjustify[icol] = gcolwidth[icol] = growheight[icol] = + gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0; + mydaemonlevel = daemonlevel; } /* update mydaemonlevel */ +/* ------------------------------------------------------------------------- process optional size,lcr preamble if present -------------------------------------------------------------------------- */ /* --- reset size, get lcr's, and push exprptr past preamble --- */ @@ -7213,9 +10165,9 @@ while ( *preptr != '\000' ) /* check p if ( prepcase != 0 ) /* only check upper,lowercase */ { int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */ - if ( ispropagate ) /* set row or col propagation */ + if ( ispropagate ) { /* set row or col propagation */ if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */ - else if ( prepcase == 2 ) rowpropagate = 1; /* propagating row values */ + else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/ if ( !colpropagate && prepcase == 1 ) { colwidth[icol] = 0; /* reset colwidth */ fixcolsize[icol] = 0; } /* reset width flag */ @@ -7267,6 +10219,7 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de tokenize and rasterize components a & b \\ c & d \\ etc of subexpr -------------------------------------------------------------------------- */ /* --- rasterize tokens one at a time, and maintain row,col counts --- */ +nrows = 0; /* start with top row */ ncols[nrows] = 0; /* no tokens/cols in top row yet */ while ( 1 ) /* scan chars till end */ { @@ -7312,25 +10265,40 @@ while ( 1 ) /* scan chars till end */ if ( iseoc ) /* we have a completed token */ { *tokptr = '\000'; /* first, null-terminate token */ - /* --- check first token in row for \hline or \hdash --- */ + /* --- check first token in row for [len] and/or \hline or \hdash --- */ ishonly = 0; /*init for token not only an \hline*/ if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/ { tokptr=token; skipwhite(tokptr); /* skip whitespace after // */ + /* --- first check for optional [len] --- */ + if ( *tokptr == '[' ) { /* have [len] if leading char is [ */ + /* ---parse [len] and bump tokptr past it, interpret as double--- */ + char lenexpr[128]; int len; /* chars between [...] as int */ + tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0); + if ( *lenexpr != '\000' ) { /* got [len] expression */ + evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */ + len = iround(unitlength*((double)evalue)); /* len in pixels */ + if ( len>=(-63) && len<=255 ) { /* sanity check */ + vrowspace[nrows] = len; /* extra vspace before this row */ + strsqueezep(token,tokptr); /* flush [len] from token */ + tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */ + } /* --- end-of-if(*tokptr=='[') --- */ + /* --- now check for \hline or \hdash --- */ tokptr = texchar(tokptr,hltoken); /* extract first char from token */ hltoklen = strlen(hltoken); /* length of first char */ - if ( hltoklen >= minhltoklen ) /*token must be at least \hl or \hd*/ + if ( hltoklen >= 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 */ + 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 */ + { istokwhite = 1; /* so token contains \hline only */ + if ( iseox ) ishonly = 1; } /* ignore entire row at eox */ else /* token contains more than \hline */ - strcpy(token,tokptr); } /* so flush \hline from token */ + {strsqueezep(token,tokptr);} } /* so flush \hline */ } /* --- end-of-if(ncols[nrows]==0) --- */ /* --- rasterize completed token --- */ toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */ @@ -7358,8 +10326,9 @@ while ( 1 ) /* scan chars till end */ } /* --- 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 */ + if ( ncols[nrows] < maxarraysz ) /* don't overflow arrays */ + { 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 */ @@ -7371,7 +10340,8 @@ while ( 1 ) /* scan chars till end */ { 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 */ + if ( nrows < maxarraysz ) /* don't overflow arrays */ + 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 */ @@ -7418,6 +10388,7 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows); for ( irow=0; irow<=nrows; irow++ ) /* and for each row */ { height += rowheight[irow]; /*height of this row (0 for nrows)*/ + height += vrowspace[irow]; /*plus extra //[len], if present*/ height += hlinespace(irow); /*plus space for hline, if present*/ if ( msglevel>=29 && msgfp!=NULL ) /* debugging */ fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); } @@ -7447,11 +10418,16 @@ for ( irow=0; irow<=nrows; irow++ ) /*to 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 += vrowspace[irow]; /* extra //[len] space above irow */ + if ( toprow < 0 ) toprow = 0; /* check for large negative [-len] */ toprow += hlinespace(irow); /* space for hline above irow */ leftcol = 0; /* start at leftmost column */ for ( icol=0; icolbaseline;/*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 */ + /*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 */ @@ -7518,7 +10494,7 @@ end_of_job: * string immediately following \picture to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \picture * (unused, but passed for consistency) @@ -7542,7 +10518,7 @@ 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 */ + pream[96], *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 */ @@ -7550,13 +10526,13 @@ subraster *rasterize(), *picelemsp=NUL 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*/ +double 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 evalterm(); /* evaluate [arg] and {arg}'s */ int iscenter=0; /* center or lowerleft put position*/ int *oldworkingparam = workingparam, /* save working param on entry */ origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */ @@ -7571,9 +10547,9 @@ if ( *putexpr == '\000' ) goto end_of_jo /* --- 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*/ +width=height = eround(putexpr); /*width pixels*/ if ( putptr != NULL ) /* 2nd arg, if present, is height */ - height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/ + height = eround(putptr+1); /*in pixels*/ /* ------------------------------------------------------------------------- Then obtain entire picture {...} subexpression following (width,height) -------------------------------------------------------------------------- */ @@ -7619,11 +10595,13 @@ while ( *picptr != '\000' ) /* until we *pream = '\000'; /* init preamble as empty string */ if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/ { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */ - strcpy(pream,putexpr); } /* copy leading preamble from put */ + strninit(pream,putexpr,92); } /* copy leading preamble from put */ else /* look for any non-digit preamble */ - { for ( preptr=pream,putptr=putexpr; ; putptr++ ) + { int npream = 0; /* #chars in preamble */ + for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ ) if ( *putptr == '\000' /* end-of-putdata signalled */ - || !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */ + || !isalpha((int)(*putptr)) /* or found non-alpha char */ + || npream > 92 ) break; /* or preamble too long */ else *preptr++ = *putptr; /* copy alpha char to preamble */ *preptr = '\000'; } /* null-terminate preamble */ /* --- interpret preamble --- */ @@ -7638,16 +10616,17 @@ while ( *picptr != '\000' ) /* until we 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 */ + if ( *pream != '\000' ) /* have preamble */ + {strsqueezep(putexpr,putptr);} /* squeeze it out */ /* --- 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*/ + x = (double)(eround(putexpr)); /* x coord in pixels*/ if ( putptr != NULL ) /* 2nd arg, if present, is y coord */ - y = unitlength*strtod(putptr+1,NULL); /* in pixels */ + y = (double)(eround(putptr+1)); /* in pixels */ /* --- interpret xinc,yinc,num if we have a multiput --- */ if ( multptr != NULL ) /* found ';' signalling multiput */ { @@ -7656,9 +10635,9 @@ while ( *picptr != '\000' ) /* until we 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 */ + xinc = (double)(eround(multptr+1)); /* xinc in pixels */ if ( putptr != NULL ) /* 2nd arg, if present, is yinc */ - yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */ + yinc = (double)(eround(putptr+1)); /* 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') --- */ @@ -7735,7 +10714,7 @@ end_of_job: * string immediately following \line to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \line * (unused, but passed for consistency) @@ -7761,10 +10740,12 @@ char *texsubexpr(),linexpr[257], *xptr=l subraster *new_subraster(), *linesp=NULL; /* subraster for line */ /*char *origexpression = *expression;*/ /*original expression after \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, */ +int thickness = 1; /* line thickness */ +double 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 width=0, height=0, /* #pixels width,height of line */ + rwidth=0, rheight=0; /*alloc width,height plus thickness*/ +int evalterm(); /* evaluate [arg] and {arg}'s */ 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 */ @@ -7774,13 +10755,16 @@ obtain (xinc,yinc) arguments immediately /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */ *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0); if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */ -/* --- now interpret xinc,yinc returned in linexpr --- */ +/* --- now interpret xinc,yinc;thickness returned in linexpr --- */ +if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */ + { *xptr = '\000'; /* terminate linexpr at ; */ + thickness = evalterm(mimestore,xptr+1); } /* get int thickness */ 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 */ + xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */ if ( xptr != NULL ) /* 2nd arg, if present, is yinc */ - yinc = ylen = strtod(xptr+1,NULL); /* in user units */ + yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */ /* ------------------------------------------------------------------------- obtain optional {xlen} following (xinc,yinc), and calculate ylen -------------------------------------------------------------------------- */ @@ -7790,7 +10774,7 @@ if ( *(*expression) == '{' ) /*have {xl /* --- parse {xlen} and bump expression past it, interpret as double --- */ *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0); if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */ - xlen = strtod(linexpr,NULL); /* xlen in user units */ + xlen = (double)evalterm(mimestore,linexpr); /* 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*/ @@ -7803,8 +10787,10 @@ calculate width,height, etc, based on xl 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 */ +width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/ +height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */ +rwidth = width + (ylen<0.001?0:max2(0,thickness-1)); +rheight = height + (xlen<0.001?0:max2(0,thickness-1)); /* --- 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)*/ @@ -7813,18 +10799,20 @@ if ( msgfp!=NULL && msglevel>=29 ) /* de 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 +allocate subraster and raster for line -------------------------------------------------------------------------- */ -/* --- sanity check on width,height args --- */ +/* --- sanity check on width,height,thickness args --- */ if ( width < 1 || width > 600 -|| height < 1 || height > 600 ) goto end_of_job; +|| height < 1 || height > 600 +|| thickness<1||thickness>25 ) goto end_of_job; /* --- allocate and initialize subraster for constructed line --- */ -if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */ +if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc 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->baseline = height/2 + 2 /* is a little above center good? */ + + (rheight-height)/2; /* account for line thickness too */ linesp->size = size; /* size (probably unneeded) */ /* ------------------------------------------------------------------------- draw the line @@ -7834,7 +10822,7 @@ line_raster ( linesp->image, /* embedde (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 */ + thickness ); /* line thickness (usually 1 pixel)*/ /* ------------------------------------------------------------------------- return constructed line to caller -------------------------------------------------------------------------- */ @@ -7846,6 +10834,102 @@ end_of_job: /* ========================================================================== + * Function: rastrule ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: \rule handler, returns subraster corresponding to rule + * parameters [lift]{width}{height} + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \rule to be + * rasterized, and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \rule + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) ptr to subraster corresponding to rule + * requested, or NULL for any parsing error + * -------------------------------------------------------------------------- + * Notes: o Summary of syntax... + * \rule[lift]{width}{height} + * o if [lift] not given, then bottom of rule on baseline + * o if width=0 then you get an invisible strut 1 (one) pixel wide + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastrule ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), rulexpr[257]; /* rule[lift]{wdth}{hgt} */ +subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */ +int pixsz = 1; /* pixels are one bit each */ +int lift=0, width=0, height=0; /* default rule parameters */ +double dval; /* convert ascii params to doubles */ +int rwidth=0, rheight=0; /* alloc width, height plus lift */ +int rule_raster(); /* draw rule in rulesp->image */ +int evalterm(); /* evaluate args */ +/* ------------------------------------------------------------------------- +Obtain lift,width,height +-------------------------------------------------------------------------- */ +/* --- check for optional lift arg --- */ +if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ + { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0); + dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */ + if ( dval <= 99 && dval >= (-99) ) /* sanity check */ + lift = iround(unitlength*dval); } /* scale by unitlength and round */ +/* --- parse for width --- */ +*expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0); +if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */ +dval = evalterm(mimestore,rulexpr); /* convert {width} to int */ +if ( dval <= 500 && dval >= 0 ) /* sanity check */ + width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/ +/* --- parse for height --- */ +*expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0); +if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */ +dval = evalterm(mimestore,rulexpr); /* convert {height} to int */ +if ( dval <= 500 && dval > 0 ) /* sanity check */ + height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/ +/* --- raster width,height in pixels --- */ +rwidth = max2(1,width); /* raster must be at least 1 pixel*/ +rheight = height + (lift>=0?lift: /* raster height plus lift */ + (-lift 600 +|| rheight < 1 || rheight > 600 ) goto end_of_job; +/* --- allocate and initialize subraster for constructed rule --- */ +if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */ +== NULL ) goto end_of_job; /* quit if failed */ +/* --- initialize line subraster parameters --- */ +rulesp->type = IMAGERASTER; /* image */ +rulesp->symdef = NULL; /* not applicable for image */ +rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/ +rulesp->size = size; /* size (probably unneeded) */ +/* ------------------------------------------------------------------------- +draw the rule +-------------------------------------------------------------------------- */ +rule_raster ( rulesp->image, /* embedded raster image */ + (-lift0? 0:4 ) ); /* rule type */ +/* ------------------------------------------------------------------------- +return constructed rule to caller +-------------------------------------------------------------------------- */ +end_of_job: + return ( rulesp ); /* return rule to caller */ +} /* --- end-of-function rastrule() --- */ + + +/* ========================================================================== * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \circle handler, returns subraster corresponding to ellipse * parameters (xdiam[,ydiam]) @@ -7854,7 +10938,7 @@ end_of_job: * string immediately following \circle to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-4 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \circle * (unused, but passed for consistency) @@ -7881,10 +10965,10 @@ char *qptr=NULL, quads[256]="1234"; /* d double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/ 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 */ +double 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 evalterm(); /* evaluate [arg],{arg} expressions*/ int origin = 55; /* force origin centered */ int circle_raster(), /* draw ellipse in circsp->image */ circle_recurse(); /* for theta0,theta1 args */ @@ -7892,16 +10976,16 @@ int circle_raster(), /* draw ellipse in obtain (xdiam[,ydiam]) arguments immediately following \circle command -------------------------------------------------------------------------- */ /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */ -*expression = texsubexpr(*expression,circexpr,511,"(",")",0,0); +*expression = texsubexpr(*expression,circexpr,500,"(",")",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 */ + strninit(quads,qptr+1,128); /* save user-requested quads */ if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */ { *qptr = '\000'; /* replace , with null */ - theta0 = strtod(quads,NULL); /* theta0 precedes , */ - theta1 = strtod(qptr+1,NULL); /* theta1 follows , */ + theta0 = (double)evalterm(mimestore,quads); /* theta0 precedes , */ + theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */ qptr = NULL; } /* signal thetas instead of quads */ else qptr = quads; } /* set qptr arg for circle_raster()*/ @@ -7909,9 +10993,10 @@ else /* no ;quads at all */ qptr = quads; /* default to all 4 quadrants */ 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 */ +xdiam = ydiam = /* xdiam=ydiam in user units */ + (double)evalterm(mimestore,circexpr); /* evaluate expression */ if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */ - ydiam = strtod(xptr+1,NULL); /* in user units */ + ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */ /* ------------------------------------------------------------------------- calculate width,height, etc -------------------------------------------------------------------------- */ @@ -7969,7 +11054,7 @@ end_of_job: * string immediately following \bezier to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \bezier * (unused, but passed for consistency) @@ -7994,13 +11079,13 @@ 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 evalterm(); /* evaluate [arg],{arg} expressions*/ 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 */ @@ -8019,8 +11104,10 @@ for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0 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 */ + /* --- row=y-coord in pixels --- */ + r = unitlength*((double)evalterm(mimestore,xptr+1)); } + /* --- col=x-coord in pixels --- */ + c = unitlength*((double)evalterm(mimestore,bezexpr)); /* --- store r,c --- */ switch ( iarg ) { case 0: r0=r; c0=c; break; @@ -8093,9 +11180,9 @@ end_of_job: * string immediately following \raisebox to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) - * immediately preceding \rotatebox + * immediately preceding \raisebox * (unused, but passed for consistency) * arg1 (I) int unused * arg2 (I) int unused @@ -8115,16 +11202,18 @@ subraster *rastraise ( char **expression /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192], *liftexpr=subexpr; /* args */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */ subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */ int lift=0; /* amount to raise/lower baseline */ +int evalterm(); /* evaluate [arg],{arg} expressions*/ /* ------------------------------------------------------------------------- obtain {lift} argument immediately following \raisebox command -------------------------------------------------------------------------- */ +rastlift = 0; /* reset global lift adjustment */ /* --- parse for {lift} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0); if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */ -lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/ +lift = eround(liftexpr); /* {lift} to integer */ if ( abs(lift) > 200 ) lift=0; /* sanity check */ /* ------------------------------------------------------------------------- obtain {subexpr} argument after {lift}, and rasterize it @@ -8139,6 +11228,7 @@ raise/lower baseline and return it to ca -------------------------------------------------------------------------- */ /* --- raise/lower baseline --- */ raisesp->baseline += lift; /* new baseline (no height checks) */ +rastlift = lift; /* set global to signal adjustment */ /* --- return raised subexpr to caller --- */ end_of_job: return ( raisesp ); /* return raised subexpr to caller */ @@ -8155,7 +11245,7 @@ end_of_job: * string immediately following \rotatebox to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \rotatebox * (unused, but passed for consistency) @@ -8177,22 +11267,22 @@ subraster *rastrotate ( char **expressio /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], *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 */ +double 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 */ +int evalterm(); /* evaluate [arg],{arg} expressions*/ /* ------------------------------------------------------------------------- obtain {degrees} argument immediately following \rotatebox command -------------------------------------------------------------------------- */ /* --- parse for {degrees} arg, and bump expression past it --- */ *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0); if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */ -degrees = strtod(degexpr,NULL); /* degrees to be rotated */ +degrees = (double)evalterm(mimestore,degexpr); /* 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 */ @@ -8245,12 +11335,12 @@ if ( rotrp != NULL ) /* rotated raster { 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 ( 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 */ + 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: @@ -8259,6 +11349,161 @@ end_of_job: /* ========================================================================== + * Function: rastmagnify ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: \magnify{magstep}{subexpression} handler, returns subraster + * containing magnified subexpression + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \reflectbox to + * be rasterized, and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \reflectbox + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) ptr to subraster corresponding to \magnify + * requested, or NULL for any parsing error + * -------------------------------------------------------------------------- + * Notes: o Summary of syntax... + * \magnify{magstep}{subexpression} + * o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastmagnify ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */ +subraster *rasterize(), *magsp=NULL; /* subraster for magnified subexpr */ +raster *rastmag(), *magrp=NULL; /* magnify subraster->image */ +int magstep = 1; /* default magnification */ +int delete_raster(); /* delete intermediate raster */ +int baseline=0; /* baseline of rasterized image */ +/* ------------------------------------------------------------------------- +obtain {magstep} argument immediately following \magnify command +-------------------------------------------------------------------------- */ +/* --- parse for {magstep} arg, and bump expression past it --- */ +*expression = texsubexpr(*expression,magexpr,255,"{","}",0,0); +magstep = atoi(magexpr); /* convert {magstep} to int */ +if ( magstep<1 || magstep>10 ) /* check magstep input */ + magstep = 1; /* back to default if illegal */ +/* ------------------------------------------------------------------------- +obtain {subexpr} argument after {magstep}, and rasterize it +-------------------------------------------------------------------------- */ +/* --- parse for {subexpr} arg, and bump expression past it --- */ +*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); +/* --- rasterize subexpression to be reflected --- */ +if ( (magsp = rasterize(subexpr,size)) /* rasterize subexpression */ +== NULL ) goto end_of_job; /* and quit if failed */ +/* --- return unmodified image if no magnification requested --- */ +if ( magstep<=1 ) goto end_of_job; /* don't bother magnifying image */ +/* --- extract params for image to be magnified --- */ +magrp = magsp->image; /* unmagnified rasterized image */ +baseline = magsp->baseline; /* and baseline of that image */ +/* ------------------------------------------------------------------------- +magnify image and adjust its parameters +-------------------------------------------------------------------------- */ +/* --- magnify image --- */ +magrp = rastmag(magsp->image,magstep); /* magnify raster image */ +if ( magrp == NULL ) goto end_of_job; /* failed to magnify image */ +delete_raster(magsp->image); /* free original raster image */ +magsp->image = magrp; /*and replace it with magnified one*/ +/* --- adjust parameters --- */ +baseline *= magstep; /* scale baseline */ +if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */ +magsp->baseline = baseline; /*reset baseline of magnified image*/ +/* --- return magnified subexpr to caller --- */ +end_of_job: + return ( magsp ); /*back to caller with magnified expr*/ +} /* --- end-of-function rastmagnify() --- */ + + +/* ========================================================================== + * Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster + * containing subexpression reflected horizontally (i.e., around + * vertical axis, |_ becomes _|) if [axis] not given or axis=1, + * or reflected vertically if axis=2 given. + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \reflectbox to + * be rasterized, and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \reflectbox + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) ptr to subraster corresponding to \reflectbox + * requested, or NULL for any parsing error + * -------------------------------------------------------------------------- + * Notes: o Summary of syntax... + * \reflectbox[axis]{subexpression} + * o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastreflect ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */ +subraster *rasterize(), *refsp=NULL; /* subraster for reflected subexpr */ +raster *rastref(), *refrp=NULL; /* reflect subraster->image */ +int axis = 1; /* default horizontal reflection */ +int delete_raster(); /* delete intermediate raster */ +int baseline=0; /* baseline of rasterized image */ +/* ------------------------------------------------------------------------- +obtain [axis] argument immediately following \reflectbox command, if given +-------------------------------------------------------------------------- */ +/* --- check for optional [axis] arg --- */ +if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/ + { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0); + axis = atoi(axisexpr); /* convert [axis] to int */ + if ( axis<1 || axis>2 ) /* check axis input */ + axis = 1; } /* back to default if illegal */ +/* ------------------------------------------------------------------------- +obtain {subexpr} argument after optional [axis], and rasterize it +-------------------------------------------------------------------------- */ +/* --- parse for {subexpr} arg, and bump expression past it --- */ +*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); +/* --- rasterize subexpression to be reflected --- */ +if ( (refsp = rasterize(subexpr,size)) /* rasterize subexpression */ +== NULL ) goto end_of_job; /* and quit if failed */ +/* --- return unmodified image if no reflection requested --- */ +if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */ +/* --- extract params for image to be reflected --- */ +refrp = refsp->image; /* unreflected rasterized image */ +baseline = refsp->baseline; /* and baseline of that image */ +/* ------------------------------------------------------------------------- +reflect image and adjust its parameters +-------------------------------------------------------------------------- */ +/* --- reflect image --- */ +refrp = rastref(refsp->image,axis); /* reflect raster image */ +if ( refrp == NULL ) goto end_of_job; /* failed to reflect image */ +delete_raster(refsp->image); /* free original raster image */ +refsp->image = refrp; /*and replace it with reflected one*/ +/* --- adjust parameters --- */ +if ( axis == 2 ) /* for vertical reflection */ + baseline = refrp->height - 1; /* set with nothing descending */ +refsp->baseline = baseline; /* reset baseline of reflected image*/ +/* --- return reflected subexpr to caller --- */ +end_of_job: + return ( refsp ); /*back to caller with reflected expr*/ +} /* --- end-of-function rastreflect() --- */ + + +/* ========================================================================== * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 ) * Purpose: \fbox{subexpression} handler, returns subraster * containing subexpression with frame box drawn around it @@ -8267,7 +11512,7 @@ end_of_job: * string immediately following \fbox to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \fbox * (unused, but passed for consistency) @@ -8289,29 +11534,51 @@ subraster *rastfbox ( char **expression, /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192], widtharg[512]; /* args */ +char *texsubexpr(), subexpr[MAXSUBXSZ+1], 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 evalterm(), evalue=0; /* interpret [width][height] */ +int fwidth=6, fthick=1, /*extra frame width, line thickness*/ + fsides=0; /* frame sides: 1=left,2=top,4=right,8=bot */ 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,511,"[","]",0,0); - if ( *widtharg != '\000' ) /* got widtharg */ - { width = max2(1,iround(unitlength*strtod(widtharg,NULL))); - height = 1; fwidth = 2; iscompose = 1; } +if ( *(*expression) == '[' ) { /* check for []-enclosed width arg */ + *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0); + if ( !isempty(widtharg) ) { /* got widtharg */ + char *comma = strchr(widtharg,','); /* look for [width,sides] */ + if ( comma == (char *)NULL ) /* no comma */ + comma = strchr(widtharg,';'); /* permit semicolon [width;sides] */ + if ( comma != (char *)NULL ) { /* optional [width,fsides] found */ + fsides = atoi(comma+1); /* interpret fsides after comma */ + if ( size < 5 ) /* for smaller fonts */ + { fwidth = 2; fthick = 1; } /* tighten frame, thinner accent */ + else { fwidth = 3; fthick = 2; } /* loosen frame, thicken accent */ + *comma = '\000'; /* null-terminate width at comma */ + trimwhite(widtharg); } /*remove leading/trailing whitespace*/ + if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */ + height = 1; /* default explicit height, too */ + if ( fsides == 0 ) { /* a normal framebox */ + evalue = eround(widtharg); /* interpret and scale width */ + width = max2(1,evalue); /* must be >0 */ + fwidth = 2; iscompose = 1; } + else /* absolute pixels for "accents" */ + width = evalterm(mimestore,widtharg); } + } /* --- end-of-if(!isempty(widtharg)) --- */ } /* --- end-of-if(**expression=='[') --- */ -if ( width > 0 ) /* found leading [width], so... */ +if ( width > 0 || fsides > 0) /* found leading [width], so... */ if ( *(*expression) == '[' ) /* check for []-enclosed height arg */ { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0); - if ( *widtharg != '\000' ) /* got widtharg */ - { height = max2(1,iround(unitlength*strtod(widtharg,NULL))); - fwidth = 0; } /* no extra border */ + if ( !isempty(widtharg) ) { /* got widtharg */ + if ( fsides == 0 ) { /* a normal framebox */ + evalue = eround(widtharg); /* interpret and scale height */ + height = max2(1,evalue); /* must be >0 */ + fwidth = 0; } /* no extra border */ + else /* absolute pixels for "accents" */ + height = evalterm(mimestore,widtharg); } } /* --- end-of-if(**expression=='[') --- */ /* ------------------------------------------------------------------------- obtain {subexpr} argument @@ -8324,7 +11591,7 @@ if ( width<0 || height<0 ) /* no explic == 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}", + sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}", width,height,subexpr); if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */ == NULL ) goto end_of_job; } /* and quit if failed */ @@ -8332,6 +11599,7 @@ else draw frame, reset params, and return it to caller -------------------------------------------------------------------------- */ /* --- draw border --- */ +if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */ 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 --- */ @@ -8355,7 +11623,7 @@ end_of_job: * string immediately following \input to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \input * (unused, but passed for consistency) @@ -8367,7 +11635,9 @@ end_of_job: * in filename, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... - * \input{filename} + * \input{filename} reads entire file named filename + * \input{filename:tag} reads filename, but returns only + * those characters between ... in that file. * o * ======================================================================= */ /* --- entry point --- */ @@ -8377,47 +11647,73 @@ subraster *rastinput ( char **expression /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), tag[512]="\000", filename[1024]="\000"; /* args */ +char *texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */ subraster *rasterize(), *inputsp=NULL; /* rasterized input image */ int status, rastreadfile(); /* read input file */ int format=0, npts=0; /* don't reformat (numerical) input */ -char subexpr[8192], /* concatanated lines from input file */ +int isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/ +/*int evalterm();*/ /* evaluate expressions */ +char *inputpath = INPUTPATH; /* permitted \input{} paths for any user */ +int isstrstr(); /* search for valid inputpath in filename */ +char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/ *mimeprep(), /* preprocess inputted data */ - *dtoa(), *reformat=NULL; /* reformat numerical input */ + *dbltoa(), *reformat=NULL; /* reformat numerical input */ /* ------------------------------------------------------------------------- obtain [tag]{filename} argument -------------------------------------------------------------------------- */ /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */ if ( *(*expression) == '[' ) /* check for []-enclosed value */ - { char argfld[2048]; /* optional argument field */ - *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0); - if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /* dtoa requested */ - { format = 1; /* signal dtoa() format */ + { char argfld[MAXTOKNSZ+1]; /* optional argument field */ + *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0); + if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/ + { format = 1; /* signal dtoa()/dbltoa() format */ if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */ npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */ - if ( format == 0 ) /* reformat not requested */ - strcpy(tag,argfld); } /* so interpret arg as tag */ + if ( format == 0 ) { /* reformat not requested */ + strninit(tag,argfld,1020); } } /* so interpret arg as tag */ /* --- parse for {filename} arg, and bump expression past it --- */ -*expression = texsubexpr(*expression,filename,1023,"{","}",0,0); +*expression = texsubexpr(*expression,filename,1020,"{","}",0,0); /* --- check for alternate filename:tag --- */ -if ( *filename != '\000' /* got filename */ -/*&& *tag == '\000'*/ ) /* but no [tag] */ +if ( !isempty(filename) /* got filename */ +/*&& isempty(tag)*/ ) /* but no [tag] */ { char *delim = strchr(filename,':'); /* look for : in filename:tag */ if ( delim != (char *)NULL ) /* found it */ { *delim = '\000'; /* null-terminate filename at : */ - strcpy(tag,delim+1); } } /* and stuff after : is tag */ + strninit(tag,delim+1,1020); } } /* and stuff after : is tag */ +/* --- check filename for an inputpath valid for all users --- */ +if ( !isinput /* if this user can't \input{} */ +&& !isempty(filename) /* and we got a filename */ +&& !isempty(inputpath) ) /* and an inputpath */ + if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */ + isinput = 1; /* okay to \input{} this filename */ +/* --- guard against recursive runaway (e.g., file \input's itself) --- */ +if ( ++ninputcmds > 8 ) /* max \input's per expression */ + isinput = 0; /* flip flag off after the max */ /* -------------------------------------------------------------------------- -Read file and rasterize constructed subexpression +Read file (and convert to numeric if [dtoa] option was given) +-------------------------------------------------------------------------- */ +if ( isinput ) { /* user permitted to use \input{} */ + status = rastreadfile(filename,0,tag,subexpr); /* read file */ + if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */ + /* --- rasterize input subexpression --- */ + mimeprep(subexpr); /* preprocess subexpression */ + if ( format == 1 ) { /* dtoa()/dbltoa() */ + double d = strtod(subexpr,NULL); /* interpret subexpr as double */ + if ( d != 0.0 ) /* conversion to double successful */ + if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */ + strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/ + } /* --- end-of-if(isinput) --- */ +/* -------------------------------------------------------------------------- +emit error message for unauthorized users trying to use \input{} +-------------------------------------------------------------------------- */ +else { /* inputseclevel > seclevel */ + sprintf(subexpr, + "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ", + (isempty(filename)?"???":filename)); + } /* --- end-of-if/else(isinput) --- */ +/* -------------------------------------------------------------------------- +Rasterize constructed subexpression -------------------------------------------------------------------------- */ -status = rastreadfile(filename,tag,subexpr); /* read file */ -if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */ -/* --- rasterize input subexpression --- */ -mimeprep(subexpr); /* preprocess subexpression */ -if ( format == 1 ) /* dtoa() */ - { double d = strtod(subexpr,NULL); /* interpret subexpr as double */ - if ( d != 0.0 ) /* conversion to double successful */ - if ( (reformat=dtoa(d,npts)) != NULL ) /* reformat successful */ - strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/ inputsp = rasterize(subexpr,size); /* rasterize subexpression */ /* --- return input image to caller --- */ end_of_job: @@ -8435,7 +11731,7 @@ end_of_job: * string immediately following \counter to be * rasterized, and returning ptr immediately * following last character processed. - * size (I) int containing 0-5 default font size + * size (I) int containing 0-7 default font size * basesp (I) subraster * to character (or subexpression) * immediately preceding \counter * (unused, but passed for consistency) @@ -8447,8 +11743,8 @@ end_of_job: * requested, or NULL for any parsing error * -------------------------------------------------------------------------- * Notes: o Summary of syntax... - * \counter[value][logfile]{filename} - * o + * \counter[value][logfile]{filename:tag} + * o :tag is optional * ======================================================================= */ /* --- entry point --- */ subraster *rastcounter ( char **expression, int size, subraster *basesp, @@ -8458,18 +11754,23 @@ subraster *rastcounter ( char **expressi Allocations and Declarations -------------------------------------------------------------------------- */ char *texsubexpr(), filename[1024]="\000", /* counter file */ - logfile[1024]="\000", tag[512]="\000"; /* log file and tag */ + logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/ subraster *rasterize(), *countersp=NULL; /* rasterized counter image */ FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */ -int rastreadfile(), rastwritefile(); /* to read and write counter file */ -char text[2048] = "1_", /* first (and only) line in counter file */ +int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/ + iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/ + isstrict = 1; /* true to only write to existing files */ +char text[MAXFILESZ] = "1_", /* only line in counter file without tags */ *delim = NULL, /* delimiter in text */ - utext[32] = "1_", /* default delimiter */ + utext[128] = "1_", /* default delimiter */ *udelim = utext+1; /* underscore delimiter */ -char *timestamp(), /* timestamp for logging */ - *dtoa(); /* double to comma-separated */ +char *rasteditfilename(), /* edit log file name */ + *timestamp(), /* timestamp for logging */ + *dbltoa(); /* double to comma-separated ascii */ int counter = 1, /* atoi(text) (after _ removed, if present) */ - gotcount = 0, /* set true once counter value determined */ + value = 1, /* optional [value] argument */ + gotvalue = 0, /* set true if [value] supplied */ + isdelta = 0, /* set true if [+value] or [-value] is delta*/ ordindex = (-1); /* ordinal[] index to append ordinal suffix */ /*--- ordinal suffixes based on units digit of counter ---*/ static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"}; @@ -8480,28 +11781,34 @@ first obtain optional [value][logfile] a -------------------------------------------------------------------------- */ /* --- first check for optional \counter[value] --- */ if ( *(*expression) == '[' ) /* check for []-enclosed value */ - { *expression = texsubexpr(*expression,text,2047,"[","]",0,0); - if ( *text != '\000' ) /* got counter value */ - if ( strlen(text) >= 1 ) /* and it's not an empty string */ - if ( isdigit((int)(*text)) ) /* leading 0-9 digit signals value */ - { counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */ - gotcount = 1; } /* signal we got counter value */ - else /* not a digit, so must be logfile */ - strcpy(logfile,text); /* so just copy it */ + { *expression = texsubexpr(*expression,text,1023,"[","]",0,0); + if ( *text != '\000' ) /* got counter value (or logfile) */ + if ( strlen(text) >= 1 ) { /* and it's not an empty string */ + if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */ + gotvalue = 1; /* signal we got optional value */ + else /* not +-digit, so must be logfile */ + strcpy(logfile,text); } /* so just copy it */ } /* --- end-of-if(**expression=='[') --- */ /* --- next check for optional \counter[][logfile] --- */ if ( *(*expression) == '[' ) /* check for []-enclosed logfile */ { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0); - if ( *text != '\000' ) /* got logfile value */ - if ( strlen(filename) >= 1 ) /* and it's not an empty string */ - if ( !(isdigit((int)(*filename))) /*leading non-digit signals logfile*/ - || gotcount ) /* or we already got counter value */ - strcpy(logfile,filename); /* so just copy it */ - else /* 0-9 digit, so must be value */ - { strcpy(text,filename); /* copy value to text line */ - counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */ - gotcount = 1; } /* signal we got counter value */ + if ( *filename != '\000' ) /* got logfile (or counter value) */ + if ( strlen(filename) >= 1 ) { /* and it's not an empty string */ + if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */ + || gotvalue ) /* or we already got counter value */ + strcpy(logfile,filename); /* so just copy it */ + else /* leading +-digit must be value */ + { strcpy(text,filename); /* copy value to text line */ + gotvalue = 1; } } /* and signal we got optional value*/ } /* --- end-of-if(**expression=='[') --- */ +/* --- evaluate [value] if present --- */ +if ( gotvalue ) { /*leading +-digit should be in text*/ + if ( *text == '+' ) isdelta = (+1); /* signal adding */ + if ( *text == '-' ) isdelta = (-1); /* signal subtracting */ + value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/ + if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */ + counter = value; /* re-init counter */ + } /* --- end-of-if(gotvalue) --- */ /* ------------------------------------------------------------------------- obtain counter {filename} argument -------------------------------------------------------------------------- */ @@ -8514,27 +11821,41 @@ if ( *filename != '\000' ) /* got filen { *delim = '\000'; /* null-terminate filename at : */ strcpy(tag,delim+1); } /* and stuff after : is tag */ /* -------------------------------------------------------------------------- +emit error message for unauthorized users trying to use \counter{} +-------------------------------------------------------------------------- */ +if ( !iscounter ) { /* counterseclevel > seclevel */ + sprintf(text, + "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ", + (isempty(filename)?"???":filename)); + goto rasterize_counter; /* rasterize error message */ + } /* --- end-of-if(!iscounter) --- */ +/* -------------------------------------------------------------------------- Read and parse file, increment and rewrite counter (with optional underscore) -------------------------------------------------------------------------- */ if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */ { /* --- read and interpret first (and only) line from counter file --- */ - if ( !gotcount ) /* if no [count] argument supplied */ - if ( rastreadfile(filename,tag,text) != 0 ) /* try reading it from file */ - { counter= 1 + (int)(strtod(text,&udelim)+0.1); /*counter val and delim*/ - gotcount = 1; } /* signal we got counter value */ + if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/ + if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/ + { char *vdelim = NULL; /* underscore delim from file */ + double fileval = strtod(text,&vdelim); /* value and delim from file */ + counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */ + counter += value; /* bump count by 1 or add/sub delta*/ + if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */ /* --- check for ordinal suffix --- */ if ( udelim != (char *)NULL ) /* have some delim after value */ if ( *udelim == '_' ) /* underscore signals ordinal */ - { ordindex = counter%10; /* least significant digit */ - if ( counter >= 10 ) /* counter is 10 or greater */ - if ( (counter/10)%10 == 1 ) /* and the last two are 10-19 */ + { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */ + ordindex = abscount%10; /* least significant digit */ + if ( abscount >= 10 ) /* counter is 10 or greater */ + if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */ ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */ /* --- rewrite counter file --- */ - sprintf(text,"%d",counter); /*build image of incremented counter*/ - if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */ - if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */ - rastwritefile(filename,tag,text,1); /* rewrite incremented counter */ + if ( status >= 0 ) /* file was read okay */ + { sprintf(text,"%d",counter); /*build image of incremented counter*/ + if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */ + if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */ + status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/ } /* --- end-of-if(strlen(filename)>1) --- */ /* -------------------------------------------------------------------------- log counter request @@ -8543,41 +11864,51 @@ if ( strlen(logfile) > 1 ) /* optional { char comment[1024] = "\000", /* embedded comment, logfile:comment*/ *commptr = strchr(logfile,':'); /* check for : signalling comment */ + int islogokay = 1; /* logfile must exist if isstrict */ if ( commptr != NULL ) /* have embedded comment */ { strcpy(comment,commptr+1); /* comment follows : */ *commptr = '\000'; } /* null-terminate actual logfile */ - if ( (logfp = fopen(logfile,"a")) /* open logfile */ - != (FILE *)NULL ) /* opened successfully */ - { - int ilog=0; /* logvars[] index */ - fprintf(logfp,"%s ",timestamp()); /* first emit timestamp */ - if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */ - else fprintf(logfp,"<%s>",tag); /* or tag if we have one */ - fprintf(logfp,"=%d",counter); /* emit counter value */ - for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */ - if ( ilog == commentvar /* replace with comment... */ - && commptr != NULL ) /* ...if available */ - fprintf(logfp," %.256s",comment); /* log embedded comment */ - else - { char *logval = getenv(logvars[ilog]); /* getenv(variable) to be logged*/ - fprintf(logfp," %.64s", /* log variable */ + strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */ + if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */ + else if ( isstrict ) { /*okay, but only write if it exists*/ + if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/ + islogokay = 0; /* so don't write log file */ + else fclose(logfp); } /* close file opened for test read */ + if ( islogokay ) /* okay to write logfile */ + if ( (logfp = fopen(logfile,"a")) /* open logfile */ + != (FILE *)NULL ) { /* opened successfully for append */ + int ilog=0; /* logvars[] index */ + fprintf(logfp,"%s ",timestamp(TZDELTA,0)); /* first emit timestamp */ + if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */ + else fprintf(logfp,"<%s>",tag); /* or tag if we have one */ + fprintf(logfp,"=%d",counter); /* emit counter value */ + if ( status < 1 ) /* read or re-write failed */ + fprintf(logfp,"(%s %d)","error status",status); /* emit error */ + for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */ + if ( ilog == commentvar /* replace with comment... */ + && commptr != NULL ) /* ...if available */ + fprintf(logfp," %.256s",comment); /* log embedded comment */ + else + { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/ + fprintf(logfp," %.64s", /* log variable */ (logval!=NULL?logval:"")); } /* emit value or */ - fprintf(logfp,"\n"); /* terminating newline */ - fclose(logfp); /* close logfile */ - } /* --- end-of-if(logfp!=NULL) --- */ + fprintf(logfp,"\n"); /* terminating newline */ + fclose(logfp); /* close logfile */ + } /* --- end-of-if(islogokay&&logfp!=NULL) --- */ } /* --- end-of-if(strlen(logfile)>1) --- */ /* -------------------------------------------------------------------------- construct counter expression and rasterize it -------------------------------------------------------------------------- */ /* --- construct expression --- */ /*sprintf(text,"%d",counter);*/ /* start with counter */ -strcpy(text,dtoa(((double)counter),0)); /* comma-separated counter value */ +strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */ if ( ordindex >= 0 ) /* need to tack on ordinal suffix */ { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */ strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */ strcat(text,"}}"); } /* finish with }} */ /* --- rasterize it --- */ -countersp = rasterize(text,size); /* rasterize counter subexpression */ +rasterize_counter: + countersp = rasterize(text,size); /* rasterize counter subexpression */ /* --- return counter image to caller --- */ /*end_of_job:*/ return ( countersp ); /* return counter image to caller */ @@ -8585,6 +11916,349 @@ countersp = rasterize(text,size); /* ras /* ========================================================================== + * Function: rasteval ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: handle \eval + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \eval, + * and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \eval + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) subraster ptr to date stamp + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rasteval ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */ +subraster *rasterize(), *evalsp=NULL; /* rasterize evaluated expression */ +int evalterm(), value=0; /* evaluate expression */ +/* ------------------------------------------------------------------------- +Parse for subexpr to be \eval-uated, and bump expression past it +-------------------------------------------------------------------------- */ +*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); +if ( *subexpr == '\000' ) /* couldn't get subexpression */ + goto end_of_job; /* nothing to do, so quit */ +/* ------------------------------------------------------------------------- +Evaluate expression, ascii-ize integer result, rasterize it +-------------------------------------------------------------------------- */ +/* --- evaluate expression --- */ +value = evalterm(mimestore,subexpr); /* evaluate expression */ +/* --- ascii-ize it --- */ +sprintf(subexpr,"%d",value); /* ascii version of value */ +/* --- rasterize ascii-ized expression value --- */ +evalsp = rasterize(subexpr,size); /* rasterize evaluated expression */ +/* --- return evaluated expression raster to caller --- */ +end_of_job: + return ( evalsp ); /* return evaluated expr to caller */ +} /* --- end-of-function rasteval() --- */ + + +/* ========================================================================== + * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: handle \today + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \today, + * and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \today + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) subraster ptr to date stamp + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rasttoday ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */ +char *timestamp(), *today=optarg; /* timestamp to be rasterized */ +subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */ +int ifmt=1, tzdelta=0; /* default timestamp() args */ +/* ------------------------------------------------------------------------- +Get optional args \today[+/-tzdelta,ifmt] +-------------------------------------------------------------------------- */ +/* --- check for optional \today[+/-tzdelta,ifmt] --- */ +if ( *(*expression) == '[' ) /* check for []-enclosed value */ + { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0); + if ( *optarg != '\000' ) /* got optional arg */ + { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */ + int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */ + if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */ + for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */ + { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */ + if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */ + tzdelta = atoi(arg); /* so interpret arg as tzdelta */ + else ifmt = atoi(arg); } /* else interpret args as ifmt */ + } /* --- end-of-if(*optarg!='\0') --- */ + } /* --- end-of-if(**expression=='[') --- */ +/* ------------------------------------------------------------------------- +Get timestamp and rasterize it +-------------------------------------------------------------------------- */ +strcpy(today,"\\text{"); /* rasterize timestamp as text */ +strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */ +strcat(today,"}"); /* terminate \text{} braces */ +todaysp = rasterize(today,size); /* rasterize timestamp */ +/* --- return timestamp raster to caller --- */ +/*end_of_job:*/ + return ( todaysp ); /* return timestamp to caller */ +} /* --- end-of-function rasttoday() --- */ + + +/* ========================================================================== + * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: handle \calendar + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \calendar + * and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \calendar + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) subraster ptr to rendered one-month calendar + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastcalendar ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), optarg[2050]; /* optional [year,month] args */ +char *calendar(), *calstr=NULL; /* calendar to be rasterized */ +subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */ +int year=0,month=0,day=0, argval=0; /* default calendar() args */ +/* ------------------------------------------------------------------------- +Get optional args \today[+/-tzdelta,ifmt] +-------------------------------------------------------------------------- */ +/* --- check for optional \calendar[year,month] --- */ +if ( *(*expression) == '[' ) /* check for []-enclosed value */ + { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0); + if ( *optarg != '\000' ) /* got optional arg */ + { char *comma = strchr(optarg,','), /* comma between year,month */ + *comma2 = NULL; /* second comma before day */ + int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */ + if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/ + if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */ + { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */ + for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */ + { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/ + argval = atoi(arg); /* interpret arg as integer */ + if ( iarg < 3 ) /* first two args are month,year */ + {if ( argval>1972 && argval<2100 ) year = argval; /* year value */ + else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/ + else /* only 3rd arg can be day */ + if ( argval>=1 && argval<=31 ) day = argval; } /* day value */ + } /* --- end-of-if(*optarg!='\0') --- */ + } /* --- end-of-if(**expression=='[') --- */ +/* ------------------------------------------------------------------------- +Get calendar string and rasterize it +-------------------------------------------------------------------------- */ +if ( msgfp!= NULL && msglevel>=9 ) + fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n", + year,month,day); +calstr = calendar(year,month,day); /* get calendar string */ +calendarsp = rasterize(calstr,size); /* rasterize calendar string */ +/* --- return calendar raster to caller --- */ +/*end_of_job:*/ + return ( calendarsp ); /* return calendar to caller */ +} /* --- end-of-function rastcalendar() --- */ + + +/* ========================================================================== + * Function: rastenviron ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: handle \environment + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \environment + * and returning ptr immediately + * following last character processed (in this + * case, \environment takes no arguments, so + * expression is returned unchanged). + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \environment + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) subraster ptr to rendered environment image + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastenviron ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), optarg[255]; /* optional [...] args (for future)*/ +char environstr[8192] = "\000", /* string for all environment vars */ + environvar[1024] = "\000"; /* one environment variable */ +char *strwrap(), /* wrap long lines */ + *strdetex(), /* removes/replaces any math chars */ + *environptr = NULL; /* ptr to preprocessed environvar */ +char *mimeprep(); /* preprocess environvar string */ +int unescape_url(); /* convert all %xx's to chars */ +int isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/ +int maxvarlen = 512, /* max chars in environment var */ + maxenvlen = 6400, /* max chars in entire string */ + wraplen = 48; /* strwrap() wrap lines at 48 chars*/ +int ienv = 0; /* environ[] index */ +subraster *rasterize(), *environsp=NULL; /* rasterize environment string */ +/* ------------------------------------------------------------------------- +Get args +-------------------------------------------------------------------------- */ +/* --- check for optional \environment args --- */ +if ( 1 ) /* there aren't any args (yet) */ + if ( *(*expression) == '[' ) { /* check for []-enclosed value */ + *expression = texsubexpr(*expression,optarg,250,"[","]",0,0); + if ( *optarg != '\000' ) { ; /* got optional arg, so process it */ + wraplen = atoi(optarg); /* interpret \environment[wraplen] */ + if ( wraplen < 1 ) wraplen = 8; /* set minimum */ + } /* --- end-of-if(*optarg!='\0') --- */ + } /* --- end-of-if(**expression=='[') --- */ +/* -------------------------------------------------------------------------- +emit error message for unauthorized users trying to use \environ +-------------------------------------------------------------------------- */ +if ( !isenviron ) { /* environseclevel > seclevel */ + sprintf(environstr, + "\\ \\text{[\\backslash environment\\ not permitted]}\\ "); + goto rasterize_environ; /* rasterize error message */ + } /* --- end-of-if(!isenviron) --- */ +/* ------------------------------------------------------------------------- +Accumulate environment variables and rasterize string containing them +-------------------------------------------------------------------------- */ +*environstr = '\000'; /* reset environment string */ +strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/ +for ( ienv=0; ; ienv++ ) { /* loop over environ[] strings */ + if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */ + if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */ + strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */ + if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */ + strcat(environvar,"..."); /* so add an ellipsis */ + unescape_url(environvar,0); /* convert all %xx's to chars */ + environptr = strdetex(environvar,1); /* remove/replace any math chars */ + strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/ + environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */ + strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */ + mimeprep(environvar); /* preprocess environvar string */ + if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break; + sprintf(environstr+strlen(environstr), /* display environment string */ + " %2d. %s\\\\\n", ienv+1,environvar); + if ( msgfp!= NULL && msglevel>=9 ) + fprintf(msgfp,"rastenviron> %2d. %.256s\n", + ienv+1,/*environ[ienv]*/environvar); + if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */ + } /* --- end-of-for(ienv) --- */ +strcat(environstr,"}}"); /* end {\text{...}} mode */ +rasterize_environ: + environsp = rasterize(environstr,size); /* rasterize environment string */ +/* --- return environment raster to caller --- */ +/*end_of_job:*/ + return ( environsp ); /* return environment to caller */ +} /* --- end-of-function rastenviron() --- */ + + +/* ========================================================================== + * Function: rastmessage ( expression, size, basesp, arg1, arg2, arg3 ) + * Purpose: handle \message + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) char ** to first char of null-terminated + * string immediately following \message + * and returning ptr immediately + * following last character processed. + * size (I) int containing 0-7 default font size + * basesp (I) subraster * to character (or subexpression) + * immediately preceding \mesasge + * (unused, but passed for consistency) + * arg1 (I) int unused + * arg2 (I) int unused + * arg3 (I) int unused + * -------------------------------------------------------------------------- + * Returns: ( subraster * ) subraster ptr to rendered message image + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +subraster *rastmessage ( char **expression, int size, subraster *basesp, + int arg1, int arg2, int arg3 ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *texsubexpr(), amsg[256]="\000"; /* message number text */ +int imsg = 0; /* default message number */ +char msg[4096]; +subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */ +int strreplace(); /*replace SERVER_NAME in refmsgnum*/ +int reflevels = REFLEVELS; /* #topmost levels to match */ +char *urlprune(); /*prune referer_match in refmsgnum*/ +char *strdetex(); /* remove math chars from messages */ +char *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */ + *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */ + *referer_match = (!isempty(http_host)?http_host: /*match http_host*/ + (!isempty(server_name)?server_name:(NULL))); /* or server_name */ +/* ------------------------------------------------------------------------- +obtain message {amsg} argument +-------------------------------------------------------------------------- */ +/* --- parse for {amsg} arg, and bump expression past it --- */ +*expression = texsubexpr(*expression,amsg,255,"{","}",0,0); +/* --- interpret argument --- */ +if ( *amsg != '\000' ) { /* got amsg arg */ + imsg = atoi(amsg); /* interpret as an int */ + if ( imsg < 0 /* if too small */ + || imsg > maxmsgnum ) /* or too big */ + imsg = 0; } /* default to first message */ +/* --- retrieve requested message --- */ +strninit(msg,msgtable[imsg],4095); /* local copy of message */ +/* --- process as necessary --- */ +if ( imsg == refmsgnum) { /* urlncmp() failed to validate */ + if ( reflevels > 0 ) /* have #levels to validate */ + strreplace(msg,"SERVER_NAME", /* replace SERVER_NAME */ + strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/ + } /* --- end-of-switch(imsg) --- */ +/* --- rasterize requested message --- */ +messagesp = rasterize(msg,size); /* rasterize message string */ +/* --- return message raster to caller --- */ +/*end_of_job:*/ + return ( messagesp ); /* return message to caller */ +} /* --- end-of-function rastmessage() --- */ + + +/* ========================================================================== * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 ) * Purpose: no op -- flush \escape without error * -------------------------------------------------------------------------- @@ -8594,7 +12268,7 @@ countersp = rasterize(text,size); /* ras * following last character processed. * size (I) int containing 0-5 default font size * basesp (I) subraster * to character (or subexpression) - * immediately preceding \fbox + * immediately preceding \escape * (unused, but passed for consistency) * nargs (I) int containing number of {}-args after * \escape to be flushed along with it @@ -8612,7 +12286,7 @@ subraster *rastnoop ( char **expression, /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -char *texsubexpr(), subexpr[8192]; /* flush dummy args eaten by \escape*/ +char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/ subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */ /* --- flush accompanying args if necessary --- */ if ( nargs != NOVALUE /* not unspecified */ @@ -8648,32 +12322,22 @@ FILE *rastopenfile ( char *filename, cha Allocations and Declarations -------------------------------------------------------------------------- */ FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/ -char texfile[2048] = "\000", /* local copy of input filename */ - amode[128] = "r"; /* test open mode if arg mode=NULL */ -int ismode = 0, /* true of mode!=NULL */ - isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */ +char texfile[2050] = "\000", /* local, edited copy of filename */ + *rasteditfilename(), /* prepend pathprefix if necessary */ + amode[512] = "r"; /* test open mode if arg mode=NULL */ +int ismode = 0; /* true of mode!=NULL */ /* -------------------------------------------------------------------------- Check mode and open file -------------------------------------------------------------------------- */ -/* --- check filename --- */ -if ( filename != (char *)NULL ) /* caller passed filename arg */ - if ( strlen(filename) >= 1 ) /* make sure we got actual filename*/ - { char *pfilename = filename; /* ptr to 1st char of filename */ - *texfile = '\000'; /* init filename as null string */ - while ( isthischar(*pfilename," /\\") ) /* absolute paths invalid */ - pfilename++; /* so flush leading / or \ (or ' ')*/ - if ( isprefix && *pfilename!='\000' ) /* paths preceded by prefix */ - { strcat(texfile,pathprefix); /* init filename with path */ - while ( memcmp(pfilename,"../",3)==0 /* have leading ../ */ - || memcmp(pfilename,"..\\",3)==0 ) /* or ..\ with prefix */ - pfilename += 3; } /* flush leading ../ or ..\ */ - strcat(texfile,pfilename); /* local copy of given filename */ - compress(texfile,' '); } /* remove embedded blanks */ +/* --- edit filename --- */ +strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/ +texfile[2047] = '\000'; /* make sure it's null terminated */ /* --- check mode --- */ if ( mode != (char *)NULL ) /* caller passed mode arg */ if ( *mode != '\000' ) /* and it's not an empty string */ { ismode = 1; /* so flip mode flag true */ - strcpy(amode,mode); /* and replace "r" with caller's */ + strncpy(amode,mode,254); /* and replace "r" with caller's */ + amode[254] = '\000'; /* make sure it's null terminated */ compress(amode,' '); } /* remove embedded blanks */ /* --- open filename or filename.tex --- */ if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/ @@ -8697,13 +12361,67 @@ if ( !ismode && fp!=NULL ) /* no mode, /* ========================================================================== - * Function: rastreadfile ( filename, tag, value ) + * Function: rasteditfilename ( filename ) + * Purpose: edits filename to remove security problems, + * e.g., removes all ../'s and ..\'s. + * -------------------------------------------------------------------------- + * Arguments: filename (I) char * to null-terminated string containing + * name of file to be edited + * -------------------------------------------------------------------------- + * Returns: ( char * ) pointer to edited filename, + * or empty string "\000" if any problem + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +char *rasteditfilename ( char *filename ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +static char editname[2050]; /*edited filename returned to caller*/ +char *strchange(); /* prepend pathprefix if necessary */ +int strreplace(), /* remove ../'s and ..\'s */ + isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */ +/* -------------------------------------------------------------------------- +edit filename +-------------------------------------------------------------------------- */ +/* --- first check filename arg --- */ +*editname = '\000'; /* init edited name as empty string*/ +if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */ +if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */ +/* --- init edited filename --- */ +strcpy(editname,filename); /* init edited name as input name */ +compress(editname,' '); /* remove embedded blanks */ +/* --- remove leading or embedded ....'s --- */ +while ( strreplace(editname,"....",NULL,0) > 0 ) ; /* squeeze out ....'s */ +/* --- remove leading / and \ and dots (and blanks) --- */ +if ( *editname != '\000' ) /* still have chars in filename */ + while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */ + {strsqueeze(editname,1);} /* so flush leading / or \ (or ' ')*/ +if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */ +/* --- remove leading or embedded ../'s and ..\'s --- */ +while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */ +while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */ +while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* and ../'s again */ +/* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */ +if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */ + strchange(0,editname,pathprefix); /* so prepend prefix */ +end_of_job: + return ( editname ); /* back with edited filename */ +} /* --- end-of-function rasteditfilename() --- */ + + +/* ========================================================================== + * Function: rastreadfile ( filename, islock, tag, value ) * Purpose: Read filename, returning value as string * between ... or entire file if tag=NULL passed. * -------------------------------------------------------------------------- * Arguments: filename (I) char * to null-terminated string containing * name of file to read (preceded by path * relative to mimetex executable) + * islock (I) int containing 1 to lock file while reading + * (hopefully done by opening in "r+" mode) * tag (I) char * to null-terminated string containing * html-like tagname. File contents between * and will be returned, or @@ -8716,18 +12434,19 @@ if ( !ismode && fp!=NULL ) /* no mode, * Notes: o * ======================================================================= */ /* --- entry point --- */ -int rastreadfile ( char *filename, char *tag, char *value ) +int rastreadfile ( char *filename, int islock, char *tag, char *value ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */ -char texfile[2048] = "\000", /* local copy of input filename */ - text[4096]; /* line from input file */ -char *tagp, tag1[512], tag2[512]; /* left and right */ -int vallen=0, maxvallen=8000; /* #chars in value, max allowed */ -int status = 0; /* status returned, 1=okay */ +char texfile[1024] = "\000", /* local copy of input filename */ + text[MAXLINESZ+1]; /* line from input file */ +char *tagp, tag1[1024], tag2[1024]; /* left and right */ +int vallen=0, maxvallen=MAXFILESZ; /* #chars in value, max allowed */ +int status = (-1); /* status returned, 1=okay */ int tagnum = 0; /* tag we're looking for */ +/*int islock = 1;*/ /* true to lock file */ /* -------------------------------------------------------------------------- Open file -------------------------------------------------------------------------- */ @@ -8736,12 +12455,15 @@ if ( value == (char *)NULL ) goto end_of *value = '\000'; /* init buffer with empty string */ /* --- open filename or filename.tex --- */ if ( filename != (char *)NULL ) /* make sure we got filename arg */ - { strcpy(texfile,filename); /* local copy of filename */ - fp = rastopenfile(texfile,"r"); } /* try opening it */ + { strncpy(texfile,filename,1023); /* local copy of filename */ + texfile[1023] = '\000'; /* make sure it's null terminated */ + fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */ /* --- check that file opened --- */ if ( fp == (FILE *)NULL ) /* failed to open file */ { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile); goto end_of_job; } /* return error message to caller */ +status = 0; /* file opened successfully */ +if ( islock ) rewind(fp); /* start at beginning of file */ /* -------------------------------------------------------------------------- construct 's -------------------------------------------------------------------------- */ @@ -8755,17 +12477,19 @@ if ( tag != (char *)NULL ) /* caller pa /* -------------------------------------------------------------------------- Read file, concatnate lines -------------------------------------------------------------------------- */ -while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */ +while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/ switch ( tagnum ) { /* look for left- or right-tag */ - case 0: break; /* no tag to look for */ + case 0: status = 1; break; /* no tag to look for */ case 1: /* looking for opening left */ if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/ - strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */ + tagp += strlen(tag1); /* first char past tag */ + strsqueezep(text,tagp); /*shift out preceding text and tag*/ tagnum = 2; /*now looking for closing right tag*/ case 2: /* looking for closing right */ if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/ *tagp = '\000'; /* terminate line at tag */ tagnum = 3; /* done after this line */ + status = 1; /* successfully read tag */ break; } /* ---end-of-switch(tagnum) --- */ if ( tagnum != 1 ) { /* no tag or left tag already found*/ @@ -8812,15 +12536,16 @@ int rastwritefile( char *filename, char Allocations and Declarations -------------------------------------------------------------------------- */ FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */ -char texfile[2048] = "\000", /* local copy of input filename */ - filebuff[16384] = "\000", /* entire contents of file */ - tag1[512], tag2[512], /* left and right */ +char texfile[1024] = "\000", /* local copy of input filename */ + filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */ + tag1[1024], tag2[1024], /* left and right */ *strchange(), /* put value between ...*/ *timestamp(); /* log modification time */ int istag=0, rastreadfile(), /* read file if tag!=NULL */ /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/ isnewfile = 0, /* true if writing new file */ status = 0; /* status returned, 1=okay */ +int istimestamp = 0; /* true to update tag */ /* -------------------------------------------------------------------------- check args -------------------------------------------------------------------------- */ @@ -8830,7 +12555,8 @@ if ( filename == (char *)NULL /* quit i if ( strlen(filename) < 2 /* quit if unreasonable filename */ || *value == '\000' ) goto end_of_job; /* or empty value string supplied */ /* --- establish filename[.tex] --- */ -strcpy(texfile,filename); /* local copy of input filename */ +strncpy(texfile,filename,1023); /* local copy of input filename */ +texfile[1023] = '\000'; /* make sure it's null terminated */ if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */ == (FILE *)NULL ) /* can't open, so write new file */ { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */ @@ -8850,8 +12576,8 @@ read existing file if just rewriting a s *filebuff = '\000'; /* init as empty file */ if ( !isnewfile ) /* if file already exists */ if ( istag ) /* and just rewriting one tag */ - if ( rastreadfile(texfile,NULL,filebuff) /* read entire existing file */ - == 0 ) goto end_of_job; /* signal error if failed to read */ + if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */ + <= 0 ) goto end_of_job; /* signal error if failed to read */ /* -------------------------------------------------------------------------- construct new file data if needed (entire file replaced by value if no tag) -------------------------------------------------------------------------- */ @@ -8866,7 +12592,7 @@ if ( istag ) /* only replacing tag in { /* --- preprocess filebuff --- */ if ( tagp2 != (char *)NULL ) /* apparently have ... */ - strcpy(filebuff,tagp2+tlen2); /* so get rid of leading ... */ + {strsqueezep(filebuff,tagp2+tlen2);} /* remove ... */ if ( (flen = strlen(filebuff)) /* #chars currently in buffer */ > 0 ) /* we have non-empty buffer */ if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/ @@ -8888,7 +12614,7 @@ if ( istag ) /* only replacing tag in >= 0 ) /* usually precedes */ strchange(flen,tagp1+tlen1,value); /* change ...'s to value */ else /* weirdly, precedes */ - { char fbuff[2048]; /* field buff for value */ + { char fbuff[4096]; /* field buff for value */ if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(...) */ <= 0 ) goto end_of_job; /* must be internal error */ strcpy(fbuff,tag1); /* set opening */ @@ -8908,13 +12634,15 @@ if ( fputs((istag?filebuff:value),fp) /* != EOF ) status = 1; /* signal success if succeeded */ fclose ( fp ); /* close output file after writing */ /* --- modify timestamp --- */ -if ( istag ) /* log mod time in tagged file */ - if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */ - { char fbuff[2048]; /* field buff value */ - strcpy(fbuff,tag); /* tag modified */ - strcat(fbuff," modified at "); /* spacer */ - strcat(fbuff,timestamp()); /* start with timestamp */ - rastwritefile(filename,"timestamp",fbuff,1); } +if ( status > 0 ) /*forget timestamp if write failed*/ + if ( istimestamp ) /* if we're updating timestamp */ + if ( istag ) /* only log time in tagged file */ + if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */ + { char fbuff[2048]; /* field buff value */ + strcpy(fbuff,tag); /* tag modified */ + strcat(fbuff," modified at "); /* spacer */ + strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */ + status = rastwritefile(filename,"timestamp",fbuff,1); } /* --- return status to caller --- */ end_of_job: return ( status ); /* return status to caller */ @@ -8922,11 +12650,117 @@ end_of_job: /* ========================================================================== - * Function: timestamp ( ) + * Function: calendar ( year, month, day ) + * Purpose: returns null-terminated character string containing + * \begin{array}...\end{array} for the one-month calendar + * specified by year=1973...2099 and month=1...12. + * If either arg out-of-range, today's value is used. + * -------------------------------------------------------------------------- + * Arguments: year (I) int containing 1973...2099 or 0 for current + * year + * month (I) int containing 1...12 or 0 for current month + * day (I) int containing day to emphasize or 0 + * -------------------------------------------------------------------------- + * Returns: ( char * ) char ptr to null-terminated buffer + * containing \begin{array}...\end{array} + * string that will render calendar for + * requested month, or NULL for any error. + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +char *calendar( int year, int month, int day ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +static char calbuff[4096]; /* calendar returned to caller */ +time_t time_val = (time_t)(0); /* binary value returned by time() */ +struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */ +int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */ +int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */ +char aval[64]; /* ascii day or 4-digit year */ +/* --- calendar data --- */ +static char *monthnames[] = { "?", "January", "February", "March", "April", + "May", "June", "July", "August", "September", "October", + "November", "December", "?" } ; +static int modays[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 }; +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- get current date/time --- */ +time((time_t *)(&time_val)); /* get date and time */ +tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */ +yy = 1900 + (int)(tmstruct->tm_year); /* current four-digit year */ +mm = 1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */ +dd = (int)(tmstruct->tm_mday); /* current day, 1-31 */ +/* --- check args --- */ +if ( year<1973 || year>2099 ) year = yy; /* current year if out-of-bounds */ +if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */ +if ( month==mm && year==yy && day==0 ) /* current month and default day */ + day = dd; /* emphasize current day */ +modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */ +/* --- initialize calendar string --- */ +strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */ +strcat(calbuff,"\\small\\text{"); /* month set in roman */ +strcat(calbuff,monthnames[month]); /* insert month name */ +strcat(calbuff," }"); /* add a space */ +sprintf(aval,"%d",year); /* convert year to ascii */ +strcat(calbuff,aval); /* add year */ +strcat(calbuff,"\\\\"); /* end top row */ +strcat(calbuff, /* now begin calendar arrayr */ + "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline" + "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &" + "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &" + "\\tiny\\text{Sat} \\\\ \\hline " ); +/* ------------------------------------------------------------------------- +generate calendar +-------------------------------------------------------------------------- */ +for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */ + { + /* --- get day-of-week for this day --- */ + iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */ + if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */ + /* --- may need empty cells at beginning of month --- */ + if ( idd == 1 ) /* first day of month */ + if ( iday > 0 ) /* need to skip cells */ + { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/ + aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/ + strcat(calbuff,aval); } /* add skip string to buffer */ + /* --- add idd to current cell --- */ + sprintf(aval,"%d",idd); /* convert idd to ascii */ + if ( idd == day /* emphasize today's date */ + /*&& month==mm && year==yy*/ ) /* only if this month's calendar */ + { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/ + strcat(calbuff,aval); /* put in idd */ + strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */ + else /* not today's date */ + strcat(calbuff,aval); /* so just put in idd */ + /* --- terminate cell --- */ + if ( idd < modays[month] ) { /* not yet end-of-month */ + if ( iday < 6 ) /* still have days left in week */ + strcat(calbuff,"&"); /* new cell in same week */ + else /* reached end-of-week */ + strcat(calbuff,"\\\\ \\hline"); } /* so start new week */ + } /* --- end-of-for(idd) --- */ +strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */ +/* --- return calendar to caller --- */ +strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */ +return ( calbuff ); /* back to caller with calendar */ +} /* --- end-of-function calendar() --- */ + + +/* ========================================================================== + * Function: timestamp ( tzdelta, ifmt ) * Purpose: returns null-terminated character string containing * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm} * -------------------------------------------------------------------------- - * Arguments: ( none ) + * Arguments: tzdelta (I) integer, positive or negative, containing + * containing number of hours to be added or + * subtracted from system time (to accommodate + * your desired time zone). + * ifmt (I) integer containing 0 for default format * -------------------------------------------------------------------------- * Returns: ( char * ) ptr to null-terminated buffer * containing current date:time stamp @@ -8934,40 +12768,483 @@ end_of_job: * Notes: o * ======================================================================= */ /* --- entry point --- */ -char *timestamp( ) +char *timestamp( int tzdelta, int ifmt ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -static char timebuff[64]; /* date:time buffer back to caller */ +static char timebuff[256]; /* date:time buffer back to caller */ /*long time_val = 0L;*/ /* binary value returned by time() */ time_t time_val = (time_t)(0); /* 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 */ +int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */ + month=0, day=0, /* adjust day and month for delta */ + minute=0,second=0; /* minute and second not adjusted */ +int tzadjust(); /* time zone adjustment function */ +int daynumber(); /* #days since Jan 1, 1973 */ +static char *daynames[] = { "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", "Sunday" } ; +static char *monthnames[] = { "?", "January", "February", "March", "April", + "May", "June", "July", "August", "September", "October", + "November", "December", "?" } ; /* ------------------------------------------------------------------------- get current date:time, adjust values, and and format stamp -------------------------------------------------------------------------- */ +/* --- first init returned timebuff in case of any error --- */ +*timebuff = '\000'; /* --- get current date:time --- */ time((time_t *)(&time_val)); /* get date and time */ tmstruct = localtime((time_t *)(&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 */ +/* --- extract fields --- */ +year = (int)(tmstruct->tm_year); /* local copy of year, 0=1900 */ +month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */ +day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */ +hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */ +minute= (int)(tmstruct->tm_min); /* local copy of minute,0-59 */ +second= (int)(tmstruct->tm_sec); /* local copy of second,0-59 */ +/* --- adjust year --- */ 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 */ +/* --- adjust for timezone --- */ +tzadjust(tzdelta,&year,&month,&day,&hour); +/* --- check params --- */ +if ( hour<0 || hour>23 +|| day<1 || day>31 +|| month<1 || month>12 +|| year<1973 ) goto end_of_job; +/* --- adjust hour for am/pm --- */ +switch ( ifmt ) + { + default: + case 0: + 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 */ + break; + } /* --- end-of-switch(ifmt) --- */ /* --- 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 */ +switch ( ifmt ) + { + default: + case 0: /* --- 2005-03-05:11:49:59am --- */ + sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day, + hour,minute,second,((ispm)?"pm":"am")); + break; + case 1: /* --- Saturday, March 5, 2005 --- */ + sprintf(timebuff,"%s, %s %d, %d", + daynames[daynumber(year,month,day)%7],monthnames[month],day,year); + break; + case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */ + sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s", + daynames[daynumber(year,month,day)%7],monthnames[month],day,year, + hour,minute,second,((ispm)?"pm":"am")); + break; + case 3: /* --- 11:49:59am --- */ + sprintf(timebuff,"%d:%02d:%02d%s", + hour,minute,second,((ispm)?"pm":"am")); + break; + case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */ + sprintf(timebuff,"%d%02d%02d%02d%02d", + month,day,hour,minute,second); + break; + } /* --- end-of-switch(ifmt) --- */ +end_of_job: + return ( timebuff ); /* return stamp to caller */ } /* --- end-of-function timestamp() --- */ /* ========================================================================== - * Function: dtoa ( dblval, npts ) + * Function: tzadjust ( tzdelta, year, month, day, hour ) + * Purpose: Adjusts hour, and day,month,year if necessary, + * by delta increment to accommodate your time zone. + * -------------------------------------------------------------------------- + * Arguments: tzdelta (I) integer, positive or negative, containing + * containing number of hours to be added or + * subtracted from given time (to accommodate + * your desired time zone). + * year (I) addr of int containing 4-digit year + * month (I) addr of int containing month 1=Jan - 12=Dec. + * day (I) addr of int containing day 1-31 for Jan. + * hour (I) addr of int containing hour 0-23 + * Returns: ( int ) 1 for success, or 0 for error + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour ) +{ +/* -------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/ +/* --- calendar data --- */ +static int modays[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 }; +/* -------------------------------------------------------------------------- +check args +-------------------------------------------------------------------------- */ +if ( mm<1 || mm>12 ) return(-1); /* bad month */ +if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */ +if ( hh<0 || hh>23 ) return(-1); /* bad hour */ +if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */ +/* -------------------------------------------------------------------------- +make adjustments +-------------------------------------------------------------------------- */ +/* --- adjust hour --- */ +hh += tzdelta; /* apply caller's delta */ +/* --- adjust for feb 29 --- */ +modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */ +/* --- adjust day --- */ +if ( hh < 0 ) /* went to preceding day */ + { dd--; hh += 24; } +if ( hh > 23 ) /* went to next day */ + { dd++; hh -= 24; } +/* --- adjust month --- */ +if ( dd < 1 ) /* went to preceding month */ + { mm--; dd = modays[mm]; } +if ( dd > modays[mm] ) /* went to next month */ + { mm++; dd = 1; } +/* --- adjust year --- */ +if ( mm < 1 ) /* went to preceding year */ + { yy--; mm = 12; dd = modays[mm]; } +if ( mm > 12 ) /* went to next year */ + { yy++; mm = 1; dd = 1; } +/* --- back to caller --- */ +*year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */ +return ( 1 ); +} /* --- end-of-function tzadjust() --- */ + + +/* ========================================================================== + * Function: daynumber ( year, month, day ) + * Purpose: Returns number of actual calendar days from Jan 1, 1973 + * to the given date (e.g., bvdaynumber(1974,1,1)=365). + * -------------------------------------------------------------------------- + * Arguments: year (I) int containing year -- may be either 1995 or + * 95, or may be either 2010 or 110 for those + * years. + * month (I) int containing month, 1=Jan thru 12=Dec. + * day (I) int containing day of month, 1-31 for Jan, etc. + * Returns: ( int ) Number of days from Jan 1, 1973 to given date, + * or -1 for error (e.g., year<1973). + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int daynumber ( int year, int month, int day ) +{ +/* -------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +/* --- returned value (note: returned as a default "int") --- */ +int ndays; /* #days since jan 1, year0 */ +/* --- initial conditions --- */ +static int year0 = 73, /* jan 1 was a monday, 72 was a leap */ + days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */ + days1yr = 365; +/* --- table of accumulated days per month (last index not used) --- */ +static int modays[] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; +/* --- variables for #days since day0 --- */ +int nyears, nfouryrs; /*#years, #4-yr periods since year0*/ +/* -------------------------------------------------------------------------- +Check input +-------------------------------------------------------------------------- */ +if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/ + return ( -1 ); /* otherwise, forget it */ +if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/ +/* -------------------------------------------------------------------------- +Find #days since jan 1, 1973 +-------------------------------------------------------------------------- */ +/* --- figure #complete 4-year periods and #remaining yrs till current --- */ +nyears = year - year0; /* #years since year0 */ +if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */ +nfouryrs = nyears/4; /* #complete four-year periods */ +nyears -= (4*nfouryrs); /* remainder excluding current year*/ +/* --- #days from jan 1, year0 till jan 1, this year --- */ +ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */ + + (days1yr*nyears); /* +remaining days */ +/*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */ +/* --- add #days within current year --- */ +ndays += (modays[month-1] + (day-1)); +/* --- may need an extra day if current year is a leap year --- */ +if ( nyears == 3 ) /*three preceding yrs so this is 4th*/ + { if ( month > 2 ) /* past feb so need an extra day */ + /*if ( year != 100 )*/ /* unless it's 2000AD */ + ndays++; } /* so add it in */ +return ( (int)(ndays) ); /* #days back to caller */ +} /* --- end-of-function daynumber() --- */ + + +/* ========================================================================== + * Function: strwrap ( s, linelen, tablen ) + * Purpose: Inserts \n's and spaces in (a copy of) s to wrap lines + * at linelen and indent them by tablen. + * -------------------------------------------------------------------------- + * Arguments: s (I) char * to null-terminated string + * to be wrapped. + * linelen (I) int containing maximum linelen + * between \\'s. + * tablen (I) int containing number of spaces to indent + * lines. 0=no indent. Positive means + * only indent first line and not others. + * Negative means indent all lines except first. + * -------------------------------------------------------------------------- + * Returns: ( char * ) ptr to "line-wrapped" copy of s + * or "" (empty string) for any error. + * -------------------------------------------------------------------------- + * Notes: o The returned copy of s has embedded \\'s as necessary + * to wrap lines at linelen. Any \\'s in the input copy + * are removed first. If (and only if) the input s contains + * a terminating \\ then so does the returned copy. + * o The returned pointer addresses a static buffer, + * so don't call strwrap() again until you're finished + * with output from the preceding call. + * o Modified for mimetex from original version written + * for mathtex (where \n in verbatim mode instead of \\ + * produced linebreaks). + * ======================================================================= */ +/* --- entry point --- */ +char *strwrap ( char *s, int linelen, int tablen ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +static char sbuff[4096]; /* line-wrapped copy of s */ +char *sol = sbuff; /* ptr to start of current line*/ +char tab[32] = " "; /* tab string */ +int strreplace(); /* remove \n's */ +char *strchange(); /* add \n's and indent space */ +int finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/ +int istab = (tablen>0?1:0), /* init true to indent first line */ + iswhite = 0; /* true if line break on whitespace*/ +int rhslen = 0, /* remaining right hand side length*/ + thislen = 0, /* length of current line segment */ + thistab = 0, /* length of tab on current line */ + wordlen = 0; /* length to next whitespace char */ +/* ------------------------------------------------------------------------- +Make a clean copy of s +-------------------------------------------------------------------------- */ +/* --- check input --- */ +*sbuff = '\000'; /* initialize in case of error */ +if ( isempty(s) ) goto end_of_job; /* no input */ +if ( tablen < 0 ) tablen = (-tablen); /* set positive tablen */ +if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */ +tab[min2(tablen,16)] = '\000'; /* null-terminate tab string */ +tablen = strlen(tab); /* reset to actual tab length */ +finalnewline = 0; /* turned off for mimetex version */ +/* --- start with copy of s --- */ +strninit(sbuff,s,3000); /* leave room for \n's and tabs */ +if ( linelen < 1 ) goto end_of_job; /* can't do anything */ +trimwhite(sbuff); /*remove leading/trailing whitespace*/ +strreplace(sbuff,"\n"," ",0); /* remove any original \n's */ +strreplace(sbuff,"\r"," ",0); /* remove any original \r's */ +strreplace(sbuff,"\t"," ",0); /* remove any original \t's */ +strreplace(sbuff,"\f"," ",0); /* remove any original \f's */ +strreplace(sbuff,"\v"," ",0); /* remove any original \v's */ +strreplace(sbuff,"\\\\"," ",0); /* remove any original \\'s */ +/* ------------------------------------------------------------------------- +Insert \\'s and spaces as needed +-------------------------------------------------------------------------- */ +while ( 1 ) { /* till end-of-line */ + /* --- init --- */ + trimwhite(sol); /*remove leading/trailing whitespace*/ + thislen = thistab = 0; /* no chars in current line yet */ + if ( istab && tablen>0 ) { /* need to indent this line */ + strchange(0,sol,tab); /* insert indent at start of line */ + thistab = tablen; } /* line starts with whitespace tab */ + if ( sol == sbuff ) istab = 1-istab; /* flip tab flag after first line */ + sol += thistab; /* skip tab */ + rhslen = strlen(sol); /* remaining right hand side chars */ + if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */ + if ( 0 && msgfp!=NULL && msglevel >= 99 ) { + fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol); + fflush(msgfp); } + /* --- look for last whitespace preceding linelen --- */ + while ( 1 ) { /* till we exceed linelen */ + wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/ + if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */ + goto end_of_job; /* so nothing more we can do */ + if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */ + if ( thislen > 0 ) break; /* but make sure line has one word */ + thislen += (wordlen+1); } /* ptr past next whitespace char */ + if ( thislen < 1 ) break; /* line will have one too-long word*/ + /*sol[thislen-1] = '\n';*/ /* replace last space with newline */ + /*sol += thislen;*/ /* next line starts after newline */ + iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/ + strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */ + sol += (thislen+2-iswhite); /* next line starts after \\ */ + } /* --- end-of-while(1) --- */ +end_of_job: + if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */ + return ( sbuff ); /* back with clean copy of s */ +} /* --- end-of-function strwrap() --- */ + + +/* ========================================================================== + * Function: strnlower ( s, n ) + * Purpose: lowercase the first n chars of string s + * -------------------------------------------------------------------------- + * Arguments: s (I/O) (char *)pointer to null-terminated string + * whose chars are to be lowercased + * n (I) int containing max number of chars to be + * lowercased (less than n will be lowercased + * if terminating '\000' found first) + * If n<=0 (or n>=strlen(s)) then the entire + * string s will be lowercased + * -------------------------------------------------------------------------- + * Returns: ( char * ) s (always same as input) + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +char *strnlower ( char *s, int n ) +{ +/* ------------------------------------------------------------------------- +lowercase s +-------------------------------------------------------------------------- */ +char *p = s; /* save s for return to caller */ +if ( !isempty(s) ) /* check for valid input */ + while ( *p != '\000' ) { /* lowercase each char till end */ + *p = tolower(*p); /* lowercase this char */ + if ( n > 0 ) /* only lowercase first n chars */ + if ( --n < 1 ) break; /* quit when we're done */ + p++; } /* proceed to next char */ +return ( s ); /* back to caller with s */ +} /* --- end-of-function strnlower() --- */ + + +/* ========================================================================== + * Function: urlprune ( url, n ) + * Purpose: Prune http://abc.def.ghi.com/etc into abc.def.ghi.com + * (if n=2 only ghi.com is returned, or if n=-1 only "ghi") + * -------------------------------------------------------------------------- + * Arguments: url (I) char * to null-terminated string + * containing url to be pruned + * n (i) int containing number of levels retained + * in pruned url. If n<0 its abs() is used, + * but the topmost level (usually .com, .org, + * etc) is omitted. That is, if n=2 would + * return "ghi.com" then n=-1 returns "ghi". + * n=0 retains all levels. + * -------------------------------------------------------------------------- + * Returns: ( char * ) pointer to (static) null-terminated string + * containing pruned url with the first n + * top-level domain, e.g., for n=2, + * http://abc.def.ghi.com/etc returns ghi.com, + * or an empty string "\000" for any error + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +char *urlprune ( char *url, int n ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +static char pruned[2048]; /* pruned url returned to caller */ +char *purl = /*NULL*/pruned; /* ptr to pruned, init for error */ +char *delim = NULL; /* delimiter separating components */ +char *strnlower(); /* lowercase a string */ +int istruncate = (n<0?1:0); /*true to truncate .com from pruned*/ +int ndots = 0; /* number of dots found in url */ +/* ------------------------------------------------------------------------- +prune the url +-------------------------------------------------------------------------- */ +/* --- first check input --- */ +*pruned = '\000'; /* init for error */ +if ( isempty(url) ) goto end_of_job; /* missing input, so return NULL */ +if ( n < 0 ) n = (-n); /* flip n positive */ +if ( n == 0 ) n = 999; /* retain all levels of url */ +/* --- preprocess url --- */ +strninit(pruned,url,2032); /* copy url to our static buffer */ +strlower(pruned); /* lowercase it and... */ +trimwhite(pruned); /*remove leading/trailing whitespace*/ +/* --- first remove leading http:// --- */ +if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */ + if ( ((int)(delim-pruned)) <= 8 ) { /* make sure it's a prefix */ + strsqueezep(pruned,delim+3); /* squeeze out leading http:// */ + trimwhite(pruned); } /*remove leading/trailing whitespace*/ +/* --- next remove leading www. --- */ +if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */ + if ( ((int)(delim-pruned)) == 0 ) { /* make sure it's the leading chars*/ + strsqueezep(pruned,delim+4); /* squeeze out leading www. */ + trimwhite(pruned); } /*remove leading/trailing whitespace*/ +/* --- finally remove leading / and everything following it --- */ +if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */ + *delim = '\000'; /* null-terminate url at first / */ +if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */ +/* --- count dots from back of url --- */ +delim = pruned + strlen(pruned); /*ptr to '\000' terminating pruned*/ +while ( ((int)(delim-pruned)) > 0 ) { /* don't back up before first char */ + delim--; /* ptr to preceding character */ + if ( *delim != '.' ) continue; /* not a dot, so keep looking */ + ndots++; /* count another dot found */ + if ( istruncate ) { /* remove trailing .com */ + istruncate = 0; /* don't truncate any more dots */ + *delim = '\000'; /* truncate pruned url */ + ndots = 0; } /* and reset dot count */ + if ( ndots >= n ) { /* have all requested levels */ + strsqueezep(pruned,delim+1); /* squeeze out leading levels */ + break; } /* and we're done */ + } /* --- end-of-while() --- */ +purl = pruned; /*completed okay, return pruned url*/ +end_of_job: + return ( purl ); /* back with pruned url */ +} /* --- end-of-function urlprune() --- */ + + +/* ========================================================================== + * Function: urlncmp ( url1, url2, n ) + * Purpose: Compares the n topmost levels of two urls + * -------------------------------------------------------------------------- + * Arguments: url1 (I) char * to null-terminated string + * containing url to be compared with url2 + * url2 (I) char * to null-terminated string + * containing url to be compared with url1 + * n (I) int containing number of top levels + * to compare, or 0 to compare them all. + * n<0 compares that many top levels excluding + * the last, i.e., for n=-1, xxx.com and xxx.org + * would be considered a match + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 if url's match, or + * 0 if not. + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int urlncmp ( char *url1, char *url2, int n ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +char *urlprune(), *prune=NULL, /* prune url's */ + prune1[4096], prune2[4096]; /* pruned copies of url1,url2 */ +int ismatch = 0; /* true if url's match */ +/* ------------------------------------------------------------------------- +prune url's and compare the pruned results +-------------------------------------------------------------------------- */ +/* --- check input --- */ +if ( isempty(url1) /*make sure both url1,url2 supplied*/ +|| isempty(url2) ) goto end_of_job; /* missing input, so return 0 */ +/* --- prune url's --- */ +prune = urlprune(url1,n); /* ptr to pruned version of url1 */ +if ( isempty(prune) ) goto end_of_job; /* some problem with url1 */ +strninit(prune1,prune,4064); /* local copy of pruned url1 */ +prune = urlprune(url2,n); /* ptr to pruned version of url2 */ +if ( isempty(prune) ) goto end_of_job; /* some problem with url2 */ +strninit(prune2,prune,4064); /* local copy of pruned url2 */ +/* --- compare pruned url's --- */ +if ( strcmp(prune1,prune2) == 0 ) /* pruned url's are identical */ + ismatch = 1; /* signal match to caller */ +end_of_job: + return ( ismatch ); /*back with #matching url components*/ +} /* --- end-of-function urlncmp() --- */ + + +/* ========================================================================== + * Function: dbltoa ( dblval, npts ) * Purpose: Converts double to ascii, in financial format * (e.g., comma-separated and negatives enclosed in ()'s). * ------------------------------------------------------------------------- @@ -8980,14 +13257,14 @@ return ( timebuff ); /* return stamp t * Notes: o * ======================================================================= */ /* --- entry point --- */ -char *dtoa ( dblval, npts ) -double dblval; -int npts; +char *dbltoa ( double dblval, int npts ) +/* double dblval; + int npts; */ { /* ------------------------------------------------------------------------- Allocations and Declarations ------------------------------------------------------------------------- */ -static char finval[128]; /* buffer returned to caller */ +static char finval[256]; /* buffer returned to caller */ static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */ char *finptr = finval; /* ptr to next char being converted*/ double floor(); /* integer which is glb(double) */ @@ -9048,7 +13325,7 @@ End-of-Job if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/ *finptr = '\000'; /* null-terminate converted double */ return ( finval ); /* converted double back to caller */ -} /* --- end-of-function dtoa() --- */ +} /* --- end-of-function dbltoa() --- */ /* ========================================================================== @@ -9152,7 +13429,7 @@ for ( irow=0; irowheight; irow++ ) && 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 ( !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 */ @@ -9160,7 +13437,7 @@ for ( irow=0; irowheight; irow++ ) 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 */ + bytemap[ipixel] = 0; } } /* so leave point white */ } /* --- end-of-for(irow,icol) --- */ /* ------------------------------------------------------------------------- Back to caller with gray-scale anti-aliased bytemap @@ -9202,6 +13479,7 @@ int width=rp->width, height=rp->height, icol = 0, irow = 0, /* width, height indexes */ imap = (-1); /* pixel index = icol + irow*width */ int bgbitval=0, fgbitval=1; /* background, foreground bitval */ +int isfirstaa = 1; /*debugging switch signals 1st pixel*/ #if 0 int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */ int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */ @@ -9279,6 +13557,39 @@ for ( irow=0; irow 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */ + if ( ngapsmaxgaps ) continue; + } /* --- end-of-if(1) --- */ /* --- antialias if necessary --- */ if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */ || (isfgalias && isfgedge) /* alias pixel surrounding fg */ @@ -9291,9 +13602,11 @@ for ( irow=0; irow=99 && msgfp!=NULL ) fprintf(msgfp, /* debugging */ - "aapnm> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n", + if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/ + "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n", + (isfirstaa?"aapnm algorithm":"aapnm"), irow,icol,imap, aawtval,aabyteval); + isfirstaa = 0; } } /* --- end-of-if(isedge) --- */ } /* --- end-of-for(irow,icol) --- */ /* ------------------------------------------------------------------------- @@ -9305,6 +13618,1186 @@ Back to caller with gray-scale anti-alia /* ========================================================================== + * Function: aapnmlookup ( rp, bytemap, grayscale ) + * Purpose: calculates a lowpass anti-aliased bytemap + * for rp->bitmap, with each byte 0...grayscale-1, + * based on the pnmalias.c algorithm. + * This version uses aagridnum() and aapatternnum() lookups + * to interpret 3x3 lowpass pixel grids. + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * bytemap (O) intbyte * to bytemap, calculated + * by applying pnm-based 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 Based on the pnmalias.c algorithm in the netpbm package + * on sourceforge. + * o This version uses aagridnum() and aapatternnum() lookups + * to interpret 3x3 lowpass pixel grids. + * ======================================================================= */ +/* --- entry point --- */ +int aapnmlookup (raster *rp, intbyte *bytemap, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int width=rp->width, height=rp->height, /* width, height of raster */ + icol = 0, irow = 0, /* width, height indexes */ + imap = (-1); /* pixel index = icol + irow*width */ +int bgbitval=0, fgbitval=1; /* background, foreground bitval */ +int isfirstaa = 1; /*debugging switch signals 1st pixel*/ +int aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt, + totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/ +int isfgalias = fgalias, /*(1) true to antialias fg bits */ + isfgonly = fgonly, /*(0) true to only antialias fg bits*/ + isbgalias = bgalias, /*(0) true to antialias bg bits */ + isbgonly = bgonly; /*(0) true to only antialias bg bits*/ +int gridnum=(-1), aagridnum(), /* grid# for 3x3 grid at irow,icol */ + patternum=(-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/ +int aapatterns(); /* to antialias special patterns */ +/* --- + * pattern number data + * ------------------- */ +/* --- number of adjacent fg pixels set in pattern --- */ +static int nadjacents[] = { -1, /* #adjacent fg pixels for pattern */ + 0, 4, 0, 1, 4, 3, 1, 0, 1, 0, /* 1-10 */ + 2, 2, 3, 4, 3, 4, 2, 2, 1, 2, /* 11-20 */ + 1, 2, 1, 2, 0, 1, 3, 2, 3, 2, /* 21-30 */ + 3, 2, 3, 2, 4, 3, 1, 2, 2, 2, /* 31-40 */ + 2, 1, 2, 2, 3, 0, 3, 2, 2, 1, 4, /* 41-51 */ + -1 } ; /* --- end-of-nadjacents[] --- */ +/* --- number of corner fg pixels set in pattern --- */ +static int ncorners[] = { -1, /* #corner fg pixels for pattern */ + 0, 4, 1, 0, 3, 4, 1, 2, 1, 2, /* 1-10 */ + 0, 0, 3, 2, 3, 2, 4, 4, 2, 1, /* 11-20 */ + 2, 1, 2, 1, 3, 2, 0, 1, 2, 3, /* 21-30 */ + 2, 3, 2, 3, 1, 2, 4, 3, 2, 2, /* 31-40 */ + 2, 3, 2, 2, 1, 4, 1, 2, 2, 3, 0, /* 41-51 */ + -1 } ; /* --- end-of-ncorners[] --- */ +/* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */ +static int horzedges[] = { -1, /* 0,1,2 = horz bg,fg,both edge */ + 0, 1, 0, 0, 1, 1, 0, 0, 0, -1, /* 1-10 */ + 0, -1, 1, 1, 1, -1, 1, -1, 2, 0, /* 11-20 */ + -1, -1, -1, 0, -1, -1, -1, -1, 2, 1, /* 21-30 */ + -1, -1, -1, 1, -1, -1, -1, -1, 2, -1, /* 31-40 */ + -1, 1, 1, -1, -1, -1, 0, 0, -1, -1, -1, /* 41-51 */ + -1 } ; /* --- end-of-horzedges[] --- */ +/* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */ +static int vertedges[] = { -1, /* 0,1,2 = vert bg,fg,both edge */ + 0, 1, 0, 0, 1, 1, 0, -1, -1, -1, /* 1-10 */ + 0, 0, 1, -1, -1, -1, 1, 1, -1, -1, /* 11-20 */ + -1, 0, 0, 0, -1, -1, 0, -1, -1, -1, /* 21-30 */ + -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, /* 31-40 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 41-51 */ + -1 } ; /* --- end-of-vertedges[] --- */ +/* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */ +static int diagedges[] = { -1, /* 0,1,2 = diag bg,fg,both edge */ + 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, /* 1-10 */ + 2, -1, 1, 1, 1, 1, 1, -1, 0, 2, /* 11-20 */ + 0, -1, 0, 2, 0, -1, 1, 1, 1, 1, /* 21-30 */ + 1, -1, 1, 2, 1, 1, -1, 1, 2, -1, /* 31-40 */ + 1, 0, -1, 2, 1, 0, 1, -1, 1, -1, 1, /* 41-51 */ + -1 } ; /* --- end-of-diagedges[] --- */ +/* ------------------------------------------------------------------------- +Calculate bytemap as 9-point weighted average over bitmap +-------------------------------------------------------------------------- */ +for ( irow=0; irow511 ) continue; /* gridnum out of bounds*/ + /* --- check if we're antialiasing this pixel --- */ + if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */ + || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */ + continue; /* leave default and do next bit */ + /* --- look up pattern number, 1-51, corresponding to input gridnum --- */ + patternum = aapatternnum(gridnum); /* look up pattern number */ + if ( patternum<1 || patternum>51 ) continue; /* some internal error */ + /* --- special pattern number processing --- */ + if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale)) + >= 0 ) { /* special processing for pattern */ + bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */ + continue; } /* and continue with next pixel */ + /* --- check for diagonal edges --- */ + isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/ + diagedges[patternum]==0 ); + isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/ + diagedges[patternum]==1 ); + /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */ + if ( 1 ) { /* true to perform test */ + int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */ + horzedge=horzedges[patternum], vertedge=vertedges[patternum]; + isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */ + isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */ + isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */ + isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */ + if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/ + || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */ + continue; /* don't antialias */ + } /* --- end-of-if(1) --- */ +#if 0 + /* --- check #gaps for checkerboard (added by j.forkosh) --- */ + if ( 0 ) { /* true to perform test */ + int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */ + if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */ + if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */ + if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */ + if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */ + if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */ + if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */ + if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */ + if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */ + if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */ + if ( ngapsmaxgaps ) continue; + } /* --- end-of-if(1) --- */ +#endif + /* --- antialias if necessary --- */ + if ( (isbgalias && isbgdiag) /* alias pixel surrounding bg */ + || (isfgalias && isfgdiag) /* alias pixel surrounding fg */ + || (isbgdiag && isfgdiag) ) /* neighboring fg and bg pixel */ + { + int aasumval = /* sum wts[]*bitmap[] */ + aacenterwt*bitval + /* apply centerwt to center pixel */ + aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */ + aacornerwt*ncorners[patternum]; /* and corners */ + double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */ + aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/ + bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */ + if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/ + "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d", + (isfirstaa?"aapnmlookup algorithm":"aapnm"), + irow,icol,imap, aawtval,aabyteval); + if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */ + else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum); + isfirstaa = 0; } + } /* --- end-of-if(isedge) --- */ + } /* --- end-of-for(irow,icol) --- */ +/* ------------------------------------------------------------------------- +Back to caller with gray-scale anti-aliased bytemap +-------------------------------------------------------------------------- */ +/*end_of_job:*/ + return ( 1 ); +} /* --- end-of-function aapnmlookup() --- */ + + +/* ========================================================================== + * Function: aapatterns ( rp, irow, icol, gridnum, patternum, grayscale ) + * Purpose: For patterns requireing special processing, + * calculates anti-aliased value for pixel at irow,icol, + * whose surrounding 3x3 pixel grid is coded by gridnum + * (which must correspond to a pattern requiring special + * processing). + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * irow (I) int containing row, 0...height-1, + * of pixel to be antialiased + * icol (I) int containing col, 0...width-1, + * of pixel to be antialiased + * gridnum (I) int containing 0...511 corresponding to + * 3x3 pixel grid surrounding irow,icol + * patternum (I) int containing 1...51 pattern# of + * the 3x3 grid surrounding irow,icol + * grayscale (I) int containing number of grayscales + * to be calculated, 0...grayscale-1 + * (should typically be given as 256) + * -------------------------------------------------------------------------- + * Returns: ( int ) 0...grayscale-1 for success, + * -1 = error, or no special processing required + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int aapatterns (raster *rp, int irow, int icol, + int gridnum, int patternum, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int aaval = (-1); /* antialiased value returned */ +int iscenter = (gridnum&1); /* true if center pixel set/black */ +int aapatternnum(), /* if patternum not supplied */ + aapattern1124(), /* routine for patterns #11,24 */ + aapattern19(), /* special routine for pattern #19 */ + aapattern20(), /* special routine for pattern #20 */ + aapattern39(); /* special routine for pattern #39 */ +/* ------------------------------------------------------------------------- +special pattern number processing +-------------------------------------------------------------------------- */ +if ( 1 ) { + if ( patternum < 1 ) /* pattern# not supplied by caller */ + patternum = aapatternnum(gridnum); /* so look it up ourselves */ + switch ( patternum ) { + default: break; /* no special processing */ + case 11: + case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break; + case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break; + case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break; + case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break; + /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */ + case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */ + } /* --- end-of-switch(patternum) --- */ + } /* --- end-of-if() --- */ +return ( aaval ); /* return antialiased val to caller*/ +} /* --- end-of-function aapatterns() --- */ + + +/* ========================================================================== + * Function: aapattern1124 ( rp, irow, icol, gridnum, grayscale ) + * Purpose: calculates anti-aliased value for pixel at irow,icol, + * whose surrounding 3x3 pixel grid is coded by gridnum + * (which must correspond to pattern #11 or #24). + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * irow (I) int containing row, 0...height-1, + * of pixel to be antialiased + * icol (I) int containing col, 0...width-1, + * of pixel to be antialiased + * gridnum (I) int containing 0...511 corresponding to + * 3x3 pixel grid surrounding irow,icol + * grayscale (I) int containing number of grayscales + * to be calculated, 0...grayscale-1 + * (should typically be given as 256) + * -------------------------------------------------------------------------- + * Returns: ( int ) 0...grayscale-1 for success, -1=any error + * -------------------------------------------------------------------------- + * Notes: o Handles the eight gridnum's + * (gridnum/2 shown to eliminate irrelevant low-order bit) + * --- --- -*- -*- + * --* = 10 *-- = 18 --* = 72 *-- = 80 (pattern$11) + * -*- -*- --- --- + * + * --- --- -** **- + * --* = 11 *-- = 22 --* = 104 *-- = 208 (pattern$24) + * -** **- --- --- + * o For black * center pixel, using grid#10 as an example, + * pixel stays --- antialiased ---* + * black if -*** if part of -** + * part of a -*- a diagonal -*- + * corner, eg, * line, eg, * + * ======================================================================= */ +/* --- entry point --- */ +int aapattern1124 (raster *rp, int irow, int icol, + int gridnum, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int aaval = (-1); /* antialiased value returned */ +int iscenter = gridnum&1; /* true if pixel at irow,icol black*/ +int patternum = 24; /* init for pattern#24 default */ +pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */ +int width=rp->width, height=rp->height; /* width, height of raster */ +int jrow=irow, jcol=icol; /* corner or diagonal row,col */ +int vertcornval=0, horzcornval=0, /* vertical, horizontal corner bits*/ + topdiagval=0, botdiagval=0, /* upper,lower diagonal pixel bits */ + cornval=0, diagval=0; /* vert+horzcorn, top+botdiag */ +int hdirection=99, vdirection=99, /* horz,vert corner direction */ + hturn=99,vturn=99, aafollowline(); /* follow corner till turns */ +/* ------------------------------------------------------------------------- +Check corner and diagonal pixels +-------------------------------------------------------------------------- */ +if ( 0 ) goto end_of_job; /* true to turn off pattern1124 */ +switch ( gridnum/2 ) { /* check pattern#11,24 corner, diag*/ + default: goto end_of_job; /* not a pattern#11,24 gridnum */ + case 10: patternum=11; case 11: + hdirection = 2; vdirection = -1; /* directions to follow corner */ + if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */ + vertcornval = getlongbit(bitmap,(icol+jrow*width)); + if ( (icol-1) >= 0 ) /* lower diag left of center */ + botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); } + if ( (jcol=icol+2) < width ) { /* horz corner right of center */ + horzcornval = getlongbit(bitmap,(jcol+irow*width)); + if ( (irow-1) >= 0 ) /* upper diag above center */ + topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); } + break; + case 18: patternum=11; case 22: + hdirection = -2; vdirection = -1; /* directions to follow corner */ + if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */ + vertcornval = getlongbit(bitmap,(icol+jrow*width)); + if ( (icol+1) < width ) /* lower diag right of center */ + botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); } + if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */ + horzcornval = getlongbit(bitmap,(jcol+irow*width)); + if ( (irow-1) >= 0 ) /* upper diag above center */ + topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); } + break; + case 72: patternum=11; case 104: + hdirection = 2; vdirection = 1; /* directions to follow corner */ + if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */ + vertcornval = getlongbit(bitmap,(icol+jrow*width)); + if ( (icol-1) >= 0 ) /* upper diag left of center */ + topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); } + if ( (jcol=icol+2) < width ) { /* horz corner right of center */ + horzcornval = getlongbit(bitmap,(jcol+irow*width)); + if ( (irow+1) < height ) /* lower diag below center */ + botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); } + break; + case 80: patternum=11; case 208: + hdirection = -2; vdirection = 1; /* directions to follow corner */ + if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */ + vertcornval = getlongbit(bitmap,(icol+jrow*width)); + if ( (icol+1) < width ) /* upper diag right of center */ + topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); } + if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */ + horzcornval = getlongbit(bitmap,(jcol+irow*width)); + if ( (irow+1) < height ) /* lower diag below center */ + botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); } + break; + } /* --- end-of-switch(gridnum/2) --- */ +cornval = vertcornval+horzcornval; /* 0=no corner bits, 1, 2=both */ +diagval = topdiagval+botdiagval; /* 0=no diag bits, 1, 2=both */ +/* ------------------------------------------------------------------------- +Handle white center +-------------------------------------------------------------------------- */ +if ( 1 && !iscenter ) { aaval = (patternum==11?51:64); goto end_of_job; } +/* ------------------------------------------------------------------------- +Handle black center +-------------------------------------------------------------------------- */ +if ( diagval > 1 ) aaval = ( patternum==24? 255:191 ); +else { + hturn = aafollowline(rp,irow,icol,hdirection); + vturn = aafollowline(rp,irow,icol,vdirection); + if ( vturn*hdirection < 0 && hturn*vdirection < 0 ) + aaval = ( patternum==24? 255:191 ); + else aaval = grayscale-1; } /* actual corner */ +/* ------------------------------------------------------------------------- +Back to caller with grayscale antialiased value for pixel at irow,icol +-------------------------------------------------------------------------- */ +end_of_job: + if ( aaval >= 0 ) /* have antialiasing result */ + if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */ + "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, " + "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n", + irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval, + vdirection,hdirection, vturn,hturn, aaval); + return ( aaval ); /* back with antialiased value */ +} /* --- end-of-function aapattern1124() --- */ + + +/* ========================================================================== + * Function: aapattern19 ( rp, irow, icol, gridnum, grayscale ) + * Purpose: calculates anti-aliased value for pixel at irow,icol, + * whose surrounding 3x3 pixel grid is coded by gridnum + * (which must correspond to pattern #19). + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * irow (I) int containing row, 0...height-1, + * of pixel to be antialiased + * icol (I) int containing col, 0...width-1, + * of pixel to be antialiased + * gridnum (I) int containing 0...511 corresponding to + * 3x3 pixel grid surrounding irow,icol + * grayscale (I) int containing number of grayscales + * to be calculated, 0...grayscale-1 + * (should typically be given as 256) + * -------------------------------------------------------------------------- + * Returns: ( int ) 0...grayscale-1 for success, -1=any error + * -------------------------------------------------------------------------- + * Notes: o Handles the four gridnum's + * (gridnum/2 shown to eliminate irrelevant low-order bit) + * --- --* *-- *** + * --- = 7 --* = 41 *-- = 148 --- = 224 + * *** --* *-- --- + * ======================================================================= */ +/* --- entry point --- */ +int aapattern19 (raster *rp, int irow, int icol, + int gridnum, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int aaval = (-1); /* antialiased value returned */ +int iscenter = gridnum&1; /* true if pixel at irow,icol black*/ +int orientation = 1, /* 1=vertical, 2=horizontal */ + jrow=irow, jcol=icol; /* middle pixel of *** line */ +int turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */ +/* ------------------------------------------------------------------------- +Initialization and calculation of antialiased value +-------------------------------------------------------------------------- */ +/* --- check input -- */ +if ( iscenter ) goto end_of_job; /* we only antialias white pixels */ +/* --- set params --- */ +switch ( gridnum/2 ) { /* check pattern#19 orientation */ + default: goto end_of_job; /* not a pattern#19 gridnum */ + case 7: orientation=2; jrow++; break; + case 41: orientation=1; jcol++; break; + case 148: orientation=1; jcol--; break; + case 224: orientation=2; jrow--; break; + } /* --- end-of-switch(gridnum/2) --- */ +/* --- get turns in both directions --- */ +if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job; +if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job; +if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */ +/* --- weight pixel --- */ +aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) ); +/* ------------------------------------------------------------------------- +Back to caller with grayscale antialiased value for pixel at irow,icol +-------------------------------------------------------------------------- */ +end_of_job: + if ( aaval >= 0 ) /* have antialiasing result */ + if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */ + "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n", + irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval); + return ( aaval ); /* back with antialiased value */ +} /* --- end-of-function aapattern19() --- */ + + +/* ========================================================================== + * Function: aapattern20 ( rp, irow, icol, gridnum, grayscale ) + * Purpose: calculates anti-aliased value for pixel at irow,icol, + * whose surrounding 3x3 pixel grid is coded by gridnum + * (which must correspond to pattern #20). + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * irow (I) int containing row, 0...height-1, + * of pixel to be antialiased + * icol (I) int containing col, 0...width-1, + * of pixel to be antialiased + * gridnum (I) int containing 0...511 corresponding to + * 3x3 pixel grid surrounding irow,icol + * grayscale (I) int containing number of grayscales + * to be calculated, 0...grayscale-1 + * (should typically be given as 256) + * -------------------------------------------------------------------------- + * Returns: ( int ) 0...grayscale-1 for success, -1=any error + * -------------------------------------------------------------------------- + * Notes: o Handles the eight gridnum's + * (gridnum/2 shown to eliminate irrelevant low-order bit) + * --- --- --* -*- + * --* = 14 *-- = 19 --* = 42 --* = 73 + * **- -** -*- --* + * + * -*- -** *-- **- + * *-- = 84 *-- = 112 *-- = 146 --* = 200 + * *-- --- -*- --- + * ======================================================================= */ +/* --- entry point --- */ +int aapattern20 (raster *rp, int irow, int icol, + int gridnum, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int aaval = (-1); /* antialiased value returned */ +int iscenter = gridnum&1; /* true if pixel at irow,icol black*/ +int direction = 1, /* direction to follow ** line */ + jrow1=irow, jcol1=icol, /* coords of * */ + jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */ +int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */ +/* ------------------------------------------------------------------------- +Initialization and calculation of antialiased value +-------------------------------------------------------------------------- */ +/* --- check input -- */ +if ( 1 ) goto end_of_job; /* don't want this one */ +if ( iscenter ) goto end_of_job; /* we only antialias white pixels */ +/* --- set params --- */ +switch ( gridnum/2 ) { /* check pattern#20 orientation */ + default: goto end_of_job; /* not a pattern#20 gridnum */ + case 14: direction=-2; jcol1++; jrow2++; break; + case 19: direction=2; jcol1--; jrow2++; break; + case 42: direction=1; jrow1++; jcol2++; break; + case 73: direction=-1; jrow1--; jcol2++; break; + case 84: direction=-1; jrow1--; jcol2--; break; + case 112: direction=2; jcol1--; jrow2--; break; + case 146: direction=1; jrow1++; jcol2--; break; + case 200: direction=-2; jcol1++; jrow2--; break; + } /* --- end-of-switch(gridnum/2) --- */ +/* --- get turns in both directions --- */ +if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job; +if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job; +if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */ +/* --- weight pixel --- */ +aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) ); +/* ------------------------------------------------------------------------- +Back to caller with grayscale antialiased value for pixel at irow,icol +-------------------------------------------------------------------------- */ +end_of_job: + if ( aaval >= 0 ) /* have antialiasing result */ + if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */ + "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n", + irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval); + return ( aaval ); /* back with antialiased value */ +} /* --- end-of-function aapattern20() --- */ + + +/* ========================================================================== + * Function: aapattern39 ( rp, irow, icol, gridnum, grayscale ) + * Purpose: calculates anti-aliased value for pixel at irow,icol, + * whose surrounding 3x3 pixel grid is coded by gridnum + * (which must correspond to pattern #39). + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * irow (I) int containing row, 0...height-1, + * of pixel to be antialiased + * icol (I) int containing col, 0...width-1, + * of pixel to be antialiased + * gridnum (I) int containing 0...511 corresponding to + * 3x3 pixel grid surrounding irow,icol + * grayscale (I) int containing number of grayscales + * to be calculated, 0...grayscale-1 + * (should typically be given as 256) + * -------------------------------------------------------------------------- + * Returns: ( int ) 0...grayscale-1 for success, -1=any error + * -------------------------------------------------------------------------- + * Notes: o Handles the eight gridnum's + * (gridnum/2 shown to eliminate irrelevant low-order bit) + * --- --- --* -** + * --* = 15 *-- = 23 --* = 43 --* = 105 + * *** *** -** --* + * + * **- *** *-- *** + * *-- = 212 *-- = 240 *-- = 150 --* = 232 + * *-- --- **- --- + * ======================================================================= */ +/* --- entry point --- */ +int aapattern39 (raster *rp, int irow, int icol, + int gridnum, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int aaval = (-1); /* antialiased value returned */ +int iscenter = gridnum&1; /* true if pixel at irow,icol black*/ +int direction = 1, /* direction to follow ** line */ + jrow1=irow, jcol1=icol, /* coords of * */ + jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */ +int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */ +/* ------------------------------------------------------------------------- +Initialization and calculation of antialiased value +-------------------------------------------------------------------------- */ +/* --- check input -- */ +if ( iscenter ) goto end_of_job; /* we only antialias white pixels */ +/* --- set params --- */ +switch ( gridnum/2 ) { /* check pattern#39 orientation */ + default: goto end_of_job; /* not a pattern#39 gridnum */ + case 15: direction=-2; jcol1++; jrow2++; break; + case 23: direction=2; jcol1--; jrow2++; break; + case 43: direction=1; jrow1++; jcol2++; break; + case 105: direction=-1; jrow1--; jcol2++; break; + case 212: direction=-1; jrow1--; jcol2--; break; + case 240: direction=2; jcol1--; jrow2--; break; + case 150: direction=1; jrow1++; jcol2--; break; + case 232: direction=-2; jcol1++; jrow2--; break; + } /* --- end-of-switch(gridnum/2) --- */ +/* --- get turns directions (tunr1==1 signals inside corner) --- */ +if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 ) + { aaval=0; goto end_of_job; } +if ( 1 ) goto end_of_job; /* stop here for now */ +if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job; +if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */ +/* --- weight pixel --- */ +aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) ); +/* ------------------------------------------------------------------------- +Back to caller with grayscale antialiased value for pixel at irow,icol +-------------------------------------------------------------------------- */ +end_of_job: + if ( aaval >= 0 ) /* have antialiasing result */ + if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */ + "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n", + irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval); + return ( aaval ); /* back with antialiased value */ +} /* --- end-of-function aapattern39() --- */ + + +/* ========================================================================== + * Function: aafollowline ( rp, irow, icol, direction ) + * Purpose: starting with pixel at irow,icol, moves in + * specified direction looking for a "turn" + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster containing pixel image + * irow (I) int containing row, 0...height-1, + * of first pixel + * icol (I) int containing col, 0...width-1, + * of first pixel + * direction (I) int containing +1 to follow line up/north + * (decreasing irow), -1 to follow line + * down/south (increasing irow), +2 to follow + * line right/east (increasing icol), + * -2 to follow line left/west (decreasing icol) + * -------------------------------------------------------------------------- + * Returns: ( int ) #rows or #cols traversed prior to turn, + * or 0 if no turn detected (or for any error). + * Sign is + if turn direction is right/east or + * up/north, or is - if left/west or down/south. + * -------------------------------------------------------------------------- + * Notes: o Here are some examples illustrating turn detection in + * +2 (right/east) direction. Turns in other directions + * are detected similarly/symmetrically. * denotes black + * bits (usually fg), - denotes white bits (usually bg), + * and ? denotes "don't care" bit (won't affect outcome). + * Arrow --> points to start pixel denoted by irow,icol. + * + * *??? -??? turn=0 (no turn) is returned + * -->*??? or -->-??? because the start pixel isn't + * *??? -??? on an edge to begin with + * + * ---- **-- turn=0 returned because the + * -->***- or -->***- line ends abruptly without + * ---- ---- turning (even the second case) + * + * ---* ---* turn=0 returned because the + * -->***- or -->**** line forms a Y or T rather + * ---* ---* than turning + * + * ***- **** turn=+3 returned + * -->***- or -->***- (outside corner) + * ---- ---- + * + * ***** ****- turn=-4 returned + * -->***** or -->****- (inside corner) + * ----* ----* + * + * ----* ----* turn=+4 returned + * -->****- or -->***** (outside or inside corner) + * ----- ----- + * ======================================================================= */ +/* --- entry point --- */ +int aafollowline (raster *rp, int irow, int icol, int direction) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */ +int width=rp->width, height=rp->height; /* width, height of raster */ +int drow=0, dcol=0, /* delta row,col to follow line */ + jrow=irow, jcol=icol; /* current row,col following line */ +int bitval=1, /* value of rp bit at irow,icol */ + fgval=1, bgval=0, /* "fg" is whatever bitval is */ + bitminus=0, bitplus=0; /* value of left/down, right/up bit*/ +int isline=1, isedge=0; /*isline signals one-pixel wide line*/ +int turn = 0, /* detected turn back to caller */ + maxturn = maxfollow; /* don't follow more than max pixels*/ +/* ------------------------------------------------------------------------- +Initialization +-------------------------------------------------------------------------- */ +/* --- check input --- */ +if ( irow<0 || irow>=height /* irow out-of-bounds */ +|| icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */ +/* --- adjust maxturn for magstep --- */ +if ( 1 ) /* guard */ + if ( magstep > 1 && magstep <= 10 ) /* sanity check */ + maxturn *= magstep; /* factor in magnification */ +/* --- starting bit -- see if we're following a fg (usual), or bg line --- */ +bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/ +fgval = bitval; bgval = (1-bitval); /* define "fg" as whatever bitval is*/ +/* --- set drow,dcol corresponding to desired direction --- */ +switch ( direction ) { /* determine drow,dcol for direction*/ + default: goto end_of_job; /* unrecognized direction arg */ + case 1: drow = (-1); break; /* follow line up/north */ + case -1: drow = 1; break; /* down/south */ + case 2: dcol = 1; break; /* right/east */ + case -2: dcol = (-1); break; } /* left/west */ +/* --- set bitminus and bitplus --- */ +if ( drow == 0 ) { /* we're following line right/left */ + if ( irow < height ) /* there's a pixel below current */ + bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */ + if ( irow > 0 ) /* there's a pixel above current */ + bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */ +if ( dcol == 0 ) { /* we're following line up/down */ + if ( icol < width ) /* there's a pixel to the right */ + bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */ + if ( icol > 0 ) /* there's a pixel to the left */ + bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */ +/* --- check for lack of line to follow --- */ +if ( bitval == bitplus /* starting pixel same as above */ +&& bitval == bitminus ) /* and below (or right and left) */ + goto end_of_job; /* so there's no line to follow */ +/* --- set isline and isedge (already initted for isline) --- */ +if ( bitval == bitplus ) /* starting pixel same as above */ + { isedge = (-1); isline = 0; } /* so we're at an edge below */ +if ( bitval == bitminus ) /* starting pixel same as below */ + { isedge = 1; isline = 0; } /* so we're at an edge above */ +/* ------------------------------------------------------------------------- +follow line +-------------------------------------------------------------------------- */ +while ( 1 ) { /* until turn found (or max) */ + /* --- local allocations and declarations --- */ + int dbitval=0, /* value of bit at jrow,jcol */ + dbitminus=0, dbitplus=0; /* value of left/down, right/up bit*/ + /* --- bump pixel count and indexes; check for max or end-of-raster --- */ + turn++; /* bump #pixels followed */ + jrow += drow; jcol += dcol; /* indexes of next pixel to check */ + if ( turn > maxturn /* already followed max #pixels */ + || jrow<0 || jrow>=height /* or jrow past end-of-raster */ + || jcol<0 || jcol>=width ) /* or jcol past end-of-raster */ + { turn = 0; goto end_of_job; } /* so quit without finding a turn */ + /* --- set current bit (dbitval) --- */ + dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/ + /* --- set dbitminus and dbitplus --- */ + if ( drow == 0 ) { /* we're following line right/left */ + if ( irow < height ) /* there's a pixel below current */ + dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */ + if ( irow > 0 ) /* there's a pixel above current */ + dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */ + if ( dcol == 0 ) { /* we're following line up/down */ + if ( icol < width ) /* there's a pixel to the right */ + dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */ + if ( icol > 0 ) /* there's a pixel to the left */ + dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */ + /* --- first check for abrupt end-of-line, or for T or Y --- */ + if ( isline != 0 ) /* abrupt end or T,Y must be a line*/ + if ( ( bgval == dbitval /* end-of-line if pixel flips to bg*/ + && bgval == dbitplus /* and bg same as above pixel */ + && bgval == dbitminus ) /* and below (or right and left) */ + || ( fgval == dbitplus /* T or Y if fg same as above pixel*/ + && fgval == dbitminus ) ) /* and below (or right and left) */ + { turn = 0; goto end_of_job; } /* so we're at a T or Y */ + /* --- check for turning line --- */ + if ( isline != 0 ) { /* turning line must be a line */ + if ( fgval == dbitminus ) /* turning down */ + { turn = -turn; goto end_of_job; } /* so return negative turn */ + else if ( fgval == dbitplus ) /* turning up */ + goto end_of_job; } /* so return positive turn */ + /* --- check for inside corner at edge --- */ + if ( isedge != 0 ) { /* inside corner must be a edge */ + if ( isedge < 0 && fgval == bitminus ) /* corner below */ + { turn = -turn; goto end_of_job; } /* so return negative turn */ + if ( isedge > 0 && fgval == bitplus ) /* corner above */ + goto end_of_job; } /* so return positive turn */ + /* --- check for abrupt end at edge --- */ + if ( isedge != 0 /* abrupt edge end must be an edge */ + && fgval == dbitval ) /* and line must not end */ + if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */ + || (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */ + { turn = 0; goto end_of_job; } /* so edge ended abruptly */ + /* --- check for outside corner at edge --- */ + if ( isedge != 0 /* outside corner must be a edge */ + && bgval == dbitval ) { /* and line must end */ + if ( isedge > 0 ) turn = -turn; /* outside turn down from edge above*/ + goto end_of_job; } + } /* --- end-of-while(1) --- */ +/* ------------------------------------------------------------------------- +Back to caller with #rows or #cols traversed, and direction of detected turn +-------------------------------------------------------------------------- */ +end_of_job: + if ( msglevel>=99 && msgfp!=NULL ) /* debugging/diagnostic output */ + fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n", + irow,icol,direction,turn); + return ( turn ); +} /* --- end-of-function aafollowline() --- */ + + +/* ========================================================================== + * Function: aagridnum ( rp, irow, icol ) + * Purpose: calculates gridnum, 0-511 (see Notes below), + * for 3x3 grid centered at irow,icol + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster containing + * bitmap image (to be anti-aliased) + * irow (I) int containing row, 0...height-1, + * at center of 3x3 grid + * icol (I) int containing col, 0...width-1, + * at center of 3x3 grid + * -------------------------------------------------------------------------- + * Returns: ( int ) 0-511 grid number, or -1=any error + * -------------------------------------------------------------------------- + * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid + * whose bit positions (and corresponding values) in gridnum are + * 876 256 128 64 + * 504 = 32 1 16 + * 321 8 4 2 + * Thus, for example (*=pixel set/black, -=pixel not set/white), + * *-- *-- -** (note that 209 is the + * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset, + * --* *** --- of 302) + * o A set pixel is considered black, an unset pixel considered + * white. + * ======================================================================= */ +/* --- entry point --- */ +int aagridnum (raster *rp, int irow, int icol) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */ +int width=rp->width, height=rp->height, /* width, height of raster */ + imap = icol + irow*width; /* pixel index = icol + irow*width */ +int bitval=0, /* value of rp bit at irow,icol */ + nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/ + ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0, /*compass pt names*/ + gridnum = (-1); /* grid# 0-511 for above 9 bits */ +/* ------------------------------------------------------------------------- +check input +-------------------------------------------------------------------------- */ +if ( irow<0 || irow>=height /* irow out-of-bounds */ +|| icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */ +/* ------------------------------------------------------------------------- +get the 9 bits comprising the 3x3 grid centered at irow,icol +-------------------------------------------------------------------------- */ +/* --- get center bit --- */ +bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */ +/* --- get 8 surrounding bits --- */ +if ( irow > 0 ) /* nn (north) bit available */ + nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */ +if ( irow < height-1 ) /* ss (south) bit available */ + ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */ +if ( icol > 0 ) /* ww (west) bit available */ + { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */ + if ( irow > 0 ) /* nw bit available */ + nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */ + if ( irow < height-1 ) /* sw bit available */ + swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */ +if ( icol < width-1 ) /* ee (east) bit available */ + { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */ + if ( irow > 0 ) /* ne bit available */ + nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */ + if ( irow < height-1 ) /* se bit available */ + sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */ +/* --- set gridnum --- */ +gridnum = 0; /* clear all bits */ +if ( bitval ) gridnum = 1; /* set1bit(gridnum,0); */ +if ( nwbitval ) gridnum += 256; /* set1bit(gridnum,8); */ +if ( nnbitval ) gridnum += 128; /* set1bit(gridnum,7); */ +if ( nebitval ) gridnum += 64; /* set1bit(gridnum,6); */ +if ( wwbitval ) gridnum += 32; /* set1bit(gridnum,5); */ +if ( eebitval ) gridnum += 16; /* set1bit(gridnum,4); */ +if ( swbitval ) gridnum += 8; /* set1bit(gridnum,3); */ +if ( ssbitval ) gridnum += 4; /* set1bit(gridnum,2); */ +if ( sebitval ) gridnum += 2; /* set1bit(gridnum,1); */ +/* ------------------------------------------------------------------------- +Back to caller with gridnum coding 3x3 grid centered at irow,icol +-------------------------------------------------------------------------- */ +end_of_job: + return ( gridnum ); +} /* --- end-of-function aagridnum() --- */ + + +/* ========================================================================== + * Function: aapatternnum ( gridnum ) + * Purpose: Looks up the pattern number 1...51 + * corresponding to the 3x3 pixel grid coded by gridnum 0=no + * pixels set (white) to 511=all pixels set (black). + * -------------------------------------------------------------------------- + * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid + * (see Notes below) + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 to 51, or -1=error + * -------------------------------------------------------------------------- + * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid + * whose bit positions (and corresponding values) in gridnum are + * 876 256 128 64 + * 504 = 32 1 16 + * 321 8 4 2 + * Thus, for example (*=pixel set/black, -=pixel not set/white), + * *-- *-- -** (note that 209 is the + * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset, + * --* *** --- of 302) + * o A set pixel is considered black, an unset pixel considered + * white. + * o Ignoring whether the center pixel is set or unset, and + * taking rotation, reflection and inversion (set<-->unset) + * symmetries into account, there are 32 unique pixel patterns. + * If inversions are listed separately, there are 51 patterns. + * o Here are the 51 unique patterns, with ? always denoting the + * undetermined center pixel. At the upper-left corner of each + * pattern is the "pattern index number" assigned to it in this + * function. At the upper-right is the pattern's multiplicity, + * i.e., the number of different patterns obtained by rotations + * and reflection of the illustrated one. Inverse patters are + * illustrated immediately beneath the original (the first three + * four-pixel patterns have identical inverses). + * ------------------------------------------------------------- + * No pixels set: + * #1 1 (in this case, 1 signifies that rotation + * --- and reflection give no different grids) + * -?- + * --- + * Inverse, all eight pixels set + * #2 1 (the inverse multiplicity is always the same) + * *** + * *?* + * *** + * ------------------------------------------------------------- + * One pixel set: + * #3 4 #4 4 + * *-- -*- + * -?- -?- + * --- --- + * Inverse, seven pixels set: + * #5 4 #6 4 + * -** *-* + * *?* *?* + * *** *** + * ------------------------------------------------------------- + * Two pixels set: + * #7 8 #8 4 #9 8 10 2 11 4 12 2 + * **- *-* *-- *-- -*- -*- + * -?- -?- -?* -?- -?* -?- + * --- --- --- --* --- -*- + * Inverse, six pixels set: + * #13 8 14 4 15 8 16 2 17 4 18 2 + * --* -*- -** -** *-* *-* + * *?* *?* *?- *?* *?- *?* + * *** *** *** **- *** *-* + * ------------------------------------------------------------- + * Three pixels set: + * #19 4 20 8 21 8 22 8 23 8 24 4 25 4 26 4 27 4 28 4 + * *** **- **- **- **- **- *-* *-* -*- -*- + * -?- -?* -?- -?- -?- *?- -?- -?- -?* -?* + * --- --- --* -*- *-- --- --* -*- -*- *-- + * Inverse, five pixels set: + * #29 4 30 8 31 8 32 8 33 8 34 4 35 4 36 4 37 4 38 4 + * --- --* --* --* --* --* -*- -*- *-* *-* + * *?* *?- *?* *?* *?* -?* *?* *?* *?- *?- + * *** *** **- *-* -** *** **- *-* *-* -** + * ------------------------------------------------------------- + * Four pixels set (including inverses): + * #39 8 40 4 41 8 42 8 43 4 44 4 45 8 46 1 + * *** **- **- *** *** **- **- *-* + * -?* -?- -?* -?- -?- -?* -?* -?- + * --- -** *-- --* -*- --* -*- *-* + * + * #47 8 48 4 49 4 50 8 51 1 + * --- --- --* --* -*- + * *?* *?* *?- *?- *?* + * **- *-* **- *-* -*- + * ======================================================================= */ +/* --- entry point --- */ +int aapatternnum ( int gridnum ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int pattern = (-1); /*pattern#, 1-51, for input gridnum*/ +/* --- + * pattern number corresponding to input gridnum/2 code + * ( gridnum/2 strips off gridnum's low bit because it's + * the same pattern whether or not center pixel is set ) + * --- */ +static int patternnum[] = { + 1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39, /* 0- 15 */ + 4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29, /* 16- 31 */ + 3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34, /* 32- 47 */ + 9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13, /* 48- 63 */ + 4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33, /* 64- 79 */ + 11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14, /* 80- 95 */ + 7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13, /* 96-111 */ + 20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5, /* 112-127 */ + 3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30, /* 128-143 */ + 7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13, /* 144-159 */ + 8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17, /* 160-175 */ + 23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6, /* 176-191 */ + 7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15, /* 192-207 */ + 24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5, /* 208-223 */ + 19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6, /* 224-239 */ + 39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2, /* 240-255 */ + -1 } ; /* --- end-of-patternnum[] --- */ +/* ------------------------------------------------------------------------- +look up pattern number for gridnum +-------------------------------------------------------------------------- */ +/* --- first check input --- */ +if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */ +/* --- look up pattern number, 1-51, corresponding to input gridnum --- */ +pattern = patternnum[gridnum/2]; /* /2 strips off gridnum's low bit */ +if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */ +end_of_job: + return ( pattern ); /* back to caller with pattern# */ +} /* --- end-of-function aapatternnum() --- */ + + +/* ========================================================================== + * Function: aalookup ( gridnum ) + * Purpose: Looks up the grayscale value 0=white to 255=black + * corresponding to the 3x3 pixel grid coded by gridnum 0=no + * pixels set (white) to 511=all pixels set (black). + * -------------------------------------------------------------------------- + * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid + * -------------------------------------------------------------------------- + * Returns: ( int ) 0=white to 255=black, or -1=error + * -------------------------------------------------------------------------- + * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid + * o A set pixel is considered black, an unset pixel considered + * white. Likewise, the returned grayscale is 255 for black, + * 0 for white. You'd more typically want to use 255-grayscale + * so that 255 is white and 0 is black. + * o The returned number is the (lowpass) antialiased grayscale + * for the center pixel (gridnum bit 0) of the grid. + * ======================================================================= */ +/* --- entry point --- */ +int aalookup ( int gridnum ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int grayscale = (-1); /*returned grayscale, init for error*/ +int pattern = (-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/ +int iscenter = gridnum&1; /*low-order bit set for center pixel*/ +/* --- gray scales --- */ +#define WHT 0 +#define LGT 64 +#define GRY 128 +#define DRK 192 +#define BLK 255 +#if 1 +/* --- + * modified aapnm() grayscales (second try) + * --- */ +/* --- grayscale for each pattern when center pixel set/black --- */ +static int grayscale1[] = { -1, /* [0] index not used */ + BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160, /* 1-10 */ +/* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */ + BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */ + BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */ + 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */ + 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */ + -1 } ; /* --- end-of-grayscale1[] --- */ +/* --- grayscale for each pattern when center pixel not set/white --- */ +static int grayscale0[] = { -1, /* [0] index not used */ + WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */ + 64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */ + /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */ + WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */ + 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */ + 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */ + -1 } ; /* --- end-of-grayscale0[] --- */ +#endif +#if 0 +/* --- + * modified aapnm() grayscales (first try) + * --- */ +/* --- grayscale for each pattern when center pixel set/black --- */ +static int grayscale1[] = { -1, /* [0] index not used */ + BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK, /* 1-10 */ +/* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */ + BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */ + BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191, /* 21-30 */ +/* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */ /* 21-30 */ + 204,BLK,204,BLK,217,204,BLK,191,GRY,BLK, /* 31-40 */ +/* 204,BLK,204,191,217,204,BLK,191,178,BLK, */ /* 31-40 */ + 178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204, /* 41-51 */ +/* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */ + -1 } ; /* --- end-of-grayscale1[] --- */ +/* --- grayscale for each pattern when center pixel not set/white --- */ +static int grayscale0[] = { -1, /* [0] index not used */ + WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */ + GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY, /* 11-20 */ +/* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */ + WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89, /* 21-30 */ +/* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */ /* 21-30 */ + 102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT, /* 31-40 */ +/* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */ /* 31-40 */ + 76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */ +/* 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */ + -1 } ; /* --- end-of-grayscale0[] --- */ +#endif +#if 0 +/* --- + * these grayscales _exactly_ correspond to the aapnm() algorithm + * --- */ +/* --- grayscale for each pattern when center pixel set/black --- */ +static int grayscale1[] = { -1, /* [0] index not used */ + BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, /* 1-10 */ + BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */ + BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */ + 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */ + 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */ + -1 } ; /* --- end-of-grayscale1[] --- */ +/* --- grayscale for each pattern when center pixel not set/white --- */ +static int grayscale0[] = { -1, /* [0] index not used */ + WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */ + 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */ + WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */ + 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */ + 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */ + -1 } ; /* --- end-of-grayscale0[] --- */ +#endif +/* ------------------------------------------------------------------------- +look up grayscale for gridnum +-------------------------------------------------------------------------- */ +/* --- first check input --- */ +if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */ +/* --- look up pattern number, 1-51, corresponding to input gridnum --- */ +pattern = aapatternnum(gridnum); /* look up pattern number */ +if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */ +if ( ispatternnumcount ) { /* counts being accumulated */ + if (iscenter) patternnumcount1[pattern] += 1; /* bump diagnostic count */ + else patternnumcount0[pattern] += 1; } +/* --- look up grayscale for this pattern --- */ +grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] ); +end_of_job: + return ( grayscale ); /* back to caller with grayscale */ +} /* --- end-of-function aalookup() --- */ + + +/* ========================================================================== + * Function: aalowpasslookup ( rp, bytemap, grayscale ) + * Purpose: calls aalookup() for each pixel in rp->bitmap + * to create anti-aliased bytemap + * -------------------------------------------------------------------------- + * Arguments: rp (I) raster * to raster whose bitmap + * is to be anti-aliased + * bytemap (O) intbyte * to bytemap, calculated + * by calling aalookup() for each pixel + * in 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 + * ======================================================================= */ +/* --- entry point --- */ +int aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int width=rp->width, height=rp->height, /* width, height of raster */ + icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */ +int bgbitval=0 /*, fgbitval=1*/; /* background, foreground bitval */ +int bitval=0, /* value of rp bit at irow,icol */ + aabyteval=0; /* antialiased (or unchanged) value*/ +int gridnum=0, aagridnum(), /* grid# for 3x3 grid at irow,icol */ + aalookup(); /* table look up antialiased value*/ +/* ------------------------------------------------------------------------- +generate bytemap by table lookup for each pixel of bitmap +-------------------------------------------------------------------------- */ +for ( irow=0; irow=0 && aabyteval<=255 ) /* check for success */ + bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */ + } /* --- end-of-for(irow,icol) --- */ +ispatternnumcount = 0; /* accumulate counts only once */ +/* ------------------------------------------------------------------------- +Back to caller with gray-scale anti-aliased bytemap +-------------------------------------------------------------------------- */ +/*end_of_job:*/ + return ( 1 ); +} /* --- end-of-function aalowpasslookup() --- */ + + +/* ========================================================================== * Function: aasupsamp ( rp, aa, sf, grayscale ) * Purpose: calculates a supersampled anti-aliased bytemap * for rp->bitmap, with each byte 0...grayscale-1 @@ -9469,7 +14962,7 @@ end_of_job: * 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[] + * of bytemap (where values are replaced by colors[] * indexes) in colormap[]. * -------------------------------------------------------------------------- * Arguments: bytemap (I) intbyte * to bytemap containing @@ -9480,7 +14973,8 @@ end_of_job: * (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 + * values in bytemap, in ascending value order, + * and with gamma correction applied * colormap (O) intbyte * returning a bytemap "image", * i.e., in one-to-one pixel correspondence * with bytemap, but where the values have been @@ -9501,7 +14995,8 @@ int ncolors = 0, /* #different values 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*/ +int isscale = 0, /* true to scale largest val to 255*/ + isgamma = 1; /* true to apply gamma correction */ int maxcolors = 0; /* maximum ncolors */ /* ------------------------------------------------------------------------- Accumulate colors[] from values occurring in bytemap @@ -9532,6 +15027,20 @@ if ( isscale ) /* only rescale if req { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5)); if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); } } /* --- end-of-if(isscale) --- */ +/* --- apply gamma correction --- */ +if ( isgamma /* only gamma correct if requested */ +&& gammacorrection > 0.0001 ) /* and if we have gamma correction */ + if ( ncolors > 1 ) /* and if not a "blank" raster */ + if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/ + { + for ( igray=1; igray= 0 ) msglevel = newmsglevel; +if ( newmsgfp != NULL ) msgfp = newmsgfp; +return ( 1 ); +} /* --- end-of-function mimetexsetmsg() --- */ #endif /* PART3 */ /* --- @@ -9805,6 +15341,9 @@ globals for gif and png callback functio -------------------------------------------------------------------------- */ GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */ GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */ +GLOBAL(int,raster_width,0); /* width of final/displayed image */ +GLOBAL(int,raster_height,0); /* height of final/displayed image */ +GLOBAL(int,raster_baseline,0); /* baseline of final/displayed image*/ /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */ #ifdef AA /* if anti-aliasing requested */ #define ISAAVALUE 1 /* turn flag on */ @@ -9844,31 +15383,6 @@ STATIC logdata mimelog[] #endif ; -/* ------------------------------------------------------------------------- -messages --------------------------------------------------------------------------- */ -static char *copyright = /* copyright, gnu/gpl notice */ - "+-----------------------------------------------------------------------+\n" - "|mimeTeX vers 1.61, Copyright(c) 2002-2005, 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" - "+-----------------------------------------------------------------------+"; -static int maxmsgnum = 2; /* maximum msgtable[] index */ -static char *msgtable[] = { /* messages referenced by [index] */ - "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */ - "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~" - "on~your~own~server.\\\\Thank~you,~John~Forkosh}}", - "\\red\\small\\rm\\fbox{\\array{" /* [1] */ - "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\" - "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\" - "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}", - "\\red\\small\\rm\\fbox{\\array{" /* [2] */ - "The~public~mimetex~server~is~for~testing.~~For~production,\\\\" - "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\" - "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}", - NULL } ; /* trailer */ - /* --- entry point --- */ int main ( int argc, char *argv[] @@ -9881,7 +15395,7 @@ 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 */ +static char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */ char *expression = exprbuffer; /* ptr to expression */ int size = NORMALSIZE; /* default font size */ char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */ @@ -9890,24 +15404,36 @@ int unescape_url(); /* convert %xx's t int emitcache(); /* emit cached image if it exists */ int isquery = 0, /* true if input from QUERY_STRING */ isqempty = 0, /* true if query string empty */ + isqforce = 0, /* true to force query emulation */ isqlogging = 0, /* true if logging in query mode */ isformdata = 0, /* true if input from html form */ - isdumpimage = 0; /* true to dump image on stdout */ + isinmemory = 1, /* true to generate image in memory*/ + isdumpimage = 0, /* true to dump image on stdout */ + isdumpbuffer = 0; /* true to dump to memory buffer */ /* --- rasterization --- */ -subraster *rasterize(), *sp; /* rasterize expression */ -raster *border_raster(), *bp; /* put a border around raster */ +subraster *rasterize(), *sp=NULL; /* rasterize expression */ +raster *border_raster(), *bp=NULL; /* put a border around raster */ +int delete_subraster(); /* for clean-up at end-of-job */ 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 */ +char *inputreferer = INPUTREFERER; /*http_referer's permitted to \input*/ +int reflevels = REFLEVELS, urlncmp(); /* cmp http_referer,server_name */ +int strreplace(); /* replace SERVER_NAME in errmsg */ +char *urlprune(); /* prune referer_match */ struct { char *referer; int msgnum; } /* http_referer can't contain this */ denyreferer[] = { /* referer table to deny access to */ #ifdef DENYREFERER #include DENYREFERER /* e.g., {"",1}, for no referer */ #endif { NULL, -999 } }; /* trailer */ -char *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */ -int ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1)); +char *http_referer = getenv("HTTP_REFERER"), /* referer using mimeTeX */ + *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */ + *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */ + *referer_match = (!isempty(http_host)?http_host: /*match http_host*/ + (!isempty(server_name)?server_name:(NULL))); /* or server_name */ +int ishttpreferer = (isempty(http_referer)?0:1); int isstrstr(); /* search http_referer for referer */ int isinvalidreferer = 0; /* true for inavlid referer */ int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/ @@ -9918,9 +15444,15 @@ int norefmaxlen = NOREFMAXLEN; /*max que void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */ #endif char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */ + gif_buffer[MAXGIFSZ] = "\000", /* or gif written in memory buffer */ cachefile[256] = "\000", /* full path and name to cache file*/ *md5str(); /* md5 has of expression */ int maxage = 7200; /* max-age is two hours */ +int valign = (-9999); /*Vertical-Align:baseline-(height-1)*/ +/* --- pbm/pgm (-g switch) --- */ +int ispbmpgm = 0; /* true to write pbm/pgm file */ +int type_pbmpgm(), ptype=0; /* entry point, graphic format */ +char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */ /* --- anti-aliasing --- */ intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */ colors[256]; /* grayscale vals in bytemap */ @@ -9928,16 +15460,23 @@ int aalowpass(), aapnm(), /*lowpass fil grayscale = 256; /* 0-255 grayscales in 8-bit bytes */ int ncolors=2, /* #colors (2=b&w) */ aacolormap(); /* build colormap from bytemap */ +int ipattern; /*patternnumcount[] index diagnostic*/ +/* --- advertisement preprocessing --- */ +int advertisement(), crc16(); /*wrap expression in advertisement*/ +char *adtemplate = NULL; /* usually use default message */ +char *host_showad = HOST_SHOWAD; /* show ads only on this host */ /* --- messages --- */ char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/ cachelog[256] = CACHELOG; /* cached image log in cachepath/ */ char *timestamp(); /* time stamp for logged messages */ +char *strdetex(); /* remove math chars from messages */ int logger(); /* logs environ variables */ int ismonth(); /* check argv[0] for current month */ char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */ char *dashes = /* separates logfile entries */ "--------------------------------------------------------------------------"; -char *invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */ +char *invalid_referer_msg = msgtable[invmsgnum]; /*msg to invalid referer*/ +char *invalid_referer_match = msgtable[refmsgnum]; /*referer isn't host*/ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ @@ -9946,24 +15485,52 @@ initialization system(SYSTEM); #endif /* --- set global variables --- */ +daemonlevel++; /* signal other funcs to reset */ msgfp = stdout; /* for comamnd-line mode output */ isss = issupersampling; /* set supersampling flag */ +isemitcontenttype = 1; /* true to emit mime content-type */ +iscachecontenttype = 0; /* true to cache mime content-type */ +*contenttype = '\000'; /* reset content-type:, etc. cache */ +isnomath = 0; /* true to inhibit math mode */ +seclevel = SECURITY; /* overall security level */ +inputseclevel = INPUTSECURITY; /* security level for \input{} */ +counterseclevel = COUNTERSECURITY; /* security level for \counter{} */ +environseclevel = ENVIRONSECURITY; /* security level for \environ */ +ninputcmds = 0; /* reset count of \input commands */ +exitstatus=0; errorstatus=ERRORSTATUS; /* reset exit/error status */ +iscaching = ISCACHING; /* true if caching images */ +if ( iscaching ) { /* images are being cached */ + strcpy(cachepath,CACHEPATH); /* relative path to cached files */ + if ( *cachepath == '%' ) { /* leading % signals cache headers */ + iscachecontenttype = 1; /* signal caching mime content-type*/ + strsqueeze(cachepath,1); } } /* and squeeze out leading % char */ +gifSize = 0; /* signal that image not in memory */ +fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */ +bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */ shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */ +for ( ipattern=1; ipattern<=51; ipattern++ ) + patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0; /* --- * 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,16384); /* so use it as expression */ - expression[16384] = '\000'; /* make sure it's null terminated */ - isquery = 1; } /* and set isquery flag */ -if ( !isquery ) /* empty query string */ - { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */ - *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR"); - if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */ - { isquery = 1; /* set flag to signal query */ - strcpy(expression,"\\red\\small\\rm~missing~query~string"); } - isqempty = 1; /* signal empty query string */ + if ( strlen(query) >= 1 ) { /* caller gave us a query string */ + strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */ + expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */ + if ( 0 ) /*true to remove leading whitespace*/ + while ( isspace(*expression) && *expression!='\000' ) + {strsqueeze(expression,1);} /* squeeze out white space */ + isquery = 1; } /* and set isquery flag */ +if ( !isquery ) { /* empty query string */ + char *host = getenv("HTTP_HOST"), /* additional getenv("") results */ + *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR"); + if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */ + isquery = 1; /* set flag to signal query */ + if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */ + strcpy(expression, /* and give user an error message */ + "\\red\\small\\rm\\fbox{\\begin{gather}\\LaTeX~expression~not~supplied" + "\\\\i.e.,~no~?query\\_string~given~to~mimetex.cgi\\end{gather}}"); } + isqempty = 1; /* signal empty query string */ } /* --- end-of-if(!isquery) --- */ /* --- * process command-line input args (if not a query) @@ -9997,23 +15564,31 @@ if ( !isquery /* don't have an html q if ( !isstopsignal /* haven't seen stopsignal switch */ && *argv[argnum] == '-' ) /* and have some '-' switch */ { - char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */ - int arglen = strlen(argv[argnum]) - 1; /* #chars following - */ + char *field = argv[argnum] + 1; /* ptr to char(s) following - */ + char flag = tolower(*field); /* single char following '-' */ + int arglen = strlen(field); /* #chars following - */ argnum++; /* arg following flag/switch is usually its value */ nswitches++; /* another switch on command line */ - if ( isstrict && arglen!=1 ) /* only single-char switch allowed */ + if ( isstrict && /* if strict checking then... */ + !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/ { nbadargs++; argnum--; } /* so ignore longer -xxx switch */ else /* process single-char -x switch */ switch ( flag ) { /* see what user wants to tell us */ /* --- ignore uninterpreted flag --- */ default: nbadargs++; argnum--; break; /* --- adjustable program parameters (not checking input) --- */ + case 'b': isdumpimage++; isdumpbuffer++; argnum--; break; case 'd': isdumpimage++; argnum--; break; case 'e': isdumpimage++; gif_outfile=argv[argnum]; break; case 'f': isdumpimage++; infilearg=argnum; break; - case 'm': msglevel = atoi(argv[argnum]); break; - case 'o': istransparent = 0; argnum--; break; - case 's': size = atoi(argv[argnum]); break; + case 'g': ispbmpgm++; + if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */ + if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/ + else pbm_outfile = argv[argnum]; break; /*next arg is filename*/ + case 'm': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break; + case 'o': istransparent = (istransparent?0:1); argnum--; break; + case 'q': isqforce = 1; argnum--; break; + case 's': if ( argnum < argc ) size = atoi(argv[argnum]); break; } /* --- end-of-switch(flag) --- */ } /* --- end-of-if(*argv[argnum]=='-') --- */ else /* expression if arg not a -flag */ @@ -10024,8 +15599,10 @@ if ( !isquery /* don't have an html q else nbadargs++; /* infile and expression invalid */ } /* --- end-of-while(argc>++argnum) --- */ if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */ - fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n", - argc,progname,nargs,nbadargs); + { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n", + argc,progname,nargs,nbadargs); + fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n", + cachepath,pathprefix); } /* --- * decide whether command-line input overrides query_string * -------------------------------------------------------- */ @@ -10040,8 +15617,8 @@ if ( !isquery /* don't have an html q && 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],16384); /*expression from command-line*/ - expression[16384] = '\000'; /* make sure it's null terminated */ + { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/ + expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */ isquery = 0; } /* and not from a query_string */ /* --- * or read expression from input file @@ -10050,31 +15627,57 @@ if ( !isquery /* don't have an html q { 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 */ + { char instring[MAXLINESZ+1]; /* line from file */ + int exprsz = 0; /* total #bytes read 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*/ + while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/ + if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */ + strcat(expression,instring); /* concat line to end of expression*/ + exprsz += strlen(instring); } /* update expression buffer length */ fclose ( infile ); } /*close input file after reading expression*/ } /* --- end-of-if(infilearg>0) --- */ + /* --- + * xlate +++'s to blanks only if query + * ----------------------------------- */ + if ( !isquery ) isplusblank = 0; /* don't xlate +++'s to blanks */ + /* --- + * check if emulating query (for testing) + * -------------------------------------- */ + if ( isqforce ) isquery = 1; /* emulate query string processing */ + /* --- + * check if emitting pbm/pgm graphic + * --------------------------------- */ + if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */ + if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */ + if ( *gif_outfile != '\000' ) /* make sure string isn't empty */ + { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */ + gif_outfile = (char *)NULL; /* reset gif output file */ + /*isdumpimage--;*/ } /* and decrement -e count */ } /* --- end-of-if(!isquery) --- */ /* --- * check for
input * ---------------------- */ -if ( isquery ) /* must be */ +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 */ - strcpy(expression,delim+1); /* so shift name= out of expression*/ + {strsqueezep(expression,delim+1);} /* so shift name= out */ while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/ *delim = ' '; /* is "shorthand" for blank space */ /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */ unescape_url(expression,0); /* convert all %xx's to chars */ unescape_url(expression,0); /* repeat */ - msglevel = FORMLEVEL; /* msglevel for forms */ + if(0) 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 */ + unescape_url(expression,0); } /* convert _all_ %xx's to chars */ +/* --- + * check queries for prefixes/suffixes/embedded that might cause problems + * ---------------------------------------------------------------------- */ +/* --- expression whose last char is \ --- */ +if ( lastchar(expression) == '\\' ) /* last char is backslash */ + strcat(expression," "); /* assume "\ " lost the final space*/ /* --- * check queries for embedded prefixes signalling special processing * ----------------------------------------------------------------- */ @@ -10087,7 +15690,7 @@ if ( isquery ) /* only check queries { *delim = '\000'; /* replace delim with null */ if ( seclevel <= 9 ) /* permit msglevel specification */ msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */ - strcpy(expression,delim+1); } } /* shift out prefix and delim */ + strsqueezep(expression,delim+1); } } /* squeeze out prefix & 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=*/ @@ -10095,7 +15698,7 @@ if ( isquery ) /* only check queries { *delim = '\000'; /* replace delim with null */ if ( seclevel <= 3 ) /* permit logfile specification */ strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */ - strcpy(expression,delim+1); } } /* shift out prefix and delim */ + strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */ } /* --- end-of-if(isquery) --- */ /* --- * log query (e.g., for debugging) @@ -10104,7 +15707,7 @@ if ( isquery ) /* only log query_stri if ( msglevel >= LOGLEVEL /* check if logging */ && seclevel <= 5 ) /* and if logging permitted */ if ( logfile != NULL ) /* if a logfile is given */ - if ( *logfile != '\000' ) /*and if it's not an empty string*/ + 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 */ { @@ -10155,27 +15758,49 @@ if ( isquery ) /* only log query_stri isqlogging = 1; /* set query logging flag */ } /* --- end-of-if(msglevel>=LOGLEVEL) --- */ else /* couldn't open logfile */ - msglevel = 0; /* can't emit messages */ + msglevel = 0; } /* can't emit messages */ /* --- * prepend prefix to submitted expression * -------------------------------------- */ if ( 1 || isquery ) /* queries or command-line */ if ( *exprprefix != '\000' ) /* we have a prefix string */ { int npref = strlen(exprprefix); /* #chars in prefix */ - memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/ + memmove(expression+npref+1,expression,strlen(expression)+1);/*make room*/ memcpy(expression,exprprefix,npref); /* copy prefix into expression */ expression[npref] = '{'; /* followed by { */ strcat(expression,"}"); } /* and terminating } to balance { */ /* --- - * 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 ( strcmp(referer,"month") != 0 ) /* nor if it's *only* "month" */ - if ( http_referer != NULL ) /* nor if called "standalone" */ - if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */ - { expression = invalid_referer_msg; /* so give user error message */ - isinvalidreferer = 1; } /* and signal invalid referer */ + * check if http_referer is allowed to use this image and to use \input{} + * ---------------------------------------------------------------------- */ +if ( isquery ) { /* not relevant if "interactive" */ + /* --- check -DREFERER=\"comma,separated,list\" of valid referers --- */ + if ( referer != NULL ) { /* compiled with -DREFERER=\"...\" */ + if ( strcmp(referer,"month") != 0 ) /* but it's *only* "month" signal */ + if ( ishttpreferer ) /* or called "standalone" */ + if ( !isstrstr(http_referer,referer,0) ) { /* invalid http_referer */ + expression = invalid_referer_msg; /* so give user error message */ + isinvalidreferer = 1; } } /* and signal invalid referer */ + else /* compiled without -DREFERER= */ + if ( reflevels > 0 ) { /*match referer unless -DREFLEVELS=0*/ + /* --- check topmost levels of http_referer against http_host --- */ + if ( ishttpreferer /* have http_referer */ + && !isempty(referer_match) ) /* and something to match it with */ + if ( !urlncmp(http_referer,referer_match,reflevels) ) { /*match failed*/ + strcpy(exprbuffer,invalid_referer_match); /* init error message */ + strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */ + strdetex(urlprune(referer_match,reflevels),1),0);/*with referer_match*/ + isinvalidreferer = 1; } /* and signal invalid referer */ + } /* --- end-of-if(reflevels>0) --- */ + /* --- check -DINPUTREFERER=\"comma,separated,list\" of \input users --- */ + inputseclevel = INPUTSECURITY; /* set default input security */ + if ( inputreferer != NULL ) { /* compiled with -DINPUTREFERER= */ + if ( http_referer == NULL ) /* but no http_referer given */ + inputseclevel = (-1); /* unknown user can't \input{} */ + else /*have inputreferer and http_referer*/ + if ( !isstrstr(http_referer,inputreferer,0) ) /*http_referer can't \input*/ + inputseclevel = (-1); /* this known user can't \input{} */ + } /* --- end-of-if(inputreferer!=NULL) --- */ + } /* --- end-of-if(isquery) --- */ /* --- * check if referer contains "month" signal * ---------------------------------------- */ @@ -10215,17 +15840,47 @@ if ( isquery ) /* not relevant if "in if ( isquery ) /* not relevant if "interactive" */ if ( !isinvalidreferer ) /* nor if already invalid referer */ if ( !ishttpreferer ) /* no http_referer supplied */ - if ( strlen(expression) > norefmaxlen ) /* query_string too long */ - { expression = invalid_referer_msg; /* set invalid http_referer message*/ - isinvalidreferer = 1; } /* and signal invalid referer */ + if ( strlen(expression) > norefmaxlen ) { /* query_string too long */ + if ( isempty(referer_match) ) /* no referer_match to display */ + expression = invalid_referer_msg; /* set invalid http_referer message*/ + else { /* error with referer_match display*/ + strcpy(exprbuffer,invalid_referer_match); /* init error message */ + strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */ + strdetex(urlprune(referer_match,reflevels),1),0); } /*with host_http*/ + isinvalidreferer = 1; } /* and signal invalid referer */ /* --- - * check for image caching - * ----------------------- */ -if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */ -|| strstr(expression,"\\input") != NULL /* can't cache \input{} */ -|| strstr(expression,"\\nocach") != NULL /* no caching requested */ + * check for "advertisement" + * ------------------------- */ +/* --- check if advertisement messages only for one particular host --- */ +if ( !isempty(host_showad) ) /* messages only for this referer */ + if ( !isempty(referer_match) ) /* have HTTP_HOST or SERVER_NAME */ + if ( strstr(referer_match,host_showad) /* see if this host sees ad */ + == NULL ) /* not mimetex host for ad */ + adfrequency = 0; /* turn off advertisements */ +/* --- check for advertisement directive (\advertisement) --- */ +if ( strreplace(expression,"\\advertisement","",0) /*remove \advertisement*/ +>= 1 ) adfrequency = 1; /* force advertisement display */ +if ( adfrequency > 0 ) { /* advertising enabled */ + int npump = crc16(expression)%16; /* #times, 0-15, to pump rand() */ + srand(atoi(timestamp(TZDELTA,4))); /* init rand() with mmddhhmmss */ + while ( npump-- >= 0 ) rand(); /* pre-pump rand() before use */ + if ( (1+rand())%adfrequency == 0 ) { /* once every adfrequency calls */ + advertisement(expression,adtemplate); } } /*wrap expression in advert*/ +/* --- + * check for image caching (and whether or not caching content type) + * ----------------------------------------------------------------- */ +if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */ +|| strstr(expression,"\\input") != NULL /* can't cache \input{} */ +|| strstr(expression,"\\today") != NULL /* can't cache \today */ +|| strstr(expression,"\\calendar") != NULL /* can't cache \calendar */ +|| strstr(expression,"\\nocach") != NULL /* no caching requested */ +|| isformdata /* don't cache user form input */ ) { iscaching = 0; /* so turn caching off */ - maxage = 2; } /* and set max-age to two seconds */ + maxage = 5; } /* and set max-age to 5 seconds */ +if ( strstr(expression,"\\depth") != NULL ) /* cache content-type lines */ + iscachecontenttype = 1; /* set flag to cache content-type */ +if ( strstr(expression,"\\nodepth") != NULL ) /* don't cache content-type */ + iscachecontenttype = 0; /*set flag to not cache content-type*/ if ( isquery ) /* don't cache command-line images */ if ( iscaching ) /* image caching enabled */ { @@ -10239,11 +15894,14 @@ if ( isquery ) /* don't cache command strcat(cachefile,md5hash); /* add md5 hash of expression */ strcat(cachefile,".gif"); /* finish with .gif extension */ gif_outfile = cachefile; /* signal GIF_Create() to cache */ - /* --- (always) emit mime content-type line --- */ - fprintf( stdout, "Cache-Control: max-age=%d\n",maxage ); - fprintf( stdout, "Content-type: image/gif\n\n" ); + /* --- emit mime content-type line --- */ + if ( 0 && isemitcontenttype ) /* now done in emitcache() */ + { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage ); + if ( abs(valign) < 999 ) /* have vertical align */ + fprintf( stdout, "Vertical-Align: %d\n",valign ); + fprintf( stdout, "Content-type: image/gif\n\n" ); } /* --- emit cached image if it already exists --- */ - if ( emitcache(cachefile) > 0 ) /* cached image emitted */ + if ( emitcache(cachefile,maxage,valign,0) > 0 ) /* cached image emitted */ goto end_of_job; /* so nothing else to do */ /* --- log caching request --- */ if ( msglevel >= 1 /* check if logging */ @@ -10258,7 +15916,7 @@ if ( isquery ) /* don't cache command != NULL ) /* ignore logging if can't open */ { int isreflogged = 0; /* set true if http_referer logged */ fprintf(filefp,"%s %s\n", /* timestamp, md5 file */ - timestamp(),cachefile+strlen(cachepath)); /* (path not shown) */ + timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/ fprintf(filefp,"%s\n",expression); /* expression in filename */ if ( http_referer != NULL ) /* show referer if we have one */ if ( *http_referer != '\000' ) /* and if not an empty string*/ @@ -10280,31 +15938,72 @@ if ( isquery ) /* don't cache command * emit copyright, gnu/gpl notice (if "interactive") * ------------------------------------------------- */ if ( !isdumpimage ) /* don't mix ascii with image dump */ - if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */ - fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */ + if ( (!isquery||isqlogging) && msgfp!=NULL ) { /* called from command line */ + fprintf(msgfp,"%s\n%s\n",copyright1,copyright2); /* display copyright */ + fprintf(msgfp,"Most recent revision: %s\n",REVISIONDATE); /*revision date*/ + } /* --- end-of-if(!isquery...) --- */ /* ------------------------------------------------------------------------- rasterize expression and put a border around it -------------------------------------------------------------------------- */ /* --- preprocess expression, converting LaTeX constructs for mimeTeX --- */ -expression = mimeprep(expression); /* preprocess expression */ +if ( expression != NULL ) { /* have expression to rasterize */ + expression = mimeprep(expression); } /* preprocess expression */ /* --- double-check that we actually have an expression to rasterize --- */ -if ( expression == NULL ) /* nothing to rasterize */ - { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/ - fprintf(msgfp,"No expression to rasterize\n"); - goto end_of_job; } /* and then quit */ +if ( expression == NULL ) { /* nothing to rasterize */ + if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/ + if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/ + if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus); + fprintf(msgfp,"No LaTeX expression to rasterize\n"); } + goto end_of_job; } /* and then quit */ /* --- rasterize expression --- */ -if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */ - { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/ - fprintf(msgfp,"Failed to rasterize %s\n",expression); - if ( isquery ) sp = rasterize( /* or emit error raster if query */ - "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1); - if ( sp == NULL ) goto end_of_job; } /* re-check for failure */ +if ( (sp = rasterize(expression,size)) == NULL ) { /* failed to rasterize */ + if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/ + if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/ + if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus); + fprintf(msgfp,"Failed to rasterize %.2048s\n",expression); } + if ( isquery ) { /* try to display failed expression*/ + char errormsg[4096]; /* buffer for failed expression */ + strcpy(errormsg, /* init error message */ + "\\red\\fbox{\\begin{gather}" + "{\\rm~mi\\underline{meTeX~failed~to~render~your~expressi}on}\\\\[5]"); + strcat(errormsg,"{\\rm\\hspace{10}{"); /*render expression as \rm*/ + strcat(errormsg,strdetex(expression,0));/*add detexed expression to msg*/ + strcat(errormsg,"}\\hspace{10}}\\end{gather}}"); /* finish up */ + if ( (sp = rasterize(errormsg,1)) == NULL ) /*couldn't rasterize errmsg*/ + sp = rasterize( /* so rasterize generic error */ + "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1); } + if ( sp == NULL ) goto end_of_job; /* re-check for err message failure*/ + magstep = 1; /* don't magstep error msgs */ + } /* --- end-of-if((sp=rasterize())==NULL) --- */ +/* --- magnify entire image here if we need >>bit< 1 && magstep <= 10 ) { /* magnify entire bitmap image */ + raster *rastmag(), *magrp=NULL; /* bitmap magnify function */ + int baseline = sp->baseline; /* original image baseline */ + magrp = rastmag(sp->image,magstep); /* magnify raster image */ + if ( magrp != NULL ) { /* succeeded to magnify image */ + delete_raster(sp->image); /* free original raster image */ + sp->image = magrp; /*and replace it with magnified one*/ + /* --- adjust parameters --- */ + baseline *= magstep; /* scale baseline */ + if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */ + sp->baseline = baseline; } /*reset baseline of magnified image*/ + magstep = (-1); /*done, don't also use bytemapmag()*/ + } /* --- end-of-if(magstep) --- */ + } /* --- end-of-if(1||(ispbmpgm&&ptype<2)) --- */ /* ---no border requested, but this adjusts width to multiple of 8 bits--- */ if ( issupersampling ) /* no border needed for gifs */ bp = sp->image; /* so just extract pixel map */ else /* for mime xbitmaps must have... */ 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 */ +raster_width = bp->width; raster_height = bp->height; /* global copy */ +raster_baseline = sp->baseline; /* global copy (not needed) */ +if ( sp!=NULL && bp!=NULL ) { /* have raster */ + valign= raster_baseline -(raster_height -1);/*#pixels for Vertical-Align:*/ + if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */ +if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */ + type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */ /* ------------------------------------------------------------------------- generate anti-aliased bytemap from (bordered) bitmap -------------------------------------------------------------------------- */ @@ -10313,7 +16012,7 @@ if ( isaa ) /* we want anti-aliased b /* --- * allocate bytemap and colormap as per width*height of bitmap * ----------------------------------------------------------- */ - int nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/ + int nbytes = (raster_width)*(raster_height); /*#bytes for byte,colormap*/ if ( isss ) /* anti-aliasing by supersampling */ bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/ else /* need to allocate bytemap */ @@ -10322,9 +16021,6 @@ if ( isaa ) /* we want anti-aliased b else /* anti-aliasing wanted */ if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */ == NULL ) isaa = 0; /* reset flag if malloc failed */ - if ( isaa ) /* have bytemap, 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 * ---------------------------------------------------------- */ @@ -10334,23 +16030,89 @@ if ( isaa ) /* we want anti-aliased b * select anti-aliasing algorithm * ------------------------------ */ if ( !isss ) /* generate bytemap for lowpass */ - if ( aaalgorithm == 1 ) /* 1 for aalowpass() */ - { if ( aalowpass(bp,bytemap_raster,grayscale) /* my lowpass filter */ - == 0 ) isaa = 0; } /*failed, so turn off anti-aliasing*/ - else /* or 2 for aapnm() */ - if ( aaalgorithm == 2 ) /*2 for netpbm pnmalias.c algorithm*/ - { if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */ - == 0 ) isaa = 0; } /*failed, so turn off anti-aliasing*/ - else isaa = 0; /* unrecognized algorithm */ + switch ( aaalgorithm ) { /* choose antialiasing algorithm */ + default: isaa = 0; break; /* unrecognized algorithm */ + case 1: /* 1 for aalowpass() */ + if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/ + == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/ + break; + case 2: /*2 for netpbm pnmalias.c algorithm*/ + if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */ + == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/ + break; + case 3: /*3 for aapnm() based on aagridnum()*/ + if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */ + == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/ + break; + case 4: /* 4 for aalookup() table lookup */ + if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */ + == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/ + break; + } /* --- end-of-switch(aaalgorithm) --- */ + /* --- + * emit aalookup() pattern# counts/percents diagnostics + * ---------------------------------------------------- */ + if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/ + int pcount0=0, pcount1=0; /* init total w,b center counts */ + for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/ + if ( ipattern > 1 ) /* ignore all-white squares */ + pcount0 += patternnumcount0[ipattern]; /* bump total white centers */ + pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */ + if ( pcount0+pcount1 > 0 ) /* have pcounts (using aalookup) */ + fprintf(msgfp, " aalookup() patterns excluding#1 white" + " (%%'s are in tenths of a percent)...\n"); + for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/ + int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern]; + if ( tot > 0 ) /* this pattern occurs in image */ + fprintf(msgfp, + " pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n", + ipattern, patternnumcount0[ipattern], (ipattern<=1? 999.99: + 1000.*((double)patternnumcount0[ipattern])/((double)pcount0)), + patternnumcount1[ipattern], + 1000.*((double)patternnumcount1[ipattern])/((double)pcount1), + tot, (ipattern<=1? 999.99: + 1000.*((double)tot)/((double)(pcount0+pcount1))) ); } + if ( pcount0+pcount1 > 0 ) /* true when using aalookup() */ + fprintf(msgfp, + "all patterns: %7d +%7d =%7d total pixels\n", + pcount0,pcount1,pcount0+pcount1); } + /* --- + * apply magstep if requested and not already done to bitmap above + * --------------------------------------------------------------- */ + if ( 1 ) { /* or use rastmag() above instead */ + if ( magstep > 1 && magstep <= 10 ) { /*magnify entire bytemap image*/ + intbyte *bytemapmag(), *magmap=NULL; /* bytemap magnify function */ + magmap=bytemapmag(bytemap_raster,raster_width,raster_height,magstep); + if ( magmap != NULL ) { /* succeeded to magnify image */ + free(bytemap_raster); /* free original bytemap image */ + bytemap_raster = magmap; /*and replace it with magnified one*/ + /* --- adjust parameters --- */ + raster_width *= magstep; raster_height *= magstep; /*scale raster*/ + nbytes *= (magstep*magstep); /* scale total image size */ + if ( abs(valign) < 255 ) { /* valign okay */ + valign *= magstep; /* scale by magstep */ + if ( abs(valign) > 512 ) valign = (-9999); } /* sanity check */ + } /* --- end-of-if(magmap!=NULL) --- */ + magstep = (-1); /*done, don't also use bytemapmag()*/ + } /* --- end-of-if(magstep) --- */ + } /* --- end-of-if(1) --- */ /* --- * finally, generate colors and colormap * ------------------------------------- */ - if ( isaa ) { /* we have bytemap_raster */ + if ( isaa ) /* have bytemap, so... */ + if ( (colormap_raster = (intbyte *)malloc(nbytes)) /*malloc colormap*/ + == NULL ) isaa = 0; /* reset flag if malloc failed */ + if ( isaa ) { /* we have byte/colormap_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) --- */ + if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch */ + raster pbm_raster; /*construct arg for write_pbmpgm()*/ + pbm_raster.width = raster_width; pbm_raster.height = raster_height; + pbm_raster.pixsz = 8; pbm_raster.pixmap = (pixbyte *)bytemap_raster; + type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/ } /* --- end-of-if(isaa) --- */ } /* --- end-of-if(isaa) --- */ /* ------------------------------------------------------------------------- @@ -10375,11 +16137,13 @@ if ( (!isquery||isqlogging) || msglevel if ( msgfp!=NULL && 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); } + type_bytemap(bytemap_raster,grayscale, + raster_width,raster_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); + type_bytemap(colormap_raster,ncolors, + raster_width,raster_height,msgfp); /* --- rgb values corresponding to colormap indexes */ fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors); for ( igray=0; igray= 99 ) /* or for debugging */ { int igray = 0; /* grayscale index */ @@ -10400,23 +16164,56 @@ if ( isquery /* called from browser /* ------------------------------------------------------------------------ emit GIF image ------------------------------------------------------------------------- */ + /* --- don't use memory buffer if outout file given --- */ + if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */ + /* --- construct contenttype[] buffer containing mime headers --- */ + if ( 1 ) { /* always construct buffer */ + sprintf( contenttype, "Cache-Control: max-age=%d\n", maxage ); + /*sprintf(contenttype+strlen(contenttype), + "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/ + /*sprintf(contenttype+strlen(contenttype), + "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n");*/ + if ( abs(valign) < 999 ) /* have Vertical-Align: header info*/ + sprintf( contenttype+strlen(contenttype), + "Vertical-Align: %d\n", valign ); + sprintf( contenttype+strlen(contenttype), + "Content-type: image/gif\n\n" ); } /* --- emit mime content-type line --- */ - if ( !isdumpimage /* don't mix ascii with image dump */ - && !iscaching ) /* already emitted if caching */ - { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage ); - /*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" ); } + if ( isemitcontenttype /* content-type lines wanted */ + && !isdumpimage /* don't mix ascii with image dump */ + && !isinmemory /* done below if in memory */ + && !iscaching ) /* done by emitcache() if caching */ + { fputs(contenttype,stdout); } /* emit content-type: header buffer*/ + /* --- write output to memory buffer, possibly for testing --- */ + if ( isinmemory /* want gif written to memory */ + || isdumpbuffer ) /*or dump memory buffer for testing*/ + if ( gif_outfile == NULL ) /* and don't already have a file */ + { *gif_buffer = '\000'; /* init buffer as empty string */ + memset(gif_buffer,0,MAXGIFSZ); /* zero out buffer */ + gif_outfile = gif_buffer; /* and point outfile to buffer */ + if ( isdumpbuffer ) /* buffer dump test requested */ + isdumpbuffer = 999; } /* so signal dumping to buffer */ /* --- initialize gifsave library and colors --- */ if ( msgfp!=NULL && msglevel>=999 ) - fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n", - bp->width,bp->height,ncolors); + { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n", + raster_width,raster_height,ncolors); fflush(msgfp); } while ( 1 ) /* init gifsave lib, and retry if caching fails */ - { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8); + { int status = GIF_Create(gif_outfile, + raster_width,raster_height, ncolors, 8); if ( status == 0 ) break; /* continue if succeeded */ if ( iscaching == 0 ) goto end_of_job; /* quit if failed */ iscaching = 0; /* retry without cache file */ - gif_outfile = (char *)NULL; } /* emit images to stdout */ + isdumpbuffer = 0; /* reset isdumpbuffer signal */ + if ( isquery ) isinmemory = 1; /* force in-memory image generation*/ + if ( isinmemory ) { /* using memory buffer */ + gif_outfile = gif_buffer; /* emit images to memory buffer */ + *gif_outfile = '\000'; } /* empty string signals buffer */ + else { /* or */ + gif_outfile = (char *)NULL; /* emit images to stdout */ + if ( isemitcontenttype ) { /* content-type lines wanted */ + fprintf( stdout, "Cache-Control: max-age=%d\n",maxage ); + fprintf( stdout, "Content-type: image/gif\n\n" ); } } + } /* --- end-of-while(1) --- */ GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */ if ( !isaa ) /* just b&w if not anti-aliased */ { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */ @@ -10441,9 +16238,26 @@ if ( isquery /* called from browser /* --- emit compressed gif image (to stdout or cache file) --- */ GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */ GIF_Close(); /* close file */ - /* --- may need to emit image from cached file --- */ - if ( isquery && iscaching ) /* caching enabled */ - emitcache(cachefile); /* cached image (hopefully) emitted*/ + if ( msgfp!=NULL && msglevel>=9 ) + { fprintf(msgfp,"main> created gifSize=%d\n", gifSize); + fflush(msgfp); } + /* --- may need to emit image from cached file or from memory --- */ + if ( isquery /* have an actual query string */ + || isdumpimage /* or dumping image */ + || msglevel >= 99 ) { /* or debugging */ + int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */ + if ( iscaching ) /* caching enabled */ + emitcache(cachefile,maxage2,valign,0); /*emit cached image (hopefully)*/ + else if ( isinmemory ) /* or emit image from memory buffer*/ + emitcache(gif_buffer,maxage2,valign,1); } /*emitted from memory buffer*/ + /* --- for testing, may need to write image buffer to file --- */ + if ( isdumpbuffer > 99 ) /* gif image in memory buffer */ + if ( gifSize > 0 ) /* and it's not an empty buffer */ + { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */ + if ( dumpfp != NULL ) /* file opened successfully */ + { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/ + fclose(dumpfp); } /* and close file */ + } /* --- end-of-if(isdumpbuffer>99) --- */ #else /* ------------------------------------------------------------------------ emit mime XBITMAP image @@ -10453,83 +16267,80 @@ if ( isquery /* called from browser } /* --- end-of-if(isquery) --- */ /* --- exit --- */ end_of_job: - if ( bytemap_raster != NULL ) free(bytemap_raster); /*free bytemap_raster*/ + if ( !isss ) /*bytemap raster in sp for supersamp*/ + if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/ if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/ + if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */ + if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */ if ( msgfp != NULL /* have message/log file open */ && msgfp != stdout ) /* and it's not stdout */ - { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",timestamp()); + { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n", + timestamp(TZDELTA,0)); fprintf(msgfp,"%s\n",dashes); /* so log separator line */ fclose(msgfp); } /* and close logfile */ - exit ( 0 ); + /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */ + #if defined(_CRTDBG_MAP_ALLOC) + _CrtDumpMemoryLeaks(); + #endif + /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */ + #if !defined(_USRDLL) + if ( errorstatus == 0 ) /*user doesn't want errors signalled*/ + exitstatus = 0; /* so reset error status */ + exit ( exitstatus ); + #endif } /* --- end-of-function main() --- */ /* ========================================================================== - * Function: isstrstr ( char *string, char *snippets, int iscase ) - * Purpose: determine whether any substring of 'string' - * matches any of the comma-separated list of 'snippets', - * ignoring case if iscase=0. - * -------------------------------------------------------------------------- - * Arguments: string (I) char * containing null-terminated - * string that will be searched for - * any one of the specified snippets - * snippets (I) char * containing null-terminated, - * comma-separated list of snippets - * to be searched for in string - * iscase (I) int containing 0 for case-insensitive - * comparisons, or 1 for case-sensitive - * -------------------------------------------------------------------------- - * Returns: ( int ) 1 if any snippet is a substring of - * string, 0 if not - * -------------------------------------------------------------------------- - * Notes: o - * ======================================================================= */ + * Function: CreateGifFromEq ( expression, gifFileName ) + * Purpose: shortcut method to create GIF file for expression, + * with antialising and all other capabilities + * -------------------------------------------------------------------------- + * Arguments: expression (I) char *ptr to null-terminated string + * containing LaTeX expression to be rendred + * gifFileName (I) char *ptr to null-terminated string + * containing name of output gif file + * -------------------------------------------------------------------------- + * Returns: ( int ) exit value from main (0 if successful) + * -------------------------------------------------------------------------- + * Notes: o This function is the entry point when mimeTeX is built + * as a Win32 DLL rather then a standalone app or CGI + * o Contributed to mimeTeX by Shital Shah. See his homepage + * http://www.shitalshah.com + * o Shital discusses the mimeTeX Win32 DLL project at + * http://www.codeproject.com/dotnet/Eq2Img.asp + * and you can download his latest code from + * http://www.shitalshah.com/dev/eq2img_all.zip + * ======================================================================= */ +/* --- include function to expose Win32 DLL to outside world --- */ +#if defined(_USRDLL) + extern _declspec(dllexport)int _cdecl + CreateGifFromEq ( char *expression, char *gifFileName ); +#endif /* --- entry point --- */ -int isstrstr ( char *string, char *snippets, int iscase ) +int CreateGifFromEq ( char *expression, char *gifFileName ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -int status = 0; /*1 if any snippet found in string*/ -char snip[99], *snipptr = snippets, /* munge through each snippet */ - delim = ',', *delimptr = NULL; /* separated by delim's */ -char stringcp[999], *cp = stringcp; /*maybe lowercased copy of string*/ -/* ------------------------------------------------------------------------- -initialization --------------------------------------------------------------------------- */ -/* --- arg check --- */ -if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */ -if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */ -/* --- copy string and lowercase it if case-insensitive --- */ -strcpy(stringcp,string); /* local copy of string */ -if ( !iscase ) /* want case-insensitive compares */ - for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */ - if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/ +int main(); /* main() akways returns an int */ +/* --- set constants --- */ +int argc = 4; /* count of args supplied to main() */ +char *argv[5] = /* command line args to run with -e option */ + { "MimeTeXWin32DLL", "-e", /* constant args */ + /*gifFileName, expression,*/ NULL, NULL, NULL }; +/* --- set argv[]'s not computable at load time --- */ +argv[2] = gifFileName; /* args are -e gifFileName */ +argv[3] = expression; /* and now -e gifFileName expression */ /* ------------------------------------------------------------------------- -extract each snippet and see if it's a substring of string +Run mimeTeX in command-line mode with -e (export) option, and then return -------------------------------------------------------------------------- */ -while ( snipptr != NULL ) /* while we still have snippets */ - { - /* --- extract next snippet --- */ - if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */ - == NULL ) /*not found following last snippet*/ - { strcpy(snip,snipptr); /* local copy of last snippet */ - snipptr = NULL; } /* signal end-of-string */ - else /* snippet ends just before delim */ - { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */ - memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */ - snip[sniplen] = '\000'; /* null-terminated snippet */ - snipptr = delimptr + 1; } /* next snippet starts after delim */ - /* --- lowercase snippet if case-insensitive --- */ - if ( !iscase ) /* want case-insensitive compares */ - for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */ - if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/ - /* --- check if snippet in string --- */ - if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */ - { status = 1; /* so reset return status */ - break; } /* no need to check any further */ - } /* --- end-of-while(*snipptr!=0) --- */ -end_of_job: return ( status ); /*1 if snippet found in list, else 0*/ -} /* --- end-of-function isstrstr() --- */ +return main ( argc, argv + #ifdef DUMPENVP + , NULL + #endif + ) ; +} /* --- end-of-function CreateGifFromEq() --- */ + /* ========================================================================== * Function: ismonth ( char *month ) @@ -10587,59 +16398,6 @@ end_of_job: return ( isokay ); /*1 if month contains current month*/ } /* --- end-of-function ismonth() --- */ -/* ========================================================================== - * Functions: int unescape_url ( char *url, int isescape ) - * char x2c ( char *what ) - * Purpose: unescape_url replaces 3-character sequences %xx in url - * with the single character represented by hex xx. - * x2c returns the single character represented by hex xx - * passed as a 2-character sequence in what. - * -------------------------------------------------------------------------- - * Arguments: url (I) char * containing null-terminated - * string with embedded %xx sequences - * to be converted. - * isescape (I) int containing 1 to _not_ unescape - * \% sequences (0 would be NCSA default) - * what (I) char * whose first 2 characters are - * interpreted as ascii representations - * of hex digits. - * -------------------------------------------------------------------------- - * Returns: ( int ) unescape_url always returns 0. - * ( char ) x2c returns the single char - * corresponding to hex xx passed in what. - * -------------------------------------------------------------------------- - * Notes: o These two functions were taken verbatim from util.c in - * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z - * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03 - * so unescape_url() can be safely applied to input which may or - * may not have been url-encoded. - * ======================================================================= */ -/* --- entry point --- */ -int unescape_url(char *url, int isescape) { - int x=0,y=0,prevescape=0,gotescape=0; - char x2c(); - static char *hex="0123456789ABCDEFabcdef"; - for(;url[y];++x,++y) { - gotescape = prevescape; - prevescape = (url[x]=='\\'); - if((url[x] = url[y]) == '%') - if(!isescape || !gotescape) - if(isthischar(url[y+1],hex) - && isthischar(url[y+2],hex)) - { url[x] = x2c(&url[y+1]); - y+=2; } - } - url[x] = '\0'; - return 0; -} /* --- end-of-function unescape_url() --- */ -/* --- entry point --- */ -char x2c(char *what) { - char digit; - digit = (what[0] >= '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 ) @@ -10669,7 +16427,7 @@ char *value = NULL; /* getenv(name) to /* ------------------------------------------------------------------------- Log each variable -------------------------------------------------------------------------- */ -fprintf(fp,"%s\n",timestamp()); /* emit timestamp before first var */ +fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/ if ( message != NULL ) /* optional message supplied */ fprintf(fp," MESSAGE = %s\n",message); /* emit caller-supplied message */ if ( logvars != (logdata *)NULL ) /* have logvars */ @@ -10685,37 +16443,63 @@ if ( logvars != (logdata *)NULL ) /* hav return ( nlogged ); /* back to caller */ } /* --- end-of-function logger() --- */ + /* ========================================================================== - * Function: emitcache ( cachefile ) + * Function: emitcache ( cachefile, maxage, valign, isbuffer ) * Purpose: dumps bytes from cachefile to stdout * -------------------------------------------------------------------------- * Arguments: cachefile (I) pointer to null-terminated char string - * containing full path to file to be dumped + * containing full path to file to be dumped, + * or contains buffer of bytes to be dumped + * maxage (I) int containing maxage, in seconds, for + * http header, or -1 to not emit headers + * valign (I) int containing Vertical-Align:, in pixels, + * for http header, or <= -999 to not emit + * isbuffer (I) 1 if cachefile is buffer of bytes to be + * dumped * -------------------------------------------------------------------------- * Returns: ( int ) #bytes dumped (0 signals error) * -------------------------------------------------------------------------- * Notes: o * ======================================================================= */ /* --- entry point --- */ -int emitcache ( char *cachefile ) +int emitcache ( char *cachefile, int maxage, int valign, int isbuffer ) { /* ------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -FILE *cacheptr = fopen(cachefile,"rb"), /*open cachefile for binary read*/ - *emitptr = stdout; /* emit cachefile to stdout */ -unsigned char buffer[64]; /* characters from cachefile */ -int buflen = 32, /* #bytes we try to read from cache*/ - nread = 0, /* #bytes actually read */ - nbytes = 0; /* total #bytes emitted */ +int nbytes=gifSize, readcachefile(); /* read cache file */ +FILE *emitptr = stdout; /* emit cachefile to stdout */ +unsigned char buffer[MAXGIFSZ+1]; /* bytes from cachefile */ +unsigned char *buffptr = buffer; /* ptr to buffer */ +int isvalign = (abs(valign)<999?1:0); /* true to emit Vertical-Align: */ +int iscontenttypecached = iscachecontenttype; /*true if headers cached*/ /* ------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ /* --- check that files opened okay --- */ -if ( cacheptr == (FILE *)NULL /* failed to open cachefile */ -|| emitptr == (FILE *)NULL ) /* or failed to open emit file */ +if ( emitptr == (FILE *)NULL ) /* failed to open emit file */ goto end_of_job; /* so return 0 bytes to caller */ -/* --- set stdout to binary mode (for Windows) --- */ +/* --- read the file if necessary --- */ +if ( isbuffer ) { /* cachefile is buffer */ + buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */ + iscontenttypecached = 0; } /* and iscontenttypecached flag */ +else { /* cachefile is file name */ + if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */ + < 1 ) goto end_of_job; } /* quit if file not read */ +/* --- first emit http headers if requested --- */ +if ( isemitcontenttype /* content-type lines enabled */ +&& !iscontenttypecached /* and not in cached image */ +&& maxage >= 0 ) /* caller wants http headers */ + { /* --- emit mime content-type line --- */ + fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage ); + fprintf( emitptr, "Content-Length: %d\n",nbytes ); + if ( isvalign ) /* Vertical-Align: header wanted */ + fprintf( emitptr, "Vertical-Align: %d\n",valign ); + fprintf( emitptr, "Content-type: image/gif\n\n" ); } +/* ------------------------------------------------------------------------- +set stdout to binary mode (for Windows) +-------------------------------------------------------------------------- */ /* emitptr = fdopen(STDOUT_FILENO,"wb"); */ /* doesn't work portably, */ #ifdef WINDOWS /* so instead... */ #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */ @@ -10732,23 +16516,155 @@ if ( cacheptr == (FILE *)NULL /* failed /* ------------------------------------------------------------------------- emit bytes from cachefile -------------------------------------------------------------------------- */ +/* --- write bytes to stdout --- */ +if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */ +< nbytes ) /* failed to write all bytes */ + nbytes = 0; /* reset total count to 0 */ +end_of_job: + return ( nbytes ); /* back with #bytes emitted */ +} /* --- end-of-function emitcache() --- */ + + +/* ========================================================================== + * Function: readcachefile ( cachefile, buffer ) + * Purpose: read cachefile into buffer + * -------------------------------------------------------------------------- + * Arguments: cachefile (I) pointer to null-terminated char string + * containing full path to file to be read + * buffer (O) pointer to unsigned char string + * returning contents of cachefile + * (max 64000 bytes) + * -------------------------------------------------------------------------- + * Returns: ( int ) #bytes read (0 signals error) + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int readcachefile ( char *cachefile, unsigned char *buffer ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/ +unsigned char cachebuff[64]; /* bytes from cachefile */ +int buflen = 32, /* #bytes we try to read from file */ + nread = 0, /* #bytes actually read from file */ + maxbytes = MAXGIFSZ, /* max #bytes returned in buffer */ + nbytes = 0; /* total #bytes read */ +/* ------------------------------------------------------------------------- +initialization +-------------------------------------------------------------------------- */ +/* --- check that files opened okay --- */ +if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/ +/* --- check that output buffer provided --- */ +if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */ +/* ------------------------------------------------------------------------- +read bytes from cachefile +-------------------------------------------------------------------------- */ while ( 1 ) { /* --- read bytes from cachefile --- */ - nread = fread(buffer,sizeof(unsigned char),buflen,cacheptr); /* read */ + nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */ + if ( nbytes + nread > maxbytes ) /* block too big for buffer */ + nread = maxbytes - nbytes; /* so truncate it */ if ( nread < 1 ) break; /* no bytes left in cachefile */ - /* --- write bytes to stdout --- */ - if ( fwrite(buffer,sizeof(unsigned char),nread,emitptr) /* write buffer */ - < nread) /* failed to write all bytes */ - { nbytes = 0; /* reset total count to 0 */ - goto end_of_job; } /* and signal error to caller */ + /* --- store bytes in buffer --- */ + memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */ + /* --- ready to read next block --- */ nbytes += nread; /* bump total #bytes emitted */ if ( nread < buflen ) break; /* no bytes left in cachefile */ + if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */ } /* --- end-of-while(1) --- */ end_of_job: if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */ return ( nbytes ); /* back with #bytes emitted */ -} /* --- end-of-function emitcache() --- */ +} /* --- end-of-function readcachefile() --- */ + + +/* ========================================================================== + * Function: advertisement ( expression, message ) + * Purpose: wrap expression in advertisement message + * -------------------------------------------------------------------------- + * Arguments: expression (I/O) pointer to null-terminated char string + * containing expression to be "wrapped", + * and returning wrapped expression + * message (I) pointer to null-terminated char string + * containing template for advertisement + * message, or NULL to use default message + * -------------------------------------------------------------------------- + * Returns: ( int ) 1 if successful, 0=error + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int advertisement ( char *expression, char *message ) +{ +/* ------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +/* --- advertisement template --- */ +char *adtemplate = + #if defined(ADVERTISEMENT) /* cc -DADVERTISEMENT=\"filename\" */ + #include ADVERTISEMENT /* filename with advertisement */ + #else /* formatted as illustrated below */ + "\\begin{gather} {\\small\\text \\fbox{\\begin{gather}" + "mime\\TeX rendering courtesy of\\\\" + "\\homepagetext \\end{gather}}}\\\\" + " %%beginmath%% %%expression%% %%endmath%% \\end{gather}" + #endif + ; /* terminating semicolon */ +/* --- other variables --- */ +char adbuffer[MAXEXPRSZ+2048]; /*construct wrapped expression here*/ +char *beginmath = " ", /* start math mode */ + *endmath = " "; /* end math mode */ +int strreplace(); /* replace %%keywords%% with values*/ +/* ------------------------------------------------------------------------- +wrap expression in advertisement +-------------------------------------------------------------------------- */ +/* --- start with template --- */ +if ( isempty(message) ) /* caller didn't supply message */ + message = adtemplate; /* so use default message */ +strcpy(adbuffer,message); /* copy message template to buffer */ +/* --- replace %%beginmath%%...%%endmath%% --- */ + strreplace(adbuffer,"%%beginmath%%",beginmath,0); + strreplace(adbuffer,"%%endmath%%",endmath,0); +/* --- replace %%expression%% in template with expression --- */ + strreplace(adbuffer,"%%expression%%",expression,0); +/* --- replace original expression --- */ +strcpy(expression,adbuffer); /* expression now wrapped in ad */ +return ( 1 ); /* always just return 1 */ +} /* --- end-of-function advertisement() --- */ + + +/* ========================================================================== + * Function: crc16 ( s ) + * Purpose: 16-bit crc of string s + * -------------------------------------------------------------------------- + * Arguments: s (I) pointer to null-terminated char string + * whose crc is desired + * -------------------------------------------------------------------------- + * Returns: ( int ) 16-bit crc of s + * -------------------------------------------------------------------------- + * Notes: o From Numerical Recipes in C, 2nd ed, page 900. + * ======================================================================= */ +/* --- entry point --- */ +int crc16 ( char *s ) +{ +/* ------------------------------------------------------------------------- +Compute the crc +-------------------------------------------------------------------------- */ +unsigned short crc = 0; /* returned crc */ +int ibit; /* for(ibit) eight one-bit shifts */ +while ( !isempty(s) ) { /* while there are still more chars*/ + crc = (crc ^ (*s)<<8); /* add next char */ + for ( ibit=0; ibit<8; ibit++ ) /* generator polynomial */ + if ( crc & 0x8000 ) { crc<<=1; crc=crc^4129; } + else crc <<= 1; + s++; /* next xhar */ + } /* --- end-of-while(!isempty(s)) --- */ +return ( (int)crc ); /* back to caller with crc */ +} /* --- end-of-function crc16() --- */ + /* ========================================================================== * Function: md5str ( instr ) @@ -10976,6 +16892,7 @@ void md5_finish( md5_context *ctx, uint8 PUT_UINT32( ctx->state[3], digest, 12 ); } /* --- end-of-function md5str() and "friends" --- */ + #if defined(GIF) /* ========================================================================== * Function: GetPixel ( int x, int y ) @@ -10994,7 +16911,7 @@ void md5_finish( md5_context *ctx, uint8 /* --- entry point --- */ int GetPixel ( int x, int y ) { -int ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/ +int ipixel = y*raster_width + x; /* pixel index for x,y-coords*/ int pixval =0; /* value of pixel */ if ( !isaa ) /* use bitmap if not anti-aliased */ pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/