/****************************************************************************
*
* Copyright(c) 2002-2009, 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 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.
* By using mimeTeX, you warrant that you have read, understood and
* 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.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
* LaTeX math in your html pages. It parses a LaTeX math
* expression and immediately emits the corresponding gif
* image, rather than the usual TeX dvi. And mimeTeX is an
* 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
* 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.
* 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 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: 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.
* ===================== Raster Functions ======================
* PART2 --- raster constructor functions ---
* new_raster(width,height,pixsz) allocation (and constructor)
* new_subraster(width,height,pixsz)allocation (and constructor)
* new_chardef() allocate chardef struct
* delete_raster(rp) deallocate raster (rp = raster ptr)
* delete_subraster(sp) deallocate subraster (sp=subraster ptr)
* delete_chardef(cp) deallocate chardef (cp = chardef ptr)
* --- primitive (sub)raster functions ---
* rastcpy(rp) allocate new copy of rp
* subrastcpy(sp) allocate new copy of sp
* rastrot(rp) new raster rotated right 90 degrees to rp
* 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
* 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
* arrow_subraster(width,height,drctn,isBig) left/right arrow
* uparrow_subraster(width,height,drctn,isBig) up/down arrow
* rule_raster(rp,top,left,width,height,type) draw rule in rp
* line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
* line_recurse(rp,row0,col0,row1,col1,thickness) recurse line
* circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
* 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) 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
* make_delim(symbol,height) construct delim exactly height size
* ================= 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
* preamble(expression,size,subexpr) parse preamble
* 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) replace math/tex chars like \^_{} for err display
* strtexchr(string,texchr) find texchr in string
* findbraces(expression,command) find opening { or closing }
* PART3 =========== Rasterize an Expression (recursively) ===========
* --- here's the primary entry point for all of mimeTeX ---
* rasterize(expression,size) parse and rasterize expression
* --- explicitly called handlers that rasterize... ---
* rastparen(subexpr,size,basesp) parenthesized subexpr
* rastlimits(expression,size,basesp) dispatch super/sub call
* rastscripts(expression,size,basesp) super/subscripted exprssn
* rastdispmath(expression,size,sp) scripts for displaymath
* --- table-driven handlers that rasterize... ---
* 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) \\
* rastarrow(expression,size,basesp,width,height,drctn) \longarr
* rastuparrow(expression,size,basesp,width,height,drctn)up/down
* rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not
* rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
* rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel
* rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
* rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt
* rastaccent(expression,size,basesp,accent,isabove,isscript)
* rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
* rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{}
* rastarray(expression,size,basesp,arg1,arg2,arg3) \array
* rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture
* rastline(expression,size,basesp,arg1,arg2,arg3) \line
* 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
* 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
* rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
* rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
* rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
* --- helper functions for handlers ---
* rastopenfile(filename,mode) opens filename[.tex] in mode
* rasteditfilename(filename) edit filename (for security)
* rastreadfile(filename,islock,tag,value) read ...
* rastwritefile(filename,tag,value,isstrict)write...
* 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
* 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
* CreateGifFromEq(expression,gifFileName) entry pt for win dll
* isstrstr(string,snippets,iscase) are any snippets in string?
* 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,maxage,valign,isbuffer) emit cachefile
* readcachefile(cachefile,buffer) read cachefile into buffer
* 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 when compiled with -DAA or -DGIF)
*
* --------------------------------------------------------------------------
* 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
* anti-aliasing (see Notes below). You may also compile
* cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
* to produce an executable that emits gif images without
* anti-aliasing. Alternatively, compile mimeTeX as
* cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
* to produce an executable that just emits mime xbitmaps.
* In either case you'll need mimetex.h and texfonts.h,
* and with -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). 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 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.
* 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 Optional compile-line -D defined symbols are documented
* in mimetex.html#options . They include (additional -D
* switches are discussed in mimetex.html#options)...
* -DAA
* Turns on gif anti-aliasing with default values
* (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
* for the following anti-aliasing parameters...
* -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 2 1
* 2 8 2
* 1 2 1
* to neighboring pixels. The defaults weights are
* 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.
* Experimentation is recommended to determine
* what value works best for you.
* -DCACHEPATH=\"path/\"
* This option saves each rendered image to a file
* in directory path/ which mimeTeX reads rather than
* re-rendering the same image every time it's given
* the same LaTeX expression. Sometimes mimeTeX disables
* caching, e.g., expressions containing \input{ } are
* re-rendered since the contents of the inputted file
* may have changed. If compiled without -DCACHEPATH
* mimeTeX always re-renders expressions. This usually
* isn't too cpu intensive, but if you have unusually
* high hit rates then image caching may be helpful.
* The path/ is relative to mimetex.cgi, and must
* 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,
* and rendered \displaystyle at font sizes \large and
* larger. This default corresponds to -DDISPLAYSIZE=3,
* which you can adjust; e.g., -DDISPLAYSIZE=0 always
* defaults to \displaystyle, and 99 (or any large number)
* always defaults to \textstyle. Note that explicit
* \textstyle, \displaystyle, \limits or \nolimits
* directives in an expression always override
* the DISPLAYSIZE default.
* -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
* may be using your server's mimetex.cgi without permission.
* If REFERER is defined, mimeTeX checks for the environment
* variable HTTP_REFERER and, if it exists, performs a
* case-insensitive test to make sure it contains 'domain'
* as a substring. If given several 'domain's (second form)
* then HTTP_REFERER must contain either 'domain1' or
* 'domain2', etc, as a (case-insensitive) substring.
* If HTTP_REFERER fails to contain a substring matching
* any of these domain(s), mimeTeX emits an error message
* image corresponding to the expression specified by
* the invalid_referer_msg string defined in main().
* Note: if HTTP_REFERER is not an environment variable,
* mimeTeX correctly generates the requested expression
* (i.e., no referer error).
* -DWARNINGS=n -or-
* -DNOWARNINGS
* If an expression submitted to mimeTeX contains an
* unrecognzied escape sequence, e.g., "y=x+\abc+1", then
* mimeTeX generates a gif image containing an embedded
* warning in the form "y=x+[\abc?]+1". If you want these
* warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
* mimeTeX to ignore unrecognized symbols, and the rendered
* image is "y=x++1" instead.
* -DWHITE
* MimeTeX usually renders black symbols on a white
* background. This option renders white symbols on
* a black background instead.
* --------------------------------------------------------------------------
* Revision History:
* 09/18/02 J.Forkosh Installation.
* 12/11/02 J.Forkosh Version 1.00 released.
* 07/04/03 J.Forkosh Version 1.01 released.
* 10/17/03 J.Forkosh Version 1.20 released.
* 12/21/03 J.Forkosh Version 1.30 released.
* 02/01/04 J.Forkosh Version 1.40 released.
* 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.
* 03/25/09 J.Forkosh Most recent revision (also see REVISIONDATE).
*
****************************************************************************/
/* -------------------------------------------------------------------------
Program id
-------------------------------------------------------------------------- */
#define VERSION "1.71" /* mimeTeX version number */
#define REVISIONDATE "25 March 2009" /* date of most recent revision */
/* -------------------------------------------------------------------------
header files and macros
-------------------------------------------------------------------------- */
/* --- standard headers --- */
#include
#include
/*#include */
#include
#include
#include
#include
/* --- windows-specific header info --- */
#ifndef WINDOWS /* -DWINDOWS not supplied by user */
#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
#ifdef WINDOWS /* Windows opens stdout in char mode, and */
#include /* precedes every 0x0A with spurious 0x0D.*/
#include /* So emitcache() issues a Win _setmode() */
/* call to put stdout in binary mode. */
#if defined(_O_BINARY) && !defined(O_BINARY) /* only have _O_BINARY */
#define O_BINARY _O_BINARY /* make O_BINARY available, etc... */
#define setmode _setmode
#define fileno _fileno
#endif
#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 --- */
#ifdef SS
#define ISSUPERSAMPLING 1
#ifndef AAALGORITHM
#define AAALGORITHM 1 /* default supersampling algorithm */
#endif
#ifndef AA /* anti-aliasing not explicitly set */
#define AA /* so define it ourselves */
#endif
#ifndef SSFONTS /* need supersampling fonts */
#define SSFONTS
#endif
#else
#define ISSUPERSAMPLING 0
#ifndef AAALGORITHM
#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 --- */
#if defined(AA) || defined(GIF) || defined(PNG) \
|| defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
|| defined(MINADJACENT) || defined(MAXADJACENT)
#if !defined(GIF) && !defined(AA) /* aa not explicitly specified */
#define AA /* so define it ourselves */
#endif
#if !defined(GIF) && !defined(PNG) /* neither gif nor png specified */
#define GIF /* so default to gif */
#endif
#endif
/* --- resolve output option inconsistencies --- */
#if defined(XBITMAP) /* xbitmap supercedes gif and png */
#ifdef AA
#undef AA
#endif
#ifdef GIF
#undef GIF
#endif
#ifdef PNG
#undef PNG
#endif
#endif
/* --- decide whether 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 */
#endif
#else
#ifndef TEXFONTS
#define NOTEXFONTS /* texfonts not required */
#endif
#endif
/* --- 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 --- */
#ifndef CENTERWT
/*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
/*#define CENTERWT 10*/ /* 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*/
#endif
#ifndef CORNERWT
#define CORNERWT 1 /* anti-aliasing cornerwt default*/
#endif
#ifndef MINADJACENT
#define MINADJACENT 6 /*anti-aliasing minadjacent default*/
#endif
#ifndef MAXADJACENT
#define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/
#endif
/* --- variables for anti-aliasing parameters --- */
GLOBAL(int,centerwt,CENTERWT); /*lowpass matrix center pixel wt */
GLOBAL(int,adjacentwt,ADJACENTWT); /*lowpass matrix adjacent pixel wt*/
GLOBAL(int,cornerwt,CORNERWT); /*lowpass matrix corner pixel wt */
GLOBAL(int,minadjacent,MINADJACENT); /* darken if>=adjacent pts black*/
GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */
GLOBAL(int,weightnum,1); /* font wt, */
GLOBAL(int,maxaaparams,4); /* #entries in table */
/* --- anti-aliasing parameter values by font weight --- */
#define aaparameters struct aaparameters_struct /* typedef */
aaparameters
{ int centerwt; /* lowpass matrix center pixel wt*/
int adjacentwt; /* lowpass matrix adjacent pixel wt*/
int cornerwt; /* lowpass matrix corner pixel wt*/
int minadjacent; /* darken if >= adjacent pts black */
int maxadjacent; /* darken if <= adjacent pts black */
int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
STATIC aaparameters aaparams[] /* set params by weight */
#ifdef INITVALS
=
{ /* ----------------------------------------------------
centerwt adj corner minadj max fgalias,only,bgalias,only
------------------------------------------------------- */
{ 64, 1, 1, 6, 8, 1,0,0,0 }, /* 0 = light */
{ CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
{ 8, 1, 1, 5, 8, 1,0,0,0 }, /* 2 = semibold */
{ 8, 2, 1, 4, 9, 1,0,0,0 } /* 3 = bold */
} /* --- 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
-------------------------------------------------------------------------- */
/* --- black on white background (default), or white on black --- */
#ifdef WHITE
#define ISBLACKONWHITE 0 /* white on black background */
#else
#define ISBLACKONWHITE 1 /* black on white background */
#endif
/* --- colors --- */
#define BGRED (ISBLACKONWHITE?255:0)
#define BGGREEN (ISBLACKONWHITE?255:0)
#define BGBLUE (ISBLACKONWHITE?255:0)
#ifndef FGRED
#define FGRED (ISBLACKONWHITE?0:255)
#endif
#ifndef FGGREEN
#define FGGREEN (ISBLACKONWHITE?0:255)
#endif
#ifndef FGBLUE
#define FGBLUE (ISBLACKONWHITE?0:255)
#endif
/* --- "smash" margin (0 means no smashing) --- */
#ifndef SMASHMARGIN
#ifdef NOSMASH
#define SMASHMARGIN 0
#else
#define SMASHMARGIN 3
#endif
#endif
#ifndef SMASHCHECK
#define SMASHCHECK 0
#endif
/* --- textwidth --- */
#ifndef TEXTWIDTH
#define TEXTWIDTH (400)
#endif
/* --- font "combinations" --- */
#define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
/* --- prefix prepended to all expressions --- */
#ifndef PREFIX
#define PREFIX "\000" /* default no prepended prefix */
#endif
/* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
#ifdef NOARGSIGNAL
#define ARGSIGNAL NULL
#endif
#ifndef ARGSIGNAL
#define ARGSIGNAL "++"
#endif
/* --- security and logging (inhibit message logging, etc) --- */
#ifndef SECURITY
#define SECURITY 999 /* default highest security level */
#endif
#ifndef LOGFILE
#define LOGFILE "mimetex.log" /* default log file */
#endif
#ifndef CACHELOG
#define CACHELOG "mimetex.log" /* default caching log file */
#endif
#if !defined(NODUMPENVP) && !defined(DUMPENVP)
#define DUMPENVP /* assume char *envp[] available */
#endif
/* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
#ifndef CACHEPATH
#define ISCACHING 0 /* no caching */
#define CACHEPATH "\000" /* same directory as mimetex.cgi */
#else
#define ISCACHING 1 /* caching if -DCACHEPATH="path" */
#endif
/* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
#ifndef PATHPREFIX
#define PATHPREFIX "\000" /* paths relative mimetex.cgi */
#endif
/* --- time zone delta t (in hours) --- */
#ifndef TZDELTA
#define TZDELTA 0
#endif
/* --- treat +'s in query string as blanks? --- */
#ifdef PLUSBLANK /* + always interpreted as blank */
#define ISPLUSBLANK 1
#else
#ifdef PLUSNOTBLANK /* + never interpreted as blank */
#define ISPLUSBLANK 0
#else /* program tries to determine */
#define ISPLUSBLANK (-1)
#endif
#endif
/* -------------------------------------------------------------------------
debugging and logging / error reporting
-------------------------------------------------------------------------- */
/* --- debugging and error reporting --- */
#ifndef MSGLEVEL
#define MSGLEVEL 1
#endif
#define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */
#define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */
#ifndef FORMLEVEL
#define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/
#endif
#ifndef ERRORSTATUS /* exit(ERRORSTATUS) for any error */
#define ERRORSTATUS 0 /* default doesn't signal errors */
#endif
GLOBAL(int,seclevel,SECURITY); /* security level */
GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */
GLOBAL(int,errorstatus,ERRORSTATUS); /* exit status if error encountered*/
GLOBAL(int,exitstatus,0); /* exit status (0=success) */
STATIC FILE *msgfp; /* output in command-line mode */
/* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
#ifdef WARNINGS
#define WARNINGLEVEL WARNINGS
#else
#ifdef NOWARNINGS
#define WARNINGLEVEL 0
#else
#define WARNINGLEVEL 1
#endif
#endif
GLOBAL(int,warninglevel,WARNINGLEVEL); /* warning level */
/* -------------------------------------------------------------------------
control flags and values
-------------------------------------------------------------------------- */
GLOBAL(int,daemonlevel,0); /* incremented in main() */
GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
GLOBAL(int,isligature,0); /* true if ligature found */
GLOBAL(char,*subexprptr,(char *)NULL); /* ptr within expression to subexpr*/
/*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
GLOBAL(int,fontsize,NORMALSIZE); /* current size */
GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
/*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
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,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);
GLOBAL(int,bgonly,0); /* aapnm() params */
GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
GLOBAL(int,isss,ISSUPERSAMPLING); /* supersampling flag for main() */
GLOBAL(int,*workingparam,(int *)NULL); /* working parameter */
GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
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 */
/* -------------------------------------------------------------------------
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 */
#define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
#define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
#define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
#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
#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*/
/* --- 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)))
/* --- 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'; } }
/* ---
* PART2
* ------ */
#if !defined(PARTS) || defined(PART2)
/* ==========================================================================
* Function: new_raster ( width, height, pixsz )
* Purpose: Allocation and constructor for raster.
* mallocs and initializes memory for width*height pixels,
* and returns raster struct ptr to caller.
* --------------------------------------------------------------------------
* Arguments: width (I) int containing width, in bits,
* of raster pixmap to be allocated
* height (I) int containing height, in bits/scans,
* of raster pixmap to be allocated
* pixsz (I) int containing #bits per pixel, 1 or 8
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to allocated and initialized
* raster struct, or NULL for any error.
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
raster *new_raster ( int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *rp = (raster *)NULL; /* raster ptr returned to caller */
pixbyte *pixmap = NULL; /* raster pixel map to be malloced */
int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
int filler = (isstring?' ':0); /* pixmap filler */
int delete_raster(); /* in case pixmap malloc() fails */
int npadding = (0&&issupersampling?8+256:0); /* padding bytes */
/* -------------------------------------------------------------------------
allocate and initialize raster struct and embedded bitmap
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
width,height,pixsz); fflush(msgfp); }
/* --- allocate and initialize raster struct --- */
rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
if ( rp == (raster *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
rp->width = width; /* store width in raster struct */
rp->height = height; /* and store height */
rp->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 --- */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
nbytes); fflush(msgfp); }
if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/
pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
if ( pixmap == (pixbyte *)NULL ) /* malloc failed */
{ delete_raster(rp); /* so free everything */
rp = (raster *)NULL; /* reset pointer */
goto end_of_job; } /* and return error to caller */
memset((void *)pixmap,filler,nbytes); /* init bytes to binary 0's or ' 's*/
*pixmap = (pixbyte)0; /* and first byte alwasy 0 */
rp->pixmap = pixmap; /* store ptr to malloced memory */
/* -------------------------------------------------------------------------
Back to caller with address of raster struct, or NULL ptr for any error.
-------------------------------------------------------------------------- */
end_of_job:
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
return ( rp ); /* back to caller with raster */
} /* --- end-of-function new_raster() --- */
/* ==========================================================================
* Function: new_subraster ( width, height, pixsz )
* Purpose: Allocate a new subraster along with
* an embedded raster of width x height.
* --------------------------------------------------------------------------
* Arguments: width (I) int containing width of embedded raster
* height (I) int containing height of embedded raster
* pixsz (I) int containing #bits per pixel, 1 or 8
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to newly-allocated subraster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o if width or height <=0, embedded raster not allocated
* ======================================================================= */
/* --- entry point --- */
subraster *new_subraster ( int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *sp=NULL; /* subraster returned to caller */
raster *new_raster(), *rp=NULL; /* image raster embedded in sp */
int delete_subraster(); /* in case new_raster() fails */
int size = NORMALSIZE, /* default size */
baseline = height-1; /* and baseline */
/* -------------------------------------------------------------------------
allocate and initialize subraster struct
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
width,height,pixsz); fflush(msgfp); }
/* --- allocate subraster struct --- */
sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */
if ( sp == (subraster *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
/* --- initialize subraster struct --- */
sp->type = NOVALUE; /* character or image raster */
sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */
sp->baseline = baseline; /*0 if image is entirely descending*/
sp->size = size; /* font size 0-4 */
sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */
sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/
/* -------------------------------------------------------------------------
allocate raster and embed it in subraster, and return to caller
-------------------------------------------------------------------------- */
/* --- allocate raster struct if desired --- */
if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */
{ if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
!= NULL ) /* if allocate succeeded */
sp->image = rp; /* embed raster in subraster */
else /* or if allocate failed */
{ delete_subraster(sp); /* free non-unneeded subraster */
sp = NULL; } } /* signal error */
/* --- back to caller with new subraster or NULL --- */
end_of_job:
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
return ( sp );
} /* --- end-of-function new_subraster() --- */
/* ==========================================================================
* Function: new_chardef ( )
* Purpose: Allocates and initializes a chardef struct,
* but _not_ the embedded raster struct.
* --------------------------------------------------------------------------
* Arguments: none
* --------------------------------------------------------------------------
* Returns: ( chardef * ) ptr to allocated and initialized
* chardef struct, or NULL for any error.
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
chardef *new_chardef ( )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */
/* -------------------------------------------------------------------------
allocate and initialize chardef struct
-------------------------------------------------------------------------- */
cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
if ( cp == (chardef *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
cp->charnum = cp->location = 0; /* init character description */
cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
cp->image.width = cp->image.height = 0; /* init raster dimensions */
cp->image.format = 0; /* init raster format */
cp->image.pixsz = 0; /* and #bits per pixel */
cp->image.pixmap = NULL; /* init raster pixmap as null */
/* -------------------------------------------------------------------------
Back to caller with address of chardef struct, or NULL ptr for any error.
-------------------------------------------------------------------------- */
end_of_job:
return ( cp );
} /* --- end-of-function new_chardef() --- */
/* ==========================================================================
* Function: delete_raster ( rp )
* Purpose: Destructor for raster.
* Frees memory for raster bitmap and struct.
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_raster ( raster *rp )
{
/* -------------------------------------------------------------------------
free raster bitmap and struct
-------------------------------------------------------------------------- */
if ( rp != (raster *)NULL ) /* can't free null ptr */
{
if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */
free((void *)rp->pixmap); /* free pixmap within raster */
free((void *)rp); /* lastly, free raster struct */
} /* --- end-of-if(rp!=NULL) --- */
return ( 1 ); /* back to caller, 1=okay 0=failed */
} /* --- end-of-function delete_raster() --- */
/* ==========================================================================
* Function: delete_subraster ( sp )
* Purpose: Deallocates a subraster (and embedded raster)
* --------------------------------------------------------------------------
* Arguments: sp (I) ptr to subraster struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_subraster ( subraster *sp )
{
/* -------------------------------------------------------------------------
free subraster struct
-------------------------------------------------------------------------- */
int delete_raster(); /* to delete embedded raster */
if ( sp != (subraster *)NULL ) /* can't free null ptr */
{
if ( sp->type != CHARASTER ) /* not static character data */
if ( sp->image != NULL ) /*raster allocated within subraster*/
delete_raster(sp->image); /* so free embedded raster */
free((void *)sp); /* and free subraster struct itself*/
} /* --- end-of-if(sp!=NULL) --- */
return ( 1 ); /* back to caller, 1=okay 0=failed */
} /* --- end-of-function delete_subraster() --- */
/* ==========================================================================
* Function: delete_chardef ( cp )
* Purpose: Deallocates a chardef (and bitmap of embedded raster)
* --------------------------------------------------------------------------
* Arguments: cp (I) ptr to chardef struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_chardef ( chardef *cp )
{
/* -------------------------------------------------------------------------
free chardef struct
-------------------------------------------------------------------------- */
if ( cp != (chardef *)NULL ) /* can't free null ptr */
{
if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */
free((void *)cp->image.pixmap); /* so free embedded pixmap */
free((void *)cp); /* and free chardef struct itself */
} /* --- end-of-if(cp!=NULL) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
return ( 1 );
} /* --- end-of-function delete_chardef() --- */
/* ==========================================================================
* Function: rastcpy ( rp )
* Purpose: makes duplicate copy of rp
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be copied
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to new copy rp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
raster *rastcpy ( raster *rp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/
int height= (rp==NULL?0:rp->height), /* original and copied height */
width = (rp==NULL?0:rp->width), /* original and copied width */
pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
/* -------------------------------------------------------------------------
allocate rotated raster and fill it
-------------------------------------------------------------------------- */
/* --- allocate copied raster with same width,height, and copy bitmap --- */
if ( rp != NULL ) /* nothing to copy if ptr null */
if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
!= NULL ) /* check that allocate succeeded */
memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
return ( newrp ); /* return copied raster to caller */
} /* --- end-of-function rastcpy() --- */
/* ==========================================================================
* Function: subrastcpy ( sp )
* Purpose: makes duplicate copy of sp
* --------------------------------------------------------------------------
* Arguments: sp (I) ptr to subraster struct to be copied
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to new copy sp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *subrastcpy ( subraster *sp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
raster *rastcpy(), *newrp=NULL; /* and new raster image within it */
int delete_subraster(); /* dealloc newsp if rastcpy() fails*/
/* -------------------------------------------------------------------------
make copy, and return it to caller
-------------------------------------------------------------------------- */
if ( sp == NULL ) goto end_of_job; /* nothing to copy */
/* --- allocate new subraster "envelope" for copy --- */
if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
== NULL ) goto end_of_job; /* and quit if we fail to allocate */
/* --- transparently copy original envelope to new one --- */
memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
/* --- make a copy of the rasterized image itself, if there is one --- */
if ( sp->image != NULL ) /* there's an image embedded in sp */
if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */
== NULL ) /* failed to copy successfully */
{ delete_subraster(newsp); /* won't need newsp any more */
newsp = NULL; /* because we're returning error */
goto end_of_job; } /* back to caller with error signal*/
/* --- set new params in new envelope --- */
newsp->image = newrp; /* new raster image we just copied */
switch ( sp->type ) /* set new raster image type */
{ case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
case 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:
return ( newsp ); /* copy back to caller */
} /* --- end-of-function subrastcpy() --- */
/* ==========================================================================
* Function: rastrot ( rp )
* Purpose: rotates rp image 90 degrees right/clockwise
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be rotated
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to new raster rotated relative to rp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o An underbrace is } rotated 90 degrees clockwise,
* a hat is <, etc.
* ======================================================================= */
/* --- entry point --- */
raster *rastrot ( raster *rp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/
int height = rp->height, irow, /* original height, row index */
width = rp->width, icol, /* original width, column index */
pixsz = rp->pixsz; /* #bits per pixel */
/* -------------------------------------------------------------------------
allocate rotated raster and fill it
-------------------------------------------------------------------------- */
/* --- allocate rotated raster with flipped width<-->height --- */
if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
!= NULL ) /* check that allocation succeeded */
/* --- fill rotated raster --- */
for ( irow=0; irowheight, 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; irowheight - 1
* left (I) int containing 0 ... target->width - 1
* isopaque (I) int containing false (zero) to allow
* original 1-bits of target to "show through"
* 0-bits of source.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int rastput ( raster *target, raster *source,
int top, int left, int isopaque )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow, icol, /* indexes over source raster */
twidth=target->width, theight=target->height, /*target width,height*/
tpix, ntpix = twidth*theight; /* #pixels in target */
int isfatal = 0, /* true to abend on out-of-bounds error */
isstrict = 0/*1*/, /* true for strict bounds check - no "wrap"*/
isokay = 1; /* true if no pixels out-of-bounds */
/* -------------------------------------------------------------------------
superimpose source onto target, one bit at a time
-------------------------------------------------------------------------- */
if ( isstrict && (top<0 || left<0) ) /* args fail strict test */
isokay = 0; /* so just return error */
else
for ( irow=0; irowheight; irow++ ) /* for each scan line */
{
tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/
for ( icol=0; icolwidth; icol++ ) /* each pixel in scan line */
{
int svalue = getpixel(source,irow,icol); /* source pixel value */
++tpix; /* bump target pixel */
if ( msgfp!=NULL && msglevel>=9999 ) /* debugging output */
{ fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
"left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
left,icol,twidth); fflush(msgfp); }
if ( tpix >= ntpix /* bounds check failed */
|| (isstrict && (irow+top>=theight || icol+left>=twidth)) )
{ isokay = 0; /* reset okay flag */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else break; } /*or just go on to next row*/
if ( tpix >= 0 ) /* bounds check okay */
if ( svalue!=0 || isopaque ) { /*got dark or opaque source*/
setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
} /* --- end-of-for(icol) --- */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
end_of_job:
return ( isokay /*isfatal? (tpixbaseline, /*baseline for underlying subraster*/
height1 = (sp1->image)->height, /* height for underlying subraster */
width1 = (sp1->image)->width, /* width for underlying subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */
base2 = sp2->baseline, /*baseline for overlaid subraster */
height2 = (sp2->image)->height, /* height for overlaid subraster */
width2 = (sp2->image)->width, /* width for overlaid subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
int height=0, width=0, pixsz=0, base=0; /* overlaid composite */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine height, width and baseline of composite raster --- */
if ( isalign ) /* baselines of sp1,sp2 aligned */
{ height = max2(base1+1,base2+1) /* max height above baseline */
+ max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
base = max2(base1,base2); } /* max space above baseline */
else /* baselines not aligned */
{ height = max2(height1,height2); /* max height */
base = base1 + (height-height1)/2; } /* baseline for sp1 */
width = max2(width1,width2+abs(offset2)); /* max width */
pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
/* -------------------------------------------------------------------------
allocate concatted composite subraster
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) goto end_of_job; /* failed, so quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* image */
sp->baseline = base; /* composite baseline */
sp->size = sp1->size; /* underlying char is sp1 */
/* --- extract raster from subraster --- */
rp = sp->image; /* raster allocated in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
if ( isalign )
{ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
rastput (rp, sp2->image, base-base2, /*overlaid*/
(width-width2)/2+offset2, 0); }
else
{ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
(width-width2)/2+offset2, 0); }
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to concatted subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastcompose() --- */
/* ==========================================================================
* Function: rastcat ( sp1, sp2, isfree )
* Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged
* and returning a newly-allocated subraster.
* Frees/deletes input sp1 and/or sp2 depending on value
* of isfree (0=none, 1=sp1, 2=sp2, 3=both).
* --------------------------------------------------------------------------
* Arguments: sp1 (I) subraster * to left-hand subraster
* sp2 (I) subraster * to right-hand subraster
* isfree (I) int containing 1=free sp1 before return,
* 2=free sp2, 3=free both, 0=free none.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to constructed subraster sp1||sp2
* or NULL for any error
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
raster *rp=(raster *)NULL; /* new concatted raster */
int delete_subraster(); /* in case isfree non-zero */
int rastput(); /*place sp1,sp2 in concatted raster*/
int type_raster(); /* debugging display */
int base1 = sp1->baseline, /*baseline for left-hand subraster*/
height1 = (sp1->image)->height, /* height for left-hand subraster */
width1 = (sp1->image)->width, /* width for left-hand subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */
type1 = sp1->type, /* image type for left-hand */
base2 = sp2->baseline, /*baseline for right-hand subraster*/
height2 = (sp2->image)->height, /* height for right-hand subraster */
width2 = (sp2->image)->width, /* width for right-hand subraster */
pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
type2 = sp2->type; /* image type for right-hand */
int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
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 */
class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
class1==OPENING||class1==CLOSING||class1==PUNCTION),
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
-------------------------------------------------------------------------- */
/* --- determine inter-character space from character class --- */
if ( !isstring )
space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
else space = 1; /* space for ascii string */
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
&& type1!=FRACRASTER && type2!=FRACRASTER ) )
/*maxsmash = 0;*/ /* turn off smash */
margin = max2(space-1,0); /* force small smashmargin */
else /* adjust for delta if images */
if ( issmashdelta ) /* smashmargin is a delta value */
margin += fontsize; /* add displaystyle base to margin */
if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
isblank = 1; /* set blank flag signal */
else /* see how much extra space we have*/
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(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-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 */
pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
base = max2(base1,base2); /* max space above baseline */
if ( msgfp!=NULL && msglevel>=9999 ) /* display components */
{ fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
height1,width1,pixsz1,base1);
type_raster(sp1->image,msgfp); /* display left-hand raster */
fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
height2,width2,pixsz2,base2);
type_raster(sp2->image,msgfp); /* display right-hand raster */
fprintf(msgfp,
"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
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
width,height,pixsz); fflush(msgfp); }
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) /* failed */
{ if ( msgfp!=NULL && msglevel>=1 ) /* report failure */
{ fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
width,height,pixsz); fflush(msgfp); }
goto end_of_job; } /* failed, so quit */
/* --- initialize subraster parameters --- */
/* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/
if ( !isstring )
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 */
sp->baseline = base; /* composite baseline */
sp->size = sp2->size; /* rightmost char is sp2 */
if ( isblank ) /* need to propagate blanksignal */
sp->type = blanksignal; /* may not be completely safe??? */
/* --- extract raster from subraster --- */
rp = sp->image; /* raster allocated in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=9999 )
{ fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
fflush(msgfp); } /* flush msgfp buffer */
if ( !isstring )
rastput (rp, sp1->image, base-base1, /* overlay left-hand */
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 )
{ 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*/
if ( msgfp!=NULL && msglevel>=9999 )
{ type_raster(sp->image,msgfp); /* display composite raster */
fflush(msgfp); } /* flush msgfp buffer */
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to concatted subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
smashmargin = oldsmashmargin; /* reset original smashmargin */
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastcat() --- */
/* ==========================================================================
* Function: rastack ( sp1, sp2, base, space, iscenter, isfree )
* Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged
* and returning a newly-allocated subraster,
* whose baseline is sp1's if base=1, or sp2's if base=2.
* Frees/deletes input sp1 and/or sp2 depending on value
* of isfree (0=none, 1=sp1, 2=sp2, 3=both).
* --------------------------------------------------------------------------
* Arguments: sp1 (I) subraster * to lower subraster
* sp2 (I) subraster * to upper subraster
* base (I) int containing 1 if sp1 is baseline,
* or 2 if sp2 is baseline.
* space (I) int containing #rows blank space inserted
* between sp1's image and sp2's image.
* iscenter (I) int containing 1 to center both sp1 and sp2
* in stacked array, 0 to left-justify both
* isfree (I) int containing 1=free sp1 before return,
* 2=free sp2, 3=free both, 0=free none.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1
* or NULL for any error
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
subraster *rastack ( subraster *sp1, subraster *sp2,
int base, int space, int iscenter, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
raster *rp=(raster *)NULL; /* new stacked raster in sp */
int delete_subraster(); /* in case isfree non-zero */
int rastput(); /* place sp1,sp2 in stacked raster */
int base1 = sp1->baseline, /* baseline for lower subraster */
height1 = (sp1->image)->height, /* height for lower subraster */
width1 = (sp1->image)->width, /* width for lower subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */
base2 = sp2->baseline, /* baseline for upper subraster */
height2 = (sp2->image)->height, /* height for upper subraster */
width2 = (sp2->image)->width, /* width for upper subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */
int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */
*symdef2 = sp2->symdef; /* mathchardef of right upper char */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine height, width and baseline of composite raster --- */
height = height1 + space + height2; /* sum of heights plus space */
width = max2(width1,width2); /* max width is overall width */
pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
/* -------------------------------------------------------------------------
allocate stacked composite subraster (with embedded raster)
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) goto end_of_job; /* failed, so quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* stacked rasters */
sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
sp->baseline = baseline; /* composite baseline */
sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
/* --- extract raster from subraster --- */
rp = sp->image; /* raster embedded in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
if ( iscenter == 1 ) /* center both sp1 and sp2 */
{ rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */
rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
else /* left-justify both sp1 and sp2 */
{ rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */
rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to stacked subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastack() --- */
/* ==========================================================================
* Function: rastile ( tiles, ntiles )
* Purpose: Allocate and build up a composite raster
* from the ntiles components/characters supplied in tiles.
* --------------------------------------------------------------------------
* Arguments: tiles (I) subraster * to array of subraster structs
* describing the components and their locations
* ntiles (I) int containing number of subrasters in tiles[]
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to composite raster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o The top,left corner of a raster is row=0,col=0
* with row# increasing as you move down,
* and col# increasing as you move right.
* Metafont numbers rows with the baseline=0,
* so the top row is a positive number that
* decreases as you move down.
* o rastile() is no longer used.
* It was used by an earlier rasterize() algorithm,
* and I've left it in place should it be needed again.
* But recent changes haven't been tested/exercised.
* ======================================================================= */
/* --- entry point --- */
raster *rastile ( subraster *tiles, int ntiles )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/
int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */
int itile; /* tiles[] index */
int rastput(); /* overlay each tile in composite raster */
/* -------------------------------------------------------------------------
run through tiles[] to determine dimensions for composite raster
-------------------------------------------------------------------------- */
/* --- determine row and column bounds of composite raster --- */
for ( itile=0; itiletoprow);
leftcol = min2(leftcol, tile->leftcol);
/* --- lower-right corner of composite --- */
botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1);
/* --- pixsz of composite --- */
pixsz = max2(pixsz,(tile->image)->pixsz);
} /* --- end-of-for(itile) --- */
/* --- calculate width and height from bounds --- */
width = rightcol - leftcol + 1;
height = botrow - toprow + 1;
/* --- sanity check (quit if bad dimensions) --- */
if ( width<1 || height<1 ) goto end_of_job;
/* -------------------------------------------------------------------------
allocate composite raster, and embed tiles[] within it
-------------------------------------------------------------------------- */
/* --- allocate composite raster --- */
if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
== (raster *)NULL ) goto end_of_job; /* and quit if failed */
/* --- embed tiles[] in composite --- */
for ( itile=0; itileimage, /* overlay tile image at...*/
tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
/* -------------------------------------------------------------------------
Back to caller with composite raster (or null for any error)
-------------------------------------------------------------------------- */
end_of_job:
return ( composite ); /* back with composite or null ptr */
} /* --- end-of-function rastile() --- */
/* ==========================================================================
* Function: rastsmash ( sp1, sp2 )
* Purpose: When concatanating sp1||sp2, calculate #pixels
* 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 smash sp1||sp2,
* or "blanksignal" if sp2 intentionally blank,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
int rastsmash ( subraster *sp1, subraster *sp2 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
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 */
base2 = sp2->baseline, /*baseline for right-hand subraster*/
height2 = (sp2->image)->height, /* height for right-hand subraster */
width2 = (sp2->image)->width; /* width for right-hand subraster */
int base = max2(base1,base2), /* max ascenders - 1 above baseline*/
top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
height = max2(bot1,bot2)+1; /* total height */
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 smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
int type_raster(); /* display debugging output */
/* -------------------------------------------------------------------------
find right edge of sp1 and left edge of sp2 (these will be abutting edges)
-------------------------------------------------------------------------- */
/* --- check args --- */
if ( isstring ) goto end_of_job; /* ignore string rasters */
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 smash intentional blank */
/* --- init firstcol1[], firstcol2[] --- */
for ( irow1=0; irow1image,irow2-top2,icol) != 0 ) /* found a set pixel */
{ firstcol2[irow2] = icol; /* icol is #cols from left edge */
nfirst2++; /* bump #rows containing set pixels*/
break; } /* and go on to next row */
if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
{ nsmash = blanksignal; /* signal intentional blanks */
goto end_of_job; } /* don't smash intentional blanks */
/* --- now check if preceding image in sp1 was an intentional blank --- */
if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
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 */
if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
{ firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
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 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 */
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>smashmargin && dx= 99 ) /* display for debugging */
{ fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
nsmash,smashmargin);
if ( msglevel >= 999 ) /* also display rasters */
{ fprintf(msgfp,"rastsmash>left-hand image...\n");
if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
fprintf(msgfp,"rastsmash>right-hand image...\n");
if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
fflush(msgfp); }
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 )
* Purpose: Allocate a new subraster of width x height
* (or maybe different dimensions, depending on accent),
* and draw an accent (\hat or \vec or \etc) that fills it
* --------------------------------------------------------------------------
* Arguments: accent (I) int containing either HATACCENT or VECACCENT,
* etc, indicating the type of accent desired
* width (I) int containing desired width of accent (#cols)
* height (I) int containing desired height of accent(#rows)
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o Some accents have internally-determined dimensions,
* and caller should check dimensions in returned subraster
* ======================================================================= */
/* --- entry point --- */
subraster *accent_subraster ( int accent, int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- general info --- */
raster *new_raster(), *rp=NULL; /*raster containing desired accent*/
subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
int delete_raster(), delete_subraster(); /*free allocated raster on err*/
int line_raster(), /* draws lines */
rule_raster(), /* draw solid boxes */
thickness = 1; /* line thickness */
/*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
/* --- other working info --- */
int col0, col1, /* cols for line */
row0, row1; /* rows for line */
subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/
/* --- info for under/overbraces, tildes, etc --- */
char brace[16]; /*"{" for over, "}" for under, etc*/
raster *rastrot(), /* rotate { for overbrace, etc */
*rastcpy(); /* may need copy of original */
subraster *arrow_subraster(); /* rightarrow for vec */
subraster *rastack(); /* stack accent atop extra space */
int iswidthneg = 0; /* set true if width<0 arg passed */
int serifwidth=0; /* serif for surd */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
/* -------------------------------------------------------------------------
outer switch() traps accents that may change caller's height,width
-------------------------------------------------------------------------- */
switch ( accent )
{
default:
/* -----------------------------------------------------------------------
inner switch() first allocates fixed-size raster for accents that don't
------------------------------------------------------------------------ */
if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
!= NULL ) /* and if we succeeded... */
switch ( accent ) /* ...draw requested accent in it */
{
/* --- unrecognized request --- */
default: delete_raster(rp); /* unrecognized accent requested */
rp = NULL; break; /* so free raster and signal error */
/* --- bar request --- */
case UNDERBARACCENT:
case BARACCENT:
thickness = 1; /*height-1;*/ /* adjust thickness */
if ( accent == BARACCENT ) /* bar is above expression */
{ 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 */
{ 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*/
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+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 = 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:
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+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;
} /* --- end-of-inner-switch(accent) --- */
break; /* break from outer accent switch */
/* --- underbrace, overbrace request --- */
case UNDERBRACE:
case OVERBRACE:
if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
if ( accent == OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
!= NULL ) /* found desired brace */
{ rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
delete_subraster(accsp); } /* and free subraster "envelope" */
break;
/* --- hat request --- */
case HATACCENT:
if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
!= NULL ) /* found desired brace */
{ rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
delete_subraster(accsp); } /* and free subraster "envelope" */
break;
/* --- vec request --- */
case VECACCENT:
height = 2*(height/2) + 1; /* force height odd */
if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
!= NULL ) /* succeeded */
{ rp = accsp->image; /* "extract" raster with bitmap */
free((void *)accsp); } /* and free subraster "envelope" */
break;
/* --- tilde request --- */
case TILDEACCENT:
accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
get_delim("~",-width,CMEX10)); /*width search for tilde*/
if ( accsp != NULL ) /* found desired tilde */
if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
!= NULL ) /* have tilde with space below it */
{ rp = sp->image; /* "extract" raster with bitmap */
free((void *)sp); /* and free subraster "envelope" */
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 ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
== NULL ) /* and if we fail to allocate */
delete_raster(rp); /* free now-unneeded raster */
else /* subraster allocated okay */
{ /* --- init subraster parameters, embedding raster in it --- */
sp->type = IMAGERASTER; /* constructed image */
sp->image = rp; /* raster we just constructed */
sp->size = (-1); /* can't set font size here */
sp->baseline = 0; } /* can't set baseline here */
} /* --- 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() --- */
/* ==========================================================================
* Function: arrow_subraster ( width, height, pixsz, drctn, isBig )
* Purpose: Allocate a raster/subraster and draw left/right arrow in it
* --------------------------------------------------------------------------
* Arguments: width (I) int containing number of cols for arrow
* height (I) int containing number of rows for arrow
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* drctn (I) int containing +1 for right arrow,
* or -1 for left, 0 for leftright
* isBig (I) int containing 1/true for \Long arrows,
* or false for \long arrows, i.e.,
* true for ===> or false for --->.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to constructed left/right arrow
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *arrow_subraster ( int width, int height, int pixsz,
int drctn, int isBig )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int irow, midrow=height/2; /* index, midrow is arrowhead apex */
int icol, thickness=(height>15?2: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[] */
/* -------------------------------------------------------------------------
allocate raster/subraster and draw arrow line
-------------------------------------------------------------------------- */
if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */
if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
== NULL ) goto end_of_job; /* and quit if failed */
if ( !isBig ) /* single line */
rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
else
{ int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
/* -------------------------------------------------------------------------
construct arrowhead(s)
-------------------------------------------------------------------------- */
for ( irow=0; irow= 0 ) /* right arrowhead wanted */
for ( icol=0; icol= 0 ) { /* bounds check */
if ( pixsz == 1 ) /* have a bitmap */
setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
/* --- left arrowhead (same as right except for ipix calculation) --- */
if ( drctn <= 0 ) /* left arrowhead wanted */
for ( icol=0; icolimage)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
} /* --- end-of-for(irow) --- */
end_of_job:
return ( arrowsp ); /*back to caller with arrow or NULL*/
} /* --- end-of-function arrow_subraster() --- */
/* ==========================================================================
* Function: uparrow_subraster ( width, height, pixsz, drctn, isBig )
* Purpose: Allocate a raster/subraster and draw up/down arrow in it
* --------------------------------------------------------------------------
* Arguments: width (I) int containing number of cols for arrow
* height (I) int containing number of rows for arrow
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* drctn (I) int containing +1 for up arrow,
* or -1 for down, or 0 for updown
* isBig (I) int containing 1/true for \Long arrows,
* or false for \long arrows, i.e.,
* true for ===> or false for --->.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to constructed up/down arrow
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *uparrow_subraster ( int width, int height, int pixsz,
int drctn, int isBig )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int icol, midcol=width/2; /* index, midcol is arrowhead apex */
int irow, thickness=(width>15?2: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[] */
/* -------------------------------------------------------------------------
allocate raster/subraster and draw arrow line
-------------------------------------------------------------------------- */
if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */
if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
== NULL ) goto end_of_job; /* and quit if failed */
if ( !isBig ) /* single line */
rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
else
{ int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
/* -------------------------------------------------------------------------
construct arrowhead(s)
-------------------------------------------------------------------------- */
for ( icol=0; icol= 0 ) /* up arrowhead wanted */
for ( irow=0; irowimage)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
/* --- down arrowhead (same as up except for ipix calculation) --- */
if ( drctn <= 0 ) /* down arrowhead wanted */
for ( irow=0; irow 0 ) { /* bounds check */
if ( pixsz == 1 ) /* have a bitmap */
setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
} /* --- end-of-for(icol) --- */
end_of_job:
return ( arrowsp ); /*back to caller with arrow or NULL*/
} /* --- end-of-function uparrow_subraster() --- */
/* ==========================================================================
* Function: rule_raster ( rp, top, left, width, height, type )
* Purpose: Draw a solid or dashed line (or box) in existing raster rp,
* starting at top,left with dimensions width,height.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which rule
* will be drawn
* top (I) int containing row at which top-left corner
* of rule starts (0 is topmost)
* left (I) int containing col at which top-left corner
* of rule starts (0 is leftmost)
* width (I) int containing number of cols for rule
* height (I) int containing number of rows for rule
* type (I) int containing 0 for solid rule,
* 1 for horizontal dashes, 2 for vertical
* 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.
* --------------------------------------------------------------------------
* Notes: o Rule line is implicitly "horizontal" or "vertical" depending
* on relative width,height dimensions. It's a box if they're
* more or less comparable.
* ======================================================================= */
/* --- entry point --- */
int rule_raster ( raster *rp, int top, int left,
int width, int height, int type )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow=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 */
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 ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
rp = workingbox->image; /* use workingbox if possible */
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
-------------------------------------------------------------------------- */
if ( width > 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 ( 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 */
} /* --- end-of-for(icol) --- */
} /* --- end-of-for(irow) --- */
end_of_job:
return ( isfatal? (ipixheight-1 is bottom-most)
* col1 (I) int containing col at which
* line will end (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the line will be
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o if row0==row1, a horizontal line is drawn
* between col0 and col1, with row0(==row1) the top row
* and row0+(thickness-1) the bottom row
* o if col0==col1, a vertical bar is drawn
* between row0 and row1, with col0(==col1) the left col
* and col0+(thickness-1) the right col
* o if both the above, you get a square thickness x thickness
* whose top-left corner is row0,col0.
* ======================================================================= */
/* --- entry point --- */
int line_raster ( raster *rp, int row0, int col0,
int row1, int col1, int thickness )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow=0, icol=0, /* indexes over rp raster */
locol=col0, hicol=col1, /* col limits at irow */
lorow=row0, hirow=row1; /* row limits at icol */
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=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)),
xheight = 1.0;
int line_recurse(), isrecurse=1; /* true to draw line recursively */
/* -------------------------------------------------------------------------
Check args
-------------------------------------------------------------------------- */
if ( rp == (raster *)NULL ) { /* no raster arg supplied */
if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
rp = workingbox->image; /* use workingbox if possible */
else return ( 0 ); } /* otherwise signal error to caller */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=29 ) { /* debugging */
fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
"\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
row0,col0, row1,col1, thickness, dy,dx, a, xwidth); fflush(msgfp); }
/* --- check for recursive line drawing --- */
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 */
if ( 0&&isbar ) /*interpret col as left col*/
hicol = col0 + (thickness-1); /* set right col for bar */
/* -------------------------------------------------------------------------
draw line one row at a time
-------------------------------------------------------------------------- */
for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
{
if ( !isbar && !isline ) /* neither vert nor horiz */
{ xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */
locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
irow,xcol,locol,hicol);
ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
if ( ++ipix >= npix ) /* bounds check failed */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else break; /*or just go on to next row*/
else /* turn on pixel in line */
if ( rp->pixsz == 1 ) /* have a pixel bitmap */
setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
else /* should have a bytemap */
if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
now _redraw_ line one col at a time to avoid "gaps"
-------------------------------------------------------------------------- */
if ( 1 )
for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
{
if ( !isbar && !isline ) /* neither vert nor horiz */
{ xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */
lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
icol,xrow,lorow,hirow);
ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
if ( irow<0 || irow>=rp->height
|| icol<0 || icol>=rp->width ) /* bounds check */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else continue; /*or just go on to next row*/
else
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
end_of_job:
return ( isfatal? (ipixheight-1 is bottom-most)
* col1 (I) double containing col at which
* line will end (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the line will be
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o Recurses, drawing left- and right-halves of line
* until a horizontal or vertical segment is found
* ======================================================================= */
/* --- entry point --- */
int line_recurse ( raster *rp, double row0, double col0,
double row1, double col1, int thickness )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
double delrow = fabs(row1-row0), /* 0 if line horizontal */
delcol = fabs(col1-col0), /* 0 if line vertical */
tolerance = 0.5; /* draw line when it goes to point */
double midrow = 0.5*(row0+row1), /* midpoint row */
midcol = 0.5*(col0+col1); /* midpoint col */
/* -------------------------------------------------------------------------
recurse if either delta > tolerance
-------------------------------------------------------------------------- */
if ( delrow > tolerance /* row hasn't converged */
|| delcol > tolerance ) /* col hasn't converged */
{ line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
return ( 1 ); }
/* -------------------------------------------------------------------------
draw converged point
-------------------------------------------------------------------------- */
setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
return ( 1 );
} /* --- end-of-function line_recurse() --- */
/* ==========================================================================
* Function: circle_raster ( rp, row0, col0, row1, col1,
* thickness, quads )
* Purpose: Draw quad(rant)s of an ellipse in box determined by
* diagonally opposite corner points (row0,col0) and
* (row1,col1), of thickness pixels in existing raster rp.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which an ellipse
* will be drawn
* row0 (I) int containing 1st corner row bounding ellipse
* (0 is topmost)
* col0 (I) int containing 1st corner col bounding ellipse
* (0 is leftmost)
* row1 (I) int containing 2nd corner row bounding ellipse
* (rp->height-1 is bottom-most)
* col1 (I) int containing 2nd corner col bounding ellipse
* (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the ellipse arc line will be
* quads (I) char * to null-terminated string containing
* any subset/combination of "1234" specifying
* which quadrant(s) of ellipse to draw.
* NULL ptr draws all four quadrants;
* otherwise 1=upper-right quadrant,
* 2=uper-left, 3=lower-left, 4=lower-right,
* i.e., counterclockwise from 1=positive quad.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if ellipse drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o row0==row1 or col0==col1 are errors
* o using ellipse equation x^2/a^2 + y^2/b^2 = 1
* ======================================================================= */
/* --- entry point --- */
int circle_raster ( raster *rp, int row0, int col0,
int row1, int col1, int thickness, char *quads )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- lower-left and upper-right bounding points (in our coords) --- */
int lorow = min2(row0,row1), /* lower bounding row (top of box) */
locol = min2(col0,col1), /* lower bounding col (left of box)*/
hirow = max2(row0,row1), /* upper bounding row (bot of box) */
hicol = max2(col0,col1); /* upper bounding col (right of box)*/
/* --- a and b ellipse params --- */
int width = hicol-locol+1, /* width of bounding box */
height= hirow-lorow+1, /* height of bounding box */
islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
double a = ((double)width)/2.0, /* x=a when y=0 */
b = ((double)height)/2.0, /* y=b when x=0 */
abmajor = (islandscape? a : b), /* max2(a,b) */
abminor = (islandscape? b : a), /* min2(a,b) */
abmajor2 = abmajor*abmajor, /* abmajor^2 */
abminor2 = abminor*abminor; /* abminor^2 */
/* --- other stuff --- */
int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
iminor=0, nminor=min2(width,height); /* solved index on minor axis */
int irow, icol, /* raster indexes at circumference */
rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/
double midrow= ((double)(row0+row1))/2.0, /* center row */
midcol= ((double)(col0+col1))/2.0; /* center col */
double xy, xy2, /* major axis ellipse coord */
yx2, yx; /* solved minor ellipse coord */
int isokay = 1; /* true if no pixels out-of-bounds */
char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */
int circle_recurse(), isrecurse=1; /* true to draw ellipse recursively*/
/* -------------------------------------------------------------------------
pixel-by-pixel along positive major axis, quit when it goes negative
-------------------------------------------------------------------------- */
if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */
if ( msgfp!=NULL && msglevel>=39 ) /* debugging */
fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
width,height,quads);
if ( nmajor < 1 ) isokay = 0; /* problem with input args */
else
{
if ( isrecurse ) /* use recursive algorithm */
{
for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
{
double theta0=0.0, theta1=0.0; /* set thetas based on quadrant */
switch ( *qptr ) /* check for quadrant 1,2,3,4 */
{ default: /* unrecognized, assume quadrant 1 */
case '1': theta0= 0.0; theta1= 90.0; break; /* first quadrant */
case '2': theta0= 90.0; theta1=180.0; break; /* second quadrant */
case '3': theta0=180.0; theta1=270.0; break; /* third quadrant */
case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
} /* --- end-of-for(qptr) --- */
return ( 1 );
} /* --- end-of-if(isrecurse) --- */
for ( imajor=(nmajor+1)/2; ; imajor-- )
{
/* --- xy is coord along major axis, yx is "solved" along minor axis --- */
xy = ((double)imajor); /* xy = abmajor ... 0 */
if ( xy < 0.0 ) break; /* negative side symmetrical */
yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */
iminor = iround(yx); /* nearest integer */
/* --- set pixels for each requested quadrant --- */
for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
{
rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
switch ( *qptr ) /* check for quadrant 1,2,3,4 */
{ default: break; /* unrecognized, assume quadrant 1 */
case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
icol = iround(midcol + (double)csign*(islandscape?xy:yx));
icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
imajor,iminor,*qptr,irow,icol);
if ( irow<0 || irow>=rp->height /* row outside raster */
|| icol<0 || icol>=rp->width ) /* col outside raster */
{ isokay = 0; /* signal out-of-bounds pixel */
continue; } /* but still try remaining points */
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(qptr) --- */
} /* --- end-of-for(imajor) --- */
/* ------------------------------------------------------------------------
now do it _again_ along minor axis to avoid "gaps"
------------------------------------------------------------------------- */
if ( 1 && iminor>0 )
for ( iminor=(nminor+1)/2; ; iminor-- )
{
/* --- yx is coord along minor axis, xy is "solved" along major axis --- */
yx = ((double)iminor); /* yx = abminor ... 0 */
if ( yx < 0.0 ) break; /* negative side symmetrical */
xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */
imajor = iround(xy); /* nearest integer */
/* --- set pixels for each requested quadrant --- */
for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
{
rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
switch ( *qptr ) /* check for quadrant 1,2,3,4 */
{ default: break; /* unrecognized, assume quadrant 1 */
case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
icol = iround(midcol + (double)csign*(islandscape?xy:yx));
icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
iminor,imajor,*qptr,irow,icol);
if ( irow<0 || irow>=rp->height /* row outside raster */
|| icol<0 || icol>=rp->width ) /* col outside raster */
{ isokay = 0; /* signal out-of-bounds pixel */
continue; } /* but still try remaining points */
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(qptr) --- */
} /* --- end-of-for(iminor) --- */
} /* --- end-of-if/else(nmajor<1) --- */
return ( isokay );
} /* --- end-of-function circle_raster() --- */
/* ==========================================================================
* Function: circle_recurse ( rp, row0, col0, row1, col1,
* thickness, theta0, theta1 )
* Purpose: Recursively draws arc theta0<=theta<=theta1 of the ellipse
* in box determined by diagonally opposite corner points
* (row0,col0) and (row1,col1), of thickness pixels in raster rp.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which an ellipse
* will be drawn
* row0 (I) int containing 1st corner row bounding ellipse
* (0 is topmost)
* col0 (I) int containing 1st corner col bounding ellipse
* (0 is leftmost)
* row1 (I) int containing 2nd corner row bounding ellipse
* (rp->height-1 is bottom-most)
* col1 (I) int containing 2nd corner col bounding ellipse
* (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the ellipse arc line will be
* theta0 (I) double containing first angle -360 -> +360
* theta1 (I) double containing second angle -360 -> +360
* 0=x-axis, positive moving counterclockwise
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if ellipse drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o row0==row1 or col0==col1 are errors
* o using ellipse equation x^2/a^2 + y^2/b^2 = 1
* Then, with x=r*cos(theta), y=r*sin(theta), ellipse
* equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
* ======================================================================= */
/* --- entry point --- */
int circle_recurse ( raster *rp, int row0, int col0,
int row1, int col1, int thickness, double theta0, double theta1 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- lower-left and upper-right bounding points (in our coords) --- */
int lorow = min2(row0,row1), /* lower bounding row (top of box) */
locol = min2(col0,col1), /* lower bounding col (left of box)*/
hirow = max2(row0,row1), /* upper bounding row (bot of box) */
hicol = max2(col0,col1); /* upper bounding col (right of box)*/
/* --- a and b ellipse params --- */
int width = hicol-locol+1, /* width of bounding box */
height= hirow-lorow+1; /* height of bounding box */
double a = ((double)width)/2.0, /* col x=a when row y=0 */
b = ((double)height)/2.0, /* row y=b when col x=0 */
ab=a*b, a2=a*a, b2=b*b; /* product and squares */
/* --- arc parameters --- */
double rads = 0.017453292, /* radians per degree = 1/57.29578 */
lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
xlo=rlo*locos, ylo=rlo*losin, /*col,row pixel coords for lotheta*/
xhi=rhi*hicos, yhi=rhi*hisin, /*col,row pixel coords for hitheta*/
xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
tolerance = 0.5; /* convergence tolerance */
/* -------------------------------------------------------------------------
recurse if either delta > tolerance
-------------------------------------------------------------------------- */
if ( ydelta > tolerance /* row hasn't converged */
|| xdelta > tolerance ) /* col hasn't converged */
{ double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta); /*lo*/
circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
/* -------------------------------------------------------------------------
draw converged point
-------------------------------------------------------------------------- */
else
{ double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi), /* relative to center*/
centerrow = 0.5*((double)(lorow+hirow)), /* ellipse y-center */
centercol = 0.5*((double)(locol+hicol)), /* ellipse x-center */
midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
return ( 1 );
} /* --- end-of-function circle_recurse() --- */
/* ==========================================================================
* Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
* Purpose: Recursively draw bezier from r0,c0 to r1,c1
* (with tangent point rt,ct) in existing raster rp.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which a line
* will be drawn
* r0 (I) double containing row at which
* bezier will start (0 is topmost)
* c0 (I) double containing col at which
* bezier will start (0 is leftmost)
* r1 (I) double containing row at which
* bezier will end (rp->height-1 is bottom-most)
* c1 (I) double containing col at which
* bezier will end (rp->width-1 is rightmost)
* rt (I) double containing row for tangent point
* ct (I) double containing col for tangent point
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o Recurses, drawing left- and right-halves of bezier curve
* until a point is found
* ======================================================================= */
/* --- entry point --- */
int bezier_raster ( raster *rp, double r0, double c0,
double r1, double c1, double rt, double ct )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
double delrow = fabs(r1-r0), /* 0 if same row */
delcol = fabs(c1-c0), /* 0 if same col */
tolerance = 0.5; /* draw curve when it goes to point*/
double midrow = 0.5*(r0+r1), /* midpoint row */
midcol = 0.5*(c0+c1); /* midpoint col */
int irow=0, icol=0; /* point to be drawn */
int status = 1; /* return status */
/* -------------------------------------------------------------------------
recurse if either delta > tolerance
-------------------------------------------------------------------------- */
if ( delrow > tolerance /* row hasn't converged */
|| delcol > tolerance ) /* col hasn't converged */
{ bezier_raster(rp, r0,c0, /* left half */
0.5*(rt+midrow), 0.5*(ct+midcol),
0.5*(r0+rt), 0.5*(c0+ct) );
bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
r1,c1,
0.5*(r1+rt), 0.5*(c1+ct) );
return ( 1 ); }
/* -------------------------------------------------------------------------
draw converged point
-------------------------------------------------------------------------- */
/* --- get integer point --- */
irow = iround(midrow); /* row pixel coord */
icol = iround(midcol); /* col pixel coord */
/* --- bounds check --- */
if ( irow>=0 && irowheight /* row in bounds */
&& icol>=0 && icolwidth ) /* col in bounds */
setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/
else status = 0; /* bad status if out-of-bounds */
return ( status );
} /* --- end-of-function bezier_raster() --- */
/* ==========================================================================
* Function: border_raster ( rp, ntop, nbot, isline, isfree )
* Purpose: Allocate a new raster containing a copy of input rp,
* along with ntop extra rows at top and nbot at bottom,
* and whose width is either adjusted correspondingly,
* or is automatically enlarged to a multiple of 8
* with original bitmap centered
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster on which a border
* is to be placed
* ntop (I) int containing number extra rows at top.
* if negative, abs(ntop) used, and same
* number of extra cols added at left.
* nbot (I) int containing number extra rows at bottom.
* if negative, abs(nbot) used, and same
* number of extra cols added at right.
* isline (I) int containing 0 to leave border pixels clear
* or >0 to draw a line around border of width
* isline.
* isfree (I) int containing true to free rp before return
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to bordered raster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
raster *border_raster ( raster *rp, int ntop, int nbot,
int isline, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/
int rastput(); /* overlay rp in new bordered raster */
int width = (rp==NULL?0:rp->width), /* 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(); /* free input rp if isfree is true */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
if ( rp == NULL ) goto end_of_job; /* no input raster provided */
if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
{ bp=rp; goto end_of_job; } /* return ascii string unchanged */
/* --- check for negative args --- */
if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
/* --- adjust height for ntop and nbot margins --- */
height += (ntop+nbot); /* adjust height for margins */
/* --- adjust width for left and right margins --- */
if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/
{ /* --- adjust width (and leftmargin) as requested by caller -- */
if ( istopneg ) { width += ntop; leftmargin = ntop; }
if ( isbotneg ) width += nbot; }
else
{ /* --- or adjust width (and leftmargin) to whole number of bytes --- */
leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
width += leftmargin; /* width now multiple of 8 */
leftmargin /= 2; } /* center original raster */
/* -------------------------------------------------------------------------
allocate bordered raster, and embed rp within it
-------------------------------------------------------------------------- */
/* --- allocate bordered raster --- */
if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/
== (raster *)NULL ) goto end_of_job; /* and quit if failed */
/* --- embed rp in it --- */
rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */
/* -------------------------------------------------------------------------
draw border if requested
-------------------------------------------------------------------------- */
if ( isline )
{ int irow, icol, nthick=isline; /*height,width index, line thickness*/
/* --- draw left- and right-borders --- */
for ( irow=0; 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 ( 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.
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct for which an
* ascii dump is to be constructed.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int type_raster ( raster *rp, FILE *fp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
static int display_width = 72; /* max columns for display */
static char display_chars[16] = /* display chars for bytemap */
{ ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
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 */
|| (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 */
while ( width > display_width-2 ) /* too big for one line */
{ fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
string += (display_width-2); /* bump string past displayed chars*/
width -= (display_width-2); } /* decrement remaining width */
fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
return ( 1 );
} /* --- end-of-if(isstring) --- */
/* --------------------------------------------------------------------------
display ascii dump of bitmap image (in segments if display_width < rp->width)
-------------------------------------------------------------------------- */
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 */
if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
scan_width = hicol-locol+1; /* #chars in this scan */
if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
/* ------------------------------------------------------------------------
display all scan lines for this local...hicol segment range
------------------------------------------------------------------------ */
for ( irow=0; irowheight; irow++ ) /* all scan lines for col range */
{
/* --- allocations and declarations --- */
int ipix, /* pixmap[] index for this scan */
lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
/* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
for ( ipix=0; ipixpixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
else /* should have a bytemap */
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 --- */
fprintf(fp,"%.*s\n",scan_width,scanline);
} /* --- end-of-for(irow) --- */
} /* --- end-of-while(hicolwidth) --- */
/* -------------------------------------------------------------------------
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() --- */
/* ==========================================================================
* Function: type_bytemap ( bp, grayscale, width, height, fp )
* Purpose: Emit an ascii dump representing bp, on fp.
* --------------------------------------------------------------------------
* Arguments: bp (I) intbyte * to bytemap for which an
* ascii dump is to be constructed.
* grayscale (I) int containing #gray shades, 256 for 8-bit
* width (I) int containing #cols in bytemap
* height (I) int containing #rows in bytemap
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int type_bytemap ( intbyte *bp, int grayscale,
int width, int height, FILE *fp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
static int display_width = 72; /* max columns for display */
int byte_width = 3, /* cols to display byte (ff+space) */
maxbyte = 0; /* if maxbyte<16, set byte_width=2 */
int white_byte = 0, /* show dots for white_byte's */
black_byte = grayscale-1; /* show stars for black_byte's */
char scanline[133]; /* ascii image for one scan line */
int scan_width, /* #chars in scan (<=display_width)*/
scan_cols; /* #cols in scan (hicol-locol+1) */
int ibyte, /* bp[] index */
irow, locol,hicol=(-1); /* height index, width indexes */
/* --------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- check for ascii string --- */
if ( isstring ) /* bp has ascii string, not raster */
{ width = strlen((char *)bp); /* #chars in ascii string */
height = 1; } /* default */
/* --- see if we can get away with byte_width=1 --- */
for ( ibyte=0; ibytewidth)
-------------------------------------------------------------------------- */
while ( (locol=hicol+1) < width ) /*start where prev segment left off*/
{
/* --- set hicol for this pass (locol set above) --- */
hicol += display_width/byte_width; /* show as much as display allows */
if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
scan_cols = hicol-locol+1; /* #cols in this scan */
scan_width = byte_width*scan_cols; /* #chars in this scan */
if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
/* ------------------------------------------------------------------------
display all scan lines for this local...hicol segment range
------------------------------------------------------------------------ */
for ( irow=0; irow 1 ) /* don't blank out single char */
scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
if ( byteval != white_byte /* format bytes that are non-white */
&& byteval != black_byte ) /* and that are non-black */
sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
/* --- display completed scan line --- */
fprintf(fp,"%.*s\n",scan_width,scanline);
} /* --- end-of-for(irow) --- */
} /* --- end-of-while(hicolbitmap image
-------------------------------------------------------------------------- */
/* --- first redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- check for ascii string --- */
if ( isstring ) /* pixmap has string, not raster */
return ( 0 ); /* can't handle ascii string */
/* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
title,rp->width, title,rp->height );
fprintf( fp, "static char %s_bits[] = {\n", title );
hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */
fprintf (fp,"};\n"); /* ending with "};" for C array */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
return ( 1 );
} /* --- end-of-function xbitmap_raster() --- */
/* ==========================================================================
* Function: 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.
* --------------------------------------------------------------------------
* Arguments: cp (I) ptr to chardef struct for which
* a C struct is to be generated.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char field[64]; /* field within output line */
int cstruct_raster(), /* emit a raster */
emit_string(); /* emit a string and comment */
/* -------------------------------------------------------------------------
emit charnum, location, name / hirow, hicol, lorow, locol
-------------------------------------------------------------------------- */
/* --- charnum, location, name --- */
sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
emit_string ( fp, col1, field, "character number, location");
/* --- toprow, topleftcol, botrow, botleftcol --- */
sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
/* -------------------------------------------------------------------------
emit raster and chardef's closing brace, and then return to caller
-------------------------------------------------------------------------- */
cstruct_raster(&cp->image,fp,col1+4); /* emit raster */
emit_string ( fp, 0, " }", NULL); /* emit closing brace */
return ( 1 ); /* back to caller with 1=okay, 0=failed */
} /* --- end-of-function cstruct_chardef() --- */
/* ==========================================================================
* Function: cstruct_raster ( rp, fp, col1 )
* Purpose: Emit a C struct of rp on fp, starting in col1.
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct for which
* a C struct is to be generated.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int cstruct_raster ( raster *rp, FILE *fp, int col1 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char field[64]; /* field within output line */
char typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
int hex_bitmap(); /* to emit raster bitmap */
int emit_string(); /* emit a string and comment */
/* -------------------------------------------------------------------------
emit width and height
-------------------------------------------------------------------------- */
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
-------------------------------------------------------------------------- */
hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */
emit_string ( fp, 0, " }", NULL); /* emit closing brace */
return ( 1 ); /* back to caller with 1=okay, 0=failed */
} /* --- end-of-function cstruct_raster() --- */
/* ==========================================================================
* Function: hex_bitmap ( rp, fp, col1, isstr )
* Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1.
* If isstr (is string) is true, the dump is of the form
* "\x01\x02\x03\x04\x05..."
* Otherwise, if isstr is false, the dump is of the form
* 0x01,0x02,0x03,0x04,0x05...
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct for which
* a hex dump is to be constructed.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* isstr (I) int specifying dump format as described above
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int ibyte, /* 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 */
int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
/* --------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- emit initial stub if wanted --- */
if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
/* --------------------------------------------------------------------------
emit hex dump of rp->bitmap image
-------------------------------------------------------------------------- */
if ( isstr ) fprintf(fp,"\""); /* opening " before first line */
for ( ibyte=0; ibytepixmap)[ibyte]); /*print byte as hex char*/
else /* comma-separated format wanted */
fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/
/* --- add a separator and newline, etc, as necessary --- */
if ( ibyte < nbytes-1) /* not the last byte yet */
{
if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
if ( (ibyte+1)%ncols==0 ) { /* need new line after every ncols */
if ( !isstr ) /* for hex numbers format ... */
fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
else /* for string format... */
fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
} /* --- end-of-if(ibyte 6 ) /* can fit all or part of comment */
sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
'*', fieldlen-6,comment, '*');
col1 = linelen; } /* indicate line filled */
/* --- line completed --- */
line[col1] = '\000'; /* null-terminate completed line */
/* -------------------------------------------------------------------------
emit line, then back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
/* --- first redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- emit line (and optional newline) --- */
fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */
if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/
return ( 1 );
} /* --- end-of-function emit_string() --- */
/* ==========================================================================
* Function: gftobitmap ( gf )
* Purpose: convert .gf-like pixmap to bitmap image
* --------------------------------------------------------------------------
* Arguments: gf (I) raster * to struct in .gf-format
* --------------------------------------------------------------------------
* Returns: ( raster * ) image-format raster * if successful,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
raster *gftobitmap ( raster *gf )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
int width=0, height=0, totbits=0; /* gf->width, 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
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing symbol
* whose corresponding mathchardef is wanted
* --------------------------------------------------------------------------
* Returns: ( mathchardef * ) pointer to struct defining symbol,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o Input symbol need only contain a leading substring to match,
* e.g., \gam passed in symbol will match \gamma in the table.
* If the table contains two or more possible matches,
* the shortest is returned, e.g., input \e will return with
* data for \eta rather than \epsilon. To get \epsilon,
* you must pass a leading substring long enough to eliminate
* shorter table matches, i.e., in this case \ep
* ======================================================================= */
/* --- entry point --- */
mathchardef *get_symdef ( char *symbol )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
mathchardef *symdefs = symtable; /* table of mathchardefs */
int 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 */
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"},
/* --- 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 */
for ( idef=0; ; idef++ ) { /* lookup symbol in displaysyms */
char *fromsym = displaysyms[idef][0], /* look for this symbol */
*tosym = displaysyms[idef][1]; /* and xlate it to this symbol */
if ( fromsym == NULL ) break; /* end-of-table */
if ( !strcmp(symbol,fromsym) ) /* found a match */
{ if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
{ fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
isdisplaystyle,symbol,tosym); fflush(msgfp); }
symbol = tosym; /* so look up tosym instead */
symlen = strlen(symbol); /* reset symbol length */
break; } /* no need to search further */
} /* --- end-of-for(idef) --- */
/* -------------------------------------------------------------------------
search symdefs[] in order for first occurrence of symbol
-------------------------------------------------------------------------- */
for ( idef=0; ;idef++ ) /* until trailer record found */
if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
else /* check against caller's symbol */
if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */
|| (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 ( 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[]*/
} /* --- 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-5 for desired size
* --------------------------------------------------------------------------
* Returns: ( chardef * ) pointer to struct defining symbol at size,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o if size unavailable, the next-closer-to-normalsize
* is returned instead.
* ======================================================================= */
/* --- entry point --- */
chardef *get_chardef ( mathchardef *symdef, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
fontfamily *fonts = fonttable; /* table of font families */
chardef **fontdef, /*tables for desired font, by size*/
*gfdata = (chardef *)NULL; /* chardef for symdef,size */
int ifont; /* fonts[] index */
int family, charnum; /* indexes retrieved from symdef */
int sizeinc = 0, /*+1 or -1 to get closer to normal*/
normalsize = 2; /* this size always present */
int isBig = 0; /*true if symbol's 1st char is upper*/
char *symptr = NULL; /* look for 1st alpha of symbol */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- check symdef --- */
if ( symdef == NULL ) 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 */
/* --- check for supersampling --- */
if ( issupersampling ) /* check for supersampling fonts */
if ( fonts != ssfonttable ) /* uh oh--probably internal error */
{ fonts = ssfonttable; } /* force it */
/* --- check requested size, and set size increment --- */
if ( 0 && issupersampling ) /* set size index for supersampling */
size = LARGESTSIZE+1; /* index 1 past largest size */
else /* low pass indexes 0...LARGESTSIZE */
{
if( size<0 ) size = 0; /* size was definitely too small */
if( size>LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */
if( sizenormalsize ) sizeinc = (-1); /*or next smaller if size too large*/
}
/* --- check for really big symbol (1st char of symbol name uppercase) --- */
for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
if ( isalpha(*symptr) ) /* found leading alpha char */
{ isBig = isupper(*symptr); /* is 1st char of name uppercase? */
if ( !isBig /* 1st char lowercase */
&& strlen(symptr) >= 4 ) /* but followed by at least 3 chars */
isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
|| !memcmp(symptr,"bigg",4); /* or with bigg */
break; } /* don't check beyond 1st char */
/* -------------------------------------------------------------------------
find font family in table of fonts[]
-------------------------------------------------------------------------- */
/* --- look up font family --- */
for ( ifont=0; ;ifont++ ) /* until trailer record found */
if ( fonts[ifont].family < 0 ) { /* 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 */
/* -------------------------------------------------------------------------
get font in desired size, or closest available size, and return symbol
-------------------------------------------------------------------------- */
/* --- get font in desired size --- */
while ( 1 ) /* find size or closest available */
if ( fontdef[size] != NULL ) break; /* found available size */
else /* adjust size closer to normal */
if ( size == NORMALSIZE /* already normal so no more sizes,*/
|| 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 --- */
gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/
/* -------------------------------------------------------------------------
kludge to tweak CMEX10 (which appears to have incorrect descenders)
-------------------------------------------------------------------------- */
if ( family == CMEX10 ) /* cmex10 needs tweak */
{ int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
gfdata->botrow = (isBig? (-height/3) : (-height/4));
gfdata->toprow = gfdata->botrow + gfdata->image.height; }
/* -------------------------------------------------------------------------
return subraster containing chardef data for symbol in requested size
-------------------------------------------------------------------------- */
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() --- */
/* ==========================================================================
* Function: get_charsubraster ( symdef, size )
* Purpose: returns new subraster ptr containing
* data for symdef at given size
* --------------------------------------------------------------------------
* Arguments: symdef (I) mathchardef * corresponding to symbol whose
* corresponding chardef 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 wraps a subraster envelope around get_chardef()
* ======================================================================= */
/* --- entry point --- */
subraster *get_charsubraster ( mathchardef *symdef, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
int get_baseline(); /* baseline of gfdata */
subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
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 */
/* -------------------------------------------------------------------------
look up chardef for symdef at size, and embed data (gfdata) in subraster
-------------------------------------------------------------------------- */
if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */
!= NULL ) /* and check that we found it */
if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
!= NULL ) /* and check that we succeeded */
{
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 */
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 */
int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
if ( status ) /* supersampled successfully */
{ int baseline = sp->baseline; /* baseline before supersampling */
int height = gfdata->image.height; /* #rows before supersampling */
sp->image = aa; /* replace chardef with ss image */
if ( baseline >= height-1 ) /* baseline at bottom of char */
sp->baseline = aa->height -1; /* so keep it at bottom */
else /* char has descenders */
sp->baseline /= shrinkfactor; /* rescale baseline */
sp->type = IMAGERASTER; } /* character is an image raster */
} /* --- end-of-if(issupersampling) --- */
} /* --- end-of-if(sp!=NULL) --- */
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
* --------------------------------------------------------------------------
* Arguments: gfdata (I) chardef * containing chardef for symbol
* whose baseline is wanted
* --------------------------------------------------------------------------
* Returns: ( int ) baseline for symdef,
* or -1 for any error
* --------------------------------------------------------------------------
* Notes: o Unlike TeX, the top-left corners of our rasters are (0,0),
* with (row,col) increasing as you move down and right.
* Baselines are calculated with respect to this scheme,
* so 0 would mean the very top row is on the baseline
* and everything else descends below the baseline.
* ======================================================================= */
/* --- entry point --- */
int get_baseline ( chardef *gfdata )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int /*toprow = gfdata->toprow,*/ /*TeX top row from .gf file info*/
botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/
height = gfdata->image.height; /* #rows comprising symbol */
/* -------------------------------------------------------------------------
give caller baseline
-------------------------------------------------------------------------- */
return ( (height-1) + botrow ); /* note: descenders have botrow<0 */
} /* --- end-of-function get_baseline() --- */
/* ==========================================================================
* Function: get_delim ( char *symbol, int height, int family )
* Purpose: returns subraster corresponding to the samllest
* character containing symbol, but at least as large as height,
* and in caller's family (if specified).
* If no symbol character as large as height is available,
* then the largest availabale character is returned instead.
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing (substring of) desired
* symbol, e.g., if symbol="(", then any
* mathchardef like "(" or "\\(", etc, match.
* height (I) int containing minimum acceptable height
* for returned character
* family (I) int containing -1 to consider all families,
* or, e.g., CMEX10 for only that family
* --------------------------------------------------------------------------
* Returns: ( subraster * ) best matching character available,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o If height is passed as negative, its absolute value is used
* but the best-fit width is searched for (rather than height)
* ======================================================================= */
/* --- entry point --- */
subraster *get_delim ( char *symbol, int height, int family )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
mathchardef *symdefs = symtable; /* table of mathchardefs */
subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
subraster *make_delim(); /* construct delim if can't find it*/
chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */
char lcsymbol[256], *symptr, /* lowercase symbol for comparison */
*unescsymbol = symbol; /* unescaped symbol */
int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
deflen = 0; /* length of symdef (aka lcsymbol) */
int idef = 0, /* symdefs[] index */
bestdef = (-9999), /* index of best fit symdef */
bigdef = (-9999); /*index of biggest (in case no best)*/
int size = 0, /* size index 0...LARGESTSIZE */
bestsize = (-9999), /* index of best fit size */
bigsize = (-9999); /*index of biggest (in case no best)*/
int defheight, bestheight=9999, /* height of best fit symdef */
bigheight = (-9999); /*height of biggest(in case no best)*/
int iswidth = 0; /* true if best-fit width desired */
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
-------------------------------------------------------------------------- */
/* --- 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 */
if ( strstr(symbol,"sq") != NULL ) /* \sq symbol requested */
issq = 1; /* seq \sq signal */
if ( strstr(symbol,"oint") != NULL ) /* \oint symbol requested */
isoint = 1; /* seq \oint signal */
if ( *symbol=='\\' ) /* have leading \ */
{ unescsymbol = symbol+1; /* push past leading \ */
if ( --symlen < 1 ) return(sp); /* one less char */
if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
unescsymbol = bigint; /* but big version looks better */
if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
unescsymbol = bigoint; /* but big version looks better */
symlen = strlen(unescsymbol); /* explicitly recalculate length */
isunesc = 1; } /* signal leading escape removed */
} /* --- end-of-if(family) --- */
/* --- determine whether searching for best-fit height or width --- */
if ( height < 0 ) /* negative signals width search */
{ height = (-height); /* flip "height" positive */
iswidth = 1; } /* set flag for width search */
/* --- search symdefs[] for best-fit height (or width) --- */
for ( idef=0; ;idef++ ) /* until trailer record found */
{
char *defsym = symdefs[idef].symbol; /* local copies */
int deffam = symdefs[idef].family;
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||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*/
if ( 0 ) /* don't ignore case */
for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
if ((symptr=strstr(lcsymbol,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 */
symptr == lcsymbol /* caller's sym is a prefix */
&& deflen == symlen: /* and same length */
(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 */
if ( iswidth ) /* width search wanted instead... */
defheight = gfdata->image.width; /* ...so substitute width */
leftsymdef = &(symdefs[idef]); /* set symbol class, etc */
if ( defheight>=height && defheight= bigheight ) /* new biggest character */
{ bigdef=idef; bigsize=size; /* save indexes of biggest */
bigheight = defheight; } /* and save new big height */
} /* --- end-of-if(gfdata!=NULL) --- */
} /* --- end-of-if(family) --- */
} /* --- end-of-for(idef) --- */
/* -------------------------------------------------------------------------
construct subraster for best fit character, and return it to caller
-------------------------------------------------------------------------- */
if ( bestdef >= 0 ) /* found a best fit for caller */
sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
|| 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() --- */
/* ==========================================================================
* Function: make_delim ( char *symbol, int height )
* Purpose: constructs subraster corresponding to symbol
* exactly as large as height,
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing, e.g., if symbol="("
* for desired delimiter
* height (I) int containing height
* for returned character
* --------------------------------------------------------------------------
* Returns: ( subraster * ) constructed delimiter
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o If height is passed as negative, its absolute value is used
* and interpreted as width (rather than height)
* ======================================================================= */
/* --- entry point --- */
subraster *make_delim ( char *symbol, int height )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *sp = (subraster *)NULL, /* subraster returned to caller */
*new_subraster(); /* allocate subraster */
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 */
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, /* 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
-------------------------------------------------------------------------- */
/* --- determine whether constructing height or width --- */
if ( height < 0 ) /* negative "height" signals width */
{ width = height = (-height); /* flip height positive */
iswidth = 1; } /* set flag for width */
if ( height < 3 ) goto end_of_job; /* too small, must be error */
/* --- set default width (or height) accordingly --- */
if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
else width = (height+(aspectratio+1)/2)/aspectratio;
if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
|| 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 */
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 ( 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 */
{
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 */
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 */
|| (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 */
{
/* --- 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 ( 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) --- */
/* -------------------------------------------------------------------------
< > brackets
-------------------------------------------------------------------------- */
else
if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
|| (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
{
/* --- 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,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,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
if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */
|| (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */
|| (rp=strchr(symbol,'=')) != NULL /* left or right || bracket wanted */
|| (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
{
/* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
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 = (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 = (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) --- */
/* -------------------------------------------------------------------------
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 */
{ 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() --- */
/* ==========================================================================
* Function: texchar ( expression, chartoken )
* Purpose: scans expression, returning either its first character,
* or the next \sequence if that first char is \,
* and a pointer to the first expression char past that.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* chartoken (O) char * to null-terminated string returning
* either the first (non-whitespace) character
* of expression if that char isn't \, or else
* the \ and everything following it up to
* the next non-alphabetic character (but at
* least one char following the \ even if
* it's non-alpha)
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned chartoken,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o Does *not* skip leading whitespace, but simply
* returns any whitespace character as the next character.
* ======================================================================= */
/* --- entry point --- */
char *texchar ( char *expression, char *chartoken )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int esclen = 0, /*length of escape sequence*/
maxesclen = 128; /* max len of esc sequence */
char *ptoken = chartoken; /* ptr into chartoken */
int iprefix = 0; /* prefix index */
static char *prefixes[] = /*e.g., \big followed by ( */
{ /* "\\left", "\\right", */
"\\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 \
-------------------------------------------------------------------------- */
/* --- error check for end-of-string --- */
*ptoken = '\000'; /* init in case of error */
if ( expression == NULL ) return(NULL); /* nothing to scan */
if ( *expression == '\000' ) return(NULL); /* nothing to scan */
/* --- always returning first character (either \ or some other char) --- */
*ptoken++ = *expression++; /* here's first character */
/* --- if first char isn't \, then just return it to caller --- */
if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */
{ *ptoken = '\000'; /* add a null terminator */
goto end_of_job; } /* ptr past returned char */
if ( *expression == '\000' ) /* \ is very last char */
{ *chartoken = '\000'; /* flush bad trailing \ */
return(NULL); } /* and signal end-of-job */
/* -------------------------------------------------------------------------
we have an escape sequence, so return all alpha chars following \
-------------------------------------------------------------------------- */
/* --- accumulate chars until first non-alpha char found --- */
for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */
{ if ( esclen < maxesclen-3 ) /* more room in chartoken */
*ptoken++ = *expression; /*copy alpha char, bump ptr*/
expression++; } /* bump expression ptr */
/* --- if we have a prefix, append next texchar, e.g., \big( --- */
*ptoken = '\000'; /* set null for compare */
for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
{ char nextchar[256]; int nextlen=0; /* texchar after prefix */
skipwhite(expression); /* skip space after prefix*/
expression = texchar(expression,nextchar); /* get nextchar */
if ( (nextlen = strlen(nextchar)) > 0 ) /* #chars in nextchar */
{ strcpy(ptoken,nextchar); /* append nextchar */
ptoken += strlen(nextchar); /* point to null terminator*/
esclen += strlen(nextchar); } /* and bump escape length */
break; } /* stop checking prefixes */
/* --- every \ must be followed by at least one char, e.g., \[ --- */
if ( esclen < 1 ) /* \ followed by non-alpha */
*ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
*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 )
{ fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
fflush(msgfp); }
return ( expression ); /*ptr to 1st non-alpha char*/
} /* --- end-of-function texchar() --- */
/* ==========================================================================
* Function: texsubexpr (expression,subexpr,maxsubsz,
* left,right,isescape,isdelim)
* Purpose: scans expression, returning everything between a balanced
* left{...right} subexpression if the first non-whitespace
* char of expression is an (escaped or unescaped) left{,
* or just the next texchar() otherwise,
* and a pointer to the first expression char past that.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* subexpr (O) char * to null-terminated string returning
* either everything between a balanced {...}
* subexpression if the first char is {,
* or the next texchar() otherwise.
* maxsubsz (I) int containing max #bytes returned
* in subexpr buffer (0 means unlimited)
* left (I) char * specifying allowable left delimiters
* that begin subexpression, e.g., "{[(<"
* right (I) char * specifying matching right delimiters
* in the same order as left, e.g., "}])>"
* isescape (I) int controlling whether escaped and/or
* unescaped left,right are matched;
* see isbrace() comments below for details.
* isdelim (I) int containing true (non-zero) to return
* the leading left and trailing right delims
* (if any were found) along with subexpr,
* or containing false=0 to return subexpr
* without its delimiters
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned subexpr (see Notes),
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o If subexpr is of the form left{...right},
* the outer {}'s are returned as part of subexpr
* if isdelim is true; if isdelim is false the {}'s aren't
* returned. In either case the returned pointer is
* *always* bumped past the closing right}, even if
* that closing right} isn't returned in subexpr.
* o If subexpr is not of the form left{...right},
* the returned pointer is on the character immediately
* following the last character returned in subexpr
* o \. acts as LaTeX \right. and matches any \left(
* And it also acts as a LaTeX \left. and matches any \right)
* ======================================================================= */
/* --- entry point --- */
char *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
char *left, char *right, int isescape, int isdelim )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texchar(); /*next char (or \sequence) from expression*/
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=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
/* -------------------------------------------------------------------------
skip leading whitespace and just return the next char if it's not {
-------------------------------------------------------------------------- */
/* --- skip leading whitespace and error check for end-of-string --- */
*subexpr = '\000'; /* init in case of error */
if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/
skipwhite(expression); /* leading whitespace gone */
if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
/* --- set maxsubsz --- */
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 ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
return ( texchar(expression,subexpr) ); /* next char to caller */
else /* --- kludge for super/subscripts to accommodate texscripts() --- */
{ *subexpr++ = *expression; /* signal script */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression ); } } /* leave script in stream */
/* --- extract left and find matching right delimiter --- */
*leftdelim = *(expression+gotescape); /* the left( in expression */
if ( (gotescape && *leftdelim == '.') /* we have a left \. */
|| (gotescape && isanyright) ) /*or are matching any right*/
{ isleftdot = 1; /* so just set flag */
*leftdelim = '\000'; } /* and reset leftdelim */
else /* find matching \right */
if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
*rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */
else /* can't happen -- pgm bug */
return ( NULL ); /*just signal eoj to caller*/
/* -------------------------------------------------------------------------
accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
-------------------------------------------------------------------------- */
/* --- first initialize by bumping past left{ or \{ --- */
if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/
else expression++; /* always bump past left{ */
if ( gotescape ) { /*need to bump another char*/
if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
else expression++; } /* else just bump past it */
/* --- set maximum size for numerical arguments --- */
if ( 0 ) /* check turned on or off? */
if ( !isescape && !isdelim ) /*looking for numerical arg*/
maxsubsz = 96; /* set max arg size */
/* --- search for matching right} --- */
while ( 1 ) /*until balanced right} */
{
/* --- error check for end-of-string --- */
if ( *expression == '\000' ) /* premature end-of-string */
{ 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 ( gotescape ) /* need escaped right */
{ *subexpr++ = '\\'; /* set escape char */
*subexpr++ = '.'; } /* and fake \right. */
else /* escape not wanted */
*subexpr++ = *rightdelim; } /* so fake actual right */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression ); } /* back with final token */
/* --- check preceding char for escape --- */
if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */
prevescape = 1-prevescape; /* so flip escape flag */
else prevescape = 0; /* or turn flag off */
/* --- check for { and } (un/escaped as per leading left) --- */
if ( gotescape == prevescape ) /* escaped iff leading is */
{ /* --- check for (closing) right delim and see if we're done --- */
if ( isthischar(*expression,rightdelim) /* found a right} */
|| (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
|| (prevescape && isthischar(*expression,".")) ) /*or found \right. */
if ( --nestlevel < 1 ) /*\right balances 1st \left*/
{ if ( isdelim ) /*caller wants } in subexpr*/
*subexpr++ = *expression; /* so end subexpr with } */
else /*check for \ before right}*/
if ( prevescape ) /* have unwanted \ */
*(subexpr-1) = '\000'; /* so replace it with null */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression+1 ); } /* back with char after } */
/* --- check for (another) left{ --- */
if ( isthischar(*expression,leftdelim) /* found another left{ */
|| (isleftdot && isthischar(*expression,left)) ) /* any left{ */
nestlevel++;
} /* --- end-of-if(gotescape==prevescape) --- */
/* --- not done, so copy char to subexpr and continue with next char --- */
if ( ++subsz < maxsubsz-5 ) /* more room in subexpr */
*subexpr++ = *expression; /* so copy char and bump ptr*/
expression++; /* bump expression ptr */
} /* --- end-of-while(1) --- */
} /* --- end-of-function texsubexpr() --- */
/* ==========================================================================
* 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},
* or any (valid LaTeX) permutation of the above,
* and a pointer to the first expression char past "scripts"
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* subscript (O) char * to null-terminated string returning
* subscript (without _), if found, or "\000"
* superscript (O) char * to null-terminated string returning
* superscript (without ^), if found, or "\000"
* which (I) int containing 1 for subscript only,
* 2 for superscript only, >=3 for either/both
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned "scripts" (unchanged
* except for skipped whitespace if
* neither subscript nor superscript found),
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o an input expression like ^a^b_c will return superscript="b",
* i.e., totally ignoring all but the last "script" encountered
* ======================================================================= */
/* --- entry point --- */
char *texscripts ( char *expression, char *subscript,
char *superscript, int which )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(); /* next subexpression from expression */
int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
/* -------------------------------------------------------------------------
init "scripts"
-------------------------------------------------------------------------- */
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 ) {
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 */
|| subscript == NULL ) break; /* or no subscript buffer */
gotsub = 1; /* set subscript flag */
expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
else /* no _, check for ^ */
if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */
&& which>=2 ) /* and caller wants it */
{ if ( gotsup /* found 2nd superscript */
|| superscript == NULL ) break; /* or no superscript buffer*/
gotsup = 1; /* set superscript flag */
expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
else /* neither _ nor ^ */
return ( expression ); /*return ptr past "scripts"*/
} /* --- end-of-while(expression!=NULL) --- */
return ( expression );
} /* --- end-of-function texscripts() --- */
/* ==========================================================================
* Function: isbrace ( expression, braces, isescape )
* Purpose: checks leading char(s) of expression for a brace,
* either escaped or unescaped depending on isescape,
* except that { and } are always matched, if they're
* in braces, regardless of isescape.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing a valid LaTeX expression
* whose leading char(s) are checked for braces
* that begin subexpression, e.g., "{[(<"
* braces (I) char * specifying matching brace delimiters
* to be checked for, e.g., "{[(<" or "}])>"
* isescape (I) int containing 0 to match only unescaped
* braces, e.g., (...) or {...}, etc,
* or containing 1 to match only escaped
* braces, e.g., \(...\) or \[...\], etc,
* or containing 2 to match either.
* But note: if {,} are in braces
* then they're *always* matched whether
* escaped or not, regardless of isescape.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if the leading char(s) of expression
* is a brace, or 0 if not.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
int isbrace ( char *expression, char *braces, int isescape )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int gotescape = 0, /* true if leading char is an escape */
gotbrace = 0; /*true if first non-escape char is a brace*/
/* -------------------------------------------------------------------------
check for brace
-------------------------------------------------------------------------- */
/* --- first check for end-of-string 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 */
expression++; } /* and bump past escape */
/* --- check (maybe next char) for brace --- */
if ( isthischar(*expression,braces) ) /* expression is braced */
gotbrace = 1; /* so set flag accordingly */
if ( gotescape && *expression == '.' ) /* \. matches any brace */
gotbrace = 1; /* set flag */
/* --- check for TeX brace { or } --- */
if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
if ( isescape ) isescape = 2; /* reset escape flag */
/* -------------------------------------------------------------------------
back to caller
-------------------------------------------------------------------------- */
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 */
} /* --- end-of-function isbrace() --- */
/* ==========================================================================
* Function: preamble ( expression, size, subexpr )
* Purpose: parses $-terminated preamble, if present, at beginning
* of expression, re-setting size if necessary, and
* returning any other parameters besides size in subexpr.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing LaTeX expression possibly
* preceded by $-terminated preamble
* size (I/O) int * containing 0-4 default font size,
* and returning size modified by first
* preamble parameter (or unchanged)
* subexpr(O) char * returning any remaining preamble
* parameters past size
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to first char past preamble in expression
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o size can be any number >=0. If preceded by + or -, it's
* interpreted as an increment to input size; otherwise
* it's interpreted as the size.
* o if subexpr is passed as NULL ptr, then returned expression
* ptr will have "flushed" and preamble parameters after size
* ======================================================================= */
/* --- entry point --- */
char *preamble ( char *expression, int *size, char *subexpr )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char pretext[512], *prep=expression, /*pream from expression, ptr into it*/
*dollar, *comma; /* preamble delimiters */
int prelen = 0, /* preamble length */
sizevalue = 0, /* value of size parameter */
isfontsize = 0, /*true if leading fontsize present*/
isdelta = 0; /*true to increment passed size arg*/
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
if ( subexpr != NULL ) /* caller passed us an address */
*subexpr = '\000'; /* so init assuming no preamble */
if ( expression == NULL ) goto end_of_job; /* no input */
if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
/* -------------------------------------------------------------------------
process preamble if present
-------------------------------------------------------------------------- */
/*process_preamble:*/
if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
!= 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 */
memcpy(pretext,expression,prelen); /* local copy of preamble */
pretext[prelen] = '\000'; /* null-terminated */
if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
&& strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
/* --- skip any leading whitespace --- */
prep = pretext; /* start at beginning of preamble */
skipwhite(prep); /* skip any leading white space */
/* --- check for embedded , or leading +/- (either signalling size) --- */
if ( isthischar(*prep,"+-") ) /* have leading + or - */
isdelta = 1; /* so use size value as increment */
comma = strchr(pretext,','); /* , signals leading size param */
/* --- process leading size parameter if present --- */
if ( comma != NULL /* size param explicitly signalled */
|| isdelta || isdigit(*prep) ) { /* or inferred implicitly */
/* --- parse size parameter and reset size accordingly --- */
if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
sizevalue = atoi(prep); /* convert size string to integer */
if ( size != NULL ) /* caller passed address for size */
*size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
/* --- finally, set flag and shift size parameter out of preamble --- */
isfontsize = 1; /*set flag showing font size present*/
if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/
} /* --- 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*/
if ( subexpr != NULL ) /* caller passed us an address */
strcpy(subexpr,pretext); /*so return extra params to caller*/
/* --- finally, set prep to shift preamble out of expression --- */
prep = expression + prelen+1; /* set prep past $ in expression */
} /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
} /* --- end-of-if(prelen<65) --- */
} /* --- end-of-if(prelen>0) --- */
else { /* $ is first char of expression */
int ndollars = 0; /* number of $...$ pairs removed */
prep = expression; /* start at beginning of expression*/
while ( *prep == '$' ) { /* remove all matching $...$'s */
int explen = strlen(prep)-1; /* index of last char in expression*/
if ( explen < 2 ) break; /* no $...$'s left to remove */
if ( prep[explen] != '$' ) break; /* unmatched $ */
prep[explen] = '\000'; /* remove trailing $ */
prep++; /* and remove matching leading $ */
ndollars++; /* count another pair removed */
} /* --- end-of-while(*prep=='$') --- */
ispreambledollars = ndollars; /* set flag to fix \displaystyle */
if ( ndollars == 1 ) /* user submitted $...$ expression */
isdisplaystyle = 0; /* so set \textstyle */
if ( ndollars > 1 ) /* user submitted $$...$$ */
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
-------------------------------------------------------------------------- */
end_of_job:
return ( prep ); /*expression, or ptr past preamble*/
} /* --- end-of-function preamble() --- */
/* ==========================================================================
* Function: mimeprep ( expression )
* Purpose: preprocessor for mimeTeX input, e.g.,
* (a) removes comments,
* (b) converts \left( to \( and \right) to \),
* (c) xlates &html; special chars to equivalent latex
* Should only be called once (after unescape_url())
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char * to first char of null-terminated
* string containing mimeTeX/LaTeX expression,
* and returning preprocessed string
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to input expression,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
char *mimeprep ( char *expression )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *expptr=expression, /* ptr within expression */
*tokptr=NULL, /*ptr to token found in expression*/
*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*/
int xlateleft = 0; /* true to xlate \left and \right */
/* ---
* comments
* -------- */
char *leftptr=NULL; /* find leftcomment in expression */
static char *leftcomment = "%%", /* open comment */
*rightcomment[] = {"\n", "%%", NULL}; /* close comments */
/* ---
* special long (more than 1-char) \left and \right delimiters
* ----------------------------------------------------------- */
static char *leftfrom[] = /* xlate any \left suffix... */
{ "\\|", /* \left\| */
"\\{", /* \left\{ */
"\\langle", /* \left\langle */
NULL } ; /* --- end-of-leftfrom[] --- */
static char *leftto[] = /* ...to this instead */
{ "=", /* = */
"{", /* { */
"<", /* < */
NULL } ; /* --- end-of-leftto[] --- */
static char *rightfrom[] = /* xlate any \right suffix... */
{ "\\|", /* \right\| */
"\\}", /* \right\} */
"\\rangle", /* \right\rangle */
NULL } ; /* --- end-of-rightfrom[] --- */
static char *rightto[] = /* ...to this instead */
{ "=", /* = */
"}", /* } */
">", /* > */
NULL } ; /* --- end-of-rightto[] --- */
/* ---
* { \atop }-like commands
* ----------------------- */
char *atopsym=NULL; /* atopcommands[isymbol] */
static char *atopcommands[] = /* list of {a+b\command c+d}'s */
{ "\\over", /* plain tex for \frac */
"\\choose", /* binomial coefficient */
#ifndef NOATOP /*noatop preserves old mimeTeX rule*/
"\\atop",
#endif
NULL } ; /* --- end-of-atopcommands[] --- */
static char *atopdelims[] = /* delims for atopcommands[] */
{ NULL, NULL, /* \\over has no delims */
"\\left(", "\\right)", /* \\choose has ( ) delims*/
#ifndef NOATOP /*noatop preserves old mimeTeX rule*/
NULL, NULL, /* \\atop has no delims */
#endif
NULL, NULL } ; /* --- end-of-atopdelims[] --- */
/* ---
* html special/escape chars converted to latex equivalents
* -------------------------------------------------------- */
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
/* --------------------------------------------
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 Copyright (c) 2002-2009, John Forkosh Associates, Inc.}" },
{ "\\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 char termchar LaTeX equivalent...
-------------------------------------------- */
{ """, ";", "\"" }, /* " is first, " */
{ "&", ";", "&" },
{ "<", ";", "<" },
{ ">", ";", ">" },
/*{ "\", ";", "\\" },*/ /* backslash */
{ "&backslash",";", "\\" },
{ " ", ";", "~" },
{ "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
{ "¦", ";", "|" },
{ "±", ";", "\\pm" },
{ "²", ";", "{{}^2}" },
{ "³", ";", "{{}^3}" },
{ "µ", ";", "\\mu" },
{ "¹", ";", "{{}^1}" },
{ "¼", ";", "{\\frac14}" },
{ "½", ";", "{\\frac12}" },
{ "¾", ";", "{\\frac34}" },
{ "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
{ "Â", ";", "{\\rm~\\hat~A}" },
{ "Ã", ";", "{\\rm~\\tilde~A}" },
{ "Ä", ";", "{\\rm~\\ddot~A}" },
{ "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
{ "ã", ";", "{\\rm~\\tilde~a}" },
{ "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
{ "", ";", "{[\\&\\#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.}"},
{ "\\notin", NULL, "{\\not\\in}" },
{ "\\neq", NULL, "{\\not=}" },
{ "\\ne", NULL, "{\\not=}" },
{ "\\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, "\\\\" },
{ "\\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", "{{\\rm~e}^{#1}}" },
{ "det", "1", "{\\left|{#1}\\right|}" },
/* --------------------------------------------
LaTeX Constant termchar value...
-------------------------------------------- */
{ "\\thinspace", NULL, "2" },
{ "\\thinmathspace", NULL, "2" },
{ "\\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
-------------------------------------------------------------------------- */
expptr = expression; /* start search at beginning */
while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
{
char *rightsym=NULL; /* rightcomment[isymbol] */
expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
/* --- check for any closing rightcomment, in given precedent order --- */
if ( *expptr != '\000' ) /*have chars after this leftcomment*/
for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
{ tokptr += strlen(rightsym); /* first char after rightcomment */
if ( *tokptr == '\000' ) /*nothing after this rightcomment*/
{ *leftptr = '\000'; /*so terminate expr at leftcomment*/
break; } /* and stop looking for comments */
*leftptr = '~'; /* replace entire comment by ~ */
strcpy(leftptr+1,tokptr); /* and squeeze out comment */
goto next_comment; } /* stop looking for rightcomment */
/* --- no rightcomment after opening leftcomment --- */
*leftptr = '\000'; /* so terminate expression */
/* --- resume search past squeezed-out comment --- */
next_comment:
if ( *leftptr == '\000' ) break; /* reached end of expression */
expptr = leftptr+1; /*resume search after this comment*/
} /* --- end-of-while(leftptr!=NULL) --- */
/* -------------------------------------------------------------------------
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*/
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 ( 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=(!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 ( !isembedded ) /*embedded sequences not terminated*/
if ( htmlterm != NULL ) /* sequence may have terminator */
escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
/* --- 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 */
/* --- 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 */
char *optarg = args+1; /* ptr 1 char past #args digit 0-9 */
expptr = arg1ptr; /* ptr to beginning of next arg */
for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
{
char argsignal[32] = "#1", /* #1...#9 signals arg replacement */
*argsigptr = NULL; /* ptr to argsignal in abuff[] */
/* --- get argument value --- */
*argval = '\000'; /* init arg as empty string */
skipwhite(expptr); /* and skip leading white space */
if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
&& !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*/
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 ( 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(*expptr!='\000') --- */
/* --- 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*/
while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
} /* --- end-of-for(iarg) --- */
escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
} /* --- 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-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*/
{ 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 {a+b\atop c+d} to \atop{a+b}{c+d}
-------------------------------------------------------------------------- */
for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
{
int atoplen = strlen(atopsym); /* #chars in \atop */
expptr = expression; /* re-start search at beginning */
while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
{ char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
if ( msgfp!=NULL && msglevel>=999 )
{ fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
(int)(tokptr-expression),tokptr);
fflush(msgfp); }
if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
{ expptr = tokptr+atoplen; /* just resume search after prefix */
continue; } /* but don't process it */
leftbrace = findbraces(expression,tokptr); /* find left { */
rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
if ( leftbrace==NULL || rightbrace==NULL )
{ expptr += atoplen; continue; } /* skip command if didn't find */
else /* we have bracketed { \atop } */
{
int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
char arg[8192], command[8192]; /* left/right args, new \atop{}{} */
*command = '\000'; /* start with null string */
if (open!=NULL) strcat(command,open); /* add open delim if needed */
strcat(command,atopsym); /* add command with \atop */
arg[0] = '{'; /* arg starts with { */
memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
arg[leftlen+1] = '\000'; /* and null terminate it */
strcat(command,arg); /* concatanate {left-arg to \atop */
strcat(command,"}{"); /* close left-arg, open right-arg */
memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
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 */
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{}{}} */
expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
}
} /* --- end-of-while(tokptr!=NULL) --- */
} /* --- end-of-for(isymbol) --- */
/* -------------------------------------------------------------------------
back to caller with preprocessed expression
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */
{ fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
fflush(msgfp); }
return ( expression );
} /* --- end-of-function mimeprep() --- */
/* ==========================================================================
* Function: strchange ( int nfirst, char *from, char *to )
* Purpose: Changes the nfirst leading chars of `from` to `to`.
* For example, to change char x[99]="12345678" to "123ABC5678"
* call strchange(1,x+3,"ABC")
* --------------------------------------------------------------------------
* Arguments: nfirst (I) int containing #leading chars of `from`
* that will be replace by `to`
* from (I/O) char * to null-terminated string whose nfirst
* leading chars will be replaced by `to`
* to (I) char * to null-terminated string that will
* replace the nfirst leading chars of `from`
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to first char of input `from`
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o If strlen(to)>nfirst, from must have memory past its null
* (i.e., we don't do a realloc)
* ======================================================================= */
/* --- entry point --- */
char *strchange ( int nfirst, char *from, char *to )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
nshift = abs(tolen-nfirst); /*need to shift from left or right*/
/* -------------------------------------------------------------------------
shift from left or right to accommodate replacement of its nfirst chars by to
-------------------------------------------------------------------------- */
if ( tolen < nfirst ) /* shift left is easy */
strcpy(from,from+nshift); /* because memory doesn't overlap */
if ( tolen > nfirst ) /* need more room at start of from */
{ char *pfrom = from+strlen(from); /* ptr to null terminating from */
for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
*(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */
/* -------------------------------------------------------------------------
from has exactly the right number of free leading chars, so just put to there
-------------------------------------------------------------------------- */
if ( tolen != 0 ) /* make sure to not empty or null */
memcpy(from,to,tolen); /* chars moved into place */
return ( from ); /* changed string back to caller */
} /* --- end-of-function strchange() --- */
/* ==========================================================================
* Function: strreplace (char *string, char *from, char *to, int nreplace)
* Purpose: Changes the first nreplace occurrences of 'from' to 'to'
* in string, or all occurrences if nreplace=0.
* --------------------------------------------------------------------------
* Arguments: string (I/0) char * to null-terminated string in which
* occurrence of 'from' will be replaced by 'to'
* from (I) char * to null-terminated string
* to be replaced by 'to'
* to (I) char * to null-terminated string that will
* replace 'from'
* nreplace (I) int containing (maximum) number of
* replacements, or 0 to replace all.
* --------------------------------------------------------------------------
* Returns: ( int ) number of replacements performed,
* or 0 for no replacements or -1 for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
int strreplace ( char *string, char *from, char *to, int nreplace )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
char *pfrom = (char *)NULL, /*ptr to 1st char of from in string*/
*pstring = string, /*ptr past previously replaced from*/
*strchange(); /* change 'from' to 'to' */
int nreps = 0; /* #replacements returned to caller*/
/* -------------------------------------------------------------------------
repace occurrences of 'from' in string to 'to'
-------------------------------------------------------------------------- */
if ( string == (char *)NULL /* no input string */
|| (fromlen<1 && nreplace<=0) ) /* replacing empty string forever */
nreps = (-1); /* so signal error */
else /* args okay */
while (nreplace<1 || nreps 0 ) /* have 'from' string */
pfrom = strstr(pstring,from); /*ptr to 1st char of from in string*/
else pfrom = pstring; /*or empty from at start of string*/
if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
if ( strchange(fromlen,pfrom,to) /* leading 'from' changed to 'to' */
== (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
nreps++; /* count another replacement */
pstring = pfrom+tolen; /* pick up search after 'to' */
if ( *pstring == '\000' ) break; /* but quit at end of string */
} /* --- end-of-while() --- */
return ( nreps ); /* #replacements back to caller */
} /* --- end-of-function strreplace() --- */
/* ==========================================================================
* 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 */
strcpy(pwhite,pwhite+1); /* so squeeze it out */
while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
strcpy(pwhite,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 )
* 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
* --------------------------------------------------------------------------
* 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 )
{
/* -------------------------------------------------------------------------
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(0)strreplace(sbuff,"_","\\_",0); /* change all _'s to \_ */
strreplace(sbuff,"_","\\underline{\\qquad}",0); /* change all _'s 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,"^","{\\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
* first occurrence of delim will be found
* texchr (I) char * to null-terminated string that
* will be searched for
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to first char of texchr in string
* or NULL if not found or for any error.
* --------------------------------------------------------------------------
* Notes: o texchr should contain its leading \, e.g., "\\left"
* ======================================================================= */
/* --- entry point --- */
char *strtexchr ( char *string, char *texchr )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
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 */
while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
!= (char *)NULL ) /* found it */
if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */
== '\000' ) break; /* texchr at very end of string */
else /* if there are chars after texchr */
if ( isalpha(delim) /*texchr is prefix of longer symbol*/
|| 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*/
return ( ptexchr ); /* ptr to texchar back to caller */
} /* --- end-of-function strtexchr() --- */
/* ==========================================================================
* Function: findbraces ( char *expression, char *command )
* Purpose: If expression!=NULL, finds opening left { preceding command;
* if expression==NULL, finds closing right } after command.
* For example, to parse out {a+b\over c+d} call findbraces()
* twice.
* --------------------------------------------------------------------------
* Arguments: expression (I) NULL to find closing right } after command,
* or char * to null-terminated string to find
* left opening { preceding command.
* command (I) char * to null-terminated string whose
* first character is usually the \ of \command
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to either opening { or closing },
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
char *findbraces ( char *expression, char *command )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int isopen = (expression==NULL?0:1); /* true to find left opening { */
char *left="{", *right="}", /* delims bracketing {x\command y} */
*delim = (isopen?left:right), /* delim we want, { if isopen */
*match = (isopen?right:left), /* matching delim, } if isopen */
*brace = NULL; /* ptr to delim returned to caller */
int inc = (isopen?-1:+1); /* pointer increment */
int level = 1; /* nesting level, for {{}\command} */
char *ptr = command; /* start search here */
int setbrace = 1; /* true to set {}'s if none found */
/* -------------------------------------------------------------------------
search for left opening { before command, or right closing } after command
-------------------------------------------------------------------------- */
while ( 1 ) /* search for brace, or until end */
{
/* --- next char to check for delim --- */
ptr += inc; /* bump ptr left or right */
/* --- check for beginning or end of expression --- */
if ( isopen ) /* going left, check for beginning */
{ if ( ptr < expression ) break; } /* went before start of string */
else { if ( *ptr == '\000' ) break; } /* went past end of string */
/* --- don't check this char if it's escaped --- */
if ( !isopen || ptr>expression ) /* very first char can't be escaped*/
if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */
continue; /* so don't check this char */
/* --- check for delim --- */
if ( isthischar(*ptr,delim) ) /* found delim */
if ( --level == 0 ) /* and it's not "internally" nested*/
{ brace = ptr; /* set ptr to brace */
goto end_of_job; } /* and return it to caller */
/* --- check for matching delim --- */
if ( isthischar(*ptr,match) ) /* found matching delim */
level++; /* so bump nesting level */
} /* --- end-of-while(1) --- */
end_of_job:
if ( brace == (char *)NULL ) /* open{ or close} not found */
if ( setbrace ) /* want to force one at start/end? */
brace = ptr; /* { before expressn, } after cmmnd*/
return ( brace ); /*back to caller with delim or NULL*/
} /* --- end-of-function findbraces() --- */
#endif /* PART2 */
/* ---
* PART3
* ------ */
#if !defined(PARTS) || defined(PART3)
/* ==========================================================================
* Function: rasterize ( expression, size )
* Purpose: returns subraster corresponding to (a valid LaTeX) expression
* at font size
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be rasterized
* size (I) int containing 0-4 default font size
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o This is mimeTeX's "main" reusable entry point. Easy to use:
* just call it with a LaTeX expression, and get back a bitmap
* of that expression. Then do what you want with the bitmap.
* ======================================================================= */
/* --- entry point --- */
subraster *rasterize ( char *expression, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *preamble(), pretext[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 */
*rastparen(), /* handle parenthesized subexpr's */
*rastlimits(); /* handle sub/superscripted expr's */
subraster *rastcat(), /* concatanate atom subrasters */
*subrastcpy(), /* copy final result if a charaster*/
*new_subraster(); /* new subraster for isstring mode */
subraster *get_charsubraster(), /* character subraster */
*sp=NULL, *prevsp=NULL, /* raster for current, prev char */
*expraster = (subraster *)NULL; /* raster returned to caller */
int delete_subraster(); /* free everything before returning*/
int 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 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 */
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*/
double oldunitlength = unitlength; /* initial unitlength */
mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
recurlevel++; /* wind up one more recursion level*/
leftexpression = NULL; /* no leading left half yet */
isreplaceleft = 0; /* reset replaceleft flag */
if(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 >= 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
-------------------------------------------------------------------------- */
expression = preamble(expression,&size,pretext); /* size may be modified */
if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
fontsize = size; /* start at requested size */
if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/
if ( !ispreambledollars ) /* style fixed by $$...$$'s */
isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
/* -------------------------------------------------------------------------
build up raster one character (or subexpression) at a time
-------------------------------------------------------------------------- */
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 >= 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 */
/* --- check for parenthesized subexpression --- */
if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */
{ if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
== NULL ) continue; } /* flush it if failed to rasterize */
else /* --- single-character atomic token --- */
if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
{
/* --- first 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 */
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)); /* 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 */
{ 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 */
if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
== NULL ) continue; /* flush token if malloc failed */
sp->type = ASCIISTRING; /* set subraster type */
sp->symdef = symdef; /* and set symbol definition */
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(!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 >= 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 */
{ 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 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 */
/* --- bump count --- */
natoms++; /* bump #atoms count */
} /* --- end-of-while(expression!=NULL) --- */
/* -------------------------------------------------------------------------
back to caller with rasterized expression
-------------------------------------------------------------------------- */
end_of_job:
delete_subraster(prevsp); /* free last (if not a CHARASTER) */
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
{ fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
recurlevel,natoms);
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 --- */
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 */
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 */
recurlevel--; /* unwind one recursion level */
/* --- return final subraster to caller --- */
return ( expraster );
} /* --- end-of-function rasterize() --- */
/* ==========================================================================
* Function: rastparen ( subexpr, size, basesp )
* Purpose: parentheses handler, returns a subraster corresponding to
* parenthesized subexpression at font size
* --------------------------------------------------------------------------
* Arguments: subexpr (I) char ** to first char of null-terminated
* string beginning with a LEFTBRACES
* to be rasterized
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o This "handler" isn't in the mathchardef symbol table,
* but is called directly from rasterize(), as necessary.
* o Though subexpr is returned unchanged, it's passed as char **
* for consistency with other handlers. Ditto, basesp is unused
* but passed for consistency
* ======================================================================= */
/* --- entry point --- */
subraster *rastparen ( char **subexpr, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *expression = *subexpr; /* dereference subexpr to get char* */
int explen = strlen(expression); /* total #chars, including parens */
int isescape = 0, /* true if parens \escaped */
isrightdot = 0, /* true if right paren is \right. */
isleftdot = 0; /* true if left paren is \left. */
char left[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 = /*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*/
/* -------------------------------------------------------------------------
rasterize "interior" of expression, i.e., without enclosing parens
-------------------------------------------------------------------------- */
/* --- first see if enclosing parens are \escaped --- */
if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */
isescape = 1; /* so set flag accordingly */
/* --- get expression *without* enclosing parens --- */
strcpy(noparens,expression); /* get local copy of expression */
noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */
/* --- rasterize it --- */
if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
== NULL ) goto end_of_job; /* quit if failed */
/* --- no need to add parentheses for unescaped { --- */
if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
goto end_of_job; /* just return sp to caller */
/* -------------------------------------------------------------------------
obtain paren characters to enclose noparens[] raster with
-------------------------------------------------------------------------- */
/* --- first get left and right parens from expression --- */
memset(left,0,16); memset(right,0,16); /* init parens with nulls */
left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */
right[0] = *(expression+explen-1); /* right} is always last char */
isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */
isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
/* --- need height of noparens[] raster as minimum parens height --- */
height = (sp->image)->height; /* height of noparens[] raster */
baseline = sp->baseline; /* baseline of noparens[] raster */
if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */
/* --- get best-fit parentheses characters --- */
if ( !isleftdot ) /* if not \left. */
lp = get_delim(left,height+1,family); /* get left paren char */
if ( !isrightdot ) /* and if not \right. */
rp = get_delim(right,height+1,family); /* get right paren char */
if ( (lp==NULL && !isleftdot) /* check that we got left( */
|| (rp==NULL && !isrightdot) ) /* and right) if needed */
{ delete_subraster(sp); /* if failed, free subraster */
if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
sp = (subraster *)NULL; /* signal error to caller */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
set paren baselines to center on noparens[] raster, and concat components
-------------------------------------------------------------------------- */
/* --- set baselines to center paren chars on raster --- */
if ( lp != NULL ) /* ignore for \left. */
lp->baseline = baseline + ((lp->image)->height - height)/2;
if ( rp != NULL ) /* ignore for \right. */
rp->baseline = baseline + ((rp->image)->height - height)/2;
/* --- concat lp||sp||rp to obtain final result --- */
if ( lp != NULL ) /* ignore \left. */
sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
if ( sp != NULL ) /* succeeded or ignored \left. */
if ( rp != NULL ) /* ignore \right. */
sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
/* --- back to caller --- */
end_of_job:
return ( sp );
} /* --- end-of-function rastparen() --- */
/* ==========================================================================
* Function: rastlimits ( expression, size, basesp )
* Purpose: \limits, \nolimts, _ and ^ handler,
* dispatches call to rastscripts() or to rastdispmath()
* as necessary, to handle sub/superscripts following symbol
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to current character (or
* subexpression) immediately preceding script
* indicator
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster returned by rastscripts()
* or rastdispmath(), or NULL for any error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastlimits ( char **expression, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
*rastcat(), /* may need to concat scripts */
*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 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) */
mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */
int class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
/* -------------------------------------------------------------------------
determine whether or not to use displaymath
-------------------------------------------------------------------------- */
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 ( !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 */
if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */
isdisplay = 1; /* so explicitly set displaymath */
else /* wasn't \limits */
if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
isdisplay = 0; } /* so explicitly reset displaymath */
/* --- see if we found \[no]limits --- */
if ( isdisplay != (-1) ) /* explicit directive found */
*expression = exprptr; /* so bump expression past it */
else /* noexplicit directive */
{ isdisplay = 0; /* init displaymath flag off */
if ( 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 */
else
if ( isdisplaystyle >= 2 ) /*or mode forced conditionally true*/
{ if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
&& class!=OPENING && class!=CLOSING /*don't force ('s and )'s*/
&& class!=BINARYOP /* don't force binary operators */
&& class!=NOVALUE ) /* finally, don't force "images" */
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 */
/* -------------------------------------------------------------------------
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 ( 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 */
isscripted = 1; /*signal current term text-scripted*/
if ( basesp != NULL ) /* have base symbol */
{ /*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:
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":"..."));
if ( scriptsp != NULL ) /* have a constructed raster */
type_raster(scriptsp->image,msgfp); /*display constructed raster*/
fflush(msgfp); }
scriptlevel--; /*lastly, decrement subscript level*/
return ( scriptsp );
} /* --- end-of-function rastlimits() --- */
/* ==========================================================================
* Function: rastscripts ( expression, size, basesp )
* Purpose: super/subscript handler, returns subraster for the leading
* scripts in expression, whose base symbol is at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string beginning with a super/subscript,
* and returning ptr immediately following
* last script character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading script
* (scripts will be placed relative to base)
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to scripts,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o This "handler" isn't in the mathchardef symbol table,
* but is called directly from rasterize(), as necessary.
* ======================================================================= */
/* --- entry point --- */
subraster *rastscripts ( char **expression, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for scripts */
subscript[512], supscript[512]; /* scripts parsed from expression */
subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */
*rastack(); /*sets scripts in displaymath mode*/
raster *rp=NULL; /* image raster embedded in sp */
int height=0, width=0, baseline=0, /* height,width,baseline of sp */
subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */
supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */
baseht=0, baseln=0; /* height,baseline of base */
int bdescend=0, sdescend=0; /* descender of base, subscript */
int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */
isbase=0; /* true if we have base symbol */
int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
vbetween = 2, /* vertical space between scripts */
vabove = szval+1, /*sup's top/bot above base's top/bot*/
vbelow = szval+1, /*sub's top/bot below base's top/bot*/
vbottom = szval+1; /*sup's bot above (sub's below) bsln*/
/*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
-------------------------------------------------------------------------- */
/* --- parse for sub,superscript(s), and bump expression past it(them) --- */
if ( expression == NULL ) goto end_of_job; /* no *ptr given */
if ( *expression == NULL ) goto end_of_job; /* no expression given */
if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
*expression = texscripts(*expression,subscript,supscript,3);
/* --- rasterize scripts --- */
if ( *subscript != '\000' ) /* have a subscript */
subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
if ( *supscript != '\000' ) /* have a superscript */
supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
/* --- set flags for convenience --- */
issub = (subsp != (subraster *)NULL); /* true if we have subscript */
issup = (supsp != (subraster *)NULL); /* true if we have superscript */
isboth = (issub && issup); /* true if we have both */
if (!issub && !issup) goto end_of_job; /* quit if we have neither */
/* --- 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
-------------------------------------------------------------------------- */
/* --- get height and width of components --- */
if ( issub ) /* we have a subscript */
{ subht = (subsp->image)->height; /* so get its height */
subwidth = (subsp->image)->width; /* and width */
subln = subsp->baseline; } /* and baseline */
if ( issup ) /* we have a superscript */
{ supht = (supsp->image)->height; /* so get its height */
supwidth = (supsp->image)->width; /* and width */
supln = supsp->baseline; } /* and baseline */
/* --- get height and baseline of base, and descender of base and sub --- */
if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */
basesp = leftexpression; /* try using left side thus far */
if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */
{ baseht = (basesp->image)->height; /* height of base symbol */
baseln = basesp->baseline; /* and its baseline */
bdescend = baseht-(baseln+1); /* and base symbol descender */
sdescend = bdescend + vbelow; /*sub must descend by at least this*/
if ( baseht > 0 ) isbase = 1; } /* set flag */
/* -------------------------------------------------------------------------
determine width of constructed raster
-------------------------------------------------------------------------- */
width = max2(subwidth,supwidth); /*widest component is overall width*/
/* -------------------------------------------------------------------------
determine height and baseline of constructed raster
-------------------------------------------------------------------------- */
/* --- both super/subscript --- */
if ( isboth ) /*we have subscript and superscript*/
{ height = max2(subht+vbetween+supht, /* script heights + space bewteen */
vbelow+baseht+vabove); /*sub below base bot, sup above top*/
baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
/* --- superscript only --- */
if ( !issub ) /* we only have a superscript */
{ height = max3(baseln+1+vabove, /* sup's top above base symbol top */
supht+vbottom, /* sup's bot above baseln */
supht+vabove-bdescend); /* sup's bot above base symbol bot */
baseline = height-1; } /*sup's baseline at bottom of raster*/
/* --- subscript only --- */
if ( !issup ) { /* we only have a subscript */
if ( subht > sdescend ) /*sub can descend below base bot...*/
{ height = subht; /* ...without extra space on top */
baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
else /* sub's top will be below baseln */
{ height = sdescend+1; /* sub's bot below base symbol bot */
baseline = 0; } } /* sub's baseline at top of raster */
/* -------------------------------------------------------------------------
construct raster with superscript over subscript
-------------------------------------------------------------------------- */
/* --- allocate subraster containing constructed raster --- */
if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
== NULL ) /* and if we fail to allocate */
goto end_of_job; /* quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* set type as constructed image */
sp->size = size; /* set given size */
sp->baseline = baseline; /* composite scripts baseline */
rp = sp->image; /* raster embedded in subraster */
/* --- place super/subscripts in new raster --- */
if ( issup ) /* we have a superscript */
rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */
if ( issub ) /* we have a subscript */
rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
if ( issup ) delete_subraster(supsp); /* and superscript */
return ( sp );
} /* --- end-of-function rastscripts() --- */
/* ==========================================================================
* Function: rastdispmath ( expression, size, sp )
* Purpose: displaymath handler, returns sp along with
* its immediately following super/subscripts
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following sp to be
* rasterized along with its super/subscripts,
* and returning ptr immediately following last
* character processed.
* size (I) int containing 0-4 default font size
* sp (I) subraster * to display math operator
* to which super/subscripts will be added
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to sp
* plus its scripts, or NULL for any error
* --------------------------------------------------------------------------
* Notes: o sp returned unchanged if no super/subscript(s) follow it.
* ======================================================================= */
/* --- entry point --- */
subraster *rastdispmath ( char **expression, int size, subraster *sp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for scripts */
subscript[512], supscript[512]; /* scripts parsed from expression */
int issub=0, issup=0; /* true if we have sub,sup */
subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
*rastack(), /* stack operator with scripts */
*new_subraster(); /* for dummy base sp, if needed */
int vspace = 1; /* vertical space between scripts */
/* -------------------------------------------------------------------------
Obtain subscript and/or superscript expressions, and rasterize them/it
-------------------------------------------------------------------------- */
/* --- parse for sub,superscript(s), and bump expression past it(them) --- */
if ( expression == NULL ) goto end_of_job; /* no *ptr given */
if ( *expression == NULL ) goto end_of_job; /* no expression given */
if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
*expression = texscripts(*expression,subscript,supscript,3);
/* --- rasterize scripts --- */
if ( *subscript != '\000' ) /* have a subscript */
subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
if ( *supscript != '\000' ) /* have a superscript */
supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
/* --- set flags for convenience --- */
issub = (subsp != (subraster *)NULL); /* true if we have subscript */
issup = (supsp != (subraster *)NULL); /* true if we have superscript */
if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/
/* -------------------------------------------------------------------------
stack operator and its script(s)
-------------------------------------------------------------------------- */
/* --- stack superscript atop operator --- */
if ( issup ) { /* we have a superscript */
if ( sp == NULL ) /* but no base expression */
sp = supsp; /* so just use superscript */
else /* have base and superscript */
if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
== NULL ) goto end_of_job; } /* and quit if failed */
/* --- stack operator+superscript atop subscript --- */
if ( issub ) { /* we have a subscript */
if ( sp == NULL ) /* but no base expression */
sp = subsp; /* so just use subscript */
else /* have base and subscript */
if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
== NULL ) goto end_of_job; } /* and quit if failed */
sp->type = IMAGERASTER; /* flip type of composite object */
sp->size = size; /* and set font size */
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( sp );
} /* --- end-of-function rastdispmath() --- */
/* ==========================================================================
* Function: rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
* Purpose: \left...\right handler, returns a subraster corresponding to
* delimited subexpression at font size
* --------------------------------------------------------------------------
* 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
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
* ildelim (I) int containing ldelims[] index of
* left 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 *rastleft ( char **expression, int size, subraster *basesp,
int ildelim, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *rasterize(), *sp=NULL; /*rasterize between \left...\right*/
subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
subraster *rastlimits(); /*handle sub/super scripts on lp,rp*/
subraster *rastcat(); /* concat lp||sp||rp subrasters */
int family=CMSYEX, /* get_delim() family */
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 /* *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[] = {
"unused", ".", /* 1 for \left., \right. */
"(", ")", /* 2,3 for \left(, \right) */
"\\{","\\}", /* 4,5 for \left\{, \right\} */
"[", "]", /* 6,7 for \left[, \right] */
"<", ">", /* 8,9 for \left<, \right> */
"|", "\\|", /* 10,11 for \left,\right |,\|*/
NULL };
/* --- recognized operator delimiters --- */
static char *opdelims[] = { /* operator delims from cmex10 */
"int", "sum", "prod",
"cup", "cap", "dot",
"plus", "times", "wedge",
"vee",
NULL }; /* --- end-of-opdelims[] --- */
/* --- delimiter xlation --- */
static char *xfrom[] = /* xlate any delim suffix... */
{ "\\|", /* \| */
"\\{", /* \{ */
"\\}", /* \} */
"\\lbrace", /* \lbrace */
"\\rbrace", /* \rbrace */
"\\langle", /* \langle */
"\\rangle", /* \rangle */
NULL } ; /* --- end-of-xfrom[] --- */
static char *xto[] = /* ...to this instead */
{ "=", /* \| to = */
"{", /* \{ to { */
"}", /* \} to } */
"{", /* \lbrace to { */
"}", /* \rbrace to } */
"<", /* \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
-------------------------------------------------------------------------- */
/* --- check args --- */
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 */
/* gotldelim = 1; */ } /* and set flag that we got it */
else /* trapped \left without delim */
{ skipwhite(*expression); /* interpret \left ( as \left( */
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
-------------------------------------------------------------------------- */
/* --- 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 --- */
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) --- */
/* -------------------------------------------------------------------------
push past \left(_a^b sub/superscripts, if present
-------------------------------------------------------------------------- */
pleft = *expression; /*reset pleft after opening \left( */
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
-------------------------------------------------------------------------- */
/* --- get delimiter following \right --- */
if ( pright == (char *)NULL ) { /* assume \right. at end of exprssn*/
strcpy(rdelim,"."); /* set default \right. */
sublen = strlen(*expression); /* use entire remaining expression */
memcpy(subexpr,*expression,sublen); /* copy all remaining chars */
*expression += sublen; } /* and push expression to its null */
else { /* have explicit matching \right */
sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
memcpy(subexpr,*expression,sublen); /* copy chars preceding \right */
*expression = pright+strlen(right); /* push expression past \right */
skipwhite(*expression); /* interpret \right ) as \right) */
*expression = texchar(*expression,rdelim); /*pull delim from expression*/
if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
/* --- 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 */
break; } /* no need to check rest of table */
/* --- 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 */
for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
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 )
fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
ldelim,rdelim,subexpr);
/* -------------------------------------------------------------------------
rasterize subexpression
-------------------------------------------------------------------------- */
/* --- rasterize subexpression --- */
if ( (sp = rasterize(subexpr,size)) /* rasterize chars between delims */
== NULL ) goto end_of_job; /* quit if failed */
height = (sp->image)->height; /* height of subexpr raster */
rheight = height+margin; /*default rheight as subexpr height*/
/* -------------------------------------------------------------------------
rasterize delimiters, reset baselines, and add sub/superscripts if present
-------------------------------------------------------------------------- */
/* --- check for dot delimiter --- */
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 = (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 */
/* --- reset lp delim baseline to center delim on subexpr raster --- */
if ( lp != NULL ) /* if get_delim() succeeded */
{ int lheight = (lp->image)->height; /* actual height of left delim */
lp->baseline = sp->baseline + (lheight - height)/2;
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*/
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 */
/* --- reset rp delim baseline to center delim on subexpr raster --- */
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*/
isrightscript = isscripted; } /* check if right delim scripted */
isdisplaystyle = wasdisplaystyle; /* original \displystyle default */
/* --- check that we got delimiters --- */
if ( 0 )
if ( (lp==NULL && !isleftdot) /* check that we got left( */
|| (rp==NULL && !isrightdot) ) /* and right) if needed */
{ if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
if (0) { delete_subraster(sp); /* if failed, free subraster */
sp = (subraster *)NULL; } /* signal error to caller */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
concat lp || sp || rp components
-------------------------------------------------------------------------- */
/* --- concat lp||sp||rp to obtain final result --- */
if ( lp != NULL ) /* ignore \left. */
sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
if ( sp != NULL ) /* succeeded or ignored \left. */
if ( rp != NULL ) /* ignore \right. */
sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
/* --- back to caller --- */
end_of_job:
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-5 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-5 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
* so nothing is displayed
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding "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
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) NULL so nothing is displayed
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastflags ( char **expression, int size, subraster *basesp,
int flag, int value, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /* parse expression for... */
valuearg[1024]="NOVALUE"; /* value from expression, if needed */
int argvalue=NOVALUE, /* atoi(valuearg) */
isdelta=0, /* true if + or - precedes valuearg */
valuelen=0; /* strlen(valuearg) */
double dblvalue=(-99.), strtod(); /*convert ascii {valuearg} to double*/
static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */
/* -------------------------------------------------------------------------
set flag or value
-------------------------------------------------------------------------- */
switch ( flag )
{
default: break; /* unrecognized flag */
case ISFONTFAM:
if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
(*expression)++; /* skip leading ~ after \rm */
fontnum = value; /* set font family */
break;
case ISSTRING: isstring=value; break; /* set string/image mode */
case ISDISPLAYSTYLE: /* set \displaystyle mode */
displaystylelevel = recurlevel; /* \displaystyle set at recurlevel */
isdisplaystyle=value; break;
case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */
case ISREVERSE: /* reverse video */
if ( value==1 || value==NOVALUE )
{ fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
if ( value==2 || value==NOVALUE )
{ 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 */
value = 0; /* so force lowpass */
#endif
isss = issupersampling = value;
fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
break;
case ISFONTSIZE: /* set fontsize */
case ISDISPLAYSIZE: /* set displaysize */
case ISCONTENTTYPE: /*enable/disable content-type lines*/
case ISSHRINK: /* set shrinkfactor */
case ISAAALGORITHM: /* set anti-aliasing algorithm */
case ISWEIGHT: /* set font weight */
case ISCENTERWT: /* set lowpass center pixel weight */
case ISADJACENTWT: /* set lowpass adjacent weight */
case ISCORNERWT: /* set lowpass corner weight */
case ISCOLOR: /* set red(1),green(2),blue(3) */
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 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 */
if ( !isalpha(*valuearg) ) /* and against alpha string args */
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 */
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;
case ISCOLOR: /* set color */
slower(valuearg); /* convert arg to lower case */
if ( argvalue==1 || strstr(valuearg,"red") )
{ fggreen = fgblue = (isblackonwhite?0:255);
fgred = (isblackonwhite?255:0); }
if ( argvalue==2 || strstr(valuearg,"green") )
{ fgred = fgblue = (isblackonwhite?0:255);
fggreen = (isblackonwhite?255:0); }
if ( argvalue==3 || strstr(valuearg,"blue") )
{ fgred = fggreen = (isblackonwhite?0:255);
fgblue = (isblackonwhite?255:0); }
if ( argvalue==0 || strstr(valuearg,"black") )
fgred = fggreen = fgblue = (isblackonwhite?0:255);
if ( argvalue==7 || strstr(valuearg,"white") )
fgred = fggreen = fgblue = (isblackonwhite?255:0);
break;
case ISFONTSIZE: /* set fontsize */
if ( argvalue != NOVALUE ) /* got a value */
{ int largestsize = (issupersampling?16:LARGESTSIZE);
fontsize = (isdelta? fontsize+argvalue : argvalue);
fontsize = max2(0,min2(fontsize,largestsize));
shrinkfactor = shrinkfactors[fontsize];
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 */
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 */
valuelen = strlen(valuearg); /* ought to be 1 */
if ( *expression != '\000' ) /* ill-formed expression */
{ *expression = (char *)(*expression-valuelen); /*back up buff*/
memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
break;
case ISDISPLAYSIZE: /* set displaysize */
if ( argvalue != NOVALUE ) /* got a value */
displaysize = (isdelta? displaysize+argvalue : argvalue);
break;
case ISCONTENTTYPE: /*enable/disable content-type lines*/
if ( argvalue != NOVALUE ) /* got a value */
isemitcontenttype = (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 */
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 */
shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
break;
case ISAAALGORITHM: /* set anti-aliasing algorithm */
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 */
(isdelta? weightnum+argvalue : argvalue));
if ( value>=0 && value= 0.0 ) /* got a value */
gammacorrection = dblvalue; /* set gamma correction */
break;
} /* --- end-of-switch() --- */
break;
case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/
*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
valuelen = strlen(valuearg); /* ought to be 1-4 */
if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
break;
case UNITLENGTH:
if ( value != NOVALUE ) /* passed a fixed value to be set */
unitlength = (double)(value); /* set given fixed value */
else /* get value from expression */
{ *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
if ( *valuearg != '\000' ) /* guard against empty string */
unitlength = strtod(valuearg,NULL); } /* convert to double */
break;
} /* --- end-of-switch(flag) --- */
return ( NULL ); /*just set value, nothing to display*/
} /* --- end-of-function rastflags() --- */
/* ==========================================================================
* Function: rastspace(expression, size, basesp, width, isfill, isheight)
* Purpose: returns a blank/space subraster width wide,
* with baseline and height corresponding to basep
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* 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
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to empty/blank subraster
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastspace ( char **expression, int size, subraster *basesp,
int width, int isfill, int isheight )
{
/* -------------------------------------------------------------------------
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} */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
subraster *rastcat(); /* cat rightsp after \hfill */
/* -------------------------------------------------------------------------
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 */
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, interpret as double--- */
*expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
if ( *widtharg != '\000' ) /* got [minspace] */
minspace = iround(unitlength*strtod(widtharg,NULL)); /* in pixels */
} /* --- end-of-if(*(*expression)=='[') --- */
width = 1; /* set default width */
*expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
dwidth = unitlength*strtod(widtharg,NULL); /* scaled width value */
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
-------------------------------------------------------------------------- */
if ( isfill /* called as \hfill{} */
&& !isheight ) /* parameter conflict */
{ if ( leftexpression != NULL ) /* if we have left half */
width -= (leftexpression->image)->width; /*reduce left width from total*/
if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
!= NULL ) /* succeeded */
width -= (rightsp->image)->width; } /* reduce right width from total */
/* -------------------------------------------------------------------------
construct blank subraster, and return it to caller
-------------------------------------------------------------------------- */
/* --- get parameters from base symbol --- */
if ( basesp != (subraster *)NULL ) /* we have base symbol for space */
{ baseht = (basesp->image)->height; /* height of base symbol */
baseln = basesp->baseline; } /* and its baseline */
/* --- flip params for height --- */
if ( isheight ) /* width is actually height */
{ baseht = width; /* use given width as height */
width = 1; } /* and set default width */
/* --- generate and init space subraster --- */
if ( width > 0 ) /*make sure we have positive width*/
if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
!= NULL ) /* and if we succeed... */
{ /* --- ...re-init subraster parameters --- */
spacesp->size = size; /*propagate base font size forward*/
if(1)spacesp->type = blanksignal; /* need to propagate blanks (???) */
spacesp->baseline = baseln; } /* ditto baseline */
/* -------------------------------------------------------------------------
concat right half if \hfill-ing
-------------------------------------------------------------------------- */
if ( rightsp != NULL ) /* we have a right half after fill */
{ spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/
rastcat(spacesp,rightsp,3)); /* or cat right half after space */
spacesp->type = blanksignal; /* need to propagate blanks */
*expression += strlen((*expression)); } /* push expression to its null */
end_of_job:
return ( spacesp );
} /* --- end-of-function rastspace() --- */
/* ==========================================================================
* Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \\ handler, returns subraster corresponding to
* left-hand expression preceding \\ above right-hand expression
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \\ to be
* rasterized, and returning ptr immediately
* to terminating null.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \\
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastnewline ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
double strtod(); /* convert ascii param to double */
int vspace = size+2; /* #pixels between lines */
/* -------------------------------------------------------------------------
obtain optional [vspace] argument immediately following \\ command
-------------------------------------------------------------------------- */
/* --- check if [vspace] given --- */
if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/
{
/* ---parse [vspace] and bump expression past it, interpret as double--- */
*expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
} /* --- end-of-if(*(*expression)=='[') --- */
if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
/* -------------------------------------------------------------------------
rasterize right half of expression and stack left half above it
-------------------------------------------------------------------------- */
/* --- rasterize right half --- */
if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
== NULL ) goto end_of_job; /* quit if failed */
/* --- stack left half above it --- */
/*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
/* --- back to caller --- */
end_of_job:
if ( newlsp != NULL ) /* returning entire expression */
{ int newht = (newlsp->image)->height; /* height of returned subraster */
newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
isreplaceleft = 1; /* so set flag to replace left half*/
*expression += strlen(*expression); } /* and push to terminating null*/
return ( newlsp ); /* 1st line over 2nd, or null=error*/
} /* --- end-of-function rastnewline() --- */
/* ==========================================================================
* Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 )
* Purpose: returns left/right arrow subraster (e.g., for \longrightarrow)
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* drctn (I) int containing +1 for right, -1 for left,
* or 0 for leftright
* isBig (I) int containing 0 for ---> or 1 for ===>
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to left/right arrow subraster
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o An optional argument [width] may *immediately* follow
* the \longxxx to explicitly set the arrow's width in pixels.
* For example, \longrightarrow calculates a default width
* (as usual in LaTeX), whereas \longrightarrow[50] explicitly
* draws a 50-pixel long arrow. This can be used, e.g.,
* to draw commutative diagrams in conjunction with
* \array (and maybe with \stackrel and/or \relstack, too).
* o In case you really want to render, say, [f]---->[g], just
* use an intervening space, i.e., [f]\longrightarrow~[g].
* In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
* ======================================================================= */
/* --- entry point --- */
subraster *rastarrow ( char **expression, int size, subraster *basesp,
int drctn, int isBig, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
char *texsubexpr(), widtharg[256]; /* parse for optional [width] */
char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
int delete_subraster(); /*free work areas in case of error*/
double strtod(); /* convert ascii [width] to value */
int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
int vspace = 1; /* #empty rows below arrow */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
construct longleft/rightarrow subraster, with limits, and return it to caller
-------------------------------------------------------------------------- */
/* --- check for optional width arg and replace default width --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int widthval; /* test [width] before using it */
*expression = texsubexpr(*expression,widtharg,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 */
/* --- now parse for limits, and bump expression past it(them) --- */
if ( islimits ) /* handling limits internally */
{ *expression = texscripts(*expression,sub,super,3); /* parse for limits */
if ( *sub != '\000' ) /*have a subscript following arrow*/
subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
if ( *super != '\000' ) /*have superscript following arrow*/
supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
/* --- set height based on width --- */
height = min2(17,max2(9,(width+2)/6)); /* height based on width */
height = 1 + (height/2)*2; /* always force odd height */
/* --- generate arrow subraster --- */
if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- add space below arrow --- */
if ( vspace > 0 ) /* if we have space below arrow */
if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
!= NULL ) /* and if we succeeded */
if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- init arrow subraster parameters --- */
arrowsp->size = size; /*propagate base font size forward*/
arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */
/* --- add limits above/below arrow, as necessary --- */
if ( subsp != NULL ) /* stack subscript below arrow */
if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
== NULL ) goto end_of_job; /* quit if failed */
if ( supsp != NULL ) /* stack superscript above arrow */
if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
== NULL ) goto end_of_job; /* quit if failed */
/* --- return arrow (or NULL) to caller --- */
end_of_job:
return ( arrowsp );
} /* --- end-of-function rastarrow() --- */
/* ==========================================================================
* Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 )
* Purpose: returns an up/down arrow subraster (e.g., for \longuparrow)
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* drctn (I) int containing +1 for up, -1 for down,
* or 0 for updown
* isBig (I) int containing 0 for ---> or 1 for ===>
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to up/down arrow subraster
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o An optional argument [height] may *immediately* follow
* the \longxxx to explicitly set the arrow's height in pixels.
* For example, \longuparrow calculates a default height
* (as usual in LaTeX), whereas \longuparrow[25] explicitly
* draws a 25-pixel high arrow. This can be used, e.g.,
* to draw commutative diagrams in conjunction with
* \array (and maybe with \stackrel and/or \relstack, too).
* o In case you really want to render, say, [f]---->[g], just
* use an intervening space, i.e., [f]\longuparrow~[g].
* In text use two spaces {\rm~[f]\longuparrow~~[g]}.
* ======================================================================= */
/* --- entry point --- */
subraster *rastuparrow ( char **expression, int size, subraster *basesp,
int drctn, int isBig, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
char *texsubexpr(), heightarg[256]; /* parse for optional [height] */
char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *rastcat(); /* cat superscript left, sub right */
double strtod(); /* convert ascii [height] to value */
int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
construct blank subraster, and return it to caller
-------------------------------------------------------------------------- */
/* --- check for optional height arg and replace default height --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int heightval; /* test height before using it */
*expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
heightval = /* convert [height] to integer */
(int)((unitlength*strtod(heightarg,NULL))+0.5);
if ( heightval>=2 && heightval<=600 ) /* sanity check */
height = heightval; } /* replace deafault height */
/* --- now parse for limits, and bump expression past it(them) --- */
if ( islimits ) /* handling limits internally */
{ *expression = texscripts(*expression,sub,super,3); /* parse for limits */
if ( *sub != '\000' ) /*have a subscript following arrow*/
subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
if ( *super != '\000' ) /*have superscript following arrow*/
supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
/* --- set width based on height --- */
width = min2(17,max2(9,(height+2)/4)); /* width based on height */
width = 1 + (width/2)*2; /* always force odd width */
/* --- generate arrow subraster --- */
if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- init arrow subraster parameters --- */
arrowsp->size = size; /*propagate base font size forward*/
arrowsp->baseline = height-1; /* set baseline at bottom of arrow */
/* --- add limits above/below arrow, as necessary --- */
if ( supsp != NULL ) /* cat superscript to left of arrow*/
{ int supht = (supsp->image)->height, /* superscript height */
deltab = (1+abs(height-supht))/2; /* baseline difference to center */
supsp->baseline = supht-1; /* force script baseline to bottom */
if ( supht <= height ) /* arrow usually taller than script*/
arrowsp->baseline -= deltab; /* so bottom of script goes here */
else supsp->baseline -= deltab; /* else bottom of arrow goes here */
if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
== NULL ) goto end_of_job; } /* quit if failed */
if ( subsp != NULL ) /* cat subscript to right of arrow */
{ int subht = (subsp->image)->height, /* subscript height */
deltab = (1+abs(height-subht))/2; /* baseline difference to center */
arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
subsp->baseline = subht-1; /* force script baseline to bottom */
if ( subht <= height ) /* arrow usually taller than script*/
arrowsp->baseline -= deltab; /* so bottom of script goes here */
else subsp->baseline -= deltab; /* else bottom of arrow goes here */
if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
== NULL ) goto end_of_job; } /* quit if failed */
/* --- return arrow (or NULL) to caller --- */
end_of_job:
arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
return ( arrowsp );
} /* --- end-of-function rastuparrow() --- */
/* ==========================================================================
* Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3)
* Purpose: overlays one raster on another
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following overlay \cmd to
* be rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding overlay \cmd
* (unused, but passed for consistency)
* overlay (I) int containing 1 to overlay / (e.g., \not)
* or NOVALUE to pick up 2nd arg from expression
* offset2 (I) int containing #pixels to horizontally offset
* overlay relative to underlying symbol,
* positive(right) or negative or 0,
* or NOVALUE to pick up optional [offset] arg
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to composite,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastoverlay ( char **expression, int size, subraster *basesp,
int overlay, int offset2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for base,overlay*/
expr1[512], expr2[512]; /* base, overlay */
subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
*new_subraster(); /*explicitly alloc sp2 if necessary*/
subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
int line_raster(); /* draw diagonal for \Not */
/* -------------------------------------------------------------------------
Obtain base, and maybe overlay, and rasterize them
-------------------------------------------------------------------------- */
/* --- check for optional offset2 arg --- */
if ( offset2 == NOVALUE ) /* only if not explicitly specified*/
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int offsetval; /* test before using it */
*expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
if ( abs(offsetval) <= 25 ) /* sanity check */
offset2 = offsetval; } /* replace deafault */
if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
/* --- parse for base, bump expression past it, and rasterize it --- */
*expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
== NULL ) goto end_of_job; /* quit if failed to rasterize */
overlaysp = sp1; /*in case we return with no overlay*/
/* --- get overlay expression, and rasterize it --- */
if ( overlay == NOVALUE ) /* get overlay from input stream */
{ *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
if ( *expr2 != '\000' ) /* have an overlay */
sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */
else /* specific overlay */
switch ( overlay )
{
default: break;
case 1: /* e.g., \not overlays slash */
sp2 = rasterize("/",size+1); /* rasterize overlay expression */
offset2 = max2(1,size-3); /* push / right a bit */
offset2 = 0;
break;
case 2: /* e.g., \Not draws diagonal */
sp2 = NULL; /* no overlay required */
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 */
if ( 0 ) /* diagonal within bounding box */
line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
else /* construct "wide" diagonal */
{ int margin=3; /* desired extra margin width */
sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
if ( sp2 != NULL ) /* allocated successfully */
line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
break;
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 */
int width=rp->width, height=rp->height; /* raster dimensions */
int baseline = overlaysp->baseline; /* we'll ignore 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*/
break;
} /* --- end-of-switch(overlay) --- */
if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/
/* -------------------------------------------------------------------------
construct composite overlay
-------------------------------------------------------------------------- */
overlaysp = rastcompose(sp1,sp2,offset2,0,3);
end_of_job:
return ( overlaysp );
} /* --- end-of-function rastoverlay() --- */
/* ==========================================================================
* Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 )
* Purpose: \frac,\atop handler, returns a subraster corresponding to
* expression (immediately following \frac,\atop) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \frac to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \frac
* (unused, but passed for consistency)
* isfrac (I) int containing true to draw horizontal line
* between numerator and denominator,
* or false not to draw it (for \atop).
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to fraction,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastfrac ( char **expression, int size, subraster *basesp,
int isfrac, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for numer,denom*/
numer[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 */
int width=0, /* width of constructed raster */
numheight=0; /* height of numerator */
int baseht=0, baseln=0; /* height,baseline of base symbol */
/*int istweak = 1;*/ /*true to tweak baseline alignment*/
int rule_raster(), /* draw horizontal line for frac */
lineheight = 1; /* thickness of fraction line */
int vspace = (size>2?2:1); /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
int type_raster(); /* display debugging output */
/* -------------------------------------------------------------------------
Obtain numerator and denominator, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for numerator,denominator and bump expression past them --- */
*expression = texsubexpr(*expression,numer,0,"{","}",0,0);
*expression = texsubexpr(*expression,denom,0,"{","}",0,0);
if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize numerator, denominator --- */
if ( *numer != '\000' ) /* have a numerator */
if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
== NULL ) goto end_of_job; /* and quit if failed */
if ( *denom != '\000' ) /* have a denominator */
if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
== NULL ) /* failed */
{ if ( numsp != NULL ) /* already rasterized numerator */
delete_subraster(numsp); /* so free now-unneeded numerator */
goto end_of_job; } /* and quit */
/* --- if one componenet missing, use a blank space for it --- */
if ( numsp == NULL ) /* no numerator given */
numsp = rasterize("[?]",size-1); /* missing numerator */
if ( densp == NULL ) /* no denominator given */
densp = rasterize("[?]",size-1); /* missing denominator */
/* --- check that we got both components --- */
if ( numsp==NULL || densp==NULL ) /* some problem */
{ delete_subraster(numsp); /*delete numerator (if it existed)*/
delete_subraster(densp); /*delete denominator (if it existed)*/
goto end_of_job; } /* and quit */
/* --- get height of numerator (to determine where line belongs) --- */
numheight = (numsp->image)->height; /* get numerator's height */
/* -------------------------------------------------------------------------
construct raster with numerator stacked over denominator
-------------------------------------------------------------------------- */
/* --- construct raster with numer/denom --- */
if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
== NULL ) /* failed to construct numer/denom */
{ delete_subraster(numsp); /* so free now-unneeded numerator */
delete_subraster(densp); /* and now-unneeded denominator */
goto end_of_job; } /* and then quit */
/* --- determine width of constructed raster --- */
width = (fracsp->image)->width; /*just get width of embedded image*/
/* --- initialize subraster parameters --- */
fracsp->size = size; /* propagate font size forward */
fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
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 */
} /* --- end-of-if(basesp!=NULL) --- */
/* -------------------------------------------------------------------------
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,fraccenterline,0,width,lineheight,0);
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( msgfp!=NULL && msglevel>=99 )
{ fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
if ( fracsp != NULL ) /* have a constructed raster */
type_raster(fracsp->image,msgfp); } /* display constructed raster */
return ( fracsp );
} /* --- end-of-function rastfrac() --- */
/* ==========================================================================
* Function: rastackrel ( expression, size, basesp, base, arg2, arg3 )
* Purpose: \stackrel handler, returns a subraster corresponding to
* stacked relation
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \stackrel to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \stackrel
* (unused, but passed for consistency)
* base (I) int containing 1 if upper/first subexpression
* is base relation, or 2 if lower/second is
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to stacked
* relation, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastackrel ( char **expression, int size, subraster *basesp,
int base, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for 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 */
lowsize = (base==2? size:size-1); /* font size for lower component */
int vspace = 1; /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
/* -------------------------------------------------------------------------
Obtain numerator and denominator, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for numerator,denominator and bump expression past them --- */
*expression = texsubexpr(*expression,upper,0,"{","}",0,0);
*expression = texsubexpr(*expression,lower,0,"{","}",0,0);
if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize upper, lower --- */
if ( *upper != '\000' ) /* have upper component */
if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */
== NULL ) goto end_of_job; /* and quit if failed */
if ( *lower != '\000' ) /* have lower component */
if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
== NULL ) /* failed */
{ if ( upsp != NULL ) /* already rasterized upper */
delete_subraster(upsp); /* so free now-unneeded upper */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
construct stacked relation raster
-------------------------------------------------------------------------- */
/* --- construct stacked relation --- */
if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
relsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( relsp );
} /* --- end-of-function rastackrel() --- */
/* ==========================================================================
* Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 )
* Purpose: \log, \lim, etc handler, returns a subraster corresponding
* to math functions
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \mathfunc to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \mathfunc
* (unused, but passed for consistency)
* mathfunc (I) int containing 1=arccos, 2=arcsin, etc.
* islimits (I) int containing 1 if function may have
* limits underneath, e.g., \lim_{n\to\infty}
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to mathfunc,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
int mathfunc, int islimits, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for _limits */
func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
char *texsubexpr(), /* parse expression for 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 */
int vspace = 1; /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
/* --- table of function names by mathfunc number --- */
static int numnames = 34; /* number of names in table */
static char *funcnames[] = {
"error", /* 0 index is illegal/error bucket*/
"arccos", "arcsin", "arctan", /* 1 - 3 */
"arg", "cos", "cosh", /* 4 - 6 */
"cot", "coth", "csc", /* 7 - 9 */
"deg", "det", "dim", /* 10 - 12 */
"exp", "gcd", "hom", /* 13 - 15 */
"inf", "ker", "lg", /* 16 - 18 */
"lim", "liminf", "limsup", /* 19 - 21 */
"ln", "log", "max", /* 22 - 24 */
"min", "Pr", "sec", /* 25 - 27 */
"sin", "sinh", "sup", /* 28 - 30 */
"tan", "tanh", /* 31 - 32 */
/* --- extra mimetex funcnames --- */
"tr", /* 33 */
"pmod" /* 34 */
} ;
/* -------------------------------------------------------------------------
set up and rasterize function name in \rm
-------------------------------------------------------------------------- */
if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
switch ( mathfunc ) /* check for special processing */
{
default: /* no special processing */
strcpy(func,"{\\rm~"); /* init string with {\rm~ */
strcat(func,funcnames[mathfunc]); /* concat function name */
strcat(func,"}"); /* and add terminating } */
break;
case 34: /* \pmod{x} --> (mod x) */
/* --- parse for \pmod{arg} argument --- */
*expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
strcpy(func,"{\\({\\rm~mod}"); /* init with {\left({\rm~mod} */
strcat(func,"\\hspace2"); /* concat space */
strcat(func,funcarg); /* and \pmodargument */
strcat(func,"\\)}"); /* and add terminating \right)} */
break;
} /* --- end-of-switch(mathfunc) --- */
if ( (funcsp = rasterize(func,size)) /* rasterize function name */
== NULL ) goto end_of_job; /* and quit if failed */
mathfuncsp = funcsp; /* just return funcsp if no limits */
if ( !islimits ) goto end_of_job; /* treat any subscript normally */
/* -------------------------------------------------------------------------
Obtain limits, if permitted and if provided, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for subscript limits, and bump expression past it(them) --- */
*expression = texscripts(*expression,limits,limits,1);
if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */
/* --- rasterize limits --- */
if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
== NULL ) goto end_of_job; /* and quit if failed */
/* -------------------------------------------------------------------------
construct func atop limits
-------------------------------------------------------------------------- */
/* --- construct func atop limits --- */
if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
mathfuncsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( mathfuncsp );
} /* --- end-of-function rastmathfunc() --- */
/* ==========================================================================
* Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \sqrt handler, returns a subraster corresponding to
* expression (immediately following \sqrt) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \sqrt to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastsqrt ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[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]*/
int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
rootheight=0, rootwidth=0, /* height,width of rootarg raster */
subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
int rastput(); /* put subexpr in constructed sqrt */
int overspace = 2; /*space between subexpr and overbar*/
int delete_subraster(); /* free work areas */
/* -------------------------------------------------------------------------
Obtain subexpression to be sqrt-ed, and rasterize it
-------------------------------------------------------------------------- */
/* --- first check for optional \sqrt[rootarg]{...} --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
if ( *rootarg != '\000' ) /* got rootarg */
if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
!= NULL ) /* rasterized successfully */
{ rootheight = (rootsp->image)->height; /* get height of rootarg */
rootwidth = (rootsp->image)->width; } /* and its width */
} /* --- end-of-if(**expression=='[') --- */
/* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
if ( *subexpr == '\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize subexpression to be accented --- */
if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
== NULL ) goto end_of_job; /* quit if failed */
/* -------------------------------------------------------------------------
determine height and width of sqrt raster to be constructed
-------------------------------------------------------------------------- */
/* --- first get height and width of subexpr --- */
subheight = (subsp->image)->height; /* height of subexpr */
subwidth = (subsp->image)->width; /* and its width */
pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
/* --- determine height and width of sqrt to contain subexpr --- */
sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
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,
(rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,pixsz))
== NULL ) goto end_of_job; /* quit if failed to build sqrt */
/* --- embed subexpr in sqrt at lower-right corner--- */
rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
/* --- "embed" rootarg at upper-left --- */
if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/
{
/* --- allocate full raster to contain sqrtsp and rootsp --- */
int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
if ( fullsp != NULL ) /* allocated successfully */
{ /* --- embed sqrtsp exactly at lower-right corner --- */
rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
fullheight-sqrtheight,fullwidth-sqrtwidth,1);
/* --- embed rootsp near upper-left, nestled above leading surd --- */
rastput(fullsp->image,rootsp->image,
0,max2(0,surdwidth-rootwidth-2-size),0);
/* --- replace sqrtsp with fullsp --- */
delete_subraster(sqrtsp); /* free original sqrtsp */
sqrtsp = fullsp; /* and repoint it to fullsp instead*/
sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
} /* --- end-of-if(rootsp!=NULL) --- */
/* --- initialize subraster parameters --- */
sqrtsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
return ( sqrtsp );
} /* --- end-of-function rastsqrt() --- */
/* ==========================================================================
* Function: rastaccent (expression,size,basesp,accent,isabove,isscript)
* Purpose: \hat, \vec, \etc handler, returns a subraster corresponding
* to expression (immediately following \accent) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \accent to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* accent (I) int containing HATACCENT or VECACCENT, etc,
* between numerator and denominator,
* or false not to draw it (for \over).
* isabove (I) int containing true if accent is above
* expression to be accented, or false
* if accent is below (e.g., underbrace)
* isscript (I) int containing true if sub/superscripts
* allowed (for under/overbrace), or 0 if not.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way
* of isabove and isscript args.
* ======================================================================= */
/* --- entry point --- */
subraster *rastaccent ( char **expression, int size, subraster *basesp,
int accent, int isabove, int isscript )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
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 */
subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
int delete_subraster(); /*free work areas in case of error*/
int vspace = 0; /*vertical space between accent,sub*/
/* -------------------------------------------------------------------------
Obtain subexpression to be accented, and rasterize it
-------------------------------------------------------------------------- */
/* --- parse for subexpr to be accented, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
if ( *subexpr=='\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize subexpression to be accented --- */
if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
== NULL ) goto end_of_job; /* quit if failed */
/* -------------------------------------------------------------------------
determine desired accent width and height
-------------------------------------------------------------------------- */
/* --- first get height and width of subexpr --- */
subheight = (subsp->image)->height; /* height of subexpr */
subwidth = (subsp->image)->width; /* and its width is overall width */
pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
/* --- determine desired width, height of accent --- */
accwidth = subwidth; /* same width as subexpr */
accheight = 4; /* default for bars */
switch ( accent )
{ default: break; /* default okay */
case DOTACCENT: case DDOTACCENT:
accheight = (size<4? 3:4); /* default for dots */
break;
case VECACCENT:
vspace = 1; /* set 1-pixel vertical space */
case HATACCENT:
accheight = 7; /* default */
if ( subwidth < 10 ) accheight = 5; /* unless small width */
else if ( subwidth > 25 ) accheight = 9; /* or large */
break;
} /* --- end-of-switch(accent) --- */
accheight = min2(accheight,subheight); /*never higher than accented subexpr*/
/* -------------------------------------------------------------------------
construct accent, and construct subraster with accent over (or under) subexpr
-------------------------------------------------------------------------- */
/* --- first construct accent --- */
if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
== NULL ) goto end_of_job; /* quit if failed to build accent */
/* --- now stack accent above (or below) subexpr, and free both args --- */
accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
: rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/
if ( accsubsp == NULL ) /* failed to stack accent */
{ delete_subraster(subsp); /* free unneeded subsp */
delete_subraster(accsp); /* and unneeded accsp */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
look for super/subscript (annotation for over/underbrace)
-------------------------------------------------------------------------- */
/* --- first check whether accent permits accompanying annotations --- */
if ( !isscript ) goto end_of_job; /* no annotations for this accent */
/* --- now get scripts if there actually are any --- */
*expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
script = (isabove? supscript : subscript); /*select above^ or below_ script*/
if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
/* --- rasterize script annotation at size-2 --- */
if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
== NULL ) goto end_of_job; /* quit if failed */
/* --- stack annotation above (or below) accent, and free both args --- */
accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
: rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( accsubsp != NULL ) /* initialize subraster parameters */
accsubsp->size = size; /* propagate font size forward */
return ( accsubsp );
} /* --- end-of-function rastaccent() --- */
/* ==========================================================================
* Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
* Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
* to char(s) within {}'s rendered at size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \font to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to chars
* between {}'s, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastfont ( char **expression, int size, subraster *basesp,
int ifontnum, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
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; /* 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 oldsmashmargin = smashmargin; /* turn off smash in text mode */
#if 0
/* --- fonts recognized by rastfont --- */
static int nfonts = 6; /* 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 --- */
{ "\\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 */
{ NULL, 0 }
} ; /* --- end-of-fonts[] --- */
#endif
/* -------------------------------------------------------------------------
first get font name and class to determine type of conversion desired
-------------------------------------------------------------------------- */
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 ( 1 || class<0 ) /* not character-by-character */
{
/* ---
if \font not immediately followed by { then it has no arg, so just set flag
------------------------------------------------------------------------ */
if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
{
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
goto end_of_job;
} /* --- end-of-if(*(*expression)!='{') --- */
/* ---
convert \font{abc} --> {\font~abc}
---------------------------------- */
/* --- parse for {fontchars} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
/* --- convert all fontchars at the same time --- */
strcpy(subexpr,"{"); /* start off with opening { */
strcat(subexpr,name); /* followed by font name */
strcat(subexpr,"~"); /* followed by whitespace */
strcat(subexpr,fontchars); /* followed by all the chars */
strcat(subexpr,"}"); /* terminate with closing } */
} /* --- end-of-if(class<0) --- */
else /* character-by-character */
{
/* ---
convert ABC to \calA\calB\calC
------------------------------ */
int isprevchar=0; /* true if prev char converted */
/* --- parse for {fontchars} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
/* --- convert fontchars one at a time --- */
strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */
strcpy(subexpr,"{"); /* nope, just start off with { */
for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
{
if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
{ 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;
case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
case 4: if ( islower((int)fchar) ) isinclass=1; break;
case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
case 9: isinclass=1; break; }
if ( isinclass ) /* convert current char to \font */
{ strcat(subexpr,name); /* by prefixing it with font name */
isprevchar = 1; } /* and set flag to signal separator*/
else /* current char not in \font */
{ if ( isprevchar ) /* extra separator only after \font*/
if ( isalpha(fchar) ) /* separator only before alpha */
strcat(subexpr,"~"); /* need separator after \font */
isprevchar = 0; } /* reset flag for next char */
exprlen = strlen(subexpr); /* #chars so far */
subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/
subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */
} /* --- end-of-for(pfchars) --- */
strcat(subexpr,"}"); /* add closing } */
} /* --- end-of-if/else(class<0) --- */
/* -------------------------------------------------------------------------
rasterize subexpression containing chars to be rendered at font
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
== NULL ) goto end_of_job; /* and quit if failed */
/* -------------------------------------------------------------------------
back to caller with chars rendered in font
-------------------------------------------------------------------------- */
end_of_job:
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() --- */
/* ==========================================================================
* Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \begin{}...\end{} handler, returns a subraster corresponding
* to array expression within environment, i.e., rewrites
* \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \begin to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \begin
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to array
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastbegin ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[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 */
char *strchange(); /*\begin...\end --> {\begin...\end}*/
char *delims = (char *)NULL; /* mdelims[ienviron] */
subraster *rasterize(), *sp=NULL; /* rasterize environment */
int ienviron = 0; /* environs[] index */
int nbegins = 0; /* #\begins nested beneath this one*/
int envlen=0, sublen=0; /* #chars in environ, subexpr */
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 };
static char *environs[] = { /* types of environments we process*/
"eqnarray", /* 0 eqnarray environment */
"array", /* 1 array environment */
"matrix", /* 2 array environment */
"tabular", /* 3 array environment */
"pmatrix", /* 4 ( ) */
"bmatrix", /* 5 [ ] */
"Bmatrix", /* 6 { } */
"vmatrix", /* 7 | | */
"Vmatrix", /* 8 || || */
"gather", /* 9 gather environment */
"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
-------------------------------------------------------------------------- */
/* --- first bump nesting level --- */
blevel++; /* count \begin...\begin...'s */
/* --- \begin must be followed by {type_of_environment} --- */
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 */
/* --- look up environment in our table --- */
for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
if ( environs[ienviron] == NULL ) /* found NULL before match */
goto end_of_job; /* so quit */
else /* see if we have an exact match */
if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
break; /* leave loop with ienviron index */
/* --- accumulate any additional params for this environment --- */
*subexpr = '\000'; /* reset subexpr to empty string */
delims = mdelims[ienviron]; /* mdelims[] string for ienviron */
if ( delims != NULL ) /* add appropriate opening delim */
{ strcpy(subexpr,"\\"); /* start with \ for (,[,{,|,= */
strcat(subexpr,delims); /* then add opening delim */
subexpr[2] = '\000'; } /* remove extraneous closing delim */
switch ( ienviron )
{
default: goto end_of_job; /* environ not implemented yet */
case 0: /* \begin{eqnarray} */
strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */
break;
case 1: case 2: case 3: /* \begin{array} followed by {lcr} */
strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
skipwhite(exprptr); /* bump to next non-white char */
if ( *exprptr == '{' ) /* assume we have {lcr} argument */
{ exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
strcat(subexpr,"$"); } /* add terminating $ to lcr */
break;
case 4: case 5: case 6: /* \begin{pmatrix} or b,B,v,Vmatrix */
case 7: case 8:
strcat(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
break;
case 9: /* gather */
strcat(subexpr,"\\array{c$"); /* center equations */
break;
case 10: /* align */
strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
break;
case 11: /* verbatim */
strcat(subexpr,"{\\rm "); /* {\rm ...} */
/*strcat(subexpr,"\\\\{\\rm ");*/ /* \\{\rm } doesn't work in context */
break;
case 12: /* picture */
strcat(subexpr,"\\picture"); /* picture environment */
skipwhite(exprptr); /* bump to next non-white char */
if ( *exprptr == '(' ) /*assume we have (width,height) arg*/
{ exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
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{...}
-------------------------------------------------------------------------- */
/* --- first \end following \begin --- */
if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */
== NULL ) goto end_of_job; /* and quit if no \end found */
/* --- find matching endptr by pushing past any nested \begin's --- */
begptr = exprptr; /* start after first \begin{...} */
while ( 1 ) /*break when we find matching \end*/
{
/* --- first, set ptr to closing } terminating current \end{...} --- */
if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
== NULL ) goto end_of_job; /* and quit if no } found */
/* -- locate next nested \begin --- */
if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
== NULL ) break; /*no more, so we have matching \end*/
begptr += strlen(begtoken); /* push ptr past token */
if ( begptr >= endptr ) break; /* past endptr, so not nested */
/* --- have nested \begin, so push forward to next \end --- */
nbegins++; /* count another nested \begin */
if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
== NULL ) goto end_of_job; /* and quit if none found */
} /* --- end-of-while(1) --- */
/* --- push expression past closing } of \end{} --- */
*expression = braceptr+1; /* resume processing after } */
/* -------------------------------------------------------------------------
add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
-------------------------------------------------------------------------- */
/* --- add on everything, completing subexpr for \begin{}...\end{} --- */
sublen = strlen(subexpr); /* #chars in "preamble" */
envlen = (int)(endptr-exprptr); /* #chars between \begin{}{}...\end */
memcpy(subexpr+sublen,exprptr,envlen); /*concatanate environ after subexpr*/
subexpr[sublen+envlen] = '\000'; /* and null-terminate */
if ( 2 > 1 ) /* always... */
strcat(subexpr,"}"); /* ...followed by terminating } */
/* --- add terminating \right), etc, if necessary --- */
if ( delims != (char *)NULL ) /* need closing delim */
{ strcat(subexpr,"\\"); /* start with \ for ),],},|,= */
strcat(subexpr,delims+1); } /* add appropriate closing delim */
/* -------------------------------------------------------------------------
change nested \begin...\end to {\begin...\end} so \array{} can handle them
-------------------------------------------------------------------------- */
if ( nbegins > 0 ) /* have nested begins */
if ( blevel < 2 ) /* only need to do this once */
{
begptr = subexpr; /* start at beginning of subexpr */
while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
{ strchange(0,begptr,"{"); /* \begin --> {\begin */
begptr += strlen(begtoken); } /* continue past {\begin */
endptr = subexpr; /* start at beginning of subexpr */
while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
== NULL ) goto end_of_job; /* and quit if no } found */
else /* found terminating } */
{ strchange(0,braceptr,"}"); /* \end{...} --> \end{...}} */
endptr = braceptr+1; } /* continue past \end{...} */
} /* --- end-of-if(nbegins>0) --- */
/* -------------------------------------------------------------------------
post process as necessary
-------------------------------------------------------------------------- */
switch ( ienviron )
{
default: break; /* no post-processing required */
case 10: /* align */
strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
if ( nbegins < 1 ) /* don't modify nested arrays */
strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
break;
case 11: /* verbatim */
strreplace(subexpr,"\n","\\\\",0); /* xlate \n newline to latex \\ */
/*strcat(subexpr,"\\\\");*/ /* add final latex \\ newline */
break;
case 12: /* picture */
strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
break;
} /* --- end-of-switch(ienviron) --- */
/* -------------------------------------------------------------------------
return rasterized mimeTeX equivalent of \begin{}...\end{} environment
-------------------------------------------------------------------------- */
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
/* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
sp = rasterize(subexpr,size); /* rasterize subexpr */
end_of_job:
blevel--; /* decrement \begin nesting level */
return ( sp ); /* back to caller with sp or NULL */
} /* --- end-of-function rastbegin() --- */
/* ==========================================================================
* Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \array handler, returns a subraster corresponding to array
* expression (immediately following \array) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \array to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \array
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to array
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \array{3,lcrBC$a&b&c\\d&e&f\\etc}
* o The 3,lcrBC$ part is an optional "preamble". The lcr means
* what you think, i.e., "horizontal" left,center,right
* justification down corresponding column. The new BC means
* "vertical" baseline,center justification across corresponding
* row. The leading 3 specifies the font size 0-4 to be used.
* You may also specify +1,-1,+2,-2, etc, which is used as an
* increment to the current font size, e.g., -1,lcr$ uses
* one font size smaller than current. Without a leading
* + or -, the font size is "absolute".
* o The preamble can also be just lcrBC$ without a leading
* size-part, or just 3$ without a trailing lcrBC-part.
* The default size is whatever is current, and the
* default justification is c(entered) and B(aseline).
* ======================================================================= */
/* --- entry point --- */
subraster *rastarray ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[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 = 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},
hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
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},
fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
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},
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};
static int /* --- propagate global values across arrays --- */
gjustify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
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},
gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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},
gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
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},
gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
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},
growcenter[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};
int rowglobal=0, colglobal=0, /* true to set global values */
rowpropagate=0, colpropagate=0; /* true if propagating values */
int irow,nrows=0, icol,ncols[65], /*#rows in array, #cols in each row*/
maxcols=0; /* max# cols in any single row */
int itoken, ntokens=0, /* index, total #tokens in array */
subtoklen=0, /* strlen of {...} subtoken */
istokwhite=1, /* true if token all whitespace */
nnonwhite=0; /* #non-white tokens */
int isescape=0,wasescape=0, /* current,prev chars escape? */
ischarescaped=0, /* is current char escaped? */
nescapes=0; /* #consecutive escapes */
subraster *rasterize(), *toksp[1025], /* rasterize tokens */
*new_subraster(), *arraysp=NULL; /* subraster for entire array */
raster *arrayrp=NULL; /* raster for entire array */
int delete_subraster(); /* free toksp[] workspace at eoj */
int rowspace=2, colspace=4, /* blank space between rows, cols */
hspace=1, vspace=1; /*space to accommodate hline,vline*/
int width=0, height=0, /* width,height of array */
leftcol=0, toprow=0; /*upper-left corner for cell in it*/
int rastput(); /* embed tokens/cells in array */
int rule_raster(); /* draw hlines and vlines in array */
char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
char *texchar(), hltoken[1025]; /* extract \hline from token */
int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
int isnewrow=1; /* true for new row */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
static int mydaemonlevel = 0; /* check against global daemonlevel*/
/* -------------------------------------------------------------------------
Macros to determine extra raster space required for vline/hline
-------------------------------------------------------------------------- */
#define vlinespace(icol) \
( vline[icol] == 0? 0 : /* no vline so no space needed */ \
( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
#define hlinespace(irow) \
( hline[irow] == 0? 0 : /* no hline so no space needed */ \
( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
/* -------------------------------------------------------------------------
Obtain array subexpression
-------------------------------------------------------------------------- */
/* --- parse for array subexpression, and bump expression past it --- */
subexpr[1] = *subexpr = ' '; /* set two leading blanks */
*expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display array */
fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
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 --- */
exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
/* --- init with global values --- */
for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
justify[icol] = gjustify[icol]; /* -1,0,+1 = l,c,r */
colwidth[icol] = gcolwidth[icol]; /* column width */
rowheight[icol] = growheight[icol]; /* row height */
fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
/* --- process lcr's, etc in preamble --- */
itoken = 0; /* debugging flag */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display preamble */
if ( *preptr != '\000' ) /* if we have one */
fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
preptr);
irow = icol = 0; /* init lcr counts */
while ( *preptr != '\000' ) /* check preamble text for lcr */
{
char prepchar = *preptr; /* current preamble character */
int prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
if ( irow=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp," %c[%d]",prepchar,
(prepcase==1?icol+1:(prepcase==2?irow+1:0)));
preptr++; /* check next char for lcr */
itoken++; /* #lcr's processed (debugging only)*/
/* --- check for number or +number specifying colwidth or rowheight --- */
if ( prepcase != 0 ) /* only check upper,lowercase */
{
int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
if ( ispropagate ) { /* set row or col propagation */
if ( prepcase == 1 ) colpropagate = 1; /* propagating col 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 */
if ( !rowpropagate && prepcase == 2 )
{ rowheight[irow] = 0; /* reset row height */
fixrowsize[irow] = 0; } /* reset height flag */
if ( ispropagate ) preptr++; /* bump past leading + */
if ( isdigit(*preptr) ) /* digit follows character */
{ char *endptr = NULL; /* preptr set to 1st char after num*/
int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
char *whchars="?wh"; /* debugging width/height labels */
preptr = endptr; /* skip over all digits */
if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
int index; /* icol,irow...maxarraysz index */
if ( prepcase == 1 ) /* lowercase signifies colwidth */
for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
colwidth[index] = size; /* set colwidth to fixed size */
fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
justify[index] = justify[icol]; /* and propagate justification */
if ( colglobal ) { /* set global values */
gcolwidth[index] = colwidth[index]; /* set global col width */
gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
gjustify[index] = justify[icol]; } /* set global col justify */
if ( !ispropagate ) break; } /* don't propagate */
else /* uppercase signifies rowheight */
for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
rowheight[index] = size; /* set rowheight to size */
fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
rowcenter[index] = rowcenter[irow]; /* and propagate row center */
if ( rowglobal ) { /* set global values */
growheight[index] = rowheight[index]; /* set global row height */
gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
growcenter[index] = rowcenter[irow]; } /*set global row center*/
if ( !ispropagate ) break; } /* don't propagate */
} /* --- end-of-if(size>=3&&size<=500) --- */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
(prepcase==1?colwidth[icol]:rowheight[irow]),
(prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
} /* --- end-of-if(isdigit()) --- */
} /* --- end-of-if(prepcase!=0) --- */
if ( prepcase == 1 ) icol++; /* bump col if lowercase lcr */
else if ( prepcase == 2 ) irow++; /* bump row if uppercase BC */
} /* --- end-of-while(*preptr!='\000') --- */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging, emit final newline */
if ( itoken > 0 ) /* if we have preamble */
fprintf(msgfp,"\n");
/* -------------------------------------------------------------------------
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 */
{
/* --- local control flags --- */
int iseox = (*exprptr == '\000'), /* null signals end-of-expression */
iseor = iseox, /* \\ or eox signals end-of-row */
iseoc = iseor; /* & or eor signals end-of-col */
/* --- check for escapes --- */
isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
nescapes = (wasescape?nescapes+1:0); /* # preceding consecutive escapes */
ischarescaped = (nescapes%2==0?0:1); /* is current char escaped? */
/* -----------------------------------------------------------------------
check for {...} subexpression starting from where we are now
------------------------------------------------------------------------ */
if ( *exprptr == '{' /* start of {...} subexpression */
&& !ischarescaped ) /* if not escaped \{ */
{
subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
subtoklen = strlen(subtok); /* #chars in {...} */
memcpy(tokptr,exprptr,subtoklen); /* copy {...} to accumulated token */
tokptr += subtoklen; /* bump tokptr to end of token */
exprptr += subtoklen; /* and bump exprptr past {...} */
istokwhite = 0; /* signal non-empty token */
continue; /* continue with char after {...} */
} /* --- end-of-if(*exprptr=='{') --- */
/* -----------------------------------------------------------------------
check for end-of-row(\\) and/or end-of-col(&)
------------------------------------------------------------------------ */
/* --- check for (escaped) end-of-row delimiter --- */
if ( isescape && !ischarescaped ) /* current char is escaped */
if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
|| *(exprptr+1) == '\000' ) /* or a pathological null */
{ iseor = 1; /* so set end-of-row flag */
wasescape=isescape=nescapes = 0; } /* reset flags for new row */
/* --- check for end-of-col delimiter --- */
if (iseor /* end-of-row signals end-of-col */
|| (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
iseoc = 1; /* so set end-of-col flag */
/* -----------------------------------------------------------------------
rasterize completed token
------------------------------------------------------------------------ */
if ( iseoc ) /* we have a completed token */
{
*tokptr = '\000'; /* first, null-terminate token */
/* --- 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 */
len = iround(unitlength*strtod(lenexpr,NULL)); /* len in pixels */
if ( len>=(-63) && len<=255 ) { /* sanity check */
vrowspace[nrows] = len; /* extra vspace before this row */
strcpy(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 ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
hline[nrows] += 1; /* bump \hline count for row */
else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
hline[nrows] = (-1); } /* set \hdash flag for row */
if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
{ skipwhite(tokptr); /* flush whitespace after \hline */
if ( *tokptr == '\000' /* end-of-expression after \hline */
|| isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
{ istokwhite = 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 */
} /* --- end-of-if(ncols[nrows]==0) --- */
/* --- rasterize completed token --- */
toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
rasterize(token,size)); /* rasterize non-empty token */
if ( toksp[ntokens] != NULL ) /* have a rasterized token */
nnonwhite++; /* bump rasterized token count */
/* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
if ( toksp[ntokens] != NULL ) /* we have a rasterized token */
{
/* --- update max token "height" in current row, and baseline --- */
int twidth = ((toksp[ntokens])->image)->width, /* width of token */
theight = ((toksp[ntokens])->image)->height, /* height of token */
tbaseln = (toksp[ntokens])->baseline, /* baseline of token */
rheight = rowheight[nrows], /* current max height for row */
rbaseln = rowbaseln[nrows]; /* current baseline for max height */
if ( 0 || fixrowsize[nrows]==0 ) /* rowheight not fixed */
rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
max2(rbaseln+1,tbaseln+1) /* max height above baseline */
+ max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
/* --- update max token width in current column --- */
icol = ncols[nrows]; /* current column index */
if ( 0 || fixcolsize[icol]==0 ) /* colwidth not fixed */
colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
} /* --- end-of-if(toksp[]!=NULL) --- */
/* --- bump counters --- */
if ( !ishonly ) /* don't count only an \hline */
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 */
} /* --- end-of-if(iseoc) --- */
/* -----------------------------------------------------------------------
bump row as necessary
------------------------------------------------------------------------ */
if ( iseor ) /* we have a completed row */
{
maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
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 */
iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
isnewrow = 1; /* signal start of new row */
} /* --- end-of-if(iseor) --- */
else
isnewrow = 0; /* no longer first col of new row */
/* -----------------------------------------------------------------------
quit when done, or accumulate char in token and proceed to next char
------------------------------------------------------------------------ */
/* --- quit when done --- */
if ( iseox ) break; /* null terminator signalled done */
/* --- accumulate chars in token --- */
if ( !iseoc ) /* don't accumulate delimiters */
{ *tokptr++ = *exprptr; /* accumulate non-delim char */
if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
istokwhite = 0; } /* so reset flag to rasterize it */
/* --- ready for next char --- */
exprptr++; /* bump ptr */
} /* --- end-of-while(*exprptr!='\000') --- */
/* --- make sure we got something to do --- */
if ( nnonwhite < 1 ) /* completely empty array */
goto end_of_job; /* NULL back to caller */
/* -------------------------------------------------------------------------
determine dimensions of array raster and allocate it
-------------------------------------------------------------------------- */
/* --- adjust colspace --- */
colspace = 2 + 2*size; /* temp kludge */
/* --- reset propagated sizes at boundaries of array --- */
colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
/* --- determine width of array raster --- */
width = colspace*(maxcols-1); /* empty space between cols */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp,"rastarray> %d cols, widths: ",maxcols);
for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */
{ width += colwidth[icol]; /*width of this col (0 for maxcols)*/
width += vlinespace(icol); /*plus space for vline, if present*/
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
/* --- determine height of array raster --- */
height = rowspace*(nrows-1); /* empty space between rows */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
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))); }
/* --- allocate subraster and raster for array --- */
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
width,colspace, height,rowspace);
if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
arraysp->type = IMAGERASTER; /* image */
arraysp->symdef = NULL; /* not applicable for image */
arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
arraysp->size = size; /* size (probably unneeded) */
arrayrp = arraysp->image; /* raster embedded in subraster */
/* -------------------------------------------------------------------------
embed tokens/cells in array
-------------------------------------------------------------------------- */
itoken = 0; /* start with first token */
toprow = 0; /* start at top row of array */
for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/
{
/* --- initialization for row --- */
int baseline = rowbaseln[irow]; /* baseline for this row */
if ( hline[irow] != 0 ) /* need hline above this row */
{ int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
toprow += 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; icolimage)->width, /* token width */
theight= (tsp->image)->height, /* token height */
tokencol = 0, /*H offset (init for left justify)*/
tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
/* --- adjust leftcol for vline to left of icol, if present ---- */
/*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
/* --- reset justification (if not left-justified) --- */
if ( justify[icol] == 0 ) /* but user wants it centered */
tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
else if ( justify[icol] == 1 ) /* or user wants right-justify */
tokencol = cwidth-twidth; /* so put entire margin at left */
/* --- reset vertical centering (if not baseline-aligned) --- */
if ( rowcenter[irow] ) /* center cells in row vertically */
tokenrow = (rowheight[irow]-theight)/2; /* center row */
/* --- embed token raster at appropriate place in array raster --- */
rastput(arrayrp,tsp->image, /* overlay cell token in array */
toprow+ tokenrow, /*with aligned baseline or centered*/
leftcol+tokencol, 1); /* and justified as requested */
} /* --- end-of-if(tsp!=NULL) --- */
itoken++; /* bump index for next cell */
leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
/* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
} /* --- end-of-for(icol) --- */
toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
draw vlines as necessary
-------------------------------------------------------------------------- */
leftcol = 0; /* start at leftmost column */
for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */
{
if ( vline[icol] != 0 ) /* need vline to left of this col */
{ int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
leftcol += vlinespace(icol); /* space for vline to left of col */
if ( icol < maxcols ) /* don't address past end of array */
leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
} /* --- end-of-for(icol) --- */
/* -------------------------------------------------------------------------
free workspace and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
/* --- free workspace --- */
if ( ntokens > 0 ) /* if we have workspace to free */
while ( --ntokens >= 0 ) /* free each token subraster */
if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */
delete_subraster(toksp[ntokens]); /* then free it */
/* --- return final result to caller --- */
return ( arraysp );
} /* --- end-of-function rastarray() --- */
/* ==========================================================================
* Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \picture handler, returns subraster corresponding to picture
* expression (immediately following \picture) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \picture to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \picture
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to picture
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastpicture ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
pream[64], *preptr, /* optional put preamble */
picelem[1025]; /* picture element following put */
subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
*new_subraster(), *picturesp=NULL, /* subraster for entire picture */
*oldworkingbox = workingbox; /* save working box on entry */
raster *picturerp=NULL; /* raster for entire picture */
int delete_subraster(); /* free picelemsp[] workspace */
int pixsz = 1; /* pixels are one bit each */
double strtod(), /* convert ascii params to doubles */
x=0.0,y=0.0, /* x,y-coords for put,multiput*/
xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
int width=0, height=0, /* #pixels width,height of picture */
ewidth=0, eheight=0, /* pic element width,height */
ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
num=1, inum; /* number reps, index of element */
int iscenter=0; /* center or lowerleft put position*/
int *oldworkingparam = workingparam, /* save working param on entry */
origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
int rastput(); /* embed elements in picture */
int type_raster(); /* display debugging output */
/* -------------------------------------------------------------------------
First obtain (width,height) arguments immediately following \picture command
-------------------------------------------------------------------------- */
/* --- parse for (width,height) arguments, and bump expression past it --- */
*expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
/* --- now interpret width,height returned in putexpr --- */
if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
*putptr = '\000'; /* found it, so replace ',' by '\0'*/
width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
if ( putptr != NULL ) /* 2nd arg, if present, is height */
height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
/* -------------------------------------------------------------------------
Then obtain entire picture {...} subexpression following (width,height)
-------------------------------------------------------------------------- */
/* --- parse for picture subexpression, and bump expression past it --- */
*expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
/* -------------------------------------------------------------------------
allocate subraster and raster for complete picture
-------------------------------------------------------------------------- */
/* --- sanity check on width,height args --- */
if ( width < 2 || width > 600
|| height < 2 || height > 600 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed picture --- */
if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
== NULL ) goto end_of_job; /* quit if failed */
workingbox = picturesp; /* set workingbox to our picture */
/* --- initialize picture subraster parameters --- */
picturesp->type = IMAGERASTER; /* image */
picturesp->symdef = NULL; /* not applicable for image */
picturesp->baseline = height/2 + 2; /* is a little above center good? */
picturesp->size = size; /* size (probably unneeded) */
picturerp = picturesp->image; /* raster embedded in subraster */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
/* -------------------------------------------------------------------------
parse out each picture element, rasterize it, and place it in picture
-------------------------------------------------------------------------- */
while ( *picptr != '\000' ) /* until we run out of pic_elems */
{
/* -----------------------------------------------------------------------
first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
------------------------------------------------------------------------ */
/* --- init default values in case not explicitly supplied in args --- */
x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */
iscenter = origin = 0; /* center, origin */
/* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
while ( *picptr != '\000' ) /* skip invalid chars preceding ( */
if ( *picptr == '(' ) break; /* found opening ( */
else picptr++; /* else skip invalid char */
picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
/* --- first look for $-terminated or for any non-digit preamble --- */
*pream = '\000'; /* init preamble as empty string */
if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
{ *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
strcpy(pream,putexpr); } /* copy leading preamble from put */
else /* look for any non-digit preamble */
{ for ( preptr=pream,putptr=putexpr; ; putptr++ )
if ( *putptr == '\000' /* end-of-putdata signalled */
|| !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
else *preptr++ = *putptr; /* copy alpha char to preamble */
*preptr = '\000'; } /* null-terminate preamble */
/* --- interpret preamble --- */
for ( preptr=pream; ; preptr++ ) /* examine each preamble char */
if ( *preptr == '\000' ) break; /* end-of-preamble signalled */
else switch ( tolower(*preptr) ) /* check lowercase preamble char */
{
default: break; /* unrecognized flag */
case 'c': iscenter=1; break; /* center pic_elem at x,y coords */
} /* --- end-of-switch --- */
/* --- interpret x,y;xinc,yinc;num following preamble --- */
if ( *putptr != '\000' ) /*check for put data after preamble*/
{
/* --- first squeeze preamble out of put expression --- */
if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
/* --- interpret x,y --- */
if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
*multptr = '\000'; /* replace semicolon by '\0' */
if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
*putptr = '\000'; /* replace comma by '\0' */
if ( *putexpr != '\000' ) /* leading , may be placeholder */
x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
y = unitlength*strtod(putptr+1,NULL); /* in pixels */
/* --- interpret xinc,yinc,num if we have a multiput --- */
if ( multptr != NULL ) /* found ';' signalling multiput