/****************************************************************************
*
* Copyright(c) 2002-2004, John Forkosh Associates, Inc. All rights reserved.
* --------------------------------------------------------------------------
* This file is part of mimeTeX, which is free software. You may redistribute
* and/or modify it under the terms of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
* MimeTeX is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
* See the GNU General Public License for specific details.
* By using mimeTeX, you warrant that you have read, understood and
* agreed to these terms and conditions, and that you are at least 18 years
* of age and possess the legal right and ability to enter into this
* agreement and to use mimeTeX in accordance with it.
* Your mimeTeX distribution should contain a copy of the GNU General
* Public License. If not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or point your browser to http://www.gnu.org/licenses/gpl.html
* --------------------------------------------------------------------------
*
* Purpose: o Parses a LaTeX math expression and emits a gif image or mime
* xbitmap of it, rather than the usual TeX dvi. MimeTeX images
* can therefore be directly inserted into html documents using
* the standard html tag, e.g.,
*
* without intermediate dvi-to-gif conversion, and without
* storing lots of resulting little gif image files, one file
* for each converted expression. This makes your site and
* html documents more readable and easily maintained.
* o MimeTeX isn't primarily meant for latex2html-like tasks
* where you're maintaining native (La)TeX documents that are
* later redistributed in several formats, including html.
* Rather, mimeTeX is primarily meant to help maintain native
* html documents containing math. In this sense it's a kind
* of "lightweight" alternative to MathML, with the advantage
* that mimeTeX preserves LaTeX syntax, and works with any
* browser and server.
*
* Functions: ===================== Raster Functions ======================
* --- raster constructor functions ---
* new_raster(width,height,pixsz) allocation (and constructor)
* new_subraster(width,height,pixsz)allocation (and constructor)
* new_chardef() allocate chardef struct
* delete_raster(rp) deallocate raster (rp = raster ptr)
* delete_subraster(sp) deallocate subraster (sp=subraster ptr)
* delete_chardef(cp) deallocate chardef (cp = chardef ptr)
* --- primitive (sub)raster functions ---
* rastcpy(rp) allocate new copy of rp
* subrastcpy(sp) allocate new copy of sp
* rastrot(rp) new raster rotated right 90 degrees to rp
* rastput(target,source,top,left,isopaque) overlay src on trgt
* rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
* rastcat(sp1,sp2,isfree) concatanate sp1||sp2
* rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
* rastile(tiles,ntiles) create composite raster from tiles
* --- raster "drawing" functions ---
* accent_subraster(accent,width,height) draw \hat\vec\etc
* arrow_subraster(width,height,drctn,isBig) left/right arrow
* uparrow_subraster(width,height,drctn,isBig) up/down arrow
* rule_raster(rp,top,left,width,height,type) draw rule in rp
* line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
* line_recurse(rp,row0,col0,row1,col1,thickness) recurse line
* circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
* bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively
* border_raster(rp,ntop,nbot,isline,isfree)put border around rp
* --- raster (and chardef) output functions ---
* type_raster(rp,fp) emit ascii dump of rp on file ptr fp
* type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
* xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp
* cstruct_chardef(cp,fp,col1) emit C struct of cp on fp
* cstruct_raster(rp,fp,col1) emit C struct of rp on fp
* hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
* --- ancillary output functions ---
* emit_string(fp,col1,string,comment) emit string and C comment
* ====================== Font Functions =======================
* --- font lookup functions ---
* get_symdef(symbol) returns mathchardef for symbol
* get_chardef(symdef,size) returns chardef for symdef,size
* get_charsubraster(symdef,size) wrap subraster around chardef
* --- ancillary font functions ---
* get_baseline(gfdata) determine baseline (in our coords)
* get_delim(symbol,height,family) delim just larger than height
* make_delim(symbol,height) construct delim exactly height size
* ================= Tokenize/Parse Functions ==================
* texchar(expression,chartoken) retruns next char or \sequence
* texsubexpr(expression,subexpr,left,right,isescape,isdelim)
* texscripts(expression,subscript,superscript,which)get scripts
* --- ancillary parse functions ---
* isbrace(expression,braces,isescape) check for leading brace
* preamble(expression,size,subexpr) parse preamble
* mimeprep(expression) preprocessor converts \left( to \(, etc.
* strchange(nfirst,from,to) change nfirst chars of from to to
* findbraces(expression,command) find opening { or closing }
* =========== Rasterize an Expression (recursively) ===========
* --- here's the primary entry point for all of mimeTeX ---
* rasterize(expression,size) parse and rasterize expression
* --- explicitly called handlers that rasterize... ---
* rastparen(subexpr,size,basesp) parenthesized subexpr
* rastlimits(expression,size,basesp) dispatch super/sub call
* rastscripts(expression,size,basesp) super/subscripted exprssn
* rastdispmath(expression,size,sp) scripts for displaymath
* --- table-driven handlers that rasterize... ---
* rastflags(expression,size,basesp,flag,value,arg3) set flag
* rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
* rastnewline(expression,size,basesp,arg1,arg2,arg3) \\
* rastarrow(expression,size,basesp,width,height,drctn) \longarr
* rastuparrow(expression,size,basesp,width,height,drctn)up/down
* rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not
* rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
* rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel
* rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
* rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt
* rastaccent(expression,size,basesp,accent,isabove,isscript)
* rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
* rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{}
* rastarray(expression,size,basesp,arg1,arg2,arg3) \array
* rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture
* rastline(expression,size,basesp,arg1,arg2,arg3) \line
* rastcircle(expression,size,basesp,arg1,arg2,arg3) \circle
* rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier
* rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox
* rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox
* rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
* rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
* ================ Anti-alias completed raster ================
* aalowpass(rp,bytemap,grayscale) make grayscale bytemap
* aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
* --- the following two functions tested but not used yet ---
* aaweights(width,height) builds "canonical" weight matrix
* aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
* ========================== Driver ===========================
* main(argc,argv) parses math expression and emits mime xbitmap
* isstrstr(string,snippets,iscase) are any snippets in string?
* unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
* logger(fp,msglevel,logvars) logs environment variables
* timestamp( ) formats timestamp string (used by logger)
* GetPixel(x,y) callback function for gifsave library
*
* Source: mimetex.c (needs mimetex.h and texfonts.h to compile,
* and also needs gifsave.c if compiled with -DGIF switch)
*
* --------------------------------------------------------------------------
* Notes o See bottom of file for main() driver, and compile as
* cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
* to produce an executable that emits gif images with
* anti-aliasing (see Notes below). You may also compile
* cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
* to produce an executable that emits gif images without
* anti-aliasing. Alternatively, compile mimeTeX as
* cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
* to produce an executable that just emits mime xbitmaps.
* In either case you'll need mimetex.h and texfonts.h,
* and with -DGIF you'll also need gifsave.c
* o For gif images, the gifsave.c library by Sverre H. Huseby
* , slightly modified by me to allow
* (a)sending output to stdout and (b)specifying a transparent
* background color index, is included with mimeTeX,
* and documented in mimetex.html.
* o Optional compile-line -D defined variables include...
* -DAA
* Turns on gif anti-aliasing with default values
* (CENTERWT=12, MINADJACENT=4, MAXADJACENT=7)
* for anti-aliasing parameters below...
* -DCENTERWT=n
* At this time, mimeTeX only provides a lowpass filtering
* algorithm for anti-aliasing (see the Notes section of
* function aalowpass() below), which is applied to the
* existing set of fonts, using weights
* 1 3 1
* 3 x 3
* 1 3 1
* for adjacent pixels, and weight x=CENTERWT
* (which defaults to 10) for the center pixel.
* If you modify the weights[] array for adjacent
* pixels in aalowpass(), make sure to also modify
* neighborwts which must be their sum (16 as shown).
* I've modified the "canonical" lowpass algorithm,
* so that if the center pixel is black, then the
* anti-aliased value remains completely black
* regardless of adjacent pixels/weights.
* The intent is to prevent thin lines,
* typical for text, from getting washed out.
* As a result, anti-aliased lines can only become
* heavier than they started. A low CENTERWT value
* will blur/spread out lines without washing out their
* centers. A high CENTERWT value will sharpen lines.
* Experimentation is recommended to determine
* what value works best for you.
* -DMINADJACENT=n
* Besides CENTERWT, mimeTeX's lowpass algorithm differs
* from the "canonical" one in that the weighted grayscale
* value calculated for a pixel is applied to it _only_
* if the summed weights of adjacent black pixels is
* greater than or equal to MINADJACENT (default 6);
* or, as above, originally black pixels remain black.
* That is, an originally white pixel remains white
* if the summed weights of adjacent black pixels is
* less than MINADJACENT.
* -DMAXADJACENT=n
* As above, an originally white pixel remains white if
* the summed weights of adjacent black pixels is greater
* than MAXADJACENT (default 8). Note that default
* MINADJACENT=6 and MAXADJACENT=8 apply lowpass
* anti-aliasing only to originally white pixels
* surrounded by adjacent black pixels whose summed
* weights are between 6 and 8, inclusive.
* Experimentation with these two parameters is also
* recommended.
* -NORMALSIZE=n
* mimeTeX has six font sizes numbered 0-5, and starts
* in NORMALSIZE (default 2).
* -DREFERER=\"domain\" -or-
* -DREFERER=\"domain1,domain2,etc\"
* If REFERER is defined, mimeTeX checks for the environment
* variable HTTP_REFERER and, if it exists, performs a
* case-insensitive test to make sure it contains 'domain'
* as a substring. If given several 'domain's (second form)
* then HTTP_REFERER must contain either 'domain1' or
* 'domain2', etc, as a (case-insensitive) substring.
* If HTTP_REFERER fails to contain a substring matching
* any of these domain(s), mimeTeX emits an error message
* image corresponding to the expression specified by
* the invalid_referer_msg string defined in main().
* Note: if HTTP_REFERER is not an environment variable,
* mimeTeX correctly generates the requested expression
* (i.e., no referer error).
* -DWARNINGS=n -or-
* -DNOWARNINGS
* If an expression submitted to mimeTeX contains an
* unrecognzied escape sequence, e.g., "y=x+\abc+1", then
* mimeTeX generates a gif image containing an embedded
* warning in the form "y=x+[\abc?]+1". If you want these
* warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
* mimeTeX to ignore unrecognized symbols, and the rendered
* image is "y=x++1" instead.
* o See individual function entry points for further comments.
* o The font information in texfonts.h was produced by multiple
* runs of gfuntype, one run per struct (i.e., one run per font
* family at a particular size). See gfuntype.c, and also
* mimetex.html#fonts, for details.
* o mimetex.c contains library functions implementing a raster
* datatype, functions to manipulate rasterized .mf fonts
* (see gfuntype.c which rasterizes .mf fonts), functions
* to parse LaTeX expressions, etc. A complete list of
* mimetex.c functions is above. See their individual entry
* points below for further comments.
* All these functions eventually belong in several
* different modules, possibly along the lines suggested
* by the divisions above. But until the best decomposition
* becomes clear, it seems better to keep mimetex.c
* neatly together, avoiding a bad decomposition that
* becomes permanent by default.
* o The "main" reusable function is rasterize(),
* which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
* and returns a (sub)raster representing it as a bit or bytemap.
* Your application can do anything it likes with this pixel map.
* MimeTeX just outputs it, either as a mime xbitmap or as a gif.
* --------------------------------------------------------------------------
* Revision History:
* 09/18/02 J.Forkosh Installation.
* 12/11/02 J.Forkosh Version 1.00 released.
* 07/04/03 J.Forkosh Version 1.01 released.
* 10/17/03 J.Forkosh Version 1.20 released.
* 12/21/03 J.Forkosh Version 1.30 released.
* 02/01/04 J.Forkosh Version 1.40 released.
*
****************************************************************************/
/* -------------------------------------------------------------------------
header files
-------------------------------------------------------------------------- */
/* --- standard headers --- */
#include
#include
/*#include */
#include
#include
#include
/* --- application headers --- */
#define TEXFONTS /* to include texfonts.h */
#include "mimetex.h"
/* -------------------------------------------------------------------------
driver conditionally compiled if output format (xbitmap,gif,png) specified
-------------------------------------------------------------------------- */
/* --- explicitly set gif if any gif options specified --- */
#if defined(AA) || defined(CENTERWT) || defined(ADJACENTWT) \
|| defined(MINADJACENT) || defined(MAXADJACENT)
#ifndef GIF /* gif not explicitly requested */
#define GIF /* so define it ourselves */
#endif
#endif
/* --- check whether xbitmap (default), gif or png output desired --- */
#if defined(XBITMAP) || defined(GIF) || defined(PNG)
#define DRIVER /* driver will be compiled */
/* --- check whether or not to perform http_referer check --- */
#ifndef REFERER /* all http_referer's allowed */
#define REFERER NULL
#endif
/* --- check whether or not to apply anti-aliasing --- */
#if defined(CENTERWT) || defined(ADJACENTWT) \
|| defined(MINADJACENT) || defined(MAXADJACENT)
#ifndef AA /* but not explicitly requested */
#define AA /* so define it ourselves */
#endif
#endif
/* --- resolve inconsistencies --- */
#if defined(XBITMAP) /* xbitmap supercedes gif */
#ifdef GIF
#undef GIF
#endif
#endif
#endif
/* -------------------------------------------------------------------------
adjustable default values
-------------------------------------------------------------------------- */
/* --- anti-aliasing parameters --- */
#ifndef CENTERWT
#define CENTERWT 10 /* anti-aliasing centerwt default */
#endif
#ifndef ADJACENTWT
#define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/
#endif
#ifndef MINADJACENT
#define MINADJACENT 6 /*anti-aliasing minadjacent default*/
#endif
#ifndef MAXADJACENT
#define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/
#endif
/* --- variables for anti-aliasing parameters --- */
static int centerwt = CENTERWT, /*lowpass matrix center pixel weight*/
adjacentwt = ADJACENTWT, /*lowpass matrix adjacent pixel wt*/
minadjacent = MINADJACENT, /* darken if>=adjacent pts black*/
maxadjacent = MAXADJACENT; /* darken if<=adjacent pts black */
static int weightnum=1, maxaaparams=4; /* font wt, #entries in table */
/* --- parameter values by font weight --- */
static struct
{ int centerwt; /*lowpass matrix center pixel weight*/
int adjacentwt; /* lowpass matrix adjacent pixel wt*/
int minadjacent; /* darken if >= adjacent pts black */
int maxadjacent; } /* darken if <= adjacent pts black */
aaparams[] = /* set params by weight */
{ /* ------------------------------------------------
centerwt adjacentwt minadj. maxadjacent
------------------------------------------------ */
{ 64, 3, 6, 8 }, /* 0 = light */
{ CENTERWT,ADJACENTWT,MINADJACENT,MAXADJACENT },/* 1 = regular */
{ 12, 2, 5, 8 }, /* 2 = semibold */
{ 8, 2, 4, 9 } /* 3 = bold */
} ; /* --- end-of-aaparams[] --- */
/* -------------------------------------------------------------------------
other variables
-------------------------------------------------------------------------- */
/* --- black on white background (default), or white on black --- */
#ifdef WHITE
#define ISBLACKONWHITE 0 /* white on black background */
#else
#define ISBLACKONWHITE 1 /* black on white background */
#endif
/* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
#ifdef NOARGSIGNAL
#define ARGSIGNAL NULL
#endif
#ifndef ARGSIGNAL
#define ARGSIGNAL "++"
#endif
/* -------------------------------------------------------------------------
debugging and logging / error reporting
-------------------------------------------------------------------------- */
/* --- debugging and error reporting --- */
#ifndef MSGLEVEL
#define MSGLEVEL 1
#endif
#define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */
#define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */
#ifndef FORMLEVEL
#define FORMLEVEL MSGLEVEL /*msglevel if called from html form*/
#endif
static int msglevel = MSGLEVEL; /* message level for verbose/debug */
static FILE *msgfp; /* output in command-line mode */
/* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
static int warninglevel = /* warning level */
#ifdef WARNINGS
WARNINGS ;
#else
#ifdef NOWARNINGS
0;
#else
1;
#endif
#endif
/* -------------------------------------------------------------------------
control flags and values
-------------------------------------------------------------------------- */
static int recurlevel = 0, /* inc/decremented in rasterize() */
scriptlevel = 0; /* inc/decremented in rastlimits() */
static int istext = 0, /* text mode when true, italics=2 */
isdisplaymath = 1, /* displaymath mode (forced if 2) */
displaysize = NORMALSIZE; /* current size */
static double unitlength = 1.0; /* #pixels per unit (may be <1.0) */
static int istransparent = 1, /*true to set background transparent*/
fgcolor = (ISBLACKONWHITE?0:255), /* default fg black */
bgcolor = (ISBLACKONWHITE?255:0), /* default bg white */
isblackonwhite=ISBLACKONWHITE; /*1=black on white,0=reverse*/
static int *workingparam = (int *)NULL; /* working parameter */
static subraster *workingbox = (subraster *)NULL; /*working subraster box*/
static int isreplaceleft = 0; /* true to replace leftexpression */
static subraster *leftexpression = (subraster *)NULL; /*rasterized so far*/
static mathchardef *leftsymdef = NULL; /* mathchardef for preceding symbol*/
/* -------------------------------------------------------------------------
miscellaneous macros
-------------------------------------------------------------------------- */
#define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */
#define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
#define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
#define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
#define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
#define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
/* ==========================================================================
* Function: new_raster ( width, height, pixsz )
* Purpose: Allocation and constructor for raster.
* mallocs and initializes memory for width*height pixels,
* and returns raster struct ptr to caller.
* --------------------------------------------------------------------------
* Arguments: width (I) int containing width, in bits,
* of raster pixmap to be allocated
* height (I) int containing height, in bits/scans,
* of raster pixmap to be allocated
* pixsz (I) int containing #bits per pixel, 1 or 8
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to allocated and initialized
* raster struct, or NULL for any error.
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
raster *new_raster ( int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *rp = (raster *)NULL; /* raster ptr returned to caller */
pixbyte *pixmap = NULL; /* raster pixel map to be malloced */
int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
int delete_raster(); /* in case pixmap malloc() fails */
/* -------------------------------------------------------------------------
allocate and initialize raster struct and embedded bitmap
-------------------------------------------------------------------------- */
/* --- allocate and initialize raster struct --- */
rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */
if ( rp == (raster *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
rp->width = width; /* store width in raster struct */
rp->height = height; /* and store height */
rp->pixsz = pixsz; /* store #bits per pixel */
rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
/* --- allocate and initialize bitmap array --- */
if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/
pixmap = (pixbyte *)malloc(nbytes); /*bytes for width*height bits*/
if ( pixmap == (pixbyte *)NULL ) /* malloc failed */
{ delete_raster(rp); /* so free everything */
rp = (raster *)NULL; /* reset pointer */
goto end_of_job; } /* and return error to caller */
memset((void *)pixmap,0,nbytes); /* init bytes to binary 0's */
rp->pixmap = pixmap; /* store ptr to malloced memory */
/* -------------------------------------------------------------------------
Back to caller with address of raster struct, or NULL ptr for any error.
-------------------------------------------------------------------------- */
end_of_job:
return ( rp ); /* back to caller with raster */
} /* --- end-of-function new_raster() --- */
/* ==========================================================================
* Function: new_subraster ( width, height, int pixsz )
* Purpose: Allocate a new subraster along with
* an embedded raster of width x height.
* --------------------------------------------------------------------------
* Arguments: width (I) int containing width of embedded raster
* height (I) int containing height of embedded raster
* pixsz (I) int containing #bits per pixel, 1 or 8
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to newly-allocated subraster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o if width or height <=0, embedded raster not allocated
* ======================================================================= */
/* --- entry point --- */
subraster *new_subraster ( int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *sp=NULL; /* subraster returned to caller */
raster *new_raster(), *rp=NULL; /* image raster embedded in sp */
int delete_subraster(); /* in case new_raster() fails */
int size = NORMALSIZE, /* default size */
baseline = height-1; /* and baseline */
/* -------------------------------------------------------------------------
allocate and initialize subraster struct
-------------------------------------------------------------------------- */
/* --- allocate subraster struct --- */
sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */
if ( sp == (subraster *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
/* --- initialize subraster struct --- */
sp->type = (-1); /* charcter or image raster */
sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */
sp->baseline = baseline; /*0 if image is entirely descending*/
sp->size = size; /* font size 0-4 */
sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */
sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/
/* -------------------------------------------------------------------------
allocate raster and embed it in subraster, and return to caller
-------------------------------------------------------------------------- */
/* --- allocate raster struct if desired --- */
if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */
if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
!= NULL ) /* if allocate succeeded */
sp->image = rp; /* embed raster in subraster */
else /* or if allocate failed */
{ delete_subraster(sp); /* free non-unneeded subraster */
sp = NULL; } /* signal error */
/* --- back to caller with new subraster or NULL --- */
end_of_job:
return ( sp );
} /* --- end-of-function new_subraster() --- */
/* ==========================================================================
* Function: new_chardef ( )
* Purpose: Allocates and initializes a chardef struct,
* but _not_ the embedded raster struct.
* --------------------------------------------------------------------------
* Arguments: none
* --------------------------------------------------------------------------
* Returns: ( chardef * ) ptr to allocated and initialized
* chardef struct, or NULL for any error.
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
chardef *new_chardef ( )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */
/* -------------------------------------------------------------------------
allocate and initialize chardef struct
-------------------------------------------------------------------------- */
cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
if ( cp == (chardef *)NULL ) /* malloc failed */
goto end_of_job; /* return error to caller */
cp->charnum = cp->location = 0; /* init character description */
cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
cp->image.width = cp->image.height = 0; /* init raster dimensions */
cp->image.pixsz = 0; /* and #bits per pixel */
cp->image.pixmap = NULL; /* init raster pixmap as null */
/* -------------------------------------------------------------------------
Back to caller with address of chardef struct, or NULL ptr for any error.
-------------------------------------------------------------------------- */
end_of_job:
return ( cp );
} /* --- end-of-function new_chardef() --- */
/* ==========================================================================
* Function: delete_raster ( rp )
* Purpose: Destructor for raster.
* Frees memory for raster bitmap and struct.
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_raster ( raster *rp )
{
/* -------------------------------------------------------------------------
free raster bitmap and struct
-------------------------------------------------------------------------- */
if ( rp != (raster *)NULL ) /* can't free null ptr */
{
if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */
free((void *)rp->pixmap); /* free pixmap within raster */
free((void *)rp); /* lastly, free raster struct */
} /* --- end-of-if(rp!=NULL) --- */
return ( 1 ); /* back to caller, 1=okay 0=failed */
} /* --- end-of-function delete_raster() --- */
/* ==========================================================================
* Function: delete_subraster ( sp )
* Purpose: Deallocates a subraster (and embedded raster)
* --------------------------------------------------------------------------
* Arguments: sp (I) ptr to subraster struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_subraster ( subraster *sp )
{
/* -------------------------------------------------------------------------
free subraster struct
-------------------------------------------------------------------------- */
int delete_raster(); /* to delete embedded raster */
if ( sp != (subraster *)NULL ) /* can't free null ptr */
{
if ( sp->type != CHARASTER ) /* not static character data */
if ( sp->image != NULL ) /*raster allocated within subraster*/
delete_raster(sp->image); /* so free embedded raster */
free((void *)sp); /* and free subraster struct itself*/
} /* --- end-of-if(sp!=NULL) --- */
return ( 1 ); /* back to caller, 1=okay 0=failed */
} /* --- end-of-function delete_subraster() --- */
/* ==========================================================================
* Function: delete_chardef ( cp )
* Purpose: Deallocates a chardef (and bitmap of embedded raster)
* --------------------------------------------------------------------------
* Arguments: cp (I) ptr to chardef struct to be deleted.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int delete_chardef ( chardef *cp )
{
/* -------------------------------------------------------------------------
free chardef struct
-------------------------------------------------------------------------- */
if ( cp != (chardef *)NULL ) /* can't free null ptr */
{
if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */
free((void *)cp->image.pixmap); /* so free embedded pixmap */
free((void *)cp); /* and free chardef struct itself */
} /* --- end-of-if(cp!=NULL) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
return ( 1 );
} /* --- end-of-function delete_chardef() --- */
/* ==========================================================================
* Function: rastcpy ( rp )
* Purpose: makes duplicate copy of rp
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be copied
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to new copy rp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
raster *rastcpy ( raster *rp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/
int height= (rp==NULL?0:rp->height), /* original and copied height */
width = (rp==NULL?0:rp->width), /* original and copied width */
pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
/* -------------------------------------------------------------------------
allocate rotated raster and fill it
-------------------------------------------------------------------------- */
/* --- allocate copied raster with same width,height, and copy bitmap --- */
if ( rp != NULL ) /* nothing to copy if ptr null */
if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
!= NULL ) /* check that allocate succeeded */
memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
return ( newrp ); /* return copied raster to caller */
} /* --- end-of-function rastcpy() --- */
/* ==========================================================================
* Function: subrastcpy ( sp )
* Purpose: makes duplicate copy of sp
* --------------------------------------------------------------------------
* Arguments: sp (I) ptr to subraster struct to be copied
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to new copy sp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *subrastcpy ( subraster *sp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
raster *rastcpy(), *newrp=NULL; /* and new raster image within it */
int delete_subraster(); /* dealloc newsp if rastcpy() fails*/
/* -------------------------------------------------------------------------
make copy, and return it to caller
-------------------------------------------------------------------------- */
if ( sp == NULL ) goto end_of_job; /* nothing to copy */
/* --- allocate new subraster "envelope" for copy --- */
if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
== NULL ) goto end_of_job; /* and quit if we fail to allocate */
/* --- transparently copy original envelope to new one --- */
memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
/* --- make a copy of the rasterized image itself, if there is one --- */
if ( sp->image != NULL ) /* there's an image embedded in sp */
if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */
== NULL ) /* failed to copy successfully */
{ delete_subraster(newsp); /* won't need newsp any more */
newsp = NULL; /* because we're returning error */
goto end_of_job; } /* back to caller with error signal*/
/* --- set new params in new envelope --- */
newsp->image = newrp; /* new raster image we just copied */
switch ( sp->type ) /* set new raster image type */
{ case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
case IMAGERASTER: default: newsp->type = IMAGERASTER; break; }
/* --- return copy of sp to caller --- */
end_of_job:
return ( newsp ); /* copy back to caller */
} /* --- end-of-function subrastcpy() --- */
/* ==========================================================================
* Function: rastrot ( rp )
* Purpose: rotates rp image 90 degrees right/clockwise
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct to be rotated
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to new raster rotated ralative to rp,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o An underbrace is } rotated 90 degrees clockwise,
* a hat is <, etc.
* ======================================================================= */
/* --- entry point --- */
raster *rastrot ( raster *rp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/
int height = rp->height, irow, /* original height, row index */
width = rp->width, icol, /* original width, column index */
pixsz = rp->pixsz; /* #bits per pixel */
/* -------------------------------------------------------------------------
allocate rotated raster and fill it
-------------------------------------------------------------------------- */
/* --- allocate rotated raster with flipped width<-->height --- */
if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
!= NULL ) /* check that allocation succeeded */
/* --- fill rotated raster --- */
for ( irow=0; irowheight - 1
* left (I) int containing 0 ... target->width - 1
* isopaque (I) int containing false (zero) to allow
* original 1-bits of target to "show through"
* 0-bits of source.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int rastput ( raster *target, raster *source,
int top, int left, int isopaque )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow, icol, /* indexes over source raster */
twidth=target->width, theight=target->height, /*target width,height*/
tpix, ntpix = twidth*theight; /* #pixels in target */
int isfatal = 0, /* true to abend on out-of-bounds error */
isstrict = 1, /* true for strict bounds check - no "wrap"*/
isokay = 1; /* true if no pixels out-of-bounds */
/* -------------------------------------------------------------------------
superimpose source onto target, one bit at a time
-------------------------------------------------------------------------- */
if ( isstrict && (top<0 || left<0) ) /* args fail strict test */
isokay = 0; /* so just return error */
else
for ( irow=0; irowheight; irow++ ) /* for each scan line */
{
tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/
for ( icol=0; icolwidth; icol++ ) /* each pixel in scan line */
{
int svalue = getpixel(source,irow,icol); /* source pixel value */
if ( ++tpix >= ntpix /* bounds check failed */
|| (isstrict && (irow+top>=theight || icol+left>=twidth)) )
{ isokay = 0; /* reset okay flag */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else break; } /*or just go on to next row*/
if ( tpix >= 0 ) /* bounds check okay */
if ( svalue!=0 || isopaque ) /*got dark or opaque source*/
setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/
} /* --- end-of-for(icol) --- */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
end_of_job:
return ( isokay /*isfatal? (tpixbaseline, /*baseline for underlying subraster*/
height1 = (sp1->image)->height, /* height for underlying subraster */
width1 = (sp1->image)->width, /* width for underlying subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */
base2 = sp2->baseline, /*baseline for overlaid subraster */
height2 = (sp2->image)->height, /* height for overlaid subraster */
width2 = (sp2->image)->width, /* width for overlaid subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
int height=0, width=0, pixsz=0, base=0; /* overlaid composite */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine height, width and baseline of composite raster --- */
if ( isalign ) /* baselines of sp1,sp2 aligned */
{ height = max2(base1+1,base2+1) /* max height above baseline */
+ max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
base = max2(base1,base2); } /* max space above baseline */
else /* baselines not aligned */
{ height = max2(height1,height2); /* max height */
base = base1 + (height-height1)/2; } /* baseline for sp1 */
width = max2(width1,width2+abs(offset2)); /* max width */
pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
/* -------------------------------------------------------------------------
allocate concatted composite subraster
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) goto end_of_job; /* failed, so quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* image */
sp->baseline = base; /* composite baseline */
sp->size = sp1->size; /* underlying char is sp1 */
/* --- extract raster from subraster --- */
rp = sp->image; /* raster allocated in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
if ( isalign )
{ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
rastput (rp, sp2->image, base-base2, /*overlaid*/
(width-width2)/2+offset2, 0); }
else
{ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
(width-width2)/2+offset2, 0); }
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to concatted subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastcompose() --- */
/* ==========================================================================
* Function: rastcat ( sp1, sp2, isfree )
* Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged
* and returning a newly-allocated subraster.
* Frees/deletes input sp1 and/or sp2 depending on value
* of isfree (0=none, 1=sp1, 2=sp2, 3=both).
* --------------------------------------------------------------------------
* Arguments: sp1 (I) subraster * to left-hand subraster
* sp2 (I) subraster * to right-hand subraster
* isfree (I) int containing 1=free sp1 before return,
* 2=free sp2, 3=free both, 0=free none.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to constructed subraster sp1||sp2
* or NULL for any error
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
raster *rp=(raster *)NULL; /* new concatted raster */
int delete_subraster(); /* in case isfree non-zero */
int rastput(); /*place sp1,sp2 in concatted raster*/
int base1 = sp1->baseline, /*baseline for left-hand subraster*/
height1 = (sp1->image)->height, /* height for left-hand subraster */
width1 = (sp1->image)->width, /* width for left-hand subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */
base2 = sp2->baseline, /*baseline for right-hand subraster*/
height2 = (sp2->image)->height, /* height for right-hand subraster */
width2 = (sp2->image)->width, /* width for right-hand subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for right-hand subraster */
int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
*symdef2 = sp2->symdef; /* mathchardef of right-hand char */
int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
space = displaysize/2+1; /* #cols between sp1 and sp2 */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine inter-character space from character class --- */
space = max2(2,(symspace[class1][class2] + displaysize-4)); /* space */
/* --- determine height, width and baseline of composite raster --- */
height = max2(base1+1,base2+1) /* max height above baseline */
+ max2(height1-base1-1,height2-base2-1); /*plus max descending below*/
width = width1 + width2 + space; /* just add widths and space */
pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
base = max2(base1,base2); /* max space above baseline */
/* -------------------------------------------------------------------------
allocate concatted composite subraster
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) goto end_of_job; /* failed, so quit */
/* --- initialize subraster parameters --- */
sp->type = STRINGRASTER; /* concatted string */
sp->symdef = symdef2; /* rightmost char is sp2 */
sp->baseline = base; /* composite baseline */
sp->size = sp2->size; /* rightmost char is sp2 */
/* --- extract raster from subraster --- */
rp = sp->image; /* raster allocated in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
rastput (rp, sp1->image, base-base1, 0, 1); /* overlay left-hand */
rastput (rp, sp2->image, base-base2, width1+space, 1); /*and right*/
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to concatted subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastcat() --- */
/* ==========================================================================
* Function: rastack ( sp1, sp2, base, space, iscenter, isfree )
* Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged
* and returning a newly-allocated subraster,
* whose baseline is sp1's if base=1, or sp2's if base=2.
* Frees/deletes input sp1 and/or sp2 depending on value
* of isfree (0=none, 1=sp1, 2=sp2, 3=both).
* --------------------------------------------------------------------------
* Arguments: sp1 (I) subraster * to lower subraster
* sp2 (I) subraster * to upper subraster
* base (I) int containing 1 if sp1 is baseline,
* or 2 if sp2 is baseline.
* space (I) int containing #rows blank space inserted
* between sp1's image and sp2's image.
* iscenter (I) int containing 1 to center both sp1 and sp2
* in stacked array, 0 to left-justify both
* isfree (I) int containing 1=free sp1 before return,
* 2=free sp2, 3=free both, 0=free none.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1
* or NULL for any error
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
subraster *rastack ( subraster *sp1, subraster *sp2,
int base, int space, int iscenter, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
raster *rp=(raster *)NULL; /* new stacked raster in sp */
int delete_subraster(); /* in case isfree non-zero */
int rastput(); /* place sp1,sp2 in stacked raster */
int base1 = sp1->baseline, /* baseline for lower subraster */
height1 = (sp1->image)->height, /* height for lower subraster */
width1 = (sp1->image)->width, /* width for lower subraster */
pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */
base2 = sp2->baseline, /* baseline for upper subraster */
height2 = (sp2->image)->height, /* height for upper subraster */
width2 = (sp2->image)->width, /* width for upper subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */
int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */
*symdef2 = sp2->symdef; /* mathchardef of right upper char */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine height, width and baseline of composite raster --- */
height = height1 + space + height2; /* sum of heights plus space */
width = max2(width1,width2); /* max width is overall width */
pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
/* -------------------------------------------------------------------------
allocate stacked composite subraster (with embedded raster)
-------------------------------------------------------------------------- */
/* --- allocate returned subraster (and then initialize it) --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== (subraster *)NULL ) goto end_of_job; /* failed, so quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* stacked rasters */
sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
sp->baseline = baseline; /* composite baseline */
sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
/* --- extract raster from subraster --- */
rp = sp->image; /* raster embedded in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
if ( iscenter == 1 ) /* center both sp1 and sp2 */
{ rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */
rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
else /* left-justify both sp1 and sp2 */
{ rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */
rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
if ( isfree > 0 ) /* caller wants input freed */
{ if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
/* -------------------------------------------------------------------------
Back to caller with pointer to stacked subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastack() --- */
/* ==========================================================================
* Function: rastile ( tiles, ntiles )
* Purpose: Allocate and build up a composite raster
* from the ntiles components/characters supplied in tiles.
* --------------------------------------------------------------------------
* Arguments: tiles (I) subraster * to array of subraster structs
* describing the components and their locations
* ntiles (I) int containing number of subrasters in tiles[]
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to composite raster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o The top,left corner of a raster is row=0,col=0
* with row# increasing as you move down,
* and col# increasing as you move right.
* Metafont numbers rows with the baseline=0,
* so the top row is a positive number that
* decreases as you move down.
* o rastile() is no longer used.
* It was used by an earlier rasterize() algorithm,
* and I've left it in place should it be needed again.
* But recent changes haven't been tested/exercised.
* ======================================================================= */
/* --- entry point --- */
raster *rastile ( subraster *tiles, int ntiles )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/
int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */
int itile; /* tiles[] index */
int rastput(); /* overlay each tile in composite raster */
/* -------------------------------------------------------------------------
run through tiles[] to determine dimensions for composite raster
-------------------------------------------------------------------------- */
/* --- determine row and column bounds of composite raster --- */
for ( itile=0; itiletoprow);
leftcol = min2(leftcol, tile->leftcol);
/* --- lower-right corner of composite --- */
botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1);
/* --- pixsz of composite --- */
pixsz = max2(pixsz,(tile->image)->pixsz);
} /* --- end-of-for(itile) --- */
/* --- calculate width and height from bounds --- */
width = rightcol - leftcol + 1;
height = botrow - toprow + 1;
/* --- sanity check (quit if bad dimensions) --- */
if ( width<1 || height<1 ) goto end_of_job;
/* -------------------------------------------------------------------------
allocate composite raster, and embed tiles[] within it
-------------------------------------------------------------------------- */
/* --- allocate composite raster --- */
if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
== (raster *)NULL ) goto end_of_job; /* and quit if failed */
/* --- embed tiles[] in composite --- */
for ( itile=0; itileimage, /* overlay tile image at...*/
tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
/* -------------------------------------------------------------------------
Back to caller with composite raster (or null for any error)
-------------------------------------------------------------------------- */
end_of_job:
return ( composite ); /* back with composite or null ptr */
} /* --- end-of-function rastile() --- */
/* ==========================================================================
* Function: accent_subraster ( accent, width, height, pixsz )
* Purpose: Allocate a new subraster of width x height
* (or maybe different dimensions, depending on accent),
* and draw an accent (\hat or \vec or \etc) that fills it
* --------------------------------------------------------------------------
* Arguments: accent (I) int containing either HATACCENT or VECACCENT,
* etc, indicating the type of accent desired
* width (I) int containing desired width of accent (#cols)
* height (I) int containing desired height of accent(#rows)
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o Some accents have internally-determined dimensions,
* and caller should check dimensions in returned subraster
* ======================================================================= */
/* --- entry point --- */
subraster *accent_subraster ( int accent, int width, int height, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- general info --- */
raster *new_raster(), *rp=NULL; /*raster containing desired accent*/
subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
int delete_raster(), delete_subraster(); /*free allocated raster on err*/
int line_raster(), /* draws lines */
thickness = 1; /* line thickness */
int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
/* --- other working info --- */
int col0, col1, /* cols for line */
row0, row1; /* rows for line */
subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/
/* --- info for under/overbraces, tildes, etc --- */
char brace[16]; /*"{" for over, "}" for under, etc*/
raster *rastrot(), /* rotate { for overbrace, etc */
*rastcpy(); /* may need copy of original */
subraster *arrow_subraster(); /* rightarrow for vec */
subraster *rastack(); /* stack accent atop extra space */
/* -------------------------------------------------------------------------
outer switch() traps accents that may change caller's height,width
-------------------------------------------------------------------------- */
switch ( accent )
{
default:
/* -----------------------------------------------------------------------
inner switch() first allocates fixed-size raster for accents that don't
------------------------------------------------------------------------ */
if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
!= NULL ) /* and if we succeeded... */
switch ( accent ) /* ...draw requested accent in it */
{
/* --- unrecognized request --- */
default: delete_raster(rp); /* unrecognized accent requested */
rp = NULL; break; /* so free raster and signal error */
/* --- bar request --- */
case UNDERBARACCENT:
case BARACCENT:
thickness = height-1; /* adjust thickness */
if ( accent == BARACCENT ) /* bar is above expression */
line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/
else /* underbar is below expression */
line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/
break;
/* --- dot request --- */
case DOTACCENT:
thickness = height-1; /* adjust thickness */
line_raster(rp,0,width/2,0,width/2,thickness); /* centered dot */
break;
/* --- ddot request --- */
case DDOTACCENT:
thickness = height-1; /* adjust thickness */
col0 = max2(width/3-(thickness-1),0); /* one-third of width */
col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/
line_raster(rp,0,col0,0,col0,thickness); /* set a dot at 1st third */
line_raster(rp,0,col1,0,col1,thickness); /* and another at 2nd */
break;
/* --- hat request --- */
case HATACCENT:
thickness = (width<=12? 2 : 3); /* adjust thickness */
line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/
line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
break;
/* --- sqrt request --- */
case SQRTACCENT:
col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */
col0 = (col1+2)/3; /* midpoint col of sqrt */
row0 = (height+1)/2; /* midpoint row of sqrt */
row1 = height-1; /* bottom row of sqrt */
line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
break;
} /* --- end-of-inner-switch(accent) --- */
break; /* break from outer accent switch */
/* --- underbrace, overbrace request --- */
case UNDERBRACE:
case OVERBRACE:
if ( accent == UNDERBRACE ) strncpy(brace,"}",16); /* start with } brace */
if ( accent == OVERBRACE ) strncpy(brace,"{",16); /* start with { brace */
if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
!= NULL ) /* found desired brace */
{ rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
delete_subraster(accsp); } /* and free subraster "envelope" */
break;
/* --- hat request --- */
case HATACCENT:
if ( accent == HATACCENT ) strncpy(brace,"<",16); /* start with < */
if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
!= NULL ) /* found desired brace */
{ rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
delete_subraster(accsp); } /* and free subraster "envelope" */
break;
/* --- vec request --- */
case VECACCENT:
height = 2*(height/2) + 1; /* force height odd */
if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
!= NULL ) /* succeeded */
{ rp = accsp->image; /* "extract" raster with bitmap */
free((void *)accsp); } /* and free subraster "envelope" */
break;
/* --- tilde request --- */
case TILDEACCENT:
if ( (accsp=get_delim("~",-width,CMEX10)) /* width search for tilde */
!= NULL ) /* found desired tilde */
if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
!= NULL ) /* have tilde with space below it */
{ rp = sp->image; /* "extract" raster with bitmap */
free((void *)sp); } /* and free subraster "envelope" */
break;
} /* --- end-of-outer-switch(accent) --- */
/* -------------------------------------------------------------------------
if we constructed accent raster okay, embed it in a subraster and return it
-------------------------------------------------------------------------- */
/* --- if all okay, allocate subraster to contain constructed raster --- */
if ( rp != NULL ) /* accent raster constructed okay */
if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
== NULL ) /* and if we fail to allocate */
delete_raster(rp); /* free now-unneeded raster */
else /* subraster allocated okay */
{ /* --- init subraster parameters, embedding raster in it --- */
sp->type = IMAGERASTER; /* constructed image */
sp->image = rp; /* raster we just constructed */
sp->size = (-1); /* can't set font size here */
sp->baseline = 0; } /* can't set baseline here */
/* --- return subraster containing desired accent to caller --- */
return ( sp ); /* return accent or NULL to caller */
} /* --- end-of-function accent_subraster() --- */
/* ==========================================================================
* Function: arrow_subraster ( width, height, pixsz, drctn, isBig )
* Purpose: Allocate a raster/subraster and draw left/right arrow in it
* --------------------------------------------------------------------------
* Arguments: width (I) int containing number of cols for arrow
* height (I) int containing number of rows for arrow
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* drctn (I) int containing +1 for right arrow,
* or -1 for left, 0 for leftright
* isBig (I) int containing 1/true for \Long arrows,
* or false for \long arrows, i.e.,
* true for ===> or false for --->.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to constructed left/right arrow
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *arrow_subraster ( int width, int height, int pixsz,
int drctn, int isBig )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int irow, midrow=height/2; /* index, midrow is arrowhead apex */
int icol, thickness=(height>15?2:1); /* arrowhead thickness and index */
int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
int ipix, /* raster pixmap[] index */
npix = width*height; /* #pixels malloced in pixmap[] */
/* -------------------------------------------------------------------------
allocate raster/subraster and draw arrow line
-------------------------------------------------------------------------- */
if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */
if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
== NULL ) goto end_of_job; /* and quit if failed */
if ( !isBig ) /* single line */
rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
else
{ int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
/* -------------------------------------------------------------------------
construct arrowhead(s)
-------------------------------------------------------------------------- */
for ( irow=0; irow= 0 ) /* right arrowhead wanted */
for ( icol=0; icol= 0 ) /* bounds check */
if ( pixsz == 1 ) /* have a bitmap */
setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
/* --- left arrowhead (same as right except for ipix calculation) --- */
if ( drctn <= 0 ) /* left arrowhead wanted */
for ( icol=0; icolimage)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
} /* --- end-of-for(irow) --- */
end_of_job:
return ( arrowsp ); /*back to caller with arrow or NULL*/
} /* --- end-of-function arrow_subraster() --- */
/* ==========================================================================
* Function: uparrow_subraster ( width, height, pixsz, drctn, isBig )
* Purpose: Allocate a raster/subraster and draw up/down arrow in it
* --------------------------------------------------------------------------
* Arguments: width (I) int containing number of cols for arrow
* height (I) int containing number of rows for arrow
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* drctn (I) int containing +1 for up arrow,
* or -1 for down, or 0 for updown
* isBig (I) int containing 1/true for \Long arrows,
* or false for \long arrows, i.e.,
* true for ===> or false for --->.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to constructed up/down arrow
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *uparrow_subraster ( int width, int height, int pixsz,
int drctn, int isBig )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int icol, midcol=width/2; /* index, midcol is arrowhead apex */
int irow, thickness=(width>15?2:1); /* arrowhead thickness and index */
int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
int ipix, /* raster pixmap[] index */
npix = width*height; /* #pixels malloced in pixmap[] */
/* -------------------------------------------------------------------------
allocate raster/subraster and draw arrow line
-------------------------------------------------------------------------- */
if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */
if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
== NULL ) goto end_of_job; /* and quit if failed */
if ( !isBig ) /* single line */
rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
else
{ int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
/* -------------------------------------------------------------------------
construct arrowhead(s)
-------------------------------------------------------------------------- */
for ( icol=0; icol= 0 ) /* up arrowhead wanted */
for ( irow=0; irowimage)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
/* --- down arrowhead (same as up except for ipix calculation) --- */
if ( drctn <= 0 ) /* down arrowhead wanted */
for ( irow=0; irow 0 ) /* bounds check */
if ( pixsz == 1 ) /* have a bitmap */
setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
else /* should have a bytemap */
if ( pixsz == 8 ) /* check pixsz for bytemap */
((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
} /* --- end-of-for(irow) --- */
end_of_job:
return ( arrowsp ); /*back to caller with arrow or NULL*/
} /* --- end-of-function uparrow_subraster() --- */
/* ==========================================================================
* Function: rule_raster ( rp, top, left, width, height, type )
* Purpose: Draw a solid or dashed line (or box) in existing raster rp,
* starting at top,left with dimensions width,height.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which rule
* will be drawn
* top (I) int containing row at which top-left corner
* of rule starts (0 is topmost)
* left (I) int containing col at which top-left corner
* of rule starts (0 is leftmost)
* width (I) int containing number of cols for rule
* height (I) int containing number of rows for rule
* type (I) int containing 0 for solid rule,
* 1 for horizontal dashes, 2 for vertical
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if rule drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o Rule line is implicitly "horizontal" or "vertical" depending
* on relative width,height dimensions. It's a box if they're
* more or less comparable.
* ======================================================================= */
/* --- entry point --- */
int rule_raster ( raster *rp, int top, int left,
int width, int height, int type )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow, icol; /* indexes over rp raster */
int ipix, /* raster pixmap[] index */
npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
int isfatal = 0; /* true to abend on out-of-bounds error */
int hdash=1, vdash=2; /* type for horizontal, vertical dashes */
int dashlen=3, spacelen=2, /* #pixels for dash followed by space */
isdraw=1; /* true when drawing dash (init for solid) */
/* -------------------------------------------------------------------------
Check args
-------------------------------------------------------------------------- */
if ( rp == (raster *)NULL ) /* no raster arg supplied */
if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
rp = workingbox->image; /* use workingbox if possible */
else return ( 0 ); /* otherwise signal error to caller */
/* -------------------------------------------------------------------------
Fill line/box
-------------------------------------------------------------------------- */
for ( irow=top; irowwidth + left - 1; /*first pixel preceding icol*/
for ( icol=left; icol= npix ) /* bounds check failed */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else break; /*or just go on to next row*/
else /*ibit is within rp bounds*/
if ( isdraw ) /*and we're drawing this bit*/
if ( rp->pixsz == 1 ) /* have a bitmap */
setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
else /* should have a bytemap */
if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
(rp->pixmap)[ipix] = 255; /* set black byte in line */
} /* --- end-of-for(icol) --- */
} /* --- end-of-for(irow) --- */
end_of_job:
return ( isfatal? (ipixheight-1 is bottom-most)
* col1 (I) int containing col at which
* line will end (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the line will be
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o if row0==row1, a horizontal line is drawn
* between col0 and col1, with row0(==row1) the top row
* and row0+(thickness-1) the bottom row
* o if col0==col1, a vertical bar is drawn
* between row0 and row1, with col0(==col1) the left col
* and col0+(thickness-1) the right col
* o if both the above, you get a square thickness x thickness
* whose top-left corner is row0,col0.
* ======================================================================= */
/* --- entry point --- */
int line_raster ( raster *rp, int row0, int col0,
int row1, int col1, int thickness )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int irow, icol, /* indexes over rp raster */
locol=col0, hicol=col1, /* col limits at irow */
lorow=row0, hirow=row1; /* row limits at icol */
int ipix, /* raster pixmap[] index */
npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
int isfatal = 0; /* true to abend on out-of-bounds error */
int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
xcol, xrow; /* calculated col at irow, or row at icol */
double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
xheight = 1.0;
int line_recurse(), isrecurse=1; /* true to draw line recursively */
/* -------------------------------------------------------------------------
Check args
-------------------------------------------------------------------------- */
if ( rp == (raster *)NULL ) /* no raster arg supplied */
if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
rp = workingbox->image; /* use workingbox if possible */
else return ( 0 ); /* otherwise signal error to caller */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
"\t dy,dx=%3.1lf,%3.1lf, a=%4.3lf, xwidth=%4.3lf\n",
row0,col0, row1,col1, thickness, dy,dx, a, xwidth);
/* --- check for recursive line drawing --- */
if ( isrecurse ) /* drawing lines recursively */
{ line_recurse(rp,(double)row0,(double)col0,
(double)row1,(double)col1,thickness);
return ( 1 ); }
/* --- set params for horizontal line or vertical bar --- */
if ( isline ) /*interpret row as top row*/
row1 = row0 + (thickness-1); /* set bottom row for line */
if ( 0&&isbar ) /*interpret col as left col*/
hicol = col0 + (thickness-1); /* set right col for bar */
/* -------------------------------------------------------------------------
draw line one row at a time
-------------------------------------------------------------------------- */
for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
{
if ( !isbar && !isline ) /* neither vert nor horiz */
{ xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */
locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t irow=%d, xcol=%4.2lf, lo,hicol=%d,%d\n",
irow,xcol,locol,hicol);
ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
if ( ++ipix >= npix ) /* bounds check failed */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else break; /*or just go on to next row*/
else /* turn on pixel in line */
if ( rp->pixsz == 1 ) /* have a pixel bitmap */
setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
else /* should have a bytemap */
if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
(rp->pixmap)[ipix] = 255; /* set black byte in line */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
now _redraw_ line one col at a time to avoid "gaps"
-------------------------------------------------------------------------- */
if ( 1 )
for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
{
if ( !isbar && !isline ) /* neither vert nor horiz */
{ xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */
lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t icol=%d, xrow=%4.2lf, lo,hirow=%d,%d\n",
icol,xrow,lorow,hirow);
ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
if ( irow<0 || irow>=rp->height
|| icol<0 || icol>=rp->width ) /* bounds check */
if ( isfatal ) goto end_of_job; /* abort if error is fatal */
else continue; /*or just go on to next row*/
else
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
end_of_job:
return ( isfatal? (ipixheight-1 is bottom-most)
* col1 (I) double containing col at which
* line will end (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the line will be
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o Recurses, drawing left- and right-halves of line
* until a horizontal or vertical segment is found
* ======================================================================= */
/* --- entry point --- */
int line_recurse ( raster *rp, double row0, double col0,
double row1, double col1, int thickness )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
double delrow = fabs(row1-row0), /* 0 if line horizontal */
delcol = fabs(col1-col0), /* 0 if line vertical */
tolerance = 0.5; /* draw line when it goes to point */
double midrow = 0.5*(row0+row1), /* midpoint row */
midcol = 0.5*(col0+col1); /* midpoint col */
/* -------------------------------------------------------------------------
recurse if either delta > tolerance
-------------------------------------------------------------------------- */
if ( delrow > tolerance /* row hasn't converged */
|| delcol > tolerance ) /* col hasn't converged */
{ line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
return ( 1 ); }
/* -------------------------------------------------------------------------
draw converged point
-------------------------------------------------------------------------- */
setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
return ( 1 );
} /* --- end-of-function line_recurse() --- */
/* ==========================================================================
* Function: circle_raster ( rp, row0, col0, row1, col1,
* thickness, quads )
* Purpose: Draw quad(rant)s of an ellipse in box determined by
* diagonally opposite corner points (row0,col0) and
* (row1,col1), of thickness pixels in existing raster rp.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which an ellipse
* will be drawn
* row0 (I) int containing 1st corner row bounding ellipse
* (0 is topmost)
* col0 (I) int containing 1st corner col bounding ellipse
* (0 is leftmost)
* row1 (I) int containing 2nd corner row bounding ellipse
* (rp->height-1 is bottom-most)
* col1 (I) int containing 2nd corner col bounding ellipse
* (rp->width-1 is rightmost)
* thickness (I) int containing number of pixels/bits
* thick the ellipse arc line will be
* quads (I) char * to null-terminated string containing
* any subset/combination of "1234" specifying
* which quadrant(s) of ellipse to draw.
* NULL ptr draws all four quadrants;
* otherwise 1=upper-right quadrant,
* 2=uper-left, 3=lower-left, 4=lower-right,
* i.e., counterclockwise from 1=positive quad.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if ellipse drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o row0==row1 or col0==col1 are errors
* o using ellipse equation x^2/a^2 + y^2/b^2 = 1
* ======================================================================= */
/* --- entry point --- */
int circle_raster ( raster *rp, int row0, int col0,
int row1, int col1, int thickness, char *quads )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- lower-left and upper-right bounding points (in our coords) --- */
int lorow = min2(row0,row1), /* lower bounding row (top of box) */
locol = min2(col0,col1), /* lower bounding col (left of box)*/
hirow = max2(row0,row1), /* upper bounding row (bot of box) */
hicol = max2(col0,col1); /* upper bounding col (right of box)*/
/* --- a and b ellipse params --- */
int width = hicol-locol+1, /* width of bounding box */
height= hirow-lorow+1, /* height of bounding box */
islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
double a = ((double)width)/2.0, /* x=a when y=0 */
b = ((double)height)/2.0, /* y=b when x=0 */
abmajor = (islandscape? a : b), /* max2(a,b) */
abminor = (islandscape? b : a), /* min2(a,b) */
abmajor2 = abmajor*abmajor, /* abmajor^2 */
abminor2 = abminor*abminor; /* abminor^2 */
/* --- other stuff --- */
int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
iminor=0, nminor=min2(width,height); /* solved index on minor axis */
int irow, icol, /* raster indexes at circumference */
rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/
double midrow= ((double)(row0+row1))/2.0, /* center row */
midcol= ((double)(col0+col1))/2.0; /* center col */
double xy, xy2, /* major axis ellipse coord */
yx2, yx; /* solved minor ellipse coord */
int isokay = 1; /* true if no pixels out-of-bounds */
char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */
/* -------------------------------------------------------------------------
pixel-by-pixel along positive major axis, quit when it goes negative
-------------------------------------------------------------------------- */
if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
width,height,quads);
if ( nmajor < 1 ) isokay = 0; /* problem with input args */
else
{
for ( imajor=(nmajor+1)/2; ; imajor-- )
{
/* --- xy is coord along major axis, yx is "solved" along minor axis --- */
xy = ((double)imajor); /* xy = abmajor ... 0 */
if ( xy < 0.0 ) break; /* negative side symmetrical */
yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */
iminor = iround(yx); /* nearest integer */
/* --- set pixels for each requested quadrant --- */
for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
{
rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
switch ( *qptr ) /* check for quadrant 1,2,3,4 */
{ default: break; /* unrecognized, assume quadrant 1 */
case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
icol = iround(midcol + (double)csign*(islandscape?xy:yx));
icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
imajor,iminor,*qptr,irow,icol);
if ( irow<0 || irow>=rp->height /* row outside raster */
|| icol<0 || icol>=rp->width ) /* col outside raster */
{ isokay = 0; /* signal out-of-bounds pixel */
continue; } /* but still try remaining points */
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(qptr) --- */
} /* --- end-of-for(imajor) --- */
/* ------------------------------------------------------------------------
now do it _again_ along minor axis to avoid "gaps"
------------------------------------------------------------------------- */
if ( 1 && iminor>0 )
for ( iminor=(nminor+1)/2; ; iminor-- )
{
/* --- yx is coord along minor axis, xy is "solved" along major axis --- */
yx = ((double)iminor); /* yx = abminor ... 0 */
if ( yx < 0.0 ) break; /* negative side symmetrical */
xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */
imajor = iround(xy); /* nearest integer */
/* --- set pixels for each requested quadrant --- */
for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
{
rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
switch ( *qptr ) /* check for quadrant 1,2,3,4 */
{ default: break; /* unrecognized, assume quadrant 1 */
case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
icol = iround(midcol + (double)csign*(islandscape?xy:yx));
icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
iminor,imajor,*qptr,irow,icol);
if ( irow<0 || irow>=rp->height /* row outside raster */
|| icol<0 || icol>=rp->width ) /* col outside raster */
{ isokay = 0; /* signal out-of-bounds pixel */
continue; } /* but still try remaining points */
setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
} /* --- end-of-for(qptr) --- */
} /* --- end-of-for(iminor) --- */
} /* --- end-of-if/else(nmajor<1) --- */
return ( isokay );
} /* --- end-of-function circle_raster() --- */
/* ==========================================================================
* Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
* Purpose: Recursively draw bezier from r0,c0 to r1,c1
* (with tangent point rt,ct) in existing raster rp.
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster in which a line
* will be drawn
* r0 (I) double containing row at which
* bezier will start (0 is topmost)
* c0 (I) double containing col at which
* bezier will start (0 is leftmost)
* r1 (I) double containing row at which
* bezier will end (rp->height-1 is bottom-most)
* c1 (I) double containing col at which
* bezier will end (rp->width-1 is rightmost)
* rt (I) double containing row for tangent point
* ct (I) double containing col for tangent point
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if line drawn okay,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o Recurses, drawing left- and right-halves of bezier curve
* until a point is found
* ======================================================================= */
/* --- entry point --- */
int bezier_raster ( raster *rp, double r0, double c0,
double r1, double c1, double rt, double ct )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
double delrow = fabs(r1-r0), /* 0 if same row */
delcol = fabs(c1-c0), /* 0 if same col */
tolerance = 0.5; /* draw curve when it goes to point*/
double midrow = 0.5*(r0+r1), /* midpoint row */
midcol = 0.5*(c0+c1); /* midpoint col */
int irow=0, icol=0; /* point to be drawn */
int status = 1; /* return status */
/* -------------------------------------------------------------------------
recurse if either delta > tolerance
-------------------------------------------------------------------------- */
if ( delrow > tolerance /* row hasn't converged */
|| delcol > tolerance ) /* col hasn't converged */
{ bezier_raster(rp, r0,c0, /* left half */
0.5*(rt+midrow), 0.5*(ct+midcol),
0.5*(r0+rt), 0.5*(c0+ct) );
bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
r1,c1,
0.5*(r1+rt), 0.5*(c1+ct) );
return ( 1 ); }
/* -------------------------------------------------------------------------
draw converged point
-------------------------------------------------------------------------- */
/* --- get integer point --- */
irow = iround(midrow); /* row pixel coord */
icol = iround(midcol); /* col pixel coord */
/* --- bounds check --- */
if ( irow>=0 && irowheight /* row in bounds */
&& icol>=0 && icolwidth ) /* col in bounds */
setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/
else status = 0; /* bad status if out-of-bounds */
return ( status );
} /* --- end-of-function bezier_raster() --- */
/* ==========================================================================
* Function: border_raster ( rp, ntop, nbot, isline, isfree )
* Purpose: Allocate a new raster containing a copy of input rp,
* along with ntop extra rows at top and nbot at bottom,
* and whose width is either adjusted correspondingly,
* or is automatically enlarged to a multiple of 8
* with original bitmap centered
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster on which a border
* is to be placed
* ntop (I) int containing number extra rows at top.
* if negative, abs(ntop) used, and same
* number of extra cols added at left.
* nbot (I) int containing number extra rows at bottom.
* if negative, abs(nbot) used, and same
* number of extra cols added at right.
* isline (I) int containing 0 to leave border pixels clear
* or >0 to draw a line around border of width
* isline.
* isfree (I) int containing true to free rp before return
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to bordered raster,
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
raster *border_raster ( raster *rp, int ntop, int nbot,
int isline, int isfree )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/
int rastput(); /* overlay rp in new bordered raster */
int width = (rp==NULL?0:rp->width), /* height of raster */
height = (rp==NULL?0:rp->height), /* width of raster */
istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
leftmargin = 0; /* adjust width to whole number of bytes */
int delete_raster(); /* to free input rp if isdelete is true */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
if ( rp == NULL ) goto end_of_job; /* no input raster provided */
/* --- check for negative args --- */
if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
/* --- adjust height for ntop and nbot margins --- */
height += (ntop+nbot); /* adjust height for margins */
/* --- adjust width for left and right margins --- */
if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/
{ /* --- adjust width (and leftmargin) as requested by caller -- */
if ( istopneg ) { width += ntop; leftmargin = ntop; }
if ( isbotneg ) width += nbot; }
else
{ /* --- or adjust width (and leftmargin) to whole number of bytes --- */
leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
width += leftmargin; /* width now multiple of 8 */
leftmargin /= 2; } /* center original raster */
/* -------------------------------------------------------------------------
allocate bordered raster, and embed rp within it
-------------------------------------------------------------------------- */
/* --- allocate bordered raster --- */
if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/
== (raster *)NULL ) goto end_of_job; /* and quit if failed */
/* --- embed rp in it --- */
rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */
/* -------------------------------------------------------------------------
draw border if requested
-------------------------------------------------------------------------- */
if ( isline )
{ int irow, icol, nthick=isline; /*height,width index, line thickness*/
/* --- draw left- and right-borders --- */
for ( irow=0; irowwidth)
-------------------------------------------------------------------------- */
while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
{
/* --- set hicol for this pass (locol set above) --- */
hicol += display_width; /* show as much as display allows */
if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
scan_width = hicol-locol+1; /* #chars in this scan */
if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
/* ------------------------------------------------------------------------
display all scan lines for this local...hicol segment range
------------------------------------------------------------------------ */
for ( irow=0; irowheight; irow++ ) /* all scan lines for col range */
{
/* --- allocations and declarations --- */
int ipix, /* pixmap[] index for this scan */
lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
/* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
for ( ipix=0; ipixpixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.');
else /* should have a bytemap */
if ( rp->pixsz == 8 ) /* double-check pixsz for bytemap */
{ int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/
ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
/* --- display completed scan line --- */
fprintf(fp,"%.*s\n",scan_width,scanline);
} /* --- end-of-for(irow) --- */
} /* --- end-of-while(hicolwidth) --- */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
return ( 1 );
} /* --- end-of-function type_raster() --- */
/* ==========================================================================
* Function: type_bytemap ( bp, grayscale, width, height, fp )
* Purpose: Emit an ascii dump representing bp, on fp.
* --------------------------------------------------------------------------
* Arguments: bp (I) intbyte * to bytemap for which an
* ascii dump is to be constructed.
* grayscale (I) int containing #gray shades, 256 for 8-bit
* width (I) int containing #cols in bytemap
* height (I) int containing #rows in bytemap
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int type_bytemap ( intbyte *bp, int grayscale,
int width, int height, FILE *fp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
static int display_width = 72; /* max columns for display */
int byte_width = 3, /* cols to display byte (ff+space) */
maxbyte = 0; /* if maxbyte<16, set byte_width=2 */
int white_byte = 0, /* show dots for white_byte's */
black_byte = grayscale-1; /* show stars for black_byte's */
char scanline[133]; /* ascii image for one scan line */
int scan_width, /* #chars in scan (<=display_width)*/
scan_cols; /* #cols in scan (hicol-locol+1) */
int ibyte, /* bp[] index */
irow, locol,hicol=(-1); /* height index, width indexes */
/* --------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- see if we can get away with byte_width=1 --- */
for ( ibyte=0; ibytewidth)
-------------------------------------------------------------------------- */
while ( (locol=hicol+1) < width ) /*start where prev segment left off*/
{
/* --- set hicol for this pass (locol set above) --- */
hicol += display_width/byte_width; /* show as much as display allows */
if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
scan_cols = hicol-locol+1; /* #cols in this scan */
scan_width = byte_width*scan_cols; /* #chars in this scan */
if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
/* ------------------------------------------------------------------------
display all scan lines for this local...hicol segment range
------------------------------------------------------------------------ */
for ( irow=0; irow 1 ) /* don't blank out single char */
scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
if ( byteval != white_byte /* format bytes that are non-white */
&& byteval != black_byte ) /* and that are non-black */
sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
/* --- display completed scan line --- */
fprintf(fp,"%.*s\n",scan_width,scanline);
} /* --- end-of-for(irow) --- */
} /* --- end-of-while(hicolbitmap image
-------------------------------------------------------------------------- */
/* --- first redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
title,rp->width, title,rp->height );
fprintf( fp, "static char %s_bits[] = {\n", title );
hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */
fprintf (fp,"};\n"); /* ending with "};" for C array */
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
return ( 1 );
} /* --- end-of-function xbitmap_raster() --- */
/* ==========================================================================
* Function: cstruct_chardef ( cp, fp, col1 )
* Purpose: Emit a C struct of cp on fp, starting in col1.
* --------------------------------------------------------------------------
* Arguments: cp (I) ptr to chardef struct for which
* a C struct is to be generated.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char field[64]; /* field within output line */
int cstruct_raster(), /* emit a raster */
emit_string(); /* emit a string and comment */
/* -------------------------------------------------------------------------
emit charnum, location, name / hirow, hicol, lorow, locol
-------------------------------------------------------------------------- */
/* --- charnum, location, name --- */
sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
emit_string ( fp, col1, field, "character number, location");
/* --- toprow, topleftcol, botrow, botleftcol --- */
sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
/* -------------------------------------------------------------------------
emit raster and chardef's closing brace, and then return to caller
-------------------------------------------------------------------------- */
cstruct_raster(&cp->image,fp,col1+4); /* emit raster */
emit_string ( fp, 0, " }", NULL); /* emit closing brace */
return ( 1 ); /* back to caller with 1=okay, 0=failed */
} /* --- end-of-function cstruct_chardef() --- */
/* ==========================================================================
* Function: cstruct_raster ( rp, fp, col1 )
* Purpose: Emit a C struct of rp on fp, starting in col1.
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct for which
* a C struct is to be generated.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int cstruct_raster ( raster *rp, FILE *fp, int col1 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char field[64]; /* field within output line */
int hex_bitmap(); /* to emit raster bitmap */
int emit_string(); /* emit a string and comment */
/* -------------------------------------------------------------------------
emit width and height
-------------------------------------------------------------------------- */
sprintf(field,"{ %2d, %3d,%2d,\n", /* format width,height,pixsz */
rp->width,rp->height,rp->pixsz);
emit_string ( fp, col1, field, "width x height, pixsz, bitmap...");
/* -------------------------------------------------------------------------
emit bitmap and closing brace, and return to caller
-------------------------------------------------------------------------- */
hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */
emit_string ( fp, 0, " }", NULL); /* emit closing brace */
return ( 1 ); /* back to caller with 1=okay, 0=failed */
} /* --- end-of-function cstruct_raster() --- */
/* ==========================================================================
* Function: hex_bitmap ( rp, fp, col1, isstr )
* Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1.
* If isstr (is string) is true, the dump is of the form
* "\x01\x02\x03\x04\x05..."
* Otherwise, if isstr is false, the dump is of the form
* 0x01,0x02,0x03,0x04,0x05...
* --------------------------------------------------------------------------
* Arguments: rp (I) ptr to raster struct for which
* a hex dump is to be constructed.
* fp (I) File ptr to output device (defaults to
* stdout if passed as NULL).
* col1 (I) int containing 0...65; output lines
* are preceded by col1 blanks.
* isstr (I) int specifying dump format as described above
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if completed successfully,
* or 0 otherwise (for any error).
* --------------------------------------------------------------------------
* Notes:
* ======================================================================= */
/* --- entry point --- */
int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */
char stub[64]=" ";/* col1 leading blanks */
int linewidth = 64, /* (roughly) rightmost column */
colwidth = (isstr? 4:5); /* #cols required for each byte */
int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
/* --------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- emit initial stub if wanted --- */
if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
/* --------------------------------------------------------------------------
emit hex dump of rp->bitmap image
-------------------------------------------------------------------------- */
if ( isstr ) fprintf(fp,"\""); /* opening " before first line */
for ( ibyte=0; ibytepixmap)[ibyte]); /*print byte as hex char*/
else /* comma-separated format wanted */
fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/
/* --- add a separator and newline, etc, as necessary --- */
if ( ibyte < nbytes-1) /* not the last byte yet */
{
if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
if ( (ibyte+1)%ncols==0 ) /* need new line after every ncols */
if ( !isstr ) /* for hex numbers format ... */
fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
else /* for string format... */
fprintf(fp,"\"\n%.*s\"",col1,stub); /* ...need closing, opening "s */
} /* --- end-of-if(ibyte 6 ) /* can fit all or part of comment */
sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
'*', fieldlen-6,comment, '*');
col1 = linelen; } /* indicate line filled */
/* --- line completed --- */
line[col1] = '\000'; /* null-terminate completed line */
/* -------------------------------------------------------------------------
emit line, then back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
/* --- first redirect null fp --- */
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- emit line (and optional newline) --- */
fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */
if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/
return ( 1 );
} /* --- end-of-function emit_string() --- */
/* ==========================================================================
* Function: get_symdef ( symbol )
* Purpose: returns mathchardef struct for symbol
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing symbol
* whose corresponding mathchardef is wanted
* --------------------------------------------------------------------------
* Returns: ( mathchardef * ) pointer to struct defining symbol,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o Input symbol need only contain a leading substring to match,
* e.g., \gam passed in symbol will match \gamma in the table.
* If the table contains two or more possible matches,
* the shortest is returned, e.g., input \e will return with
* data for \eta rather than \epsilon. To get \epsilon,
* you must pass a leading substring long enough to eliminate
* shorter table matches, i.e., in this case \ep
* ======================================================================= */
/* --- entry point --- */
mathchardef *get_symdef ( char *symbol )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
mathchardef *symdefs = symtable; /* table of mathchardefs */
int idef = 0, /* symdefs[] index */
bestdef = (-9999); /*index of shortest matching symdef*/
int symlen = strlen(symbol), /* length of input symbol */
deflen, minlen=9999; /*length of shortest matching symdef*/
int alphasym = (symlen==1 && isalpha(*symbol)); /* symbol is an alpha */
/* -------------------------------------------------------------------------
search symdefs[] in order for first occurrence of symbol
-------------------------------------------------------------------------- */
for ( idef=0; ;idef++ ) /* until trailer record found */
if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
else /* check against caller's symbol */
if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
if ( !istext || !alphasym /* mathmode or (textmode&&!alpha) */
|| (istext==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
|| (istext==2 && symdefs[idef].family==CMMI10) ) /*textmode && it text*/
if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
{ bestdef = idef; /* save index of new best match */
if ( (minlen = deflen) /* and save its len for next test */
== symlen ) break; } /*perfect match, so return with it*/
return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/
} /* --- end-of-function get_symdef() --- */
/* ==========================================================================
* Function: get_chardef ( symdef, size )
* Purpose: returns chardef ptr containing data for symdef at given size
* --------------------------------------------------------------------------
* Arguments: symdef (I) mathchardef * corresponding to symbol
* whose corresponding chardef is wanted
* size (I) int containing 0-4 for desired size
* --------------------------------------------------------------------------
* Returns: ( chardef * ) pointer to struct defining symbol at size,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o if size unavailable, the next-closer-to-normalsize
* is returned instead.
* ======================================================================= */
/* --- entry point --- */
chardef *get_chardef ( mathchardef *symdef, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
fontfamily *fonts = fonttable; /* table of font families */
chardef **fontdef, /*tables for desired font, by size*/
*gfdata = (chardef *)NULL; /* chardef for symdef,size */
int ifont; /* fonts[] index */
int family, charnum; /* indexes retrieved from symdef */
int sizeinc = 0; /*+1 or -1 to get closer to normal*/
int isBig = 0; /*true if symbol's 1st char is upper*/
char *symptr = NULL; /* look for 1st alpha of symbol */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- check symdef --- */
if ( symdef == NULL ) return ( NULL ); /* get_symdef() probably failed */
/* --- get local copy of indexes from symdef --- */
family = symdef->family; /* font family containing symbol */
charnum = symdef->charnum; /* char# of symbol within font */
/* --- check requested size, and set size increment --- */
if ( size < 0 ) size = 0; /* size was definitely too small */
if ( size > LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */
if ( size < NORMALSIZE ) sizeinc = (+1); /*use next larger if size too small*/
if ( size > NORMALSIZE ) sizeinc = (-1); /*or next smaller if size too large*/
/* --- check for really big symbol (1st char of symbol name uppercase) --- */
for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
if ( isalpha(*symptr) ) /* found leading alpha char */
{ isBig = isupper(*symptr); /* is 1st char of name uppercase? */
break; } /* don't check beyond 1st char */
/* -------------------------------------------------------------------------
find font family in table of fonts[]
-------------------------------------------------------------------------- */
/* --- look up font family --- */
for ( ifont=0; ;ifont++ ) /* until trailer record found */
if ( fonts[ifont].family < 0 ) return ( NULL ); /* error, no such family */
else if ( fonts[ifont].family == family ) break; /* found font family */
/* --- get local copy of table for this family by size --- */
fontdef = fonts[ifont].fontdef; /* font by size */
/* -------------------------------------------------------------------------
get font in desired size, or closest available size, and return symbol
-------------------------------------------------------------------------- */
/* --- get font in desired size --- */
while ( 1 ) /* find size or closest available */
if ( fontdef[size] != NULL ) break; /* found available size */
else /* adjust size closer to normal */
if ( size == NORMALSIZE ) return ( NULL ); /* already normal, no sizes */
else size += sizeinc; /* see if adjusted size available */
/* --- ptr to chardef struct --- */
gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/
/* -------------------------------------------------------------------------
kludge to tweak CMEX10 (which appears to have incorrect descenders)
-------------------------------------------------------------------------- */
if ( family == CMEX10 ) /* cmex10 needs tweak */
{ int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
gfdata->botrow = (isBig? (-height/3) : (-height/4));
gfdata->toprow = gfdata->botrow + gfdata->image.height; }
/* -------------------------------------------------------------------------
return subraster containing chardef data for symbol in requested size
-------------------------------------------------------------------------- */
return ( gfdata ); /*ptr to chardef for symbol in size*/
} /* --- end-of-function get_chardef() --- */
/* ==========================================================================
* Function: get_charsubraster ( symdef, size )
* Purpose: returns new subraster ptr containing
* data for symdef at given size
* --------------------------------------------------------------------------
* Arguments: symdef (I) mathchardef * corresponding to symbol
* whose corresponding chardef is wanted
* size (I) int containing 0-4 for desired size
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to struct defining symbol at size,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o just wraps a subraster envelope around get_chardef()
* ======================================================================= */
/* --- entry point --- */
subraster *get_charsubraster ( mathchardef *symdef, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
int get_baseline(); /* baseline of gfdata */
subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
/* -------------------------------------------------------------------------
look up chardef for symdef at size, and embed data (gfdata) in subraster
-------------------------------------------------------------------------- */
if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */
!= NULL ) /* and check that we found it */
if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
!= NULL ) /* and check that we succeeded */
{
sp->type = CHARASTER; /* static character raster */
sp->symdef = symdef; /* replace NULL with caller's arg */
sp->size = size; /*replace default with caller's size*/
sp->baseline = get_baseline(gfdata); /* get baseline of character */
sp->image = &(gfdata->image); /* store ptr to its bitmap */
} /* --- end-of-if(sp!=NULL) --- */
return ( sp ); /* back to caller */
} /* --- end-of-function get_charsubraster() --- */
/* ==========================================================================
* Function: get_baseline ( gfdata )
* Purpose: returns baseline for a chardef struct
* --------------------------------------------------------------------------
* Arguments: gfdata (I) chardef * containing chardef for symbol
* whose baseline is wanted
* --------------------------------------------------------------------------
* Returns: ( int ) baseline for symdef,
* or -1 for any error
* --------------------------------------------------------------------------
* Notes: o Unlike TeX, the top-left corners of our rasters are (0,0),
* with (row,col) increasing as you move down and right.
* Baselines are calculated with respect to this scheme,
* so 0 would mean the very top row is on the baseline
* and everything else descends below the baseline.
* ======================================================================= */
/* --- entry point --- */
int get_baseline ( chardef *gfdata )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int toprow = gfdata->toprow, /*TeX top row from .gf file info*/
botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/
height = gfdata->image.height; /* #rows comprising symbol */
/* -------------------------------------------------------------------------
give caller baseline
-------------------------------------------------------------------------- */
return ( (height-1) + botrow ); /* note: descenders have botrow<0 */
} /* --- end-of-function get_baseline() --- */
/* ==========================================================================
* Function: get_delim ( char *symbol, int height, int family )
* Purpose: returns subraster corresponding to the samllest
* character containing symbol, but at least as large as height,
* and in caller's family (if specified).
* If no symbol character as large as height is available,
* then the largest availabale character is returned instead.
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing (substring of) desired
* symbol, e.g., if symbol="(", then any
* mathchardef like "(" or "\\(", etc, match.
* height (I) int containing minimum acceptable height
* for returned character
* family (I) int containing -1 to consider all families,
* or, e.g., CMEX10 for only that family
* --------------------------------------------------------------------------
* Returns: ( subraster * ) best matching character available,
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o If height is passed as negative, its absolute value is used
* but the best-fit width is searched for (rather than height)
* ======================================================================= */
/* --- entry point --- */
subraster *get_delim ( char *symbol, int height, int family )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
mathchardef *symdefs = symtable; /* table of mathchardefs */
subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
subraster *make_delim(); /* construct delim if can't find it*/
chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */
char lcsymbol[99], *symptr; /* lowercase symbol for comparison */
int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
deflen = 0; /* length of symdef (aka lcsymbol) */
int idef = 0, /* symdefs[] index */
bestdef = (-9999), /* index of best fit symdef */
bigdef = (-9999); /*index of biggest (in case no best)*/
int size = 0, /* size index 0...LARGESTSIZE */
bestsize = (-9999), /* index of best fit size */
bigsize = (-9999); /*index of biggest (in case no best)*/
int defheight, bestheight=9999, /* height of best fit symdef */
bigheight = (-9999); /*height of biggest(in case no best)*/
int iswidth = 0; /* true if best-fit width desired */
/* -------------------------------------------------------------------------
determine if searching height or width, and search symdefs[] for best-fit
-------------------------------------------------------------------------- */
/* --- determine whether searching for best-fit height or width --- */
if ( height < 0 ) /* negative signals width search */
{ height = (-height); /* flip "height" positive */
iswidth = 1; } /* set flag for width search */
/* --- search symdefs[] for best-fit height (or width) --- */
for ( idef=0; ;idef++ ) /* until trailer record found */
if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
else /* check against caller's symbol */
if ( family<0 || symdefs[idef].family == family ) /*if in caller's family*/
{
strncpy(lcsymbol,symdefs[idef].symbol,99); /*local copy of symdefs[] symbol*/
if ( 0 ) /* don't ignore case */
for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
if ( (symptr=strstr(lcsymbol,symbol)) != NULL ) /*caller's sym embedded*/
if ( symptr == lcsymbol /* caller's sym is a prefix */
|| symptr == lcsymbol+deflen-symlen ) /* or a suffix */
for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
{ defheight = gfdata->image.height; /* height of this character */
if ( iswidth ) /* width search wanted instead... */
defheight = gfdata->image.width; /* ...so substitute width */
if ( defheight>=height && defheight= bigheight ) /* new biggest character */
{ bigdef=idef; bigsize=size; /* save indexes of biggest */
bigheight = defheight; } /* and save new big height */
} /* --- end-of-if(gfdata!=NULL) --- */
} /* --- end-of-if(family) --- */
/* -------------------------------------------------------------------------
construct subraster for best fit character, and return it to caller
-------------------------------------------------------------------------- */
if ( bestdef >= 0 ) /* found a best fit for caller */
sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
if ( sp==NULL && height-bigheight>5 ) /* try to construct delim */
sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
return ( sp );
} /* --- end-of-function get_delim() --- */
/* ==========================================================================
* Function: make_delim ( char *symbol, int height )
* Purpose: constructs subraster corresponding to symbol
* exactly as large as height,
* --------------------------------------------------------------------------
* Arguments: symbol (I) char * containing, e.g., if symbol="("
* for desired delimiter
* height (I) int containing height
* for returned character
* --------------------------------------------------------------------------
* Returns: ( subraster * ) constructed delimiter
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o If height is passed as negative, its absolute value is used
* and interpreted as width (rather than height)
* ======================================================================= */
/* --- entry point --- */
subraster *make_delim ( char *symbol, int height )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *sp = (subraster *)NULL, /* subraster returned to caller */
*new_subraster(); /* allocate subraster */
raster *rasp = (raster *)NULL; /* sp->image */
int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
int pixsz = 1; /* pixels are one bit each */
int thickness = 1; /* drawn lines are one pixel thick */
int aspectratio = 8; /* default height/width for parens */
int iswidth = 0, /*true if width specified by height*/
width = height; /* #pixels width (e.g., of ellipse)*/
char *lp=NULL, *rp=NULL, *strchr(), /* check symbol for left or right */
*lp2=NULL, *rp2=NULL; /* synonym for lp,rp */
int circle_raster(), /* ellipse for ()'s in sp->image */
rule_rsater(), /* horizontal or vertical lines */
line_raster(); /* line between two points */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- determine whether constructing height or width --- */
if ( height < 0 ) /* negative "height" signals width */
{ width = height = (-height); /* flip height positive */
iswidth = 1; } /* set flag for width */
/* --- set default width (or height) accordingly --- */
if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
else width = (height+(aspectratio+1)/2)/aspectratio;
if ( strchr(symbol,'=') != NULL ) /* left or right || bracket wanted */
width = max2(width,5); /* need space between two |'s */
/* --- allocate and initialize subraster for constructed delimiter --- */
if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize delimiter subraster parameters --- */
sp->type = IMAGERASTER; /* image */
sp->symdef = NULL; /* not applicable for image */
sp->baseline = height/2 + 2; /* is a little above center good? */
sp->size = NORMALSIZE; /* size (probably unneeded) */
rasp = sp->image; /* pointer to image in subraster */
/* -------------------------------------------------------------------------
( ) parens
-------------------------------------------------------------------------- */
if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
|| (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
{
circle_raster ( rasp, /* embedded raster image */
0, 0, /* row0,col0 are upper-left corner */
height-1, width-1, /* row1,col1 are lower-right */
thickness, /* line thickness is 1 pixel */
(rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-() paren wanted) --- */
/* -------------------------------------------------------------------------
[ ] brackets
-------------------------------------------------------------------------- */
else
if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
|| (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */
{
/* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
thickness = 2; /* set lines two pixels thick */
rule_raster(rasp, 0,0, width,thickness, 0); /* top horizontal bar */
rule_raster(rasp, height-thickness,0, width,thickness, 0); /* bottom */
if ( lp != NULL ) /* left [ bracket wanted */
rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
if ( rp != NULL ) /* right ] bracket wanted */
rule_raster(rasp, 0,width-thickness, thickness,height, 0); /* right */
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-[] bracket wanted) --- */
/* -------------------------------------------------------------------------
< > brackets
-------------------------------------------------------------------------- */
else
if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
|| (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
{
/* --- line_raster ( rasp, row0, col0, row1, col1, thickness ) --- */
thickness = 1; /* set line pixel thickness */
if ( lp != NULL ) /* left < bracket wanted */
{ line_raster(rasp,height/2,0,0,width-1,thickness);
line_raster(rasp,height/2,0,height-1,width-1,thickness); }
if ( rp != NULL ) /* right > bracket wanted */
{ line_raster(rasp,height/2,width-1,0,0,thickness);
line_raster(rasp,height/2,width-1,height-1,0,thickness); }
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-<> bracket wanted) --- */
/* -------------------------------------------------------------------------
\- for | | brackets or \= for || || brackets
-------------------------------------------------------------------------- */
else
if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */
|| (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */
|| (rp=strchr(symbol,'=')) != NULL ) /* left or right || bracket wanted */
{
/* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
int midcol = width/2; /* middle col, left of mid if even */
if ( lp != NULL /* left or right | bracket wanted */
|| lp2 != NULL ) /* ditto for synomym */
{ thickness = 1; /* set | two pixels thick */
rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
if ( rp != NULL ) /* right ] bracket wanted */
{ thickness = 1; /* each | of || one pixel thick */
rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-[] bracket wanted) --- */
/* -------------------------------------------------------------------------
back to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( !isokay ) /* don't have requested delimiter */
{ delete_subraster(sp); /* so free unneeded structure */
sp = NULL; } /* and signal error to caller */
return ( sp ); /*back to caller with delim or NULL*/
} /* --- end-of-function make_delim() --- */
/* ==========================================================================
* Function: texchar ( expression, chartoken )
* Purpose: scans expression, returning either its first character,
* or the next \sequence if that first char is \,
* and a pointer to the first expression char past that.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* chartoken (O) char * to null-terminated string returning
* either the first (non-whitespace) character
* of expression if that char isn't \, or else
* the \ and everything following it up to
* the next non-alphabetic character (but at
* least one char following the \ even if
* it's non-alpha)
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned chartoken,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o Does *not* skip leading whitespace, but simply
* returns any whitespace character as the next character.
* o \left( is returned as \(, and \right) is returned as \).
* And similarly for [] and <> and {} delimiters.
* Actually, and non-alpha character, say ?, following
* either \left or \right is returned as \? .
* ======================================================================= */
/* --- entry point --- */
char *texchar ( char *expression, char *chartoken )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int esclen = 0; /*length of escape sequence*/
char *chartoken0 = chartoken; /* save start of chartoken */
int iprefix = 0; /* prefix index */
static char *prefixes[] = /*e.g., \big followed by ( */
{ "\\big", "\\Big", "\\bigg", "\\Bigg", "\\left", "\\right", NULL };
/* -------------------------------------------------------------------------
just return the next char if it's not \
-------------------------------------------------------------------------- */
/* --- error check for end-of-string --- */
*chartoken = '\000'; /* init in case of error */
if ( expression == NULL ) return(NULL); /* nothing to scan */
if ( *expression == '\000' ) return(NULL); /* nothing to scan */
/* --- always returning first character (either \ or some other char) --- */
*chartoken++ = *expression++; /* here's first character */
/* --- if first char isn't \, then just return it to caller --- */
if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */
{ *chartoken = '\000'; /* add a null terminator */
goto end_of_job; } /* ptr past returned char */
if ( *expression == '\000' ) /* \ is very last char */
{ *chartoken0 = '\000'; /* flush bad \ */
return(NULL); } /* and signal end-of-job */
/* -------------------------------------------------------------------------
we have an escape sequence, so return all alpha chars following \
-------------------------------------------------------------------------- */
/* --- accumulate chars until first non-alpha char found --- */
for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */
*chartoken++ = *expression++; /*copy alpha char, bump ptrs*/
/* --- if we have a prefix, append next (non-alpha) char, e.g., \big( --- */
*chartoken = '\000'; /* set null for comparison */
for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
if ( strcmp(chartoken0,prefixes[iprefix]) == 0 ) /* have an exact match */
{ *chartoken++ = *expression++; /* so append next char */
esclen++; /* bump escape length */
break; } /* stop checking prefixes */
/* --- every \ must be followed by at least one char, e.g., \[ --- */
if ( esclen < 1 ) /* \ followed by non-alpha */
*chartoken++ = *expression++; /*copy non-alpha, bump ptrs*/
else /* normal alpha \sequence */
{
/* --- respect spaces in text mode, except first space after \escape --- */
if ( istext>0 ) /* in \rm or \it text mode */
if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
expression++; /* so flush delim */
}
*chartoken = '\000'; /* null-terminate token */
/* -------------------------------------------------------------------------
Translate \leftX or \rightX to just \X for X in ()[]<>{}
-------------------------------------------------------------------------- */
if ( 0 ) /* --- not yet implemented properly --- */
if ( strcmp(chartoken0,"\\left") == 0 /* have \left */
|| strcmp(chartoken0,"\\right") == 0 ) /* or have \right */
{ skipwhite(expression); /* skip leading whitespace */
chartoken0[1] = *expression++; /*and return only next char*/
chartoken0[2] = '\000'; } /* null-terminated */
/* --- back to caller --- */
end_of_job:
if ( msgfp!=NULL && msglevel>=999 )
fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken0);
return ( expression ); /*ptr to 1st non-alpha char*/
} /* --- end-of-function texchar() --- */
/* ==========================================================================
* Function: texsubexpr (expression,subexpr,left,right,isescape,isdelim)
* Purpose: scans expression, returning everything between a balanced
* left{...right} subexpression if the first non-whitespace
* char of expression is an (escaped or unescaped) left{,
* or just the next texchar() otherwise,
* and a pointer to the first expression char past that.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* subexpr (O) char * to null-terminated string returning
* either everything between a balanced {...}
* subexpression if the first char is {,
* or the next texchar() otherwise.
* left (I) char * specifying allowable left delimiters
* that begin subexpression, e.g., "{[(<"
* right (I) char * specifying matching right delimiters
* in the same order as left, e.g., "}])>"
* isescape (I) int controlling whether escaped and/or
* unescaped left,right are matched;
* see isbrace() comments below for details.
* isdelim (I) int containing true (non-zero) to return
* the leading left and trailing right delims
* (if any were found) along with subexpr,
* or containing false=0 to return subexpr
* without its delimiters
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned subexpr (see Notes),
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o If subexpr is of the form left{...right},
* the outer {}'s are returned as part of subexpr
* if isdelim is true; if isdelim is false the {}'s aren't
* returned. In either case the returned pointer is
* *always* bumped past the closing right}, even if
* that closing right} isn't returned in subexpr.
* o If subexpr is not of the form left{...right},
* the returned pointer is on the character immediately
* following the last character returned in subexpr
* o \. acts as LaTeX \right. and matches any \left(
* And it also acts as a LaTeX \left. and matches any \right)
* ======================================================================= */
/* --- entry point --- */
char *texsubexpr ( char *expression, char *subexpr,
char *left, char *right, int isescape, int isdelim )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texchar(); /*next char (or \sequence) from expression*/
char *leftptr, leftdelim[32] = "(\000", /* left( found in expression */
rightdelim[32] = ")\000"; /* and matching right) */
int gotescape = 0, /* true if leading char of expression is \ */
prevescape = 0; /* while parsing, true if preceding char \ */
int isbrace(); /* check for left,right braces */
int isleftdot = 0; /* true if left brace is a \. */
int nestlevel = 1; /* current # of nested braces */
/* -------------------------------------------------------------------------
skip leading whitespace and just return the next char if it's not {
-------------------------------------------------------------------------- */
/* --- skip leading whitespace and error check for end-of-string --- */
*subexpr = '\000'; /* init in case of error */
if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/
skipwhite(expression); /* leading whitespace gone */
if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
/* --- check for escape --- */
if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
gotescape = 1; /* so set flag accordingly */
/* --- if first char isn't left{ or script, just return it to caller --- */
if ( !isbrace(expression,left,isescape) ) /* not a left{ */
if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
return ( texchar(expression,subexpr) ); /* next char to caller */
else /* --- kludge for super/subscripts to accommodate texscripts() --- */
{ *subexpr++ = *expression; /* signal script */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression ); } /* leave script in stream */
/* --- extract left and find matching right delimiter --- */
*leftdelim = *(expression+gotescape); /* the left( in expression */
if ( gotescape && *leftdelim == '.' ) /* we have a left \. */
{ isleftdot = 1; /* so just set flag */
*leftdelim = '\000'; } /* and reset leftdelim */
else /* find matching \right */
if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
*rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */
else /* can't happen -- pgm bug */
return ( NULL ); /*just signal eoj to caller*/
/* -------------------------------------------------------------------------
accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
-------------------------------------------------------------------------- */
/* --- first initialize by bumping past left{ or \{ --- */
if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/
else expression++; /* always bump past left{ */
if ( gotescape ) /*need to bump another char*/
if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
else expression++; /* else just bump past it */
/* --- search for matching right} --- */
while ( 1 ) /*until balanced right} */
{
/* --- error check for end-of-string --- */
if ( *expression == '\000' ) /* premature end-of-string */
{ if ( isdelim ) /* generate fake right */
if ( gotescape ) /* need escaped right */
{ *subexpr++ = '\\'; /* set escape char */
*subexpr++ = '.'; } /* and fake \right. */
else /* escape not wanted */
*subexpr++ = *rightdelim; /* so fake actual right */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression ); } /* back with final token */
/* --- check preceding char for escape --- */
if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */
prevescape = 1-prevescape; /* so flip escape flag */
else prevescape = 0; /* or turn flag off */
/* --- check for { and } (un/escaped as per leading left) --- */
if ( gotescape == prevescape ) /* escaped iff leading is */
{ /* --- check for (closing) right delim and see if we're done --- */
if ( isthischar(*expression,rightdelim) /* found a right} */
|| (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
|| (prevescape && isthischar(*expression,".")) ) /*or found \right. */
if ( --nestlevel < 1 ) /*\right balances 1st \left*/
{ if ( isdelim ) /*caller wants } in subexpr*/
*subexpr++ = *expression; /* so end subexpr with } */
else /*check for \ before right}*/
if ( prevescape ) /* have unwanted \ */
*(subexpr-1) = '\000'; /* so replace it with null */
*subexpr = '\000'; /* null-terminate subexpr */
return ( expression+1 ); } /* back with char after } */
/* --- check for (another) left{ --- */
if ( isthischar(*expression,leftdelim) /* found another left{ */
|| (isleftdot && isthischar(*expression,left)) ) /* any left{ */
nestlevel++;
} /* --- end-of-if(gotescape==prevescape) --- */
/* --- not done, so copy char to subexpr and continue with next char --- */
*subexpr++ = *expression++; /* copy char and bump ptrs */
} /* --- end-of-while(1) --- */
} /* --- end-of-function texsubexpr() --- */
/* ==========================================================================
* Function: texscripts ( expression, subscript, superscript, which )
* Purpose: scans expression, returning subscript and/or superscript
* if expression is of the form _x^y or ^{x}_{y},
* or any (valid LaTeX) permutation of the above,
* and a pointer to the first expression char past "scripts"
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be scanned
* subscript (O) char * to null-terminated string returning
* subscript (without _), if found, or "\000"
* superscript (O) char * to null-terminated string returning
* superscript (without ^), if found, or "\000"
* which (I) int containing 1 for subscript only,
* 2 for superscript only, >=3 for either/both
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to the first char of expression
* past returned "scripts" (unchanged
* except for skipped whitespace if
* neither subscript nor superscript found),
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o an input expression like ^a^b_c will return superscript="b",
* i.e., totally ignoring all but the last "script" encountered
* ======================================================================= */
/* --- entry point --- */
char *texscripts ( char *expression, char *subscript,
char *superscript, int which )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(); /* next subexpression from expression */
int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
/* -------------------------------------------------------------------------
init "scripts" and skip leading whitespace
-------------------------------------------------------------------------- */
/* --- skip leading whitespace and check for end-of-string --- */
*subscript = *superscript = '\000'; /* init in case no scripts */
skipwhite(expression); /* leading whitespace gone */
if ( *expression == '\000' ) return(expression); /* nothing left to scan */
/* -------------------------------------------------------------------------
get subscript and/or superscript from expression
-------------------------------------------------------------------------- */
while ( expression != NULL )
if ( isthischar(*expression,SUBSCRIPT) /* found _ */
&& (which==1 || which>2 ) ) /* and caller wants it */
{ if ( gotsub /* found 2nd subscript */
|| subscript == NULL ) break; /* or no subscript buffer */
gotsub = 1; /* set subscript flag */
expression = texsubexpr(expression+1,subscript,"{","}",0,0); }
else /* no _, check for ^ */
if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */
&& which>=2 ) /* and caller wants it */
{ if ( gotsup /* found 2nd superscript */
|| superscript == NULL ) break; /* or no superscript buffer*/
gotsup = 1; /* set superscript flag */
expression = texsubexpr(expression+1,superscript,"{","}",0,0); }
else /* neither _ nor ^ */
return ( expression ); /*return ptr past "scripts"*/
return ( expression );
} /* --- end-of-function texscripts() --- */
/* ==========================================================================
* Function: isbrace ( expression, braces, isescape )
* Purpose: checks leading char(s) of expression for a brace,
* either escaped or unescaped depending on isescape,
* except that { and } are always matched, if they're
* in braces, regardless of isescape.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing a valid LaTeX expression
* whose leading char(s) are checked for braces
* that begin subexpression, e.g., "{[(<"
* braces (I) char * specifying matching brace delimiters
* to be checked for, e.g., "{[(<" or "}])>"
* isescape (I) int containing 0 to match only unescaped
* braces, e.g., (...) or {...}, etc,
* or containing 1 to match only escaped
* braces, e.g., \(...\) or \[...\], etc,
* or containing 2 to match either.
* But note: if {,} are in braces
* then they're *always* matched whether
* escaped or not, regardless of isescape.
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if the leading char(s) of expression
* is a brace, or 0 if not.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
int isbrace ( char *expression, char *braces, int isescape )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int gotescape = 0, /* true if leading char is an escape */
gotbrace = 0; /*true if first non-escape char is a brace*/
/* -------------------------------------------------------------------------
check for brace
-------------------------------------------------------------------------- */
/* --- first check for end-of-string --- */
if ( *expression == '\000' ) return(0); /* nothing to check */
/* --- check leading char for escape --- */
if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
{ gotescape = 1; /* so set flag accordingly */
expression++; } /* and bump past escape */
/* --- check (maybe next char) for brace --- */
if ( isthischar(*expression,braces) ) /* expression is braced */
gotbrace = 1; /* so set flag accordingly */
if ( gotescape && *expression == '.' ) /* \. matches any brace */
gotbrace = 1; /* set flag */
/* --- check for TeX brace { or } --- */
if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
if ( isescape ) isescape = 2; /* reset escape flag */
/* -------------------------------------------------------------------------
back to caller
-------------------------------------------------------------------------- */
if ( gotbrace && /* found a brace */
( isescape==2 || /* escape irrelevant */
gotescape==isescape ) /* un/escaped as requested */
) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */
} /* --- end-of-function isbrace() --- */
/* ==========================================================================
* Function: preamble ( expression, size, subexpr )
* Purpose: parses $-terminated preamble, if present, at beginning
* of expression, re-setting size if necessary, and
* returning any other parameters besides size in subexpr.
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing LaTeX expression possibly
* preceded by $-terminated preamble
* size (I/O) int * containing 0-4 default font size,
* and returning size modified by first
* preamble parameter (or unchanged)
* subexpr(O) char * returning any remaining preamble
* parameters past size
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to first char past preamble in expression
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o size can be any number >=0. If preceded by + or -, it's
* interpreted as an increment to input size; otherwise
* it's interpreted as the size.
* o if subexpr is passed as NULL ptr, then returned expression
* ptr will have "flushed" and preamble parameters after size
* ======================================================================= */
/* --- entry point --- */
char *preamble ( char *expression, int *size, char *subexpr )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char pretext[512], *prep=expression, /*pream from expression, ptr into it*/
*dollar, *comma; /* preamble delimiters */
int prelen = 0, /* preamble length */
sizevalue = 0, /* value of size parameter */
issize = 0, /* true if leading size present */
isdelta = 0; /*true to increment passed size arg*/
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
if ( subexpr != NULL ) /* caller passed us an address */
*subexpr = '\000'; /* so init assuming no preamble */
/* -------------------------------------------------------------------------
process preamble if present
-------------------------------------------------------------------------- */
if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
!= NULL ) /* found embedded $ */
if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
> 0 ) /* must have preamble preceding $ */
{
memcpy(pretext,expression,prelen); /* local copy of preamble */
pretext[prelen] = '\000'; /* null-terminated */
if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
&& strchr(pretext,'{') == NULL ) /*shouldn't be a left{ in preamble*/
{
/* --- skip any leading whitespace --- */
prep = pretext; /* start at beginning of preamble */
skipwhite(prep); /* skip any leading white space */
/* --- check for embedded , or leading +/- (either signalling size) --- */
if ( isthischar(*prep,"+-") ) /* have leading + or - */
isdelta = 1; /* so use size value as increment */
comma = strchr(pretext,','); /* , signals leading size param */
/* --- process leading size parameter if present --- */
if ( comma != NULL /* size param explicitly signalled */
|| isdelta || isdigit(*prep) ) /* or inferred implicitly */
{ /* --- parse size parameter and reset size accordingly --- */
if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
sizevalue = atoi(prep); /* convert size string to integer */
if ( size != NULL ) /* caller passed address for size */
*size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
/* --- finally, set flag and shift size parameter out of preamble --- */
issize = 1; /* set flag showing size present */
if ( comma != NULL ) strncpy(pretext,comma+1,512);/*leading size param gone*/
} /* --- end-of-if(comma!=NULL||etc) --- */
/* --- copy any preamble params following size to caller's subexpr --- */
if ( comma != NULL || !issize ) /*preamb contains params past size*/
if ( subexpr != NULL ) /* caller passed us an address */
strcpy(subexpr,pretext); /*so return extra params to caller*/
/* --- finally, set prep to shift preamble out of expression --- */
prep = expression + prelen+1; /* set prep past $ in expression */
} /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
} /* --- end-of-if(dollar!=NULL)/if(prelen>0) --- */
/* -------------------------------------------------------------------------
back to caller
-------------------------------------------------------------------------- */
return ( prep ); /*expression, or ptr past preamble*/
} /* --- end-of-function preamble() --- */
/* ==========================================================================
* Function: mimeprep ( expression )
* Purpose: preprocessor for mimeTeX input, e.g.,
* (a) removes comments,
* (b) converts \left( to \( and \right) to \),
* (c) xlates &html; special chars to equivalent latex
* Should only be called once (after unescape_url())
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char * to first char of null-terminated
* string containing mimeTeX/LaTeX expression,
* and returning preprocessed string
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to input expression,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
char *mimeprep ( char *expression )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *expptr=expression, /* ptr within expression */
*tokptr=NULL; /*ptr to token found in expression*/
char *strchange(); /* change leading chars of string */
char *findbraces(); /*find left { and right } for \atop*/
int idelim=0, /* left- or right-index */
isymbol=0; /*symbols[],rightcomment[],etc index*/
/* ---
* comments
* -------- */
char *leftptr=NULL; /* find leftcomment in expression */
static char *leftcomment = "%%", /* open comment */
*rightcomment[] = {"\n", "%%", NULL}; /* close comments */
/* ---
* special long (more than 1-char) \left and \right delimiters
* ----------------------------------------------------------- */
static char *leftfrom[] = /* xlate any \left suffix... */
{ "\\|", /* \left\| */
"\\{", /* \left\{ */
"\\langle", /* \left\langle */
NULL } ; /* --- end-of-leftfrom[] --- */
static char *leftto[] = /* ...to this instead */
{ "=", /* = */
"{", /* { */
"<", /* < */
NULL } ; /* --- end-of-leftto[] --- */
static char *rightfrom[] = /* xlate any \right suffix... */
{ "\\|", /* \right\| */
"\\}", /* \right\} */
"\\rangle", /* \right\rangle */
NULL } ; /* --- end-of-rightfrom[] --- */
static char *rightto[] = /* ...to this instead */
{ "=", /* = */
"}", /* } */
">", /* > */
NULL } ; /* --- end-of-rightto[] --- */
/* ---
* { \atop }-like commands
* ----------------------- */
char *atopsym=NULL; /* atopcommands[isymbol] */
static char *atopcommands[] = /* list of {a+b\command c+d}'s */
{ "\\over", /* plain tex for \frac */
#ifndef NOATOP /*noatop preserves old mimeTeX rule*/
"\\atop",
#endif
NULL } ; /* --- end-of-atopcommands[] --- */
/* ---
* html special/escape chars converted to latex equivalents
* -------------------------------------------------------- */
char *htmlsym=NULL; /* symbols[isymbol].html */
static struct { char *html; char *term; char *latex; } symbols[] =
{ /* ---------------------------------------
html termchar LaTeX equivalent...
--------------------------------------- */
#ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */
#include NEWCOMMANDS
#endif
{ """, ";", "\"" }, /* " is first, " */
{ "&", ";", "&" },
{ "<", ";", "<" },
{ ">", ";", ">" },
{ " ", ";", "~" },
{ "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
{ "¦", ";", "|" },
{ "±", ";", "\\pm" },
{ "²", ";", "{{}^2}" },
{ "³", ";", "{{}^3}" },
{ "µ", ";", "\\mu" },
{ "¹", ";", "{{}^1}" },
{ "¼", ";", "{\\frac14}" },
{ "½", ";", "{\\frac12}" },
{ "¾", ";", "{\\frac34}" },
{ "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
{ "Â", ";", "{\\rm~\\hat~A}" },
{ "Ã", ";", "{\\rm~\\tilde~A}" },
{ "Ä", ";", "{\\rm~\\ddot~A}" },
{ "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
{ "ã", ";", "{\\rm~\\tilde~a}" },
{ "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
/* ---------------------------------------
LaTeX termchar mimeTeX equivalent...
--------------------------------------- */
{ "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
{ "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
{ "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
{ "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
{ "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
{ "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
{ "\\notin", NULL, "{\\not\\in}" },
{ "\\neq", NULL, "{\\not=}" },
{ "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
{ "\\cr", NULL, "\\\\" },
{ "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
{ "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
/* ---------------------------------------
LaTeX Constant termchar value...
--------------------------------------- */
{ "\\thinspace", NULL, "2" },
{ "\\thinmathspace", NULL, "2" },
{ NULL, NULL, NULL }
} ; /* --- end-of-symbols[] --- */
/* -------------------------------------------------------------------------
first remove comments
-------------------------------------------------------------------------- */
expptr = expression; /* start search at beginning */
while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
{
char *rightsym=NULL; /* rightcomment[isymbol] */
expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
/* --- check for any closing rightcomment, in given precedent order --- */
if ( *expptr != '\000' ) /*have chars after this leftcomment*/
for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
{ tokptr += strlen(rightsym); /* first char after rightcomment */
if ( *tokptr == '\000' ) /*nothing after this rightcomment*/
{ *leftptr = '\000'; /*so terminate expr at leftcomment*/
break; } /* and stop looking for comments */
*leftptr = '~'; /* replace entire comment by ~ */
strcpy(leftptr+1,tokptr); /* and squeeze out comment */
goto next_comment; } /* stop looking for rightcomment */
/* --- no rightcomment after opening leftcomment --- */
*leftptr = '\000'; /* so terminate expression */
/* --- resume search past squeezed-out comment --- */
next_comment:
if ( *leftptr == '\000' ) break; /* reached end of expression */
expptr = leftptr+1; /*resume search after this comment*/
} /* --- end-of-while(leftptr!=NULL) --- */
/* -------------------------------------------------------------------------
convert \left( to \( and \right) to \), etc.
-------------------------------------------------------------------------- */
for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left and 1 for \right */
{
char *lrstr = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
int lrlen = (idelim==0?5:6); /* strlen() of \left or \right */
char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{*/
**lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
**lrto = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
*lrsym = NULL; /* lrfrom[isymbol] */
expptr = expression; /* start search at beginning */
while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
{
if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
{ strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/
expptr = tokptr+2; } /* and resume search past brace */
else /* may be a "long" brace like \| */
{
expptr = tokptr+lrlen; /*init to resume search past\left\rt*/
for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
{ int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
{ strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
*(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
break; } /* no need to check more lrsym's */
} /* --- end-of-for(isymbol) --- */
} /* --- end-of-if/else(isthischar()) --- */
} /* --- end-of-while(tokptr!=NULL) --- */
} /* --- end-of-for(idelim) --- */
/* -------------------------------------------------------------------------
run thru table, converting all occurrences of each html escape to latex equiv
-------------------------------------------------------------------------- */
for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
{
int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
char *htmlterm = symbols[isymbol].term, /* optional htmlsym terminators */
*latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
expptr = expression; /* re-start search at beginning */
while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */
{ char termchar = *(tokptr+htmllen); /* char terminating html sequence */
int escapelen = htmllen; /* total length of escape sequence */
if ( htmlterm != NULL ) /* sequence may have terminator */
escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
{ expptr = tokptr+htmllen; /* just resume search after prefix */
continue; } /* but don't replace it */
if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */
&& !isthischar(*htmlsym,"&") ) /* and not an &html; special char */
if ( tokptr != expression ) /* then if we're past beginning */
if ( isthischar(*(tokptr-1),ESCAPE) ) /*and if inline symbol escaped*/
{ expptr = tokptr+escapelen; /*just resume search after literal*/
continue; } /* but don't replace it */
strchange(escapelen,tokptr,latexsym); /* replace html symbol */
expptr = tokptr + strlen(latexsym); } /* and resume search after it */
} /* --- end-of-for(isymbol) --- */
/* -------------------------------------------------------------------------
run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
-------------------------------------------------------------------------- */
for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
{
int atoplen = strlen(atopsym); /* #chars in \atop */
expptr = expression; /* re-start search at beginning */
while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
{ char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
{ expptr = tokptr+atoplen; /* just resume search after prefix */
continue; } /* but don't process it */
if ( (leftbrace=findbraces(expression,tokptr))==NULL /*find left {*/
|| (rightbrace=findbraces(NULL,tokptr+atoplen-1))==NULL ) /*right }*/
expptr += atoplen; /* skip command if didn't find */
else /* we have bracketed { \atop } */
{
int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
char arg[8192], command[8192]; /* left/right args, new \atop{}{} */
strcpy(command,atopsym); /* start command with \atop */
memcpy(arg,leftbrace,leftlen+1); /* extract left-hand arg with { */
arg[leftlen+1] = '\000'; /* and null terminate it */
strcat(command,arg); /* concatanate {left-arg to \atop */
strcat(command,"}{"); /* close left-arg, open right-arg */
memcpy(arg,tokptr+atoplen,rightlen+1); /* right-hand arg with } */
arg[rightlen+1] = '\000'; /* and null terminate it */
if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
strcpy(arg,arg+1); /* so squeeze it out */
strcat(command,arg); /* concatanate right-arg} */
strchange(totlen,leftbrace,command); /* { \atop } --> \atop{}{} */
expptr = leftbrace + totlen; /* resume search after \atop{}{} */
}
} /* --- end-of-while(tokptr!=NULL) --- */
} /* --- end-of-for(isymbol) --- */
/* -------------------------------------------------------------------------
back to caller with preprocessed expression
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */
fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
return ( expression );
} /* --- end-of-function mimeprep() --- */
/* ==========================================================================
* Function: strchange ( int nfirst, char *from, char *to )
* Purpose: Changes the nfirst leading chars of `from` to `to`.
* For example, to change char x[99]="12345678" to "123ABC5678"
* call strchange(1,x+3,"ABC")
* --------------------------------------------------------------------------
* Arguments: nfirst (I) int containing #leading chars of `from`
* that will be replace by `to`
* from (I/O) char * to null-terminated string whose nfirst
* leading chars will be replaced by `to`
* to (I) char * to null-terminated string that will
* replace the nfirst leading chars of `from`
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to first char of input `from`
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o If strlen(to)>nfirst, from must have memory past its null
* (i.e., we don't do a realloc)
* ======================================================================= */
/* --- entry point --- */
char *strchange ( int nfirst, char *from, char *to )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
nshift = abs(tolen-nfirst); /*need to shift from left or right*/
/* -------------------------------------------------------------------------
shift from left or right to accommodate replacement of its nfirst chars by to
-------------------------------------------------------------------------- */
if ( tolen < nfirst ) /* shift left is easy */
strcpy(from,from+nshift); /* because memory doesn't overlap */
if ( tolen > nfirst ) /* need more room at start of from */
{ char *pfrom = from+strlen(from); /* ptr to null terminating from */
for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
*(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */
/* -------------------------------------------------------------------------
from has exactly the right number of free leading chars, so just put to there
-------------------------------------------------------------------------- */
if ( tolen != 0 ) /* make sure to not empty or null */
memcpy(from,to,tolen); /* chars moved into place */
return ( from ); /* changed string back to caller */
} /* --- end-of-function strchange() --- */
/* ==========================================================================
* Function: findbraces ( char *expression, char *command )
* Purpose: If expression!=NULL, finds opening left { preceding command;
* if expression==NULL, finds closing right } after command.
* For example, to parse out {a+b\over c+d} call findbraces()
* twice.
* --------------------------------------------------------------------------
* Arguments: expression (I) NULL to find closing right } after command,
* or char * to null-terminated string to find
* left opening { preceding command.
* command (I) char * to null-terminated string whose
* first character is usually the \ of \command
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to either opening { or closing },
* or NULL for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
char *findbraces ( char *expression, char *command )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int isopen = (expression==NULL?0:1); /* true to find left opening { */
char *left="{", *right="}", /* delims bracketing {x\command y} */
*delim = (isopen?left:right), /* delim we want, { if isopen */
*match = (isopen?right:left), /* matching delim, } if isopen */
*brace = NULL; /* ptr to delim returned to caller */
int inc = (isopen?-1:+1); /* pointer increment */
int level = 1; /* nesting level, for {{}\command} */
char *ptr = command; /* start search here */
/* -------------------------------------------------------------------------
search for left opening { before command, or right closing } after command
-------------------------------------------------------------------------- */
while ( 1 ) /* search for brace, or until end */
{
/* --- next char to check for delim --- */
ptr += inc; /* bump ptr left or right */
/* --- check for beginning or end of expression --- */
if ( isopen ) /* going left, check for beginning */
if ( ptr < expression ) break; /* went before start of string */
else if ( *ptr == '\000' ) break; /* went past end of string */
/* --- don't check this char if it's escaped --- */
if ( !isopen || ptr>expression ) /* very first char can't be escaped*/
if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */
continue; /* so don't check this char */
/* --- check for delim --- */
if ( isthischar(*ptr,delim) ) /* found delim */
if ( --level == 0 ) /* and it's not "internally" nested*/
{ brace = ptr; /* set ptr to brace */
goto end_of_job; } /* and return it to caller */
/* --- check for matching delim --- */
if ( isthischar(*ptr,match) ) /* found matching delim */
level++; /* so bump nesting level */
} /* --- end-of-while(1) --- */
end_of_job:
return ( brace ); /*back to caller with delim or NULL*/
} /* --- end-of-function findbraces() --- */
/* ==========================================================================
* Function: rasterize ( expression, size )
* Purpose: returns subraster corresponding to (a valid LaTeX) expression
* at font size
* --------------------------------------------------------------------------
* Arguments: expression (I) char * to first char of null-terminated
* string containing valid LaTeX expression
* to be rasterized
* size (I) int containing 0-4 default font size
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error.
* --------------------------------------------------------------------------
* Notes: o This is mimeTeX's "main" reusable entry point. Easy to use:
* just call it with a LaTeX expression, and get back a bitmap
* of that expression. Then do what you want with the bitmap.
* ======================================================================= */
/* --- entry point --- */
subraster *rasterize ( char *expression, int size )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *preamble(), pretext[256]; /* process preamble, if present */
char chartoken[8192], *texsubexpr(), /*get subexpression from expression*/
*subexpr = chartoken; /* token may be parenthesized expr */
int isbrace(); /* check subexpr for braces */
mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/
int natoms=0; /* #atoms/tokens processed so far */
subraster *rasterize(), /* recurse */
*rastparen(), /* handle parenthesized subexpr's */
*rastlimits(); /* handle sub/superscripted expr's */
subraster *rastcat(), /* concatanate atom subrasters */
*subrastcpy(); /* copy final result if a charaster*/
subraster *get_charsubraster(), /* character subraster */
*sp=NULL, *prevsp=NULL, /* raster for current, prev char */
*expraster = (subraster *)NULL; /* raster returned to caller */
int delete_subraster(); /* free everything before returning*/
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* --- global values saved/restored at each recursive iteration --- */
int wastext = istext, /* initial istext mode flag */
wasdisplaymath = isdisplaymath, /* initial displaymath mode flag */
olddisplaysize = displaysize, /* initial displaysize */
*oldworkingparam = workingparam; /* initial working parameter */
subraster *oldworkingbox = workingbox, /* initial working box */
*oldleftexpression = leftexpression; /*left half rasterized so far*/
double oldunitlength = unitlength; /* initial unitlength */
mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
recurlevel++; /* wind up one more recursion level*/
leftexpression = NULL; /* no leading left half yet */
isreplaceleft = 0; /* reset replaceleft flag */
if ( msgfp!=NULL && msglevel >= 29 ) /*display expression for debugging*/
fprintf(msgfp,"rasterize> recursion level=%d, size=%d.\nexpression=\"%s\"\n",
recurlevel,size,(expression==NULL?"null":expression));
if ( expression == NULL ) goto end_of_job; /* nothing given to do */
/* -------------------------------------------------------------------------
preocess optional $-terminated preamble preceding expression
-------------------------------------------------------------------------- */
expression = preamble(expression,&size,pretext); /* size may be modified */
if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
displaysize = size; /* start at requested size */
/* -------------------------------------------------------------------------
build up raster one character (or subexpression) at a time
-------------------------------------------------------------------------- */
while ( 1 )
{
/* --- get next character/token or subexpression --- */
expression = texsubexpr(expression,chartoken,LEFTBRACES,RIGHTBRACES,1,1);
subexpr = chartoken; /* "local" copy of chartoken ptr */
leftsymdef = NULL; /* no character identified yet */
sp = NULL; /* no subraster yet */
size = displaysize; /* in case reset by \tiny, etc */
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel >= 999 ) /* display chartoken for debugging */
fprintf(msgfp,"rasterize> recursion level=%d, atom#%d = \"%s\"\n",
recurlevel,natoms+1,chartoken);
if ( expression == NULL /* no more tokens */
&& *subexpr == '\000' ) break; /* and this token empty */
if ( *subexpr == '\000' ) break; /* enough if just this token empty */
/* --- check for parenthesized subexpression --- */
if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */
{ if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
== NULL ) continue; } /* flush it if failed to rasterize */
else /* --- single-character atomic token --- */
if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
{
/* --- first look up mathchardef for atomic token in table --- */
if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
== NULL ) /* lookup failed */
{ char literal[512] = "[?]"; /*display for unrecognized literal*/
if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */
fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
chartoken);
sp = (subraster *)NULL; /* init to signal failure */
if ( warninglevel < 1 ) continue; /* warnings not wanted */
if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */
{ /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
strcat(literal,chartoken+1); /* add chars following leading \ */
strcat(literal,"?]}"); } /* add closing brace */
if ( (sp = rasterize(literal,size-1)) /* rasterize literal token */
== (subraster *)NULL ) continue; } /*flush token if rasterize fails*/
else /* --- check if we have special handler to process this token --- */
if ( symdef->handler != NULL ) /* have a handler for this token */
{ int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
if ( (sp = (subraster *) /* returned void* is subraster* */
(*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))== NULL )
continue; } /* flush token if handler failed */
else /* --- no handler, so just get subraster for this character --- */
if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
== NULL ) continue; /* flush token if failed */
} /* --- end-of-if/else ... if/else --- */
/* --- handle any super/subscripts following symbol or subexpression --- */
sp = rastlimits(&expression,size,sp);
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
{ fprintf(msgfp,"rasterize> recursion level=%d, atom#%d%s\n",
recurlevel,natoms+1,(sp==NULL?" = null":"..."));
if(sp!=NULL) type_raster(sp->image,msgfp); } /* display raster */
/* --- accumulate atom or parenthesized subexpression --- */
if ( natoms < 1 /* nothing previous to concat */
|| isreplaceleft ) /* or we're replacing previous */
{ expraster = subrastcpy(sp); /* so just copy static CHARASTER */
isreplaceleft = 0; } /* reset replacement flag */
else /*we've already built up atoms so...*/
if ( sp != NULL ) /* ...if we have a new component */
expraster = rastcat(expraster,sp,1); /* concat new one, free previous */
delete_subraster(prevsp); /* free prev (if not a CHARASTER) */
prevsp = sp; /* current becomes previous */
leftexpression = expraster; /* left half rasterized so far */
/* --- bump count --- */
natoms++; /* bump #atoms count */
} /* --- end-of-while(expression!=NULL) --- */
/* -------------------------------------------------------------------------
back to caller with rasterized expression
-------------------------------------------------------------------------- */
end_of_job:
delete_subraster(prevsp); /* free last (if not a CHARASTER) */
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
{ fprintf(msgfp,"rasterize> final recursion level=%d, atom#%d...\n",
recurlevel,natoms);
type_raster(expraster->image,msgfp); } /* display completed raster */
/* --- restore flags/values to original saved values --- */
istext = wastext; /* text mode reset */
isdisplaymath = wasdisplaymath; /* displaymath mode reset */
displaysize = olddisplaysize; /* displaysize reset */
workingparam = oldworkingparam; /* working parameter reset */
workingbox = oldworkingbox; /* working box reset */
leftexpression = oldleftexpression; /* leftexpression reset */
leftsymdef = oldleftsymdef; /* leftsymdef reset */
unitlength = oldunitlength; /* unitlength reset */
recurlevel--; /* unwind one recursion level */
/* --- return final subraster to caller --- */
return ( expraster );
} /* --- end-of-function rasterize() --- */
/* ==========================================================================
* Function: rastparen ( subexpr, size, basesp )
* Purpose: parentheses handler, returns a subraster corresponding to
* parenthesized subexpression at font size
* --------------------------------------------------------------------------
* Arguments: subexpr (I) char ** to first char of null-terminated
* string beginning with a LEFTBRACES
* to be rasterized
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o This "handler" isn't in the mathchardef symbol table,
* but is called directly from rasterize(), as necessary.
* o Though subexpr is returned unchanged, it's passed as char **
* for consistency with other handlers. Ditto, basesp is unused
* but passed for consistency
* ======================================================================= */
/* --- entry point --- */
subraster *rastparen ( char **subexpr, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *expression = *subexpr; /* dereference subexpr to get char* */
int explen = strlen(expression); /* total #chars, including parens */
int isescape = 0, /* true if parens \escaped */
isrightdot = 0, /* true if right paren is \right. */
isleftdot = 0; /* true if left paren is \left. */
char left[16], right[16]; /* parens enclosing expresion */
char noparens[8192]; /* get subexpr without parens */
subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */
int isheight = 1; /*true=full height, false=baseline*/
int height, /* height of rasterized noparens[] */
baseline; /* and its baseline */
int family = CMEX10; /* family for paren chars */
subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
subraster *rastcat(); /* concatanate subrasters */
int delete_subraster(); /*in case of error after allocation*/
/* -------------------------------------------------------------------------
rasterize "interior" of expression, i.e., without enclosing parens
-------------------------------------------------------------------------- */
/* --- first see if enclosing parens are \escaped --- */
if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */
isescape = 1; /* so set flag accordingly */
/* --- get expression *without* enclosing parens --- */
strcpy(noparens,expression); /* get local copy of expression */
noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */
/* --- rasterize it --- */
if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
== NULL ) goto end_of_job; /* quit if failed */
/* --- no need to add parentheses for unescaped { --- */
if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
goto end_of_job; /* just return sp to caller */
/* -------------------------------------------------------------------------
obtain paren characters to enclose noparens[] raster with
-------------------------------------------------------------------------- */
/* --- first get left and right parens from expression --- */
memset(left,0,16); memset(right,0,16); /* init parens with nulls */
left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */
right[0] = *(expression+explen-1); /* right} is always last char */
isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */
isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
/* --- need height of noparens[] raster as minimum parens height --- */
height = (sp->image)->height; /* height of noparens[] raster */
baseline = sp->baseline; /* baseline of noparens[] raster */
if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */
/* --- get best-fit parentheses characters --- */
if ( !isleftdot ) /* if not \left. */
lp = get_delim(left,height+1,family); /* get left paren char */
if ( !isrightdot ) /* and if not \right. */
rp = get_delim(right,height+1,family); /* get right paren char */
if ( (lp==NULL && !isleftdot) /* check that we got left( */
|| (rp==NULL && !isrightdot) ) /* and right) if needed */
{ delete_subraster(sp); /* if failed, free subraster */
if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
sp = (subraster *)NULL; /* signal error to caller */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
set paren baselines to center on noparens[] raster, and concat components
-------------------------------------------------------------------------- */
/* --- set baselines to center paren chars on raster --- */
if ( lp != NULL ) /* ignore for \left. */
lp->baseline = baseline + ((lp->image)->height - height)/2;
if ( rp != NULL ) /* ignore for \right. */
rp->baseline = baseline + ((rp->image)->height - height)/2;
/* --- concat lp||sp||rp to obtain final result --- */
if ( lp != NULL ) /* ignore \left. */
sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
if ( sp != NULL ) /* succeeded or ignored \left. */
if ( rp != NULL ) /* ignore \right. */
sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
/* --- back to caller --- */
end_of_job:
return ( sp );
} /* --- end-of-function rastparen() --- */
/* ==========================================================================
* Function: rastlimits ( expression, size, basesp )
* Purpose: \limits, \nolimts, _ and ^ handler,
* dispatches call to rastscripts() or to rastdispmath()
* as necessary, to handle sub/superscripts following symbol
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to current character (or
* subexpression) immediately preceding script
* indicator
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster returned by rastscripts()
* or rastdispmath(), or NULL for any error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastlimits ( char **expression, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
*rastcat(), /* may need to concat scripts */
*scriptsp = basesp; /* and this will become the result */
int isdisplay = (-1); /*set 1 for displaymath, 0 otherwise*/
/* --- to check for \limits or \nolimits preceding scripts --- */
char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
int toklen=0; /* strlen(limtoken) */
mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */
int class = (leftsymdef==NULL?(-1):leftsymdef->class); /*base sym class*/
/* -------------------------------------------------------------------------
determine whether or not to use displaymath
-------------------------------------------------------------------------- */
scriptlevel++; /* first, increment subscript level*/
*limtoken = '\000'; /* no token yet */
/* --- check for \limits or \nolimits --- */
skipwhite(exprptr); /* skip white space before \limits */
if ( exprptr != NULL ) /* expression ptr supplied */
if ( *exprptr != '\000' ) /* something in expression */
exprptr = texchar(exprptr,limtoken); /* retrieve next token */
if ( *limtoken != '\000' ) /* have token */
if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
if ( memcmp("\\limits",limtoken,toklen) == 0 /* may be \limits */
|| memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */
!= NULL ) /* found token in table */
if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */
isdisplay = 1; /* so explicitly set displaymath */
else /* wasn't \limits */
if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
isdisplay = 0; /* so explicitly reset displaymath */
/* --- see if we found \[no]limits --- */
if ( isdisplay != (-1) ) /* explicit directive found */
*expression = exprptr; /* so bump expression past it */
else /* noexplicit directive */
{ isdisplay = 0; /* init displaymath flag off */
if ( isdisplaymath ) /* we're in displaymath mode */
if ( isdisplaymath >= 2 ) /* and mode forced true */
{ if ( class!=VARIABLE && class!=ORDINARY ) /*don't force variables*/
isdisplay = 1; } /* so set flag */
else /* determine mode from base symbol */
if ( class == DISPOPER ) /*it's a displaymath operator*/
isdisplay = 1; } /* so set flag */
/* -------------------------------------------------------------------------
dispatch call to create sub/superscripts
-------------------------------------------------------------------------- */
if ( isdisplay ) /* scripts above/below base symbol */
scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
else /* scripts alongside base symbol */
if ( (scriptsp=rastscripts(expression,size,basesp)) == NULL ) /*no scripts*/
scriptsp = basesp; /* so just return unscripted symbol*/
else /* symbols followed by scripts */
if ( basesp != NULL ) /* have base symbol */
scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base symbol*/
end_of_job:
if ( msgfp!=NULL && msglevel>=99 )
{ fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
scriptlevel,(scriptsp==NULL?"null":"..."));
if ( scriptsp != NULL ) /* have a constructed raster */
type_raster(scriptsp->image,msgfp); } /*display constructed raster*/
scriptlevel--; /*lastly, decrement subscript level*/
return ( scriptsp );
} /* --- end-of-function rastlimits() --- */
/* ==========================================================================
* Function: rastscripts ( expression, size, basesp )
* Purpose: super/subscript handler, returns subraster for the leading
* scripts in expression, whose base symbol is at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string beginning with a super/subscript,
* and returning ptr immediately following
* last script character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading script
* (scripts will be placed relative to base)
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to scripts,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o This "handler" isn't in the mathchardef symbol table,
* but is called directly from rasterize(), as necessary.
* ======================================================================= */
/* --- entry point --- */
subraster *rastscripts ( char **expression, int size, subraster *basesp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for scripts */
subscript[512], supscript[512]; /* scripts parsed from expression */
subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */
*rastack(); /*sets scripts in displaymath mode*/
raster *rp=NULL; /* image raster embedded in sp */
int height=0, width=0, baseline=0, /* height,width,baseline of sp */
subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */
supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */
baseht=0, baseln=0; /* height,baseline of base */
int bdescend=0, sdescend=0; /* descender of base, subscript */
int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */
isbase=0; /* true if we have base symbol */
int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
vbetween = 2, /* vertical space between scripts */
vabove = szval+1, /*sup's top/bot above base's top/bot*/
vbelow = szval+1, /*sub's top/bot below base's top/bot*/
vbottom = szval+1, /*sup's bot above (sub's below) bsln*/
istweak = 1; /* true to tweak script positioning */
int rastput(); /*put scripts in constructed raster*/
int delete_subraster(); /* free work areas */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
Obtain subscript and/or superscript expressions, and rasterize them/it
-------------------------------------------------------------------------- */
/* --- parse for sub,superscript(s), and bump expression past it(them) --- */
if ( expression == NULL ) goto end_of_job; /* no *ptr given */
if ( *expression == NULL ) goto end_of_job; /* no expression given */
if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
*expression = texscripts(*expression,subscript,supscript,3);
/* --- rasterize scripts --- */
if ( *subscript != '\000' ) /* have a subscript */
subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
if ( *supscript != '\000' ) /* have a superscript */
supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
/* --- set flags for convenience --- */
issub = (subsp != (subraster *)NULL); /* true if we have subscript */
issup = (supsp != (subraster *)NULL); /* true if we have superscript */
isboth = (issub && issup); /* true if we have both */
if (!issub && !issup) goto end_of_job; /* quit if we have neither */
/* -------------------------------------------------------------------------
get height, width, baseline of scripts, and height, baseline of base symbol
-------------------------------------------------------------------------- */
/* --- get height and width of components --- */
if ( issub ) /* we have a subscript */
{ subht = (subsp->image)->height; /* so get its height */
subwidth = (subsp->image)->width; /* and width */
subln = subsp->baseline; } /* and baseline */
if ( issup ) /* we have a superscript */
{ supht = (supsp->image)->height; /* so get its height */
supwidth = (supsp->image)->width; /* and width */
supln = supsp->baseline; } /* and baseline */
/* --- get height and baseline of base, and descender of base and sub --- */
if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */
basesp = leftexpression; /* try using left side thus far */
if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */
{ baseht = (basesp->image)->height; /* height of base symbol */
baseln = basesp->baseline; /* and its baseline */
bdescend = baseht-(baseln+1); /* and base symbol descender */
sdescend = bdescend + vbelow; /*sub must descend by at least this*/
if ( baseht > 0 ) isbase = 1; } /* set flag */
/* -------------------------------------------------------------------------
determine width of constructed raster
-------------------------------------------------------------------------- */
width = max2(subwidth,supwidth); /*widest component is overall width*/
/* -------------------------------------------------------------------------
determine height and baseline of constructed raster
-------------------------------------------------------------------------- */
/* --- both super/subscript --- */
if ( isboth ) /*we have subscript and superscript*/
{ height = max2(subht+vbetween+supht, /* script heights + space bewteen */
vbelow+baseht+vabove); /*sub below base bot, sup above top*/
baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
/* --- superscript only --- */
if ( !issub ) /* we only have a superscript */
{ height = max3(baseln+1+vabove, /* sup's top above base symbol top */
supht+vbottom, /* sup's bot above baseln */
supht+vabove-bdescend); /* sup's bot above base symbol bot */
baseline = height-1; } /*sup's baseline at bottom of raster*/
/* --- subscript only --- */
if ( !issup ) /* we only have a subscript */
if ( subht > sdescend ) /*sub can descend below base bot...*/
{ height = subht; /* ...without extra space on top */
baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
else /* sub's top will be below baseln */
{ height = sdescend+1; /* sub's bot below base symbol bot */
baseline = 0; } /* sub's baseline at top of raster */
/* -------------------------------------------------------------------------
construct raster with superscript over subscript
-------------------------------------------------------------------------- */
/* --- allocate subraster containing constructed raster --- */
if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
== NULL ) /* and if we fail to allocate */
goto end_of_job; /* quit */
/* --- initialize subraster parameters --- */
sp->type = IMAGERASTER; /* set type as constructed image */
sp->size = size; /* set given size */
sp->baseline = baseline; /* composite scripts baseline */
rp = sp->image; /* raster embedded in subraster */
/* --- place super/subscripts in new raster --- */
if ( issup ) /* we have a superscript */
rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */
if ( issub ) /* we have a subscript */
rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
if ( issup ) delete_subraster(supsp); /* and superscript */
return ( sp );
} /* --- end-of-function rastscripts() --- */
/* ==========================================================================
* Function: rastdispmath ( expression, size, sp )
* Purpose: displaymath handler, returns sp along with
* its immediately following super/subscripts
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following sp to be
* rasterized along with its super/subscripts,
* and returning ptr immediately following last
* character processed.
* size (I) int containing 0-4 default font size
* sp (I) subraster * to display math operator
* to which super/subscripts will be added
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to sp
* plus its scripts, or NULL for any error
* --------------------------------------------------------------------------
* Notes: o sp returned unchanged if no super/subscript(s) follow it.
* ======================================================================= */
/* --- entry point --- */
subraster *rastdispmath ( char **expression, int size, subraster *sp )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for scripts */
subscript[512], supscript[512]; /* scripts parsed from expression */
int issub=0, issup=0; /* true if we have sub,sup */
subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
*rastack(), /* stack operator with scripts */
*new_subraster(); /* for dummy base sp, if needed */
int vspace = 1; /* vertical space between scripts */
/* -------------------------------------------------------------------------
Obtain subscript and/or superscript expressions, and rasterize them/it
-------------------------------------------------------------------------- */
/* --- parse for sub,superscript(s), and bump expression past it(them) --- */
*expression = texscripts(*expression,subscript,supscript,3);
/* --- rasterize scripts --- */
if ( *subscript != '\000' ) /* have a subscript */
subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
if ( *supscript != '\000' ) /* have a superscript */
supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
/* --- set flags for convenience --- */
issub = (subsp != (subraster *)NULL); /* true if we have subscript */
issup = (supsp != (subraster *)NULL); /* true if we have superscript */
if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/
/* -------------------------------------------------------------------------
stack operator and its script(s)
-------------------------------------------------------------------------- */
/* --- stack superscript atop operator --- */
if ( issup ) /* we have a superscript */
if ( sp == NULL ) /* but no base expression */
sp = supsp; /* so just use superscript */
else /* have base and superscript */
if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
== NULL ) goto end_of_job; /* and quit if failed */
/* --- stack operator+superscript atop subscript --- */
if ( issub ) /* we have a subscript */
if ( sp == NULL ) /* but no base expression */
sp = subsp; /* so just use subscript */
else /* have base and subscript */
if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
== NULL ) goto end_of_job; /* and quit if failed (done anyway)*/
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( sp );
} /* --- end-of-function rastdispmath() --- */
/* ==========================================================================
* Function: rastflags ( expression, size, basesp, flag, value, arg3 )
* Purpose: sets an internal flag, e.g., for \rm, or sets an internal
* value, e.g., for \unitlength=, and returns NULL
* so nothing is displayed
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* flag (I) int containing #define'd symbol specifying
* internal flag to be set
* value (I) int containing new value of flag
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) NULL so nothing is displayed
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastflags ( char **expression, int size, subraster *basesp,
int flag, int value, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /* parse expression for... */
valuearg[256]; /* value from expression, if needed */
int argvalue=0, /* atoi(valuearg) */
isdelta=0; /* true if + or - precedes valuearg */
double strtod(); /*convert ascii {valuearg} to double*/
/* -------------------------------------------------------------------------
set flag or value
-------------------------------------------------------------------------- */
switch ( flag )
{
default: break; /* unrecognized flag */
case ISTEXT:
if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
(*expression)++; /* skip leading ~ after \rm */
istext=value; break; /* set text mode */
case ISDISPLAY: isdisplaymath=value; break; /* set displaymath mode */
case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */
case ISSIZE: /* set displaysize */
case ISWEIGHT: /* set font weight */
if ( value != NOVALUE ) /* passed a fixed value to be set */
argvalue = value; /* set given fixed value */
else /* get value from expression */
{ *expression = texsubexpr(*expression,valuearg,"{","}",0,0);
if ( *valuearg != '\000' ) /* guard against empty string */
{ isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
argvalue = atoi(valuearg); } } /* convert to int */
switch ( flag )
{
default: break;
case ISSIZE: /* set display size */
displaysize = (isdelta? displaysize+argvalue : argvalue);
break;
case ISWEIGHT: /* set font weight number */
value = (isdelta? weightnum+argvalue : argvalue);
if ( value>=0 && value=2 && widthval<=600 ) /* sanity check */
width = widthval; } /* replace deafault width */
/* -------------------------------------------------------------------------
see if width is "absolute" or fill width
-------------------------------------------------------------------------- */
if ( isfill /* called as \hfill{} */
&& !isheight ) /* parameter conflict */
{ if ( leftexpression != NULL ) /* if we have left half */
width -= (leftexpression->image)->width; /*reduce left width from total*/
if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
!= NULL ) /* succeeded */
width -= (rightsp->image)->width; } /* reduce right width from total */
/* -------------------------------------------------------------------------
construct blank subraster, and return it to caller
-------------------------------------------------------------------------- */
/* --- get parameters from base symbol --- */
if ( basesp != (subraster *)NULL ) /* we have base symbol for space */
{ baseht = (basesp->image)->height; /* height of base symbol */
baseln = basesp->baseline; } /* and its baseline */
/* --- flip params for height --- */
if ( isheight ) /* width is actually height */
{ baseht = width; /* use given width as height */
width = 1; } /* and set default width */
/* --- generate and init space subraster --- */
if ( width > 0 ) /*make sure we have positive width*/
if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
!= NULL ) /* and if we succeed... */
{ /* --- ...re-init subraster parameters --- */
spacesp->size = size; /*propagate base font size forward*/
spacesp->baseline = baseln; } /* ditto baseline */
/* -------------------------------------------------------------------------
concat right half if \hfill-ing
-------------------------------------------------------------------------- */
if ( rightsp != NULL ) /* we have a right half after fill */
{ spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/
rastcat(spacesp,rightsp,3)); /* or cat right half after space */
*expression += strlen((*expression)); } /* push expression to its null */
return ( spacesp );
} /* --- end-of-function rastspace() --- */
/* ==========================================================================
* Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \\ handler, returns subraster corresponding to
* left-hand expression preceding \\ above right-hand expression
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \\ to be
* rasterized, and returning ptr immediately
* to terminating null.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastnewline ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
char *texsubexpr(), spacexpr[129], *xptr=spacexpr; /*optional \\[vspace]*/
double strtod(); /* convert ascii param to double */
int vspace = size+2; /* #pixels between lines */
/* -------------------------------------------------------------------------
obtain optional [vspace] argument immediately following \\ command
-------------------------------------------------------------------------- */
/* --- check if [vspace] given --- */
if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/
{
/* ---parse [vspace] and bump expression past it, interpret as double--- */
*expression = texsubexpr(*expression,spacexpr,"[","]",0,0);
if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
} /* --- end-of-if(*(*expression)=='[') --- */
if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
/* -------------------------------------------------------------------------
rasterize right half of expression and stack left half above it
-------------------------------------------------------------------------- */
/* --- rasterize right half --- */
if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
== NULL ) goto end_of_job; /* quit if failed */
/* --- stack left half above it --- */
newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/
/* --- back to caller --- */
end_of_job:
if ( newlsp != NULL ) /* returning entire expression */
{ int newht = (newlsp->image)->height; /* height of returned subraster */
newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
isreplaceleft = 1; /* so set flag to replace left half*/
*expression += strlen(*expression); } /* and push to terminating null*/
return ( newlsp ); /* 1st line over 2nd, or null=error*/
} /* --- end-of-function rastnewline() --- */
/* ==========================================================================
* Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 )
* Purpose: returns left/right arrow subraster (e.g., for \longrightarrow)
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* drctn (I) int containing +1 for right, -1 for left,
* or 0 for leftright
* isBig (I) int containing 0 for ---> or 1 for ===>
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to left/right arrow subraster
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o An optional argument [width] may *immediately* follow
* the \longxxx to explicitly set the arrow's width in pixels.
* For example, \longrightarrow calculates a default width
* (as usual in LaTeX), whereas \longrightarrow[50] explicitly
* draws a 50-pixel long arrow. This can be used, e.g.,
* to draw commutative diagrams in conjunction with
* \array (and maybe with \stackrel and/or \relstack, too).
* o In case you really want to render, say, [f]---->[g], just
* use an intervening space, i.e., [f]\longrightarrow~[g].
* In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
* ======================================================================= */
/* --- entry point --- */
subraster *rastarrow ( char **expression, int size, subraster *basesp,
int drctn, int isBig, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
char *texsubexpr(), widtharg[256]; /* parse for optional [width] */
char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
int delete_subraster(); /*free work areas in case of error*/
double strtod(); /* convert ascii [width] to value */
int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
int vspace = 1; /* #empty rows below arrow */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
construct longleft/rightarrow subraster, with limits, and return it to caller
-------------------------------------------------------------------------- */
/* --- check for optional width arg and replace default width --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int widthval; /* test [width] before using it */
*expression = texsubexpr(*expression,widtharg,"[","]",0,0);
widthval = /* convert [width] to integer */
(int)((unitlength*strtod(widtharg,NULL))+0.5);
if ( widthval>=2 && widthval<=600 ) /* sanity check */
width = widthval; } /* replace deafault width */
/* --- now parse for limits, and bump expression past it(them) --- */
if ( islimits ) /* handling limits internally */
{ *expression = texscripts(*expression,sub,super,3); /* parse for limits */
if ( *sub != '\000' ) /*have a subscript following arrow*/
subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
if ( *super != '\000' ) /*have superscript following arrow*/
supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
/* --- set height based on width --- */
height = min2(17,max2(9,(width+2)/6)); /* height based on width */
height = 1 + (height/2)*2; /* always force odd height */
/* --- generate arrow subraster --- */
if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- add space below arrow --- */
if ( vspace > 0 ) /* if we have space below arrow */
if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
!= NULL ) /* and if we succeeded */
if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- init arrow subraster parameters --- */
arrowsp->size = size; /*propagate base font size forward*/
arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */
/* --- add limits above/below arrow, as necessary --- */
if ( subsp != NULL ) /* stack subscript below arrow */
if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
== NULL ) goto end_of_job; /* quit if failed */
if ( supsp != NULL ) /* stack superscript above arrow */
if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
== NULL ) goto end_of_job; /* quit if failed */
/* --- return arrow (or NULL) to caller --- */
end_of_job:
return ( arrowsp );
} /* --- end-of-function rastarrow() --- */
/* ==========================================================================
* Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 )
* Purpose: returns an up/down arrow subraster (e.g., for \longuparrow)
* --------------------------------------------------------------------------
* Arguments: expression (I) char ** to first char of null-terminated
* LaTeX expression (unused/unchanged)
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
* immediately preceding space, whose baseline
* and height params are transferred to space
* drctn (I) int containing +1 for up, -1 for down,
* or 0 for updown
* isBig (I) int containing 0 for ---> or 1 for ===>
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to up/down arrow subraster
* or NULL for any error
* --------------------------------------------------------------------------
* Notes: o An optional argument [height] may *immediately* follow
* the \longxxx to explicitly set the arrow's height in pixels.
* For example, \longuparrow calculates a default height
* (as usual in LaTeX), whereas \longuparrow[25] explicitly
* draws a 25-pixel high arrow. This can be used, e.g.,
* to draw commutative diagrams in conjunction with
* \array (and maybe with \stackrel and/or \relstack, too).
* o In case you really want to render, say, [f]---->[g], just
* use an intervening space, i.e., [f]\longuparrow~[g].
* In text use two spaces {\rm~[f]\longuparrow~~[g]}.
* ======================================================================= */
/* --- entry point --- */
subraster *rastuparrow ( char **expression, int size, subraster *basesp,
int drctn, int isBig, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
char *texsubexpr(), heightarg[256]; /* parse for optional [height] */
char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *rastcat(); /* cat superscript left, sub right */
double strtod(); /* convert ascii [height] to value */
int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
construct blank subraster, and return it to caller
-------------------------------------------------------------------------- */
/* --- check for optional height arg and replace default height --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int heightval; /* test height before using it */
*expression = texsubexpr(*expression,heightarg,"[","]",0,0);
heightval = /* convert [height] to integer */
(int)((unitlength*strtod(heightarg,NULL))+0.5);
if ( heightval>=2 && heightval<=600 ) /* sanity check */
height = heightval; } /* replace deafault height */
/* --- now parse for limits, and bump expression past it(them) --- */
if ( islimits ) /* handling limits internally */
{ *expression = texscripts(*expression,sub,super,3); /* parse for limits */
if ( *sub != '\000' ) /*have a subscript following arrow*/
subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
if ( *super != '\000' ) /*have superscript following arrow*/
supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
/* --- set width based on height --- */
width = min2(17,max2(9,(height+2)/4)); /* width based on height */
width = 1 + (width/2)*2; /* always force odd width */
/* --- generate arrow subraster --- */
if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
== NULL ) goto end_of_job; /* and quit if we failed */
/* --- init arrow subraster parameters --- */
arrowsp->size = size; /*propagate base font size forward*/
arrowsp->baseline = height-1; /* set baseline at bottom of arrow */
/* --- add limits above/below arrow, as necessary --- */
if ( supsp != NULL ) /* cat superscript to left of arrow*/
{ int supht = (supsp->image)->height, /* superscript height */
deltab = (1+abs(height-supht))/2; /* baseline difference to center */
supsp->baseline = supht-1; /* force script baseline to bottom */
if ( supht <= height ) /* arrow usually taller than script*/
arrowsp->baseline -= deltab; /* so bottom of script goes here */
else supsp->baseline -= deltab; /* else bottom of arrow goes here */
if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
== NULL ) goto end_of_job; } /* quit if failed */
if ( subsp != NULL ) /* cat subscript to right of arrow */
{ int subht = (subsp->image)->height, /* subscript height */
deltab = (1+abs(height-subht))/2; /* baseline difference to center */
arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
subsp->baseline = subht-1; /* force script baseline to bottom */
if ( subht <= height ) /* arrow usually taller than script*/
arrowsp->baseline -= deltab; /* so bottom of script goes here */
else subsp->baseline -= deltab; /* else bottom of arrow goes here */
if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
== NULL ) goto end_of_job; } /* quit if failed */
/* --- return arrow (or NULL) to caller --- */
end_of_job:
arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
return ( arrowsp );
} /* --- end-of-function rastuparrow() --- */
/* ==========================================================================
* Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3)
* Purpose: overlays one raster on another
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following overlay \cmd to
* be rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding overlay \cmd
* (unused, but passed for consistency)
* overlay (I) int containing 1 to overlay / (e.g., \not)
* or NOVALUE to pick up 2nd arg from expression
* offset2 (I) int containing #pixels to horizontally offset
* overlay relative to underlying symbol,
* positive(right) or negative or 0,
* or NOVALUE to pick up optional [offset] arg
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to composite,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastoverlay ( char **expression, int size, subraster *basesp,
int overlay, int offset2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for base,overlay*/
expr1[512], expr2[512]; /* base, overlay */
subraster *rasterize(), *sp1=NULL, *sp2=NULL; /*rasterize 1=base, 2=overlay*/
subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
/* -------------------------------------------------------------------------
Obtain base, and maybe overlay, and rasterize them
-------------------------------------------------------------------------- */
/* --- check for optional offset2 arg --- */
if ( offset2 == NOVALUE ) /* only if not explicitly specified*/
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int offsetval; /* test before using it */
*expression = texsubexpr(*expression,expr2,"[","]",0,0);
offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
if ( abs(offsetval) <= 25 ) /* sanity check */
offset2 = offsetval; } /* replace deafault */
if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
/* --- parse for base, bump expression past it, and rasterize it --- */
*expression = texsubexpr(*expression,expr1,"{","}",0,0);
if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
== NULL ) goto end_of_job; /* quit if failed to rasterize */
overlaysp = sp1; /*in case we return with no overlay*/
/* --- get overlay expression, and rasterize it --- */
if ( overlay == NOVALUE ) /* get overlay from input stream */
{ *expression = texsubexpr(*expression,expr2,"{","}",0,0);
if ( *expr2 != '\000' ) /* have an overlay */
sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */
else /* specific overlay */
switch ( overlay )
{
default: break;
case 1: /* e.g., \not overlays slash */
sp2 = rasterize("/",size+1); /* rasterize overlay expression */
offset2 = max2(1,size-3); /* push / right a bit */
offset2 = 0;
break;
} /* --- end-of-switch(overlay) --- */
if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/
/* -------------------------------------------------------------------------
construct composite overlay
-------------------------------------------------------------------------- */
overlaysp = rastcompose(sp1,sp2,offset2,0,3);
end_of_job:
return ( overlaysp );
} /* --- end-of-function rastoverlay() --- */
/* ==========================================================================
* Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 )
* Purpose: \frac,\atop handler, returns a subraster corresponding to
* expression (immediately following \frac,\atop) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \frac to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \frac
* (unused, but passed for consistency)
* isfrac (I) int containing true to draw horizontal line
* between numerator and denominator,
* or false not to draw it (for \atop).
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to fraction,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastfrac ( char **expression, int size, subraster *basesp,
int isfrac, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for numer,denom*/
numer[8192], denom[8192]; /*numer,denom parsed from expression*/
subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */
subraster *new_subraster(), *spacesp=NULL; /*may use space for num or den*/
int width=0, /* width of constructed raster */
numheight=0; /* height of numerator */
int baseht=0, baseln=0; /* height,baseline of base symbol */
int istweak = 1; /*true to tweak baseline alignment*/
int rule_raster(), /* draw horizontal line for frac */
lineheight = 1; /* thickness of fraction line */
int vspace = 1; /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
/* -------------------------------------------------------------------------
Obtain numerator and denominator, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for numerator,denominator and bump expression past them --- */
*expression = texsubexpr(*expression,numer,"{","}",0,0);
*expression = texsubexpr(*expression,denom,"{","}",0,0);
if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize numerator, denominator --- */
if ( *numer != '\000' ) /* have a numerator */
if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
== NULL ) goto end_of_job; /* and quit if failed */
if ( *denom != '\000' ) /* have a denominator */
if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
== NULL ) /* failed */
{ if ( numsp != NULL ) /* already rasterized numerator */
delete_subraster(numsp); /* so free now-unneeded numerator */
goto end_of_job; } /* and quit */
/* --- if one componenet missing, use a blank space for it --- */
if ( numsp == NULL ) /* no numerator given */
numsp = rasterize("[?]",size-1); /* missing numerator */
if ( densp == NULL ) /* no denominator given */
densp = rasterize("[?]",size-1); /* missing denominator */
/* --- check that we got both components --- */
if ( numsp==NULL || densp==NULL ) /* some problem */
{ delete_subraster(numsp); /*delete numerator (if it existed)*/
delete_subraster(densp); /*delete denominator (if it existed)*/
goto end_of_job; } /* and quit */
/* --- get height of numerator (to determine where line belongs) --- */
numheight = (numsp->image)->height; /* get numerator's height */
/* -------------------------------------------------------------------------
construct raster with numerator stacked over denominator
-------------------------------------------------------------------------- */
/* --- construct raster with numer/denom --- */
if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
== NULL ) /* failed to construct numer/denom */
{ delete_subraster(numsp); /* so free now-unneeded numerator */
delete_subraster(densp); /* and now-unneeded denominator */
goto end_of_job; } /* and then quit */
/* --- determine width of constructed raster --- */
width = (fracsp->image)->width; /*just get width of embedded image*/
/* --- initialize subraster parameters --- */
fracsp->size = size; /* propagate font size forward */
fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */
{ baseht = (basesp->image)->height; /* height of base symbol */
baseln = basesp->baseline; /* and its baseline */
} /* --- end-of-if(basesp!=NULL) --- */
/* -------------------------------------------------------------------------
draw horizontal line between numerator and denominator
-------------------------------------------------------------------------- */
if ( isfrac ) /*line for \frac, but not for \atop*/
rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0);
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( msgfp!=NULL && msglevel>=99 )
{ fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
if ( fracsp != NULL ) /* have a constructed raster */
type_raster(fracsp->image,msgfp); } /* display constructed raster */
return ( fracsp );
} /* --- end-of-function rastfrac() --- */
/* ==========================================================================
* Function: rastackrel ( expression, size, basesp, base, arg2, arg3 )
* Purpose: \stackrel handler, returns a subraster corresponding to
* stacked relation
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \stackrel to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \stackrel
* (unused, but passed for consistency)
* base (I) int containing 1 if upper/first subexpression
* is base relation, or 2 if lower/second is
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to stacked
* relation, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastackrel ( char **expression, int size, subraster *basesp,
int base, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), /*parse expression for numer,denom*/
upper[8192], lower[8192]; /*upper,lower parsed from expression*/
subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */
int upsize = (base==1? size:size-1), /* font size for upper component */
lowsize = (base==2? size:size-1); /* font size for lower component */
int vspace = 1; /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
/* -------------------------------------------------------------------------
Obtain numerator and denominator, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for numerator,denominator and bump expression past them --- */
*expression = texsubexpr(*expression,upper,"{","}",0,0);
*expression = texsubexpr(*expression,lower,"{","}",0,0);
if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize upper, lower --- */
if ( *upper != '\000' ) /* have upper component */
if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */
== NULL ) goto end_of_job; /* and quit if failed */
if ( *lower != '\000' ) /* have lower component */
if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
== NULL ) /* failed */
{ if ( upsp != NULL ) /* already rasterized upper */
delete_subraster(upsp); /* so free now-unneeded upper */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
construct stacked relation raster
-------------------------------------------------------------------------- */
/* --- construct stacked relation --- */
if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
relsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( relsp );
} /* --- end-of-function rastackrel() --- */
/* ==========================================================================
* Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 )
* Purpose: \log, \lim, etc handler, returns a subraster corresponding
* to math functions
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \mathfunc to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \mathfunc
* (unused, but passed for consistency)
* mathfunc (I) int containing 1=arccos, 2=arcsin, etc.
* islimits (I) int containing 1 if function may have
* limits underneath, e.g., \lim_{n\to\infty}
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to mathfunc,
* or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
int mathfunc, int islimits, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texscripts(), /* parse expression for _limits */
func[128], limits[8192]; /* func as {\rm func}, limits */
subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
int limsize = size-1; /* font size for limits */
int vspace = 1; /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
/* --- table of function names by mathfunc number --- */
static int numnames = 33; /* number of names in table */
static char *funcnames[] = {
"error", /* 0 index is illegal/error bucket*/
"arccos", "arcsin", "arctan", /* 1 - 3 */
"arg", "cos", "cosh", /* 4 - 6 */
"cot", "coth", "csc", /* 7 - 9 */
"deg", "det", "dim", /* 10 - 12 */
"exp", "gcd", "hom", /* 13 - 15 */
"inf", "ker", "lg", /* 16 - 18 */
"lim", "liminf", "limsup", /* 19 - 21 */
"ln", "log", "max", /* 22 - 24 */
"min", "Pr", "sec", /* 25 - 27 */
"sin", "sinh", "sup", /* 28 - 30 */
"tan", "tanh", /* 31 - 32 */
/* --- extra mimetex funcnames --- */
"tr" /* 33 */
} ;
/* -------------------------------------------------------------------------
set up and rasterize function name in \rm
-------------------------------------------------------------------------- */
if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
strcpy(func,"{\\rm~"); /* init string with {\rm~ */
strcat(func,funcnames[mathfunc]); /* concat function name */
strcat(func,"}"); /* and add terminating } */
if ( (funcsp = rasterize(func,size)) /* rasterize function name */
== NULL ) goto end_of_job; /* and quit if failed */
mathfuncsp = funcsp; /* just return funcsp if no limits */
if ( !islimits ) goto end_of_job; /* treat any subscript normally */
/* -------------------------------------------------------------------------
Obtain limits, if permitted and if provided, and rasterize them
-------------------------------------------------------------------------- */
/* --- parse for subscript limits, and bump expression past it(them) --- */
*expression = texscripts(*expression,limits,limits,1);
if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */
/* --- rasterize limits --- */
if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
== NULL ) goto end_of_job; /* and quit if failed */
/* -------------------------------------------------------------------------
construct func atop limits
-------------------------------------------------------------------------- */
/* --- construct func atop limits --- */
if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
mathfuncsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
return ( mathfuncsp );
} /* --- end-of-function rastmathfunc() --- */
/* ==========================================================================
* Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \sqrt handler, returns a subraster corresponding to
* expression (immediately following \sqrt) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \sqrt to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastsqrt ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192], /* parse subexpr to be sqrt-ed */
rootarg[8192]; /* optional \sqrt[rootarg]{...} */
subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */
subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
*new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
rootheight=0, rootwidth=0, /* height,width of rootarg raster */
subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
int rastput(); /* put subexpr in constructed sqrt */
int delete_subraster(); /* free work areas */
/* -------------------------------------------------------------------------
Obtain subexpression to be sqrt-ed, and rasterize it
-------------------------------------------------------------------------- */
/* --- first check for optional \sqrt[rootarg]{...} --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ *expression = texsubexpr(*expression,rootarg,"[","]",0,0);
if ( *rootarg != '\000' ) /* got rootarg */
if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
!= NULL ) /* rasterized successfully */
{ rootheight = (rootsp->image)->height; /* get height of rootarg */
rootwidth = (rootsp->image)->width; } /* and its width */
} /* --- end-of-if(**expression=='[') --- */
/* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0);
if ( *subexpr == '\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize subexpression to be accented --- */
if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
== NULL ) goto end_of_job; /* quit if failed */
/* -------------------------------------------------------------------------
determine height and width of sqrt raster to be constructed
-------------------------------------------------------------------------- */
/* --- first get height and width of subexpr --- */
subheight = (subsp->image)->height; /* height of subexpr */
subwidth = (subsp->image)->width; /* and its width */
pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
/* --- determine height and width of sqrt to contain subexpr --- */
sqrtheight = subheight + 2; /* subexpr + blank line + overbar */
surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */
sqrtwidth = subwidth + surdwidth;
/* -------------------------------------------------------------------------
construct sqrt (with room to move in subexpr) and embed subexpr in it
-------------------------------------------------------------------------- */
/* --- construct sqrt --- */
if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
== NULL ) goto end_of_job; /* quit if failed to build sqrt */
/* --- embed subexpr in sqrt at lower-right corner--- */
rastput(sqrtsp->image,subsp->image,2,sqrtwidth-subwidth,1);
sqrtsp->baseline = subsp->baseline + 2; /* adjust baseline */
/* --- "embed" rootarg at upper-left --- */
if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/
{
/* --- allocate full raster to contain sqrtsp and rootsp --- */
int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,surdwidth-2),
fullheight = sqrtheight+rootheight - min2(rootheight,2+size);
subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
if ( fullsp != NULL ) /* allocated successfully */
{ /* --- embed sqrtsp exactly at lower-right corner --- */
rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
fullheight-sqrtheight,fullwidth-sqrtwidth,1);
/* --- embed rootsp near upper-left, nestled above leading surd --- */
rastput(fullsp->image,rootsp->image,
0,max2(0,surdwidth-rootwidth-2-size),0);
/* --- replace sqrtsp with fullsp --- */
delete_subraster(sqrtsp); /* free original sqrtsp */
sqrtsp = fullsp; /* and repoint it to fullsp instead*/
sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
} /* --- end-of-if(rootsp!=NULL) --- */
/* --- initialize subraster parameters --- */
sqrtsp->size = size; /* propagate font size forward */
/* -------------------------------------------------------------------------
free unneeded component subrasters and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
return ( sqrtsp );
} /* --- end-of-function rastsqrt() --- */
/* ==========================================================================
* Function: rastaccent (expression,size,basesp,accent,isabove,isscript)
* Purpose: \hat, \vec, \etc handler, returns a subraster corresponding
* to expression (immediately following \accent) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \accent to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* accent (I) int containing HATACCENT or VECACCENT, etc,
* between numerator and denominator,
* or false not to draw it (for \over).
* isabove (I) int containing true if accent is above
* expression to be accented, or false
* if accent is below (e.g., underbrace)
* isscript (I) int containing true if sub/superscripts
* allowed (for under/overbrace), or 0 if not.
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to expression,
* or NULL for any parsing error
* (expression ptr unchanged if error occurs)
* --------------------------------------------------------------------------
* Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way
* of isabove and isscript args.
* ======================================================================= */
/* --- entry point --- */
subraster *rastaccent ( char **expression, int size, subraster *basesp,
int accent, int isabove, int isscript )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192]; /* parse subexpr to be accented */
char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
subscript[512], supscript[512]; /* scripts parsed from expression */
subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
int accheight=0, accwidth=0, /* height, width of accent */
subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
int delete_subraster(); /*free work areas in case of error*/
int vspace = 0; /*vertical space between accent,sub*/
/* -------------------------------------------------------------------------
Obtain subexpression to be accented, and rasterize it
-------------------------------------------------------------------------- */
/* --- parse for subexpr to be accented, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0);
if ( *subexpr=='\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* --- rasterize subexpression to be accented --- */
if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
== NULL ) goto end_of_job; /* quit if failed */
/* -------------------------------------------------------------------------
determine desired accent width and height
-------------------------------------------------------------------------- */
/* --- first get height and width of subexpr --- */
subheight = (subsp->image)->height; /* height of subexpr */
subwidth = (subsp->image)->width; /* and its width is overall width */
pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
/* --- determine desired width, height of accent --- */
accwidth = subwidth; /* same width as subexpr */
accheight = 3; /* default for bars */
switch ( accent )
{ default: break; /* default okay */
case DOTACCENT: case DDOTACCENT:
accheight = (size<3? 3:4); /* default for dots */
break;
case HATACCENT: case VECACCENT:
accheight = 7; /* default */
if ( subwidth < 10 ) accheight = 5; /* unless small width */
else if ( subwidth > 25 ) accheight = 9; /* or large */
break;
} /* --- end-of-switch(accent) --- */
accheight = min2(accheight,subheight); /*never higher than accented subexpr*/
/* -------------------------------------------------------------------------
construct accent, and construct subraster with accent over (or under) subexpr
-------------------------------------------------------------------------- */
/* --- first construct accent --- */
if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
== NULL ) goto end_of_job; /* quit if failed to build accent */
/* --- now stack accent above (or below) subexpr, and free both args --- */
accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
: rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/
if ( accsubsp == NULL ) /* failed to stack accent */
{ delete_subraster(subsp); /* free unneeded subsp */
delete_subraster(accsp); /* and unneeded accsp */
goto end_of_job; } /* and quit */
/* -------------------------------------------------------------------------
look for super/subscript (annotation for over/underbrace)
-------------------------------------------------------------------------- */
/* --- first check whether accent permits accompanying annotations --- */
if ( !isscript ) goto end_of_job; /* no annotations for this accent */
/* --- now get scripts if there actually are any --- */
*expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
script = (isabove? supscript : subscript); /*select above^ or below_ script*/
if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
/* --- rasterize script annotation at size-2 --- */
if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
== NULL ) goto end_of_job; /* quit if failed */
/* --- stack annotation above (or below) accent, and free both args --- */
accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
: rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */
/* -------------------------------------------------------------------------
return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( accsubsp != NULL ) /* initialize subraster parameters */
accsubsp->size = size; /* propagate font size forward */
return ( accsubsp );
} /* --- end-of-function rastaccent() --- */
/* ==========================================================================
* Function: rastfont (expression,size,basesp,font,arg2,arg3)
* Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
* to char(s) within {}'s rendered at size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \font to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
* font (I) int containing 1 for \cal{}, 2 for \scr{}
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to chars
* between {}'s, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastfont ( char **expression, int size, subraster *basesp,
int font, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/
subexpr[8192]; /* turn \cal{AB} into \calA\calB */
char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
char *name = NULL; /* fonts[font].name */
int class = 0; /* fonts[font].class */
subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
*rastflags(); /* or just set flag to switch font */
/* --- fonts recognized by rastfont --- */
static int nfonts = 4; /* legal font #'s are 1...nfonts */
static struct {char *name; int class;}
fonts[] =
{ /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
{ "\\badfont", 0 },
{ "\\cal", 1 }, /*(1) calligraphic, uppercase */
{ "\\scr", 1 }, /*(2) rsfs/script, uppercase */
{ "\\rm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
{ "\\it", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
{ NULL, 0 }
} ; /* --- end-of-fonts[] --- */
/* -------------------------------------------------------------------------
first get font name and class to determine type of conversion desired
-------------------------------------------------------------------------- */
if ( font<=0 || font>nfonts ) font=0; /* set error if out-of-bounds */
name = fonts[font].name; /* font name */
class = fonts[font].class; /* font class */
/* -------------------------------------------------------------------------
now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
-------------------------------------------------------------------------- */
if ( class < 0 ) /* not character-by-character */
{
/* ---
if \font not immediately followed by { then it has no arg, so just set flag
------------------------------------------------------------------------ */
if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
{
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font);
switch ( font ) /* set flag by our internal font# */
{ case 3: /* \rm, \text sets flag istext=1 */
fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break;
case 4: /* \it, \text sets flag istext=2 */
fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break;
default: break; } /* unrecognized, set no flags */
goto end_of_job;
} /* --- end-of-if(*(*expression)!='{') --- */
/* ---
convert \font{abc} --> {\font~abc}
---------------------------------- */
/* --- parse for {fontchars} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,fontchars,"{","}",0,0);
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
/* --- convert all fontchars at the same time --- */
strcpy(subexpr,"{"); /* start off with opening { */
strcat(subexpr,name); /* followed by font name */
strcat(subexpr,"~"); /* followed by whitespace */
strcat(subexpr,fontchars); /* followed by all the chars */
strcat(subexpr,"}"); /* terminate with closing } */
} /* --- end-of-if(class<0) --- */
else /* character-by-character */
{
/* ---
convert ABC to \calA\calB\calC
------------------------------ */
int isprevchar=0; /* true if prev char converted */
/* --- parse for {fontchars} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,fontchars,"{","}",0,0);
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
/* --- convert fontchars one at a time --- */
strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */
strcpy(subexpr,"{"); /* nope, just start off with { */
for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
{
if ( isthischar(fchar,WHITEMATH) ) /* some whitespace in text mode */
strcat(subexpr,"\\;"); /* so respect whitespace */
else /* char to be displayed in font */
{ int exprlen = 0; /* #chars in subexpr before fchar */
int isinclass = 0; /* set true if fchar in font class */
switch ( class ) /* check if fchar is in font class */
{ default: break; /* no chars in unrecognized class */
case 1: if ( isupper((int)fchar) ) isinclass=1; break;
case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
case 4: if ( islower((int)fchar) ) isinclass=1; break;
case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
case 9: isinclass=1; break; }
if ( isinclass ) /* convert current char to \font */
{ strcat(subexpr,name); /* by prefixing it with font name */
isprevchar = 1; } /* and set flag to signal separator*/
else /* current char not in \font */
{ if ( isprevchar ) /* extra separator only after \font*/
if ( isalpha(fchar) ) /* separator only before alpha */
strcat(subexpr,"~"); /* need separator after \font */
isprevchar = 0; } /* reset flag for next char */
exprlen = strlen(subexpr); /* #chars so far */
subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/
subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */
} /* --- end-of-for(pfchars) --- */
strcat(subexpr,"}"); /* add closing } */
} /* --- end-of-if/else(class<0) --- */
/* -------------------------------------------------------------------------
rasterize subexpression containing chars to be rendered at font
-------------------------------------------------------------------------- */
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
== NULL ) goto end_of_job; /* and quit if failed */
/* -------------------------------------------------------------------------
back to caller with chars rendered in font
-------------------------------------------------------------------------- */
end_of_job:
return ( fontsp ); /* chars rendered in font */
} /* --- end-of-function rastfont() --- */
/* ==========================================================================
* Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \begin{}...\end{} handler, returns a subraster corresponding
* to array expression within environment, i.e., rewrites
* \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \begin to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \begin
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to array
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastbegin ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192], /* \begin{} environment paramaters */
*exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
subraster *rasterize(), *sp=NULL; /* rasterize environment */
int ienviron = 0; /* environs[] index */
static char *environs[] = { /* types of environments we process*/
"eqnarray", /* eqnarray environment */
"array", /* array environment */
"matrix", /* array environment */
"tabular", /* array environment */
NULL }; /* trailer */
/* -------------------------------------------------------------------------
determine type of environment we're beginning
-------------------------------------------------------------------------- */
/* --- \begin must be followed by {type_of_environment} --- */
exprptr = texsubexpr(*expression,subexpr,"{","}",0,0);
if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
/* --- look up environment in our table --- */
for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
if ( environs[ienviron] == NULL ) /* found NULL before match */
goto end_of_job; /* so quit */
else /* see if we have an exact match */
if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
break; /* leave loop with ienviron index */
/* --- accumulate any additional params for this environment --- */
*subexpr = '\000'; /* reset subexpr to empty string */
switch ( ienviron )
{
default: goto end_of_job; /* environ not implemented yet */
case 0: /* \begin{eqnarray} */
strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */
break;
case 1: case 2: case 3: /* \begin{array} followed by {lcr} */
strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
skipwhite(exprptr); /* bump to next non-white char */
if ( *exprptr == '{' ) /* assume we have {lcr} argument */
{ exprptr = texsubexpr(exprptr,subexpr+7,"{","}",0,0); /* add on lcr */
if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
strcat(subexpr,"$"); } /* add terminating $ to lcr */
break;
} /* --- end-of-switch(ienviron) --- */
/* -------------------------------------------------------------------------
locate matching \end{...}
-------------------------------------------------------------------------- */
/* --- first \end following \begin --- */
if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */
== NULL ) goto end_of_job; /* and quit if no \end found */
/* --- find matching endptr by pushing past any nested \begin's --- */
begptr = exprptr; /* start after first \begin{...} */
while ( 1 ) /*break when we find matching \end*/
{
/* --- first, set ptr to closing } terminating current \end{...} --- */
if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
== NULL ) goto end_of_job; /* and quit if no } found */
/* -- locate next nested \begin --- */
if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
== NULL ) break; /*no more, so we have matching \end*/
begptr += strlen(begtoken); /* push ptr past token */
if ( begptr >= endptr ) break; /* past endptr, so not nested */
/* --- have nested \begin, so push forward to next \end --- */
if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
== NULL ) goto end_of_job; /* and quit if none found */
} /* --- end-of-while(1) --- */
/* -------------------------------------------------------------------------
add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
-------------------------------------------------------------------------- */
/* --- add on everything, completing subexpr for \begin{}...\end{} --- */
*endptr = '\000'; /*replace \ in \end by terminating 0*/
strcat(subexpr,exprptr); /* add on everything preceding \end*/
strcat(subexpr,"}"); /* followed by terminating } */
/* --- push expression past closing } of \end{} --- */
*expression = braceptr+1; /* resume processing after } */
/* --- debugging output --- */
if ( msgfp!=NULL && msglevel>=99 )
fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
/* -------------------------------------------------------------------------
return rasterized mimeTeX equivalent of \begin{}...\end{} environment
-------------------------------------------------------------------------- */
sp = rasterize(subexpr,size); /* rasterize subexpr */
end_of_job:
return ( sp ); /* back to caller with sp or NULL */
} /* --- end-of-function rastbegin() --- */
/* ==========================================================================
* Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \array handler, returns a subraster corresponding to array
* expression (immediately following \array) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \array to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \array
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to array
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \array{3,lcrBC$a&b&c\\d&e&f\\etc}
* o The 3,lcrBC$ part is an optional "preamble". The lcr means
* what you think, i.e., "horizontal" left,center,right
* justification down corresponding column. The new BC means
* "vertical" baseline,center justification across corresponding
* row. The leading 3 specifies the font size 0-4 to be used.
* You may also specify +1,-1,+2,-2, etc, which is used as an
* increment to the current font size, e.g., -1,lcr$ uses
* one font size smaller than current. Without a leading
* + or -, the font size is "absolute".
* o The preamble can also be just lcrBC$ without a leading
* size-part, or just 3$ without a trailing lcrBC-part.
* The default size is whatever is current, and the
* default justification is c(entered) and B(aseline).
* ======================================================================= */
/* --- entry point --- */
subraster *rastarray ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8193], *exprptr, /* parse array subexpr */
subtok[4096], *subptr=subtok, /* & or \\ inside { }'s not a delim*/
token[4096], *tokptr=token, /* token from subexpr to rasterize */
*preamble(), *preptr=token; /*process optional size,lcr preamble*/
char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
int maxarraysz = 32; /* max #rows, cols */
int justify[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
hline[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
vline[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
colwidth[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
rowheight[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
rowbaseln[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
rowcenter[33]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int irow,nrows=0, icol,ncols[33], /*#rows in array, #cols in each row*/
maxcols=0; /* max# cols in any single row */
int itoken, ntokens=0, /* index, total #tokens in array */
istokwhite=1, /* true if token all whitespace */
nnonwhite=0; /* #non-white tokens */
int isescape=0, wasescape=0; /* is current,previous char escape */
subraster *rasterize(), *toksp[1025], /* rasterize tokens */
*new_subraster(), *arraysp=NULL; /* subraster for entire array */
raster *arrayrp=NULL; /* raster for entire array */
int delete_subraster(); /* free toksp[] workspace at eoj */
int rowspace=2, colspace=4, /* blank space between rows, cols */
hspace=1, vspace=1; /*space to accommodate hline,vline*/
int width=0, height=0, /* width,height of array */
leftcol=0, toprow=0; /*upper-left corner for cell in it*/
int rastput(); /* embed tokens/cells in array */
int rule_raster(); /* draw hlines and vlines in array */
char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
char *texchar(), hltoken[1025]; /* extract \hline from token */
int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
/* -------------------------------------------------------------------------
Macros to determine extra raster space required for vline/hline
-------------------------------------------------------------------------- */
#define vlinespace(icol) \
( vline[icol] == 0? 0 : /* no vline so no space needed */ \
( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
#define hlinespace(irow) \
( hline[irow] == 0? 0 : /* no hline so no space needed */ \
( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
/* -------------------------------------------------------------------------
Obtain array subexpression
-------------------------------------------------------------------------- */
/* --- parse for array subexpression, and bump expression past it --- */
*subexpr = ' '; /* set leading blank */
*expression = texsubexpr(*expression,subexpr+1,"{","}",0,0);
if ( *(subexpr+1)=='\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* -------------------------------------------------------------------------
process optional size,lcr preamble if present
-------------------------------------------------------------------------- */
/* --- reset size, get lcr's, and push exprptr past preamble --- */
exprptr = preamble(subexpr+1,&size,preptr); /* reset size and get lcr's */
/* --- process lcr's, etc in preamble --- */
irow = icol = 0; /* init lcr counts */
while ( *preptr != '\000' ) /* check preamble text for lcr */
{
if ( irow= minhltoklen ) /*token must be at least \hl or \hd*/
if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
hline[nrows] += 1; /* bump \hline count for row */
else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
hline[nrows] = (-1); /* set \hdash flag for row */
if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
{ skipwhite(tokptr); /* flush whitespace after \hline */
if ( *tokptr == '\000' /* end-of-expression after \hline */
|| isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
istokwhite = ishonly = 1; /* so token contains \hline only */
else /* token contains more than \hline */
strcpy(token,tokptr); } /* so flush \hline from token */
} /* --- end-of-if(ncols[nrows]==0) --- */
/* --- rasterize completed token --- */
toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
rasterize(token,size)); /* rasterize non-empty token */
if ( toksp[ntokens] != NULL ) /* have a rasterized token */
nnonwhite++; /* bump rasterized token count */
/* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
if ( toksp[ntokens] != NULL ) /* we have a rasterized token */
{
/* --- update max token "height" in current row, and baseline --- */
int twidth = ((toksp[ntokens])->image)->width, /* width of token */
theight = ((toksp[ntokens])->image)->height, /* height of token */
tbaseln = (toksp[ntokens])->baseline, /* baseline of token */
rheight = rowheight[nrows], /* current max height for row */
rbaseln = rowbaseln[nrows]; /* current baseline for max height */
rowheight[nrows] = max2(rbaseln+1,tbaseln+1) /*max hght above baseln*/
+ max2(rheight-rbaseln-1,theight-tbaseln-1); /* plus max below */
rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
/* --- update max token width in current column --- */
icol = ncols[nrows]; /* current column index */
colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
} /* --- end-of-if(toksp[]!=NULL) --- */
/* --- bump counters --- */
if ( !ishonly ) /* don't count only an \hline */
{ ntokens++; /* bump total token count */
ncols[nrows] += 1; } /* and bump #cols in current row */
/* --- get ready for next token --- */
tokptr = token; /* reset ptr for next token */
istokwhite = 1; /* next token starts all white */
} /* --- end-of-if(iseoc) --- */
/* -----------------------------------------------------------------------
bump row as necessary
------------------------------------------------------------------------ */
if ( iseor ) /* we have a completed row */
{
maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
nrows++; /* bump row count */
ncols[nrows] = 0; /* no cols in this row yet */
if ( !iseox ) /* don't have a null yet */
{ exprptr++; /* bump past extra \ in \\ delim */
iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
} /* --- end-of-if(iseor) --- */
/* -----------------------------------------------------------------------
quit when done, or accumulate char in token and proceed to next char
------------------------------------------------------------------------ */
/* --- quit when done --- */
if ( iseox ) break; /* null terminator signalled done */
/* --- accumulate chars in token --- */
if ( !iseoc ) /* don't accumulate delimiters */
{ *tokptr++ = *exprptr; /* accumulate non-delim char */
if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
istokwhite = 0; } /* so reset flag to rasterize it */
/* --- ready for next char --- */
exprptr++; /* bump ptr */
} /* --- end-of-while(*exprptr!='\000') --- */
/* --- make sure we got something to do --- */
if ( nnonwhite < 1 ) /* completely empty array */
goto end_of_job; /* NULL back to caller */
/* -------------------------------------------------------------------------
determine dimensions of array raster and allocate it
-------------------------------------------------------------------------- */
/* --- adjust colspace --- */
colspace = 2 + 2*size; /* temp kludge */
/* --- determine width of array raster --- */
width = colspace*(maxcols-1); /* empty space between cols */
for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */
{ width += colwidth[icol]; /*width of this col (0 for maxcols)*/
width += vlinespace(icol); } /*plus space for vline, if present*/
/* --- determine height of array raster --- */
height = rowspace*(nrows-1); /* empty space between rows */
for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
{ height += rowheight[irow]; /*height of this row (0 for nrows)*/
height += hlinespace(irow); } /*plus space for hline, if present*/
/* --- allocate subraster and raster for array --- */
if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize subraster parameters --- */
arraysp->type = IMAGERASTER; /* image */
arraysp->symdef = NULL; /* not applicable for image */
arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
arraysp->size = size; /* size (probably unneeded) */
arrayrp = arraysp->image; /* raster embedded in subraster */
/* -------------------------------------------------------------------------
embed tokens/cells in array
-------------------------------------------------------------------------- */
itoken = 0; /* start with first token */
toprow = 0; /* start at top row of array */
for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/
{
/* --- initialization for row --- */
int baseline = rowbaseln[irow]; /* baseline for this row */
if ( hline[irow] != 0 ) /* need hline above this row */
{ int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
toprow += hlinespace(irow); /* space for hline above irow */
leftcol = 0; /* start at leftmost column */
for ( icol=0; icolimage)->width, /* token width */
theight= (tsp->image)->height, /* token height */
tokencol = 0, /*H offset (init for left justify)*/
tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
/* --- adjust leftcol for vline to left of icol, if present ---- */
leftcol += vlinespace(icol); /* space for vline to left of col */
/* --- reset justification (if not left-justified) --- */
if ( justify[icol] == 0 ) /* but user wants it centered */
tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
else if ( justify[icol] == 1 ) /* or user wants right-justify */
tokencol = cwidth-twidth; /* so put entire margin at left */
/* --- reset vertical centering (if not baseline-aligned) --- */
if ( rowcenter[irow] ) /* center cells in row vertically */
tokenrow = (rowheight[irow]-theight)/2; /* center row */
/* --- embed token raster at appropriate place in array raster --- */
rastput(arrayrp,tsp->image, /* overlay cell token in array */
toprow+ tokenrow, /*with aligned baseline or centered*/
leftcol+tokencol, 1); /* and justified as requested */
} /* --- end-of-if(tsp!=NULL) --- */
itoken++; /* bump index for next cell */
leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
} /* --- end-of-for(icol) --- */
toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
} /* --- end-of-for(irow) --- */
/* -------------------------------------------------------------------------
draw vlines as necessary
-------------------------------------------------------------------------- */
leftcol = 0; /* start at leftmost column */
for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */
{
if ( vline[icol] != 0 ) /* need vline to left of this col */
{ int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
leftcol += vlinespace(icol); /* space for vline to left of col */
if ( icol < maxcols ) /* don't address past end of array */
leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
} /* --- end-of-for(icol) --- */
/* -------------------------------------------------------------------------
free workspace and return final result to caller
-------------------------------------------------------------------------- */
end_of_job:
/* --- free workspace --- */
if ( ntokens > 0 ) /* if we have workspace to free */
while ( --ntokens >= 0 ) /* free each token subraster */
if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */
delete_subraster(toksp[ntokens]); /* then free it */
/* --- return final result to caller --- */
return ( arraysp );
} /* --- end-of-function rastarray() --- */
/* ==========================================================================
* Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \picture handler, returns subraster corresponding to picture
* expression (immediately following \picture) at font size
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \picture to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \picture
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to picture
* expression, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastpicture ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
pream[64], *preptr, /* optional put preamble */
picelem[1025]; /* picture element following put */
subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
*new_subraster(), *picturesp=NULL, /* subraster for entire picture */
*oldworkingbox = workingbox; /* save working box on entry */
raster *picturerp=NULL; /* raster for entire picture */
int delete_subraster(); /* free picelemsp[] workspace */
int pixsz = 1; /* pixels are one bit each */
double strtod(), /* convert ascii params to doubles */
x=0.0,y=0.0, /* x,y-coords for put,multiput*/
xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
int width=0, height=0, /* #pixels width,height of picture */
ewidth=0, eheight=0, /* pic element width,height */
ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
num=1, inum; /* number reps, index of element */
int iscenter=0; /* center or lowerleft put position*/
int *oldworkingparam = workingparam, /* save working param on entry */
origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
int rastput(); /* embed elements in picture */
/* -------------------------------------------------------------------------
First obtain (width,height) arguments immediately following \picture command
-------------------------------------------------------------------------- */
/* --- parse for (width,height) arguments, and bump expression past it --- */
*expression = texsubexpr(*expression,putexpr,"(",")",0,0);
if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
/* --- now interpret width,height returned in putexpr --- */
if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
*putptr = '\000'; /* found it, so replace ',' by '\0'*/
width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
if ( putptr != NULL ) /* 2nd arg, if present, is height */
height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
/* -------------------------------------------------------------------------
Then obtain entire picture {...} subexpression following (width,height)
-------------------------------------------------------------------------- */
/* --- parse for picture subexpression, and bump expression past it --- */
*expression = texsubexpr(*expression,picexpr,"{","}",0,0);
if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
/* -------------------------------------------------------------------------
allocate subraster and raster for complete picture
-------------------------------------------------------------------------- */
/* --- sanity check on width,height args --- */
if ( width < 2 || width > 600
|| height < 2 || height > 600 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed picture --- */
if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
== NULL ) goto end_of_job; /* quit if failed */
workingbox = picturesp; /* set workingbox to our picture */
/* --- initialize picture subraster parameters --- */
picturesp->type = IMAGERASTER; /* image */
picturesp->symdef = NULL; /* not applicable for image */
picturesp->baseline = height/2 + 2; /* is a little above center good? */
picturesp->size = size; /* size (probably unneeded) */
picturerp = picturesp->image; /* raster embedded in subraster */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
/* -------------------------------------------------------------------------
parse out each picture element, rasterize it, and place it in picture
-------------------------------------------------------------------------- */
while ( *picptr != '\000' ) /* until we run out of pic_elems */
{
/* -----------------------------------------------------------------------
first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
------------------------------------------------------------------------ */
/* --- init default values in case not explicitly supplied in args --- */
x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */
iscenter = origin = 0; /* center, origin */
/* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
picptr = texsubexpr(picptr,putexpr,"(",")",0,0);
if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
/* --- first look for $-terminated or for any non-digit preamble --- */
*pream = '\000'; /* init preamble as empty string */
if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
{ *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
strncpy(pream,putexpr,64); } /* copy leading preamble from put */
else /* look for any non-digit preamble */
{ for ( preptr=pream,putptr=putexpr; ; putptr++ )
if ( *putptr == '\000' /* end-of-putdata signalled */
|| !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
else *preptr++ = *putptr; /* copy alpha char to preamble */
*preptr = '\000'; } /* null-terminate preamble */
/* --- interpret preamble --- */
for ( preptr=pream; ; preptr++ ) /* examine each preamble char */
if ( *preptr == '\000' ) break; /* end-of-preamble signalled */
else switch ( tolower(*preptr) ) /* check lowercase preamble char */
{
default: break; /* unrecognized flag */
case 'c': iscenter=1; break; /* center pic_elem at x,y coords */
} /* --- end-of-switch --- */
/* --- interpret x,y;xinc,yinc;num following preamble --- */
if ( *putptr != '\000' ) /*check for put data after preamble*/
{
/* --- first squeeze preamble out of put expression --- */
if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
/* --- interpret x,y --- */
if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
*multptr = '\000'; /* replace semicolon by '\0' */
if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
*putptr = '\000'; /* replace comma by '\0' */
if ( *putexpr != '\000' ) /* leading , may be placeholder */
x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
y = unitlength*strtod(putptr+1,NULL); /* in pixels */
/* --- interpret xinc,yinc,num if we have a multiput --- */
if ( multptr != NULL ) /* found ';' signalling multiput */
{
if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
*preptr = '\000'; /* replace ';' by '\0' */
if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
*putptr = '\000'; /* replace ',' by '\0' */
if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
} /* --- end-of-if(multptr!=NULL) --- */
} /* --- end-of-if(*preptr!='\000') --- */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,
"picture> pream;x,y;xinc,yinc;num=\"%s\";%.2lf,%.2lf;%.2lf,%.2lf;%d\n",
pream,x,y,xinc,yinc,num);
/* -----------------------------------------------------------------------
now obtain {...} picture element following [multi]put, and rasterize it
------------------------------------------------------------------------ */
/* --- parse for {...} picture element and bump picptr past it --- */
picptr = texsubexpr(picptr,picelem,"{","}",0,0);
if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
/* --- rasterize picture element --- */
origin = 0; /* init origin as working param */
workingparam = &origin; /* and point working param to it */
picelemsp = rasterize(picelem,size); /* rasterize picture element */
if ( picelemsp == NULL ) continue; /* failed to rasterize, skip elem */
ewidth = (picelemsp->image)->width; /* width of element, in pixels */
eheight = (picelemsp->image)->height; /* height of element, in pixels */
if ( origin == 55 ) iscenter = 1; /* origin set to (.5,.5) for center*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
{ fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
ewidth,eheight,origin,num);
if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
/* -----------------------------------------------------------------------
embed element in picture (once, or multiple times if requested)
------------------------------------------------------------------------ */
for ( inum=0; inum=29 ) /* debugging */
fprintf(msgfp,
"picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2lf,%.2lf,%d,%d,%d,%d\n",
inum,x,y,ix,iy,xpos,ypos);
/* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
/* --- apply increment --- */
if ( xinc==0. && yinc==0. ) break; /* quit if both increments zero */
x += xinc; y += yinc; /* increment coords for next iter */
} /* --- end-of-for(inum) --- */
/* --- free picture element subraster after embedding it in picture --- */
delete_subraster(picelemsp); /* done with subraster, so free it */
} /* --- end-of-while(*picptr!=0) --- */
/* -------------------------------------------------------------------------
return picture constructed from pic_elements to caller
-------------------------------------------------------------------------- */
end_of_job:
workingbox = oldworkingbox; /* restore original working box */
workingparam = oldworkingparam; /* restore original working param */
return ( picturesp ); /* return our picture to caller */
} /* --- end-of-function rastpicture() --- */
/* ==========================================================================
* Function: rastline ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \line handler, returns subraster corresponding to line
* parameters (xinc,yinc){xlen}
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \line to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \line
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to line
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \line(xinc,yinc){xlen}
* o if {xlen} not given, then it's assumed xlen = |xinc|
* ======================================================================= */
/* --- entry point --- */
subraster *rastline ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), linexpr[129], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
subraster *new_subraster(), *linesp=NULL; /* subraster for line */
int pixsz = 1; /* pixels are one bit each */
double strtod(), /* convert ascii params to doubles */
xinc=0.0, yinc=0.0, /* x,y-increments for line, */
xlen=0.0, ylen=0.0; /* x,y lengths for line */
int width=0, height=0; /* #pixels width,height of line */
int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
int line_raster(); /* draw line in linesp->image */
/* -------------------------------------------------------------------------
obtain (xinc,yinc) arguments immediately following \line command
-------------------------------------------------------------------------- */
/* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
*expression = texsubexpr(*expression,linexpr,"(",")",0,0);
if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
/* --- now interpret xinc,yinc returned in linexpr --- */
if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
*xptr = '\000'; /* found it, so replace ',' by '\0'*/
if ( *linexpr != '\000' ) /* check against missing 1st arg */
xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */
if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
yinc = ylen = strtod(xptr+1,NULL); /* in user units */
/* -------------------------------------------------------------------------
obtain optional {xlen} following (xinc,yinc), and calculate ylen
-------------------------------------------------------------------------- */
/* --- check if {xlen} given --- */
if ( *(*expression) == '{' ) /*have {xlen} if leading char is { */
{
/* --- parse {xlen} and bump expression past it, interpret as double --- */
*expression = texsubexpr(*expression,linexpr,"{","}",0,0);
if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
xlen = strtod(linexpr,NULL); /* xlen in user units */
/* --- set other values accordingly --- */
if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
else xlen = 0.0; /* can't have xlen if xinc=0 */
} /* --- end-of-if(*(*expression)=='{') --- */
/* -------------------------------------------------------------------------
calculate width,height, etc, based on xlen,ylen, etc
-------------------------------------------------------------------------- */
/* --- force lengths positive --- */
xlen = absval(xlen); /* force xlen positive */
ylen = absval(ylen); /* force ylen positive */
/* --- calculate corresponding lengths in pixels --- */
width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
/* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
origin = isright*10 + istop; /* interpret 0=(0,0), 11=(1,1), etc*/
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
width,height,origin,xinc,yinc);
/* -------------------------------------------------------------------------
allocate subraster and raster for complete picture
-------------------------------------------------------------------------- */
/* --- sanity check on width,height args --- */
if ( width < 1 || width > 600
|| height < 1 || height > 600 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed line --- */
if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize line subraster parameters --- */
linesp->type = IMAGERASTER; /* image */
linesp->symdef = NULL; /* not applicable for image */
linesp->baseline = height/2 + 2; /* is a little above center good? */
linesp->size = size; /* size (probably unneeded) */
/* -------------------------------------------------------------------------
draw the line
-------------------------------------------------------------------------- */
line_raster ( linesp->image, /* embedded raster image */
(istop? 0 : height-1), /* row0, from bottom or top */
(isright? width-1 : 0), /* col0, from left or right */
(istop? height-1 : 0), /* row1, to top or bottom */
(isright? 0 : width-1), /* col1, to right or left */
1 ); /* line thickness is 1 pixel */
/* -------------------------------------------------------------------------
return constructed line to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( workingparam != NULL ) /* caller wants origin */
*workingparam = origin; /* return origin corner to caller */
return ( linesp ); /* return line to caller */
} /* --- end-of-function rastline() --- */
/* ==========================================================================
* Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \circle handler, returns subraster corresponding to ellipse
* parameters (xdiam[,ydiam])
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \circle to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-4 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \circle
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to ellipse
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \circle(xdiam[,ydiam])
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastcircle ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), circexpr[129],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
char *qptr=NULL, quads[99]="1234"; /* default to draw all quadrants */
subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
int pixsz = 1; /* pixels are one bit each */
double strtod(), /* convert ascii params to doubles */
xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
int width=0, height=0; /* #pixels width,height of ellipse */
int thickness = 1; /* drawn lines are one pixel thick */
int origin = 55; /* force origin centered */
int circle_raster(); /* draw ellipse in circsp->image */
/* -------------------------------------------------------------------------
obtain (xdiam[,ydiam]) arguments immediately following \circle command
-------------------------------------------------------------------------- */
/* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
*expression = texsubexpr(*expression,circexpr,"(",")",0,0);
if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
/* --- now interpret xdiam[,ydiam] returned in circexpr --- */
if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
{ *qptr = '\000'; /* replace semicolon by '\0' */
strcpy(quads,qptr+1); /* save user-requested quads */
qptr = quads; } /* set qptr arg for circle_raster()*/
if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
*xptr = '\000'; /* found it, so replace ',' by '\0'*/
xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */
if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
ydiam = strtod(xptr+1,NULL); /* in user units */
/* -------------------------------------------------------------------------
calculate width,height, etc
-------------------------------------------------------------------------- */
/* --- calculate width,height in pixels --- */
width = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
width,height,(qptr==NULL?"default":qptr));
/* -------------------------------------------------------------------------
allocate subraster and raster for complete picture
-------------------------------------------------------------------------- */
/* --- sanity check on width,height args --- */
if ( width < 1 || width > 600
|| height < 1 || height > 600 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed ellipse --- */
if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize ellipse subraster parameters --- */
circsp->type = IMAGERASTER; /* image */
circsp->symdef = NULL; /* not applicable for image */
circsp->baseline = height/2 + 2; /* is a little above center good? */
circsp->size = size; /* size (probably unneeded) */
/* -------------------------------------------------------------------------
draw the ellipse
-------------------------------------------------------------------------- */
circle_raster ( circsp->image, /* embedded raster image */
0, 0, /* row0,col0 are upper-left corner */
height-1, width-1, /* row1,col1 are lower-right */
thickness, /* line thickness is 1 pixel */
qptr ); /* "1234" quadrants to be drawn */
/* -------------------------------------------------------------------------
return constructed ellipse to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( workingparam != NULL ) /* caller wants origin */
*workingparam = origin; /* return center origin to caller */
return ( circsp ); /* return ellipse to caller */
} /* --- end-of-function rastcircle() --- */
/* ==========================================================================
* Function: rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \bezier handler, returns subraster corresponding to bezier
* parameters (col0,row0)(col1,row1)(colt,rowt)
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \bezier to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \bezier
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to bezier
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \bezier(col1,row1)(colt,rowt)
* o col0=0,row0=0 assumed, i.e., given by
* \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
* ======================================================================= */
/* --- entry point --- */
subraster *rastbezier ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
double strtod(); /* convert ascii params to doubles */
double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
rmin=0.0, cmin=0.0, /* minimum r,c */
rmax=0.0, cmax=0.0, /* maximum r,c */
rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
r=0.0, c=0.0; /* some point */
int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
int width=0, height=0; /* dimensions of bezier raster */
int pixsz = 1; /* pixels are one bit each */
int thickness = 1; /* drawn lines are one pixel thick */
int origin = 0; /*c's,r's reset to lower-left origin*/
int bezier_raster(); /* draw bezier in bezsp->image */
/* -------------------------------------------------------------------------
obtain (c1,r1)(ct,rt) args immediately following \bezier command
-------------------------------------------------------------------------- */
for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0,r0 1=c1,r1 2=ct,rt */
{
/* --- parse for (r,c) args, and bump expression past them all --- */
*expression = texsubexpr(*expression,bezexpr,"(",")",0,0);
if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
/* --- now interpret (r,c) returned in bezexpr --- */
c = r = 0.0; /* init x-coord=col, y-coord=row */
if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
{ *xptr = '\000'; /* found it, so replace ',' by '\0'*/
r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */
/* --- store r,c --- */
switch ( iarg )
{ case 0: r0=r; c0=c; break;
case 1: r1=r; c1=c; break;
case 2: rt=r; ct=c; break; }
} /* --- end-of-for(iarg) --- */
/* --- determine midpoint and maximum,minimum points --- */
rmid = 0.5*(rt + 0.5*(r0+r1)); /* y-coord at middle of bezier */
cmid = 0.5*(ct + 0.5*(c0+c1)); /* x-coord at middle of bezier */
rmin = min3(r0,r1,rmid); /* lowest row */
cmin = min3(c0,c1,cmid); /* leftmost col */
rmax = max3(r0,r1,rmid); /* highest row */
cmax = max3(c0,c1,cmid); /* rightmost col */
rdelta = rmax-rmin; /* height */
cdelta = cmax-cmin; /* width */
/* --- rescale coords so we start at 0,0 --- */
r0 -= rmin; c0 -= cmin; /* rescale r0,c0 */
r1 -= rmin; c1 -= cmin; /* rescale r1,c1 */
rt -= rmin; ct -= cmin; /* rescale rt,ct */
/* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
r0 = rdelta - r0 + 1; /* map 0-->height-1, height-1-->0 */
r1 = rdelta - r1 + 1;
rt = rdelta - rt + 1;
/* --- determine width,height of raster needed for bezier --- */
width = (int)(cdelta + 0.9999) + 1; /* round width up */
height = (int)(rdelta + 0.9999) + 1; /* round height up */
if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
"c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
/* -------------------------------------------------------------------------
allocate raster
-------------------------------------------------------------------------- */
/* --- sanity check on width,height args --- */
if ( width < 1 || width > 600
|| height < 1 || height > 600 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed bezier --- */
if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize bezier subraster parameters --- */
bezsp->type = IMAGERASTER; /* image */
bezsp->symdef = NULL; /* not applicable for image */
bezsp->baseline = height/2 + 2; /* is a little above center good? */
bezsp->size = size; /* size (probably unneeded) */
/* -------------------------------------------------------------------------
draw the bezier
-------------------------------------------------------------------------- */
bezier_raster ( bezsp->image, /* embedded raster image */
r0, c0, /* row0,col0 are lower-left corner */
r1, c1, /* row1,col1 are upper-right */
rt, ct ); /* bezier tangent point */
/* -------------------------------------------------------------------------
return constructed bezier to caller
-------------------------------------------------------------------------- */
end_of_job:
if ( workingparam != NULL ) /* caller wants origin */
*workingparam = origin; /* return center origin to caller */
return ( bezsp ); /* return bezier to caller */
} /* --- end-of-function rastbezier() --- */
/* ==========================================================================
* Function: rastraise ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \raisebox{lift}{subexpression} handler, returns subraster
* containing subexpression with its baseline "lifted" by lift
* pixels, scaled by \unitlength, or "lowered" if lift arg
* negative
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \raisebox to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \rotatebox
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to \raisebox
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \raisebox{lift}{subexpression}
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastraise ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192], *liftexpr=subexpr; /* args */
subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
int lift=0; /* amount to raise/lower baseline */
/* -------------------------------------------------------------------------
obtain {lift} argument immediately following \raisebox command
-------------------------------------------------------------------------- */
/* --- parse for {lift} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,liftexpr,"{","}",0,0);
if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/
if ( abs(lift) > 200 ) lift=0; /* sanity check */
/* -------------------------------------------------------------------------
obtain {subexpr} argument after {lift}, and rasterize it
-------------------------------------------------------------------------- */
/* --- parse for {subexpr} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0);
/* --- rasterize subexpression to be raised/lowered --- */
if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
== NULL ) goto end_of_job; /* and quit if failed */
/* -------------------------------------------------------------------------
raise/lower baseline and return it to caller
-------------------------------------------------------------------------- */
/* --- raise/lower baseline --- */
raisesp->baseline += lift; /* new baseline (no height checks) */
/* --- return raised subexpr to caller --- */
end_of_job:
return ( raisesp ); /* return raised subexpr to caller */
} /* --- end-of-function rastraise() --- */
/* ==========================================================================
* Function: rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \rotatebox{degrees}{subexpression} handler, returns subraster
* containing subexpression rotated by degrees (counterclockwise
* if degrees positive)
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \rotatebox to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \rotatebox
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to \rotatebox
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \rotatebox{degrees}{subexpression}
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastrotate ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */
subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */
raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
int delete_raster(); /* delete intermediate rasters */
int baseline=0; /* baseline of rasterized image */
double strtod(), /* convert ascii params to doubles */
degrees=0.0, ipart,fpart; /* degrees to be rotated */
int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
/* -------------------------------------------------------------------------
obtain {degrees} argument immediately following \rotatebox command
-------------------------------------------------------------------------- */
/* --- parse for {degrees} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,degexpr,"{","}",0,0);
if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
degrees = strtod(degexpr,NULL); /* degrees to be rotated */
if ( degrees < 0.0 ) /* clockwise rotation desired */
{ degrees = -degrees; /* flip sign so degrees positive */
isneg = 1; } /* and set flag to indicate flip */
fpart = modf(degrees,&ipart); /* integer and fractional parts */
ipart = (double)(((int)degrees)%360); /* degrees mod 360 */
degrees = ipart + fpart; /* restore fractional part */
if ( isneg ) /* if clockwise rotation requested */
degrees = 360.0 - degrees; /* do equivalent counterclockwise */
idegrees = (int)(degrees+0.5); /* integer degrees */
n90 = idegrees/90; /* degrees is n90 multiples of 90 */
isn90 = (90*n90==idegrees); /*true if degrees is multiple of 90*/
isn90 = 1; /* forced true for time being */
/* -------------------------------------------------------------------------
obtain {subexpr} argument after {degrees}, and rasterize it
-------------------------------------------------------------------------- */
/* --- parse for {subexpr} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0);
/* --- rasterize subexpression to be rotated --- */
if ( (rotsp = rasterize(subexpr,size)) /* rasterize subexpression */
== NULL ) goto end_of_job; /* and quit if failed */
/* --- return unmodified image if no rotation requested --- */
if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
/* --- extract params for image to be rotated --- */
rotrp = rotsp->image; /* unrotated rasterized image */
baseline = rotsp->baseline; /* and baseline of that image */
/* -------------------------------------------------------------------------
rotate by multiples of 90 degrees
-------------------------------------------------------------------------- */
if ( isn90 ) /* rotation by multiples of 90 */
if ( n90 > 0 ) /* do nothing for 0 degrees */
{
n90 = 4-n90; /* rasrot() rotates clockwise */
while ( n90 > 0 ) /* still have remaining rotations */
{ raster *nextrp = rastrot(rotrp); /* rotate raster image */
if ( nextrp == NULL ) break; /* something's terribly wrong */
delete_raster(rotrp); /* free previous raster image */
rotrp = nextrp; /* and replace it with rotated one */
n90--; } /* decrement remaining count */
} /* --- end-of-if(isn90) --- */
/* -------------------------------------------------------------------------
requested rotation not multiple of 90 degrees
-------------------------------------------------------------------------- */
if ( !isn90 ) /* explicitly construct rotation */
{ ; } /* not yet implemented */
/* -------------------------------------------------------------------------
re-populate subraster envelope with rotated image
-------------------------------------------------------------------------- */
/* --- re-init various subraster parameters, embedding raster in it --- */
if ( rotrp != NULL ) /* rotated raster constructed okay */
{ rotsp->type = IMAGERASTER; /* signal constructed image */
rotsp->image = rotrp; /* raster we just constructed */
/* --- now try to guess pleasing baseline --- */
if ( idegrees > 2 ) /* leave unchanged if unrotated */
if ( strlen(subexpr) < 3 /* we rotated a short expression */
|| abs(idegrees-180) < 3 ) /* or just turned it upside-down */
baseline = rotrp->height - 1; /* so set with nothing descending */
else /* rotated a long expression */
baseline = (65*(rotrp->height-1))/100; /* roughly center long expr */
rotsp->baseline = baseline; } /* set baseline as calculated above*/
/* --- return rotated subexpr to caller --- */
end_of_job:
return ( rotsp ); /*return rotated subexpr to caller*/
} /* --- end-of-function rastrotate() --- */
/* ==========================================================================
* Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \fbox{subexpression} handler, returns subraster
* containing subexpression with frame box drawn around it
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \fbox to be
* rasterized, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \fbox
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to subraster corresponding to \fbox
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
* \fbox{subexpression}
* o
* ======================================================================= */
/* --- entry point --- */
subraster *rastfbox ( char **expression, int size, subraster *basesp,
int arg1, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192], widtharg[512]; /* args */
subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
raster *border_raster(), *bp=NULL; /* framed image raster */
double strtod(); /* interpret [width][height] */
int fwidth=6, fthick=1; /*extra frame width, line thickness*/
int width=(-1), height=(-1), /* optional [width][height] args */
iscompose = 0; /* set true if optional args given */
/* -------------------------------------------------------------------------
obtain optional [width][height] arguments immediately following \fbox
-------------------------------------------------------------------------- */
/* --- first check for optional \fbox[width] --- */
if ( *(*expression) == '[' ) /* check for []-enclosed width arg */
{ *expression = texsubexpr(*expression,widtharg,"[","]",0,0);
if ( *widtharg != '\000' ) /* got widtharg */
{ width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
height = 1; fwidth = 2; iscompose = 1; }
} /* --- end-of-if(**expression=='[') --- */
if ( width > 0 ) /* found leading [width], so... */
if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
{ *expression = texsubexpr(*expression,widtharg,"[","]",0,0);
if ( *widtharg != '\000' ) /* got widtharg */
{ height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
fwidth = 0; } /* no extra border */
} /* --- end-of-if(**expression=='[') --- */
/* -------------------------------------------------------------------------
obtain {subexpr} argument
-------------------------------------------------------------------------- */
/* --- parse for {subexpr} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0);
/* --- rasterize subexpression to be framed --- */
if ( width<0 || height<0 ) /* no explicit dimensions given */
{ if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
== NULL ) goto end_of_job; } /* and quit if failed */
else
{ char composexpr[8192]; /* compose subexpr with empty box */
sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
width,height,subexpr);
if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
== NULL ) goto end_of_job; } /* and quit if failed */
/* -------------------------------------------------------------------------
draw frame, reset params, and return it to caller
-------------------------------------------------------------------------- */
/* --- draw border --- */
if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
== NULL ) goto end_of_job; /* draw border and quit if failed */
/* --- replace original image and raise baseline to accommodate frame --- */
framesp->image = bp; /* replace image with framed one */
if ( !iscompose ) /* simple border around subexpr */
framesp->baseline += fwidth; /* so just raise baseline */
else
framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
/* --- return framed subexpr to caller --- */
end_of_job:
return ( framesp ); /* return framed subexpr to caller */
} /* --- end-of-function rastfbox() --- */
/* ==========================================================================
* Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
* Purpose: no op -- flush \escape without error
* --------------------------------------------------------------------------
* Arguments: expression (I/O) char ** to first char of null-terminated
* string immediately following \escape to be
* flushed, and returning ptr immediately
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \fbox
* (unused, but passed for consistency)
* nargs (I) int containing number of {}-args after
* \escape to be flushed along with it
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
* Returns: ( subraster * ) NULL subraster ptr
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
subraster *rastnoop ( char **expression, int size, subraster *basesp,
int nargs, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), subexpr[8192]; /* flush dummy args eaten by \escape*/
subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */
/* --- flush accompanying args if necessary --- */
if ( nargs != NOVALUE /* not unspecified */
&& nargs > 0 ) /* and args to be flushed */
while ( --nargs >= 0 ) /* count down */
*expression = texsubexpr(*expression,subexpr,"{","}",0,0); /*flush arg*/
/* --- return null ptr to caller --- */
end_of_job:
return ( noopsp ); /* return NULL ptr to caller */
} /* --- end-of-function rastnoop() --- */
/* ==========================================================================
* Function: aalowpass ( rp, bytemap, grayscale )
* Purpose: calculates a lowpass anti-aliased bytemap
* for rp->bitmap, with each byte 0...grayscale-1
* --------------------------------------------------------------------------
* Arguments: rp (I) raster * to raster whose bitmap
* is to be anti-aliased
* bytemap (O) intbyte * to bytemap, calculated
* by applying lowpass filter to rp->bitmap,
* and returned (as you'd expect) in 1-to-1
* addressing correspondence with rp->bitmap
* grayscale (I) int containing number of grayscales
* to be calculated, 0...grayscale-1
* (should typically be given as 256)
* --------------------------------------------------------------------------
* Returns: ( int ) 1=success, 0=any error
* --------------------------------------------------------------------------
* Notes: o If the center point of the box being averaged is black,
* then the entire "average" is forced black (grayscale-1)
* regardless of the surrounding points. This is my attempt
* to avoid a "washed-out" appearance of thin (one-pixel-wide)
* lines, which would otherwise turn from black to a gray shade.
* o Also, while the weights for neighbor points are fixed,
* you may adjust the center point weight on the compile line.
* A higher weight sharpens the resulting anti-aliased image;
* lower weights blur it out more (but keep the "center" black
* as per the preceding note).
* ======================================================================= */
/* --- entry point --- */
int aalowpass (raster *rp, intbyte *bytemap, int grayscale)
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int status = 1; /* 1=success, 0=failure to caller */
pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
int irow=0, icol=0; /* rp->height, rp->width indexes */
int weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
int adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
int totwts = 0; /* sum of all weights in matrix */
int isforceavg = 1, /*force avg black if center black?*/
isminmaxwts = 1, /*use wts or #pts for min/max test */
blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- calculate total weights --- */
weights[4]= centerwt; /* weight for center point */
weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
totwts = centerwt + 4*(1+adjacentwt); /* tot is center plus neighbors */
/* -------------------------------------------------------------------------
Calculate bytemap as 9-point weighted average over bitmap
-------------------------------------------------------------------------- */
for ( irow=0; irowheight; irow++ )
for ( icol=0; icolwidth; icol++ )
{
int ipixel = icol + irow*(rp->width); /* center pixel index */
int jrow=0, jcol=0, /* box around ipixel */
bitval = 0, /* value of bit/pixel at jrow,jcol */
iscenter = 0, /* set true if center pixel black */
nadjacent=0, wadjacent=0, /* #adjacent black pixels, their wts*/
ngaps = 0, /* #gaps in 8 pixels around center */
iwt=(-1), sumwts=0; /* weights index, sum in-bound wts */
char adjmatrix[8]; /* adjacency "matrix" */
memset(adjmatrix,0,8); /* zero out adjacency matrix */
bytemap[ipixel] = 0; /* init pixel white */
/*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
for ( jrow=irow-1; jrow<=irow+1; jrow++ ) /* jrow = irow-1...irow+1 */
for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
{
int jpixel = jcol + jrow*(rp->width); /* averaging index */
iwt++; /*always bump weight index*/
if ( jrow<0 || jrow>=rp->height /* if row out pf bounds */
|| jcol<0 || jcol>=rp->width ) /* or col out of bounds */
continue; /* ignore this point */
bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
if ( bitval ) /* this is a black pixel */
{ if ( jrow==irow && jcol==icol ) /* and this is center point */
iscenter = 1; /* set flag for center point black */
else /* adjacent point black */
{ nadjacent++; /* bump adjacent black count */
adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
wadjacent += weights[iwt]; } /* sum weights for black pixels */
sumwts += weights[iwt]; /* and sum weights for all pixels */
} /* --- end-of-for(jrow,jcol) --- */
/* --- count gaps --- */
ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
for ( iwt=0; iwt<7; iwt++ ) /* clockwise around adjacency */
if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
ngaps /= 2; /*each gap has 2 black/white flips*/
/* --- anti-alias pixel, but leave it black if it was already black --- */
if ( isforceavg && iscenter ) /* force avg if center point black */
bytemap[ipixel] = grayscale-1; /* so force grayscale-1=black */
else /* center point not black */
if ( ngaps <= 2 ) /*don't darken checkerboarded pixel*/
{ bytemap[ipixel] = /* 0=white ... grayscale-1=black */
((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
if ( blackscale > 0 /* blackscale kludge turned on */
&& bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
/*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
if ( !iscenter ) /* apply min/maxadjacent test */
if ( isminmaxwts ) /* min/max refer to adjacent weights*/
{ if ( wadjacent < minadjacent /* wts of adjacent points too low */
|| wadjacent > maxadjacent ) /* or too high */
bytemap[ipixel] = 0; } /* so leave point white */
else /* min/max refer to #adjacent points*/
{ if ( nadjacent < minadjacent /* too few adjacent points black */
|| nadjacent > maxadjacent ) /* or too many */
bytemap[ipixel] = 0; } /* so leave point white */
} /* --- end-of-for(irow,icol) --- */
/* -------------------------------------------------------------------------
Back to caller with gray-scale anti-aliased bytemap
-------------------------------------------------------------------------- */
end_of_job:
return ( status );
} /* --- end-of-function aalowpass() --- */
/* ==========================================================================
* Function: aacolormap ( bytemap, nbytes, colors, colormap )
* Purpose: searches bytemap, returning a list of its discrete values
* in ascending order in colors[], and returning an "image"
* of bytemap (where vales are replaced by colors[]
* indexes) in colormap[].
* --------------------------------------------------------------------------
* Arguments: bytemap (I) intbyte * to bytemap containing
* grayscale values (usually 0=white
* through 255=black) for which colors[]
* and colormap[] will be constructed.
* nbytes (I) int containing #bytes in bytemap
* (usually just #rows * #cols)
* colors (O) intbyte * (to be interpreted as ints)
* returning a list of the discrete/different
* values in bytemap, in ascending value order
* colormap (O) intbyte * returning a bytemap "image",
* i.e., in one-to-one pixel correspondence
* with bytemap, but where the values have been
* replaced with corresponding colors[] indexes.
* --------------------------------------------------------------------------
* Returns: ( int ) #colors in colors[], or 0 for any error
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
int aacolormap ( intbyte *bytemap, int nbytes,
intbyte *colors, intbyte *colormap )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int ncolors = 0, /* #different values in bytemap */
igray, grayscale = 256; /* bytemap contains intbyte's */
intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/
int ibyte; /* bytemap/colormap index */
int isscale = 0; /* true to scale largest val to 255*/
/* -------------------------------------------------------------------------
Accumulate colors[] from values occurring in bytemap
-------------------------------------------------------------------------- */
/* --- initialization --- */
if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
== NULL ) goto end_of_job; /* signal error if malloc() failed */
memset(bytevalues,0,grayscale); /* zero out bytevalues */
/* --- now set 1's at offsets corresponding to values found in bytemap --- */
for ( ibyte=0; ibyte 1 ) /* and if not a "blank" raster */
if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
{
/* --- multiply each colors[] by factor that scales largest to 255 --- */
double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
for ( igray=1; igray5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
} /* --- end-of-if(isscale) --- */
/* -------------------------------------------------------------------------
Construct colormap
-------------------------------------------------------------------------- */
for ( ibyte=0; ibytepixsz, /* #bits per image pixel */
black1=1, black8=255, /* black for 1-bit, 8-bit pixels */
black = (pixsz==1? black1:black8), /* black value for our image */
scalefactor = (black1+black8-black), /* only scale 1-bit images */
iscenter = 0; /* set true if center pixel black */
/* --- grid dimensions and indexes --- */
int wtheight = weights->height, /* #rows in weight matrix */
wtwidth = weights->width, /* #cols in weight matrix */
imgheight = image->height, /* #rows in image */
imgwidth = image->width; /* #cols in image */
int wtrow, wtrow0 = wtheight/2, /* center row index for weights */
wtcol, wtcol0 = wtwidth/2, /* center col index for weights */
imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
/* --- rotated grid variables --- */
static int prevrotate = 0; /* rotate from previous call */
static double costheta = 1.0, /* cosine for previous rotate */
sintheta = 0.0; /* and sine for previous rotate */
double a = 1.0; /* default aspect ratio */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- refresh trig functions for rotate when it changes --- */
if ( rotate != prevrotate ) /* need new sine/cosine */
{ costheta = cos(((double)rotate)/57.29578); /*cos of rotate in radians*/
sintheta = sin(((double)rotate)/57.29578); /*sin of rotate in radians*/
prevrotate = rotate; } /* save current rotate as prev */
/* -------------------------------------------------------------------------
Calculate aapixel as weighted average over image points around ipixel
-------------------------------------------------------------------------- */
for ( wtrow=0; wtrow=0 && imgrow=0 && imgcol
* whose (mostly for demo), or
* (3) command-line arguments (mostly to test).
* If no input supplied, expression defaults to "f(x)=x^2",
* treated as test (input method 3).
* If args entered on command-line (or if no input supplied),
* output is (usually) human-viewable ascii raster images on
* stdout rather than the usual mime xbitmaps or gif images.
* --------------------------------------------------------------------------
* Command-Line Arguments:
* When running mimeTeX from the command-line, rather than
* from a browser, syntax is
* ./mimetex [-d ] dump gif to stdout
* [expression expression, e.g., x^2+y^2,
* |-f input_file] or read expression from file
* [-m msglevel] verbosity of debugging output
* [-s fontsize] default fontsize, 0-5
* -d Rather than ascii debugging output, mimeTeX dumps the
* actual gif (or xbitmap) to stdout, e.g.,
* ./mimetex -d x^2+y^2 > expression.gif
* creates a gif file containing an image of x^2+y^2
* -f Reads expression from input_file, and automatically
* assumes -d switch. The input_file may contain the
* expression on one line or spread out over many lines.
* MimeTeX will concatanate all lines from input_file
* to construct one long expression. Blanks, tabs, and
* newlines will just be ignored.
* -m 0-99, controls verbosity level for debugging output
* (usually used only while testing code).
* -s Font size, 0-5. As usual, the font size can
* also be specified in the expression by a leading
* preamble terminated by $, e.g., 3$f(x)=x^2 displays
* f(x)=x^2 at font size 3. Default font size is 2.
* --------------------------------------------------------------------------
* Exits: 0=success, 1=some error
* --------------------------------------------------------------------------
* Notes: o For an executable that emits mime xbitmaps, compile as
* cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
* or, alternatively, for an executable that emits gif images
* cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
* or for gif images with anti-aliasing
* cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
* See Notes at top of file for other compile-line -D options.
* o Move executable to your cgi-bin directory and either
* point your browser to it directly in the form
* http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
* or put a tag in your html document of the form
*
* where f(x)=x^2 (or any other expression) will be displayed
* either as a mime xbitmap or gif image (as per -D flag).
* ======================================================================= */
/* -------------------------------------------------------------------------
header files
-------------------------------------------------------------------------- */
/* --- standard headers --- */
#include
/* -------------------------------------------------------------------------
globals for gif and png callback functions
-------------------------------------------------------------------------- */
static raster *bitmap_raster=NULL; /* use 0/1 bitmap image or */
static intbyte *colormap_raster=NULL; /* anti-aliased color indexes */
/* --- anti-aliasing flag (needed by GetPixel() as well as main()) --- */
static int isaa = /* set anti-aliasing flag */
#ifdef AA /* if anti-aliasing requested */
1; /* turn flag on */
#else
0; /* else turn flag off */
#endif
/* -------------------------------------------------------------------------
logging data structure, and default data to be logged
-------------------------------------------------------------------------- */
/* --- logging data structure --- */
#define logdata struct logdata_struct /* "typedef" for logdata_struct*/
logdata
{
/* -----------------------------------------------------------------------
environment variable name, max #chars to display, min msglevel to display
------------------------------------------------------------------------ */
char *name; /* environment variable name */
int maxlen; /* max #chars to display */
int msglevel; /* min msglevel to display data */
} ; /* --- end-of-logdata_struct --- */
/* --- data logged by mimeTeX --- */
static logdata mimelog[] =
{
/* ------ variable ------ maxlen msglevel ----- */
{ "QUERY_STRING", 999, 4 },
{ "REMOTE_ADDR", 999, 3 },
{ "HTTP_REFERER", 999, 3 },
{ "REQUEST_URI", 999, 3 },
{ "HTTP_USER_AGENT", 999, 3 },
{ "HTTP_X_FORWARDED_FOR", 999, 3 },
{ NULL, -1, -1 } /* trailer record */
} ; /* --- end-of-mimelog[] --- */
/* --- entry point --- */
int main ( int argc, char *argv[] )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- expression to be emitted --- */
static char exprbuffer[16385] = "f(x)=x^2"; /* expression to be processed */
char *expression = exprbuffer; /* ptr to expression */
int size = NORMALSIZE; /* default font size */
char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
char *mimeprep(); /* preprocess expression */
char *argsignal = ARGSIGNAL; /* signals start of mimeTeX args */
int iarg=0, argnum=0, /*argv[] index for command-line args*/
nswitches = 0, /* number of -switches */
exprarg = 0, /* argv[] index for expression */
infilearg = 0; /* argv[] index for infile */
int isquery = 0, /* true if input from QUERY_STRING */
isformdata = 0, /* true if input from html form */
isdumpimage = 0; /* true to dump image on stdout */
/* --- rasterization --- */
subraster *rasterize(), *sp; /* rasterize expression */
raster *border_raster(), *bp; /* put a border around raster */
int type_raster(), type_bytemap(), /* screen dump function prototypes */
xbitmap_raster(); /* mime xbitmap output function */
/* --- http_referer --- */
char *referer = REFERER, /* http_referer must contain this */
*http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
int isstrstr(); /* search http_referer for referer */
/* --- gif --- */
#if defined(GIF)
int GetPixel(); /* feed pixels to gifsave library */
#endif
char *gif_outfile = (char *)NULL; /* gif output defaults to stdout */
/* --- anti-aliasing --- */
intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
colors[256]; /* grayscale vals in bytemap */
int aalowpass(), /*lowpass filter for anti-aliasing*/
grayscale = 256; /* 0-255 grayscales in 8-bit bytes */
int ncolors=2, /* #colors (2=b&w) */
aacolormap(); /* build colormap from bytemap */
/* --- messages --- */
char logfile[256] = "mimetex.log"; /*log queries if msglevel>=LOGLEVEL*/
int logger(); /* logs environ variables */
char *dashes = /* separates logfile entries */
"--------------------------------------------------------------------------";
char *copyright = /* copyright, gnu/gpl notice */
"+-----------------------------------------------------------------------+\n"
"|mimeTeX vers 1.40, Copyright(c) 2002-2004, John Forkosh Associates, Inc|\n"
"+-----------------------------------------------------------------------+\n"
"| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
"| and comes with absolutely no warranty whatsoever. |\n"
"+-----------------------------------------------------------------------+";
char *invalid_referer_msg = /* message to invalid http_referer */
"1$\\rm\\array{Please~read~www.forkosh.com/mimetex.html\\\\"
"and~install~your~own~mimetex.cgi\\\\Thank~you,~John~Forkosh}";
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
/* --- set global variables --- */
msgfp = stdout; /* for comamnd-line mode output */
/* ---
* check QUERY_STRING query for expression overriding command-line arg
* ------------------------------------------------------------------- */
if ( query != NULL ) /* check query string from environ */
if ( strlen(query) >= 1 ) /* caller gave us a query string */
{ strncpy(expression,query,16385); /* so use it as expression */
isquery = 1; } /* and set isquery flag */
/* ---
* process command-line input args (if not a query)
* ------------------------------------------------ */
if ( !isquery /* don't have an html query string */
|| argc > 1 ) /* or have command-line args, too */
{
if ( argsignal != NULL ) /* if compiled with -DARGSIGNAL */
while ( argc > ++iarg ) /* check each argv[] for argsignal */
if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
{ argnum = iarg; /* got it, start parsing next arg */
break; } /* stop looking for argsignal */
while ( argc > ++argnum ) /* check for switches and values, */
if ( *argv[argnum] == '-' ) /* got some '-' switch */
{
char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */
argnum++; /* arg following flag/switch is usually its value */
nswitches++; /* another switch on command line */
switch ( flag ) /* see what user wants to tell us */
{
/* --- ignore uninterpreted flag --- */
default: argnum--; break; /* unrecognized -flag */
/* --- adjustable program parameters (not checking input) --- */
case 'd': isdumpimage = 1; argnum--; break;
case 'e': isdumpimage = 1; gif_outfile=argv[argnum]; break;
case 'f': isdumpimage = 1; infilearg=argnum; break;
case 'm': msglevel = atoi(argv[argnum]); break;
case 'o': istransparent = 0; argnum--; break;
case 's': size = atoi(argv[argnum]); break;
} /* --- end-of-switch() --- */
} /* --- end-of-if(*argv[]=='-') --- */
else /* this arg not a -flag, so it must be... */
if ( infilearg == 0 ) /* no infile arg yet */
{ exprarg = argnum; /* take expression from this arg */
infilearg = (-1); } /* and set infilearg */
else /* we already have an infile arg */
; /* flush argument */
/* ---
* decide whether command-line input overrides query_string
* -------------------------------------------------------- */
if ( exprarg > 0 /* expression on command line */
&& infilearg <= 0 ) /* and not given in input file */
if ( !isquery /* no conflict if no query_string */
|| nswitches > 0 ) /* explicit -switch(es) also given */
{ strncpy(expression,argv[exprarg],16385); /* expression from command-line */
isquery = 0; } /* and not from a query_string */
/* ---
* read input file for expression
* ------------------------------ */
if ( infilearg > 0 ) /* have an -f arg */
{
FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
if ( infile != (FILE *)NULL ) /* opened input file successfully */
{ char instring[2049]; /* line from file */
isquery = 0; /* file input, not a query_string */
*expression = '\000'; /* start expresion as empty string */
while ( fgets(instring,2048,infile) != (char *)NULL ) /* read till eof*/
strcat(expression,instring); /* concat line to end of expression*/
fclose ( infile ); } /*close input file after reading expression*/
} /* --- end-of-if(infilearg>0) --- */
} /* --- end-of-if(!isquery) --- */
/* ---
* check for