--- loncom/cgi/mimeTeX/mimetex.c 2005/02/28 19:08:11 1.1
+++ loncom/cgi/mimeTeX/mimetex.c 2006/03/24 23:08:33 1.2
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright(c) 2002-2005, John Forkosh Associates, Inc. All rights reserved.
+ * Copyright(c) 2002-2006, 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,
@@ -58,7 +58,7 @@
* rastcat(sp1,sp2,isfree) concatanate sp1||sp2
* rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
* rastile(tiles,ntiles) create composite raster from tiles
- * rastsquash(sp1,sp2,xmin,ymin) calc #squash pixels sp1||sp2
+ * rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2
* --- raster "drawing" functions ---
* accent_subraster(accent,width,height) draw \hat\vec\etc
* arrow_subraster(width,height,drctn,isBig) left/right arrow
@@ -74,16 +74,19 @@
* type_raster(rp,fp) emit ascii dump of rp on file ptr fp
* type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
* xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp
+ * type_pbmpgm(rp,ptype,file) pbm or pgm image of rp to file
* cstruct_chardef(cp,fp,col1) emit C struct of cp on fp
* cstruct_raster(rp,fp,col1) emit C struct of rp on fp
* hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
* --- ancillary output functions ---
* emit_string(fp,col1,string,comment) emit string and C comment
+ * gftobitmap(rp) convert .gf-like pixmap to bitmap image
* ====================== Font Functions =======================
* --- font lookup functions ---
* get_symdef(symbol) returns mathchardef for symbol
* get_chardef(symdef,size) returns chardef for symdef,size
* get_charsubraster(symdef,size) wrap subraster around chardef
+ * get_symsubraster(symbol,size) returns subraster for symbol
* --- ancillary font functions ---
* get_baseline(gfdata) determine baseline (in our coords)
* get_delim(symbol,height,family) delim just larger than height
@@ -91,6 +94,7 @@
* ================= Tokenize/Parse Functions ==================
* texchar(expression,chartoken) retruns next char or \sequence
* texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
+ * texleft(expr,subexpr,maxsubsz,ldelim,rdelim) \left...\right
* texscripts(expression,subscript,superscript,which)get scripts
* --- ancillary parse functions ---
* isbrace(expression,braces,isescape) check for leading brace
@@ -110,6 +114,8 @@
* rastdispmath(expression,size,sp) scripts for displaymath
* --- table-driven handlers that rasterize... ---
* rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
+ * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
+ * rastmiddle(expression,size,basesp,arg1,arg2,arg3) \middle
* rastflags(expression,size,basesp,flag,value,arg3) set flag
* rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
* rastnewline(expression,size,basesp,arg1,arg2,arg3) \\
@@ -133,13 +139,19 @@
* rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
* rastinput(expression,size,basesp,arg1,arg2,arg3) \input
* rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter
+ * rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
+ * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
* rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
* --- helper functions for handlers ---
* rastopenfile(filename,mode) opens filename[.tex] in mode
- * rastreadfile(filename,tag,value) read between ...
+ * rasteditfilename(filename) edit filename (for security)
+ * rastreadfile(filename,islock,tag,value) read ...
* rastwritefile(filename,tag,value,isstrict)write...
- * timestamp( ) formats timestamp string (used by rastcounter)
- * dtoa(d,npts) double to comma-separated ascii
+ * calendar(year,month,day) formats one-month calendar string
+ * timestamp(tzdelta,ifmt) formats timestamp string
+ * tzadjust(tzdelta,year,month,day,hour) adjust date/time
+ * daynumber(year,month,day) #days since Monday, Jan 1, 1973
+ * dbltoa(d,npts) double to comma-separated ascii
* === Anti-alias completed raster (lowpass) or symbols (ss) ===
* aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap
* aapnm(rp,bytemap,grayscale) lowpass based on pnmalias.c
@@ -149,11 +161,13 @@
* aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
* PART1 ========================== Driver ===========================
* main(argc,argv) parses math expression and emits mime xbitmap
+ * CreateGifFromEq(expression,gifFileName) entry pt for win dll
* isstrstr(string,snippets,iscase) are any snippets in string?
* ismonth(month) is month current month ("jan"-"dec")?
* unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
* logger(fp,msglevel,logvars) logs environment variables
- * emitcache(cachefile) read cachefile and emit bytes to stdout
+ * emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
+ * readcachefile(cachefile,buffer) read cachefile into buffer
* md5str(instr) md5 hash library functions
* GetPixel(x,y) callback function for gifsave library
*
@@ -311,8 +325,9 @@ header files and macros
/* --- windows-specific header info --- */
#ifndef WINDOWS /* -DWINDOWS not supplied by user */
- #if defined(_WIN32) || defined(WIN32) \
- || defined(DJGPP) /* try to recognize windows */
+ #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
+ || defined(DJGPP) /* try to recognize windows compilers */ \
+ || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
#define WINDOWS /* signal windows */
#endif
#endif
@@ -328,6 +343,11 @@ header files and macros
#if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */
#define HAVE_SETMODE /* so we'll use setmode() */
#endif
+ #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
+ /* to show source file and line numbers where memory leaks occur... */
+ #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
+ #include /* and this debug library */
+ #endif
#define ISWINDOWS 1
#else
#define ISWINDOWS 0
@@ -396,20 +416,26 @@ header files and macros
#define TEXFONTS /* to include texfonts.h */
#endif
#include "mimetex.h"
+/* --- info needed when gif image returned in memory buffer --- */
+#ifdef GIF /* compiling along with gifsave.c */
+ extern int gifSize;
+ extern int maxgifSize;
+#else /* or just set dummy values */
+ static int gifSize=0, maxgifSize=0;
+#endif
/* -------------------------------------------------------------------------
adjustable default values
-------------------------------------------------------------------------- */
/* --- anti-aliasing parameters --- */
#ifndef CENTERWT
+ /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
/*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
- /*#define CENTERWT 6*/ /* anti-aliasing centerwt default */
- #define CENTERWT 32 /* anti-aliasing centerwt default */
+ #define CENTERWT 8 /* anti-aliasing centerwt default */
#endif
#ifndef ADJACENTWT
/*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
- /*#define ADJACENTWT 2*/ /* anti-aliasing adjacentwt default*/
- #define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/
+ #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
#endif
#ifndef CORNERWT
#define CORNERWT 1 /* anti-aliasing cornerwt default*/
@@ -473,12 +499,12 @@ other variables
#ifndef FGBLUE
#define FGBLUE (ISBLACKONWHITE?0:255)
#endif
-/* --- "squash" margin (0 means no squashing) --- */
-#ifndef SQUASHMARGIN
- #ifdef NOSQUASH
- #define SQUASHMARGIN 0
+/* --- "smash" margin (0 means no smashing) --- */
+#ifndef SMASHMARGIN
+ #ifdef NOSMASH
+ #define SMASHMARGIN 0
#else
- #define SQUASHMARGIN 3
+ #define SMASHMARGIN 3
#endif
#endif
/* --- textwidth --- */
@@ -486,7 +512,7 @@ other variables
#define TEXTWIDTH (400)
#endif
/* --- font "combinations" --- */
-#define CMSYEX (107) /* select CMSY10 _or_ CMEX10 */
+#define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
/* --- prefix prepended to all expressions --- */
#ifndef PREFIX
#define PREFIX "\000" /* default no prepended prefix */
@@ -522,6 +548,10 @@ other variables
#ifndef PATHPREFIX
#define PATHPREFIX "\000" /* paths relative mimetex.cgi */
#endif
+/* --- time zone delta t (in hours) --- */
+#ifndef TZDELTA
+ #define TZDELTA 0
+#endif
/* -------------------------------------------------------------------------
debugging and logging / error reporting
@@ -555,17 +585,20 @@ control flags and values
-------------------------------------------------------------------------- */
GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
-GLOBAL(int,istext,0); /* textmode if true,italics=2,bb=3 */
-GLOBAL(int,isstring ,0); /*pixmap is ascii string, not raster*/
+GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
+/*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
+GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
GLOBAL(int,fontsize,NORMALSIZE); /* current size */
GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
/*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
-GLOBAL(int,squashmargin,SQUASHMARGIN); /* minimum "squash" margin */
-GLOBAL(int,issquashdelta,1); /* true if squashmargin is a delta */
+GLOBAL(int,iscatspace,1); /* true to add space in rastcat() */
+GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
+GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
+GLOBAL(int,blanksignal,(-991234)); /*rastsmash signal right-hand blank*/
GLOBAL(int,istransparent,1); /*true to set background transparent*/
GLOBAL(int,fgred,FGRED);
GLOBAL(int,fggreen,FGGREEN);
@@ -655,6 +688,7 @@ if ( rp == (raster *)NULL ) /* malloc f
goto end_of_job; /* return error to caller */
rp->width = width; /* store width in raster struct */
rp->height = height; /* and store height */
+rp->format = 1; /* initialize as bitmap format */
rp->pixsz = pixsz; /* store #bits per pixel */
rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
/* --- allocate and initialize bitmap array --- */
@@ -775,6 +809,7 @@ cp->charnum = cp->location = 0; /* init
cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
cp->image.width = cp->image.height = 0; /* init raster dimensions */
+cp->image.format = 0; /* init raster format */
cp->image.pixsz = 0; /* and #bits per pixel */
cp->image.pixmap = NULL; /* init raster pixmap as null */
/* -------------------------------------------------------------------------
@@ -1190,11 +1225,10 @@ int base1 = sp1->baseline, /*baseline
pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
type2 = sp2->type; /* image type for right-hand */
int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
-int issquash = (squashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
- isopaque = (issquash?0:1), /* not oppaque if squashing */
- rastsquash(), isblank=0, nsquash=0, /* #cols to squash */
- oldsquashmargin = squashmargin; /* save original squashmargin */
-int blanksignal = (-991234); /*rastsquash signal right-hand blank*/
+int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
+ isopaque = (issmash?0:1), /* not oppaque if smashing */
+ rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
+ oldsmashmargin = smashmargin; /* save original smashmargin */
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 */
@@ -1211,36 +1245,37 @@ Initialization
if ( !isstring )
space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
else space = 1; /* space for ascii string */
-/* --- determine squash --- */
-if ( !isstring ) /* don't squash strings */
- if ( issquash ) { /* raster squash wanted */
- int maxsquash = rastsquash(sp1,sp2), /* calculate max squash space */
- margin = squashmargin; /* init margin without delta */
+if ( !iscatspace ) space=0; /* spacing explicitly turned off */
+/* --- determine smash --- */
+if ( !isstring ) /* don't smash strings */
+ if ( issmash ) { /* raster smash wanted */
+ int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
+ margin = smashmargin; /* init margin without delta */
if ( (1 && smash1 && smash2) /* concatanating two chars */
|| (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )
- /*maxsquash = 0;*/ /* turn off squash */
- margin = max2(space-1,0); /* force small squashmargin */
+ /*maxsmash = 0;*/ /* turn off smash */
+ margin = max2(space-1,0); /* force small smashmargin */
else /* adjust for delta if images */
- if ( issquashdelta ) /* squashmargin is a delta value */
+ if ( issmashdelta ) /* smashmargin is a delta value */
margin += fontsize; /* add displaystyle base to margin */
- if ( maxsquash == blanksignal ) /* sp2 is intentional blank */
+ if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
isblank = 1; /* set blank flag signal */
else /* see how much extra space we have*/
- if ( maxsquash > margin ) /* enough space for adjustment */
- nsquash = maxsquash-margin; /* make adjustment */
- if ( msgfp!=NULL && msglevel>=99 ) /* display squash results */
- { fprintf(msgfp,"rastcat> maxsquash=%d, margin=%d, nsquash=%d\n",
- maxsquash,margin,nsquash);
+ if ( maxsmash > margin ) /* enough space for adjustment */
+ nsmash = maxsmash-margin; /* make adjustment */
+ if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
+ { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
+ maxsmash,margin,nsmash);
fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
(symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
fflush(msgfp); }
- } /* --- end-of-if(issquash) --- */
+ } /* --- end-of-if(issmash) --- */
/* --- determine height, width and baseline of composite raster --- */
if ( !isstring )
{ height = max2(base1+1,base2+1) /* max height above baseline */
+ max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
- width = width1+width2 + space-nsquash; /*add widths and space-squash*/
- width = max3(width,width1,width2); } /* don't "over-squash" composite */
+ width = width1+width2 + space-nsmash; /*add widths and space-smash*/
+ width = max3(width,width1,width2); } /* don't "over-smash" composite */
else /* ascii string */
{ height = 1; /* default */
width = width1 + width2 + space - 1; } /* no need for two nulls */
@@ -1254,8 +1289,8 @@ if ( msgfp!=NULL && msglevel>=9999 ) /*
height2,width2,pixsz2,base2);
type_raster(sp2->image,msgfp); /* display right-hand raster */
fprintf(msgfp,
- "rastcat> Composite ht,width,squash,pixsz,base = %d,%d,%d,%d,%d\n",
- height,width,nsquash,pixsz,base);
+ "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
+ height,width,nsmash,pixsz,base);
fflush(msgfp); } /* flush msgfp buffer */
/* -------------------------------------------------------------------------
allocate concatted composite subraster
@@ -1273,7 +1308,8 @@ if ( (sp=new_subraster(width,height,pixs
/* --- initialize subraster parameters --- */
/* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/
if ( !isstring )
- sp->type = type2;/*(type1==type2?type2:IMAGERASTER);*/ /*string or image*/
+ sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
+ (type2!=CHARASTER? type2 : (type1!=CHARASTER?type1:STRINGRASTER));
else
sp->type = ASCIISTRING; /* concatted ascii string */
sp->symdef = symdef2; /* rightmost char is sp2 */
@@ -1291,7 +1327,7 @@ if ( msgfp!=NULL && msglevel>=9999 )
fflush(msgfp); } /* flush msgfp buffer */
if ( !isstring )
rastput (rp, sp1->image, base-base1, /* overlay left-hand */
- max2(0,nsquash-width1), 1); /* plus any residual squash space */
+ max2(0,nsmash-width1), 1); /* plus any residual smash space */
else
memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1); /*init left string*/
if ( msgfp!=NULL && msglevel>=9999 )
@@ -1299,7 +1335,7 @@ if ( msgfp!=NULL && msglevel>=9999 )
fflush(msgfp); } /* flush msgfp buffer */
if ( !isstring )
rastput (rp, sp2->image, base-base2, /* overlay right-hand */
- max2(0,width1+space-nsquash), isopaque); /* minus any squashed space */
+ max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
else
{ strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
@@ -1316,7 +1352,7 @@ if ( isfree > 0 ) /* caller wants inpu
Back to caller with pointer to concatted subraster or with null for error
-------------------------------------------------------------------------- */
end_of_job:
- squashmargin = oldsquashmargin; /* reset original squashmargin */
+ smashmargin = oldsmashmargin; /* reset original smashmargin */
return ( sp ); /* back with subraster or null ptr */
} /* --- end-of-function rastcat() --- */
@@ -1487,26 +1523,26 @@ end_of_job:
/* ==========================================================================
- * Function: rastsquash ( sp1, sp2 )
+ * Function: rastsmash ( sp1, sp2 )
* Purpose: When concatanating sp1||sp2, calculate #pixels
- * we can "squash sp2 left"
+ * we can "smash sp2 left"
* --------------------------------------------------------------------------
* Arguments: sp1 (I) subraster * to left-hand raster
* sp2 (I) subraster * to right-hand raster
* --------------------------------------------------------------------------
- * Returns: ( int ) max #pixels we can squash sp1||sp2,
+ * Returns: ( int ) max #pixels we can smash sp1||sp2,
* or "blanksignal" if sp2 intentionally blank,
* or 0 for any error.
* --------------------------------------------------------------------------
* Notes: o
* ======================================================================= */
/* --- entry point --- */
-int rastsquash ( subraster *sp1, subraster *sp2 )
+int rastsmash ( subraster *sp1, subraster *sp2 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-int nsquash = 0; /* #pixels to squash sp1||sp2 */
+int nsmash = 0; /* #pixels to smash sp1||sp2 */
int base1 = sp1->baseline, /*baseline for left-hand subraster*/
height1 = (sp1->image)->height, /* height for left-hand subraster */
width1 = (sp1->image)->width, /* width for left-hand subraster */
@@ -1520,7 +1556,6 @@ int base = max2(base1,base2), /* max asc
int irow1=0,irow2=0, icol=0; /* row,col indexes */
int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
-int blanksignal = (-991234); /*rastsquash signal right-hand blank*/
int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
int type_raster(); /* display debugging output */
/* -------------------------------------------------------------------------
@@ -1528,9 +1563,10 @@ find right edge of sp1 and left edge of
-------------------------------------------------------------------------- */
/* --- check args --- */
if ( isstring ) goto end_of_job; /* ignore string rasters */
-if ( height > 1023 ) goto end_of_job; /* don't try to squash huge image */
+if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
+if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
- goto end_of_job; /* don't squash intentional blank */
+ goto end_of_job; /* don't smash intentional blank */
/* --- init firstcol1[], firstcol2[] --- */
for ( irow1=0; irow1type == blanksignal ) /*blanksignal was propagated to us*/
- goto end_of_job; /* don't squash intentional blank */
+ goto end_of_job; /* don't smash intentional blank */
/* --- set firstcol1[] indicating right edge of sp1 --- */
for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
@@ -1555,7 +1591,7 @@ for ( irow1=top1; irow1<=bot1; irow1++ )
nfirst1++; /* bump #rows containing set pixels*/
break; } /* and go on to next row */
if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
- goto end_of_job; /* don't squash intentional blanks */
+ goto end_of_job; /* don't smash intentional blanks */
/* -------------------------------------------------------------------------
find minimum separation
-------------------------------------------------------------------------- */
@@ -1568,29 +1604,29 @@ for ( irow2=top2; irow2<=bot2; irow2++ )
if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
if ( ds >= smin ) continue; /* min unchanged */
- if ( dy>squashmargin && dxsmashmargin && dx= 99 ) /* display for debugging */
- { fprintf(msgfp,"rastsquash> nsquash=%d, squashmargin=%d\n",
- nsquash,squashmargin);
+ { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
+ nsmash,smashmargin);
if ( msglevel >= 999 ) /* also display rasters */
- { fprintf(msgfp,"rastsquash>left-hand image...\n");
+ { fprintf(msgfp,"rastsmash>left-hand image...\n");
if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
- fprintf(msgfp,"rastsquash>right-hand image...\n");
+ fprintf(msgfp,"rastsmash>right-hand image...\n");
if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
fflush(msgfp); }
- return ( nsquash ); /* back with #squash pixels */
-} /* --- end-of-function rastsquash() --- */
+ return ( nsmash ); /* back with #smash pixels */
+} /* --- end-of-function rastsmash() --- */
/* ==========================================================================
@@ -1622,6 +1658,7 @@ raster *new_raster(), *rp=NULL; /*raster
subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
int delete_raster(), delete_subraster(); /*free allocated raster on err*/
int line_raster(), /* draws lines */
+ rule_raster(), /* draw solid boxes */
thickness = 1; /* line thickness */
/*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
/* --- other working info --- */
@@ -1653,28 +1690,38 @@ switch ( accent )
/* --- bar request --- */
case UNDERBARACCENT:
case BARACCENT:
- thickness = height-1; /* adjust thickness */
+ thickness = 1; /*height-1;*/ /* adjust thickness */
if ( accent == BARACCENT ) /* bar is above expression */
- line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/
+ { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
+ line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
else /* underbar is below expression */
- line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/
+ { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
+ line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
break;
/* --- dot request --- */
case DOTACCENT:
thickness = height-1; /* adjust thickness */
- line_raster(rp,0,width/2,1,(width/2)+1,thickness); /* centered dot */
+ /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
+ rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
break;
/* --- ddot request --- */
case DDOTACCENT:
thickness = height-1; /* adjust thickness */
- col0 = max2(width/3-(thickness-1),0); /* one-third of width */
- col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/
- line_raster(rp,0,col0,1,col0+1,thickness); /* set a dot at 1st third*/
- line_raster(rp,0,col1,1,col1+1,thickness); /* and another at 2nd */
+ col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
+ col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
+ if ( col0+thickness >= col1 ) /* dots overlap */
+ { col0 = max2(col0-1,0); /* try moving left dot more left */
+ col1 = min2(col1+1,width-thickness); } /* and right dot right */
+ if ( col0+thickness >= col1 ) /* dots _still_ overlap */
+ thickness = max2(thickness-1,1); /* so try reducing thickness */
+ /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
+ /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
+ rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
+ rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
break;
/* --- hat request --- */
case HATACCENT:
- thickness = (width<=12? 2 : 3); /* adjust thickness */
+ thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/
line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
break;
@@ -1724,7 +1771,8 @@ switch ( accent )
if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
!= NULL ) /* have tilde with space below it */
{ rp = sp->image; /* "extract" raster with bitmap */
- free((void *)sp); } /* and free subraster "envelope" */
+ free((void *)sp); /* and free subraster "envelope" */
+ leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
break;
} /* --- end-of-outer-switch(accent) --- */
/* -------------------------------------------------------------------------
@@ -1774,7 +1822,7 @@ Allocations and Declarations
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int irow, midrow=height/2; /* index, midrow is arrowhead apex */
-int icol, thickness=(height>15?2:1); /* arrowhead thickness and index */
+int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
int ipix, /* raster pixmap[] index */
npix = width*height; /* #pixels malloced in pixmap[] */
@@ -1850,7 +1898,7 @@ Allocations and Declarations
subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
int rule_raster(); /* draw arrow line */
int icol, midcol=width/2; /* index, midcol is arrowhead apex */
-int irow, thickness=(width>15?2:1); /* arrowhead thickness and index */
+int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
int ipix, /* raster pixmap[] index */
npix = width*height; /* #pixels malloced in pixmap[] */
@@ -1892,7 +1940,7 @@ for ( icol=0; icolimage)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
- } /* --- end-of-for(irow) --- */
+ } /* --- end-of-for(icol) --- */
end_of_job:
return ( arrowsp ); /*back to caller with arrow or NULL*/
} /* --- end-of-function uparrow_subraster() --- */
@@ -1913,6 +1961,7 @@ end_of_job:
* height (I) int containing number of rows for rule
* type (I) int containing 0 for solid rule,
* 1 for horizontal dashes, 2 for vertical
+ * 3 for solid rule with corners removed
* --------------------------------------------------------------------------
* Returns: ( int ) 1 if rule drawn okay,
* or 0 for any error.
@@ -1928,8 +1977,8 @@ int rule_raster ( raster *rp, int top, i
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-int irow, icol; /* indexes over rp raster */
-int ipix, /* raster pixmap[] index */
+int irow=0, icol=0; /* indexes over rp raster */
+int ipix = 0, /* raster pixmap[] index */
npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
int isfatal = 0; /* true to abend on out-of-bounds error */
int hdash=1, vdash=2; /* type for horizontal, vertical dashes */
@@ -1942,6 +1991,8 @@ if ( rp == (raster *)NULL ) /* no raster
if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
rp = workingbox->image; /* use workingbox if possible */
else return ( 0 ); /* otherwise signal error to caller */
+if ( type == 3 ) /* remove corners of solid box */
+ if ( width<3 || height<3 ) type=0; /* too small to remove corners */
/* -------------------------------------------------------------------------
Fill line/box
-------------------------------------------------------------------------- */
@@ -1952,6 +2003,12 @@ for ( irow=top; irowwidth + left - 1; /*first pixel preceding icol*/
for ( icol=left; icol=left+width-1) /* top-right corner */
+ || (irow>=top+height-1 && icol==left) /* bottom-left corner */
+ || (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
+ isdraw = 0; else isdraw = 1; /*set isdraw to skip corner*/
if ( type == hdash ) /*set isdraw for horiz dash*/
isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
if ( ++ipix >= npix ) /* bounds check failed */
@@ -2008,17 +2065,18 @@ int line_raster ( raster *rp, int row0,
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-int irow, icol, /* indexes over rp raster */
+int irow=0, icol=0, /* indexes over rp raster */
locol=col0, hicol=col1, /* col limits at irow */
lorow=row0, hirow=row1; /* row limits at icol */
-int ipix, /* raster pixmap[] index */
- npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
+int width=rp->width, height=rp->height; /* dimensions of input raster */
+int ipix = 0, /* raster pixmap[] index */
+ npix = width*height; /* #pixels malloced in rp->pixmap[] */
int isfatal = 0; /* true to abend on out-of-bounds error */
int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
- xcol, xrow; /* calculated col at irow, or row at icol */
+ xcol=0, xrow=0; /* calculated col at irow, or row at icol */
double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
@@ -2039,10 +2097,16 @@ if ( msgfp!=NULL && msglevel>=29 ) /* d
"\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\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 ); }
+if ( isrecurse ) { /* drawing lines recursively */
+ for ( irow=0; irow(-0.001) && xcol0>(-0.001) /*check line inside raster*/
+ && xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
+ line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
+ return ( 1 ); }
/* --- set params for horizontal line or vertical bar --- */
if ( isline ) /*interpret row as top row*/
row1 = row0 + (thickness-1); /* set bottom row for line */
@@ -2588,6 +2652,8 @@ static char display_chars[16] = /* displ
char scanline[133]; /* ascii image for one scan line */
int scan_width; /* #chars in scan (<=display_width)*/
int irow, locol,hicol=(-1); /* height index, width indexes */
+raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
+int delete_raster(); /*free bitmap converted for display*/
/* --------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
@@ -2595,7 +2661,7 @@ initialization
if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
/* --- check for ascii string --- */
if ( isstring /* pixmap has string, not raster */
-|| (1 && rp->height==1) ) /* infer input rp is a string */
+|| (0 && rp->height==1) ) /* infer input rp is a string */
{
char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
int width = strlen(string); /* #chars in ascii string */
@@ -2609,7 +2675,11 @@ if ( isstring /* pixmap has string, n
/* --------------------------------------------------------------------------
display ascii dump of bitmap image (in segments if display_width < rp->width)
-------------------------------------------------------------------------- */
-while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
+if ( rp->format == 2 /* input is .gf-formatted */
+|| rp->format == 3 )
+ bitmaprp = gftobitmap(rp); /* so convert it for display */
+if ( bitmaprp != NULL ) /* if we have image for display */
+ while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
{
/* --- set hicol for this pass (locol set above) --- */
hicol += display_width; /* show as much as display allows */
@@ -2626,11 +2696,11 @@ while ( (locol=hicol+1) < rp->width ) /*
lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
/* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
for ( ipix=0; ipixpixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
- scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.');
+ if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
+ scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
else /* should have a bytemap */
- if ( rp->pixsz == 8 ) /* double-check pixsz for bytemap */
- { int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/
+ if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
+ { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
/* --- display completed scan line --- */
@@ -2640,6 +2710,10 @@ while ( (locol=hicol+1) < rp->width ) /*
/* -------------------------------------------------------------------------
Back to caller with 1=okay, 0=failed.
-------------------------------------------------------------------------- */
+if ( rp->format == 2 /* input was .gf-format */
+|| rp->format == 3 )
+ if ( bitmaprp != NULL ) /* and we converted it for display */
+ delete_raster(bitmaprp); /* no longer needed, so free it */
return ( 1 );
} /* --- end-of-function type_raster() --- */
@@ -2782,6 +2856,141 @@ return ( 1 );
/* ==========================================================================
+ * Function: type_pbmpgm ( rp, ptype, file )
+ * Purpose: Write pbm or pgm image of rp to file
+ * --------------------------------------------------------------------------
+ * Arguments: rp (I) ptr to raster struct for which
+ * a pbm/pgm file is to be written.
+ * ptype (I) int containing 1 for pbm, 2 for pgm, or
+ * 0 to determine ptype from values in rp
+ * file (I) ptr to null-terminated char string
+ * containing name of fuke to be written
+ * (see notes below).
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) total #bytes written,
+ * or 0 for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o (a) If file==NULL, output is written to stdout;
+ * (b) if *file=='\000' then file is taken as the
+ * address of an output buffer to which output
+ * is written (and is followed by a terminating
+ * '\0' which is not counted in #bytes returned);
+ * (c) otherwise file is the filename (opened and
+ * closed internally) to which output is written,
+ * except that any final .ext extension is replaced
+ * by .pbm or .pgm depending on ptype.
+ * ======================================================================= */
+/* --- entry point --- */
+int type_pbmpgm ( raster *rp, int ptype, char *file )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+int isokay=0, nbytes=0; /* completion flag, total #bytes written */
+int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
+int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
+ ngray = 0; /* #gray scale values */
+FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
+char outline[1024], outfield[256], /* output line, field */
+ cr[16] = "\n\000"; /* cr at end-of-line */
+int maxlinelen = 70; /* maximum allowed line length */
+int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
+static char
+ *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
+ *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
+ *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
+/* -------------------------------------------------------------------------
+check input, determine grayscale, and set up output file if necessary
+-------------------------------------------------------------------------- */
+/* --- check input args --- */
+if ( rp == NULL ) goto end_of_job; /* no input raster provided */
+if ( ptype != 0 ) /* we'll determine ptype below */
+ if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
+/* --- determine largest (and smallest) value in pixmap --- */
+for ( irow=0; irowheight; irow++ ) /* for each row, top-to-bottom */
+ for ( jcol=0; jcolwidth; jcol++ ) /* for each col, left-to-right */
+ { int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
+ pixmin = min2(pixmin,pixval); /* new minimum */
+ pixmax = max2(pixmax,pixval); } /* new maximum */
+ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
+if ( ptype == 0 ) /* caller wants us to set ptype */
+ ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
+/* --- open output file if necessary --- */
+if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
+else if ( *file != '\000' ) { /* explicit filename provided, so...*/
+ char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
+ strncpy(fname,file,255); /* local copy of file name */
+ fname[255] = '\000'; /* make sure it's null terminated */
+ if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
+ strcat(fname,suffix[ptype]); /* so add extension */
+ else /* we already have an extension */
+ strcpy(pdot,suffix[ptype]); /* so replace original extension */
+ if ( (fp = fopen(fname,mode[ptype])) /* open output file */
+ == (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
+ } /* --- ens-of-if(*file!='\0') --- */
+/* -------------------------------------------------------------------------
+format and write header
+-------------------------------------------------------------------------- */
+/* --- format header info --- */
+*outline = '\000'; /* initialize line buffer */
+strcat(outline,magic[ptype]); /* begin file with "magic number" */
+strcat(outline,cr); /* followed by cr to end line */
+sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
+strcat(outline,outfield); /* add width and height to header */
+strcat(outline,cr); /* followed by cr to end line */
+if ( ptype == 2 ) /* need max grayscale value */
+ { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
+ strcat(outline,outfield); /* add max value to header */
+ strcat(outline,cr); } /* followed by cr to end line */
+/* --- write header to file or memory buffer --- */
+if ( fp == NULL ) /* if we have no open file... */
+ strcat(file,outline); /* add header to caller's buffer */
+else /* or if we have an open file... */
+ if ( fputs(outline,fp) /* try writing header to open file */
+ == EOF ) goto end_of_job; /* return with error if failed */
+nbytes += strlen(outline); /* bump output byte count */
+/* -------------------------------------------------------------------------
+format and write pixels
+-------------------------------------------------------------------------- */
+*outline = '\000'; /* initialize line buffer */
+for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
+ for ( jcol=0; jcolwidth; jcol++ ) { /* for each col, left-to-right */
+ /* --- format value at irow,jcol--- */
+ *outfield = '\000'; /* init empty field */
+ if ( irow < rp->height ) { /* check row index */
+ int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
+ if ( ptype == 1 ) /* pixval must be 1 or 0 */
+ pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
+ sprintf(outfield,"%d ",pixval); } /* format pixel value */
+ /* --- write line if this value won't fit on it (or last line) --- */
+ if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
+ || irow >= rp->height ) { /* force writing last line */
+ strcat(outline,cr); /* add cr to end current line */
+ if ( fp == NULL ) /* if we have no open file... */
+ strcat(file,outline); /* add header to caller's buffer */
+ else /* or if we have an open file... */
+ if ( fputs(outline,fp) /* try writing header to open file */
+ == EOF ) goto end_of_job; /* return with error if failed */
+ nbytes += strlen(outline); /* bump output byte count */
+ *outline = '\000'; /* re-initialize line buffer */
+ } /* --- end-of-if(strlen>=maxlinelen) --- */
+ if ( irow >= rp->height ) break; /* done after writing last line */
+ /* --- concatanate value to line -- */
+ strcat(outline,outfield); /* concatanate value to line */
+ } /* --- end-of-for(jcol,irow) --- */
+isokay = 1; /* signal successful completion */
+/* -------------------------------------------------------------------------
+Back to caller with total #bytes written, or 0=failed.
+-------------------------------------------------------------------------- */
+end_of_job:
+ if ( fp != NULL /* output written to an open file */
+ && fp != stdout ) /* and it's not just stdout */
+ fclose(fp); /* so close file before returning */
+ return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
+} /* --- end-of-function type_pbmpgm() --- */
+
+
+/* ==========================================================================
* Function: cstruct_chardef ( cp, fp, col1 )
* Purpose: Emit a C struct of cp on fp, starting in col1.
* --------------------------------------------------------------------------
@@ -2813,7 +3022,7 @@ emit charnum, location, name / hirow
sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
emit_string ( fp, col1, field, "character number, location");
/* --- toprow, topleftcol, botrow, botleftcol --- */
-sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
+sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
@@ -2855,9 +3064,9 @@ int emit_string(); /* emit a string and
/* -------------------------------------------------------------------------
emit width and height
-------------------------------------------------------------------------- */
-sprintf(field,"{ %2d, %3d,%2d, %s\n", /* format width,height,pixsz */
- rp->width,rp->height,rp->pixsz,typecast);
-emit_string ( fp, col1, field, "widthxheight, pixsz,map...");
+sprintf(field,"{ %2d, %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
+ rp->width,rp->height,rp->format,rp->pixsz,typecast);
+emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
/* -------------------------------------------------------------------------
emit bitmap and closing brace, and return to caller
-------------------------------------------------------------------------- */
@@ -2894,7 +3103,8 @@ int hex_bitmap ( raster *rp, FILE *fp, i
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */
+int ibyte, /* pixmap[ibyte] index */
+ nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
char stub[64]=" ";/* col1 leading blanks */
int linewidth = 64, /* (roughly) rightmost column */
colwidth = (isstr? 4:5); /* #cols required for each byte */
@@ -3000,6 +3210,85 @@ return ( 1 );
/* ==========================================================================
+ * Function: gftobitmap ( gf )
+ * Purpose: convert .gf-like pixmap to bitmap image
+ * --------------------------------------------------------------------------
+ * Arguments: gf (I) raster * to struct in .gf-format
+ * --------------------------------------------------------------------------
+ * Returns: ( raster * ) image-format raster * if successful,
+ * or NULL for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+raster *gftobitmap ( raster *gf )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
+int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
+int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
+ ibit=0, bitval=0; /* bitmap index, bit value */
+int isrepeat = 1, /* true to process repeat counts */
+ repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
+ nrepeats=0, irepeat=0, /* scan line repeat count,index */
+ wbits = 0; /* count bits to width of scan line*/
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+/* --- check args --- */
+if ( gf == NULL ) goto end_of_job; /* input raster not provided */
+format = gf->format; /* 2 or 3 */
+if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
+ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
+/* --- allocate output raster with proper dimensions for bitmap --- */
+width=gf->width; height=gf->height; /* dimensions of raster */
+if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
+== NULL ) goto end_of_job; /* quit if failed to allocate */
+totbits = width*height; /* total #bits in image */
+/* -------------------------------------------------------------------------
+fill bitmap
+-------------------------------------------------------------------------- */
+for ( icount=0,bitval=0; icountpixmap,icount)); /*#bits to set*/
+ if ( isrepeat /* we're proxessing repeat counts */
+ && nbits == repeatcmds[format-2] ) /* and repeat opcode found */
+ if ( nrepeats == 0 ) /* recursive repeat is error */
+ { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
+ nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
+ icount += 2; } /* bump byte/nibble count */
+ else /* some internal error occurred */
+ if ( msgfp!=NULL && msglevel>=1 ) /* report error */
+ fprintf(msgfp,"gftobitmap> found embedded repeat command\n");
+ if ( 0 )
+ fprintf(stdout,
+ "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
+ icount,bitval,nbits,ibit,totbits);
+ for ( ; nbits>0; nbits-- ) /* count down */
+ { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
+ for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
+ if ( bitval == 1 ) /* set pixel */
+ { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
+ else /* clear pixel */
+ { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
+ if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
+ ibit++; } /* bump bit index */
+ bitval = 1-bitval; /* flip bit value */
+ if ( wbits >= width ) { /* completed repeats */
+ ibit += nrepeats*width; /*bump bit count past repeated scans*/
+ if ( wbits > width ) /* out-of alignment error */
+ if ( msgfp!=NULL && msglevel>=1 ) /* report error */
+ fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
+ wbits = nrepeats = 0; } /* reset repeat counts */
+ } /* --- end-of-for(icount) --- */
+end_of_job:
+ return ( rp ); /* back to caller with image */
+} /* --- end-of-function gftobitmap() --- */
+
+
+/* ==========================================================================
* Function: get_symdef ( symbol )
* Purpose: returns mathchardef struct for symbol
* --------------------------------------------------------------------------
@@ -3030,22 +3319,27 @@ int symlen = strlen(symbol), /* length o
deflen, minlen=9999; /*length of shortest matching symdef*/
int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
+int family = fontinfo[fontnum].family; /* current font family */
static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
+ /* --- see table on page 536 in TLC2 --- */
{"\\int", "\\Bigint"},
{"\\oint", "\\Bigoint"},
{"\\sum", "\\Bigsum"},
{"\\prod", "\\Bigprod"},
{"\\coprod", "\\Bigcoprod"},
- {"\\cup", "\\Bigcup"},
- {"\\sqcup", "\\Bigsqcup"},
- {"\\cap", "\\Bigcap"},
- {"\\sqcap", "\\sqcap"}, /* don't have \Bigsqcap */
- {"\\odot", "\\Bigodot"},
- {"\\oplus", "\\Bigoplus"},
- {"\\otimes", "\\Bigotimes"},
- {"\\uplus", "\\Biguplus"},
- {"\\wedge", "\\Bigwedge"},
- {"\\vee", "\\Bigvee"},
+ /* --- must be 'big' when related to similar binary operators --- */
+ {"\\bigcup", "\\Bigcup"},
+ {"\\bigsqcup", "\\Bigsqcup"},
+ {"\\bigcap", "\\Bigcap"},
+ /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
+ {"\\bigodot", "\\Bigodot"},
+ {"\\bigoplus", "\\Bigoplus"},
+ {"\\bigominus", "\\ominus"},
+ {"\\bigotimes", "\\Bigotimes"},
+ {"\\bigoslash", "\\oslash"},
+ {"\\biguplus", "\\Biguplus"},
+ {"\\bigwedge", "\\Bigwedge"},
+ {"\\bigvee", "\\Bigvee"},
{NULL, NULL} };
/* -------------------------------------------------------------------------
If in \displaystyle mode, first xlate int to Bigint, etc.
@@ -3070,26 +3364,33 @@ for ( idef=0; ;idef++ ) /* until trail
if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
else /* check against caller's symbol */
if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
- if ( symdefs[idef].handler != NULL /* mode irrelevant for directives */
- || istext==0 /* mathmode, so use first match */
- || (istext==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
- || (istext==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/
- || (istext==3 && symdefs[idef].family==BBOLD10) /*textmode && bb text*/
- || (istext!=3 && !alphasym) ) /* not bb and not alpha */
+ if (fontnum==0 /* mathmode, so check every match */
+ || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
+ || symdefs[idef].handler!=NULL)) /* or text mode and directive */
+ || (symdefs[idef].family==family /* have correct family */
+ && symdefs[idef].handler==NULL) ) /* and not a handler collision */
+#if 0
+ || (fontnum==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
+ || (fontnum==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/
+ || (fontnum==3 && symdefs[idef].family==BBOLD10 /*textmode && bb text*/
+ && symdefs[idef].handler==NULL)
+ || (fontnum==4 && symdefs[idef].family==CMMIB10 /*textmode && bf text*/
+ && symdefs[idef].handler==NULL) )
+#endif
if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
{ bestdef = idef; /* save index of new best match */
if ( (minlen = deflen) /* and save its len for next test */
== symlen ) break; } /*perfect match, so return with it*/
if ( bestdef < 0 ) /* failed to look up symbol */
- if ( istext != 0 ) /* we're in a restricted font mode */
- { int wastext = istext; /* save current mode */
- mathchardef *symdef = NULL; /* lookup result with istext=0 */
- istext = 0; /*try to look up symbol in any font*/
- symdef = get_symdef(symbol); /* repeat lookup with istext=0 */
- istext = wastext; /* reset font mode */
- return symdef; } /* caller gets istext=0 lookup */
+ if ( fontnum != 0 ) /* we're in a restricted font mode */
+ { int oldfontnum = fontnum; /* save current font family */
+ mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
+ fontnum = 0; /*try to look up symbol in any font*/
+ symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
+ fontnum = oldfontnum; /* reset font family */
+ return symdef; } /* caller gets fontnum=0 lookup */
if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
- { fprintf(msgfp,"get_symdefs> symbol=%s matches symtable[%d]=%s\n",
+ { fprintf(msgfp,"get_symdef> symbol=%s matches symtable[%d]=%s\n",
symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
fflush(msgfp); }
return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/
@@ -3102,7 +3403,7 @@ return ( (bestdef<0? NULL : &(symdefs[be
* --------------------------------------------------------------------------
* Arguments: symdef (I) mathchardef * corresponding to symbol
* whose corresponding chardef is wanted
- * size (I) int containing 0-4 for desired size
+ * size (I) int containing 0-5 for desired size
* --------------------------------------------------------------------------
* Returns: ( chardef * ) pointer to struct defining symbol at size,
* or NULL for any error
@@ -3197,9 +3498,9 @@ return ( gfdata ); /*ptr to chardef fo
* Purpose: returns new subraster ptr containing
* data for symdef at given size
* --------------------------------------------------------------------------
- * Arguments: symdef (I) mathchardef * corresponding to symbol
- * whose corresponding chardef is wanted
- * size (I) int containing 0-4 for desired size
+ * Arguments: symdef (I) mathchardef * corresponding to symbol whose
+ * corresponding chardef subraster is wanted
+ * size (I) int containing 0-5 for desired size
* --------------------------------------------------------------------------
* Returns: ( subraster * ) pointer to struct defining symbol at size,
* or NULL for any error
@@ -3215,6 +3516,8 @@ Allocations and Declarations
chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
int get_baseline(); /* baseline of gfdata */
subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
+raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
+int delete_subraster(); /* in case gftobitmap() fails */
int aasupsamp(), /*antialias char with supersampling*/
grayscale=256; /* aasupersamp() parameters */
/* -------------------------------------------------------------------------
@@ -3225,11 +3528,23 @@ if ( (gfdata=get_chardef(symdef,size)) /
if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
!= NULL ) /* and check that we succeeded */
{
- sp->type = CHARASTER; /* static character raster */
+ raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
+ int format = image->format; /* 1=bitmap, else .gf */
sp->symdef = symdef; /* replace NULL with caller's arg */
sp->size = size; /*replace default with caller's size*/
sp->baseline = get_baseline(gfdata); /* get baseline of character */
- sp->image = &(gfdata->image); /* store ptr to its bitmap */
+ if ( format == 1 ) /* already a bitmap */
+ { sp->type = CHARASTER; /* static char raster */
+ sp->image = image; } /* store ptr to its bitmap */
+ else /* need to convert .gf-to-bitmap */
+ if ( (bitmaprp = gftobitmap(image)) /* convert */
+ != (raster *)NULL ) /* successful */
+ { sp->type = IMAGERASTER; /* allocated raster will be freed */
+ sp->image = bitmaprp; } /* store ptr to converted bitmap */
+ else /* conversion failed */
+ { delete_subraster(sp); /* free unneeded subraster */
+ sp = (subraster *)NULL; /* signal error to caller */
+ goto end_of_job; } /* quit */
if ( issupersampling ) /* antialias character right here */
{
raster *aa = NULL; /* antialiased char raster */
@@ -3245,7 +3560,8 @@ if ( (gfdata=get_chardef(symdef,size)) /
sp->type = IMAGERASTER; } /* character is an image raster */
} /* --- end-of-if(issupersampling) --- */
} /* --- end-of-if(sp!=NULL) --- */
-if ( msgfp!=NULL && msglevel>=999 )
+end_of_job:
+ if ( msgfp!=NULL && msglevel>=999 )
{ fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",
symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }
return ( sp ); /* back to caller */
@@ -3253,6 +3569,42 @@ return ( sp ); /* back to caller */
/* ==========================================================================
+ * Function: get_symsubraster ( symbol, size )
+ * Purpose: returns new subraster ptr containing
+ * data for symbol at given size
+ * --------------------------------------------------------------------------
+ * Arguments: symbol (I) char * corresponding to symbol
+ * whose corresponding subraster is wanted
+ * size (I) int containing 0-5 for desired size
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) pointer to struct defining symbol at size,
+ * or NULL for any error
+ * --------------------------------------------------------------------------
+ * Notes: o just combines get_symdef() and get_charsubraster()
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *get_symsubraster ( char *symbol, int size )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
+mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
+/* -------------------------------------------------------------------------
+look up mathchardef for symbol
+-------------------------------------------------------------------------- */
+if ( symbol != NULL ) /* user supplied input symbol */
+ symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
+/* -------------------------------------------------------------------------
+look up chardef for mathchardef and wrap a subraster structure around data
+-------------------------------------------------------------------------- */
+if ( symdef != NULL ) /* lookup succeeded */
+ sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
+return ( sp ); /* back to caller with sp or NULL */
+} /* --- end-of-function get_symsubraster() --- */
+
+
+/* ==========================================================================
* Function: get_baseline ( gfdata )
* Purpose: returns baseline for a chardef struct
* --------------------------------------------------------------------------
@@ -3367,7 +3719,7 @@ for ( idef=0; ;idef++ ) /* until trail
if ( defsym == NULL ) break; /* reached end-of-table */
else /* check against caller's symbol */
if ( family<0 || deffam == family /* if explicitly in caller's family*/
- || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10)) )
+ || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
{
strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
@@ -3404,10 +3756,14 @@ construct subraster for best fit charact
-------------------------------------------------------------------------- */
if ( bestdef >= 0 ) /* found a best fit for caller */
sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
-if ( sp==NULL && height-bigheight>5 ) /* try to construct delim */
+if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
+|| bigdef < 0 ) /* delim not in font tables */
sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
+if ( msgfp!=NULL && msglevel>=99 )
+ fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
+ (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
return ( sp );
} /* --- end-of-function get_delim() --- */
@@ -3436,18 +3792,30 @@ Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *sp = (subraster *)NULL, /* subraster returned to caller */
*new_subraster(); /* allocate subraster */
+subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
+ *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
+ *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
+ *rastack(), *rastcat(); /* stack pieces, concat filler */
+int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
raster *rasp = (raster *)NULL; /* sp->image */
int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
-int pixsz = 1; /* pixels are one bit each */
+int pixsz = 1, /* pixels are one bit each */
+ symsize = 0; /* size arg for get_symsubraster() */
int thickness = 1; /* drawn lines are one pixel thick */
int aspectratio = 8; /* default height/width for parens */
int iswidth = 0, /*true if width specified by height*/
width = height; /* #pixels width (e.g., of ellipse)*/
-char *lp=NULL, *rp=NULL, *strchr(), /* check symbol for left or right */
- *lp2=NULL, *rp2=NULL; /* synonym for lp,rp */
+char *lp=NULL, *rp=NULL, /* check symbol for left or right */
+ *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
+ *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
+ *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
int circle_raster(), /* ellipse for ()'s in sp->image */
rule_rsater(), /* horizontal or vertical lines */
line_raster(); /* line between two points */
+subraster *uparrow_subraster(); /* up/down arrows */
+int isprealloc = 1; /*pre-alloc subraster, except arrow*/
+int oldsmashmargin = smashmargin, /* save original smashmargin */
+ wascatspace = iscatspace; /* save original iscatspace */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
@@ -3460,51 +3828,196 @@ if ( height < 3 ) goto end_of_job; /* to
if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
else width = (height+(aspectratio+1)/2)/aspectratio;
if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
-|| strstr(symbol,"\\|") != NULL ) /* same || in standard tex notation*/
- width = max2(width,5); /* need space between two |'s */
+|| strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
+|| strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
+ width = max2(width,6); /* need space between two |'s */
if ( width < 2 ) width=2; /* set min width */
if ( strchr(symbol,'(') != NULL /* if left ( */
|| strchr(symbol,')') != NULL ) /* or right ) paren wanted */
- width = (3*width)/2; /* adjust width */
+ { width = (3*width)/2; /* adjust width */
+ if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
+if ( strchr(symbol,'/') != NULL /* left / */
+|| strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
+|| strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
+ width = max2(height/3,5);
+if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
+ { width = min2(height/3,20); /* adjust width */
+ isprealloc = 0; } /* don't preallocate subraster */
+if ( strchr(symbol,'{') != NULL /* if left { */
+|| strchr(symbol,'}') != NULL ) /* or right } brace wanted */
+ { isprealloc = 0; } /* don't preallocate */
/* --- allocate and initialize subraster for constructed delimiter --- */
-if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
-== NULL ) goto end_of_job; /* quit if failed */
-/* --- initialize delimiter subraster parameters --- */
-sp->type = IMAGERASTER; /* image */
-sp->symdef = NULL; /* not applicable for image */
-sp->baseline = height/2 + 2; /* is a little above center good? */
-sp->size = NORMALSIZE; /* size (probably unneeded) */
-rasp = sp->image; /* pointer to image in subraster */
+if ( isprealloc ) /* pre-allocation wanted */
+ { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
+ == NULL ) goto end_of_job; /* quit if failed */
+ /* --- initialize delimiter subraster parameters --- */
+ sp->type = IMAGERASTER; /* image */
+ sp->symdef = NULL; /* not applicable for image */
+ sp->baseline = height/2 + 2; /* is a little above center good? */
+ sp->size = NORMALSIZE; /* size (probably unneeded) */
+ rasp = sp->image; } /* pointer to image in subraster */
/* -------------------------------------------------------------------------
( ) parens
-------------------------------------------------------------------------- */
if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
|| (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
{
- int mywidth = min2(width,20); /* max width for ()'s */
- circle_raster ( rasp, /* embedded raster image */
+ if ( isdrawparen ) { /* draw the paren */
+ int mywidth = min2(width,20); /* max width for ()'s */
+ circle_raster ( rasp, /* embedded raster image */
0, 0, /* row0,col0 are upper-left corner */
height-1, mywidth-1, /* row1,col1 are lower-right */
thickness, /* line thickness is 1 pixel */
(rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
- isokay = 1; /* set flag */
+ isokay = 1; } /* set flag */
+ else {
+ int isleft = (lp!=NULL?1:0); /* true for left, false for right */
+ char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
+ *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
+ *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
+ int baseht=0, barht=0, /* height of base=top+bot, bar */
+ ibar=0, nbars=0; /* bar index, #bars between top&bot*/
+ int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
+ topfill=(isleft?0:0), botfill=(isleft?0:0),
+ barfill=(isleft?0:7); /* alignment fillers */
+ /* --- get pieces at largest size smaller than total height --- */
+ for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
+ {
+ /* --- get pieces at current test size --- */
+ isokay = 1; /* check for all pieces */
+ if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
+ if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
+ if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
+ /* --- check sum of pieces against total desired height --- */
+ if ( isokay ) { /* all pieces retrieved */
+ baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
+ barht = (symbar->image)->height; /* bar height */
+ if ( baseht < height+5 ) break; /* largest base that's not too big */
+ if ( symsize < 1 ) break; /* or smallest available base */
+ } /* --- end-of-if(isokay) --- */
+ /* --- free test pieces that were too big --- */
+ if ( symtop != NULL ) delete_subraster(symtop); /* free top */
+ if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
+ if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
+ isokay = 0; /* nothing available */
+ if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
+ } /* --- end-of-for(symsize) --- */
+ /* --- construct brace from pieces --- */
+ if ( isokay ) { /* we have the pieces */
+ /* --- add alignment fillers --- */
+ smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
+ topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
+ botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
+ barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
+ smashmargin = oldsmashmargin; /* reset smashmargin */
+ iscatspace = wascatspace; /* reset iscatspace */
+ /* --- #bars needed between top and bot --- */
+ nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
+ /* --- stack pieces --- */
+ sp = topsym; /* start with top piece */
+ if ( nbars > 0 ) /* need nbars between top and bot */
+ for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
+ sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
+ delete_subraster(barsym); /* barsym no longer needed */
+ } /* --- end-of-if(isokay) --- */
+ } /* --- end-of-if/else(isdrawparen) --- */
} /* --- end-of-if(left- or right-() paren wanted) --- */
/* -------------------------------------------------------------------------
+{ } braces
+-------------------------------------------------------------------------- */
+else
+ if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
+ || (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
+ {
+ int isleft = (lp!=NULL?1:0); /* true for left, false for right */
+ char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
+ *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
+ *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
+ *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
+ int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
+ ibar=0, nbars=0; /* bar index, #bars above,below mid*/
+ int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
+ topfill=(isleft?4:0), botfill=(isleft?4:0),
+ midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
+ /* --- get pieces at largest size smaller than total height --- */
+ for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
+ {
+ /* --- get pieces at current test size --- */
+ isokay = 1; /* check for all pieces */
+ if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
+ if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
+ if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
+ if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
+ /* --- check sum of pieces against total desired height --- */
+ if ( isokay ) { /* all pieces retrieved */
+ baseht = (symtop->image)->height + (symbot->image)->height
+ + (symmid->image)->height; /* top+bot+mid height */
+ barht = (symbar->image)->height; /* bar height */
+ if ( baseht < height+5 ) break; /* largest base that's not too big */
+ if ( symsize < 1 ) break; /* or smallest available base */
+ } /* --- end-of-if(isokay) --- */
+ /* --- free test pieces that were too big --- */
+ if ( symtop != NULL ) delete_subraster(symtop); /* free top */
+ if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
+ if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
+ if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
+ isokay = 0; /* nothing available */
+ if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
+ } /* --- end-of-for(symsize) --- */
+ /* --- construct brace from pieces --- */
+ if ( isokay ) { /* we have the pieces */
+ /* --- add alignment fillers --- */
+ smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
+ topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
+ botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
+ midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
+ barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
+ smashmargin = oldsmashmargin; /* reset smashmargin */
+ iscatspace = wascatspace; /* reset iscatspace */
+ /* --- #bars needed on each side of mid piece --- */
+ nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
+ /* --- stack pieces --- */
+ sp = topsym; /* start with top piece */
+ if ( nbars > 0 ) /* need nbars above middle */
+ for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
+ sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
+ if ( nbars > 0 ) /* need nbars below middle */
+ for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
+ sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
+ delete_subraster(barsym); /* barsym no longer needed */
+ } /* --- end-of-if(isokay) --- */
+ } /* --- end-of-if(left- or right-{} brace wanted) --- */
+/* -------------------------------------------------------------------------
[ ] brackets
-------------------------------------------------------------------------- */
else
if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
- || (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */
- {
- /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
- int mywidth = min2(width,12); /* max width for horizontal bars */
- thickness = 1; /* set lines 1 or 2 pixels thick */
- rule_raster(rasp, 0,0, mywidth,thickness, 0); /* top horizontal bar */
- rule_raster(rasp, height-thickness,0, mywidth,thickness, 0); /* bottom */
- if ( lp != NULL ) /* left [ bracket wanted */
+ || (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
+ || (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
+ || (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
+ || (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
+ || (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
+ || (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
+ || (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
+ {
+ /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
+ int mywidth = min2(width,12), /* max width for horizontal bars */
+ wthick = 1; /* thickness of top.bottom bars */
+ thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
+ if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
+ wthick = thickness; /* same thickness for top/bot bar */
+ if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
+ rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
+ if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
+ rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
+ if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
- if ( rp != NULL ) /* right ] bracket wanted */
+ if ( lp4 != NULL ) /* 2nd left vertical bar needed */
+ rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
+ if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
+ if ( rp4 != NULL ) /* 2nd right vertical bar needed */
+ rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-[] bracket wanted) --- */
/* -------------------------------------------------------------------------
@@ -3514,18 +4027,69 @@ else
if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
|| (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
{
- /* --- line_raster ( rasp, row0, col0, row1, col1, thickness ) --- */
- int mywidth = min2(width,12); /* max width for brackets */
- thickness = 1; /* set line pixel thickness */
+ /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
+ int mywidth = min2(width,12), /* max width for brackets */
+ mythick = 1; /* all lines one pixel thick */
+ thickness = (height<25?1:2); /* set line pixel thickness */
if ( lp != NULL ) /* left < bracket wanted */
- { line_raster(rasp,height/2,0,0,mywidth-1,thickness);
- line_raster(rasp,height/2,0,height-1,mywidth-1,thickness); }
+ { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
+ if ( thickness>1 )
+ line_raster(rasp,height/2,1,0,mywidth-1,mythick);
+ line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
+ if ( thickness>1 )
+ line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
if ( rp != NULL ) /* right > bracket wanted */
- { line_raster(rasp,height/2,mywidth-1,0,0,thickness);
- line_raster(rasp,height/2,mywidth-1,height-1,0,thickness); }
+ { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
+ if ( thickness>1 )
+ line_raster(rasp,height/2,mywidth-2,0,0,mythick);
+ line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
+ if ( thickness>1 )
+ line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-<> bracket wanted) --- */
/* -------------------------------------------------------------------------
+/ \ delimiters
+-------------------------------------------------------------------------- */
+else
+ if ( (lp=strchr(symbol,'/')) != NULL /* left / wanted */
+ || (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
+ || (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
+ {
+ /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
+ int mywidth = width; /* max width for / \ */
+ thickness = 1; /* set line pixel thickness */
+ if ( lp != NULL ) /* left / wanted */
+ line_raster(rasp,0,mywidth-1,height-1,0,thickness);
+ if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
+ line_raster(rasp,0,0,height-1,mywidth-1,thickness);
+ isokay = 1; /* set flag */
+ } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
+/* -------------------------------------------------------------------------
+arrow delimiters
+-------------------------------------------------------------------------- */
+else
+ if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
+ {
+ /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
+ int mywidth = width; /* max width for / \ */
+ int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
+ || strstr(symbol,"Down")!=NULL); /* or a Down */
+ int drctn = +1; /* init for uparrow */
+ if ( strstr(symbol,"down")!=NULL /* down if we have down */
+ || strstr(symbol,"Down")!=NULL ) /* or Down */
+ { drctn = (-1); /* reset direction to down */
+ if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
+ || strstr(symbol,"Up")!=NULL ) /* and down or Down */
+ drctn = 0; } /* reset direction to updown */
+ sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
+ if ( sp != NULL )
+ { sp->type = IMAGERASTER; /* image */
+ sp->symdef = NULL; /* not applicable for image */
+ sp->baseline = height/2 + 2; /* is a little above center good? */
+ sp->size = NORMALSIZE; /* size (probably unneeded) */
+ isokay = 1; } /* set flag */
+ } /* --- end-of-if(arrow delimiter wanted) --- */
+/* -------------------------------------------------------------------------
\- for | | brackets or \= for || || brackets
-------------------------------------------------------------------------- */
else
@@ -3538,13 +4102,13 @@ else
int midcol = width/2; /* middle col, left of mid if even */
if ( rp != NULL /* left or right || bracket wanted */
|| rp2 != NULL ) /* or || in standard tex notation */
- { thickness = 1; /* each | of || one pixel thick */
+ { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
else /*nb, lp2 spuriously set if rp2 set*/
if ( lp != NULL /* left or right | bracket wanted */
|| lp2 != NULL ) /* ditto for synomym */
- { thickness = 1; /* set | two pixels thick */
+ { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
isokay = 1; /* set flag */
} /* --- end-of-if(left- or right-[] bracket wanted) --- */
@@ -3552,8 +4116,11 @@ else
back to caller
-------------------------------------------------------------------------- */
end_of_job:
+ if ( msgfp!=NULL && msglevel>=99 )
+ fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
+ (symbol==NULL?"null":symbol),isokay);
if ( !isokay ) /* don't have requested delimiter */
- { delete_subraster(sp); /* so free unneeded structure */
+ { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
sp = NULL; } /* and signal error to caller */
return ( sp ); /*back to caller with delim or NULL*/
} /* --- end-of-function make_delim() --- */
@@ -3639,10 +4206,9 @@ if ( esclen < 1 ) /* \ followed by no
*ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
else { /* normal alpha \sequence */
/* --- respect spaces in text mode, except first space after \escape --- */
- if ( istext > 0 ) /* in \rm or \it text mode */
- if ( istext != 3 ) /* but not \mathbb */
- if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
- expression++; } /* so flush delim */
+ if ( istextmode ) /* in \rm or \it text mode */
+ if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
+ expression++; } /* so flush delim */
*ptoken = '\000'; /* null-terminate token */
/* --- back to caller --- */
end_of_job:
@@ -3708,9 +4274,10 @@ char *texsubexpr ( char *expression, cha
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texchar(); /*next char (or \sequence) from expression*/
-char *leftptr, leftdelim[32] = "(\000", /* left( found in expression */
- rightdelim[32] = ")\000"; /* and matching right) */
+char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
+ rightdelim[256] = ")\000"; /* and matching right) */
char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
+char *strtexchr(), *texleft(); /* check for \left, and get it */
int gotescape = 0, /* true if leading char of expression is \ */
prevescape = 0; /* while parsing, true if preceding char \ */
int isbrace(); /* check for left,right braces */
@@ -3731,6 +4298,16 @@ if ( maxsubsz < 1 ) maxsubsz = 8192; /*
/* --- check for escape --- */
if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
gotescape = 1; /* so set flag accordingly */
+/* --- check for \left...\right --- */
+if ( gotescape ) /* begins with \ */
+ if ( memcmp(expression+1,"left",4) ) /* and followed by left */
+ if ( strchr(left,'l') != NULL ) /* caller wants \left's */
+ if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
+ { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
+ (isdelim?NULL:leftdelim),rightdelim);
+ if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
+ return ( pright ); /*back to caller past \right*/
+ } /* --- end-of-if(expression=="\\left") --- */
/* --- if first char isn't left{ or script, just return it to caller --- */
if ( !isbrace(expression,left,isescape) ) /* not a left{ */
if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
@@ -3811,6 +4388,112 @@ while ( 1 ) /*until balanced right}
/* ==========================================================================
+ * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
+ * Purpose: scans expression, starting after opening \left,
+ * and returning ptr after matching closing \right.
+ * Everything between is returned in subexpr, if given.
+ * Likewise, if given, ldelim returns delimiter after \left
+ * and rdelim returns delimiter after \right.
+ * If ldelim is given, the returned subexpr doesn't include it.
+ * If rdelim is given, the returned pointer is after that delim.
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I) char * to first char of null-terminated
+ * string immediately following opening \left
+ * subexpr (O) char * to null-terminated string returning
+ * either everything between balanced
+ * \left ... \right. If leftdelim given,
+ * subexpr does _not_ contain that delimiter.
+ * maxsubsz (I) int containing max #bytes returned
+ * in subexpr buffer (0 means unlimited)
+ * ldelim (O) char * returning delimiter following
+ * opening \left
+ * rdelim (O) char * returning delimiter following
+ * closing \right
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) ptr to the first char of expression
+ * past closing \right, or past closing
+ * right delimiter if rdelim!=NULL,
+ * or NULL for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+char *texleft ( char *expression, char *subexpr, int maxsubsz,
+ char *ldelim, char *rdelim )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texchar(), /* get delims after \left,\right */
+ *strtexchr(), *pright=expression; /* locate matching \right */
+static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
+int sublen = 0; /* #chars between \left...\right */
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+/* --- init output --- */
+if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
+if ( ldelim != NULL ) *ldelim = '\000'; /* init ldelim, if given */
+if ( rdelim != NULL ) *rdelim = '\000'; /* init rdelim, if given */
+/* --- check args --- */
+if ( expression == NULL ) goto end_of_job; /* no input supplied */
+if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
+/* --- determine left delimiter --- */
+if ( ldelim != NULL ) /* caller wants left delim */
+ { skipwhite(expression); /* interpret \left ( as \left( */
+ expression = texchar(expression,ldelim); } /*delim from expression*/
+/* -------------------------------------------------------------------------
+locate \right balancing opening \left
+-------------------------------------------------------------------------- */
+/* --- first \right following \left --- */
+if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
+!= NULL ) { /* found it */
+ /* --- find matching \right by pushing past any nested \left's --- */
+ char *pleft = expression; /* start after first \left( */
+ while ( 1 ) { /*break when matching \right found*/
+ /* -- locate next nested \left if there is one --- */
+ if ( (pleft=strtexchr(pleft,left)) /* find next \left */
+ == NULL ) break; /*no more, so matching \right found*/
+ pleft += strlen(left); /* push ptr past \left token */
+ if ( pleft >= pright ) break; /* not nested if \left after \right*/
+ /* --- have nested \left, so push forward to next \right --- */
+ if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
+ == NULL ) break; /* ran out of \right's */
+ } /* --- end-of-while(1) --- */
+ } /* --- end-of-if(pright!=NULL) --- */
+/* --- set subexpression length, push pright past \right --- */
+if ( pright != (char *)NULL ) /* found matching \right */
+ { sublen = (int)(pright-expression); /* #chars between \left...\right */
+ pright += strlen(right); } /* so push pright past \right */
+/* -------------------------------------------------------------------------
+get rightdelim and subexpr between \left...\right
+-------------------------------------------------------------------------- */
+/* --- get delimiter following \right --- */
+if ( rdelim != NULL ) /* caller wants right delim */
+ if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
+ { strcpy(rdelim,"."); /* set default \right. */
+ sublen = strlen(expression); /* use entire remaining expression */
+ pright = expression + sublen; } /* and push pright to end-of-string*/
+ else /* have explicit matching \right */
+ { skipwhite(pright); /* interpret \right ) as \right) */
+ pright = texchar(pright,rdelim); /* pull delim from expression */
+ if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* or set \right. */
+/* --- get subexpression between \left...\right --- */
+if ( sublen > 0 ) /* have subexpr */
+ if ( subexpr != NULL ) { /* and caller wants it */
+ if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
+ memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
+ subexpr[sublen] = '\000'; } /* null-terminate subexpr */
+end_of_job:
+ if ( msglevel>=99 && msgfp!=NULL )
+ { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
+ (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
+ (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
+ return ( pright );
+} /* --- end-of-function texleft --- */
+
+
+/* ==========================================================================
* Function: texscripts ( expression, subscript, superscript, which )
* Purpose: scans expression, returning subscript and/or superscript
* if expression is of the form _x^y or ^{x}_{y},
@@ -3846,16 +4529,16 @@ Allocations and Declarations
char *texsubexpr(); /* next subexpression from expression */
int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
/* -------------------------------------------------------------------------
-init "scripts" and skip leading whitespace
+init "scripts"
-------------------------------------------------------------------------- */
-/* --- skip leading whitespace and check for end-of-string --- */
-*subscript = *superscript = '\000'; /* init in case no scripts */
-skipwhite(expression); /* leading whitespace gone */
-if ( *expression == '\000' ) return(expression); /* nothing left to scan */
+if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
+if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
/* -------------------------------------------------------------------------
get subscript and/or superscript from expression
-------------------------------------------------------------------------- */
-while ( expression != NULL )
+while ( expression != NULL ) {
+ skipwhite(expression); /* leading whitespace gone */
+ if ( *expression == '\000' ) return(expression); /* nothing left to scan */
if ( isthischar(*expression,SUBSCRIPT) /* found _ */
&& (which==1 || which>2 ) ) /* and caller wants it */
{ if ( gotsub /* found 2nd subscript */
@@ -3871,6 +4554,7 @@ while ( expression != NULL )
expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
else /* neither _ nor ^ */
return ( expression ); /*return ptr past "scripts"*/
+ } /* --- end-of-while(expression!=NULL) --- */
return ( expression );
} /* --- end-of-function texscripts() --- */
@@ -4140,10 +4824,14 @@ static struct { char *html; char *args;
LaTeX Macro #args,default template...
------------------------------------------ */
{ "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
+ { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
+ { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
+ { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
+ { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
{ "\\overset", NULL, "\\stackrel" }, /* just an alias */
{ "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
/* ---------------------------------------
- html termchar LaTeX equivalent...
+ html char termchar LaTeX equivalent...
--------------------------------------- */
{ """, ";", "\"" }, /* " is first, " */
{ "&", ";", "&" },
@@ -4168,30 +4856,52 @@ static struct { char *html; char *args;
{ "ã", ";", "{\\rm~\\tilde~a}" },
{ "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
/* ---------------------------------------
+ html tag termchar LaTeX equivalent...
+ --------------------------------------- */
+ { "
", NULL, "\\\\" },
+ { "
", NULL, "\\\\" },
+ { "
", NULL, "\\\\" },
+ { "
", NULL, "\\\\" },
+ { "
", NULL, "\\\\" },
+ { "
", NULL, "\\\\" },
+ /* ---------------------------------------
LaTeX termchar mimeTeX equivalent...
--------------------------------------- */
{ "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
{ "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
{ "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
{ "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
+ { "\\dots", NULL, "{\\cdots}" },
{ "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
{ "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
{ "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
{ "\\notin", NULL, "{\\not\\in}" },
{ "\\neq", NULL, "{\\not=}" },
+ { "\\ne", NULL, "{\\not=}" },
{ "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
{ "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
+ { "\\textcelsius", NULL, "{\\textdegree C}"},
+ { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
{ "\\cr", NULL, "\\\\" },
+ { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
{ "\\iint", NULL, "{\\int\\int}\\limits" },
{ "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
+ { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
{ "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
{ "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
+ { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
+ { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
+ { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
+ { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
+ { "\'", "embed","^{\\fs{-1}\\prime}" },
{ "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
+ { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
{ "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
{ "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
{ "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
{ "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
{ "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
+ { "\\cancel",NULL, "\\Not" },
{ "\\hhline",NULL, "\\Hline" },
{ "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
/* ---------------------------------------------------------
@@ -4202,7 +4912,7 @@ static struct { char *html; char *args;
{ "cos", "1", "{\\cos{#1}}" },
{ "asin", "1", "{\\sin^{-1}{#1}}" },
{ "acos", "1", "{\\cos^{-1}{#1}}" },
- { "exp", "1", "{e^{#1}}" },
+ { "exp", "1", "{{\\rm~e}^{#1}}" },
{ "det", "1", "{\\left|{#1}\\right|}" },
/* ---------------------------------------
LaTeX Constant termchar value...
@@ -4277,6 +4987,7 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
{
int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
+ int isembedded = 0; /* true to xlate even if embedded */
char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
*htmlterm = args, /*if *args nonumeric, then html term*/
@@ -4288,9 +4999,13 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
{ htmlterm = NULL; /* if so, then we have no htmlterm */
*abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */
nargs = atoi(abuff); } /* interpret #args to numeric */
+ else if ( strncmp(args,"embed",5) == 0 ) /* xlate even if embedded */
+ { htmlterm = NULL; /* if so, then we have no htmlterm */
+ isembedded = 1 ; } /* turn on embedded flag */
expptr = expression; /* re-start search at beginning */
while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */
- { char termchar = *(tokptr+htmllen); /* char terminating html sequence */
+ { char termchar = *(tokptr+htmllen), /* char terminating html sequence */
+ prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
int escapelen = htmllen; /* total length of escape sequence */
*abuff = '\000'; /* default to empty string */
if ( latexsym != NULL ) /* table has .latex xlation */
@@ -4298,10 +5013,16 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
strcpy(abuff,latexsym); /* so get local copy */
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*/
+ if ( !isembedded ) /* don't xlate embedded sequence */
+ 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 ( isembedded ) /* for embedded sequence */
+ if ( isthischar(prevchar,ESCAPE) ) /* don't xlate escaped char */
+ { expptr = tokptr+htmllen; /*just resume search after literal*/
+ continue; } /* but don't replace it */
if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */
+ && isalpha(*htmlsym) /* and our symbol starts with alpha*/
&& !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*/
@@ -4527,7 +5248,7 @@ char *strtexchr ( char *string, char *te
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-char *strstr(), delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
+char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
char *pstring = string; /* start or continue up search here*/
int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
/* -------------------------------------------------------------------------
@@ -4660,14 +5381,14 @@ subraster *get_charsubraster(), /* char
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 */
- wasstring = isstring, /* initial isstring mode flag */
+int wasstring = isstring, /* initial isstring mode flag */
wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
+ oldfontnum = fontnum, /* initial font family */
oldfontsize = fontsize, /* initial fontsize */
olddisplaysize = displaysize, /* initial \displaystyle size */
oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
- oldsquashmargin = squashmargin, /* initial squashmargin */
- oldissquashdelta = issquashdelta, /* initial issquashdelta */
+ oldsmashmargin = smashmargin, /* initial smashmargin */
+ oldissmashdelta = issmashdelta, /* initial issmashdelta */
*oldworkingparam = workingparam; /* initial working parameter */
subraster *oldworkingbox = workingbox, /* initial working box */
*oldleftexpression = leftexpression; /*left half rasterized so far*/
@@ -4724,20 +5445,20 @@ while ( 1 )
if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
== NULL ) /* lookup failed */
{ char literal[512] = "[?]"; /*display for unrecognized literal*/
- int wastext = istext; /* error display in default mode */
+ int oldfontnum = fontnum; /* error display in default mode */
if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */
{ fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
chartoken); fflush(msgfp); }
sp = (subraster *)NULL; /* init to signal failure */
if ( warninglevel < 1 ) continue; /* warnings not wanted */
- istext = 0; /* reset from \mathbb, etc */
+ fontnum = 0; /* reset from \mathbb, etc */
if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */
{ /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
strcat(literal,chartoken+1); /* add chars following leading \ */
strcat(literal,"?]}"); } /* add closing brace */
sp = rasterize(literal,size-1); /* rasterize literal token */
- istext = wastext; /* reset text mode */
+ fontnum = oldfontnum; /* reset font family */
if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/
else /* --- check if we have special handler to process this token --- */
if ( symdef->handler != NULL ) /* have a handler for this token */
@@ -4773,7 +5494,9 @@ while ( 1 )
if ( natoms < 1 /* nothing previous to concat */
|| expraster == NULL /* or previous was complete error */
|| isreplaceleft ) /* or we're replacing previous */
- { expraster = subrastcpy(sp); /* so just copy static CHARASTER */
+ { if ( 1 && expraster!=NULL ) /* probably replacing left */
+ delete_subraster(expraster); /* so first free original left */
+ expraster = subrastcpy(sp); /* copy static CHARASTER or left */
isreplaceleft = 0; } /* reset replacement flag */
else /*we've already built up atoms so...*/
if ( sp != NULL ) /* ...if we have a new component */
@@ -4796,15 +5519,21 @@ end_of_job:
if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
type_raster(expraster->image,msgfp); /* display completed raster */
fflush(msgfp); } /* flush msgfp buffer */
+ /* --- set final raster buffer --- */
+ if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
+ { expraster->type = IMAGERASTER; /* set type to constructed image */
+ if ( istextmode ) /* but in text mode */
+ expraster->type = blanksignal; /* set type to avoid smash */
+ expraster->size = fontsize; } /* set original input font size */
/* --- restore flags/values to original saved values --- */
- istext = wastext; /* text mode reset */
isstring = wasstring; /* string mode reset */
isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
+ fontnum = oldfontnum; /* font family reset */
fontsize = oldfontsize; /* fontsize reset */
displaysize = olddisplaysize; /* \displaystyle size reset */
shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
- squashmargin = oldsquashmargin; /* squashmargin reset */
- issquashdelta = oldissquashdelta; /* issquashdelta reset */
+ smashmargin = oldsmashmargin; /* smashmargin reset */
+ issmashdelta = oldissmashdelta; /* issmashdelta reset */
workingparam = oldworkingparam; /* working parameter reset */
workingbox = oldworkingbox; /* working box reset */
leftexpression = oldleftexpression; /* leftexpression reset */
@@ -4812,9 +5541,6 @@ end_of_job:
unitlength = oldunitlength; /* unitlength reset */
recurlevel--; /* unwind one recursion level */
/* --- return final subraster to caller --- */
- if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
- { expraster->type = IMAGERASTER; /* set type to constructed image */
- expraster->size = fontsize; } /* set original input font size */
return ( expraster );
} /* --- end-of-function rasterize() --- */
@@ -4858,7 +5584,7 @@ subraster *rasterize(), *sp=NULL; /* ras
int isheight = 1; /*true=full height, false=baseline*/
int height, /* height of rasterized noparens[] */
baseline; /* and its baseline */
-int family = CMEX10; /* family for paren chars */
+int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
subraster *rastcat(); /* concatanate subrasters */
int delete_subraster(); /*in case of error after allocation*/
@@ -4952,7 +5678,7 @@ subraster *rastscripts(), *rastdispmath(
*rastcat(), /* may need to concat scripts */
*scriptsp = basesp; /* and this will become the result */
int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
-int oldsquashmargin = squashmargin; /* save original squashmargin */
+int oldsmashmargin = smashmargin; /* save original smashmargin */
int type_raster(); /* display debugging output */
/* --- to check for \limits or \nolimits preceding scripts --- */
char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
@@ -5013,12 +5739,13 @@ else /* scripts alongside base symbo
scriptsp = basesp; /* so just return unscripted symbol*/
else /* symbols followed by scripts */
if ( basesp != NULL ) /* have base symbol */
- { squashmargin = 0; /* don't squash script */
- scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base sym*/
+ { smashmargin = 0; /* don't smash script */
+ /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
+ scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
scriptsp->type = IMAGERASTER; /* flip type of composite object */
scriptsp->size = size; } /* and set font size */
end_of_job:
- squashmargin = oldsquashmargin; /* reset original squashmargin */
+ smashmargin = oldsmashmargin; /* reset original smashmargin */
if ( msgfp!=NULL && msglevel>=99 )
{ fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
scriptlevel,(scriptsp==NULL?"null":"..."));
@@ -5259,7 +5986,7 @@ end_of_job:
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
- * ildelim (I) int containing ldelims index of
+ * ildelim (I) int containing ldelims[] index of
* left delimiter
* arg2 (I) int unused
* arg3 (I) int unused
@@ -5284,15 +6011,17 @@ int family=CMSYEX, /* get_delim() fami
height=0, rheight=0, /* subexpr, right delim height */
margin=(size+1), /* delim height margin over subexpr*/
opmargin=(5); /* extra margin for \int,\sum,\etc */
-char subexpr[8192]; /* chars between \left...\right */
+char /* *texleft(),*/ subexpr[8192]; /* chars between \left...\right */
char *texchar(), /* get delims after \left,\right */
ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
int sublen=0; /* strlen(subexpr) */
int idelim=0; /* 1=left,2=right */
+/* int gotldelim = 0; */ /* true if ildelim given by caller */
int delete_subraster(); /* free subraster if rastleft fails*/
int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
+int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
/* --- recognized delimiters --- */
static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
static char *ldelims[] = {
@@ -5329,6 +6058,15 @@ static char *xto[] = /* ...to this ins
"<", /* \langle to < */
">", /* \rangle to > */
NULL } ; /* --- end-of-xto[] --- */
+/* --- non-displaystyle delimiters --- */
+static char *textdelims[] = /* these delims _aren't_ display */
+ { "|", "=",
+ "(", ")",
+ "[", "]",
+ "<", ">",
+ "{", "}",
+ "dbl", /* \lbrackdbl and \rbrackdbl */
+ NULL } ; /* --- end-of-textdelims[] --- */
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
@@ -5336,10 +6074,15 @@ initialization
if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
/* --- determine left delimiter, and set default \right. delimiter --- */
if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
- strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
+ { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
+ /* gotldelim = 1; */ } /* and set flag that we got it */
else /* trapped \left without delim */
{ skipwhite(*expression); /* interpret \left ( as \left( */
- *expression = texchar(*expression,ldelim); } /*pull delim from expression*/
+ if ( *(*expression) == '\000' ) /* end-of-string after \left */
+ goto end_of_job; /* so return NULL */
+ *expression = texchar(*expression,ldelim); /*pull delim from expression*/
+ if ( *expression == NULL /* probably invalid end-of-string */
+ || *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
strcpy(rdelim,"."); /* init default \right. delim */
/* -------------------------------------------------------------------------
locate \right balancing our opening \left
@@ -5364,7 +6107,10 @@ if ( (pright=strtexchr(*expression,right
push past \left(_a^b sub/superscripts, if present
-------------------------------------------------------------------------- */
pleft = *expression; /*reset pleft after opening \left( */
-/*lp=*/ rastlimits(expression,size,lp); /*dummy call push expression past b*/
+if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
+!= NULL ) /* found actual _a^b scripts, too */
+ { delete_subraster(lp); /* but we don't need them */
+ lp = NULL; } /* reset pointer, too */
/* -------------------------------------------------------------------------
get \right delimiter and subexpression between \left...\right, xlate delims
-------------------------------------------------------------------------- */
@@ -5384,6 +6130,9 @@ else { /* have explicit matching \ri
/* --- get subexpression between \left...\right --- */
if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
subexpr[sublen] = '\000'; /* and null-terminate it */
+/* --- adjust margin for expressions containing \middle's --- */
+if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
+ margin = 1; /* so don't "overwhelm" them */
/* --- check for operator delimiter --- */
for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
@@ -5391,7 +6140,7 @@ for ( idelim=0; opdelims[idelim]!=NULL;
if ( *ldelim == '\\' ) /* have leading escape */
strcpy(ldelim,ldelim+1); /* squeeze it out */
break; } /* no need to check rest of table */
-/* --- xlate delimiters --- */
+/* --- xlate delimiters and check for textstyle --- */
for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
char *lrdelim = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
int ix; char *xdelim; /* xfrom[] and xto[] index, delim */
@@ -5399,6 +6148,12 @@ for ( idelim=1; idelim<=2; idelim++ ) {
if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
{ strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
break; } /* no need to check further */
+ for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
+ if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
+ { if ( idelim == 1 ) /* if it's the \left one */
+ istextleft = 1; /* set left textstyle flag */
+ else istextright = 1; /* else set right textstyle flag */
+ break; } /* no need to check further */
} /* --- end-of-for(idelim) --- */
/* --- debugging --- */
if ( msgfp!=NULL && msglevel>=99 )
@@ -5419,7 +6174,7 @@ rasterize delimiters, reset baselines, a
isleftdot = (strchr(ldelim,'.')!=NULL); /* true if \left. */
isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
/* --- get rasters for best-fit delim characters, add sub/superscripts --- */
-isdisplaystyle = 9; /* force \displaystyle */
+isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
if ( !isleftdot ) /* if not \left. */
{ /* --- first get requested \left delimiter --- */
lp = get_delim(ldelim,rheight,family); /* get \left delim char */
@@ -5431,7 +6186,7 @@ if ( !isleftdot ) /* if not \left. */
rheight = lheight-1; } /* make sure right delim matches */
/* --- then add on any sub/superscripts attached to \left( --- */
lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/
-isdisplaystyle = 9; /* force \displaystyle */
+isdisplaystyle = (istextright?0:9); /* force \displaystyle */
if ( !isrightdot ) /* and if not \right. */
{ /* --- first get requested \right delimiter --- */
rp = get_delim(rdelim,rheight,family); /* get \right delim char */
@@ -5466,6 +6221,162 @@ end_of_job:
/* ==========================================================================
+ * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
+ * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I) char ** to first char of null-terminated
+ * string beginning with a \right
+ * to be rasterized
+ * size (I) int containing 0-5 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding leading left{
+ * (unused, but passed for consistency)
+ * ildelim (I) int containing rdelims[] index of
+ * right delimiter
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
+ * or NULL for any parsing error
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastright ( char **expression, int size, subraster *basesp,
+ int ildelim, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
+ if ( sp != NULL ) /* returning entire expression */
+ {
+ isreplaceleft = 1; /* set flag to replace left half*/
+ }
+return ( sp );
+} /* --- end-of-function rastright() --- */
+
+
+/* ==========================================================================
+ * Function: rastmiddle ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: \middle handler, returns subraster corresponding to
+ * entire expression with \middle delimiter(s) sized to fit.
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \middle to be
+ * rasterized, and returning ptr immediately
+ * to terminating null.
+ * size (I) int containing 0-5 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \middle
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) ptr to subraster corresponding to expression,
+ * or NULL for any parsing error
+ * (expression ptr unchanged if error occurs)
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastmiddle ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
+char *exprptr = *expression, /* local copy of ptr to expression */
+ *texchar(), delim[32][132], /* delimiters following \middle's */
+ *strtexchr(), /* locate \middle's */
+ subexpr[8193], *subptr=NULL; /* subexpression between \middle's */
+int height=0, habove=0, hbelow=0; /* height, above & below baseline */
+int idelim, ndelims=0, /* \middle count (max 32) */
+ family = CMSYEX; /* delims from CMSY10 or CMEX10 */
+subraster *subrastcpy(), /* copy subraster */
+ *rastcat(), /* concatanate subraster */
+ *get_delim(); /* get rasterized delimiter */
+int delete_subraster(); /* free work area subsp[]'s at eoj */
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+subsp[0] = leftexpression; /* expressn preceding 1st \middle */
+subsp[1] = NULL; /* set first null */
+/* -------------------------------------------------------------------------
+accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
+-------------------------------------------------------------------------- */
+while ( ndelims < 30 ) /* max of 31 \middle's */
+ {
+ /* --- maintain max height above,below baseline --- */
+ if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
+ { int baseline = (subsp[ndelims])->baseline; /* #rows above baseline */
+ height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
+ habove = max2(habove,baseline); /* max #rows above baseline */
+ hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
+ /* --- get delimter after \middle --- */
+ skipwhite(exprptr); /*skip space betwn \middle & \delim*/
+ exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
+ if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
+ break; /* ignore it and consider job done */
+ ndelims++; /* count another \middle\delim */
+ /* --- get subexpression between \delim and next \middle --- */
+ subsp[ndelims] = NULL; /* no subexpresion yet */
+ if ( *exprptr == '\000' ) /* end-of-expression after \delim */
+ break; /* so we have all subexpressions */
+ if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
+ == NULL ) /* no more \middle's */
+ { strncpy(subexpr,exprptr,8192); /* get entire remaining expression */
+ subexpr[8192] = '\000'; /* make sure it's null-terminated */
+ exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
+ else /* have another \middle */
+ { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
+ memcpy(subexpr,exprptr,min2(sublen,8192)); /* get subexpression */
+ subexpr[min2(sublen,8192)] = '\000'; /* and null-terminate it */
+ exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
+ /* --- rasterize subexpression --- */
+ subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
+ } /* --- end-of-while(1) --- */
+/* -------------------------------------------------------------------------
+construct \middle\delim's and concatanate them between subexpressions
+-------------------------------------------------------------------------- */
+if ( ndelims < 1 /* no delims */
+|| (height=habove+hbelow) < 1 ) /* or no subexpressions? */
+ goto end_of_job; /* just flush \middle directive */
+for ( idelim=0; idelim<=ndelims; idelim++ )
+ {
+ /* --- first add on subexpression preceding delim --- */
+ if ( subsp[idelim] != NULL ) /* have subexpr preceding delim */
+ if ( sp == NULL ) /* this is first piece */
+ { sp = subsp[idelim]; /* so just use it */
+ if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
+ else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); /* or concat it */
+ /* --- now construct delimiter --- */
+ if ( *(delim[idelim]) != '\000' ) /* have delimter */
+ { subraster *delimsp = get_delim(delim[idelim],height,family);
+ if ( delimsp != NULL ) /* rasterized delim */
+ { delimsp->baseline = habove; /* set baseline */
+ if ( sp == NULL ) /* this is first piece */
+ sp = delimsp; /* so just use it */
+ else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
+ } /* --- end-of-for(idelim) --- */
+/* --- back to caller --- */
+end_of_job:
+ if ( 0 ) /* now handled above */
+ for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
+ if ( subsp[idelim] != NULL ) /* have allocated subraster */
+ delete_subraster(subsp[idelim]); /* so free it */
+ if ( sp != NULL ) /* returning entire expression */
+ { int newht = (sp->image)->height; /* height of returned subraster */
+ sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
+ isreplaceleft = 1; /* set flag to replace left half*/
+ *expression += strlen(*expression); } /* and push to terminating null*/
+ return ( sp );
+} /* --- end-of-function rastmiddle() --- */
+
+
+/* ==========================================================================
* Function: rastflags ( expression, size, basesp, flag, value, arg3 )
* Purpose: sets an internal flag, e.g., for \rm, or sets an internal
* value, e.g., for \unitlength=, and returns NULL
@@ -5476,8 +6387,8 @@ end_of_job:
* size (I) int containing base font size (not used,
* just stored in subraster)
* basesp (I) subraster * to character (or subexpression)
- * immediately preceding space, whose baseline
- * and height params are transferred to space
+ * immediately preceding "flags" directive
+ * (unused but passed for consistency)
* flag (I) int containing #define'd symbol specifying
* internal flag to be set
* value (I) int containing new value of flag
@@ -5507,10 +6418,10 @@ set flag or value
switch ( flag )
{
default: break; /* unrecognized flag */
- case ISTEXT:
+ case ISFONTFAM:
if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
(*expression)++; /* skip leading ~ after \rm */
- istext=value; /* set text mode */
+ fontnum = value; /* set font family */
break;
case ISSTRING: isstring=value; break; /* set string/image mode */
case ISDISPLAYSTYLE: /* set \displaystyle mode */
@@ -5541,7 +6452,7 @@ switch ( flag )
case ISADJACENTWT: /* set lowpass adjacent weight */
case ISCORNERWT: /* set lowpass corner weight */
case ISCOLOR: /* set red(1),green(2),blue(3) */
- case ISSQUASH: /* set (minimum) "squash" margin */
+ case ISSMASH: /* set (minimum) "smash" margin */
if ( value != NOVALUE ) /* passed a fixed value to be set */
argvalue = value; /* set given fixed value */
else /* get value from expression */
@@ -5578,10 +6489,14 @@ switch ( flag )
fontsize = (isdelta? fontsize+argvalue : argvalue);
fontsize = max2(0,min2(fontsize,largestsize));
shrinkfactor = shrinkfactors[fontsize];
- if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/
+ if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
+ || (1 && isdisplaystyle==2) /* displaystyle enabled and set */
+ || (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
if ( !ispreambledollars ) /* respect $$...$$'s */
- isdisplaystyle = (fontsize>=displaysize? 2:1); /* forced */
+ if ( fontsize >= displaysize )
+ isdisplaystyle = 2; /* forced */
+ else isdisplaystyle = 1;
/*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
else /* embed font size in expression */
{ sprintf(valuearg,"%d",fontsize); /* convert size */
@@ -5594,12 +6509,12 @@ switch ( flag )
if ( argvalue != NOVALUE ) /* got a value */
displaysize = (isdelta? displaysize+argvalue : argvalue);
break;
- case ISSQUASH: /* set (minimum) "squash" margin */
+ case ISSMASH: /* set (minimum) "smash" margin */
if ( argvalue != NOVALUE ) /* got a value */
- { squashmargin = argvalue; /* set value */
+ { smashmargin = argvalue; /* set value */
if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
- issquashdelta = (isdelta?1:0); } /* and set delta flag */
- squashmargin = max2((isdelta?-5:0),min2(squashmargin,32)); /*sanity*/
+ issmashdelta = (isdelta?1:0); } /* and set delta flag */
+ smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
break;
case ISSHRINK: /* set shrinkfactor */
if ( argvalue != NOVALUE ) /* got a value */
@@ -5701,7 +6616,6 @@ int pixsz = 1; /*default #bits per pix
char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
subraster *rastcat(); /* cat rightsp after \hfill */
-int blanksignal = (-991234); /*rastsquash signal right-hand blank*/
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
@@ -5767,9 +6681,9 @@ return ( spacesp );
* string immediately following \\ to be
* rasterized, and returning ptr immediately
* to terminating null.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
- * immediately preceding \accent
+ * immediately preceding \\
* (unused, but passed for consistency)
* arg1 (I) int unused
* arg2 (I) int unused
@@ -5812,7 +6726,8 @@ rasterize right half of expression and s
if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
== NULL ) goto end_of_job; /* quit if failed */
/* --- stack left half above it --- */
-newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/
+/*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
+newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
/* --- back to caller --- */
end_of_job:
if ( newlsp != NULL ) /* returning entire expression */
@@ -6166,7 +7081,7 @@ int baseht=0, baseln=0; /* height,basel
/*int istweak = 1;*/ /*true to tweak baseline alignment*/
int rule_raster(), /* draw horizontal line for frac */
lineheight = 1; /* thickness of fraction line */
-int vspace = 1; /*vertical space between components*/
+int vspace = (size>2?2:1); /*vertical space between components*/
int delete_subraster(); /*free work areas in case of error*/
int type_raster(); /* display debugging output */
/* -------------------------------------------------------------------------
@@ -6588,13 +7503,15 @@ subwidth = (subsp->image)->width; /* an
pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
/* --- determine desired width, height of accent --- */
accwidth = subwidth; /* same width as subexpr */
-accheight = 3; /* default for bars */
+accheight = 4; /* default for bars */
switch ( accent )
{ default: break; /* default okay */
case DOTACCENT: case DDOTACCENT:
- accheight = (size<3? 3:4); /* default for dots */
+ accheight = (size<4? 3:4); /* default for dots */
break;
- case HATACCENT: case VECACCENT:
+ case VECACCENT:
+ vspace = 1; /* set 1-pixel vertical space */
+ case HATACCENT:
accheight = 7; /* default */
if ( subwidth < 10 ) accheight = 5; /* unless small width */
else if ( subwidth > 25 ) accheight = 9; /* or large */
@@ -6640,7 +7557,7 @@ end_of_job:
/* ==========================================================================
- * Function: rastfont (expression,size,basesp,font,arg2,arg3)
+ * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
* Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
* to char(s) within {}'s rendered at size
* --------------------------------------------------------------------------
@@ -6652,7 +7569,7 @@ end_of_job:
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
- * font (I) int containing 1 for \cal{}, 2 for \scr{}
+ * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
* arg2 (I) int unused
* arg3 (I) int unused
* --------------------------------------------------------------------------
@@ -6663,7 +7580,7 @@ end_of_job:
* ======================================================================= */
/* --- entry point --- */
subraster *rastfont ( char **expression, int size, subraster *basesp,
- int font, int arg2, int arg3 )
+ int ifontnum, int arg2, int arg3 )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
@@ -6671,39 +7588,43 @@ 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 */
- istext = 0; /* set true for text type */
+char *name = NULL; /* fontinfo[ifontnum].name */
+int family = 0, /* fontinfo[ifontnum].family */
+ istext = 0, /* fontinfo[ifontnum].istext */
+ class = 0; /* fontinfo[ifontnum].class */
subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
*rastflags(); /* or just set flag to switch font */
-int oldsquashmargin = squashmargin; /* turn off squash in text mode */
-int blanksignal = (-991234); /*rastsquash signal right-hand blank*/
+int oldsmashmargin = smashmargin; /* turn off smash in text mode */
+#if 0
/* --- fonts recognized by rastfont --- */
-static int nfonts = 5; /* legal font #'s are 1...nfonts */
+static int nfonts = 6; /* legal font #'s are 1...nfonts */
static struct {char *name; int class;}
fonts[] =
{ /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
- { "\\badfont", 0 },
- { "\\cal", 1 }, /*(1) calligraphic, uppercase */
- { "\\scr", 1 }, /*(2) rsfs/script, uppercase */
- { "\\rm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
- { "\\it", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
- { "\\bb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
+ { "\\math", 0 },
+ { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
+ { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
+ { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
+ { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
+ { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
+ { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
{ NULL, 0 }
} ; /* --- end-of-fonts[] --- */
+#endif
/* -------------------------------------------------------------------------
first get font name and class to determine type of conversion desired
-------------------------------------------------------------------------- */
-if ( font<=0 || font>nfonts ) font=0; /* set error if out-of-bounds */
-name = fonts[font].name; /* font name */
-class = fonts[font].class; /* font class */
-if ( font==3 || font==4 ) /* text (respect blanks) */
- { istext = 1; /* signal text mode */
- squashmargin = 0; } /* don't squash internal blanks */
+if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
+name = fontinfo[ifontnum].name; /* font name */
+family = fontinfo[ifontnum].family; /* font family */
+istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
+class = fontinfo[ifontnum].class; /* font class */
+if ( istext ) /* text (respect blanks) */
+ smashmargin = 0; /* don't smash internal blanks */
/* -------------------------------------------------------------------------
now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
-------------------------------------------------------------------------- */
-if ( class < 0 ) /* not character-by-character */
+if ( 1 || class<0 ) /* not character-by-character */
{
/* ---
if \font not immediately followed by { then it has no arg, so just set flag
@@ -6711,15 +7632,8 @@ if ( class < 0 ) /* not character-by-c
if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
{
if ( msgfp!=NULL && msglevel>=99 )
- fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font);
- switch ( font ) /* set flag by our internal font# */
- { case 3: /* \rm, \text sets flag istext=1 */
- fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break;
- case 4: /* \it, \text sets flag istext=2 */
- fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break;
- case 5: /* \bb, \mathbb sets flag istext=3 */
- fontsp = rastflags(expression,size,basesp,ISTEXT,3,arg3); break;
- default: break; } /* unrecognized, set no flags */
+ fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
+ fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
goto end_of_job;
} /* --- end-of-if(*(*expression)!='{') --- */
/* ---
@@ -6752,11 +7666,12 @@ else /* character-by-character */
for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
{
if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
- { if ( 0 || istext ) /* and we're in a text mode */
+ { if ( 0 || istext ) /* and we're in a text mode font */
strcat(subexpr,"\\;"); } /* so respect whitespace */
else /* char to be displayed in font */
{ int exprlen = 0; /* #chars in subexpr before fchar */
int isinclass = 0; /* set true if fchar in font class */
+ /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
switch ( class ) /* check if fchar is in font class */
{ default: break; /* no chars in unrecognized class */
case 1: if ( isupper((int)fchar) ) isinclass=1; break;
@@ -6790,9 +7705,9 @@ if ( (fontsp = rasterize(subexpr,size))
back to caller with chars rendered in font
-------------------------------------------------------------------------- */
end_of_job:
- squashmargin = oldsquashmargin; /* restore squash */
- if ( istext && fontsp!=NULL ) /* raster contains text */
- fontsp->type = blanksignal; /* signal nosquash */
+ smashmargin = oldsmashmargin; /* restore smash */
+ if ( istext && fontsp!=NULL ) /* raster contains text mode font */
+ fontsp->type = blanksignal; /* signal nosmash */
return ( fontsp ); /* chars rendered in font */
} /* --- end-of-function rastfont() --- */
@@ -7328,7 +8243,8 @@ while ( 1 ) /* scan chars till end */
{ skipwhite(tokptr); /* flush whitespace after \hline */
if ( *tokptr == '\000' /* end-of-expression after \hline */
|| isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
- istokwhite = ishonly = 1; /* so token contains \hline only */
+ { istokwhite = 1; /* so token contains \hline only */
+ if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
else /* token contains more than \hline */
strcpy(token,tokptr); } /* so flush \hline from token */
} /* --- end-of-if(ncols[nrows]==0) --- */
@@ -7452,6 +8368,9 @@ for ( irow=0; irow<=nrows; irow++ ) /*to
for ( icol=0; icolbaseline;/*V offset (init for baseline)*/
/* --- adjust leftcol for vline to left of icol, if present ---- */
- leftcol += vlinespace(icol); /* space for vline to left of col */
+ /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
/* --- reset justification (if not left-justified) --- */
if ( justify[icol] == 0 ) /* but user wants it centered */
tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
@@ -7761,10 +8680,12 @@ char *texsubexpr(),linexpr[257], *xptr=l
subraster *new_subraster(), *linesp=NULL; /* subraster for line */
/*char *origexpression = *expression;*/ /*original expression after \line*/
int pixsz = 1; /* pixels are one bit each */
+int thickness = 1; /* line thickness */
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 width=0, height=0, /* #pixels width,height of line */
+ rwidth=0, rheight=0; /*alloc width,height plus thickness*/
int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
int line_raster(); /* draw line in linesp->image */
@@ -7774,7 +8695,10 @@ obtain (xinc,yinc) arguments immediately
/* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
*expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
-/* --- now interpret xinc,yinc returned in linexpr --- */
+/* --- now interpret xinc,yinc;thickness returned in linexpr --- */
+if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
+ { *xptr = '\000'; /* terminate linexpr at ; */
+ thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */
if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
*xptr = '\000'; /* found it, so replace ',' by '\0'*/
if ( *linexpr != '\000' ) /* check against missing 1st arg */
@@ -7803,8 +8727,10 @@ calculate width,height, etc, based on xl
xlen = absval(xlen); /* force xlen positive */
ylen = absval(ylen); /* force ylen positive */
/* --- calculate corresponding lengths in pixels --- */
-width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
-height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
+width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
+height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
+rwidth = width + (ylen<0.001?0:max2(0,thickness-1));
+rheight = height + (xlen<0.001?0:max2(0,thickness-1));
/* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
@@ -7815,16 +8741,18 @@ if ( msgfp!=NULL && msglevel>=29 ) /* de
/* -------------------------------------------------------------------------
allocate subraster and raster for complete picture
-------------------------------------------------------------------------- */
-/* --- sanity check on width,height args --- */
+/* --- sanity check on width,height,thickness args --- */
if ( width < 1 || width > 600
-|| height < 1 || height > 600 ) goto end_of_job;
+|| height < 1 || height > 600
+|| thickness<1||thickness>25 ) goto end_of_job;
/* --- allocate and initialize subraster for constructed line --- */
-if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */
+if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
== NULL ) goto end_of_job; /* quit if failed */
/* --- initialize line subraster parameters --- */
linesp->type = IMAGERASTER; /* image */
linesp->symdef = NULL; /* not applicable for image */
-linesp->baseline = height/2 + 2; /* is a little above center good? */
+linesp->baseline = height/2 + 2 /* is a little above center good? */
+ + (rheight-height)/2; /* account for line thickness too */
linesp->size = size; /* size (probably unneeded) */
/* -------------------------------------------------------------------------
draw the line
@@ -7834,7 +8762,7 @@ line_raster ( linesp->image, /* embedde
(isright? width-1 : 0), /* col0, from left or right */
(istop? height-1 : 0), /* row1, to top or bottom */
(isright? 0 : width-1), /* col1, to right or left */
- 1 ); /* line thickness is 1 pixel */
+ thickness ); /* line thickness (usually 1 pixel)*/
/* -------------------------------------------------------------------------
return constructed line to caller
-------------------------------------------------------------------------- */
@@ -8381,9 +9309,9 @@ char *texsubexpr(), tag[512]="\000", fil
subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
int status, rastreadfile(); /* read input file */
int format=0, npts=0; /* don't reformat (numerical) input */
-char subexpr[8192], /* concatanated lines from input file */
+char subexpr[8192] = "\000", /* concatanated lines from input file */
*mimeprep(), /* preprocess inputted data */
- *dtoa(), *reformat=NULL; /* reformat numerical input */
+ *dbltoa(), *reformat=NULL; /* reformat numerical input */
/* -------------------------------------------------------------------------
obtain [tag]{filename} argument
-------------------------------------------------------------------------- */
@@ -8391,8 +9319,8 @@ obtain [tag]{filename} argument
if ( *(*expression) == '[' ) /* check for []-enclosed value */
{ char argfld[2048]; /* optional argument field */
*expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);
- if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /* dtoa requested */
- { format = 1; /* signal dtoa() format */
+ if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
+ { format = 1; /* signal dtoa()/dbltoa() format */
if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
if ( format == 0 ) /* reformat not requested */
@@ -8409,14 +9337,14 @@ if ( *filename != '\000' /* got filenam
/* --------------------------------------------------------------------------
Read file and rasterize constructed subexpression
-------------------------------------------------------------------------- */
-status = rastreadfile(filename,tag,subexpr); /* read file */
+status = rastreadfile(filename,0,tag,subexpr); /* read file */
if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
/* --- rasterize input subexpression --- */
mimeprep(subexpr); /* preprocess subexpression */
-if ( format == 1 ) /* dtoa() */
+if ( format == 1 ) /* dtoa()/dbltoa() */
{ double d = strtod(subexpr,NULL); /* interpret subexpr as double */
if ( d != 0.0 ) /* conversion to double successful */
- if ( (reformat=dtoa(d,npts)) != NULL ) /* reformat successful */
+ if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
inputsp = rasterize(subexpr,size); /* rasterize subexpression */
/* --- return input image to caller --- */
@@ -8447,8 +9375,8 @@ end_of_job:
* requested, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
- * \counter[value][logfile]{filename}
- * o
+ * \counter[value][logfile]{filename:tag}
+ * o :tag is optional
* ======================================================================= */
/* --- entry point --- */
subraster *rastcounter ( char **expression, int size, subraster *basesp,
@@ -8458,18 +9386,22 @@ subraster *rastcounter ( char **expressi
Allocations and Declarations
-------------------------------------------------------------------------- */
char *texsubexpr(), filename[1024]="\000", /* counter file */
- logfile[1024]="\000", tag[512]="\000"; /* log file and tag */
+ logfile[1024]="\000", tag[512]="\000"; /*optional log file and tag*/
subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
-int rastreadfile(), rastwritefile(); /* to read and write counter file */
-char text[2048] = "1_", /* first (and only) line in counter file */
+int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
+ isstrict = 1; /* true to only write to existing files */
+char text[8192] = "1_", /* only line in counter file without tags */
*delim = NULL, /* delimiter in text */
- utext[32] = "1_", /* default delimiter */
+ utext[128] = "1_", /* default delimiter */
*udelim = utext+1; /* underscore delimiter */
-char *timestamp(), /* timestamp for logging */
- *dtoa(); /* double to comma-separated */
+char *rasteditfilename(), /* edit log file name */
+ *timestamp(), /* timestamp for logging */
+ *dbltoa(); /* double to comma-separated ascii */
int counter = 1, /* atoi(text) (after _ removed, if present) */
- gotcount = 0, /* set true once counter value determined */
+ value = 1, /* optional [value] argument */
+ gotvalue = 0, /* set true if [value] supplied */
+ isdelta = 0, /* set true if [+value] or [-value] is delta*/
ordindex = (-1); /* ordinal[] index to append ordinal suffix */
/*--- ordinal suffixes based on units digit of counter ---*/
static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
@@ -8480,28 +9412,34 @@ first obtain optional [value][logfile] a
-------------------------------------------------------------------------- */
/* --- first check for optional \counter[value] --- */
if ( *(*expression) == '[' ) /* check for []-enclosed value */
- { *expression = texsubexpr(*expression,text,2047,"[","]",0,0);
- if ( *text != '\000' ) /* got counter value */
+ { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
+ if ( *text != '\000' ) /* got counter value (or logfile) */
if ( strlen(text) >= 1 ) /* and it's not an empty string */
- if ( isdigit((int)(*text)) ) /* leading 0-9 digit signals value */
- { counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */
- gotcount = 1; } /* signal we got counter value */
- else /* not a digit, so must be logfile */
- strcpy(logfile,text); /* so just copy it */
+ if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
+ gotvalue = 1; /* signal we got optional value */
+ else /* not +-digit, so must be logfile */
+ strcpy(logfile,text); /* so just copy it */
} /* --- end-of-if(**expression=='[') --- */
/* --- next check for optional \counter[][logfile] --- */
if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
{ *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
- if ( *text != '\000' ) /* got logfile value */
+ if ( *filename != '\000' ) /* got logfile (or counter value) */
if ( strlen(filename) >= 1 ) /* and it's not an empty string */
- if ( !(isdigit((int)(*filename))) /*leading non-digit signals logfile*/
- || gotcount ) /* or we already got counter value */
- strcpy(logfile,filename); /* so just copy it */
- else /* 0-9 digit, so must be value */
- { strcpy(text,filename); /* copy value to text line */
- counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */
- gotcount = 1; } /* signal we got counter value */
+ if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
+ || gotvalue ) /* or we already got counter value */
+ strcpy(logfile,filename); /* so just copy it */
+ else /* leading +-digit must be value */
+ { strcpy(text,filename); /* copy value to text line */
+ gotvalue = 1; } /* and signal we got optional value*/
} /* --- end-of-if(**expression=='[') --- */
+/* --- evaluate [value] if present --- */
+if ( gotvalue ) { /*leading +-digit should be in text*/
+ if ( *text == '+' ) isdelta = (+1); /* signal adding */
+ if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
+ value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
+ if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
+ counter = value; /* re-init counter */
+ } /* --- end-of-if(gotvalue) --- */
/* -------------------------------------------------------------------------
obtain counter {filename} argument
-------------------------------------------------------------------------- */
@@ -8519,22 +9457,27 @@ Read and parse file, increment and rewri
if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
{
/* --- read and interpret first (and only) line from counter file --- */
- if ( !gotcount ) /* if no [count] argument supplied */
- if ( rastreadfile(filename,tag,text) != 0 ) /* try reading it from file */
- { counter= 1 + (int)(strtod(text,&udelim)+0.1); /*counter val and delim*/
- gotcount = 1; } /* signal we got counter value */
+ if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
+ if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
+ { char *vdelim = NULL; /* underscore delim from file */
+ double fileval = strtod(text,&vdelim); /* value and delim from file */
+ counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
+ counter += value; /* bump count by 1 or add/sub delta*/
+ if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
/* --- check for ordinal suffix --- */
if ( udelim != (char *)NULL ) /* have some delim after value */
if ( *udelim == '_' ) /* underscore signals ordinal */
- { ordindex = counter%10; /* least significant digit */
- if ( counter >= 10 ) /* counter is 10 or greater */
- if ( (counter/10)%10 == 1 ) /* and the last two are 10-19 */
+ { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
+ ordindex = abscount%10; /* least significant digit */
+ if ( abscount >= 10 ) /* counter is 10 or greater */
+ if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
/* --- rewrite counter file --- */
- sprintf(text,"%d",counter); /*build image of incremented counter*/
- if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
- if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
- rastwritefile(filename,tag,text,1); /* rewrite incremented counter */
+ if ( status >= 0 ) /* file was read okay */
+ { sprintf(text,"%d",counter); /*build image of incremented counter*/
+ if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
+ if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
+ status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
} /* --- end-of-if(strlen(filename)>1) --- */
/* --------------------------------------------------------------------------
log counter request
@@ -8543,35 +9486,44 @@ if ( strlen(logfile) > 1 ) /* optional
{
char comment[1024] = "\000", /* embedded comment, logfile:comment*/
*commptr = strchr(logfile,':'); /* check for : signalling comment */
+ int islogokay = 1; /* logfile must exist if isstrict */
if ( commptr != NULL ) /* have embedded comment */
{ strcpy(comment,commptr+1); /* comment follows : */
*commptr = '\000'; } /* null-terminate actual logfile */
- if ( (logfp = fopen(logfile,"a")) /* open logfile */
- != (FILE *)NULL ) /* opened successfully */
- {
- int ilog=0; /* logvars[] index */
- fprintf(logfp,"%s ",timestamp()); /* first emit timestamp */
- if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
- else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
- fprintf(logfp,"=%d",counter); /* emit counter value */
- for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
- if ( ilog == commentvar /* replace with comment... */
- && commptr != NULL ) /* ...if available */
- fprintf(logfp," %.256s",comment); /* log embedded comment */
- else
- { char *logval = getenv(logvars[ilog]); /* getenv(variable) to be logged*/
- fprintf(logfp," %.64s", /* log variable */
+ strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
+ if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
+ else if ( isstrict ) { /*okay, but only write if it exists*/
+ if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
+ islogokay = 0; /* so don't write log file */
+ else fclose(logfp); } /* close file opened for test read */
+ if ( islogokay ) /* okay to write logfile */
+ if ( (logfp = fopen(logfile,"a")) /* open logfile */
+ != (FILE *)NULL ) { /* opened successfully for append */
+ int ilog=0; /* logvars[] index */
+ fprintf(logfp,"%s ",timestamp(TZDELTA,0)); /* first emit timestamp */
+ if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
+ else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
+ fprintf(logfp,"=%d",counter); /* emit counter value */
+ if ( status < 1 ) /* read or re-write failed */
+ fprintf(logfp,"(%s %d)","error status",status); /* emit error */
+ for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
+ if ( ilog == commentvar /* replace with comment... */
+ && commptr != NULL ) /* ...if available */
+ fprintf(logfp," %.256s",comment); /* log embedded comment */
+ else
+ { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
+ fprintf(logfp," %.64s", /* log variable */
(logval!=NULL?logval:"")); } /* emit value or */
- fprintf(logfp,"\n"); /* terminating newline */
- fclose(logfp); /* close logfile */
- } /* --- end-of-if(logfp!=NULL) --- */
+ fprintf(logfp,"\n"); /* terminating newline */
+ fclose(logfp); /* close logfile */
+ } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
} /* --- end-of-if(strlen(logfile)>1) --- */
/* --------------------------------------------------------------------------
construct counter expression and rasterize it
-------------------------------------------------------------------------- */
/* --- construct expression --- */
/*sprintf(text,"%d",counter);*/ /* start with counter */
-strcpy(text,dtoa(((double)counter),0)); /* comma-separated counter value */
+strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
{ strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
@@ -8585,6 +9537,135 @@ countersp = rasterize(text,size); /* ras
/* ==========================================================================
+ * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: handle \today
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \today,
+ * and returning ptr immediately
+ * following last character processed.
+ * size (I) int containing 0-5 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \today
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) subraster ptr to date stamp
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rasttoday ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
+char *timestamp(), *today=optarg; /* timestamp to be rasterized */
+subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
+int ifmt=1, tzdelta=0; /* default timestamp() args */
+/* -------------------------------------------------------------------------
+Get optional args \today[+/-tzdelta,ifmt]
+-------------------------------------------------------------------------- */
+/* --- check for optional \today[+/-tzdelta,ifmt] --- */
+if ( *(*expression) == '[' ) /* check for []-enclosed value */
+ { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
+ if ( *optarg != '\000' ) /* got optional arg */
+ { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
+ int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
+ if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
+ for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
+ { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
+ if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
+ tzdelta = atoi(arg); /* so interpret arg as tzdelta */
+ else ifmt = atoi(arg); } /* else interpret args as ifmt */
+ } /* --- end-of-if(*optarg!='\0') --- */
+ } /* --- end-of-if(**expression=='[') --- */
+/* -------------------------------------------------------------------------
+Get timestamp and rasterize it
+-------------------------------------------------------------------------- */
+strcpy(today,"\\text{"); /* rasterize timestamp as text */
+strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
+strcat(today,"}"); /* terminate \text{} braces */
+todaysp = rasterize(today,size); /* rasterize timestamp */
+/* --- return timestamp raster to caller --- */
+/*end_of_job:*/
+ return ( todaysp ); /* return timestamp to caller */
+} /* --- end-of-function rasttoday() --- */
+
+
+/* ==========================================================================
+ * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: handle \calendar
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \calendar
+ * and returning ptr immediately
+ * following last character processed.
+ * size (I) int containing 0-5 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \calendar
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastcalendar ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
+char *calendar(), *calstr=NULL; /* calendar to be rasterized */
+subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
+int year=0,month=0,day=0, argval=0; /* default calendar() args */
+/* -------------------------------------------------------------------------
+Get optional args \today[+/-tzdelta,ifmt]
+-------------------------------------------------------------------------- */
+/* --- check for optional \calendar[year,month] --- */
+if ( *(*expression) == '[' ) /* check for []-enclosed value */
+ { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
+ if ( *optarg != '\000' ) /* got optional arg */
+ { char *comma = strchr(optarg,','), /* comma between year,month */
+ *comma2 = NULL; /* second comma before day */
+ int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
+ if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
+ if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
+ { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
+ for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
+ { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
+ argval = atoi(arg); /* interpret arg as integer */
+ if ( iarg < 3 ) /* first two args are month,year */
+ {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
+ else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
+ else /* only 3rd arg can be day */
+ if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
+ } /* --- end-of-if(*optarg!='\0') --- */
+ } /* --- end-of-if(**expression=='[') --- */
+/* -------------------------------------------------------------------------
+Get calendar string and rasterize it
+-------------------------------------------------------------------------- */
+if ( msgfp!= NULL && msglevel>=9 )
+ fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
+ year,month,day);
+calstr = calendar(year,month,day); /* get calendar string */
+calendarsp = rasterize(calstr,size); /* rasterize calendar string */
+/* --- return calendar raster to caller --- */
+/*end_of_job:*/
+ return ( calendarsp ); /* return calendar to caller */
+} /* --- end-of-function rastcalendar() --- */
+
+
+/* ==========================================================================
* Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
* Purpose: no op -- flush \escape without error
* --------------------------------------------------------------------------
@@ -8594,7 +9675,7 @@ countersp = rasterize(text,size); /* ras
* following last character processed.
* size (I) int containing 0-5 default font size
* basesp (I) subraster * to character (or subexpression)
- * immediately preceding \fbox
+ * immediately preceding \escape
* (unused, but passed for consistency)
* nargs (I) int containing number of {}-args after
* \escape to be flushed along with it
@@ -8648,27 +9729,15 @@ FILE *rastopenfile ( char *filename, cha
Allocations and Declarations
-------------------------------------------------------------------------- */
FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
-char texfile[2048] = "\000", /* local copy of input filename */
+char texfile[2048] = "\000", /* local, edited copy of filename */
+ *rasteditfilename(), /* prepend pathprefix if necessary */
amode[128] = "r"; /* test open mode if arg mode=NULL */
-int ismode = 0, /* true of mode!=NULL */
- isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
+int ismode = 0; /* true of mode!=NULL */
/* --------------------------------------------------------------------------
Check mode and open file
-------------------------------------------------------------------------- */
-/* --- check filename --- */
-if ( filename != (char *)NULL ) /* caller passed filename arg */
- if ( strlen(filename) >= 1 ) /* make sure we got actual filename*/
- { char *pfilename = filename; /* ptr to 1st char of filename */
- *texfile = '\000'; /* init filename as null string */
- while ( isthischar(*pfilename," /\\") ) /* absolute paths invalid */
- pfilename++; /* so flush leading / or \ (or ' ')*/
- if ( isprefix && *pfilename!='\000' ) /* paths preceded by prefix */
- { strcat(texfile,pathprefix); /* init filename with path */
- while ( memcmp(pfilename,"../",3)==0 /* have leading ../ */
- || memcmp(pfilename,"..\\",3)==0 ) /* or ..\ with prefix */
- pfilename += 3; } /* flush leading ../ or ..\ */
- strcat(texfile,pfilename); /* local copy of given filename */
- compress(texfile,' '); } /* remove embedded blanks */
+/* --- edit filename --- */
+strcpy(texfile,rasteditfilename(filename)); /*edited copy of input filename*/
/* --- check mode --- */
if ( mode != (char *)NULL ) /* caller passed mode arg */
if ( *mode != '\000' ) /* and it's not an empty string */
@@ -8697,13 +9766,67 @@ if ( !ismode && fp!=NULL ) /* no mode,
/* ==========================================================================
- * Function: rastreadfile ( filename, tag, value )
+ * Function: rasteditfilename ( filename )
+ * Purpose: edits filename to remove security problems,
+ * e.g., removes all ../'s and ..\'s.
+ * --------------------------------------------------------------------------
+ * Arguments: filename (I) char * to null-terminated string containing
+ * name of file to be edited
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) pointer to edited filename,
+ * or empty string "\000" if any problem
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+char *rasteditfilename ( char *filename )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+static char editname[2048]; /*edited filename returned to caller*/
+char *strchange(); /* prepend pathprefix if necessary */
+int strreplace(), /* remove ../'s and ..\'s */
+ isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
+/* --------------------------------------------------------------------------
+edit filename
+-------------------------------------------------------------------------- */
+/* --- first check filename arg --- */
+*editname = '\000'; /* init edited name as empty string*/
+if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
+if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
+/* --- init edited filename --- */
+strcpy(editname,filename); /* init edited name as input name */
+compress(editname,' '); /* remove embedded blanks */
+/* --- remove leading or embedded ....'s --- */
+while ( strreplace(editname,"....",NULL,0) > 0 ) ; /* squeeze out ....'s */
+/* --- remove leading / and \ and dots (and blanks) --- */
+if ( *editname != '\000' ) /* still have chars in filename */
+ while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
+ strcpy(editname,editname+1); /* so flush leading / or \ (or ' ')*/
+if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
+/* --- remove leading or embedded ../'s and ..\'s --- */
+while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */
+while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
+while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* and ../'s again */
+/* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
+if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
+ strchange(0,editname,pathprefix); /* so prepend prefix */
+end_of_job:
+ return ( editname ); /* back with edited filename */
+} /* --- end-of-function rasteditfilename() --- */
+
+
+/* ==========================================================================
+ * Function: rastreadfile ( filename, islock, tag, value )
* Purpose: Read filename, returning value as string
* between ... or entire file if tag=NULL passed.
* --------------------------------------------------------------------------
* Arguments: filename (I) char * to null-terminated string containing
* name of file to read (preceded by path
* relative to mimetex executable)
+ * islock (I) int containing 1 to lock file while reading
+ * (hopefully done by opening in "r+" mode)
* tag (I) char * to null-terminated string containing
* html-like tagname. File contents between
* and will be returned, or
@@ -8716,7 +9839,7 @@ if ( !ismode && fp!=NULL ) /* no mode,
* Notes: o
* ======================================================================= */
/* --- entry point --- */
-int rastreadfile ( char *filename, char *tag, char *value )
+int rastreadfile ( char *filename, int islock, char *tag, char *value )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
@@ -8726,8 +9849,9 @@ char texfile[2048] = "\000", /* local c
text[4096]; /* line from input file */
char *tagp, tag1[512], tag2[512]; /* left and right */
int vallen=0, maxvallen=8000; /* #chars in value, max allowed */
-int status = 0; /* status returned, 1=okay */
+int status = (-1); /* status returned, 1=okay */
int tagnum = 0; /* tag we're looking for */
+/*int islock = 1;*/ /* true to lock file */
/* --------------------------------------------------------------------------
Open file
-------------------------------------------------------------------------- */
@@ -8737,11 +9861,13 @@ if ( value == (char *)NULL ) goto end_of
/* --- open filename or filename.tex --- */
if ( filename != (char *)NULL ) /* make sure we got filename arg */
{ strcpy(texfile,filename); /* local copy of filename */
- fp = rastopenfile(texfile,"r"); } /* try opening it */
+ fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
/* --- check that file opened --- */
if ( fp == (FILE *)NULL ) /* failed to open file */
{ sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
goto end_of_job; } /* return error message to caller */
+status = 0; /* file opened successfully */
+if ( islock ) rewind(fp); /* start at beginning of file */
/* --------------------------------------------------------------------------
construct 's
-------------------------------------------------------------------------- */
@@ -8757,7 +9883,7 @@ Read file, concatnate lines
-------------------------------------------------------------------------- */
while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */
switch ( tagnum ) { /* look for left- or right-tag */
- case 0: break; /* no tag to look for */
+ case 0: status = 1; break; /* no tag to look for */
case 1: /* looking for opening left */
if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */
@@ -8766,6 +9892,7 @@ while ( fgets(text,4090,fp) != (char *)N
if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
*tagp = '\000'; /* terminate line at tag */
tagnum = 3; /* done after this line */
+ status = 1; /* successfully read tag */
break;
} /* ---end-of-switch(tagnum) --- */
if ( tagnum != 1 ) { /* no tag or left tag already found*/
@@ -8821,6 +9948,7 @@ int istag=0, rastreadfile(), /* read fil
/*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
isnewfile = 0, /* true if writing new file */
status = 0; /* status returned, 1=okay */
+int istimestamp = 0; /* true to update tag */
/* --------------------------------------------------------------------------
check args
-------------------------------------------------------------------------- */
@@ -8850,8 +9978,8 @@ read existing file if just rewriting a s
*filebuff = '\000'; /* init as empty file */
if ( !isnewfile ) /* if file already exists */
if ( istag ) /* and just rewriting one tag */
- if ( rastreadfile(texfile,NULL,filebuff) /* read entire existing file */
- == 0 ) goto end_of_job; /* signal error if failed to read */
+ if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
+ <= 0 ) goto end_of_job; /* signal error if failed to read */
/* --------------------------------------------------------------------------
construct new file data if needed (entire file replaced by value if no tag)
-------------------------------------------------------------------------- */
@@ -8908,13 +10036,15 @@ if ( fputs((istag?filebuff:value),fp) /*
!= EOF ) status = 1; /* signal success if succeeded */
fclose ( fp ); /* close output file after writing */
/* --- modify timestamp --- */
-if ( istag ) /* log mod time in tagged file */
- if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
- { char fbuff[2048]; /* field buff value */
- strcpy(fbuff,tag); /* tag modified */
- strcat(fbuff," modified at "); /* spacer */
- strcat(fbuff,timestamp()); /* start with timestamp */
- rastwritefile(filename,"timestamp",fbuff,1); }
+if ( status > 0 ) /*forget timestamp if write failed*/
+ if ( istimestamp ) /* if we're updating timestamp */
+ if ( istag ) /* only log time in tagged file */
+ if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
+ { char fbuff[2048]; /* field buff value */
+ strcpy(fbuff,tag); /* tag modified */
+ strcat(fbuff," modified at "); /* spacer */
+ strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
+ status = rastwritefile(filename,"timestamp",fbuff,1); }
/* --- return status to caller --- */
end_of_job:
return ( status ); /* return status to caller */
@@ -8922,11 +10052,117 @@ end_of_job:
/* ==========================================================================
- * Function: timestamp ( )
+ * Function: calendar ( year, month, day )
+ * Purpose: returns null-terminated character string containing
+ * \begin{array}...\end{array} for the one-month calendar
+ * specified by year=1973...2099 and month=1...12.
+ * If either arg out-of-range, today's value is used.
+ * --------------------------------------------------------------------------
+ * Arguments: year (I) int containing 1973...2099 or 0 for current
+ * year
+ * month (I) int containing 1...12 or 0 for current month
+ * day (I) int containing day to emphasize or 0
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) char ptr to null-terminated buffer
+ * containing \begin{array}...\end{array}
+ * string that will render calendar for
+ * requested month, or NULL for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+char *calendar( int year, int month, int day )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+static char calbuff[4096]; /* calendar returned to caller */
+time_t time_val = (time_t)(0); /* binary value returned by time() */
+struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
+int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
+int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
+char aval[64]; /* ascii day or 4-digit year */
+/* --- calendar data --- */
+static char *monthnames[] = { "?", "January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October",
+ "November", "December", "?" } ;
+static int modays[] =
+ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+/* --- get current date/time --- */
+time((time_t *)(&time_val)); /* get date and time */
+tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
+yy = 1900 + (int)(tmstruct->tm_year); /* current four-digit year */
+mm = 1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
+dd = (int)(tmstruct->tm_mday); /* current day, 1-31 */
+/* --- check args --- */
+if ( year<1973 || year>2099 ) year = yy; /* current year if out-of-bounds */
+if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
+if ( month==mm && year==yy && day==0 ) /* current month and default day */
+ day = dd; /* emphasize current day */
+modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
+/* --- initialize calendar string --- */
+strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
+strcat(calbuff,"\\small\\text{"); /* month set in roman */
+strcat(calbuff,monthnames[month]); /* insert month name */
+strcat(calbuff," }"); /* add a space */
+sprintf(aval,"%d",year); /* convert year to ascii */
+strcat(calbuff,aval); /* add year */
+strcat(calbuff,"\\\\"); /* end top row */
+strcat(calbuff, /* now begin calendar arrayr */
+ "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
+ "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
+ "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
+ "\\tiny\\text{Sat} \\\\ \\hline " );
+/* -------------------------------------------------------------------------
+generate calendar
+-------------------------------------------------------------------------- */
+for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
+ {
+ /* --- get day-of-week for this day --- */
+ iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
+ if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
+ /* --- may need empty cells at beginning of month --- */
+ if ( idd == 1 ) /* first day of month */
+ if ( iday > 0 ) /* need to skip cells */
+ { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
+ aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
+ strcat(calbuff,aval); } /* add skip string to buffer */
+ /* --- add idd to current cell --- */
+ sprintf(aval,"%d",idd); /* convert idd to ascii */
+ if ( idd == day /* emphasize today's date */
+ /*&& month==mm && year==yy*/ ) /* only if this month's calendar */
+ { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
+ strcat(calbuff,aval); /* put in idd */
+ strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
+ else /* not today's date */
+ strcat(calbuff,aval); /* so just put in idd */
+ /* --- terminate cell --- */
+ if ( idd < modays[month] ) /* not yet end-of-month */
+ if ( iday < 6 ) /* still have days left in week */
+ strcat(calbuff,"&"); /* new cell in same week */
+ else /* reached end-of-week */
+ strcat(calbuff,"\\\\ \\hline"); /* so start new week */
+ } /* --- end-of-for(idd) --- */
+strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
+/* --- return calendar to caller --- */
+strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
+return ( calbuff ); /* back to caller with calendar */
+} /* --- end-of-function calendar() --- */
+
+
+/* ==========================================================================
+ * Function: timestamp ( tzdelta, ifmt )
* Purpose: returns null-terminated character string containing
* current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
* --------------------------------------------------------------------------
- * Arguments: ( none )
+ * Arguments: tzdelta (I) integer, positive or negative, containing
+ * containing number of hours to be added or
+ * subtracted from system time (to accommodate
+ * your desired time zone).
+ * ifmt (I) integer containing 0 for default format
* --------------------------------------------------------------------------
* Returns: ( char * ) ptr to null-terminated buffer
* containing current date:time stamp
@@ -8934,40 +10170,209 @@ end_of_job:
* Notes: o
* ======================================================================= */
/* --- entry point --- */
-char *timestamp( )
+char *timestamp( int tzdelta, int ifmt )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
-static char timebuff[64]; /* date:time buffer back to caller */
+static char timebuff[256]; /* date:time buffer back to caller */
/*long time_val = 0L;*/ /* binary value returned by time() */
time_t time_val = (time_t)(0); /* binary value returned by time() */
struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
-int year=0, hour=0,ispm=1; /* adjust year, and set am/pm hour */
+int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
+ month=0, day=0; /* adjust day and month for delta */
+int tzadjust(); /* time zone adjustment function */
+int daynumber(); /* #days since Jan 1, 1973 */
+static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday", "Sunday" } ;
+static char *monthnames[] = { "?", "January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October",
+ "November", "December", "?" } ;
/* -------------------------------------------------------------------------
get current date:time, adjust values, and and format stamp
-------------------------------------------------------------------------- */
+/* --- first init returned timebuff in case of any error --- */
+*timebuff = '\000';
/* --- get current date:time --- */
time((time_t *)(&time_val)); /* get date and time */
tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
-/* --- adjust year and hour as necessary --- */
-year = (int)(tmstruct->tm_year); /* local copy of year */
-hour = (int)(tmstruct->tm_hour); /* local copy of hour */
+/* --- extract fields --- */
+year = (int)(tmstruct->tm_year); /* local copy of year, 0=1900 */
+month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
+day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */
+hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */
+/* --- adjust year --- */
year += 1900; /* set century in year */
-if ( hour < 12 ) /* am check */
- { ispm=0; /* reset pm flag */
- if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
-if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
+/* --- adjust for timezone --- */
+tzadjust(tzdelta,&year,&month,&day,&hour);
+/* --- check params --- */
+if ( hour<0 || hour>23
+|| day<1 || day>31
+|| month<1 || month>12
+|| year<1973 ) goto end_of_job;
+/* --- adjust hour for am/pm --- */
+switch ( ifmt )
+ {
+ default:
+ case 0:
+ if ( hour < 12 ) /* am check */
+ { ispm=0; /* reset pm flag */
+ if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
+ if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
+ break;
+ } /* --- end-of-switch(ifmt) --- */
/* --- format date:time stamp --- */
-sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s",
- year,(int)((tmstruct->tm_mon)+1),(int)(tmstruct->tm_mday),
- hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
-return ( timebuff ); /* return stamp to caller */
+switch ( ifmt )
+ {
+ default:
+ case 0: /* --- 2005-03-05:11:49:59am --- */
+ sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
+ hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
+ break;
+ case 1: /* --- Saturday, March 5, 2005 --- */
+ sprintf(timebuff,"%s, %s %d, %d",
+ daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
+ break;
+ case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
+ sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
+ daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
+ hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
+ break;
+ case 3: /* --- 11:49:59am --- */
+ sprintf(timebuff,"%d:%02d:%02d%s",
+ hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
+ break;
+ } /* --- end-of-switch(ifmt) --- */
+end_of_job:
+ return ( timebuff ); /* return stamp to caller */
} /* --- end-of-function timestamp() --- */
/* ==========================================================================
- * Function: dtoa ( dblval, npts )
+ * Function: tzadjust ( tzdelta, year, month, day, hour )
+ * Purpose: Adjusts hour, and day,month,year if necessary,
+ * by delta increment to accommodate your time zone.
+ * --------------------------------------------------------------------------
+ * Arguments: tzdelta (I) integer, positive or negative, containing
+ * containing number of hours to be added or
+ * subtracted from given time (to accommodate
+ * your desired time zone).
+ * year (I) addr of int containing 4-digit year
+ * month (I) addr of int containing month 1=Jan - 12=Dec.
+ * day (I) addr of int containing day 1-31 for Jan.
+ * hour (I) addr of int containing hour 0-23
+ * Returns: ( int ) 1 for success, or 0 for error
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
+{
+/* --------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
+/* --- calendar data --- */
+static int modays[] =
+ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
+/* --------------------------------------------------------------------------
+check args
+-------------------------------------------------------------------------- */
+if ( mm<1 || mm>12 ) return(-1); /* bad month */
+if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
+if ( hh<0 || hh>23 ) return(-1); /* bad hour */
+if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
+/* --------------------------------------------------------------------------
+make adjustments
+-------------------------------------------------------------------------- */
+/* --- adjust hour --- */
+hh += tzdelta; /* apply caller's delta */
+/* --- adjust for feb 29 --- */
+modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
+/* --- adjust day --- */
+if ( hh < 0 ) /* went to preceding day */
+ { dd--; hh += 24; }
+if ( hh > 23 ) /* went to next day */
+ { dd++; hh -= 24; }
+/* --- adjust month --- */
+if ( dd < 1 ) /* went to preceding month */
+ { mm--; dd = modays[mm]; }
+if ( dd > modays[mm] ) /* went to next month */
+ { mm++; dd = 1; }
+/* --- adjust year --- */
+if ( mm < 1 ) /* went to preceding year */
+ { yy--; mm = 12; dd = modays[mm]; }
+if ( mm > 12 ) /* went to next year */
+ { yy++; mm = 1; dd = 1; }
+/* --- back to caller --- */
+*year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
+return ( 1 );
+} /* --- end-of-function tzadjust() --- */
+
+
+/* ==========================================================================
+ * Function: daynumber ( year, month, day )
+ * Purpose: Returns number of actual calendar days from Jan 1, 1973
+ * to the given date (e.g., bvdaynumber(1974,1,1)=365).
+ * --------------------------------------------------------------------------
+ * Arguments: year (I) int containing year -- may be either 1995 or
+ * 95, or may be either 2010 or 110 for those
+ * years.
+ * month (I) int containing month, 1=Jan thru 12=Dec.
+ * day (I) int containing day of month, 1-31 for Jan, etc.
+ * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
+ * or -1 for error (e.g., year<1973).
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int daynumber ( int year, int month, int day )
+{
+/* --------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+/* --- returned value (note: returned as a default "int") --- */
+int ndays; /* #days since jan 1, year0 */
+/* --- initial conditions --- */
+static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
+ days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
+ days1yr = 365;
+/* --- table of accumulated days per month (last index not used) --- */
+static int modays[] =
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
+/* --- variables for #days since day0 --- */
+int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
+/* --------------------------------------------------------------------------
+Check input
+-------------------------------------------------------------------------- */
+if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
+ return ( -1 ); /* otherwise, forget it */
+if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
+/* --------------------------------------------------------------------------
+Find #days since jan 1, 1973
+-------------------------------------------------------------------------- */
+/* --- figure #complete 4-year periods and #remaining yrs till current --- */
+nyears = year - year0; /* #years since year0 */
+if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
+nfouryrs = nyears/4; /* #complete four-year periods */
+nyears -= (4*nfouryrs); /* remainder excluding current year*/
+/* --- #days from jan 1, year0 till jan 1, this year --- */
+ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
+ + (days1yr*nyears); /* +remaining days */
+/*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
+/* --- add #days within current year --- */
+ndays += (modays[month-1] + (day-1));
+/* --- may need an extra day if current year is a leap year --- */
+if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
+ { if ( month > 2 ) /* past feb so need an extra day */
+ /*if ( year != 100 )*/ /* unless it's 2000AD */
+ ndays++; } /* so add it in */
+return ( (int)(ndays) ); /* #days back to caller */
+} /* --- end-of-function daynumber() --- */
+
+
+/* ==========================================================================
+ * Function: dbltoa ( dblval, npts )
* Purpose: Converts double to ascii, in financial format
* (e.g., comma-separated and negatives enclosed in ()'s).
* -------------------------------------------------------------------------
@@ -8980,9 +10385,9 @@ return ( timebuff ); /* return stamp t
* Notes: o
* ======================================================================= */
/* --- entry point --- */
-char *dtoa ( dblval, npts )
-double dblval;
-int npts;
+char *dbltoa ( double dblval, int npts )
+/* double dblval;
+ int npts; */
{
/* -------------------------------------------------------------------------
Allocations and Declarations
@@ -9048,7 +10453,7 @@ End-of-Job
if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
*finptr = '\000'; /* null-terminate converted double */
return ( finval ); /* converted double back to caller */
-} /* --- end-of-function dtoa() --- */
+} /* --- end-of-function dbltoa() --- */
/* ==========================================================================
@@ -9279,6 +10684,39 @@ for ( irow=0; irow 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
+ if ( ngapsmaxgaps ) continue;
+ } /* --- end-of-if(1) --- */
/* --- antialias if necessary --- */
if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
|| (isfgalias && isfgedge) /* alias pixel surrounding fg */
@@ -9849,7 +11287,7 @@ messages
-------------------------------------------------------------------------- */
static char *copyright = /* copyright, gnu/gpl notice */
"+-----------------------------------------------------------------------+\n"
- "|mimeTeX vers 1.61, Copyright(c) 2002-2005, John Forkosh Associates, Inc|\n"
+ "|mimeTeX vers 1.63, Copyright(c) 2002-2006, 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"
@@ -9890,12 +11328,16 @@ int unescape_url(); /* convert %xx's t
int emitcache(); /* emit cached image if it exists */
int isquery = 0, /* true if input from QUERY_STRING */
isqempty = 0, /* true if query string empty */
+ isqforce = 0, /* true to force query emulation */
isqlogging = 0, /* true if logging in query mode */
isformdata = 0, /* true if input from html form */
- isdumpimage = 0; /* true to dump image on stdout */
+ isinmemory = 1, /* true to generate image in memory*/
+ isdumpimage = 0, /* true to dump image on stdout */
+ isdumpbuffer = 0; /* true to dump to memory buffer */
/* --- rasterization --- */
-subraster *rasterize(), *sp; /* rasterize expression */
-raster *border_raster(), *bp; /* put a border around raster */
+subraster *rasterize(), *sp=NULL; /* rasterize expression */
+raster *border_raster(), *bp=NULL; /* put a border around raster */
+int delete_subraster(); /* for clean-up at end-of-job */
int type_raster(), type_bytemap(), /* screen dump function prototypes */
xbitmap_raster(); /* mime xbitmap output function */
/* --- http_referer --- */
@@ -9918,9 +11360,14 @@ int norefmaxlen = NOREFMAXLEN; /*max que
void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
#endif
char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
+ gif_buffer[64000] = "\000", /* or gif written in memory buffer */
cachefile[256] = "\000", /* full path and name to cache file*/
*md5str(); /* md5 has of expression */
int maxage = 7200; /* max-age is two hours */
+/* --- pbm/pgm (-g switch) --- */
+int ispbmpgm = 0; /* true to write pbm/pgm file */
+int type_pbmpgm(), ptype=0; /* entry point, graphic format */
+char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
/* --- anti-aliasing --- */
intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
colors[256]; /* grayscale vals in bytemap */
@@ -9948,6 +11395,7 @@ initialization
/* --- set global variables --- */
msgfp = stdout; /* for comamnd-line mode output */
isss = issupersampling; /* set supersampling flag */
+gifSize = 0; /* signal that image not in memory */
shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
/* ---
* check QUERY_STRING query for expression overriding command-line arg
@@ -9962,7 +11410,7 @@ if ( !isquery ) /* empty query string
*name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
{ isquery = 1; /* set flag to signal query */
- strcpy(expression,"\\red\\small\\rm~missing~query~string"); }
+ strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
isqempty = 1; /* signal empty query string */
} /* --- end-of-if(!isquery) --- */
/* ---
@@ -9997,22 +11445,30 @@ if ( !isquery /* don't have an html q
if ( !isstopsignal /* haven't seen stopsignal switch */
&& *argv[argnum] == '-' ) /* and have some '-' switch */
{
- char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */
- int arglen = strlen(argv[argnum]) - 1; /* #chars following - */
+ char *field = argv[argnum] + 1; /* ptr to char(s) following - */
+ char flag = tolower(*field); /* single char following '-' */
+ int arglen = strlen(field); /* #chars following - */
argnum++; /* arg following flag/switch is usually its value */
nswitches++; /* another switch on command line */
- if ( isstrict && arglen!=1 ) /* only single-char switch allowed */
+ if ( isstrict && /* if strict checking then... */
+ !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
{ nbadargs++; argnum--; } /* so ignore longer -xxx switch */
else /* process single-char -x switch */
switch ( flag ) { /* see what user wants to tell us */
/* --- ignore uninterpreted flag --- */
default: nbadargs++; argnum--; break;
/* --- adjustable program parameters (not checking input) --- */
+ case 'b': isdumpimage++; isdumpbuffer++; argnum--; break;
case 'd': isdumpimage++; argnum--; break;
case 'e': isdumpimage++; gif_outfile=argv[argnum]; break;
case 'f': isdumpimage++; infilearg=argnum; break;
+ case 'g': ispbmpgm++;
+ if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
+ if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
+ else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
case 'm': msglevel = atoi(argv[argnum]); break;
case 'o': istransparent = 0; argnum--; break;
+ case 'q': isqforce = 1; argnum--; break;
case 's': size = atoi(argv[argnum]); break;
} /* --- end-of-switch(flag) --- */
} /* --- end-of-if(*argv[argnum]=='-') --- */
@@ -10024,8 +11480,10 @@ if ( !isquery /* don't have an html q
else nbadargs++; /* infile and expression invalid */
} /* --- end-of-while(argc>++argnum) --- */
if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
- fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
- argc,progname,nargs,nbadargs);
+ { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
+ argc,progname,nargs,nbadargs);
+ fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
+ cachepath,pathprefix); }
/* ---
* decide whether command-line input overrides query_string
* -------------------------------------------------------- */
@@ -10057,6 +11515,19 @@ if ( !isquery /* don't have an html q
strcat(expression,instring); /* concat line to end of expression*/
fclose ( infile ); } /*close input file after reading expression*/
} /* --- end-of-if(infilearg>0) --- */
+ /* ---
+ * check if emulating query (for testing)
+ * -------------------------------------- */
+ if ( isqforce ) isquery = 1; /* emulate query string processing */
+ /* ---
+ * check if emitting pbm/pgm graphic
+ * --------------------------------- */
+ if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
+ if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
+ if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
+ { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
+ gif_outfile = (char *)NULL; /* reset gif output file */
+ /*isdumpimage--;*/ } /* and decrement -e count */
} /* --- end-of-if(!isquery) --- */
/* ---
* check for