--- 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