Annotation of loncom/cgi/mimeTeX/mimetex.c, revision 1.2
1.1 albertel 1: /****************************************************************************
2: *
1.2 ! albertel 3: * Copyright(c) 2002-2006, John Forkosh Associates, Inc. All rights reserved.
1.1 albertel 4: * --------------------------------------------------------------------------
5: * This file is part of mimeTeX, which is free software. You may redistribute
6: * and/or modify it under the terms of the GNU General Public License,
7: * version 2 or later, as published by the Free Software Foundation.
8: * MimeTeX is distributed in the hope that it will be useful, but
9: * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
10: * See the GNU General Public License for specific details.
11: * By using mimeTeX, you warrant that you have read, understood and
12: * agreed to these terms and conditions, and that you possess the legal
13: * right and ability to enter into this agreement and to use mimeTeX
14: * in accordance with it.
15: * Your mimeTeX distribution should contain a copy of the GNU General
16: * Public License. If not, write to the Free Software Foundation, Inc.,
17: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
18: * or point your browser to http://www.gnu.org/licenses/gpl.html
19: * --------------------------------------------------------------------------
20: *
21: * Purpose: o MimeTeX, licensed under the gpl, lets you easily embed
22: * LaTeX math in your html pages. It parses a LaTeX math
23: * expression and immediately emits the corresponding gif
24: * image, rather than the usual TeX dvi. And mimeTeX is an
25: * entirely separate little program that doesn't use TeX or
26: * its fonts in any way. It's just one cgi that you put in
27: * your site's cgi-bin/ directory, with no other dependencies.
28: * So mimeTeX is very easy to install. And it's equally easy
29: * to use. Just place an html <img> tag in your document
30: * wherever you want to see the corresponding LaTeX expression.
31: * For example,
32: * <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"
33: * alt="" border=0 align=middle>
34: * immediately generates the corresponding gif image on-the-fly,
35: * displaying the rendered expression wherever you put that
36: * <img> tag. MimeTeX doesn't need intermediate dvi-to-gif
37: * conversion, and it doesn't clutter up your filesystem with
38: * separate little gif files for each converted expression.
39: * There's also no inherent need to repeatedly write the
40: * cumbersome <img> tag illustrated above. You can write
41: * your own custom tags, or write a wrapper script around
42: * mimeTeX to simplify the necessary notation.
43: *
44: * Functions: ===================== Raster Functions ======================
45: * PART2 --- raster constructor functions ---
46: * new_raster(width,height,pixsz) allocation (and constructor)
47: * new_subraster(width,height,pixsz)allocation (and constructor)
48: * new_chardef() allocate chardef struct
49: * delete_raster(rp) deallocate raster (rp = raster ptr)
50: * delete_subraster(sp) deallocate subraster (sp=subraster ptr)
51: * delete_chardef(cp) deallocate chardef (cp = chardef ptr)
52: * --- primitive (sub)raster functions ---
53: * rastcpy(rp) allocate new copy of rp
54: * subrastcpy(sp) allocate new copy of sp
55: * rastrot(rp) new raster rotated right 90 degrees to rp
56: * rastput(target,source,top,left,isopaque) overlay src on trgt
57: * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
58: * rastcat(sp1,sp2,isfree) concatanate sp1||sp2
59: * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
60: * rastile(tiles,ntiles) create composite raster from tiles
1.2 ! albertel 61: * rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2
1.1 albertel 62: * --- raster "drawing" functions ---
63: * accent_subraster(accent,width,height) draw \hat\vec\etc
64: * arrow_subraster(width,height,drctn,isBig) left/right arrow
65: * uparrow_subraster(width,height,drctn,isBig) up/down arrow
66: * rule_raster(rp,top,left,width,height,type) draw rule in rp
67: * line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
68: * line_recurse(rp,row0,col0,row1,col1,thickness) recurse line
69: * circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
70: * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
71: * bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively
72: * border_raster(rp,ntop,nbot,isline,isfree)put border around rp
73: * --- raster (and chardef) output functions ---
74: * type_raster(rp,fp) emit ascii dump of rp on file ptr fp
75: * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
76: * xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp
1.2 ! albertel 77: * type_pbmpgm(rp,ptype,file) pbm or pgm image of rp to file
1.1 albertel 78: * cstruct_chardef(cp,fp,col1) emit C struct of cp on fp
79: * cstruct_raster(rp,fp,col1) emit C struct of rp on fp
80: * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
81: * --- ancillary output functions ---
82: * emit_string(fp,col1,string,comment) emit string and C comment
1.2 ! albertel 83: * gftobitmap(rp) convert .gf-like pixmap to bitmap image
1.1 albertel 84: * ====================== Font Functions =======================
85: * --- font lookup functions ---
86: * get_symdef(symbol) returns mathchardef for symbol
87: * get_chardef(symdef,size) returns chardef for symdef,size
88: * get_charsubraster(symdef,size) wrap subraster around chardef
1.2 ! albertel 89: * get_symsubraster(symbol,size) returns subraster for symbol
1.1 albertel 90: * --- ancillary font functions ---
91: * get_baseline(gfdata) determine baseline (in our coords)
92: * get_delim(symbol,height,family) delim just larger than height
93: * make_delim(symbol,height) construct delim exactly height size
94: * ================= Tokenize/Parse Functions ==================
95: * texchar(expression,chartoken) retruns next char or \sequence
96: * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
1.2 ! albertel 97: * texleft(expr,subexpr,maxsubsz,ldelim,rdelim) \left...\right
1.1 albertel 98: * texscripts(expression,subscript,superscript,which)get scripts
99: * --- ancillary parse functions ---
100: * isbrace(expression,braces,isescape) check for leading brace
101: * preamble(expression,size,subexpr) parse preamble
102: * mimeprep(expression) preprocessor converts \left( to \(, etc.
103: * strchange(nfirst,from,to) change nfirst chars of from to to
104: * strreplace(string,from,to,nreplace) change from to to in str
105: * strtexchr(string,texchr) find texchr in string
106: * findbraces(expression,command) find opening { or closing }
107: * PART3 =========== Rasterize an Expression (recursively) ===========
108: * --- here's the primary entry point for all of mimeTeX ---
109: * rasterize(expression,size) parse and rasterize expression
110: * --- explicitly called handlers that rasterize... ---
111: * rastparen(subexpr,size,basesp) parenthesized subexpr
112: * rastlimits(expression,size,basesp) dispatch super/sub call
113: * rastscripts(expression,size,basesp) super/subscripted exprssn
114: * rastdispmath(expression,size,sp) scripts for displaymath
115: * --- table-driven handlers that rasterize... ---
116: * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
1.2 ! albertel 117: * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
! 118: * rastmiddle(expression,size,basesp,arg1,arg2,arg3) \middle
1.1 albertel 119: * rastflags(expression,size,basesp,flag,value,arg3) set flag
120: * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
121: * rastnewline(expression,size,basesp,arg1,arg2,arg3) \\
122: * rastarrow(expression,size,basesp,width,height,drctn) \longarr
123: * rastuparrow(expression,size,basesp,width,height,drctn)up/down
124: * rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not
125: * rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
126: * rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel
127: * rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
128: * rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt
129: * rastaccent(expression,size,basesp,accent,isabove,isscript)
130: * rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
131: * rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{}
132: * rastarray(expression,size,basesp,arg1,arg2,arg3) \array
133: * rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture
134: * rastline(expression,size,basesp,arg1,arg2,arg3) \line
135: * rastcircle(expression,size,basesp,arg1,arg2,arg3) \circle
136: * rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier
137: * rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox
138: * rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox
139: * rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
140: * rastinput(expression,size,basesp,arg1,arg2,arg3) \input
141: * rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter
1.2 ! albertel 142: * rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
! 143: * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
1.1 albertel 144: * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
145: * --- helper functions for handlers ---
146: * rastopenfile(filename,mode) opens filename[.tex] in mode
1.2 ! albertel 147: * rasteditfilename(filename) edit filename (for security)
! 148: * rastreadfile(filename,islock,tag,value) read <tag>...</tag>
1.1 albertel 149: * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
1.2 ! albertel 150: * calendar(year,month,day) formats one-month calendar string
! 151: * timestamp(tzdelta,ifmt) formats timestamp string
! 152: * tzadjust(tzdelta,year,month,day,hour) adjust date/time
! 153: * daynumber(year,month,day) #days since Monday, Jan 1, 1973
! 154: * dbltoa(d,npts) double to comma-separated ascii
1.1 albertel 155: * === Anti-alias completed raster (lowpass) or symbols (ss) ===
156: * aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap
157: * aapnm(rp,bytemap,grayscale) lowpass based on pnmalias.c
158: * aasupsamp(rp,aa,sf,grayscale) or by supersampling
159: * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
160: * aaweights(width,height) builds "canonical" weight matrix
161: * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
162: * PART1 ========================== Driver ===========================
163: * main(argc,argv) parses math expression and emits mime xbitmap
1.2 ! albertel 164: * CreateGifFromEq(expression,gifFileName) entry pt for win dll
1.1 albertel 165: * isstrstr(string,snippets,iscase) are any snippets in string?
166: * ismonth(month) is month current month ("jan"-"dec")?
167: * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
168: * logger(fp,msglevel,logvars) logs environment variables
1.2 ! albertel 169: * emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
! 170: * readcachefile(cachefile,buffer) read cachefile into buffer
1.1 albertel 171: * md5str(instr) md5 hash library functions
172: * GetPixel(x,y) callback function for gifsave library
173: *
174: * Source: mimetex.c (needs mimetex.h and texfonts.h to compile,
175: * and also needs gifsave.c if compiled with -DAA or -DGIF)
176: *
177: * --------------------------------------------------------------------------
178: * Notes o See bottom of file for main() driver (and "friends"),
179: * and compile as
180: * cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
181: * to produce an executable that emits gif images with
182: * anti-aliasing (see Notes below). You may also compile
183: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
184: * to produce an executable that emits gif images without
185: * anti-aliasing. Alternatively, compile mimeTeX as
186: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
187: * to produce an executable that just emits mime xbitmaps.
188: * In either case you'll need mimetex.h and texfonts.h,
189: * and with -DAA or -DGIF you'll also need gifsave.c
190: * o For gif images, the gifsave.c library by Sverre H. Huseby
191: * <http://shh.thathost.com> slightly modified by me to allow
192: * (a)sending output to stdout and (b)specifying a transparent
193: * background color index, is included with mimeTeX,
194: * and it's documented in mimetex.html#gifsave .
195: * o Optional compile-line -D defined symbols are documented
196: * in mimetex.html#options . They include...
197: * -DAA
198: * Turns on gif anti-aliasing with default values
199: * (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
200: * for the following anti-aliasing parameters...
201: * -DCENTERWT=n
202: * -DADJACENTWT=j
203: * -DCORNERWT=k
204: * MimeTeX currently provides a lowpass filtering
205: * algorithm for anti-aliasing, which is applied to the
206: * existing set of bitmap fonts. This lowpass filter
207: * applies default weights
208: * 1 3 1
209: * 3 32 3
210: * 1 3 1
211: * to neighboring pixels. The defaults weights are
212: * CENTERWT=32, ADJACENTWT=3 and CORNERWT=1,
213: * which you can adjust to control anti-aliasing.
214: * Lower CENTERWT values will blur/spread out lines
215: * while higher values will tend to sharpen lines.
216: * Experimentation is recommended to determine
217: * what value works best for you.
218: * -DCACHEPATH=\"path/\"
219: * This option saves each rendered image to a file
220: * in directory path/ which mimeTeX reads rather than
221: * re-rendering the same image every time it's given
222: * the same LaTeX expression. Sometimes mimeTeX disables
223: * caching, e.g., expressions containing \input{ } are
224: * re-rendered since the contents of the inputted file
225: * may have changed. If compiled without -DCACHEPATH
226: * mimeTeX always re-renders expressions. This usually
227: * isn't too cpu intensive, but if you have unusually
228: * high hit rates then image caching may be helpful.
229: * The path/ is relative to mimetex.cgi, and must
230: * be writable by it. Files created under path/ are
231: * named filename.gif, where filename is the 32-character
232: * MD5 hash of the LaTeX expression.
233: * -DDISPLAYSIZE=n
234: * By default, operator limits like \int_a^b are rendered
235: * \textstyle at font sizes \normalsize and smaller,
236: * and rendered \displaystyle at font sizes \large and
237: * larger. This default corresponds to -DDISPLAYSIZE=3,
238: * which you can adjust; e.g., -DDISPLAYSIZE=0 always
239: * defaults to \displaystyle, and 99 (or any large number)
240: * always defaults to \textstyle. Note that explicit
241: * \textstyle, \displaystyle, \limits or \nolimits
242: * directives in an expression always override
243: * the DISPLAYSIZE default.
244: * -NORMALSIZE=n
245: * MimeTeX currently has six font sizes numbered 0-5,
246: * and always starts in NORMALSIZE whose default value
247: * is 2. Specify -DNORMALSIZE=3 on the compile line if
248: * you prefer mimeTeX to start in default size 3, etc.
249: * -DREFERER=\"domain\" -or-
250: * -DREFERER=\"domain1,domain2,etc\"
251: * Blocks mimeTeX requests from unauthorized domains that
252: * may be using your server's mimetex.cgi without permission.
253: * If REFERER is defined, mimeTeX checks for the environment
254: * variable HTTP_REFERER and, if it exists, performs a
255: * case-insensitive test to make sure it contains 'domain'
256: * as a substring. If given several 'domain's (second form)
257: * then HTTP_REFERER must contain either 'domain1' or
258: * 'domain2', etc, as a (case-insensitive) substring.
259: * If HTTP_REFERER fails to contain a substring matching
260: * any of these domain(s), mimeTeX emits an error message
261: * image corresponding to the expression specified by
262: * the invalid_referer_msg string defined in main().
263: * Note: if HTTP_REFERER is not an environment variable,
264: * mimeTeX correctly generates the requested expression
265: * (i.e., no referer error).
266: * -DWARNINGS=n -or-
267: * -DNOWARNINGS
268: * If an expression submitted to mimeTeX contains an
269: * unrecognzied escape sequence, e.g., "y=x+\abc+1", then
270: * mimeTeX generates a gif image containing an embedded
271: * warning in the form "y=x+[\abc?]+1". If you want these
272: * warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
273: * mimeTeX to ignore unrecognized symbols, and the rendered
274: * image is "y=x++1" instead.
275: * -DWHITE
276: * MimeTeX usually renders black symbols on a white
277: * background. This option renders white symbols on
278: * a black background instead.
279: * o See individual function entry points for further comments.
280: * o The font information in texfonts.h was produced by multiple
281: * runs of gfuntype, one run per struct (i.e., one run per font
282: * family at a particular size). See gfuntype.c, and also
283: * mimetex.html#fonts, for details.
284: * o mimetex.c contains library functions implementing a raster
285: * datatype, functions to manipulate rasterized .mf fonts
286: * (see gfuntype.c which rasterizes .mf fonts), functions
287: * to parse LaTeX expressions, etc. A complete list of
288: * mimetex.c functions is above. See their individual entry
289: * points below for further comments.
290: * All these functions eventually belong in several
291: * different modules, possibly along the lines suggested
292: * by the divisions above. But until the best decomposition
293: * becomes clear, it seems better to keep mimetex.c
294: * neatly together, avoiding a bad decomposition that
295: * becomes permanent by default.
296: * o The "main" reusable function is rasterize(),
297: * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
298: * and returns a (sub)raster representing it as a bit or bytemap.
299: * Your application can do anything it likes with this pixel map.
300: * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
301: * --------------------------------------------------------------------------
302: * Revision History:
303: * 09/18/02 J.Forkosh Installation.
304: * 12/11/02 J.Forkosh Version 1.00 released.
305: * 07/04/03 J.Forkosh Version 1.01 released.
306: * 10/17/03 J.Forkosh Version 1.20 released.
307: * 12/21/03 J.Forkosh Version 1.30 released.
308: * 02/01/04 J.Forkosh Version 1.40 released.
309: * 10/02/04 J.Forkosh Version 1.50 released.
310: * 11/30/04 J.Forkosh Version 1.60 released.
311: *
312: ****************************************************************************/
313:
314: /* -------------------------------------------------------------------------
315: header files and macros
316: -------------------------------------------------------------------------- */
317: /* --- standard headers --- */
318: #include <stdio.h>
319: #include <stdlib.h>
320: /*#include <unistd.h>*/
321: #include <string.h>
322: #include <ctype.h>
323: #include <math.h>
324: #include <time.h>
325:
326: /* --- windows-specific header info --- */
327: #ifndef WINDOWS /* -DWINDOWS not supplied by user */
1.2 ! albertel 328: #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
! 329: || defined(DJGPP) /* try to recognize windows compilers */ \
! 330: || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
1.1 albertel 331: #define WINDOWS /* signal windows */
332: #endif
333: #endif
334: #ifdef WINDOWS /* Windows opens stdout in char mode, and */
335: #include <fcntl.h> /* precedes every 0x0A with spurious 0x0D.*/
336: #include <io.h> /* So emitcache() issues a Win _setmode() */
337: /* call to put stdout in binary mode. */
338: #if defined(_O_BINARY) && !defined(O_BINARY) /* only have _O_BINARY */
339: #define O_BINARY _O_BINARY /* make O_BINARY available, etc... */
340: #define setmode _setmode
341: #define fileno _fileno
342: #endif
343: #if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */
344: #define HAVE_SETMODE /* so we'll use setmode() */
345: #endif
1.2 ! albertel 346: #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
! 347: /* to show source file and line numbers where memory leaks occur... */
! 348: #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
! 349: #include <crtdbg.h> /* and this debug library */
! 350: #endif
1.1 albertel 351: #define ISWINDOWS 1
352: #else
353: #define ISWINDOWS 0
354: #endif
355:
356: /* --- check for supersampling or low-pass anti-aliasing --- */
357: #ifdef SS
358: #define ISSUPERSAMPLING 1
359: #ifndef AAALGORITHM
360: #define AAALGORITHM 1 /* default supersampling algorithm */
361: #endif
362: #ifndef AA /* anti-aliasing not explicitly set */
363: #define AA /* so define it ourselves */
364: #endif
365: #ifndef SSFONTS /* need supersampling fonts */
366: #define SSFONTS
367: #endif
368: #else
369: #define ISSUPERSAMPLING 0
370: #ifndef AAALGORITHM
371: #define AAALGORITHM 2 /* default lowpass algorithm */
372: #endif
373: #endif
374:
375: /* --- set aa (and default gif) if any anti-aliasing options specified --- */
376: #if defined(AA) || defined(GIF) || defined(PNG) \
377: || defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
378: || defined(MINADJACENT) || defined(MAXADJACENT)
379: #if !defined(GIF) && !defined(AA) /* aa not explicitly specified */
380: #define AA /* so define it ourselves */
381: #endif
382: #if !defined(GIF) && !defined(PNG) /* neither gif nor png specified */
383: #define GIF /* so default to gif */
384: #endif
385: #endif
386: /* --- resolve output option inconsistencies --- */
387: #if defined(XBITMAP) /* xbitmap supercedes gif and png */
388: #ifdef AA
389: #undef AA
390: #endif
391: #ifdef GIF
392: #undef GIF
393: #endif
394: #ifdef PNG
395: #undef PNG
396: #endif
397: #endif
398:
399: /* --- decide whether to compile main() --- */
400: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
401: #define DRIVER /* driver will be compiled */
402: /* --- check whether or not to perform http_referer check --- */
403: #ifndef REFERER /* all http_referer's allowed */
404: #define REFERER NULL
405: #endif
406: /* --- max query_string length if no http_referer supplied --- */
407: #ifndef NOREFMAXLEN
408: #define NOREFMAXLEN 9999 /* default to any length query */
409: #endif
410: #else
411: #define NOTEXFONTS /* texfonts not required */
412: #endif
413:
414: /* --- application headers --- */
415: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
416: #define TEXFONTS /* to include texfonts.h */
417: #endif
418: #include "mimetex.h"
1.2 ! albertel 419: /* --- info needed when gif image returned in memory buffer --- */
! 420: #ifdef GIF /* compiling along with gifsave.c */
! 421: extern int gifSize;
! 422: extern int maxgifSize;
! 423: #else /* or just set dummy values */
! 424: static int gifSize=0, maxgifSize=0;
! 425: #endif
1.1 albertel 426:
427: /* -------------------------------------------------------------------------
428: adjustable default values
429: -------------------------------------------------------------------------- */
430: /* --- anti-aliasing parameters --- */
431: #ifndef CENTERWT
1.2 ! albertel 432: /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
1.1 albertel 433: /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
1.2 ! albertel 434: #define CENTERWT 8 /* anti-aliasing centerwt default */
1.1 albertel 435: #endif
436: #ifndef ADJACENTWT
437: /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
1.2 ! albertel 438: #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
1.1 albertel 439: #endif
440: #ifndef CORNERWT
441: #define CORNERWT 1 /* anti-aliasing cornerwt default*/
442: #endif
443: #ifndef MINADJACENT
444: #define MINADJACENT 6 /*anti-aliasing minadjacent default*/
445: #endif
446: #ifndef MAXADJACENT
447: #define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/
448: #endif
449: /* --- variables for anti-aliasing parameters --- */
450: GLOBAL(int,centerwt,CENTERWT); /*lowpass matrix center pixel wt */
451: GLOBAL(int,adjacentwt,ADJACENTWT); /*lowpass matrix adjacent pixel wt*/
452: GLOBAL(int,cornerwt,CORNERWT); /*lowpass matrix corner pixel wt */
453: GLOBAL(int,minadjacent,MINADJACENT); /* darken if>=adjacent pts black*/
454: GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */
455: GLOBAL(int,weightnum,1); /* font wt, */
456: GLOBAL(int,maxaaparams,4); /* #entries in table */
457: /* --- parameter values by font weight --- */
458: #define aaparameters struct aaparameters_struct /* typedef */
459: aaparameters
460: { int centerwt; /* lowpass matrix center pixel wt*/
461: int adjacentwt; /* lowpass matrix adjacent pixel wt*/
462: int cornerwt; /* lowpass matrix corner pixel wt*/
463: int minadjacent; /* darken if >= adjacent pts black */
464: int maxadjacent; /* darken if <= adjacent pts black */
465: int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
466: STATIC aaparameters aaparams[] /* set params by weight */
467: #ifdef INITVALS
468: =
469: { /* ----------------------------------------------------
470: centerwt adj corner minadj max fgalias,only,bgalias,only
471: ------------------------------------------------------- */
472: { 64, 1, 1, 6, 8, 1,0,0,0 }, /* 0 = light */
473: { CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
474: { 8, 1, 1, 5, 8, 1,0,0,0 }, /* 2 = semibold */
475: { 8, 2, 1, 4, 9, 1,0,0,0 } /* 3 = bold */
476: } /* --- end-of-aaparams[] --- */
477: #endif
478: ;
479:
480: /* -------------------------------------------------------------------------
481: other variables
482: -------------------------------------------------------------------------- */
483: /* --- black on white background (default), or white on black --- */
484: #ifdef WHITE
485: #define ISBLACKONWHITE 0 /* white on black background */
486: #else
487: #define ISBLACKONWHITE 1 /* black on white background */
488: #endif
489: /* --- colors --- */
490: #define BGRED (ISBLACKONWHITE?255:0)
491: #define BGGREEN (ISBLACKONWHITE?255:0)
492: #define BGBLUE (ISBLACKONWHITE?255:0)
493: #ifndef FGRED
494: #define FGRED (ISBLACKONWHITE?0:255)
495: #endif
496: #ifndef FGGREEN
497: #define FGGREEN (ISBLACKONWHITE?0:255)
498: #endif
499: #ifndef FGBLUE
500: #define FGBLUE (ISBLACKONWHITE?0:255)
501: #endif
1.2 ! albertel 502: /* --- "smash" margin (0 means no smashing) --- */
! 503: #ifndef SMASHMARGIN
! 504: #ifdef NOSMASH
! 505: #define SMASHMARGIN 0
1.1 albertel 506: #else
1.2 ! albertel 507: #define SMASHMARGIN 3
1.1 albertel 508: #endif
509: #endif
510: /* --- textwidth --- */
511: #ifndef TEXTWIDTH
512: #define TEXTWIDTH (400)
513: #endif
514: /* --- font "combinations" --- */
1.2 ! albertel 515: #define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
1.1 albertel 516: /* --- prefix prepended to all expressions --- */
517: #ifndef PREFIX
518: #define PREFIX "\000" /* default no prepended prefix */
519: #endif
520: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
521: #ifdef NOARGSIGNAL
522: #define ARGSIGNAL NULL
523: #endif
524: #ifndef ARGSIGNAL
525: #define ARGSIGNAL "++"
526: #endif
527: /* --- security and logging (inhibit message logging, etc) --- */
528: #ifndef SECURITY
529: #define SECURITY 999 /* default highest security level */
530: #endif
531: #ifndef LOGFILE
532: #define LOGFILE "mimetex.log" /* default log file */
533: #endif
534: #ifndef CACHELOG
535: #define CACHELOG "mimetex.log" /* default caching log file */
536: #endif
537: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
538: #define DUMPENVP /* assume char *envp[] available */
539: #endif
540: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
541: #ifndef CACHEPATH
542: #define ISCACHING 0 /* no caching */
543: #define CACHEPATH "\000" /* same directory as mimetex.cgi */
544: #else
545: #define ISCACHING 1 /* caching if -DCACHEPATH="path" */
546: #endif
547: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
548: #ifndef PATHPREFIX
549: #define PATHPREFIX "\000" /* paths relative mimetex.cgi */
550: #endif
1.2 ! albertel 551: /* --- time zone delta t (in hours) --- */
! 552: #ifndef TZDELTA
! 553: #define TZDELTA 0
! 554: #endif
1.1 albertel 555:
556: /* -------------------------------------------------------------------------
557: debugging and logging / error reporting
558: -------------------------------------------------------------------------- */
559: /* --- debugging and error reporting --- */
560: #ifndef MSGLEVEL
561: #define MSGLEVEL 1
562: #endif
563: #define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */
564: #define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */
565: #ifndef FORMLEVEL
566: #define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/
567: #endif
568: GLOBAL(int,seclevel,SECURITY); /* security level */
569: GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */
570: STATIC FILE *msgfp; /* output in command-line mode */
571: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
572: #ifdef WARNINGS
573: #define WARNINGLEVEL WARNINGS
574: #else
575: #ifdef NOWARNINGS
576: #define WARNINGLEVEL 0
577: #else
578: #define WARNINGLEVEL 1
579: #endif
580: #endif
581: GLOBAL(int,warninglevel,WARNINGLEVEL); /* warning level */
582:
583: /* -------------------------------------------------------------------------
584: control flags and values
585: -------------------------------------------------------------------------- */
586: GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
587: GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
1.2 ! albertel 588: GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
! 589: /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
1.1 albertel 590: GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
591: GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
1.2 ! albertel 592: GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
1.1 albertel 593: GLOBAL(int,fontsize,NORMALSIZE); /* current size */
594: GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
595: GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
596: GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
597: /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
1.2 ! albertel 598: GLOBAL(int,iscatspace,1); /* true to add space in rastcat() */
! 599: GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
! 600: GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
! 601: GLOBAL(int,blanksignal,(-991234)); /*rastsmash signal right-hand blank*/
1.1 albertel 602: GLOBAL(int,istransparent,1); /*true to set background transparent*/
603: GLOBAL(int,fgred,FGRED);
604: GLOBAL(int,fggreen,FGGREEN);
605: GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */
606: GLOBAL(int,bgred,BGRED);
607: GLOBAL(int,bggreen,BGGREEN);
608: GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */
609: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
610: GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */
611: GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */
612: GLOBAL(int,fgalias,1);
613: GLOBAL(int,fgonly,0);
614: GLOBAL(int,bgalias,0);
615: GLOBAL(int,bgonly,0); /* aapnm() params */
616: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
617: GLOBAL(int,isss,ISSUPERSAMPLING); /* supersampling flag for main() */
618: GLOBAL(int,*workingparam,(int *)NULL); /* working parameter */
619: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
620: GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */
621: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
622: GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/
623: GLOBAL(int,iscaching,ISCACHING); /* true if caching images */
624: GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */
625: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
626: /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */
627:
628: /* -------------------------------------------------------------------------
629: miscellaneous macros
630: -------------------------------------------------------------------------- */
631: #define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */
632: #define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
633: #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
634: #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
635: #define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
636: #define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
637: #define dmod(x,y) ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
638: #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \
639: { char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else
640: #define slower(s) if ((s)!=NULL) /* lowercase all chars in s */ \
641: { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
642:
643: /* ---
644: * PART2
645: * ------ */
646: #if !defined(PARTS) || defined(PART2)
647: /* ==========================================================================
648: * Function: new_raster ( width, height, pixsz )
649: * Purpose: Allocation and constructor for raster.
650: * mallocs and initializes memory for width*height pixels,
651: * and returns raster struct ptr to caller.
652: * --------------------------------------------------------------------------
653: * Arguments: width (I) int containing width, in bits,
654: * of raster pixmap to be allocated
655: * height (I) int containing height, in bits/scans,
656: * of raster pixmap to be allocated
657: * pixsz (I) int containing #bits per pixel, 1 or 8
658: * --------------------------------------------------------------------------
659: * Returns: ( raster * ) ptr to allocated and initialized
660: * raster struct, or NULL for any error.
661: * --------------------------------------------------------------------------
662: * Notes:
663: * ======================================================================= */
664: /* --- entry point --- */
665: raster *new_raster ( int width, int height, int pixsz )
666: {
667: /* -------------------------------------------------------------------------
668: Allocations and Declarations
669: -------------------------------------------------------------------------- */
670: raster *rp = (raster *)NULL; /* raster ptr returned to caller */
671: pixbyte *pixmap = NULL; /* raster pixel map to be malloced */
672: int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
673: int filler = (isstring?' ':0); /* pixmap filler */
674: int delete_raster(); /* in case pixmap malloc() fails */
675: int npadding = (0&&issupersampling?8+256:0); /* padding bytes */
676: /* -------------------------------------------------------------------------
677: allocate and initialize raster struct and embedded bitmap
678: -------------------------------------------------------------------------- */
679: if ( msgfp!=NULL && msglevel>=9999 )
680: { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
681: width,height,pixsz); fflush(msgfp); }
682: /* --- allocate and initialize raster struct --- */
683: rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */
684: if ( msgfp!=NULL && msglevel>=9999 )
685: { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
686: sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
687: if ( rp == (raster *)NULL ) /* malloc failed */
688: goto end_of_job; /* return error to caller */
689: rp->width = width; /* store width in raster struct */
690: rp->height = height; /* and store height */
1.2 ! albertel 691: rp->format = 1; /* initialize as bitmap format */
1.1 albertel 692: rp->pixsz = pixsz; /* store #bits per pixel */
693: rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
694: /* --- allocate and initialize bitmap array --- */
695: if ( msgfp!=NULL && msglevel>=9999 )
696: { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
697: nbytes); fflush(msgfp); }
698: if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/
699: pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
700: if ( msgfp!=NULL && msglevel>=9999 )
701: { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
702: nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
703: if ( pixmap == (pixbyte *)NULL ) /* malloc failed */
704: { delete_raster(rp); /* so free everything */
705: rp = (raster *)NULL; /* reset pointer */
706: goto end_of_job; } /* and return error to caller */
707: memset((void *)pixmap,filler,nbytes); /* init bytes to binary 0's or ' 's*/
708: *pixmap = (pixbyte)0; /* and first byte alwasy 0 */
709: rp->pixmap = pixmap; /* store ptr to malloced memory */
710: /* -------------------------------------------------------------------------
711: Back to caller with address of raster struct, or NULL ptr for any error.
712: -------------------------------------------------------------------------- */
713: end_of_job:
714: if ( msgfp!=NULL && msglevel>=9999 )
715: { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
716: width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
717: return ( rp ); /* back to caller with raster */
718: } /* --- end-of-function new_raster() --- */
719:
720:
721: /* ==========================================================================
722: * Function: new_subraster ( width, height, pixsz )
723: * Purpose: Allocate a new subraster along with
724: * an embedded raster of width x height.
725: * --------------------------------------------------------------------------
726: * Arguments: width (I) int containing width of embedded raster
727: * height (I) int containing height of embedded raster
728: * pixsz (I) int containing #bits per pixel, 1 or 8
729: * --------------------------------------------------------------------------
730: * Returns: ( subraster * ) ptr to newly-allocated subraster,
731: * or NULL for any error.
732: * --------------------------------------------------------------------------
733: * Notes: o if width or height <=0, embedded raster not allocated
734: * ======================================================================= */
735: /* --- entry point --- */
736: subraster *new_subraster ( int width, int height, int pixsz )
737: {
738: /* -------------------------------------------------------------------------
739: Allocations and Declarations
740: -------------------------------------------------------------------------- */
741: subraster *sp=NULL; /* subraster returned to caller */
742: raster *new_raster(), *rp=NULL; /* image raster embedded in sp */
743: int delete_subraster(); /* in case new_raster() fails */
744: int size = NORMALSIZE, /* default size */
745: baseline = height-1; /* and baseline */
746: /* -------------------------------------------------------------------------
747: allocate and initialize subraster struct
748: -------------------------------------------------------------------------- */
749: if ( msgfp!=NULL && msglevel>=9999 )
750: { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
751: width,height,pixsz); fflush(msgfp); }
752: /* --- allocate subraster struct --- */
753: sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */
754: if ( sp == (subraster *)NULL ) /* malloc failed */
755: goto end_of_job; /* return error to caller */
756: /* --- initialize subraster struct --- */
757: sp->type = NOVALUE; /* character or image raster */
758: sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */
759: sp->baseline = baseline; /*0 if image is entirely descending*/
760: sp->size = size; /* font size 0-4 */
761: sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */
762: sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/
763: /* -------------------------------------------------------------------------
764: allocate raster and embed it in subraster, and return to caller
765: -------------------------------------------------------------------------- */
766: /* --- allocate raster struct if desired --- */
767: if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */
768: { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
769: != NULL ) /* if allocate succeeded */
770: sp->image = rp; /* embed raster in subraster */
771: else /* or if allocate failed */
772: { delete_subraster(sp); /* free non-unneeded subraster */
773: sp = NULL; } } /* signal error */
774: /* --- back to caller with new subraster or NULL --- */
775: end_of_job:
776: if ( msgfp!=NULL && msglevel>=9999 )
777: { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
778: width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
779: return ( sp );
780: } /* --- end-of-function new_subraster() --- */
781:
782:
783: /* ==========================================================================
784: * Function: new_chardef ( )
785: * Purpose: Allocates and initializes a chardef struct,
786: * but _not_ the embedded raster struct.
787: * --------------------------------------------------------------------------
788: * Arguments: none
789: * --------------------------------------------------------------------------
790: * Returns: ( chardef * ) ptr to allocated and initialized
791: * chardef struct, or NULL for any error.
792: * --------------------------------------------------------------------------
793: * Notes:
794: * ======================================================================= */
795: /* --- entry point --- */
796: chardef *new_chardef ( )
797: {
798: /* -------------------------------------------------------------------------
799: Allocations and Declarations
800: -------------------------------------------------------------------------- */
801: chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */
802: /* -------------------------------------------------------------------------
803: allocate and initialize chardef struct
804: -------------------------------------------------------------------------- */
805: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
806: if ( cp == (chardef *)NULL ) /* malloc failed */
807: goto end_of_job; /* return error to caller */
808: cp->charnum = cp->location = 0; /* init character description */
809: cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
810: cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
811: cp->image.width = cp->image.height = 0; /* init raster dimensions */
1.2 ! albertel 812: cp->image.format = 0; /* init raster format */
1.1 albertel 813: cp->image.pixsz = 0; /* and #bits per pixel */
814: cp->image.pixmap = NULL; /* init raster pixmap as null */
815: /* -------------------------------------------------------------------------
816: Back to caller with address of chardef struct, or NULL ptr for any error.
817: -------------------------------------------------------------------------- */
818: end_of_job:
819: return ( cp );
820: } /* --- end-of-function new_chardef() --- */
821:
822:
823: /* ==========================================================================
824: * Function: delete_raster ( rp )
825: * Purpose: Destructor for raster.
826: * Frees memory for raster bitmap and struct.
827: * --------------------------------------------------------------------------
828: * Arguments: rp (I) ptr to raster struct to be deleted.
829: * --------------------------------------------------------------------------
830: * Returns: ( int ) 1 if completed successfully,
831: * or 0 otherwise (for any error).
832: * --------------------------------------------------------------------------
833: * Notes:
834: * ======================================================================= */
835: /* --- entry point --- */
836: int delete_raster ( raster *rp )
837: {
838: /* -------------------------------------------------------------------------
839: free raster bitmap and struct
840: -------------------------------------------------------------------------- */
841: if ( rp != (raster *)NULL ) /* can't free null ptr */
842: {
843: if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */
844: free((void *)rp->pixmap); /* free pixmap within raster */
845: free((void *)rp); /* lastly, free raster struct */
846: } /* --- end-of-if(rp!=NULL) --- */
847: return ( 1 ); /* back to caller, 1=okay 0=failed */
848: } /* --- end-of-function delete_raster() --- */
849:
850:
851: /* ==========================================================================
852: * Function: delete_subraster ( sp )
853: * Purpose: Deallocates a subraster (and embedded raster)
854: * --------------------------------------------------------------------------
855: * Arguments: sp (I) ptr to subraster struct to be deleted.
856: * --------------------------------------------------------------------------
857: * Returns: ( int ) 1 if completed successfully,
858: * or 0 otherwise (for any error).
859: * --------------------------------------------------------------------------
860: * Notes:
861: * ======================================================================= */
862: /* --- entry point --- */
863: int delete_subraster ( subraster *sp )
864: {
865: /* -------------------------------------------------------------------------
866: free subraster struct
867: -------------------------------------------------------------------------- */
868: int delete_raster(); /* to delete embedded raster */
869: if ( sp != (subraster *)NULL ) /* can't free null ptr */
870: {
871: if ( sp->type != CHARASTER ) /* not static character data */
872: if ( sp->image != NULL ) /*raster allocated within subraster*/
873: delete_raster(sp->image); /* so free embedded raster */
874: free((void *)sp); /* and free subraster struct itself*/
875: } /* --- end-of-if(sp!=NULL) --- */
876: return ( 1 ); /* back to caller, 1=okay 0=failed */
877: } /* --- end-of-function delete_subraster() --- */
878:
879:
880: /* ==========================================================================
881: * Function: delete_chardef ( cp )
882: * Purpose: Deallocates a chardef (and bitmap of embedded raster)
883: * --------------------------------------------------------------------------
884: * Arguments: cp (I) ptr to chardef struct to be deleted.
885: * --------------------------------------------------------------------------
886: * Returns: ( int ) 1 if completed successfully,
887: * or 0 otherwise (for any error).
888: * --------------------------------------------------------------------------
889: * Notes:
890: * ======================================================================= */
891: /* --- entry point --- */
892: int delete_chardef ( chardef *cp )
893: {
894: /* -------------------------------------------------------------------------
895: free chardef struct
896: -------------------------------------------------------------------------- */
897: if ( cp != (chardef *)NULL ) /* can't free null ptr */
898: {
899: if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */
900: free((void *)cp->image.pixmap); /* so free embedded pixmap */
901: free((void *)cp); /* and free chardef struct itself */
902: } /* --- end-of-if(cp!=NULL) --- */
903: /* -------------------------------------------------------------------------
904: Back to caller with 1=okay, 0=failed.
905: -------------------------------------------------------------------------- */
906: return ( 1 );
907: } /* --- end-of-function delete_chardef() --- */
908:
909:
910: /* ==========================================================================
911: * Function: rastcpy ( rp )
912: * Purpose: makes duplicate copy of rp
913: * --------------------------------------------------------------------------
914: * Arguments: rp (I) ptr to raster struct to be copied
915: * --------------------------------------------------------------------------
916: * Returns: ( raster * ) ptr to new copy rp,
917: * or NULL for any error.
918: * --------------------------------------------------------------------------
919: * Notes: o
920: * ======================================================================= */
921: /* --- entry point --- */
922: raster *rastcpy ( raster *rp )
923: {
924: /* -------------------------------------------------------------------------
925: Allocations and Declarations
926: -------------------------------------------------------------------------- */
927: raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/
928: int height= (rp==NULL?0:rp->height), /* original and copied height */
929: width = (rp==NULL?0:rp->width), /* original and copied width */
930: pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
931: nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
932: /* -------------------------------------------------------------------------
933: allocate rotated raster and fill it
934: -------------------------------------------------------------------------- */
935: /* --- allocate copied raster with same width,height, and copy bitmap --- */
936: if ( rp != NULL ) /* nothing to copy if ptr null */
937: if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
938: != NULL ) /* check that allocate succeeded */
939: memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
940: return ( newrp ); /* return copied raster to caller */
941: } /* --- end-of-function rastcpy() --- */
942:
943:
944: /* ==========================================================================
945: * Function: subrastcpy ( sp )
946: * Purpose: makes duplicate copy of sp
947: * --------------------------------------------------------------------------
948: * Arguments: sp (I) ptr to subraster struct to be copied
949: * --------------------------------------------------------------------------
950: * Returns: ( subraster * ) ptr to new copy sp,
951: * or NULL for any error.
952: * --------------------------------------------------------------------------
953: * Notes: o
954: * ======================================================================= */
955: /* --- entry point --- */
956: subraster *subrastcpy ( subraster *sp )
957: {
958: /* -------------------------------------------------------------------------
959: Allocations and Declarations
960: -------------------------------------------------------------------------- */
961: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
962: raster *rastcpy(), *newrp=NULL; /* and new raster image within it */
963: int delete_subraster(); /* dealloc newsp if rastcpy() fails*/
964: /* -------------------------------------------------------------------------
965: make copy, and return it to caller
966: -------------------------------------------------------------------------- */
967: if ( sp == NULL ) goto end_of_job; /* nothing to copy */
968: /* --- allocate new subraster "envelope" for copy --- */
969: if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
970: == NULL ) goto end_of_job; /* and quit if we fail to allocate */
971: /* --- transparently copy original envelope to new one --- */
972: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
973: /* --- make a copy of the rasterized image itself, if there is one --- */
974: if ( sp->image != NULL ) /* there's an image embedded in sp */
975: if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */
976: == NULL ) /* failed to copy successfully */
977: { delete_subraster(newsp); /* won't need newsp any more */
978: newsp = NULL; /* because we're returning error */
979: goto end_of_job; } /* back to caller with error signal*/
980: /* --- set new params in new envelope --- */
981: newsp->image = newrp; /* new raster image we just copied */
982: switch ( sp->type ) /* set new raster image type */
983: { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
984: case ASCIISTRING: newsp->type = ASCIISTRING; break;
985: case IMAGERASTER: default: newsp->type = IMAGERASTER; break; }
986: /* --- return copy of sp to caller --- */
987: end_of_job:
988: return ( newsp ); /* copy back to caller */
989: } /* --- end-of-function subrastcpy() --- */
990:
991:
992: /* ==========================================================================
993: * Function: rastrot ( rp )
994: * Purpose: rotates rp image 90 degrees right/clockwise
995: * --------------------------------------------------------------------------
996: * Arguments: rp (I) ptr to raster struct to be rotated
997: * --------------------------------------------------------------------------
998: * Returns: ( raster * ) ptr to new raster rotated ralative to rp,
999: * or NULL for any error.
1000: * --------------------------------------------------------------------------
1001: * Notes: o An underbrace is } rotated 90 degrees clockwise,
1002: * a hat is <, etc.
1003: * ======================================================================= */
1004: /* --- entry point --- */
1005: raster *rastrot ( raster *rp )
1006: {
1007: /* -------------------------------------------------------------------------
1008: Allocations and Declarations
1009: -------------------------------------------------------------------------- */
1010: raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/
1011: int height = rp->height, irow, /* original height, row index */
1012: width = rp->width, icol, /* original width, column index */
1013: pixsz = rp->pixsz; /* #bits per pixel */
1014: /* -------------------------------------------------------------------------
1015: allocate rotated raster and fill it
1016: -------------------------------------------------------------------------- */
1017: /* --- allocate rotated raster with flipped width<-->height --- */
1018: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
1019: != NULL ) /* check that allocation succeeded */
1020: /* --- fill rotated raster --- */
1021: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
1022: for ( icol=0; icol<width; icol++ ) /* and each column of rp */
1023: { int value = getpixel(rp,irow,icol);
1024: /* setpixel(rotated,icol,irow,value); } */
1025: setpixel(rotated,icol,(height-1-irow),value); }
1026: return ( rotated ); /* return rotated raster to caller */
1027: } /* --- end-of-function rastrot() --- */
1028:
1029:
1030: /* ==========================================================================
1031: * Function: rastput ( target, source, top, left, isopaque )
1032: * Purpose: Overlays source onto target,
1033: * with the 0,0-bit of source onto the top,left-bit of target.
1034: * --------------------------------------------------------------------------
1035: * Arguments: target (I) ptr to target raster struct
1036: * source (I) ptr to source raster struct
1037: * top (I) int containing 0 ... target->height - 1
1038: * left (I) int containing 0 ... target->width - 1
1039: * isopaque (I) int containing false (zero) to allow
1040: * original 1-bits of target to "show through"
1041: * 0-bits of source.
1042: * --------------------------------------------------------------------------
1043: * Returns: ( int ) 1 if completed successfully,
1044: * or 0 otherwise (for any error).
1045: * --------------------------------------------------------------------------
1046: * Notes:
1047: * ======================================================================= */
1048: /* --- entry point --- */
1049: int rastput ( raster *target, raster *source,
1050: int top, int left, int isopaque )
1051: {
1052: /* -------------------------------------------------------------------------
1053: Allocations and Declarations
1054: -------------------------------------------------------------------------- */
1055: int irow, icol, /* indexes over source raster */
1056: twidth=target->width, theight=target->height, /*target width,height*/
1057: tpix, ntpix = twidth*theight; /* #pixels in target */
1058: int isfatal = 0, /* true to abend on out-of-bounds error */
1059: isstrict = 0/*1*/, /* true for strict bounds check - no "wrap"*/
1060: isokay = 1; /* true if no pixels out-of-bounds */
1061: /* -------------------------------------------------------------------------
1062: superimpose source onto target, one bit at a time
1063: -------------------------------------------------------------------------- */
1064: if ( isstrict && (top<0 || left<0) ) /* args fail strict test */
1065: isokay = 0; /* so just return error */
1066: else
1067: for ( irow=0; irow<source->height; irow++ ) /* for each scan line */
1068: {
1069: tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/
1070: for ( icol=0; icol<source->width; icol++ ) /* each pixel in scan line */
1071: {
1072: int svalue = getpixel(source,irow,icol); /* source pixel value */
1073: ++tpix; /* bump target pixel */
1074: if ( msgfp!=NULL && msglevel>=9999 ) /* debugging output */
1075: { fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
1076: "left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
1077: left,icol,twidth); fflush(msgfp); }
1078: if ( tpix >= ntpix /* bounds check failed */
1079: || (isstrict && (irow+top>=theight || icol+left>=twidth)) )
1080: { isokay = 0; /* reset okay flag */
1081: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
1082: else break; } /*or just go on to next row*/
1083: if ( tpix >= 0 ) /* bounds check okay */
1084: if ( svalue!=0 || isopaque ) /*got dark or opaque source*/
1085: setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/
1086: } /* --- end-of-for(icol) --- */
1087: } /* --- end-of-for(irow) --- */
1088: /* -------------------------------------------------------------------------
1089: Back to caller with 1=okay, 0=failed.
1090: -------------------------------------------------------------------------- */
1091: end_of_job:
1092: return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
1093: } /* --- end-of-function rastput() --- */
1094:
1095:
1096: /* ==========================================================================
1097: * Function: rastcompose ( sp1, sp2, offset2, isalign, isfree )
1098: * Purpose: Overlays sp2 on top of sp1, leaving both unchanged
1099: * and returning a newly-allocated composite subraster.
1100: * Frees/deletes input sp1 and/or sp2 depending on value
1101: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1102: * --------------------------------------------------------------------------
1103: * Arguments: sp1 (I) subraster * to "underneath" subraster,
1104: * whose baseline is preserved
1105: * sp2 (I) subraster * to "overlaid" subraster
1106: * offset2 (I) int containing 0 or number of pixels
1107: * to horizontally shift sp2 relative to sp1,
1108: * either positive (right) or negative
1109: * isalign (I) int containing 1 to align baselines,
1110: * or 0 to vertically center sp2 over sp1
1111: * isfree (I) int containing 1=free sp1 before return,
1112: * 2=free sp2, 3=free both, 0=free none.
1113: * --------------------------------------------------------------------------
1114: * Returns: ( subraster * ) pointer to constructed subraster
1115: * or NULL for any error
1116: * --------------------------------------------------------------------------
1117: * Notes:
1118: * ======================================================================= */
1119: /* --- entry point --- */
1120: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
1121: int isalign, int isfree )
1122: {
1123: /* -------------------------------------------------------------------------
1124: Allocations and Declarations
1125: -------------------------------------------------------------------------- */
1126: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1127: raster *rp=(raster *)NULL; /* new composite raster in sp */
1128: int delete_subraster(); /* in case isfree non-zero */
1129: int rastput(); /*place sp1,sp2 in composite raster*/
1130: int base1 = sp1->baseline, /*baseline for underlying subraster*/
1131: height1 = (sp1->image)->height, /* height for underlying subraster */
1132: width1 = (sp1->image)->width, /* width for underlying subraster */
1133: pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */
1134: base2 = sp2->baseline, /*baseline for overlaid subraster */
1135: height2 = (sp2->image)->height, /* height for overlaid subraster */
1136: width2 = (sp2->image)->width, /* width for overlaid subraster */
1137: pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
1138: int height=0, width=0, pixsz=0, base=0; /* overlaid composite */
1139: /* -------------------------------------------------------------------------
1140: Initialization
1141: -------------------------------------------------------------------------- */
1142: /* --- determine height, width and baseline of composite raster --- */
1143: if ( isalign ) /* baselines of sp1,sp2 aligned */
1144: { height = max2(base1+1,base2+1) /* max height above baseline */
1145: + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1146: base = max2(base1,base2); } /* max space above baseline */
1147: else /* baselines not aligned */
1148: { height = max2(height1,height2); /* max height */
1149: base = base1 + (height-height1)/2; } /* baseline for sp1 */
1150: width = max2(width1,width2+abs(offset2)); /* max width */
1151: pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
1152: /* -------------------------------------------------------------------------
1153: allocate concatted composite subraster
1154: -------------------------------------------------------------------------- */
1155: /* --- allocate returned subraster (and then initialize it) --- */
1156: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1157: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1158: /* --- initialize subraster parameters --- */
1159: sp->type = IMAGERASTER; /* image */
1160: sp->baseline = base; /* composite baseline */
1161: sp->size = sp1->size; /* underlying char is sp1 */
1162: /* --- extract raster from subraster --- */
1163: rp = sp->image; /* raster allocated in subraster */
1164: /* -------------------------------------------------------------------------
1165: overlay sp1 and sp2 in new composite raster
1166: -------------------------------------------------------------------------- */
1167: if ( isalign )
1168: { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
1169: rastput (rp, sp2->image, base-base2, /*overlaid*/
1170: (width-width2)/2+offset2, 0); }
1171: else
1172: { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
1173: rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
1174: (width-width2)/2+offset2, 0); }
1175: /* -------------------------------------------------------------------------
1176: free input if requested
1177: -------------------------------------------------------------------------- */
1178: if ( isfree > 0 ) /* caller wants input freed */
1179: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1180: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
1181: /* -------------------------------------------------------------------------
1182: Back to caller with pointer to concatted subraster or with null for error
1183: -------------------------------------------------------------------------- */
1184: end_of_job:
1185: return ( sp ); /* back with subraster or null ptr */
1186: } /* --- end-of-function rastcompose() --- */
1187:
1188:
1189: /* ==========================================================================
1190: * Function: rastcat ( sp1, sp2, isfree )
1191: * Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged
1192: * and returning a newly-allocated subraster.
1193: * Frees/deletes input sp1 and/or sp2 depending on value
1194: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1195: * --------------------------------------------------------------------------
1196: * Arguments: sp1 (I) subraster * to left-hand subraster
1197: * sp2 (I) subraster * to right-hand subraster
1198: * isfree (I) int containing 1=free sp1 before return,
1199: * 2=free sp2, 3=free both, 0=free none.
1200: * --------------------------------------------------------------------------
1201: * Returns: ( subraster * ) pointer to constructed subraster sp1||sp2
1202: * or NULL for any error
1203: * --------------------------------------------------------------------------
1204: * Notes:
1205: * ======================================================================= */
1206: /* --- entry point --- */
1207: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
1208: {
1209: /* -------------------------------------------------------------------------
1210: Allocations and Declarations
1211: -------------------------------------------------------------------------- */
1212: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1213: raster *rp=(raster *)NULL; /* new concatted raster */
1214: int delete_subraster(); /* in case isfree non-zero */
1215: int rastput(); /*place sp1,sp2 in concatted raster*/
1216: int type_raster(); /* debugging display */
1217: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
1218: height1 = (sp1->image)->height, /* height for left-hand subraster */
1219: width1 = (sp1->image)->width, /* width for left-hand subraster */
1220: pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */
1221: type1 = sp1->type, /* image type for left-hand */
1222: base2 = sp2->baseline, /*baseline for right-hand subraster*/
1223: height2 = (sp2->image)->height, /* height for right-hand subraster */
1224: width2 = (sp2->image)->width, /* width for right-hand subraster */
1225: pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
1226: type2 = sp2->type; /* image type for right-hand */
1227: int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
1.2 ! albertel 1228: int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
! 1229: isopaque = (issmash?0:1), /* not oppaque if smashing */
! 1230: rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
! 1231: oldsmashmargin = smashmargin; /* save original smashmargin */
1.1 albertel 1232: mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
1233: *symdef2 = sp2->symdef; /* mathchardef of right-hand char */
1234: int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
1235: class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
1236: smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
1237: class1==OPENING||class1==CLOSING||class1==PUNCTION),
1238: smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
1239: class2==OPENING||class2==CLOSING||class2==PUNCTION),
1240: space = fontsize/2+1; /* #cols between sp1 and sp2 */
1241: /* -------------------------------------------------------------------------
1242: Initialization
1243: -------------------------------------------------------------------------- */
1244: /* --- determine inter-character space from character class --- */
1245: if ( !isstring )
1246: space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
1247: else space = 1; /* space for ascii string */
1.2 ! albertel 1248: if ( !iscatspace ) space=0; /* spacing explicitly turned off */
! 1249: /* --- determine smash --- */
! 1250: if ( !isstring ) /* don't smash strings */
! 1251: if ( issmash ) { /* raster smash wanted */
! 1252: int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
! 1253: margin = smashmargin; /* init margin without delta */
1.1 albertel 1254: if ( (1 && smash1 && smash2) /* concatanating two chars */
1255: || (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )
1.2 ! albertel 1256: /*maxsmash = 0;*/ /* turn off smash */
! 1257: margin = max2(space-1,0); /* force small smashmargin */
1.1 albertel 1258: else /* adjust for delta if images */
1.2 ! albertel 1259: if ( issmashdelta ) /* smashmargin is a delta value */
1.1 albertel 1260: margin += fontsize; /* add displaystyle base to margin */
1.2 ! albertel 1261: if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
1.1 albertel 1262: isblank = 1; /* set blank flag signal */
1263: else /* see how much extra space we have*/
1.2 ! albertel 1264: if ( maxsmash > margin ) /* enough space for adjustment */
! 1265: nsmash = maxsmash-margin; /* make adjustment */
! 1266: if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
! 1267: { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
! 1268: maxsmash,margin,nsmash);
1.1 albertel 1269: fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
1270: (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
1271: fflush(msgfp); }
1.2 ! albertel 1272: } /* --- end-of-if(issmash) --- */
1.1 albertel 1273: /* --- determine height, width and baseline of composite raster --- */
1274: if ( !isstring )
1275: { height = max2(base1+1,base2+1) /* max height above baseline */
1276: + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1.2 ! albertel 1277: width = width1+width2 + space-nsmash; /*add widths and space-smash*/
! 1278: width = max3(width,width1,width2); } /* don't "over-smash" composite */
1.1 albertel 1279: else /* ascii string */
1280: { height = 1; /* default */
1281: width = width1 + width2 + space - 1; } /* no need for two nulls */
1282: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
1283: base = max2(base1,base2); /* max space above baseline */
1284: if ( msgfp!=NULL && msglevel>=9999 ) /* display components */
1285: { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1286: height1,width1,pixsz1,base1);
1287: type_raster(sp1->image,msgfp); /* display left-hand raster */
1288: fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1289: height2,width2,pixsz2,base2);
1290: type_raster(sp2->image,msgfp); /* display right-hand raster */
1291: fprintf(msgfp,
1.2 ! albertel 1292: "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
! 1293: height,width,nsmash,pixsz,base);
1.1 albertel 1294: fflush(msgfp); } /* flush msgfp buffer */
1295: /* -------------------------------------------------------------------------
1296: allocate concatted composite subraster
1297: -------------------------------------------------------------------------- */
1298: /* --- allocate returned subraster (and then initialize it) --- */
1299: if ( msgfp!=NULL && msglevel>=9999 )
1300: { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
1301: width,height,pixsz); fflush(msgfp); }
1302: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1303: == (subraster *)NULL ) /* failed */
1304: { if ( msgfp!=NULL && msglevel>=1 ) /* report failure */
1305: { fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
1306: width,height,pixsz); fflush(msgfp); }
1307: goto end_of_job; } /* failed, so quit */
1308: /* --- initialize subraster parameters --- */
1309: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/
1310: if ( !isstring )
1.2 ! albertel 1311: sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
! 1312: (type2!=CHARASTER? type2 : (type1!=CHARASTER?type1:STRINGRASTER));
1.1 albertel 1313: else
1314: sp->type = ASCIISTRING; /* concatted ascii string */
1315: sp->symdef = symdef2; /* rightmost char is sp2 */
1316: sp->baseline = base; /* composite baseline */
1317: sp->size = sp2->size; /* rightmost char is sp2 */
1318: if ( isblank ) /* need to propagate blanksignal */
1319: sp->type = blanksignal; /* may not be completely safe??? */
1320: /* --- extract raster from subraster --- */
1321: rp = sp->image; /* raster allocated in subraster */
1322: /* -------------------------------------------------------------------------
1323: overlay sp1 and sp2 in new composite raster
1324: -------------------------------------------------------------------------- */
1325: if ( msgfp!=NULL && msglevel>=9999 )
1326: { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
1327: fflush(msgfp); } /* flush msgfp buffer */
1328: if ( !isstring )
1329: rastput (rp, sp1->image, base-base1, /* overlay left-hand */
1.2 ! albertel 1330: max2(0,nsmash-width1), 1); /* plus any residual smash space */
1.1 albertel 1331: else
1332: memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1); /*init left string*/
1333: if ( msgfp!=NULL && msglevel>=9999 )
1334: { type_raster(sp->image,msgfp); /* display composite raster */
1335: fflush(msgfp); } /* flush msgfp buffer */
1336: if ( !isstring )
1337: rastput (rp, sp2->image, base-base2, /* overlay right-hand */
1.2 ! albertel 1338: max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
1.1 albertel 1339: else
1340: { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
1341: ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
1342: if ( msgfp!=NULL && msglevel>=9999 )
1343: { type_raster(sp->image,msgfp); /* display composite raster */
1344: fflush(msgfp); } /* flush msgfp buffer */
1345: /* -------------------------------------------------------------------------
1346: free input if requested
1347: -------------------------------------------------------------------------- */
1348: if ( isfree > 0 ) /* caller wants input freed */
1349: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1350: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
1351: /* -------------------------------------------------------------------------
1352: Back to caller with pointer to concatted subraster or with null for error
1353: -------------------------------------------------------------------------- */
1354: end_of_job:
1.2 ! albertel 1355: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.1 albertel 1356: return ( sp ); /* back with subraster or null ptr */
1357: } /* --- end-of-function rastcat() --- */
1358:
1359:
1360: /* ==========================================================================
1361: * Function: rastack ( sp1, sp2, base, space, iscenter, isfree )
1362: * Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged
1363: * and returning a newly-allocated subraster,
1364: * whose baseline is sp1's if base=1, or sp2's if base=2.
1365: * Frees/deletes input sp1 and/or sp2 depending on value
1366: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1367: * --------------------------------------------------------------------------
1368: * Arguments: sp1 (I) subraster * to lower subraster
1369: * sp2 (I) subraster * to upper subraster
1370: * base (I) int containing 1 if sp1 is baseline,
1371: * or 2 if sp2 is baseline.
1372: * space (I) int containing #rows blank space inserted
1373: * between sp1's image and sp2's image.
1374: * iscenter (I) int containing 1 to center both sp1 and sp2
1375: * in stacked array, 0 to left-justify both
1376: * isfree (I) int containing 1=free sp1 before return,
1377: * 2=free sp2, 3=free both, 0=free none.
1378: * --------------------------------------------------------------------------
1379: * Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1
1380: * or NULL for any error
1381: * --------------------------------------------------------------------------
1382: * Notes:
1383: * ======================================================================= */
1384: /* --- entry point --- */
1385: subraster *rastack ( subraster *sp1, subraster *sp2,
1386: int base, int space, int iscenter, int isfree )
1387: {
1388: /* -------------------------------------------------------------------------
1389: Allocations and Declarations
1390: -------------------------------------------------------------------------- */
1391: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1392: raster *rp=(raster *)NULL; /* new stacked raster in sp */
1393: int delete_subraster(); /* in case isfree non-zero */
1394: int rastput(); /* place sp1,sp2 in stacked raster */
1395: int base1 = sp1->baseline, /* baseline for lower subraster */
1396: height1 = (sp1->image)->height, /* height for lower subraster */
1397: width1 = (sp1->image)->width, /* width for lower subraster */
1398: pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */
1399: base2 = sp2->baseline, /* baseline for upper subraster */
1400: height2 = (sp2->image)->height, /* height for upper subraster */
1401: width2 = (sp2->image)->width, /* width for upper subraster */
1402: pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */
1403: int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
1404: mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */
1405: *symdef2 = sp2->symdef; /* mathchardef of right upper char */
1406: /* -------------------------------------------------------------------------
1407: Initialization
1408: -------------------------------------------------------------------------- */
1409: /* --- determine height, width and baseline of composite raster --- */
1410: height = height1 + space + height2; /* sum of heights plus space */
1411: width = max2(width1,width2); /* max width is overall width */
1412: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
1413: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
1414: /* -------------------------------------------------------------------------
1415: allocate stacked composite subraster (with embedded raster)
1416: -------------------------------------------------------------------------- */
1417: /* --- allocate returned subraster (and then initialize it) --- */
1418: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1419: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1420: /* --- initialize subraster parameters --- */
1421: sp->type = IMAGERASTER; /* stacked rasters */
1422: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
1423: sp->baseline = baseline; /* composite baseline */
1424: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
1425: /* --- extract raster from subraster --- */
1426: rp = sp->image; /* raster embedded in subraster */
1427: /* -------------------------------------------------------------------------
1428: overlay sp1 and sp2 in new composite raster
1429: -------------------------------------------------------------------------- */
1430: if ( iscenter == 1 ) /* center both sp1 and sp2 */
1431: { rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */
1432: rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
1433: else /* left-justify both sp1 and sp2 */
1434: { rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */
1435: rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
1436: /* -------------------------------------------------------------------------
1437: free input if requested
1438: -------------------------------------------------------------------------- */
1439: if ( isfree > 0 ) /* caller wants input freed */
1440: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1441: if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
1442: /* -------------------------------------------------------------------------
1443: Back to caller with pointer to stacked subraster or with null for error
1444: -------------------------------------------------------------------------- */
1445: end_of_job:
1446: return ( sp ); /* back with subraster or null ptr */
1447: } /* --- end-of-function rastack() --- */
1448:
1449:
1450: /* ==========================================================================
1451: * Function: rastile ( tiles, ntiles )
1452: * Purpose: Allocate and build up a composite raster
1453: * from the ntiles components/characters supplied in tiles.
1454: * --------------------------------------------------------------------------
1455: * Arguments: tiles (I) subraster * to array of subraster structs
1456: * describing the components and their locations
1457: * ntiles (I) int containing number of subrasters in tiles[]
1458: * --------------------------------------------------------------------------
1459: * Returns: ( raster * ) ptr to composite raster,
1460: * or NULL for any error.
1461: * --------------------------------------------------------------------------
1462: * Notes: o The top,left corner of a raster is row=0,col=0
1463: * with row# increasing as you move down,
1464: * and col# increasing as you move right.
1465: * Metafont numbers rows with the baseline=0,
1466: * so the top row is a positive number that
1467: * decreases as you move down.
1468: * o rastile() is no longer used.
1469: * It was used by an earlier rasterize() algorithm,
1470: * and I've left it in place should it be needed again.
1471: * But recent changes haven't been tested/exercised.
1472: * ======================================================================= */
1473: /* --- entry point --- */
1474: raster *rastile ( subraster *tiles, int ntiles )
1475: {
1476: /* -------------------------------------------------------------------------
1477: Allocations and Declarations
1478: -------------------------------------------------------------------------- */
1479: raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/
1480: int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
1481: toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
1482: botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */
1483: int itile; /* tiles[] index */
1484: int rastput(); /* overlay each tile in composite raster */
1485: /* -------------------------------------------------------------------------
1486: run through tiles[] to determine dimensions for composite raster
1487: -------------------------------------------------------------------------- */
1488: /* --- determine row and column bounds of composite raster --- */
1489: for ( itile=0; itile<ntiles; itile++ )
1490: {
1491: subraster *tile = &(tiles[itile]); /* ptr to current tile */
1492: /* --- upper-left corner of composite --- */
1493: toprow = min2(toprow, tile->toprow);
1494: leftcol = min2(leftcol, tile->leftcol);
1495: /* --- lower-right corner of composite --- */
1496: botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
1497: rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1);
1498: /* --- pixsz of composite --- */
1499: pixsz = max2(pixsz,(tile->image)->pixsz);
1500: } /* --- end-of-for(itile) --- */
1501: /* --- calculate width and height from bounds --- */
1502: width = rightcol - leftcol + 1;
1503: height = botrow - toprow + 1;
1504: /* --- sanity check (quit if bad dimensions) --- */
1505: if ( width<1 || height<1 ) goto end_of_job;
1506: /* -------------------------------------------------------------------------
1507: allocate composite raster, and embed tiles[] within it
1508: -------------------------------------------------------------------------- */
1509: /* --- allocate composite raster --- */
1510: if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
1511: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
1512: /* --- embed tiles[] in composite --- */
1513: for ( itile=0; itile<ntiles; itile++ )
1514: { subraster *tile = &(tiles[itile]); /* ptr to current tile */
1515: rastput (composite, tile->image, /* overlay tile image at...*/
1516: tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
1517: /* -------------------------------------------------------------------------
1518: Back to caller with composite raster (or null for any error)
1519: -------------------------------------------------------------------------- */
1520: end_of_job:
1521: return ( composite ); /* back with composite or null ptr */
1522: } /* --- end-of-function rastile() --- */
1523:
1524:
1525: /* ==========================================================================
1.2 ! albertel 1526: * Function: rastsmash ( sp1, sp2 )
1.1 albertel 1527: * Purpose: When concatanating sp1||sp2, calculate #pixels
1.2 ! albertel 1528: * we can "smash sp2 left"
1.1 albertel 1529: * --------------------------------------------------------------------------
1530: * Arguments: sp1 (I) subraster * to left-hand raster
1531: * sp2 (I) subraster * to right-hand raster
1532: * --------------------------------------------------------------------------
1.2 ! albertel 1533: * Returns: ( int ) max #pixels we can smash sp1||sp2,
1.1 albertel 1534: * or "blanksignal" if sp2 intentionally blank,
1535: * or 0 for any error.
1536: * --------------------------------------------------------------------------
1537: * Notes: o
1538: * ======================================================================= */
1539: /* --- entry point --- */
1.2 ! albertel 1540: int rastsmash ( subraster *sp1, subraster *sp2 )
1.1 albertel 1541: {
1542: /* -------------------------------------------------------------------------
1543: Allocations and Declarations
1544: -------------------------------------------------------------------------- */
1.2 ! albertel 1545: int nsmash = 0; /* #pixels to smash sp1||sp2 */
1.1 albertel 1546: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
1547: height1 = (sp1->image)->height, /* height for left-hand subraster */
1548: width1 = (sp1->image)->width, /* width for left-hand subraster */
1549: base2 = sp2->baseline, /*baseline for right-hand subraster*/
1550: height2 = (sp2->image)->height, /* height for right-hand subraster */
1551: width2 = (sp2->image)->width; /* width for right-hand subraster */
1552: int base = max2(base1,base2), /* max ascenders - 1 above baseline*/
1553: top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
1554: bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
1555: height = max2(bot1,bot2)+1; /* total height */
1556: int irow1=0,irow2=0, icol=0; /* row,col indexes */
1557: int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
1558: firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
1559: int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
1560: int type_raster(); /* display debugging output */
1561: /* -------------------------------------------------------------------------
1562: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
1563: -------------------------------------------------------------------------- */
1564: /* --- check args --- */
1565: if ( isstring ) goto end_of_job; /* ignore string rasters */
1.2 ! albertel 1566: if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
! 1567: if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
1.1 albertel 1568: if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 ! albertel 1569: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 1570: /* --- init firstcol1[], firstcol2[] --- */
1571: for ( irow1=0; irow1<height; irow1++ ) /* for each row */
1572: firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
1573: /* --- set firstcol2[] indicating left edge of sp2 --- */
1574: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
1575: for ( icol=0; icol<width2; icol++ ) /* find first non-empty col in row */
1576: if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
1577: { firstcol2[irow2] = icol; /* icol is #cols from left edge */
1578: nfirst2++; /* bump #rows containing set pixels*/
1579: break; } /* and go on to next row */
1580: if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
1.2 ! albertel 1581: { nsmash = blanksignal; /* signal intentional blanks */
! 1582: goto end_of_job; } /* don't smash intentional blanks */
1.1 albertel 1583: /* --- now check if preceding image in sp1 was an intentional blank --- */
1584: if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 ! albertel 1585: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 1586: /* --- set firstcol1[] indicating right edge of sp1 --- */
1587: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
1588: for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
1589: if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
1590: { firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
1591: nfirst1++; /* bump #rows containing set pixels*/
1592: break; } /* and go on to next row */
1593: if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
1.2 ! albertel 1594: goto end_of_job; /* don't smash intentional blanks */
1.1 albertel 1595: /* -------------------------------------------------------------------------
1596: find minimum separation
1597: -------------------------------------------------------------------------- */
1598: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
1599: int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
1600: if ( margin2 != blanksignal ) /* irow2 not an empty/blank row */
1601: for ( irow1=max2(irow2-smin,top1); ; irow1++ )
1602: if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
1603: else
1604: if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
1605: int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
1606: if ( ds >= smin ) continue; /* min unchanged */
1.2 ! albertel 1607: if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
1.1 albertel 1608: smin=ds; xmin=dx; ymin=dy; /* set new min */
1609: } /* --- end-of-if(margin1!=blanksignal) --- */
1.2 ! albertel 1610: if ( smin<2 ) goto end_of_job; /* can't smash */
1.1 albertel 1611: } /* --- end-of-for(irow2) --- */
1.2 ! albertel 1612: /*nsmash = min2(xmin,width2);*/ /* permissible smash */
! 1613: nsmash = xmin; /* permissible smash */
1.1 albertel 1614: /* -------------------------------------------------------------------------
1.2 ! albertel 1615: Back to caller with #pixels to smash sp1||sp2
1.1 albertel 1616: -------------------------------------------------------------------------- */
1617: end_of_job:
1618: /* --- debugging output --- */
1619: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
1.2 ! albertel 1620: { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
! 1621: nsmash,smashmargin);
1.1 albertel 1622: if ( msglevel >= 999 ) /* also display rasters */
1.2 ! albertel 1623: { fprintf(msgfp,"rastsmash>left-hand image...\n");
1.1 albertel 1624: if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
1.2 ! albertel 1625: fprintf(msgfp,"rastsmash>right-hand image...\n");
1.1 albertel 1626: if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
1627: fflush(msgfp); }
1.2 ! albertel 1628: return ( nsmash ); /* back with #smash pixels */
! 1629: } /* --- end-of-function rastsmash() --- */
1.1 albertel 1630:
1631:
1632: /* ==========================================================================
1633: * Function: accent_subraster ( accent, width, height, pixsz )
1634: * Purpose: Allocate a new subraster of width x height
1635: * (or maybe different dimensions, depending on accent),
1636: * and draw an accent (\hat or \vec or \etc) that fills it
1637: * --------------------------------------------------------------------------
1638: * Arguments: accent (I) int containing either HATACCENT or VECACCENT,
1639: * etc, indicating the type of accent desired
1640: * width (I) int containing desired width of accent (#cols)
1641: * height (I) int containing desired height of accent(#rows)
1642: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
1643: * --------------------------------------------------------------------------
1644: * Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
1645: * or NULL for any error.
1646: * --------------------------------------------------------------------------
1647: * Notes: o Some accents have internally-determined dimensions,
1648: * and caller should check dimensions in returned subraster
1649: * ======================================================================= */
1650: /* --- entry point --- */
1651: subraster *accent_subraster ( int accent, int width, int height, int pixsz )
1652: {
1653: /* -------------------------------------------------------------------------
1654: Allocations and Declarations
1655: -------------------------------------------------------------------------- */
1656: /* --- general info --- */
1657: raster *new_raster(), *rp=NULL; /*raster containing desired accent*/
1658: subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
1659: int delete_raster(), delete_subraster(); /*free allocated raster on err*/
1660: int line_raster(), /* draws lines */
1.2 ! albertel 1661: rule_raster(), /* draw solid boxes */
1.1 albertel 1662: thickness = 1; /* line thickness */
1663: /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
1664: /* --- other working info --- */
1665: int col0, col1, /* cols for line */
1666: row0, row1; /* rows for line */
1667: subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/
1668: /* --- info for under/overbraces, tildes, etc --- */
1669: char brace[16]; /*"{" for over, "}" for under, etc*/
1670: raster *rastrot(), /* rotate { for overbrace, etc */
1671: *rastcpy(); /* may need copy of original */
1672: subraster *arrow_subraster(); /* rightarrow for vec */
1673: subraster *rastack(); /* stack accent atop extra space */
1674: /* -------------------------------------------------------------------------
1675: outer switch() traps accents that may change caller's height,width
1676: -------------------------------------------------------------------------- */
1677: switch ( accent )
1678: {
1679: default:
1680: /* -----------------------------------------------------------------------
1681: inner switch() first allocates fixed-size raster for accents that don't
1682: ------------------------------------------------------------------------ */
1683: if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
1684: != NULL ) /* and if we succeeded... */
1685: switch ( accent ) /* ...draw requested accent in it */
1686: {
1687: /* --- unrecognized request --- */
1688: default: delete_raster(rp); /* unrecognized accent requested */
1689: rp = NULL; break; /* so free raster and signal error */
1690: /* --- bar request --- */
1691: case UNDERBARACCENT:
1692: case BARACCENT:
1.2 ! albertel 1693: thickness = 1; /*height-1;*/ /* adjust thickness */
1.1 albertel 1694: if ( accent == BARACCENT ) /* bar is above expression */
1.2 ! albertel 1695: { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
! 1696: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
1.1 albertel 1697: else /* underbar is below expression */
1.2 ! albertel 1698: { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
! 1699: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
1.1 albertel 1700: break;
1701: /* --- dot request --- */
1702: case DOTACCENT:
1703: thickness = height-1; /* adjust thickness */
1.2 ! albertel 1704: /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
! 1705: rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
1.1 albertel 1706: break;
1707: /* --- ddot request --- */
1708: case DDOTACCENT:
1709: thickness = height-1; /* adjust thickness */
1.2 ! albertel 1710: col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
! 1711: col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
! 1712: if ( col0+thickness >= col1 ) /* dots overlap */
! 1713: { col0 = max2(col0-1,0); /* try moving left dot more left */
! 1714: col1 = min2(col1+1,width-thickness); } /* and right dot right */
! 1715: if ( col0+thickness >= col1 ) /* dots _still_ overlap */
! 1716: thickness = max2(thickness-1,1); /* so try reducing thickness */
! 1717: /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
! 1718: /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
! 1719: rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
! 1720: rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
1.1 albertel 1721: break;
1722: /* --- hat request --- */
1723: case HATACCENT:
1.2 ! albertel 1724: thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
1.1 albertel 1725: line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/
1726: line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
1727: break;
1728: /* --- sqrt request --- */
1729: case SQRTACCENT:
1730: col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */
1731: col0 = (col1+2)/3; /* midpoint col of sqrt */
1732: row0 = (height+1)/2; /* midpoint row of sqrt */
1733: row1 = height-1; /* bottom row of sqrt */
1734: line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
1735: line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
1736: line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
1737: break;
1738: } /* --- end-of-inner-switch(accent) --- */
1739: break; /* break from outer accent switch */
1740: /* --- underbrace, overbrace request --- */
1741: case UNDERBRACE:
1742: case OVERBRACE:
1743: if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
1744: if ( accent == OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
1745: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
1746: != NULL ) /* found desired brace */
1747: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
1748: delete_subraster(accsp); } /* and free subraster "envelope" */
1749: break;
1750: /* --- hat request --- */
1751: case HATACCENT:
1752: if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
1753: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
1754: != NULL ) /* found desired brace */
1755: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
1756: delete_subraster(accsp); } /* and free subraster "envelope" */
1757: break;
1758: /* --- vec request --- */
1759: case VECACCENT:
1760: height = 2*(height/2) + 1; /* force height odd */
1761: if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
1762: != NULL ) /* succeeded */
1763: { rp = accsp->image; /* "extract" raster with bitmap */
1764: free((void *)accsp); } /* and free subraster "envelope" */
1765: break;
1766: /* --- tilde request --- */
1767: case TILDEACCENT:
1768: accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
1769: get_delim("~",-width,CMEX10)); /*width search for tilde*/
1770: if ( accsp != NULL ) /* found desired tilde */
1771: if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
1772: != NULL ) /* have tilde with space below it */
1773: { rp = sp->image; /* "extract" raster with bitmap */
1.2 ! albertel 1774: free((void *)sp); /* and free subraster "envelope" */
! 1775: leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
1.1 albertel 1776: break;
1777: } /* --- end-of-outer-switch(accent) --- */
1778: /* -------------------------------------------------------------------------
1779: if we constructed accent raster okay, embed it in a subraster and return it
1780: -------------------------------------------------------------------------- */
1781: /* --- if all okay, allocate subraster to contain constructed raster --- */
1782: if ( rp != NULL ) /* accent raster constructed okay */
1783: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
1784: == NULL ) /* and if we fail to allocate */
1785: delete_raster(rp); /* free now-unneeded raster */
1786: else /* subraster allocated okay */
1787: { /* --- init subraster parameters, embedding raster in it --- */
1788: sp->type = IMAGERASTER; /* constructed image */
1789: sp->image = rp; /* raster we just constructed */
1790: sp->size = (-1); /* can't set font size here */
1791: sp->baseline = 0; } /* can't set baseline here */
1792: /* --- return subraster containing desired accent to caller --- */
1793: return ( sp ); /* return accent or NULL to caller */
1794: } /* --- end-of-function accent_subraster() --- */
1795:
1796:
1797: /* ==========================================================================
1798: * Function: arrow_subraster ( width, height, pixsz, drctn, isBig )
1799: * Purpose: Allocate a raster/subraster and draw left/right arrow in it
1800: * --------------------------------------------------------------------------
1801: * Arguments: width (I) int containing number of cols for arrow
1802: * height (I) int containing number of rows for arrow
1803: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
1804: * drctn (I) int containing +1 for right arrow,
1805: * or -1 for left, 0 for leftright
1806: * isBig (I) int containing 1/true for \Long arrows,
1807: * or false for \long arrows, i.e.,
1808: * true for ===> or false for --->.
1809: * --------------------------------------------------------------------------
1810: * Returns: ( subraster * ) ptr to constructed left/right arrow
1811: * or NULL for any error.
1812: * --------------------------------------------------------------------------
1813: * Notes: o
1814: * ======================================================================= */
1815: /* --- entry point --- */
1816: subraster *arrow_subraster ( int width, int height, int pixsz,
1817: int drctn, int isBig )
1818: {
1819: /* -------------------------------------------------------------------------
1820: Allocations and Declarations
1821: -------------------------------------------------------------------------- */
1822: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
1823: int rule_raster(); /* draw arrow line */
1824: int irow, midrow=height/2; /* index, midrow is arrowhead apex */
1.2 ! albertel 1825: int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
1.1 albertel 1826: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
1827: int ipix, /* raster pixmap[] index */
1828: npix = width*height; /* #pixels malloced in pixmap[] */
1829: /* -------------------------------------------------------------------------
1830: allocate raster/subraster and draw arrow line
1831: -------------------------------------------------------------------------- */
1832: if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */
1833: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
1834: == NULL ) goto end_of_job; /* and quit if failed */
1835: if ( !isBig ) /* single line */
1836: rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
1837: else
1838: { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
1839: rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
1840: rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
1841: /* -------------------------------------------------------------------------
1842: construct arrowhead(s)
1843: -------------------------------------------------------------------------- */
1844: for ( irow=0; irow<height; irow++ ) /* for each row of arrow */
1845: {
1846: int delta = abs(irow-midrow); /*arrowhead offset for irow*/
1847: /* --- right arrowhead --- */
1848: if ( drctn >= 0 ) /* right arrowhead wanted */
1849: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
1850: { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
1851: if ( ipix >= 0 ) /* bounds check */
1852: if ( pixsz == 1 ) /* have a bitmap */
1853: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
1854: else /* should have a bytemap */
1855: if ( pixsz == 8 ) /* check pixsz for bytemap */
1856: ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
1857: /* --- left arrowhead (same as right except for ipix calculation) --- */
1858: if ( drctn <= 0 ) /* left arrowhead wanted */
1859: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
1860: { ipix = irow*width + delta + icol; /* leftmost bit+delta+icol */
1861: if ( ipix < npix ) /* bounds check */
1862: if ( pixsz == 1 ) /* have a bitmap */
1863: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
1864: else /* should have a bytemap */
1865: if ( pixsz == 8 ) /* check pixsz for bytemap */
1866: ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
1867: } /* --- end-of-for(irow) --- */
1868: end_of_job:
1869: return ( arrowsp ); /*back to caller with arrow or NULL*/
1870: } /* --- end-of-function arrow_subraster() --- */
1871:
1872:
1873: /* ==========================================================================
1874: * Function: uparrow_subraster ( width, height, pixsz, drctn, isBig )
1875: * Purpose: Allocate a raster/subraster and draw up/down arrow in it
1876: * --------------------------------------------------------------------------
1877: * Arguments: width (I) int containing number of cols for arrow
1878: * height (I) int containing number of rows for arrow
1879: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
1880: * drctn (I) int containing +1 for up arrow,
1881: * or -1 for down, or 0 for updown
1882: * isBig (I) int containing 1/true for \Long arrows,
1883: * or false for \long arrows, i.e.,
1884: * true for ===> or false for --->.
1885: * --------------------------------------------------------------------------
1886: * Returns: ( subraster * ) ptr to constructed up/down arrow
1887: * or NULL for any error.
1888: * --------------------------------------------------------------------------
1889: * Notes: o
1890: * ======================================================================= */
1891: /* --- entry point --- */
1892: subraster *uparrow_subraster ( int width, int height, int pixsz,
1893: int drctn, int isBig )
1894: {
1895: /* -------------------------------------------------------------------------
1896: Allocations and Declarations
1897: -------------------------------------------------------------------------- */
1898: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
1899: int rule_raster(); /* draw arrow line */
1900: int icol, midcol=width/2; /* index, midcol is arrowhead apex */
1.2 ! albertel 1901: int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
1.1 albertel 1902: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
1903: int ipix, /* raster pixmap[] index */
1904: npix = width*height; /* #pixels malloced in pixmap[] */
1905: /* -------------------------------------------------------------------------
1906: allocate raster/subraster and draw arrow line
1907: -------------------------------------------------------------------------- */
1908: if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */
1909: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
1910: == NULL ) goto end_of_job; /* and quit if failed */
1911: if ( !isBig ) /* single line */
1912: rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
1913: else
1914: { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
1915: rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
1916: rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
1917: /* -------------------------------------------------------------------------
1918: construct arrowhead(s)
1919: -------------------------------------------------------------------------- */
1920: for ( icol=0; icol<width; icol++ ) /* for each col of arrow */
1921: {
1922: int delta = abs(icol-midcol); /*arrowhead offset for icol*/
1923: /* --- up arrowhead --- */
1924: if ( drctn >= 0 ) /* up arrowhead wanted */
1925: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
1926: { ipix = (irow+delta)*width + icol; /* leftmost+icol */
1927: if ( ipix < npix ) /* bounds check */
1928: if ( pixsz == 1 ) /* have a bitmap */
1929: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
1930: else /* should have a bytemap */
1931: if ( pixsz == 8 ) /* check pixsz for bytemap */
1932: ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
1933: /* --- down arrowhead (same as up except for ipix calculation) --- */
1934: if ( drctn <= 0 ) /* down arrowhead wanted */
1935: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
1936: { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
1937: if ( ipix > 0 ) /* bounds check */
1938: if ( pixsz == 1 ) /* have a bitmap */
1939: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
1940: else /* should have a bytemap */
1941: if ( pixsz == 8 ) /* check pixsz for bytemap */
1942: ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
1.2 ! albertel 1943: } /* --- end-of-for(icol) --- */
1.1 albertel 1944: end_of_job:
1945: return ( arrowsp ); /*back to caller with arrow or NULL*/
1946: } /* --- end-of-function uparrow_subraster() --- */
1947:
1948:
1949: /* ==========================================================================
1950: * Function: rule_raster ( rp, top, left, width, height, type )
1951: * Purpose: Draw a solid or dashed line (or box) in existing raster rp,
1952: * starting at top,left with dimensions width,height.
1953: * --------------------------------------------------------------------------
1954: * Arguments: rp (I) raster * to raster in which rule
1955: * will be drawn
1956: * top (I) int containing row at which top-left corner
1957: * of rule starts (0 is topmost)
1958: * left (I) int containing col at which top-left corner
1959: * of rule starts (0 is leftmost)
1960: * width (I) int containing number of cols for rule
1961: * height (I) int containing number of rows for rule
1962: * type (I) int containing 0 for solid rule,
1963: * 1 for horizontal dashes, 2 for vertical
1.2 ! albertel 1964: * 3 for solid rule with corners removed
1.1 albertel 1965: * --------------------------------------------------------------------------
1966: * Returns: ( int ) 1 if rule drawn okay,
1967: * or 0 for any error.
1968: * --------------------------------------------------------------------------
1969: * Notes: o Rule line is implicitly "horizontal" or "vertical" depending
1970: * on relative width,height dimensions. It's a box if they're
1971: * more or less comparable.
1972: * ======================================================================= */
1973: /* --- entry point --- */
1974: int rule_raster ( raster *rp, int top, int left,
1975: int width, int height, int type )
1976: {
1977: /* -------------------------------------------------------------------------
1978: Allocations and Declarations
1979: -------------------------------------------------------------------------- */
1.2 ! albertel 1980: int irow=0, icol=0; /* indexes over rp raster */
! 1981: int ipix = 0, /* raster pixmap[] index */
1.1 albertel 1982: npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
1983: int isfatal = 0; /* true to abend on out-of-bounds error */
1984: int hdash=1, vdash=2; /* type for horizontal, vertical dashes */
1985: int dashlen=3, spacelen=2, /* #pixels for dash followed by space */
1986: isdraw=1; /* true when drawing dash (init for solid) */
1987: /* -------------------------------------------------------------------------
1988: Check args
1989: -------------------------------------------------------------------------- */
1990: if ( rp == (raster *)NULL ) /* no raster arg supplied */
1991: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
1992: rp = workingbox->image; /* use workingbox if possible */
1993: else return ( 0 ); /* otherwise signal error to caller */
1.2 ! albertel 1994: if ( type == 3 ) /* remove corners of solid box */
! 1995: if ( width<3 || height<3 ) type=0; /* too small to remove corners */
1.1 albertel 1996: /* -------------------------------------------------------------------------
1997: Fill line/box
1998: -------------------------------------------------------------------------- */
1999: for ( irow=top; irow<top+height; irow++ ) /*each scan line*/
2000: {
2001: if ( type == vdash ) /*set isdraw for vert dash*/
2002: isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
2003: ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/
2004: for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */
2005: {
1.2 ! albertel 2006: if ( type == 3 ) /* remove corners of box */
! 2007: if ( (irow==top && icol==left) /* top-left corner */
! 2008: || (irow==top && icol>=left+width-1) /* top-right corner */
! 2009: || (irow>=top+height-1 && icol==left) /* bottom-left corner */
! 2010: || (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
! 2011: isdraw = 0; else isdraw = 1; /*set isdraw to skip corner*/
1.1 albertel 2012: if ( type == hdash ) /*set isdraw for horiz dash*/
2013: isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
2014: if ( ++ipix >= npix ) /* bounds check failed */
2015: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2016: else break; /*or just go on to next row*/
2017: else /*ibit is within rp bounds*/
2018: if ( isdraw ) /*and we're drawing this bit*/
2019: if ( rp->pixsz == 1 ) /* have a bitmap */
2020: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2021: else /* should have a bytemap */
2022: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
2023: ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
2024: } /* --- end-of-for(icol) --- */
2025: } /* --- end-of-for(irow) --- */
2026: end_of_job:
2027: return ( isfatal? (ipix<npix? 1:0) : 1 );
2028: } /* --- end-of-function rule_raster() --- */
2029:
2030:
2031: /* ==========================================================================
2032: * Function: line_raster ( rp, row0, col0, row1, col1, thickness )
2033: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2034: * in existing raster rp.
2035: * --------------------------------------------------------------------------
2036: * Arguments: rp (I) raster * to raster in which a line
2037: * will be drawn
2038: * row0 (I) int containing row at which
2039: * line will start (0 is topmost)
2040: * col0 (I) int containing col at which
2041: * line will start (0 is leftmost)
2042: * row1 (I) int containing row at which
2043: * line will end (rp->height-1 is bottom-most)
2044: * col1 (I) int containing col at which
2045: * line will end (rp->width-1 is rightmost)
2046: * thickness (I) int containing number of pixels/bits
2047: * thick the line will be
2048: * --------------------------------------------------------------------------
2049: * Returns: ( int ) 1 if line drawn okay,
2050: * or 0 for any error.
2051: * --------------------------------------------------------------------------
2052: * Notes: o if row0==row1, a horizontal line is drawn
2053: * between col0 and col1, with row0(==row1) the top row
2054: * and row0+(thickness-1) the bottom row
2055: * o if col0==col1, a vertical bar is drawn
2056: * between row0 and row1, with col0(==col1) the left col
2057: * and col0+(thickness-1) the right col
2058: * o if both the above, you get a square thickness x thickness
2059: * whose top-left corner is row0,col0.
2060: * ======================================================================= */
2061: /* --- entry point --- */
2062: int line_raster ( raster *rp, int row0, int col0,
2063: int row1, int col1, int thickness )
2064: {
2065: /* -------------------------------------------------------------------------
2066: Allocations and Declarations
2067: -------------------------------------------------------------------------- */
1.2 ! albertel 2068: int irow=0, icol=0, /* indexes over rp raster */
1.1 albertel 2069: locol=col0, hicol=col1, /* col limits at irow */
2070: lorow=row0, hirow=row1; /* row limits at icol */
1.2 ! albertel 2071: int width=rp->width, height=rp->height; /* dimensions of input raster */
! 2072: int ipix = 0, /* raster pixmap[] index */
! 2073: npix = width*height; /* #pixels malloced in rp->pixmap[] */
1.1 albertel 2074: int isfatal = 0; /* true to abend on out-of-bounds error */
2075: int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
2076: double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
2077: dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
2078: a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
1.2 ! albertel 2079: xcol=0, xrow=0; /* calculated col at irow, or row at icol */
1.1 albertel 2080: double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
2081: xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
2082: ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
2083: xheight = 1.0;
2084: int line_recurse(), isrecurse=1; /* true to draw line recursively */
2085: /* -------------------------------------------------------------------------
2086: Check args
2087: -------------------------------------------------------------------------- */
2088: if ( rp == (raster *)NULL ) /* no raster arg supplied */
2089: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
2090: rp = workingbox->image; /* use workingbox if possible */
2091: else return ( 0 ); /* otherwise signal error to caller */
2092: /* -------------------------------------------------------------------------
2093: Initialization
2094: -------------------------------------------------------------------------- */
2095: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2096: fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
2097: "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
2098: row0,col0, row1,col1, thickness, dy,dx, a, xwidth);
2099: /* --- check for recursive line drawing --- */
1.2 ! albertel 2100: if ( isrecurse ) { /* drawing lines recursively */
! 2101: for ( irow=0; irow<thickness; irow++ ) /* each line 1 pixel thick */
! 2102: { double xrow0=(double)row0, xcol0=(double)col0,
! 2103: xrow1=(double)row1, xcol1=(double)col1;
! 2104: if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
! 2105: else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
! 2106: if( xrow0>(-0.001) && xcol0>(-0.001) /*check line inside raster*/
! 2107: && xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
! 2108: line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
! 2109: return ( 1 ); }
1.1 albertel 2110: /* --- set params for horizontal line or vertical bar --- */
2111: if ( isline ) /*interpret row as top row*/
2112: row1 = row0 + (thickness-1); /* set bottom row for line */
2113: if ( 0&&isbar ) /*interpret col as left col*/
2114: hicol = col0 + (thickness-1); /* set right col for bar */
2115: /* -------------------------------------------------------------------------
2116: draw line one row at a time
2117: -------------------------------------------------------------------------- */
2118: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
2119: {
2120: if ( !isbar && !isline ) /* neither vert nor horiz */
2121: { xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */
2122: locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
2123: hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
2124: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2125: fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
2126: irow,xcol,locol,hicol);
2127: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2128: for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
2129: if ( ++ipix >= npix ) /* bounds check failed */
2130: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2131: else break; /*or just go on to next row*/
2132: else /* turn on pixel in line */
2133: if ( rp->pixsz == 1 ) /* have a pixel bitmap */
2134: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2135: else /* should have a bytemap */
2136: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
2137: ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
2138: } /* --- end-of-for(irow) --- */
2139: /* -------------------------------------------------------------------------
2140: now _redraw_ line one col at a time to avoid "gaps"
2141: -------------------------------------------------------------------------- */
2142: if ( 1 )
2143: for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
2144: {
2145: if ( !isbar && !isline ) /* neither vert nor horiz */
2146: { xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */
2147: lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
2148: hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
2149: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2150: fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
2151: icol,xrow,lorow,hirow);
2152: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2153: for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
2154: if ( irow<0 || irow>=rp->height
2155: || icol<0 || icol>=rp->width ) /* bounds check */
2156: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2157: else continue; /*or just go on to next row*/
2158: else
2159: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2160: } /* --- end-of-for(irow) --- */
2161: /* -------------------------------------------------------------------------
2162: Back to caller with 1=okay, 0=failed.
2163: -------------------------------------------------------------------------- */
2164: end_of_job:
2165: return ( isfatal? (ipix<npix? 1:0) : 1 );
2166: } /* --- end-of-function line_raster() --- */
2167:
2168:
2169: /* ==========================================================================
2170: * Function: line_recurse ( rp, row0, col0, row1, col1, thickness )
2171: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2172: * in existing raster rp.
2173: * --------------------------------------------------------------------------
2174: * Arguments: rp (I) raster * to raster in which a line
2175: * will be drawn
2176: * row0 (I) double containing row at which
2177: * line will start (0 is topmost)
2178: * col0 (I) double containing col at which
2179: * line will start (0 is leftmost)
2180: * row1 (I) double containing row at which
2181: * line will end (rp->height-1 is bottom-most)
2182: * col1 (I) double containing col at which
2183: * line will end (rp->width-1 is rightmost)
2184: * thickness (I) int containing number of pixels/bits
2185: * thick the line will be
2186: * --------------------------------------------------------------------------
2187: * Returns: ( int ) 1 if line drawn okay,
2188: * or 0 for any error.
2189: * --------------------------------------------------------------------------
2190: * Notes: o Recurses, drawing left- and right-halves of line
2191: * until a horizontal or vertical segment is found
2192: * ======================================================================= */
2193: /* --- entry point --- */
2194: int line_recurse ( raster *rp, double row0, double col0,
2195: double row1, double col1, int thickness )
2196: {
2197: /* -------------------------------------------------------------------------
2198: Allocations and Declarations
2199: -------------------------------------------------------------------------- */
2200: double delrow = fabs(row1-row0), /* 0 if line horizontal */
2201: delcol = fabs(col1-col0), /* 0 if line vertical */
2202: tolerance = 0.5; /* draw line when it goes to point */
2203: double midrow = 0.5*(row0+row1), /* midpoint row */
2204: midcol = 0.5*(col0+col1); /* midpoint col */
2205: /* -------------------------------------------------------------------------
2206: recurse if either delta > tolerance
2207: -------------------------------------------------------------------------- */
2208: if ( delrow > tolerance /* row hasn't converged */
2209: || delcol > tolerance ) /* col hasn't converged */
2210: { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
2211: line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
2212: return ( 1 ); }
2213: /* -------------------------------------------------------------------------
2214: draw converged point
2215: -------------------------------------------------------------------------- */
2216: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
2217: return ( 1 );
2218: } /* --- end-of-function line_recurse() --- */
2219:
2220:
2221: /* ==========================================================================
2222: * Function: circle_raster ( rp, row0, col0, row1, col1,
2223: * thickness, quads )
2224: * Purpose: Draw quad(rant)s of an ellipse in box determined by
2225: * diagonally opposite corner points (row0,col0) and
2226: * (row1,col1), of thickness pixels in existing raster rp.
2227: * --------------------------------------------------------------------------
2228: * Arguments: rp (I) raster * to raster in which an ellipse
2229: * will be drawn
2230: * row0 (I) int containing 1st corner row bounding ellipse
2231: * (0 is topmost)
2232: * col0 (I) int containing 1st corner col bounding ellipse
2233: * (0 is leftmost)
2234: * row1 (I) int containing 2nd corner row bounding ellipse
2235: * (rp->height-1 is bottom-most)
2236: * col1 (I) int containing 2nd corner col bounding ellipse
2237: * (rp->width-1 is rightmost)
2238: * thickness (I) int containing number of pixels/bits
2239: * thick the ellipse arc line will be
2240: * quads (I) char * to null-terminated string containing
2241: * any subset/combination of "1234" specifying
2242: * which quadrant(s) of ellipse to draw.
2243: * NULL ptr draws all four quadrants;
2244: * otherwise 1=upper-right quadrant,
2245: * 2=uper-left, 3=lower-left, 4=lower-right,
2246: * i.e., counterclockwise from 1=positive quad.
2247: * --------------------------------------------------------------------------
2248: * Returns: ( int ) 1 if ellipse drawn okay,
2249: * or 0 for any error.
2250: * --------------------------------------------------------------------------
2251: * Notes: o row0==row1 or col0==col1 are errors
2252: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
2253: * ======================================================================= */
2254: /* --- entry point --- */
2255: int circle_raster ( raster *rp, int row0, int col0,
2256: int row1, int col1, int thickness, char *quads )
2257: {
2258: /* -------------------------------------------------------------------------
2259: Allocations and Declarations
2260: -------------------------------------------------------------------------- */
2261: /* --- lower-left and upper-right bounding points (in our coords) --- */
2262: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
2263: locol = min2(col0,col1), /* lower bounding col (left of box)*/
2264: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
2265: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
2266: /* --- a and b ellipse params --- */
2267: int width = hicol-locol+1, /* width of bounding box */
2268: height= hirow-lorow+1, /* height of bounding box */
2269: islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
2270: double a = ((double)width)/2.0, /* x=a when y=0 */
2271: b = ((double)height)/2.0, /* y=b when x=0 */
2272: abmajor = (islandscape? a : b), /* max2(a,b) */
2273: abminor = (islandscape? b : a), /* min2(a,b) */
2274: abmajor2 = abmajor*abmajor, /* abmajor^2 */
2275: abminor2 = abminor*abminor; /* abminor^2 */
2276: /* --- other stuff --- */
2277: int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
2278: iminor=0, nminor=min2(width,height); /* solved index on minor axis */
2279: int irow, icol, /* raster indexes at circumference */
2280: rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/
2281: double midrow= ((double)(row0+row1))/2.0, /* center row */
2282: midcol= ((double)(col0+col1))/2.0; /* center col */
2283: double xy, xy2, /* major axis ellipse coord */
2284: yx2, yx; /* solved minor ellipse coord */
2285: int isokay = 1; /* true if no pixels out-of-bounds */
2286: char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */
2287: int circle_recurse(), isrecurse=1; /* true to draw ellipse recursively*/
2288: /* -------------------------------------------------------------------------
2289: pixel-by-pixel along positive major axis, quit when it goes negative
2290: -------------------------------------------------------------------------- */
2291: if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */
2292: if ( msgfp!=NULL && msglevel>=39 ) /* debugging */
2293: fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
2294: width,height,quads);
2295: if ( nmajor < 1 ) isokay = 0; /* problem with input args */
2296: else
2297: {
2298: if ( isrecurse ) /* use recursive algorithm */
2299: {
2300: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2301: {
2302: double theta0=0.0, theta1=0.0; /* set thetas based on quadrant */
2303: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2304: { default: /* unrecognized, assume quadrant 1 */
2305: case '1': theta0= 0.0; theta1= 90.0; break; /* first quadrant */
2306: case '2': theta0= 90.0; theta1=180.0; break; /* second quadrant */
2307: case '3': theta0=180.0; theta1=270.0; break; /* third quadrant */
2308: case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
2309: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
2310: } /* --- end-of-for(qptr) --- */
2311: return ( 1 );
2312: } /* --- end-of-if(isrecurse) --- */
2313: for ( imajor=(nmajor+1)/2; ; imajor-- )
2314: {
2315: /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
2316: xy = ((double)imajor); /* xy = abmajor ... 0 */
2317: if ( xy < 0.0 ) break; /* negative side symmetrical */
2318: yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
2319: yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */
2320: iminor = iround(yx); /* nearest integer */
2321: /* --- set pixels for each requested quadrant --- */
2322: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2323: {
2324: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
2325: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2326: { default: break; /* unrecognized, assume quadrant 1 */
2327: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
2328: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
2329: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
2330: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
2331: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
2332: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
2333: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
2334: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
2335: fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
2336: imajor,iminor,*qptr,irow,icol);
2337: if ( irow<0 || irow>=rp->height /* row outside raster */
2338: || icol<0 || icol>=rp->width ) /* col outside raster */
2339: { isokay = 0; /* signal out-of-bounds pixel */
2340: continue; } /* but still try remaining points */
2341: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2342: } /* --- end-of-for(qptr) --- */
2343: } /* --- end-of-for(imajor) --- */
2344: /* ------------------------------------------------------------------------
2345: now do it _again_ along minor axis to avoid "gaps"
2346: ------------------------------------------------------------------------- */
2347: if ( 1 && iminor>0 )
2348: for ( iminor=(nminor+1)/2; ; iminor-- )
2349: {
2350: /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
2351: yx = ((double)iminor); /* yx = abminor ... 0 */
2352: if ( yx < 0.0 ) break; /* negative side symmetrical */
2353: xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
2354: xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */
2355: imajor = iround(xy); /* nearest integer */
2356: /* --- set pixels for each requested quadrant --- */
2357: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2358: {
2359: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
2360: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2361: { default: break; /* unrecognized, assume quadrant 1 */
2362: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
2363: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
2364: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
2365: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
2366: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
2367: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
2368: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
2369: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
2370: fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
2371: iminor,imajor,*qptr,irow,icol);
2372: if ( irow<0 || irow>=rp->height /* row outside raster */
2373: || icol<0 || icol>=rp->width ) /* col outside raster */
2374: { isokay = 0; /* signal out-of-bounds pixel */
2375: continue; } /* but still try remaining points */
2376: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2377: } /* --- end-of-for(qptr) --- */
2378: } /* --- end-of-for(iminor) --- */
2379: } /* --- end-of-if/else(nmajor<1) --- */
2380: return ( isokay );
2381: } /* --- end-of-function circle_raster() --- */
2382:
2383:
2384: /* ==========================================================================
2385: * Function: circle_recurse ( rp, row0, col0, row1, col1,
2386: * thickness, theta0, theta1 )
2387: * Purpose: Recursively draws arc theta0<=theta<=theta1 of the ellipse
2388: * in box determined by diagonally opposite corner points
2389: * (row0,col0) and (row1,col1), of thickness pixels in raster rp.
2390: * --------------------------------------------------------------------------
2391: * Arguments: rp (I) raster * to raster in which an ellipse
2392: * will be drawn
2393: * row0 (I) int containing 1st corner row bounding ellipse
2394: * (0 is topmost)
2395: * col0 (I) int containing 1st corner col bounding ellipse
2396: * (0 is leftmost)
2397: * row1 (I) int containing 2nd corner row bounding ellipse
2398: * (rp->height-1 is bottom-most)
2399: * col1 (I) int containing 2nd corner col bounding ellipse
2400: * (rp->width-1 is rightmost)
2401: * thickness (I) int containing number of pixels/bits
2402: * thick the ellipse arc line will be
2403: * theta0 (I) double containing first angle -360 -> +360
2404: * theta1 (I) double containing second angle -360 -> +360
2405: * 0=x-axis, positive moving counterclockwise
2406: * --------------------------------------------------------------------------
2407: * Returns: ( int ) 1 if ellipse drawn okay,
2408: * or 0 for any error.
2409: * --------------------------------------------------------------------------
2410: * Notes: o row0==row1 or col0==col1 are errors
2411: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
2412: * Then, with x=r*cos(theta), y=r*sin(theta), ellipse
2413: * equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
2414: * ======================================================================= */
2415: /* --- entry point --- */
2416: int circle_recurse ( raster *rp, int row0, int col0,
2417: int row1, int col1, int thickness, double theta0, double theta1 )
2418: {
2419: /* -------------------------------------------------------------------------
2420: Allocations and Declarations
2421: -------------------------------------------------------------------------- */
2422: /* --- lower-left and upper-right bounding points (in our coords) --- */
2423: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
2424: locol = min2(col0,col1), /* lower bounding col (left of box)*/
2425: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
2426: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
2427: /* --- a and b ellipse params --- */
2428: int width = hicol-locol+1, /* width of bounding box */
2429: height= hirow-lorow+1; /* height of bounding box */
2430: double a = ((double)width)/2.0, /* col x=a when row y=0 */
2431: b = ((double)height)/2.0, /* row y=b when col x=0 */
2432: ab=a*b, a2=a*a, b2=b*b; /* product and squares */
2433: /* --- arc parameters --- */
2434: double rads = 0.017453292, /* radians per degree = 1/57.29578 */
2435: lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
2436: hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
2437: locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
2438: hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
2439: rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
2440: rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
2441: xlo=rlo*locos, ylo=rlo*losin, /*col,row pixel coords for lotheta*/
2442: xhi=rhi*hicos, yhi=rhi*hisin, /*col,row pixel coords for hitheta*/
2443: xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
2444: tolerance = 0.5; /* convergence tolerance */
2445: /* -------------------------------------------------------------------------
2446: recurse if either delta > tolerance
2447: -------------------------------------------------------------------------- */
2448: if ( ydelta > tolerance /* row hasn't converged */
2449: || xdelta > tolerance ) /* col hasn't converged */
2450: { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
2451: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta); /*lo*/
2452: circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
2453: /* -------------------------------------------------------------------------
2454: draw converged point
2455: -------------------------------------------------------------------------- */
2456: else
2457: { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi), /* relative to center*/
2458: centerrow = 0.5*((double)(lorow+hirow)), /* ellipse y-center */
2459: centercol = 0.5*((double)(locol+hicol)), /* ellipse x-center */
2460: midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
2461: setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
2462: return ( 1 );
2463: } /* --- end-of-function circle_recurse() --- */
2464:
2465:
2466: /* ==========================================================================
2467: * Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
2468: * Purpose: Recursively draw bezier from r0,c0 to r1,c1
2469: * (with tangent point rt,ct) in existing raster rp.
2470: * --------------------------------------------------------------------------
2471: * Arguments: rp (I) raster * to raster in which a line
2472: * will be drawn
2473: * r0 (I) double containing row at which
2474: * bezier will start (0 is topmost)
2475: * c0 (I) double containing col at which
2476: * bezier will start (0 is leftmost)
2477: * r1 (I) double containing row at which
2478: * bezier will end (rp->height-1 is bottom-most)
2479: * c1 (I) double containing col at which
2480: * bezier will end (rp->width-1 is rightmost)
2481: * rt (I) double containing row for tangent point
2482: * ct (I) double containing col for tangent point
2483: * --------------------------------------------------------------------------
2484: * Returns: ( int ) 1 if line drawn okay,
2485: * or 0 for any error.
2486: * --------------------------------------------------------------------------
2487: * Notes: o Recurses, drawing left- and right-halves of bezier curve
2488: * until a point is found
2489: * ======================================================================= */
2490: /* --- entry point --- */
2491: int bezier_raster ( raster *rp, double r0, double c0,
2492: double r1, double c1, double rt, double ct )
2493: {
2494: /* -------------------------------------------------------------------------
2495: Allocations and Declarations
2496: -------------------------------------------------------------------------- */
2497: double delrow = fabs(r1-r0), /* 0 if same row */
2498: delcol = fabs(c1-c0), /* 0 if same col */
2499: tolerance = 0.5; /* draw curve when it goes to point*/
2500: double midrow = 0.5*(r0+r1), /* midpoint row */
2501: midcol = 0.5*(c0+c1); /* midpoint col */
2502: int irow=0, icol=0; /* point to be drawn */
2503: int status = 1; /* return status */
2504: /* -------------------------------------------------------------------------
2505: recurse if either delta > tolerance
2506: -------------------------------------------------------------------------- */
2507: if ( delrow > tolerance /* row hasn't converged */
2508: || delcol > tolerance ) /* col hasn't converged */
2509: { bezier_raster(rp, r0,c0, /* left half */
2510: 0.5*(rt+midrow), 0.5*(ct+midcol),
2511: 0.5*(r0+rt), 0.5*(c0+ct) );
2512: bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
2513: r1,c1,
2514: 0.5*(r1+rt), 0.5*(c1+ct) );
2515: return ( 1 ); }
2516: /* -------------------------------------------------------------------------
2517: draw converged point
2518: -------------------------------------------------------------------------- */
2519: /* --- get integer point --- */
2520: irow = iround(midrow); /* row pixel coord */
2521: icol = iround(midcol); /* col pixel coord */
2522: /* --- bounds check --- */
2523: if ( irow>=0 && irow<rp->height /* row in bounds */
2524: && icol>=0 && icol<rp->width ) /* col in bounds */
2525: setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/
2526: else status = 0; /* bad status if out-of-bounds */
2527: return ( status );
2528: } /* --- end-of-function bezier_raster() --- */
2529:
2530:
2531: /* ==========================================================================
2532: * Function: border_raster ( rp, ntop, nbot, isline, isfree )
2533: * Purpose: Allocate a new raster containing a copy of input rp,
2534: * along with ntop extra rows at top and nbot at bottom,
2535: * and whose width is either adjusted correspondingly,
2536: * or is automatically enlarged to a multiple of 8
2537: * with original bitmap centered
2538: * --------------------------------------------------------------------------
2539: * Arguments: rp (I) raster * to raster on which a border
2540: * is to be placed
2541: * ntop (I) int containing number extra rows at top.
2542: * if negative, abs(ntop) used, and same
2543: * number of extra cols added at left.
2544: * nbot (I) int containing number extra rows at bottom.
2545: * if negative, abs(nbot) used, and same
2546: * number of extra cols added at right.
2547: * isline (I) int containing 0 to leave border pixels clear
2548: * or >0 to draw a line around border of width
2549: * isline.
2550: * isfree (I) int containing true to free rp before return
2551: * --------------------------------------------------------------------------
2552: * Returns: ( raster * ) ptr to bordered raster,
2553: * or NULL for any error.
2554: * --------------------------------------------------------------------------
2555: * Notes: o
2556: * ======================================================================= */
2557: /* --- entry point --- */
2558: raster *border_raster ( raster *rp, int ntop, int nbot,
2559: int isline, int isfree )
2560: {
2561: /* -------------------------------------------------------------------------
2562: Allocations and Declarations
2563: -------------------------------------------------------------------------- */
2564: raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/
2565: int rastput(); /* overlay rp in new bordered raster */
2566: int width = (rp==NULL?0:rp->width), /* height of raster */
2567: height = (rp==NULL?0:rp->height), /* width of raster */
2568: istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
2569: leftmargin = 0; /* adjust width to whole number of bytes */
2570: int delete_raster(); /* to free input rp if isdelete is true */
2571: /* -------------------------------------------------------------------------
2572: Initialization
2573: -------------------------------------------------------------------------- */
2574: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
2575: if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
2576: { bp=rp; goto end_of_job; } /* return ascii string unchanged */
2577: /* --- check for negative args --- */
2578: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
2579: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
2580: /* --- adjust height for ntop and nbot margins --- */
2581: height += (ntop+nbot); /* adjust height for margins */
2582: /* --- adjust width for left and right margins --- */
2583: if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/
2584: { /* --- adjust width (and leftmargin) as requested by caller -- */
2585: if ( istopneg ) { width += ntop; leftmargin = ntop; }
2586: if ( isbotneg ) width += nbot; }
2587: else
2588: { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
2589: leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
2590: width += leftmargin; /* width now multiple of 8 */
2591: leftmargin /= 2; } /* center original raster */
2592: /* -------------------------------------------------------------------------
2593: allocate bordered raster, and embed rp within it
2594: -------------------------------------------------------------------------- */
2595: /* --- allocate bordered raster --- */
2596: if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/
2597: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
2598: /* --- embed rp in it --- */
2599: rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */
2600: /* -------------------------------------------------------------------------
2601: draw border if requested
2602: -------------------------------------------------------------------------- */
2603: if ( isline )
2604: { int irow, icol, nthick=isline; /*height,width index, line thickness*/
2605: /* --- draw left- and right-borders --- */
2606: for ( irow=0; irow<height; irow++ ) /* for each row of bp */
2607: for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
2608: { setpixel(bp,irow,icol,255); /* left border */
2609: setpixel(bp,irow,width-1-icol,255); } /* right border */
2610: /* --- draw top- and bottom-borders --- */
2611: for ( icol=0; icol<width; icol++ ) /* for each col of bp */
2612: for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
2613: { setpixel(bp,irow,icol,255); /* top border */
2614: setpixel(bp,height-1-irow,icol,255); } /* bottom border */
2615: } /* --- end-of-if(isline) --- */
2616: /* -------------------------------------------------------------------------
2617: free rp if no longer needed
2618: -------------------------------------------------------------------------- */
2619: if ( isfree ) /*caller no longer needs rp*/
2620: delete_raster(rp); /* so free it for him */
2621: /* -------------------------------------------------------------------------
2622: Back to caller with bordered raster (or null for any error)
2623: -------------------------------------------------------------------------- */
2624: end_of_job:
2625: return ( bp ); /* back with bordered or null ptr */
2626: } /* --- end-of-function border_raster() --- */
2627:
2628:
2629: /* ==========================================================================
2630: * Function: type_raster ( rp, fp )
2631: * Purpose: Emit an ascii dump representing rp, on fp.
2632: * --------------------------------------------------------------------------
2633: * Arguments: rp (I) ptr to raster struct for which an
2634: * ascii dump is to be constructed.
2635: * fp (I) File ptr to output device (defaults to
2636: * stdout if passed as NULL).
2637: * --------------------------------------------------------------------------
2638: * Returns: ( int ) 1 if completed successfully,
2639: * or 0 otherwise (for any error).
2640: * --------------------------------------------------------------------------
2641: * Notes:
2642: * ======================================================================= */
2643: /* --- entry point --- */
2644: int type_raster ( raster *rp, FILE *fp )
2645: {
2646: /* -------------------------------------------------------------------------
2647: Allocations and Declarations
2648: -------------------------------------------------------------------------- */
2649: static int display_width = 72; /* max columns for display */
2650: static char display_chars[16] = /* display chars for bytemap */
2651: { ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
2652: char scanline[133]; /* ascii image for one scan line */
2653: int scan_width; /* #chars in scan (<=display_width)*/
2654: int irow, locol,hicol=(-1); /* height index, width indexes */
1.2 ! albertel 2655: raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
! 2656: int delete_raster(); /*free bitmap converted for display*/
1.1 albertel 2657: /* --------------------------------------------------------------------------
2658: initialization
2659: -------------------------------------------------------------------------- */
2660: /* --- redirect null fp --- */
2661: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
2662: /* --- check for ascii string --- */
2663: if ( isstring /* pixmap has string, not raster */
1.2 ! albertel 2664: || (0 && rp->height==1) ) /* infer input rp is a string */
1.1 albertel 2665: {
2666: char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
2667: int width = strlen(string); /* #chars in ascii string */
2668: while ( width > display_width-2 ) /* too big for one line */
2669: { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
2670: string += (display_width-2); /* bump string past displayed chars*/
2671: width -= (display_width-2); } /* decrement remaining width */
2672: fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
2673: return ( 1 );
2674: } /* --- end-of-if(isstring) --- */
2675: /* --------------------------------------------------------------------------
2676: display ascii dump of bitmap image (in segments if display_width < rp->width)
2677: -------------------------------------------------------------------------- */
1.2 ! albertel 2678: if ( rp->format == 2 /* input is .gf-formatted */
! 2679: || rp->format == 3 )
! 2680: bitmaprp = gftobitmap(rp); /* so convert it for display */
! 2681: if ( bitmaprp != NULL ) /* if we have image for display */
! 2682: while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
1.1 albertel 2683: {
2684: /* --- set hicol for this pass (locol set above) --- */
2685: hicol += display_width; /* show as much as display allows */
2686: if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
2687: scan_width = hicol-locol+1; /* #chars in this scan */
2688: if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
2689: /* ------------------------------------------------------------------------
2690: display all scan lines for this local...hicol segment range
2691: ------------------------------------------------------------------------ */
2692: for ( irow=0; irow<rp->height; irow++ ) /* all scan lines for col range */
2693: {
2694: /* --- allocations and declarations --- */
2695: int ipix, /* pixmap[] index for this scan */
2696: lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
2697: /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
2698: for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
1.2 ! albertel 2699: if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
! 2700: scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
1.1 albertel 2701: else /* should have a bytemap */
1.2 ! albertel 2702: if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
! 2703: { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
1.1 albertel 2704: ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
2705: scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
2706: /* --- display completed scan line --- */
2707: fprintf(fp,"%.*s\n",scan_width,scanline);
2708: } /* --- end-of-for(irow) --- */
2709: } /* --- end-of-while(hicol<rp->width) --- */
2710: /* -------------------------------------------------------------------------
2711: Back to caller with 1=okay, 0=failed.
2712: -------------------------------------------------------------------------- */
1.2 ! albertel 2713: if ( rp->format == 2 /* input was .gf-format */
! 2714: || rp->format == 3 )
! 2715: if ( bitmaprp != NULL ) /* and we converted it for display */
! 2716: delete_raster(bitmaprp); /* no longer needed, so free it */
1.1 albertel 2717: return ( 1 );
2718: } /* --- end-of-function type_raster() --- */
2719:
2720:
2721: /* ==========================================================================
2722: * Function: type_bytemap ( bp, grayscale, width, height, fp )
2723: * Purpose: Emit an ascii dump representing bp, on fp.
2724: * --------------------------------------------------------------------------
2725: * Arguments: bp (I) intbyte * to bytemap for which an
2726: * ascii dump is to be constructed.
2727: * grayscale (I) int containing #gray shades, 256 for 8-bit
2728: * width (I) int containing #cols in bytemap
2729: * height (I) int containing #rows in bytemap
2730: * fp (I) File ptr to output device (defaults to
2731: * stdout if passed as NULL).
2732: * --------------------------------------------------------------------------
2733: * Returns: ( int ) 1 if completed successfully,
2734: * or 0 otherwise (for any error).
2735: * --------------------------------------------------------------------------
2736: * Notes:
2737: * ======================================================================= */
2738: /* --- entry point --- */
2739: int type_bytemap ( intbyte *bp, int grayscale,
2740: int width, int height, FILE *fp )
2741: {
2742: /* -------------------------------------------------------------------------
2743: Allocations and Declarations
2744: -------------------------------------------------------------------------- */
2745: static int display_width = 72; /* max columns for display */
2746: int byte_width = 3, /* cols to display byte (ff+space) */
2747: maxbyte = 0; /* if maxbyte<16, set byte_width=2 */
2748: int white_byte = 0, /* show dots for white_byte's */
2749: black_byte = grayscale-1; /* show stars for black_byte's */
2750: char scanline[133]; /* ascii image for one scan line */
2751: int scan_width, /* #chars in scan (<=display_width)*/
2752: scan_cols; /* #cols in scan (hicol-locol+1) */
2753: int ibyte, /* bp[] index */
2754: irow, locol,hicol=(-1); /* height index, width indexes */
2755: /* --------------------------------------------------------------------------
2756: initialization
2757: -------------------------------------------------------------------------- */
2758: /* --- redirect null fp --- */
2759: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
2760: /* --- check for ascii string --- */
2761: if ( isstring ) /* bp has ascii string, not raster */
2762: { width = strlen((char *)bp); /* #chars in ascii string */
2763: height = 1; } /* default */
2764: /* --- see if we can get away with byte_width=1 --- */
2765: for ( ibyte=0; ibyte<width*height; ibyte++ ) /* check all bytes */
2766: { int byteval = (int)bp[ibyte]; /* current byte value */
2767: if ( byteval < black_byte ) /* if it's less than black_byte */
2768: maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
2769: if ( maxbyte < 16 ) /* bytevals will fit in one column */
2770: byte_width = 1; /* so reset display byte_width */
2771: /* --------------------------------------------------------------------------
2772: display ascii dump of bitmap image (in segments if display_width < rp->width)
2773: -------------------------------------------------------------------------- */
2774: while ( (locol=hicol+1) < width ) /*start where prev segment left off*/
2775: {
2776: /* --- set hicol for this pass (locol set above) --- */
2777: hicol += display_width/byte_width; /* show as much as display allows */
2778: if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
2779: scan_cols = hicol-locol+1; /* #cols in this scan */
2780: scan_width = byte_width*scan_cols; /* #chars in this scan */
2781: if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
2782: /* ------------------------------------------------------------------------
2783: display all scan lines for this local...hicol segment range
2784: ------------------------------------------------------------------------ */
2785: for ( irow=0; irow<height; irow++ ) /* all scan lines for col range */
2786: {
2787: /* --- allocations and declarations --- */
2788: int lobyte = irow*width + locol; /* first bp[] byte in this scan */
2789: char scanbyte[32]; /* sprintf() buffer for byte */
2790: /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
2791: memset(scanline,' ',scan_width); /* blank out scanline */
2792: for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
2793: { int byteval = (int)bp[lobyte+ibyte]; /* value of current byte */
2794: memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
2795: if ( byteval == black_byte ) /* but if we have a black byte */
2796: memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
2797: if ( byte_width > 1 ) /* don't blank out single char */
2798: scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
2799: if ( byteval != white_byte /* format bytes that are non-white */
2800: && byteval != black_byte ) /* and that are non-black */
2801: sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
2802: memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
2803: /* --- display completed scan line --- */
2804: fprintf(fp,"%.*s\n",scan_width,scanline);
2805: } /* --- end-of-for(irow) --- */
2806: } /* --- end-of-while(hicol<width) --- */
2807: /* -------------------------------------------------------------------------
2808: Back to caller with 1=okay, 0=failed.
2809: -------------------------------------------------------------------------- */
2810: return ( 1 );
2811: } /* --- end-of-function type_bytemap() --- */
2812:
2813:
2814: /* ==========================================================================
2815: * Function: xbitmap_raster ( rp, fp )
2816: * Purpose: Emit a mime xbitmap representing rp, on fp.
2817: * --------------------------------------------------------------------------
2818: * Arguments: rp (I) ptr to raster struct for which a mime
2819: * xbitmap is to be constructed.
2820: * fp (I) File ptr to output device (defaults to
2821: * stdout if passed as NULL).
2822: * --------------------------------------------------------------------------
2823: * Returns: ( int ) 1 if completed successfully,
2824: * or 0 otherwise (for any error).
2825: * --------------------------------------------------------------------------
2826: * Notes:
2827: * ======================================================================= */
2828: /* --- entry point --- */
2829: int xbitmap_raster ( raster *rp, FILE *fp )
2830: {
2831: /* -------------------------------------------------------------------------
2832: Allocations and Declarations
2833: -------------------------------------------------------------------------- */
2834: char *title = "image"; /* dummy title */
2835: int hex_bitmap(); /* dump bitmap as hex bytes */
2836: /* --------------------------------------------------------------------------
2837: emit text to display mime xbitmap representation of rp->bitmap image
2838: -------------------------------------------------------------------------- */
2839: /* --- first redirect null fp --- */
2840: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
2841: /* --- check for ascii string --- */
2842: if ( isstring ) /* pixmap has string, not raster */
2843: return ( 0 ); /* can't handle ascii string */
2844: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
2845: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
2846: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
2847: title,rp->width, title,rp->height );
2848: fprintf( fp, "static char %s_bits[] = {\n", title );
2849: hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */
2850: fprintf (fp,"};\n"); /* ending with "};" for C array */
2851: /* -------------------------------------------------------------------------
2852: Back to caller with 1=okay, 0=failed.
2853: -------------------------------------------------------------------------- */
2854: return ( 1 );
2855: } /* --- end-of-function xbitmap_raster() --- */
2856:
2857:
2858: /* ==========================================================================
1.2 ! albertel 2859: * Function: type_pbmpgm ( rp, ptype, file )
! 2860: * Purpose: Write pbm or pgm image of rp to file
! 2861: * --------------------------------------------------------------------------
! 2862: * Arguments: rp (I) ptr to raster struct for which
! 2863: * a pbm/pgm file is to be written.
! 2864: * ptype (I) int containing 1 for pbm, 2 for pgm, or
! 2865: * 0 to determine ptype from values in rp
! 2866: * file (I) ptr to null-terminated char string
! 2867: * containing name of fuke to be written
! 2868: * (see notes below).
! 2869: * --------------------------------------------------------------------------
! 2870: * Returns: ( int ) total #bytes written,
! 2871: * or 0 for any error.
! 2872: * --------------------------------------------------------------------------
! 2873: * Notes: o (a) If file==NULL, output is written to stdout;
! 2874: * (b) if *file=='\000' then file is taken as the
! 2875: * address of an output buffer to which output
! 2876: * is written (and is followed by a terminating
! 2877: * '\0' which is not counted in #bytes returned);
! 2878: * (c) otherwise file is the filename (opened and
! 2879: * closed internally) to which output is written,
! 2880: * except that any final .ext extension is replaced
! 2881: * by .pbm or .pgm depending on ptype.
! 2882: * ======================================================================= */
! 2883: /* --- entry point --- */
! 2884: int type_pbmpgm ( raster *rp, int ptype, char *file )
! 2885: {
! 2886: /* -------------------------------------------------------------------------
! 2887: Allocations and Declarations
! 2888: -------------------------------------------------------------------------- */
! 2889: int isokay=0, nbytes=0; /* completion flag, total #bytes written */
! 2890: int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
! 2891: int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
! 2892: ngray = 0; /* #gray scale values */
! 2893: FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
! 2894: char outline[1024], outfield[256], /* output line, field */
! 2895: cr[16] = "\n\000"; /* cr at end-of-line */
! 2896: int maxlinelen = 70; /* maximum allowed line length */
! 2897: int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
! 2898: static char
! 2899: *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
! 2900: *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
! 2901: *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
! 2902: /* -------------------------------------------------------------------------
! 2903: check input, determine grayscale, and set up output file if necessary
! 2904: -------------------------------------------------------------------------- */
! 2905: /* --- check input args --- */
! 2906: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
! 2907: if ( ptype != 0 ) /* we'll determine ptype below */
! 2908: if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
! 2909: /* --- determine largest (and smallest) value in pixmap --- */
! 2910: for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
! 2911: for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
! 2912: { int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
! 2913: pixmin = min2(pixmin,pixval); /* new minimum */
! 2914: pixmax = max2(pixmax,pixval); } /* new maximum */
! 2915: ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
! 2916: if ( ptype == 0 ) /* caller wants us to set ptype */
! 2917: ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
! 2918: /* --- open output file if necessary --- */
! 2919: if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
! 2920: else if ( *file != '\000' ) { /* explicit filename provided, so...*/
! 2921: char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
! 2922: strncpy(fname,file,255); /* local copy of file name */
! 2923: fname[255] = '\000'; /* make sure it's null terminated */
! 2924: if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
! 2925: strcat(fname,suffix[ptype]); /* so add extension */
! 2926: else /* we already have an extension */
! 2927: strcpy(pdot,suffix[ptype]); /* so replace original extension */
! 2928: if ( (fp = fopen(fname,mode[ptype])) /* open output file */
! 2929: == (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
! 2930: } /* --- ens-of-if(*file!='\0') --- */
! 2931: /* -------------------------------------------------------------------------
! 2932: format and write header
! 2933: -------------------------------------------------------------------------- */
! 2934: /* --- format header info --- */
! 2935: *outline = '\000'; /* initialize line buffer */
! 2936: strcat(outline,magic[ptype]); /* begin file with "magic number" */
! 2937: strcat(outline,cr); /* followed by cr to end line */
! 2938: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
! 2939: strcat(outline,outfield); /* add width and height to header */
! 2940: strcat(outline,cr); /* followed by cr to end line */
! 2941: if ( ptype == 2 ) /* need max grayscale value */
! 2942: { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
! 2943: strcat(outline,outfield); /* add max value to header */
! 2944: strcat(outline,cr); } /* followed by cr to end line */
! 2945: /* --- write header to file or memory buffer --- */
! 2946: if ( fp == NULL ) /* if we have no open file... */
! 2947: strcat(file,outline); /* add header to caller's buffer */
! 2948: else /* or if we have an open file... */
! 2949: if ( fputs(outline,fp) /* try writing header to open file */
! 2950: == EOF ) goto end_of_job; /* return with error if failed */
! 2951: nbytes += strlen(outline); /* bump output byte count */
! 2952: /* -------------------------------------------------------------------------
! 2953: format and write pixels
! 2954: -------------------------------------------------------------------------- */
! 2955: *outline = '\000'; /* initialize line buffer */
! 2956: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
! 2957: for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
! 2958: /* --- format value at irow,jcol--- */
! 2959: *outfield = '\000'; /* init empty field */
! 2960: if ( irow < rp->height ) { /* check row index */
! 2961: int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
! 2962: if ( ptype == 1 ) /* pixval must be 1 or 0 */
! 2963: pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
! 2964: sprintf(outfield,"%d ",pixval); } /* format pixel value */
! 2965: /* --- write line if this value won't fit on it (or last line) --- */
! 2966: if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
! 2967: || irow >= rp->height ) { /* force writing last line */
! 2968: strcat(outline,cr); /* add cr to end current line */
! 2969: if ( fp == NULL ) /* if we have no open file... */
! 2970: strcat(file,outline); /* add header to caller's buffer */
! 2971: else /* or if we have an open file... */
! 2972: if ( fputs(outline,fp) /* try writing header to open file */
! 2973: == EOF ) goto end_of_job; /* return with error if failed */
! 2974: nbytes += strlen(outline); /* bump output byte count */
! 2975: *outline = '\000'; /* re-initialize line buffer */
! 2976: } /* --- end-of-if(strlen>=maxlinelen) --- */
! 2977: if ( irow >= rp->height ) break; /* done after writing last line */
! 2978: /* --- concatanate value to line -- */
! 2979: strcat(outline,outfield); /* concatanate value to line */
! 2980: } /* --- end-of-for(jcol,irow) --- */
! 2981: isokay = 1; /* signal successful completion */
! 2982: /* -------------------------------------------------------------------------
! 2983: Back to caller with total #bytes written, or 0=failed.
! 2984: -------------------------------------------------------------------------- */
! 2985: end_of_job:
! 2986: if ( fp != NULL /* output written to an open file */
! 2987: && fp != stdout ) /* and it's not just stdout */
! 2988: fclose(fp); /* so close file before returning */
! 2989: return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
! 2990: } /* --- end-of-function type_pbmpgm() --- */
! 2991:
! 2992:
! 2993: /* ==========================================================================
1.1 albertel 2994: * Function: cstruct_chardef ( cp, fp, col1 )
2995: * Purpose: Emit a C struct of cp on fp, starting in col1.
2996: * --------------------------------------------------------------------------
2997: * Arguments: cp (I) ptr to chardef struct for which
2998: * a C struct is to be generated.
2999: * fp (I) File ptr to output device (defaults to
3000: * stdout if passed as NULL).
3001: * col1 (I) int containing 0...65; output lines
3002: * are preceded by col1 blanks.
3003: * --------------------------------------------------------------------------
3004: * Returns: ( int ) 1 if completed successfully,
3005: * or 0 otherwise (for any error).
3006: * --------------------------------------------------------------------------
3007: * Notes:
3008: * ======================================================================= */
3009: /* --- entry point --- */
3010: int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
3011: {
3012: /* -------------------------------------------------------------------------
3013: Allocations and Declarations
3014: -------------------------------------------------------------------------- */
3015: char field[64]; /* field within output line */
3016: int cstruct_raster(), /* emit a raster */
3017: emit_string(); /* emit a string and comment */
3018: /* -------------------------------------------------------------------------
3019: emit charnum, location, name / hirow, hicol, lorow, locol
3020: -------------------------------------------------------------------------- */
3021: /* --- charnum, location, name --- */
3022: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
3023: emit_string ( fp, col1, field, "character number, location");
3024: /* --- toprow, topleftcol, botrow, botleftcol --- */
1.2 ! albertel 3025: sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
1.1 albertel 3026: cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
3027: cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
3028: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
3029: /* -------------------------------------------------------------------------
3030: emit raster and chardef's closing brace, and then return to caller
3031: -------------------------------------------------------------------------- */
3032: cstruct_raster(&cp->image,fp,col1+4); /* emit raster */
3033: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3034: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3035: } /* --- end-of-function cstruct_chardef() --- */
3036:
3037:
3038: /* ==========================================================================
3039: * Function: cstruct_raster ( rp, fp, col1 )
3040: * Purpose: Emit a C struct of rp on fp, starting in col1.
3041: * --------------------------------------------------------------------------
3042: * Arguments: rp (I) ptr to raster struct for which
3043: * a C struct is to be generated.
3044: * fp (I) File ptr to output device (defaults to
3045: * stdout if passed as NULL).
3046: * col1 (I) int containing 0...65; output lines
3047: * are preceded by col1 blanks.
3048: * --------------------------------------------------------------------------
3049: * Returns: ( int ) 1 if completed successfully,
3050: * or 0 otherwise (for any error).
3051: * --------------------------------------------------------------------------
3052: * Notes:
3053: * ======================================================================= */
3054: /* --- entry point --- */
3055: int cstruct_raster ( raster *rp, FILE *fp, int col1 )
3056: {
3057: /* -------------------------------------------------------------------------
3058: Allocations and Declarations
3059: -------------------------------------------------------------------------- */
3060: char field[64]; /* field within output line */
3061: char typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
3062: int hex_bitmap(); /* to emit raster bitmap */
3063: int emit_string(); /* emit a string and comment */
3064: /* -------------------------------------------------------------------------
3065: emit width and height
3066: -------------------------------------------------------------------------- */
1.2 ! albertel 3067: sprintf(field,"{ %2d, %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
! 3068: rp->width,rp->height,rp->format,rp->pixsz,typecast);
! 3069: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
1.1 albertel 3070: /* -------------------------------------------------------------------------
3071: emit bitmap and closing brace, and return to caller
3072: -------------------------------------------------------------------------- */
3073: hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */
3074: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3075: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3076: } /* --- end-of-function cstruct_raster() --- */
3077:
3078:
3079: /* ==========================================================================
3080: * Function: hex_bitmap ( rp, fp, col1, isstr )
3081: * Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1.
3082: * If isstr (is string) is true, the dump is of the form
3083: * "\x01\x02\x03\x04\x05..."
3084: * Otherwise, if isstr is false, the dump is of the form
3085: * 0x01,0x02,0x03,0x04,0x05...
3086: * --------------------------------------------------------------------------
3087: * Arguments: rp (I) ptr to raster struct for which
3088: * a hex dump is to be constructed.
3089: * fp (I) File ptr to output device (defaults to
3090: * stdout if passed as NULL).
3091: * col1 (I) int containing 0...65; output lines
3092: * are preceded by col1 blanks.
3093: * isstr (I) int specifying dump format as described above
3094: * --------------------------------------------------------------------------
3095: * Returns: ( int ) 1 if completed successfully,
3096: * or 0 otherwise (for any error).
3097: * --------------------------------------------------------------------------
3098: * Notes:
3099: * ======================================================================= */
3100: /* --- entry point --- */
3101: int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
3102: {
3103: /* -------------------------------------------------------------------------
3104: Allocations and Declarations
3105: -------------------------------------------------------------------------- */
1.2 ! albertel 3106: int ibyte, /* pixmap[ibyte] index */
! 3107: nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
1.1 albertel 3108: char stub[64]=" ";/* col1 leading blanks */
3109: int linewidth = 64, /* (roughly) rightmost column */
3110: colwidth = (isstr? 4:5); /* #cols required for each byte */
3111: int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
3112: /* --------------------------------------------------------------------------
3113: initialization
3114: -------------------------------------------------------------------------- */
3115: /* --- redirect null fp --- */
3116: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3117: /* --- emit initial stub if wanted --- */
3118: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
3119: /* --------------------------------------------------------------------------
3120: emit hex dump of rp->bitmap image
3121: -------------------------------------------------------------------------- */
3122: if ( isstr ) fprintf(fp,"\""); /* opening " before first line */
3123: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* one byte at a time */
3124: {
3125: /* --- display a byte as hex char or number, depending on isstr --- */
3126: if ( isstr ) /* string format wanted */
3127: fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]); /*print byte as hex char*/
3128: else /* comma-separated format wanted */
3129: fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/
3130: /* --- add a separator and newline, etc, as necessary --- */
3131: if ( ibyte < nbytes-1) /* not the last byte yet */
3132: {
3133: if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
3134: if ( (ibyte+1)%ncols==0 ) /* need new line after every ncols */
3135: if ( !isstr ) /* for hex numbers format ... */
3136: fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
3137: else /* for string format... */
3138: fprintf(fp,"\"\n%.*s\"",col1,stub); /* ...need closing, opening "s */
3139: } /* --- end-of-if(ibyte<nbytes-1) --- */
3140: } /* --- end-of-for(ibyte) --- */
3141: if ( isstr ) fprintf(fp,"\""); /* closing " after last line */
3142: return ( 1 ); /* back with 1=okay, 0=failed */
3143: } /* --- end-of-function hex_bitmap() --- */
3144:
3145:
3146: /* ==========================================================================
3147: * Function: emit_string ( fp, col1, string, comment )
3148: * Purpose: Emit string on fp, starting in col1,
3149: * and followed by right-justified comment.
3150: * --------------------------------------------------------------------------
3151: * Arguments: fp (I) File ptr to output device (defaults to
3152: * stdout if passed as NULL).
3153: * col1 (I) int containing 0 or #blanks preceding string
3154: * string (I) char * containing string to be emitted.
3155: * If last char of string is '\n',
3156: * the emitted line ends with a newline,
3157: * otherwise not.
3158: * comment (I) NULL or char * containing right-justified
3159: * comment (we enclose between /star and star/)
3160: * --------------------------------------------------------------------------
3161: * Returns: ( int ) 1 if completed successfully,
3162: * or 0 otherwise (for any error).
3163: * --------------------------------------------------------------------------
3164: * Notes: o
3165: * ======================================================================= */
3166: /* --- entry point --- */
3167: int emit_string ( FILE *fp, int col1, char *string, char *comment )
3168: {
3169: /* -------------------------------------------------------------------------
3170: Allocations and Declarations
3171: -------------------------------------------------------------------------- */
3172: char line[256]; /* construct line with caller's fields */
3173: int fieldlen; /* #chars in one of caller's fields */
3174: int linelen = 72; /*line length (for right-justified comment)*/
3175: int isnewline = 0; /* true to emit \n at end of line */
3176: /* --------------------------------------------------------------------------
3177: construct line containing prolog, string, epilog, and finally comment
3178: -------------------------------------------------------------------------- */
3179: /* --- init line --- */
3180: memset(line,' ',255); /* start line with blanks */
3181: /* --- embed string into line --- */
3182: if ( string != NULL ) /* if caller gave us a string... */
3183: { fieldlen = strlen(string); /* #cols required for string */
3184: if ( string[fieldlen-1] == '\n' ) /* check last char for newline */
3185: { isnewline = 1; /* got it, so set flag */
3186: fieldlen--; } /* but don't print it yet */
3187: memcpy(line+col1,string,fieldlen); /* embid string starting at col1 */
3188: col1 += fieldlen; } /* bump col past epilog */
3189: /* --- embed comment into line --- */
3190: if ( comment != NULL ) /* if caller gave us a comment... */
3191: { fieldlen = 6 + strlen(comment); /* plus /star, star/, 2 spaces */
3192: if ( linelen-fieldlen < col1 ) /* comment won't fit */
3193: fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
3194: if ( fieldlen > 6 ) /* can fit all or part of comment */
3195: sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
3196: '*', fieldlen-6,comment, '*');
3197: col1 = linelen; } /* indicate line filled */
3198: /* --- line completed --- */
3199: line[col1] = '\000'; /* null-terminate completed line */
3200: /* -------------------------------------------------------------------------
3201: emit line, then back to caller with 1=okay, 0=failed.
3202: -------------------------------------------------------------------------- */
3203: /* --- first redirect null fp --- */
3204: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3205: /* --- emit line (and optional newline) --- */
3206: fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */
3207: if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/
3208: return ( 1 );
3209: } /* --- end-of-function emit_string() --- */
3210:
3211:
3212: /* ==========================================================================
1.2 ! albertel 3213: * Function: gftobitmap ( gf )
! 3214: * Purpose: convert .gf-like pixmap to bitmap image
! 3215: * --------------------------------------------------------------------------
! 3216: * Arguments: gf (I) raster * to struct in .gf-format
! 3217: * --------------------------------------------------------------------------
! 3218: * Returns: ( raster * ) image-format raster * if successful,
! 3219: * or NULL for any error.
! 3220: * --------------------------------------------------------------------------
! 3221: * Notes: o
! 3222: * ======================================================================= */
! 3223: /* --- entry point --- */
! 3224: raster *gftobitmap ( raster *gf )
! 3225: {
! 3226: /* -------------------------------------------------------------------------
! 3227: Allocations and Declarations
! 3228: -------------------------------------------------------------------------- */
! 3229: raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
! 3230: int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
! 3231: int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
! 3232: ibit=0, bitval=0; /* bitmap index, bit value */
! 3233: int isrepeat = 1, /* true to process repeat counts */
! 3234: repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
! 3235: nrepeats=0, irepeat=0, /* scan line repeat count,index */
! 3236: wbits = 0; /* count bits to width of scan line*/
! 3237: /* -------------------------------------------------------------------------
! 3238: initialization
! 3239: -------------------------------------------------------------------------- */
! 3240: /* --- check args --- */
! 3241: if ( gf == NULL ) goto end_of_job; /* input raster not provided */
! 3242: format = gf->format; /* 2 or 3 */
! 3243: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
! 3244: ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
! 3245: /* --- allocate output raster with proper dimensions for bitmap --- */
! 3246: width=gf->width; height=gf->height; /* dimensions of raster */
! 3247: if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
! 3248: == NULL ) goto end_of_job; /* quit if failed to allocate */
! 3249: totbits = width*height; /* total #bits in image */
! 3250: /* -------------------------------------------------------------------------
! 3251: fill bitmap
! 3252: -------------------------------------------------------------------------- */
! 3253: for ( icount=0,bitval=0; icount<ncounts; icount++ )
! 3254: {
! 3255: int nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
! 3256: if ( isrepeat /* we're proxessing repeat counts */
! 3257: && nbits == repeatcmds[format-2] ) /* and repeat opcode found */
! 3258: if ( nrepeats == 0 ) /* recursive repeat is error */
! 3259: { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
! 3260: nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
! 3261: icount += 2; } /* bump byte/nibble count */
! 3262: else /* some internal error occurred */
! 3263: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
! 3264: fprintf(msgfp,"gftobitmap> found embedded repeat command\n");
! 3265: if ( 0 )
! 3266: fprintf(stdout,
! 3267: "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
! 3268: icount,bitval,nbits,ibit,totbits);
! 3269: for ( ; nbits>0; nbits-- ) /* count down */
! 3270: { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
! 3271: for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
! 3272: if ( bitval == 1 ) /* set pixel */
! 3273: { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
! 3274: else /* clear pixel */
! 3275: { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
! 3276: if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
! 3277: ibit++; } /* bump bit index */
! 3278: bitval = 1-bitval; /* flip bit value */
! 3279: if ( wbits >= width ) { /* completed repeats */
! 3280: ibit += nrepeats*width; /*bump bit count past repeated scans*/
! 3281: if ( wbits > width ) /* out-of alignment error */
! 3282: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
! 3283: fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
! 3284: wbits = nrepeats = 0; } /* reset repeat counts */
! 3285: } /* --- end-of-for(icount) --- */
! 3286: end_of_job:
! 3287: return ( rp ); /* back to caller with image */
! 3288: } /* --- end-of-function gftobitmap() --- */
! 3289:
! 3290:
! 3291: /* ==========================================================================
1.1 albertel 3292: * Function: get_symdef ( symbol )
3293: * Purpose: returns mathchardef struct for symbol
3294: * --------------------------------------------------------------------------
3295: * Arguments: symbol (I) char * containing symbol
3296: * whose corresponding mathchardef is wanted
3297: * --------------------------------------------------------------------------
3298: * Returns: ( mathchardef * ) pointer to struct defining symbol,
3299: * or NULL for any error
3300: * --------------------------------------------------------------------------
3301: * Notes: o Input symbol need only contain a leading substring to match,
3302: * e.g., \gam passed in symbol will match \gamma in the table.
3303: * If the table contains two or more possible matches,
3304: * the shortest is returned, e.g., input \e will return with
3305: * data for \eta rather than \epsilon. To get \epsilon,
3306: * you must pass a leading substring long enough to eliminate
3307: * shorter table matches, i.e., in this case \ep
3308: * ======================================================================= */
3309: /* --- entry point --- */
3310: mathchardef *get_symdef ( char *symbol )
3311: {
3312: /* -------------------------------------------------------------------------
3313: Allocations and Declarations
3314: -------------------------------------------------------------------------- */
3315: mathchardef *symdefs = symtable; /* table of mathchardefs */
3316: int idef = 0, /* symdefs[] index */
3317: bestdef = (-9999); /*index of shortest matching symdef*/
3318: int symlen = strlen(symbol), /* length of input symbol */
3319: deflen, minlen=9999; /*length of shortest matching symdef*/
3320: int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
3321: alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
1.2 ! albertel 3322: int family = fontinfo[fontnum].family; /* current font family */
1.1 albertel 3323: static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
1.2 ! albertel 3324: /* --- see table on page 536 in TLC2 --- */
1.1 albertel 3325: {"\\int", "\\Bigint"},
3326: {"\\oint", "\\Bigoint"},
3327: {"\\sum", "\\Bigsum"},
3328: {"\\prod", "\\Bigprod"},
3329: {"\\coprod", "\\Bigcoprod"},
1.2 ! albertel 3330: /* --- must be 'big' when related to similar binary operators --- */
! 3331: {"\\bigcup", "\\Bigcup"},
! 3332: {"\\bigsqcup", "\\Bigsqcup"},
! 3333: {"\\bigcap", "\\Bigcap"},
! 3334: /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
! 3335: {"\\bigodot", "\\Bigodot"},
! 3336: {"\\bigoplus", "\\Bigoplus"},
! 3337: {"\\bigominus", "\\ominus"},
! 3338: {"\\bigotimes", "\\Bigotimes"},
! 3339: {"\\bigoslash", "\\oslash"},
! 3340: {"\\biguplus", "\\Biguplus"},
! 3341: {"\\bigwedge", "\\Bigwedge"},
! 3342: {"\\bigvee", "\\Bigvee"},
1.1 albertel 3343: {NULL, NULL} };
3344: /* -------------------------------------------------------------------------
3345: If in \displaystyle mode, first xlate int to Bigint, etc.
3346: -------------------------------------------------------------------------- */
3347: if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */
3348: for ( idef=0; ; idef++ ) { /* lookup symbol in displaysyms */
3349: char *fromsym = displaysyms[idef][0], /* look for this symbol */
3350: *tosym = displaysyms[idef][1]; /* and xlate it to this symbol */
3351: if ( fromsym == NULL ) break; /* end-of-table */
3352: if ( !strcmp(symbol,fromsym) ) /* found a match */
3353: { if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
3354: { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
3355: isdisplaystyle,symbol,tosym); fflush(msgfp); }
3356: symbol = tosym; /* so look up tosym instead */
3357: symlen = strlen(symbol); /* reset symbol length */
3358: break; } /* no need to search further */
3359: } /* --- end-of-for(idef) --- */
3360: /* -------------------------------------------------------------------------
3361: search symdefs[] in order for first occurrence of symbol
3362: -------------------------------------------------------------------------- */
3363: for ( idef=0; ;idef++ ) /* until trailer record found */
3364: if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
3365: else /* check against caller's symbol */
3366: if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
1.2 ! albertel 3367: if (fontnum==0 /* mathmode, so check every match */
! 3368: || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
! 3369: || symdefs[idef].handler!=NULL)) /* or text mode and directive */
! 3370: || (symdefs[idef].family==family /* have correct family */
! 3371: && symdefs[idef].handler==NULL) ) /* and not a handler collision */
! 3372: #if 0
! 3373: || (fontnum==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
! 3374: || (fontnum==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/
! 3375: || (fontnum==3 && symdefs[idef].family==BBOLD10 /*textmode && bb text*/
! 3376: && symdefs[idef].handler==NULL)
! 3377: || (fontnum==4 && symdefs[idef].family==CMMIB10 /*textmode && bf text*/
! 3378: && symdefs[idef].handler==NULL) )
! 3379: #endif
1.1 albertel 3380: if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
3381: { bestdef = idef; /* save index of new best match */
3382: if ( (minlen = deflen) /* and save its len for next test */
3383: == symlen ) break; } /*perfect match, so return with it*/
3384: if ( bestdef < 0 ) /* failed to look up symbol */
1.2 ! albertel 3385: if ( fontnum != 0 ) /* we're in a restricted font mode */
! 3386: { int oldfontnum = fontnum; /* save current font family */
! 3387: mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
! 3388: fontnum = 0; /*try to look up symbol in any font*/
! 3389: symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
! 3390: fontnum = oldfontnum; /* reset font family */
! 3391: return symdef; } /* caller gets fontnum=0 lookup */
1.1 albertel 3392: if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
1.2 ! albertel 3393: { fprintf(msgfp,"get_symdef> symbol=%s matches symtable[%d]=%s\n",
1.1 albertel 3394: symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
3395: fflush(msgfp); }
3396: return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/
3397: } /* --- end-of-function get_symdef() --- */
3398:
3399:
3400: /* ==========================================================================
3401: * Function: get_chardef ( symdef, size )
3402: * Purpose: returns chardef ptr containing data for symdef at given size
3403: * --------------------------------------------------------------------------
3404: * Arguments: symdef (I) mathchardef * corresponding to symbol
3405: * whose corresponding chardef is wanted
1.2 ! albertel 3406: * size (I) int containing 0-5 for desired size
1.1 albertel 3407: * --------------------------------------------------------------------------
3408: * Returns: ( chardef * ) pointer to struct defining symbol at size,
3409: * or NULL for any error
3410: * --------------------------------------------------------------------------
3411: * Notes: o if size unavailable, the next-closer-to-normalsize
3412: * is returned instead.
3413: * ======================================================================= */
3414: /* --- entry point --- */
3415: chardef *get_chardef ( mathchardef *symdef, int size )
3416: {
3417: /* -------------------------------------------------------------------------
3418: Allocations and Declarations
3419: -------------------------------------------------------------------------- */
3420: fontfamily *fonts = fonttable; /* table of font families */
3421: chardef **fontdef, /*tables for desired font, by size*/
3422: *gfdata = (chardef *)NULL; /* chardef for symdef,size */
3423: int ifont; /* fonts[] index */
3424: int family, charnum; /* indexes retrieved from symdef */
3425: int sizeinc = 0, /*+1 or -1 to get closer to normal*/
3426: normalsize = 2; /* this size always present */
3427: int isBig = 0; /*true if symbol's 1st char is upper*/
3428: char *symptr = NULL; /* look for 1st alpha of symbol */
3429: /* -------------------------------------------------------------------------
3430: initialization
3431: -------------------------------------------------------------------------- */
3432: /* --- check symdef --- */
3433: if ( symdef == NULL ) return ( NULL ); /* get_symdef() probably failed */
3434: /* --- get local copy of indexes from symdef --- */
3435: family = symdef->family; /* font family containing symbol */
3436: charnum = symdef->charnum; /* char# of symbol within font */
3437: /* --- check for supersampling --- */
3438: if ( issupersampling ) /* check for supersampling fonts */
3439: if ( fonts != ssfonttable ) /* uh oh--probably internal error */
3440: { fonts = ssfonttable; } /* force it */
3441: /* --- check requested size, and set size increment --- */
3442: if ( 0 && issupersampling ) /* set size index for supersampling */
3443: size = LARGESTSIZE+1; /* index 1 past largest size */
3444: else /* low pass indexes 0...LARGESTSIZE */
3445: {
3446: if( size<0 ) size = 0; /* size was definitely too small */
3447: if( size>LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */
3448: if( size<normalsize ) sizeinc = (+1); /*use next larger if size too small*/
3449: if( size>normalsize ) sizeinc = (-1); /*or next smaller if size too large*/
3450: }
3451: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
3452: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
3453: if ( isalpha(*symptr) ) /* found leading alpha char */
3454: { isBig = isupper(*symptr); /* is 1st char of name uppercase? */
3455: if ( !isBig /* 1st char lowercase */
3456: && strlen(symptr) >= 4 ) /* but followed by at least 3 chars */
3457: isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
3458: || !memcmp(symptr,"bigg",4); /* or with bigg */
3459: break; } /* don't check beyond 1st char */
3460: /* -------------------------------------------------------------------------
3461: find font family in table of fonts[]
3462: -------------------------------------------------------------------------- */
3463: /* --- look up font family --- */
3464: for ( ifont=0; ;ifont++ ) /* until trailer record found */
3465: if ( fonts[ifont].family < 0 ) return ( NULL ); /* error, no such family */
3466: else if ( fonts[ifont].family == family ) break; /* found font family */
3467: /* --- get local copy of table for this family by size --- */
3468: fontdef = fonts[ifont].fontdef; /* font by size */
3469: /* -------------------------------------------------------------------------
3470: get font in desired size, or closest available size, and return symbol
3471: -------------------------------------------------------------------------- */
3472: /* --- get font in desired size --- */
3473: while ( 1 ) /* find size or closest available */
3474: if ( fontdef[size] != NULL ) break; /* found available size */
3475: else /* adjust size closer to normal */
3476: if ( size == NORMALSIZE /* already normal so no more sizes,*/
3477: || sizeinc == 0 ) return ( NULL); /* or must be supersampling */
3478: else /*bump size 1 closer to NORMALSIZE*/
3479: size += sizeinc; /* see if adjusted size available */
3480: /* --- ptr to chardef struct --- */
3481: gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/
3482: /* -------------------------------------------------------------------------
3483: kludge to tweak CMEX10 (which appears to have incorrect descenders)
3484: -------------------------------------------------------------------------- */
3485: if ( family == CMEX10 ) /* cmex10 needs tweak */
3486: { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
3487: gfdata->botrow = (isBig? (-height/3) : (-height/4));
3488: gfdata->toprow = gfdata->botrow + gfdata->image.height; }
3489: /* -------------------------------------------------------------------------
3490: return subraster containing chardef data for symbol in requested size
3491: -------------------------------------------------------------------------- */
3492: return ( gfdata ); /*ptr to chardef for symbol in size*/
3493: } /* --- end-of-function get_chardef() --- */
3494:
3495:
3496: /* ==========================================================================
3497: * Function: get_charsubraster ( symdef, size )
3498: * Purpose: returns new subraster ptr containing
3499: * data for symdef at given size
3500: * --------------------------------------------------------------------------
1.2 ! albertel 3501: * Arguments: symdef (I) mathchardef * corresponding to symbol whose
! 3502: * corresponding chardef subraster is wanted
! 3503: * size (I) int containing 0-5 for desired size
1.1 albertel 3504: * --------------------------------------------------------------------------
3505: * Returns: ( subraster * ) pointer to struct defining symbol at size,
3506: * or NULL for any error
3507: * --------------------------------------------------------------------------
3508: * Notes: o just wraps a subraster envelope around get_chardef()
3509: * ======================================================================= */
3510: /* --- entry point --- */
3511: subraster *get_charsubraster ( mathchardef *symdef, int size )
3512: {
3513: /* -------------------------------------------------------------------------
3514: Allocations and Declarations
3515: -------------------------------------------------------------------------- */
3516: chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
3517: int get_baseline(); /* baseline of gfdata */
3518: subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
1.2 ! albertel 3519: raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
! 3520: int delete_subraster(); /* in case gftobitmap() fails */
1.1 albertel 3521: int aasupsamp(), /*antialias char with supersampling*/
3522: grayscale=256; /* aasupersamp() parameters */
3523: /* -------------------------------------------------------------------------
3524: look up chardef for symdef at size, and embed data (gfdata) in subraster
3525: -------------------------------------------------------------------------- */
3526: if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */
3527: != NULL ) /* and check that we found it */
3528: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
3529: != NULL ) /* and check that we succeeded */
3530: {
1.2 ! albertel 3531: raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
! 3532: int format = image->format; /* 1=bitmap, else .gf */
1.1 albertel 3533: sp->symdef = symdef; /* replace NULL with caller's arg */
3534: sp->size = size; /*replace default with caller's size*/
3535: sp->baseline = get_baseline(gfdata); /* get baseline of character */
1.2 ! albertel 3536: if ( format == 1 ) /* already a bitmap */
! 3537: { sp->type = CHARASTER; /* static char raster */
! 3538: sp->image = image; } /* store ptr to its bitmap */
! 3539: else /* need to convert .gf-to-bitmap */
! 3540: if ( (bitmaprp = gftobitmap(image)) /* convert */
! 3541: != (raster *)NULL ) /* successful */
! 3542: { sp->type = IMAGERASTER; /* allocated raster will be freed */
! 3543: sp->image = bitmaprp; } /* store ptr to converted bitmap */
! 3544: else /* conversion failed */
! 3545: { delete_subraster(sp); /* free unneeded subraster */
! 3546: sp = (subraster *)NULL; /* signal error to caller */
! 3547: goto end_of_job; } /* quit */
1.1 albertel 3548: if ( issupersampling ) /* antialias character right here */
3549: {
3550: raster *aa = NULL; /* antialiased char raster */
3551: int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
3552: if ( status ) /* supersampled successfully */
3553: { int baseline = sp->baseline; /* baseline before supersampling */
3554: int height = gfdata->image.height; /* #rows before supersampling */
3555: sp->image = aa; /* replace chardef with ss image */
3556: if ( baseline >= height-1 ) /* baseline at bottom of char */
3557: sp->baseline = aa->height -1; /* so keep it at bottom */
3558: else /* char has descenders */
3559: sp->baseline /= shrinkfactor; /* rescale baseline */
3560: sp->type = IMAGERASTER; } /* character is an image raster */
3561: } /* --- end-of-if(issupersampling) --- */
3562: } /* --- end-of-if(sp!=NULL) --- */
1.2 ! albertel 3563: end_of_job:
! 3564: if ( msgfp!=NULL && msglevel>=999 )
1.1 albertel 3565: { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",
3566: symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }
3567: return ( sp ); /* back to caller */
3568: } /* --- end-of-function get_charsubraster() --- */
3569:
3570:
3571: /* ==========================================================================
1.2 ! albertel 3572: * Function: get_symsubraster ( symbol, size )
! 3573: * Purpose: returns new subraster ptr containing
! 3574: * data for symbol at given size
! 3575: * --------------------------------------------------------------------------
! 3576: * Arguments: symbol (I) char * corresponding to symbol
! 3577: * whose corresponding subraster is wanted
! 3578: * size (I) int containing 0-5 for desired size
! 3579: * --------------------------------------------------------------------------
! 3580: * Returns: ( subraster * ) pointer to struct defining symbol at size,
! 3581: * or NULL for any error
! 3582: * --------------------------------------------------------------------------
! 3583: * Notes: o just combines get_symdef() and get_charsubraster()
! 3584: * ======================================================================= */
! 3585: /* --- entry point --- */
! 3586: subraster *get_symsubraster ( char *symbol, int size )
! 3587: {
! 3588: /* -------------------------------------------------------------------------
! 3589: Allocations and Declarations
! 3590: -------------------------------------------------------------------------- */
! 3591: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
! 3592: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
! 3593: /* -------------------------------------------------------------------------
! 3594: look up mathchardef for symbol
! 3595: -------------------------------------------------------------------------- */
! 3596: if ( symbol != NULL ) /* user supplied input symbol */
! 3597: symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
! 3598: /* -------------------------------------------------------------------------
! 3599: look up chardef for mathchardef and wrap a subraster structure around data
! 3600: -------------------------------------------------------------------------- */
! 3601: if ( symdef != NULL ) /* lookup succeeded */
! 3602: sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
! 3603: return ( sp ); /* back to caller with sp or NULL */
! 3604: } /* --- end-of-function get_symsubraster() --- */
! 3605:
! 3606:
! 3607: /* ==========================================================================
1.1 albertel 3608: * Function: get_baseline ( gfdata )
3609: * Purpose: returns baseline for a chardef struct
3610: * --------------------------------------------------------------------------
3611: * Arguments: gfdata (I) chardef * containing chardef for symbol
3612: * whose baseline is wanted
3613: * --------------------------------------------------------------------------
3614: * Returns: ( int ) baseline for symdef,
3615: * or -1 for any error
3616: * --------------------------------------------------------------------------
3617: * Notes: o Unlike TeX, the top-left corners of our rasters are (0,0),
3618: * with (row,col) increasing as you move down and right.
3619: * Baselines are calculated with respect to this scheme,
3620: * so 0 would mean the very top row is on the baseline
3621: * and everything else descends below the baseline.
3622: * ======================================================================= */
3623: /* --- entry point --- */
3624: int get_baseline ( chardef *gfdata )
3625: {
3626: /* -------------------------------------------------------------------------
3627: Allocations and Declarations
3628: -------------------------------------------------------------------------- */
3629: int /*toprow = gfdata->toprow,*/ /*TeX top row from .gf file info*/
3630: botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/
3631: height = gfdata->image.height; /* #rows comprising symbol */
3632: /* -------------------------------------------------------------------------
3633: give caller baseline
3634: -------------------------------------------------------------------------- */
3635: return ( (height-1) + botrow ); /* note: descenders have botrow<0 */
3636: } /* --- end-of-function get_baseline() --- */
3637:
3638:
3639: /* ==========================================================================
3640: * Function: get_delim ( char *symbol, int height, int family )
3641: * Purpose: returns subraster corresponding to the samllest
3642: * character containing symbol, but at least as large as height,
3643: * and in caller's family (if specified).
3644: * If no symbol character as large as height is available,
3645: * then the largest availabale character is returned instead.
3646: * --------------------------------------------------------------------------
3647: * Arguments: symbol (I) char * containing (substring of) desired
3648: * symbol, e.g., if symbol="(", then any
3649: * mathchardef like "(" or "\\(", etc, match.
3650: * height (I) int containing minimum acceptable height
3651: * for returned character
3652: * family (I) int containing -1 to consider all families,
3653: * or, e.g., CMEX10 for only that family
3654: * --------------------------------------------------------------------------
3655: * Returns: ( subraster * ) best matching character available,
3656: * or NULL for any error
3657: * --------------------------------------------------------------------------
3658: * Notes: o If height is passed as negative, its absolute value is used
3659: * but the best-fit width is searched for (rather than height)
3660: * ======================================================================= */
3661: /* --- entry point --- */
3662: subraster *get_delim ( char *symbol, int height, int family )
3663: {
3664: /* -------------------------------------------------------------------------
3665: Allocations and Declarations
3666: -------------------------------------------------------------------------- */
3667: mathchardef *symdefs = symtable; /* table of mathchardefs */
3668: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
3669: subraster *make_delim(); /* construct delim if can't find it*/
3670: chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */
3671: char lcsymbol[256], *symptr, /* lowercase symbol for comparison */
3672: *unescsymbol = symbol; /* unescaped symbol */
3673: int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
3674: deflen = 0; /* length of symdef (aka lcsymbol) */
3675: int idef = 0, /* symdefs[] index */
3676: bestdef = (-9999), /* index of best fit symdef */
3677: bigdef = (-9999); /*index of biggest (in case no best)*/
3678: int size = 0, /* size index 0...LARGESTSIZE */
3679: bestsize = (-9999), /* index of best fit size */
3680: bigsize = (-9999); /*index of biggest (in case no best)*/
3681: int defheight, bestheight=9999, /* height of best fit symdef */
3682: bigheight = (-9999); /*height of biggest(in case no best)*/
3683: int iswidth = 0; /* true if best-fit width desired */
3684: int isunesc = 0, /* true if leading escape removed */
3685: issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */
3686: char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
3687: /* -------------------------------------------------------------------------
3688: determine if searching height or width, and search symdefs[] for best-fit
3689: -------------------------------------------------------------------------- */
3690: /* --- arg checks --- */
3691: if ( symlen < 1 ) return (sp); /* no input symbol suplied */
3692: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
3693: /* --- ignore leading escapes for CMEX10 --- */
3694: if ( 1 ) /* ignore leading escape */
3695: if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
3696: if ( strstr(symbol,"sq") != NULL ) /* \sq symbol requested */
3697: issq = 1; /* seq \sq signal */
3698: if ( strstr(symbol,"oint") != NULL ) /* \oint symbol requested */
3699: isoint = 1; /* seq \oint signal */
3700: if ( *symbol=='\\' ) /* have leading \ */
3701: { unescsymbol = symbol+1; /* push past leading \ */
3702: if ( --symlen < 1 ) return(sp); /* one less char */
3703: if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
3704: unescsymbol = bigint; /* but big version looks better */
3705: if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
3706: unescsymbol = bigoint; /* but big version looks better */
3707: symlen = strlen(unescsymbol); /* explicitly recalculate length */
3708: isunesc = 1; } /* signal leading escape removed */
3709: } /* --- end-of-if(family) --- */
3710: /* --- determine whether searching for best-fit height or width --- */
3711: if ( height < 0 ) /* negative signals width search */
3712: { height = (-height); /* flip "height" positive */
3713: iswidth = 1; } /* set flag for width search */
3714: /* --- search symdefs[] for best-fit height (or width) --- */
3715: for ( idef=0; ;idef++ ) /* until trailer record found */
3716: {
3717: char *defsym = symdefs[idef].symbol; /* local copies */
3718: int deffam = symdefs[idef].family;
3719: if ( defsym == NULL ) break; /* reached end-of-table */
3720: else /* check against caller's symbol */
3721: if ( family<0 || deffam == family /* if explicitly in caller's family*/
1.2 ! albertel 3722: || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
1.1 albertel 3723: {
3724: strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
3725: if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
3726: strcpy(lcsymbol,lcsymbol+1); /* so squeeze it out of lcsymbol too*/
3727: if ( 0 ) /* don't ignore case */
3728: for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
3729: if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
3730: deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
3731: if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
3732: if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
3733: && (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
3734: if ( (deffam == CMSY10 ? /* CMSY10 or not CMSY10 */
3735: symptr == lcsymbol /* caller's sym is a prefix */
3736: && deflen == symlen: /* and same length */
3737: symptr == lcsymbol /* caller's sym is a prefix */
3738: || symptr == lcsymbol+deflen-symlen) ) /* or a suffix */
3739: for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
3740: if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
3741: { defheight = gfdata->image.height; /* height of this character */
3742: if ( iswidth ) /* width search wanted instead... */
3743: defheight = gfdata->image.width; /* ...so substitute width */
3744: leftsymdef = &(symdefs[idef]); /* set symbol class, etc */
3745: if ( defheight>=height && defheight<bestheight ) /*new best fit*/
3746: { bestdef=idef; bestsize=size; /* save indexes of best fit */
3747: bestheight = defheight; } /* and save new best height */
3748: if ( defheight >= bigheight ) /* new biggest character */
3749: { bigdef=idef; bigsize=size; /* save indexes of biggest */
3750: bigheight = defheight; } /* and save new big height */
3751: } /* --- end-of-if(gfdata!=NULL) --- */
3752: } /* --- end-of-if(family) --- */
3753: } /* --- end-of-for(idef) --- */
3754: /* -------------------------------------------------------------------------
3755: construct subraster for best fit character, and return it to caller
3756: -------------------------------------------------------------------------- */
3757: if ( bestdef >= 0 ) /* found a best fit for caller */
3758: sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
1.2 ! albertel 3759: if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
! 3760: || bigdef < 0 ) /* delim not in font tables */
1.1 albertel 3761: sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
3762: if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
3763: sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
1.2 ! albertel 3764: if ( msgfp!=NULL && msglevel>=99 )
! 3765: fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
! 3766: (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
1.1 albertel 3767: return ( sp );
3768: } /* --- end-of-function get_delim() --- */
3769:
3770:
3771: /* ==========================================================================
3772: * Function: make_delim ( char *symbol, int height )
3773: * Purpose: constructs subraster corresponding to symbol
3774: * exactly as large as height,
3775: * --------------------------------------------------------------------------
3776: * Arguments: symbol (I) char * containing, e.g., if symbol="("
3777: * for desired delimiter
3778: * height (I) int containing height
3779: * for returned character
3780: * --------------------------------------------------------------------------
3781: * Returns: ( subraster * ) constructed delimiter
3782: * or NULL for any error
3783: * --------------------------------------------------------------------------
3784: * Notes: o If height is passed as negative, its absolute value is used
3785: * and interpreted as width (rather than height)
3786: * ======================================================================= */
3787: /* --- entry point --- */
3788: subraster *make_delim ( char *symbol, int height )
3789: {
3790: /* -------------------------------------------------------------------------
3791: Allocations and Declarations
3792: -------------------------------------------------------------------------- */
3793: subraster *sp = (subraster *)NULL, /* subraster returned to caller */
3794: *new_subraster(); /* allocate subraster */
1.2 ! albertel 3795: subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
! 3796: *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
! 3797: *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
! 3798: *rastack(), *rastcat(); /* stack pieces, concat filler */
! 3799: int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
1.1 albertel 3800: raster *rasp = (raster *)NULL; /* sp->image */
3801: int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
1.2 ! albertel 3802: int pixsz = 1, /* pixels are one bit each */
! 3803: symsize = 0; /* size arg for get_symsubraster() */
1.1 albertel 3804: int thickness = 1; /* drawn lines are one pixel thick */
3805: int aspectratio = 8; /* default height/width for parens */
3806: int iswidth = 0, /*true if width specified by height*/
3807: width = height; /* #pixels width (e.g., of ellipse)*/
1.2 ! albertel 3808: char *lp=NULL, *rp=NULL, /* check symbol for left or right */
! 3809: *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
! 3810: *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
! 3811: *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
1.1 albertel 3812: int circle_raster(), /* ellipse for ()'s in sp->image */
3813: rule_rsater(), /* horizontal or vertical lines */
3814: line_raster(); /* line between two points */
1.2 ! albertel 3815: subraster *uparrow_subraster(); /* up/down arrows */
! 3816: int isprealloc = 1; /*pre-alloc subraster, except arrow*/
! 3817: int oldsmashmargin = smashmargin, /* save original smashmargin */
! 3818: wascatspace = iscatspace; /* save original iscatspace */
1.1 albertel 3819: /* -------------------------------------------------------------------------
3820: initialization
3821: -------------------------------------------------------------------------- */
3822: /* --- determine whether constructing height or width --- */
3823: if ( height < 0 ) /* negative "height" signals width */
3824: { width = height = (-height); /* flip height positive */
3825: iswidth = 1; } /* set flag for width */
3826: if ( height < 3 ) goto end_of_job; /* too small, must be error */
3827: /* --- set default width (or height) accordingly --- */
3828: if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
3829: else width = (height+(aspectratio+1)/2)/aspectratio;
3830: if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
1.2 ! albertel 3831: || strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
! 3832: || strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
! 3833: width = max2(width,6); /* need space between two |'s */
1.1 albertel 3834: if ( width < 2 ) width=2; /* set min width */
3835: if ( strchr(symbol,'(') != NULL /* if left ( */
3836: || strchr(symbol,')') != NULL ) /* or right ) paren wanted */
1.2 ! albertel 3837: { width = (3*width)/2; /* adjust width */
! 3838: if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
! 3839: if ( strchr(symbol,'/') != NULL /* left / */
! 3840: || strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
! 3841: || strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
! 3842: width = max2(height/3,5);
! 3843: if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
! 3844: { width = min2(height/3,20); /* adjust width */
! 3845: isprealloc = 0; } /* don't preallocate subraster */
! 3846: if ( strchr(symbol,'{') != NULL /* if left { */
! 3847: || strchr(symbol,'}') != NULL ) /* or right } brace wanted */
! 3848: { isprealloc = 0; } /* don't preallocate */
1.1 albertel 3849: /* --- allocate and initialize subraster for constructed delimiter --- */
1.2 ! albertel 3850: if ( isprealloc ) /* pre-allocation wanted */
! 3851: { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
! 3852: == NULL ) goto end_of_job; /* quit if failed */
! 3853: /* --- initialize delimiter subraster parameters --- */
! 3854: sp->type = IMAGERASTER; /* image */
! 3855: sp->symdef = NULL; /* not applicable for image */
! 3856: sp->baseline = height/2 + 2; /* is a little above center good? */
! 3857: sp->size = NORMALSIZE; /* size (probably unneeded) */
! 3858: rasp = sp->image; } /* pointer to image in subraster */
1.1 albertel 3859: /* -------------------------------------------------------------------------
3860: ( ) parens
3861: -------------------------------------------------------------------------- */
3862: if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
3863: || (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
3864: {
1.2 ! albertel 3865: if ( isdrawparen ) { /* draw the paren */
! 3866: int mywidth = min2(width,20); /* max width for ()'s */
! 3867: circle_raster ( rasp, /* embedded raster image */
1.1 albertel 3868: 0, 0, /* row0,col0 are upper-left corner */
3869: height-1, mywidth-1, /* row1,col1 are lower-right */
3870: thickness, /* line thickness is 1 pixel */
3871: (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
1.2 ! albertel 3872: isokay = 1; } /* set flag */
! 3873: else {
! 3874: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
! 3875: char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
! 3876: *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
! 3877: *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
! 3878: int baseht=0, barht=0, /* height of base=top+bot, bar */
! 3879: ibar=0, nbars=0; /* bar index, #bars between top&bot*/
! 3880: int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
! 3881: topfill=(isleft?0:0), botfill=(isleft?0:0),
! 3882: barfill=(isleft?0:7); /* alignment fillers */
! 3883: /* --- get pieces at largest size smaller than total height --- */
! 3884: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
! 3885: {
! 3886: /* --- get pieces at current test size --- */
! 3887: isokay = 1; /* check for all pieces */
! 3888: if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
! 3889: if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
! 3890: if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
! 3891: /* --- check sum of pieces against total desired height --- */
! 3892: if ( isokay ) { /* all pieces retrieved */
! 3893: baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
! 3894: barht = (symbar->image)->height; /* bar height */
! 3895: if ( baseht < height+5 ) break; /* largest base that's not too big */
! 3896: if ( symsize < 1 ) break; /* or smallest available base */
! 3897: } /* --- end-of-if(isokay) --- */
! 3898: /* --- free test pieces that were too big --- */
! 3899: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
! 3900: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
! 3901: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
! 3902: isokay = 0; /* nothing available */
! 3903: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
! 3904: } /* --- end-of-for(symsize) --- */
! 3905: /* --- construct brace from pieces --- */
! 3906: if ( isokay ) { /* we have the pieces */
! 3907: /* --- add alignment fillers --- */
! 3908: smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
! 3909: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
! 3910: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
! 3911: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
! 3912: smashmargin = oldsmashmargin; /* reset smashmargin */
! 3913: iscatspace = wascatspace; /* reset iscatspace */
! 3914: /* --- #bars needed between top and bot --- */
! 3915: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
! 3916: /* --- stack pieces --- */
! 3917: sp = topsym; /* start with top piece */
! 3918: if ( nbars > 0 ) /* need nbars between top and bot */
! 3919: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
! 3920: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
! 3921: delete_subraster(barsym); /* barsym no longer needed */
! 3922: } /* --- end-of-if(isokay) --- */
! 3923: } /* --- end-of-if/else(isdrawparen) --- */
1.1 albertel 3924: } /* --- end-of-if(left- or right-() paren wanted) --- */
3925: /* -------------------------------------------------------------------------
1.2 ! albertel 3926: { } braces
! 3927: -------------------------------------------------------------------------- */
! 3928: else
! 3929: if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
! 3930: || (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
! 3931: {
! 3932: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
! 3933: char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
! 3934: *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
! 3935: *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
! 3936: *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
! 3937: int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
! 3938: ibar=0, nbars=0; /* bar index, #bars above,below mid*/
! 3939: int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
! 3940: topfill=(isleft?4:0), botfill=(isleft?4:0),
! 3941: midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
! 3942: /* --- get pieces at largest size smaller than total height --- */
! 3943: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
! 3944: {
! 3945: /* --- get pieces at current test size --- */
! 3946: isokay = 1; /* check for all pieces */
! 3947: if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
! 3948: if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
! 3949: if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
! 3950: if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
! 3951: /* --- check sum of pieces against total desired height --- */
! 3952: if ( isokay ) { /* all pieces retrieved */
! 3953: baseht = (symtop->image)->height + (symbot->image)->height
! 3954: + (symmid->image)->height; /* top+bot+mid height */
! 3955: barht = (symbar->image)->height; /* bar height */
! 3956: if ( baseht < height+5 ) break; /* largest base that's not too big */
! 3957: if ( symsize < 1 ) break; /* or smallest available base */
! 3958: } /* --- end-of-if(isokay) --- */
! 3959: /* --- free test pieces that were too big --- */
! 3960: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
! 3961: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
! 3962: if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
! 3963: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
! 3964: isokay = 0; /* nothing available */
! 3965: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
! 3966: } /* --- end-of-for(symsize) --- */
! 3967: /* --- construct brace from pieces --- */
! 3968: if ( isokay ) { /* we have the pieces */
! 3969: /* --- add alignment fillers --- */
! 3970: smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
! 3971: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
! 3972: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
! 3973: midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
! 3974: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
! 3975: smashmargin = oldsmashmargin; /* reset smashmargin */
! 3976: iscatspace = wascatspace; /* reset iscatspace */
! 3977: /* --- #bars needed on each side of mid piece --- */
! 3978: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
! 3979: /* --- stack pieces --- */
! 3980: sp = topsym; /* start with top piece */
! 3981: if ( nbars > 0 ) /* need nbars above middle */
! 3982: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
! 3983: sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
! 3984: if ( nbars > 0 ) /* need nbars below middle */
! 3985: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
! 3986: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
! 3987: delete_subraster(barsym); /* barsym no longer needed */
! 3988: } /* --- end-of-if(isokay) --- */
! 3989: } /* --- end-of-if(left- or right-{} brace wanted) --- */
! 3990: /* -------------------------------------------------------------------------
1.1 albertel 3991: [ ] brackets
3992: -------------------------------------------------------------------------- */
3993: else
3994: if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
1.2 ! albertel 3995: || (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
! 3996: || (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
! 3997: || (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
! 3998: || (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
! 3999: || (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
! 4000: || (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
! 4001: || (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
! 4002: {
! 4003: /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
! 4004: int mywidth = min2(width,12), /* max width for horizontal bars */
! 4005: wthick = 1; /* thickness of top.bottom bars */
! 4006: thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
! 4007: if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
! 4008: wthick = thickness; /* same thickness for top/bot bar */
! 4009: if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
! 4010: rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
! 4011: if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
! 4012: rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
! 4013: if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
1.1 albertel 4014: rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
1.2 ! albertel 4015: if ( lp4 != NULL ) /* 2nd left vertical bar needed */
! 4016: rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
! 4017: if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
1.1 albertel 4018: rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
1.2 ! albertel 4019: if ( rp4 != NULL ) /* 2nd right vertical bar needed */
! 4020: rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
1.1 albertel 4021: isokay = 1; /* set flag */
4022: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
4023: /* -------------------------------------------------------------------------
4024: < > brackets
4025: -------------------------------------------------------------------------- */
4026: else
4027: if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
4028: || (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
4029: {
1.2 ! albertel 4030: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
! 4031: int mywidth = min2(width,12), /* max width for brackets */
! 4032: mythick = 1; /* all lines one pixel thick */
! 4033: thickness = (height<25?1:2); /* set line pixel thickness */
1.1 albertel 4034: if ( lp != NULL ) /* left < bracket wanted */
1.2 ! albertel 4035: { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
! 4036: if ( thickness>1 )
! 4037: line_raster(rasp,height/2,1,0,mywidth-1,mythick);
! 4038: line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
! 4039: if ( thickness>1 )
! 4040: line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
1.1 albertel 4041: if ( rp != NULL ) /* right > bracket wanted */
1.2 ! albertel 4042: { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
! 4043: if ( thickness>1 )
! 4044: line_raster(rasp,height/2,mywidth-2,0,0,mythick);
! 4045: line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
! 4046: if ( thickness>1 )
! 4047: line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
1.1 albertel 4048: isokay = 1; /* set flag */
4049: } /* --- end-of-if(left- or right-<> bracket wanted) --- */
4050: /* -------------------------------------------------------------------------
1.2 ! albertel 4051: / \ delimiters
! 4052: -------------------------------------------------------------------------- */
! 4053: else
! 4054: if ( (lp=strchr(symbol,'/')) != NULL /* left / wanted */
! 4055: || (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
! 4056: || (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
! 4057: {
! 4058: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
! 4059: int mywidth = width; /* max width for / \ */
! 4060: thickness = 1; /* set line pixel thickness */
! 4061: if ( lp != NULL ) /* left / wanted */
! 4062: line_raster(rasp,0,mywidth-1,height-1,0,thickness);
! 4063: if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
! 4064: line_raster(rasp,0,0,height-1,mywidth-1,thickness);
! 4065: isokay = 1; /* set flag */
! 4066: } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
! 4067: /* -------------------------------------------------------------------------
! 4068: arrow delimiters
! 4069: -------------------------------------------------------------------------- */
! 4070: else
! 4071: if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
! 4072: {
! 4073: /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
! 4074: int mywidth = width; /* max width for / \ */
! 4075: int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
! 4076: || strstr(symbol,"Down")!=NULL); /* or a Down */
! 4077: int drctn = +1; /* init for uparrow */
! 4078: if ( strstr(symbol,"down")!=NULL /* down if we have down */
! 4079: || strstr(symbol,"Down")!=NULL ) /* or Down */
! 4080: { drctn = (-1); /* reset direction to down */
! 4081: if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
! 4082: || strstr(symbol,"Up")!=NULL ) /* and down or Down */
! 4083: drctn = 0; } /* reset direction to updown */
! 4084: sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
! 4085: if ( sp != NULL )
! 4086: { sp->type = IMAGERASTER; /* image */
! 4087: sp->symdef = NULL; /* not applicable for image */
! 4088: sp->baseline = height/2 + 2; /* is a little above center good? */
! 4089: sp->size = NORMALSIZE; /* size (probably unneeded) */
! 4090: isokay = 1; } /* set flag */
! 4091: } /* --- end-of-if(arrow delimiter wanted) --- */
! 4092: /* -------------------------------------------------------------------------
1.1 albertel 4093: \- for | | brackets or \= for || || brackets
4094: -------------------------------------------------------------------------- */
4095: else
4096: if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */
4097: || (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */
4098: || (rp=strchr(symbol,'=')) != NULL /* left or right || bracket wanted */
4099: || (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
4100: {
4101: /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
4102: int midcol = width/2; /* middle col, left of mid if even */
4103: if ( rp != NULL /* left or right || bracket wanted */
4104: || rp2 != NULL ) /* or || in standard tex notation */
1.2 ! albertel 4105: { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
1.1 albertel 4106: rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
4107: rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
4108: else /*nb, lp2 spuriously set if rp2 set*/
4109: if ( lp != NULL /* left or right | bracket wanted */
4110: || lp2 != NULL ) /* ditto for synomym */
1.2 ! albertel 4111: { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
1.1 albertel 4112: rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
4113: isokay = 1; /* set flag */
4114: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
4115: /* -------------------------------------------------------------------------
4116: back to caller
4117: -------------------------------------------------------------------------- */
4118: end_of_job:
1.2 ! albertel 4119: if ( msgfp!=NULL && msglevel>=99 )
! 4120: fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
! 4121: (symbol==NULL?"null":symbol),isokay);
1.1 albertel 4122: if ( !isokay ) /* don't have requested delimiter */
1.2 ! albertel 4123: { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
1.1 albertel 4124: sp = NULL; } /* and signal error to caller */
4125: return ( sp ); /*back to caller with delim or NULL*/
4126: } /* --- end-of-function make_delim() --- */
4127:
4128:
4129: /* ==========================================================================
4130: * Function: texchar ( expression, chartoken )
4131: * Purpose: scans expression, returning either its first character,
4132: * or the next \sequence if that first char is \,
4133: * and a pointer to the first expression char past that.
4134: * --------------------------------------------------------------------------
4135: * Arguments: expression (I) char * to first char of null-terminated
4136: * string containing valid LaTeX expression
4137: * to be scanned
4138: * chartoken (O) char * to null-terminated string returning
4139: * either the first (non-whitespace) character
4140: * of expression if that char isn't \, or else
4141: * the \ and everything following it up to
4142: * the next non-alphabetic character (but at
4143: * least one char following the \ even if
4144: * it's non-alpha)
4145: * --------------------------------------------------------------------------
4146: * Returns: ( char * ) ptr to the first char of expression
4147: * past returned chartoken,
4148: * or NULL for any parsing error.
4149: * --------------------------------------------------------------------------
4150: * Notes: o Does *not* skip leading whitespace, but simply
4151: * returns any whitespace character as the next character.
4152: * ======================================================================= */
4153: /* --- entry point --- */
4154: char *texchar ( char *expression, char *chartoken )
4155: {
4156: /* -------------------------------------------------------------------------
4157: Allocations and Declarations
4158: -------------------------------------------------------------------------- */
4159: int esclen = 0, /*length of escape sequence*/
4160: maxesclen = 128; /* max len of esc sequence */
4161: char *ptoken = chartoken; /* ptr into chartoken */
4162: int iprefix = 0; /* prefix index */
4163: static char *prefixes[] = /*e.g., \big followed by ( */
4164: { /* "\\left", "\\right", */
4165: "\\big", "\\Big", "\\bigg", "\\Bigg",
4166: "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
4167: "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
4168: /* -------------------------------------------------------------------------
4169: just return the next char if it's not \
4170: -------------------------------------------------------------------------- */
4171: /* --- error check for end-of-string --- */
4172: *ptoken = '\000'; /* init in case of error */
4173: if ( expression == NULL ) return(NULL); /* nothing to scan */
4174: if ( *expression == '\000' ) return(NULL); /* nothing to scan */
4175: /* --- always returning first character (either \ or some other char) --- */
4176: *ptoken++ = *expression++; /* here's first character */
4177: /* --- if first char isn't \, then just return it to caller --- */
4178: if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */
4179: { *ptoken = '\000'; /* add a null terminator */
4180: goto end_of_job; } /* ptr past returned char */
4181: if ( *expression == '\000' ) /* \ is very last char */
4182: { *chartoken = '\000'; /* flush bad trailing \ */
4183: return(NULL); } /* and signal end-of-job */
4184: /* -------------------------------------------------------------------------
4185: we have an escape sequence, so return all alpha chars following \
4186: -------------------------------------------------------------------------- */
4187: /* --- accumulate chars until first non-alpha char found --- */
4188: for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */
4189: { if ( esclen < maxesclen-3 ) /* more room in chartoken */
4190: *ptoken++ = *expression; /*copy alpha char, bump ptr*/
4191: expression++; } /* bump expression ptr */
4192: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
4193: *ptoken = '\000'; /* set null for compare */
4194: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
4195: if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
4196: { char nextchar[256]; int nextlen=0; /* texchar after prefix */
4197: skipwhite(expression); /* skip space after prefix*/
4198: expression = texchar(expression,nextchar); /* get nextchar */
4199: if ( (nextlen = strlen(nextchar)) > 0 ) /* #chars in nextchar */
4200: { strcpy(ptoken,nextchar); /* append nextchar */
4201: ptoken += strlen(nextchar); /* point to null terminator*/
4202: esclen += strlen(nextchar); } /* and bump escape length */
4203: break; } /* stop checking prefixes */
4204: /* --- every \ must be followed by at least one char, e.g., \[ --- */
4205: if ( esclen < 1 ) /* \ followed by non-alpha */
4206: *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
4207: else { /* normal alpha \sequence */
4208: /* --- respect spaces in text mode, except first space after \escape --- */
1.2 ! albertel 4209: if ( istextmode ) /* in \rm or \it text mode */
! 4210: if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
! 4211: expression++; } /* so flush delim */
1.1 albertel 4212: *ptoken = '\000'; /* null-terminate token */
4213: /* --- back to caller --- */
4214: end_of_job:
4215: if ( msgfp!=NULL && msglevel>=999 )
4216: { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
4217: fflush(msgfp); }
4218: return ( expression ); /*ptr to 1st non-alpha char*/
4219: } /* --- end-of-function texchar() --- */
4220:
4221:
4222: /* ==========================================================================
4223: * Function: texsubexpr (expression,subexpr,maxsubsz,
4224: * left,right,isescape,isdelim)
4225: * Purpose: scans expression, returning everything between a balanced
4226: * left{...right} subexpression if the first non-whitespace
4227: * char of expression is an (escaped or unescaped) left{,
4228: * or just the next texchar() otherwise,
4229: * and a pointer to the first expression char past that.
4230: * --------------------------------------------------------------------------
4231: * Arguments: expression (I) char * to first char of null-terminated
4232: * string containing valid LaTeX expression
4233: * to be scanned
4234: * subexpr (O) char * to null-terminated string returning
4235: * either everything between a balanced {...}
4236: * subexpression if the first char is {,
4237: * or the next texchar() otherwise.
4238: * maxsubsz (I) int containing max #bytes returned
4239: * in subexpr buffer (0 means unlimited)
4240: * left (I) char * specifying allowable left delimiters
4241: * that begin subexpression, e.g., "{[(<"
4242: * right (I) char * specifying matching right delimiters
4243: * in the same order as left, e.g., "}])>"
4244: * isescape (I) int controlling whether escaped and/or
4245: * unescaped left,right are matched;
4246: * see isbrace() comments below for details.
4247: * isdelim (I) int containing true (non-zero) to return
4248: * the leading left and trailing right delims
4249: * (if any were found) along with subexpr,
4250: * or containing false=0 to return subexpr
4251: * without its delimiters
4252: * --------------------------------------------------------------------------
4253: * Returns: ( char * ) ptr to the first char of expression
4254: * past returned subexpr (see Notes),
4255: * or NULL for any parsing error.
4256: * --------------------------------------------------------------------------
4257: * Notes: o If subexpr is of the form left{...right},
4258: * the outer {}'s are returned as part of subexpr
4259: * if isdelim is true; if isdelim is false the {}'s aren't
4260: * returned. In either case the returned pointer is
4261: * *always* bumped past the closing right}, even if
4262: * that closing right} isn't returned in subexpr.
4263: * o If subexpr is not of the form left{...right},
4264: * the returned pointer is on the character immediately
4265: * following the last character returned in subexpr
4266: * o \. acts as LaTeX \right. and matches any \left(
4267: * And it also acts as a LaTeX \left. and matches any \right)
4268: * ======================================================================= */
4269: /* --- entry point --- */
4270: char *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
4271: char *left, char *right, int isescape, int isdelim )
4272: {
4273: /* -------------------------------------------------------------------------
4274: Allocations and Declarations
4275: -------------------------------------------------------------------------- */
4276: char *texchar(); /*next char (or \sequence) from expression*/
1.2 ! albertel 4277: char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
! 4278: rightdelim[256] = ")\000"; /* and matching right) */
1.1 albertel 4279: char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
1.2 ! albertel 4280: char *strtexchr(), *texleft(); /* check for \left, and get it */
1.1 albertel 4281: int gotescape = 0, /* true if leading char of expression is \ */
4282: prevescape = 0; /* while parsing, true if preceding char \ */
4283: int isbrace(); /* check for left,right braces */
4284: int isanyright = 1; /* true matches any right with left, (...] */
4285: int isleftdot = 0; /* true if left brace is a \. */
4286: int nestlevel = 1; /* current # of nested braces */
4287: int subsz=0 /*, maxsubsz=8192*/; /* #chars in returned subexpr[] buffer*/
4288: /* -------------------------------------------------------------------------
4289: skip leading whitespace and just return the next char if it's not {
4290: -------------------------------------------------------------------------- */
4291: /* --- skip leading whitespace and error check for end-of-string --- */
4292: *subexpr = '\000'; /* init in case of error */
4293: if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/
4294: skipwhite(expression); /* leading whitespace gone */
4295: if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
4296: /* --- set maxsubsz --- */
4297: if ( maxsubsz < 1 ) maxsubsz = 8192; /* input 0 means unlimited */
4298: /* --- check for escape --- */
4299: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
4300: gotescape = 1; /* so set flag accordingly */
1.2 ! albertel 4301: /* --- check for \left...\right --- */
! 4302: if ( gotescape ) /* begins with \ */
! 4303: if ( memcmp(expression+1,"left",4) ) /* and followed by left */
! 4304: if ( strchr(left,'l') != NULL ) /* caller wants \left's */
! 4305: if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
! 4306: { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
! 4307: (isdelim?NULL:leftdelim),rightdelim);
! 4308: if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
! 4309: return ( pright ); /*back to caller past \right*/
! 4310: } /* --- end-of-if(expression=="\\left") --- */
1.1 albertel 4311: /* --- if first char isn't left{ or script, just return it to caller --- */
4312: if ( !isbrace(expression,left,isescape) ) /* not a left{ */
4313: if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
4314: return ( texchar(expression,subexpr) ); /* next char to caller */
4315: else /* --- kludge for super/subscripts to accommodate texscripts() --- */
4316: { *subexpr++ = *expression; /* signal script */
4317: *subexpr = '\000'; /* null-terminate subexpr */
4318: return ( expression ); } /* leave script in stream */
4319: /* --- extract left and find matching right delimiter --- */
4320: *leftdelim = *(expression+gotescape); /* the left( in expression */
4321: if ( (gotescape && *leftdelim == '.') /* we have a left \. */
4322: || (gotescape && isanyright) ) /*or are matching any right*/
4323: { isleftdot = 1; /* so just set flag */
4324: *leftdelim = '\000'; } /* and reset leftdelim */
4325: else /* find matching \right */
4326: if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
4327: *rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */
4328: else /* can't happen -- pgm bug */
4329: return ( NULL ); /*just signal eoj to caller*/
4330: /* -------------------------------------------------------------------------
4331: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
4332: -------------------------------------------------------------------------- */
4333: /* --- first initialize by bumping past left{ or \{ --- */
4334: if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/
4335: else expression++; /* always bump past left{ */
4336: if ( gotescape ) /*need to bump another char*/
4337: if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
4338: else expression++; /* else just bump past it */
4339: /* --- set maximum size for numerical arguments --- */
4340: if ( 0 ) /* check turned on or off? */
4341: if ( !isescape && !isdelim ) /*looking for numerical arg*/
4342: maxsubsz = 96; /* set max arg size */
4343: /* --- search for matching right} --- */
4344: while ( 1 ) /*until balanced right} */
4345: {
4346: /* --- error check for end-of-string --- */
4347: if ( *expression == '\000' ) /* premature end-of-string */
4348: { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/
4349: { expression = origexpression; /* so end-of-string is error*/
4350: subexpr = origsubexpr; } /* so reset all ptrs */
4351: if ( isdelim ) /* generate fake right */
4352: if ( gotescape ) /* need escaped right */
4353: { *subexpr++ = '\\'; /* set escape char */
4354: *subexpr++ = '.'; } /* and fake \right. */
4355: else /* escape not wanted */
4356: *subexpr++ = *rightdelim; /* so fake actual right */
4357: *subexpr = '\000'; /* null-terminate subexpr */
4358: return ( expression ); } /* back with final token */
4359: /* --- check preceding char for escape --- */
4360: if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */
4361: prevescape = 1-prevescape; /* so flip escape flag */
4362: else prevescape = 0; /* or turn flag off */
4363: /* --- check for { and } (un/escaped as per leading left) --- */
4364: if ( gotescape == prevescape ) /* escaped iff leading is */
4365: { /* --- check for (closing) right delim and see if we're done --- */
4366: if ( isthischar(*expression,rightdelim) /* found a right} */
4367: || (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
4368: || (prevescape && isthischar(*expression,".")) ) /*or found \right. */
4369: if ( --nestlevel < 1 ) /*\right balances 1st \left*/
4370: { if ( isdelim ) /*caller wants } in subexpr*/
4371: *subexpr++ = *expression; /* so end subexpr with } */
4372: else /*check for \ before right}*/
4373: if ( prevescape ) /* have unwanted \ */
4374: *(subexpr-1) = '\000'; /* so replace it with null */
4375: *subexpr = '\000'; /* null-terminate subexpr */
4376: return ( expression+1 ); } /* back with char after } */
4377: /* --- check for (another) left{ --- */
4378: if ( isthischar(*expression,leftdelim) /* found another left{ */
4379: || (isleftdot && isthischar(*expression,left)) ) /* any left{ */
4380: nestlevel++;
4381: } /* --- end-of-if(gotescape==prevescape) --- */
4382: /* --- not done, so copy char to subexpr and continue with next char --- */
4383: if ( ++subsz < maxsubsz-5 ) /* more room in subexpr */
4384: *subexpr++ = *expression; /* so copy char and bump ptr*/
4385: expression++; /* bump expression ptr */
4386: } /* --- end-of-while(1) --- */
4387: } /* --- end-of-function texsubexpr() --- */
4388:
4389:
4390: /* ==========================================================================
1.2 ! albertel 4391: * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
! 4392: * Purpose: scans expression, starting after opening \left,
! 4393: * and returning ptr after matching closing \right.
! 4394: * Everything between is returned in subexpr, if given.
! 4395: * Likewise, if given, ldelim returns delimiter after \left
! 4396: * and rdelim returns delimiter after \right.
! 4397: * If ldelim is given, the returned subexpr doesn't include it.
! 4398: * If rdelim is given, the returned pointer is after that delim.
! 4399: * --------------------------------------------------------------------------
! 4400: * Arguments: expression (I) char * to first char of null-terminated
! 4401: * string immediately following opening \left
! 4402: * subexpr (O) char * to null-terminated string returning
! 4403: * either everything between balanced
! 4404: * \left ... \right. If leftdelim given,
! 4405: * subexpr does _not_ contain that delimiter.
! 4406: * maxsubsz (I) int containing max #bytes returned
! 4407: * in subexpr buffer (0 means unlimited)
! 4408: * ldelim (O) char * returning delimiter following
! 4409: * opening \left
! 4410: * rdelim (O) char * returning delimiter following
! 4411: * closing \right
! 4412: * --------------------------------------------------------------------------
! 4413: * Returns: ( char * ) ptr to the first char of expression
! 4414: * past closing \right, or past closing
! 4415: * right delimiter if rdelim!=NULL,
! 4416: * or NULL for any error.
! 4417: * --------------------------------------------------------------------------
! 4418: * Notes: o
! 4419: * ======================================================================= */
! 4420: /* --- entry point --- */
! 4421: char *texleft ( char *expression, char *subexpr, int maxsubsz,
! 4422: char *ldelim, char *rdelim )
! 4423: {
! 4424: /* -------------------------------------------------------------------------
! 4425: Allocations and Declarations
! 4426: -------------------------------------------------------------------------- */
! 4427: char *texchar(), /* get delims after \left,\right */
! 4428: *strtexchr(), *pright=expression; /* locate matching \right */
! 4429: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
! 4430: int sublen = 0; /* #chars between \left...\right */
! 4431: /* -------------------------------------------------------------------------
! 4432: initialization
! 4433: -------------------------------------------------------------------------- */
! 4434: /* --- init output --- */
! 4435: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
! 4436: if ( ldelim != NULL ) *ldelim = '\000'; /* init ldelim, if given */
! 4437: if ( rdelim != NULL ) *rdelim = '\000'; /* init rdelim, if given */
! 4438: /* --- check args --- */
! 4439: if ( expression == NULL ) goto end_of_job; /* no input supplied */
! 4440: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
! 4441: /* --- determine left delimiter --- */
! 4442: if ( ldelim != NULL ) /* caller wants left delim */
! 4443: { skipwhite(expression); /* interpret \left ( as \left( */
! 4444: expression = texchar(expression,ldelim); } /*delim from expression*/
! 4445: /* -------------------------------------------------------------------------
! 4446: locate \right balancing opening \left
! 4447: -------------------------------------------------------------------------- */
! 4448: /* --- first \right following \left --- */
! 4449: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
! 4450: != NULL ) { /* found it */
! 4451: /* --- find matching \right by pushing past any nested \left's --- */
! 4452: char *pleft = expression; /* start after first \left( */
! 4453: while ( 1 ) { /*break when matching \right found*/
! 4454: /* -- locate next nested \left if there is one --- */
! 4455: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
! 4456: == NULL ) break; /*no more, so matching \right found*/
! 4457: pleft += strlen(left); /* push ptr past \left token */
! 4458: if ( pleft >= pright ) break; /* not nested if \left after \right*/
! 4459: /* --- have nested \left, so push forward to next \right --- */
! 4460: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
! 4461: == NULL ) break; /* ran out of \right's */
! 4462: } /* --- end-of-while(1) --- */
! 4463: } /* --- end-of-if(pright!=NULL) --- */
! 4464: /* --- set subexpression length, push pright past \right --- */
! 4465: if ( pright != (char *)NULL ) /* found matching \right */
! 4466: { sublen = (int)(pright-expression); /* #chars between \left...\right */
! 4467: pright += strlen(right); } /* so push pright past \right */
! 4468: /* -------------------------------------------------------------------------
! 4469: get rightdelim and subexpr between \left...\right
! 4470: -------------------------------------------------------------------------- */
! 4471: /* --- get delimiter following \right --- */
! 4472: if ( rdelim != NULL ) /* caller wants right delim */
! 4473: if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
! 4474: { strcpy(rdelim,"."); /* set default \right. */
! 4475: sublen = strlen(expression); /* use entire remaining expression */
! 4476: pright = expression + sublen; } /* and push pright to end-of-string*/
! 4477: else /* have explicit matching \right */
! 4478: { skipwhite(pright); /* interpret \right ) as \right) */
! 4479: pright = texchar(pright,rdelim); /* pull delim from expression */
! 4480: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* or set \right. */
! 4481: /* --- get subexpression between \left...\right --- */
! 4482: if ( sublen > 0 ) /* have subexpr */
! 4483: if ( subexpr != NULL ) { /* and caller wants it */
! 4484: if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
! 4485: memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
! 4486: subexpr[sublen] = '\000'; } /* null-terminate subexpr */
! 4487: end_of_job:
! 4488: if ( msglevel>=99 && msgfp!=NULL )
! 4489: { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
! 4490: (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
! 4491: (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
! 4492: return ( pright );
! 4493: } /* --- end-of-function texleft --- */
! 4494:
! 4495:
! 4496: /* ==========================================================================
1.1 albertel 4497: * Function: texscripts ( expression, subscript, superscript, which )
4498: * Purpose: scans expression, returning subscript and/or superscript
4499: * if expression is of the form _x^y or ^{x}_{y},
4500: * or any (valid LaTeX) permutation of the above,
4501: * and a pointer to the first expression char past "scripts"
4502: * --------------------------------------------------------------------------
4503: * Arguments: expression (I) char * to first char of null-terminated
4504: * string containing valid LaTeX expression
4505: * to be scanned
4506: * subscript (O) char * to null-terminated string returning
4507: * subscript (without _), if found, or "\000"
4508: * superscript (O) char * to null-terminated string returning
4509: * superscript (without ^), if found, or "\000"
4510: * which (I) int containing 1 for subscript only,
4511: * 2 for superscript only, >=3 for either/both
4512: * --------------------------------------------------------------------------
4513: * Returns: ( char * ) ptr to the first char of expression
4514: * past returned "scripts" (unchanged
4515: * except for skipped whitespace if
4516: * neither subscript nor superscript found),
4517: * or NULL for any parsing error.
4518: * --------------------------------------------------------------------------
4519: * Notes: o an input expression like ^a^b_c will return superscript="b",
4520: * i.e., totally ignoring all but the last "script" encountered
4521: * ======================================================================= */
4522: /* --- entry point --- */
4523: char *texscripts ( char *expression, char *subscript,
4524: char *superscript, int which )
4525: {
4526: /* -------------------------------------------------------------------------
4527: Allocations and Declarations
4528: -------------------------------------------------------------------------- */
4529: char *texsubexpr(); /* next subexpression from expression */
4530: int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
4531: /* -------------------------------------------------------------------------
1.2 ! albertel 4532: init "scripts"
1.1 albertel 4533: -------------------------------------------------------------------------- */
1.2 ! albertel 4534: if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
! 4535: if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
1.1 albertel 4536: /* -------------------------------------------------------------------------
4537: get subscript and/or superscript from expression
4538: -------------------------------------------------------------------------- */
1.2 ! albertel 4539: while ( expression != NULL ) {
! 4540: skipwhite(expression); /* leading whitespace gone */
! 4541: if ( *expression == '\000' ) return(expression); /* nothing left to scan */
1.1 albertel 4542: if ( isthischar(*expression,SUBSCRIPT) /* found _ */
4543: && (which==1 || which>2 ) ) /* and caller wants it */
4544: { if ( gotsub /* found 2nd subscript */
4545: || subscript == NULL ) break; /* or no subscript buffer */
4546: gotsub = 1; /* set subscript flag */
4547: expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
4548: else /* no _, check for ^ */
4549: if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */
4550: && which>=2 ) /* and caller wants it */
4551: { if ( gotsup /* found 2nd superscript */
4552: || superscript == NULL ) break; /* or no superscript buffer*/
4553: gotsup = 1; /* set superscript flag */
4554: expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
4555: else /* neither _ nor ^ */
4556: return ( expression ); /*return ptr past "scripts"*/
1.2 ! albertel 4557: } /* --- end-of-while(expression!=NULL) --- */
1.1 albertel 4558: return ( expression );
4559: } /* --- end-of-function texscripts() --- */
4560:
4561:
4562: /* ==========================================================================
4563: * Function: isbrace ( expression, braces, isescape )
4564: * Purpose: checks leading char(s) of expression for a brace,
4565: * either escaped or unescaped depending on isescape,
4566: * except that { and } are always matched, if they're
4567: * in braces, regardless of isescape.
4568: * --------------------------------------------------------------------------
4569: * Arguments: expression (I) char * to first char of null-terminated
4570: * string containing a valid LaTeX expression
4571: * whose leading char(s) are checked for braces
4572: * that begin subexpression, e.g., "{[(<"
4573: * braces (I) char * specifying matching brace delimiters
4574: * to be checked for, e.g., "{[(<" or "}])>"
4575: * isescape (I) int containing 0 to match only unescaped
4576: * braces, e.g., (...) or {...}, etc,
4577: * or containing 1 to match only escaped
4578: * braces, e.g., \(...\) or \[...\], etc,
4579: * or containing 2 to match either.
4580: * But note: if {,} are in braces
4581: * then they're *always* matched whether
4582: * escaped or not, regardless of isescape.
4583: * --------------------------------------------------------------------------
4584: * Returns: ( int ) 1 if the leading char(s) of expression
4585: * is a brace, or 0 if not.
4586: * --------------------------------------------------------------------------
4587: * Notes: o
4588: * ======================================================================= */
4589: /* --- entry point --- */
4590: int isbrace ( char *expression, char *braces, int isescape )
4591: {
4592: /* -------------------------------------------------------------------------
4593: Allocations and Declarations
4594: -------------------------------------------------------------------------- */
4595: int gotescape = 0, /* true if leading char is an escape */
4596: gotbrace = 0; /*true if first non-escape char is a brace*/
4597: /* -------------------------------------------------------------------------
4598: check for brace
4599: -------------------------------------------------------------------------- */
4600: /* --- first check for end-of-string --- */
4601: if ( *expression == '\000' ) return(0); /* nothing to check */
4602: /* --- check leading char for escape --- */
4603: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
4604: { gotescape = 1; /* so set flag accordingly */
4605: expression++; } /* and bump past escape */
4606: /* --- check (maybe next char) for brace --- */
4607: if ( isthischar(*expression,braces) ) /* expression is braced */
4608: gotbrace = 1; /* so set flag accordingly */
4609: if ( gotescape && *expression == '.' ) /* \. matches any brace */
4610: gotbrace = 1; /* set flag */
4611: /* --- check for TeX brace { or } --- */
4612: if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
4613: if ( isescape ) isescape = 2; /* reset escape flag */
4614: /* -------------------------------------------------------------------------
4615: back to caller
4616: -------------------------------------------------------------------------- */
4617: if ( gotbrace && /* found a brace */
4618: ( isescape==2 || /* escape irrelevant */
4619: gotescape==isescape ) /* un/escaped as requested */
4620: ) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */
4621: } /* --- end-of-function isbrace() --- */
4622:
4623:
4624: /* ==========================================================================
4625: * Function: preamble ( expression, size, subexpr )
4626: * Purpose: parses $-terminated preamble, if present, at beginning
4627: * of expression, re-setting size if necessary, and
4628: * returning any other parameters besides size in subexpr.
4629: * --------------------------------------------------------------------------
4630: * Arguments: expression (I) char * to first char of null-terminated
4631: * string containing LaTeX expression possibly
4632: * preceded by $-terminated preamble
4633: * size (I/O) int * containing 0-4 default font size,
4634: * and returning size modified by first
4635: * preamble parameter (or unchanged)
4636: * subexpr(O) char * returning any remaining preamble
4637: * parameters past size
4638: * --------------------------------------------------------------------------
4639: * Returns: ( char * ) ptr to first char past preamble in expression
4640: * or NULL for any parsing error.
4641: * --------------------------------------------------------------------------
4642: * Notes: o size can be any number >=0. If preceded by + or -, it's
4643: * interpreted as an increment to input size; otherwise
4644: * it's interpreted as the size.
4645: * o if subexpr is passed as NULL ptr, then returned expression
4646: * ptr will have "flushed" and preamble parameters after size
4647: * ======================================================================= */
4648: /* --- entry point --- */
4649: char *preamble ( char *expression, int *size, char *subexpr )
4650: {
4651: /* -------------------------------------------------------------------------
4652: Allocations and Declarations
4653: -------------------------------------------------------------------------- */
4654: char pretext[512], *prep=expression, /*pream from expression, ptr into it*/
4655: *dollar, *comma; /* preamble delimiters */
4656: int prelen = 0, /* preamble length */
4657: sizevalue = 0, /* value of size parameter */
4658: isfontsize = 0, /*true if leading fontsize present*/
4659: isdelta = 0; /*true to increment passed size arg*/
4660: /* -------------------------------------------------------------------------
4661: initialization
4662: -------------------------------------------------------------------------- */
4663: if ( subexpr != NULL ) /* caller passed us an address */
4664: *subexpr = '\000'; /* so init assuming no preamble */
4665: if ( expression == NULL ) goto end_of_job; /* no input */
4666: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
4667: /* -------------------------------------------------------------------------
4668: process preamble if present
4669: -------------------------------------------------------------------------- */
4670: /*process_preamble:*/
4671: if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
4672: != NULL ) /* found embedded $ */
4673: if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
4674: > 0 ) { /* must have preamble preceding $ */
4675: if ( prelen < 65 ) { /* too long for a prefix */
4676: memcpy(pretext,expression,prelen); /* local copy of preamble */
4677: pretext[prelen] = '\000'; /* null-terminated */
4678: if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
4679: && strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
4680: /* --- skip any leading whitespace --- */
4681: prep = pretext; /* start at beginning of preamble */
4682: skipwhite(prep); /* skip any leading white space */
4683: /* --- check for embedded , or leading +/- (either signalling size) --- */
4684: if ( isthischar(*prep,"+-") ) /* have leading + or - */
4685: isdelta = 1; /* so use size value as increment */
4686: comma = strchr(pretext,','); /* , signals leading size param */
4687: /* --- process leading size parameter if present --- */
4688: if ( comma != NULL /* size param explicitly signalled */
4689: || isdelta || isdigit(*prep) ) { /* or inferred implicitly */
4690: /* --- parse size parameter and reset size accordingly --- */
4691: if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
4692: sizevalue = atoi(prep); /* convert size string to integer */
4693: if ( size != NULL ) /* caller passed address for size */
4694: *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
4695: /* --- finally, set flag and shift size parameter out of preamble --- */
4696: isfontsize = 1; /*set flag showing font size present*/
4697: if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/
4698: } /* --- end-of-if(comma!=NULL||etc) --- */
4699: /* --- copy any preamble params following size to caller's subexpr --- */
4700: if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
4701: if ( subexpr != NULL ) /* caller passed us an address */
4702: strcpy(subexpr,pretext); /*so return extra params to caller*/
4703: /* --- finally, set prep to shift preamble out of expression --- */
4704: prep = expression + prelen+1; /* set prep past $ in expression */
4705: } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
4706: } /* --- end-of-if(prelen<65) --- */
4707: } /* --- end-of-if(prelen>0) --- */
4708: else { /* $ is first char of expression */
4709: int ndollars = 0; /* number of $...$ pairs removed */
4710: prep = expression; /* start at beginning of expression*/
4711: while ( *prep == '$' ) { /* remove all matching $...$'s */
4712: int explen = strlen(prep)-1; /* index of last char in expression*/
4713: if ( explen < 2 ) break; /* no $...$'s left to remove */
4714: if ( prep[explen] != '$' ) break; /* unmatched $ */
4715: prep[explen] = '\000'; /* remove trailing $ */
4716: prep++; /* and remove matching leading $ */
4717: ndollars++; /* count another pair removed */
4718: } /* --- end-of-while(*prep=='$') --- */
4719: ispreambledollars = ndollars; /* set flag to fix \displaystyle */
4720: if ( ndollars == 1 ) /* user submitted $...$ expression */
4721: isdisplaystyle = 0; /* so set \textstyle */
4722: if ( ndollars > 1 ) /* user submitted $$...$$ */
4723: isdisplaystyle = 2; /* so set \displaystyle */
4724: /*goto process_preamble;*/ /*check for preamble after leading $*/
4725: } /* --- end-of-if/else(prelen>0) --- */
4726: /* -------------------------------------------------------------------------
4727: back to caller
4728: -------------------------------------------------------------------------- */
4729: end_of_job:
4730: return ( prep ); /*expression, or ptr past preamble*/
4731: } /* --- end-of-function preamble() --- */
4732:
4733:
4734: /* ==========================================================================
4735: * Function: mimeprep ( expression )
4736: * Purpose: preprocessor for mimeTeX input, e.g.,
4737: * (a) removes comments,
4738: * (b) converts \left( to \( and \right) to \),
4739: * (c) xlates &html; special chars to equivalent latex
4740: * Should only be called once (after unescape_url())
4741: * --------------------------------------------------------------------------
4742: * Arguments: expression (I/O) char * to first char of null-terminated
4743: * string containing mimeTeX/LaTeX expression,
4744: * and returning preprocessed string
4745: * --------------------------------------------------------------------------
4746: * Returns: ( char * ) ptr to input expression,
4747: * or NULL for any parsing error.
4748: * --------------------------------------------------------------------------
4749: * Notes: o
4750: * ======================================================================= */
4751: /* --- entry point --- */
4752: char *mimeprep ( char *expression )
4753: {
4754: /* -------------------------------------------------------------------------
4755: Allocations and Declarations
4756: -------------------------------------------------------------------------- */
4757: char *expptr=expression, /* ptr within expression */
4758: *tokptr=NULL, /*ptr to token found in expression*/
4759: *texsubexpr(), argval[8192]; /*parse for macro args after token*/
4760: char *strchange(); /* change leading chars of string */
4761: char *findbraces(); /*find left { and right } for \atop*/
4762: int idelim=0, /* left- or right-index */
4763: isymbol=0; /*symbols[],rightcomment[],etc index*/
4764: int xlateleft = 0; /* true to xlate \left and \right */
4765: /* ---
4766: * comments
4767: * -------- */
4768: char *leftptr=NULL; /* find leftcomment in expression */
4769: static char *leftcomment = "%%", /* open comment */
4770: *rightcomment[] = {"\n", "%%", NULL}; /* close comments */
4771: /* ---
4772: * special long (more than 1-char) \left and \right delimiters
4773: * ----------------------------------------------------------- */
4774: static char *leftfrom[] = /* xlate any \left suffix... */
4775: { "\\|", /* \left\| */
4776: "\\{", /* \left\{ */
4777: "\\langle", /* \left\langle */
4778: NULL } ; /* --- end-of-leftfrom[] --- */
4779: static char *leftto[] = /* ...to this instead */
4780: { "=", /* = */
4781: "{", /* { */
4782: "<", /* < */
4783: NULL } ; /* --- end-of-leftto[] --- */
4784: static char *rightfrom[] = /* xlate any \right suffix... */
4785: { "\\|", /* \right\| */
4786: "\\}", /* \right\} */
4787: "\\rangle", /* \right\rangle */
4788: NULL } ; /* --- end-of-rightfrom[] --- */
4789: static char *rightto[] = /* ...to this instead */
4790: { "=", /* = */
4791: "}", /* } */
4792: ">", /* > */
4793: NULL } ; /* --- end-of-rightto[] --- */
4794: /* ---
4795: * { \atop }-like commands
4796: * ----------------------- */
4797: char *atopsym=NULL; /* atopcommands[isymbol] */
4798: static char *atopcommands[] = /* list of {a+b\command c+d}'s */
4799: { "\\over", /* plain tex for \frac */
4800: "\\choose", /* binomial coefficient */
4801: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
4802: "\\atop",
4803: #endif
4804: NULL } ; /* --- end-of-atopcommands[] --- */
4805: static char *atopdelims[] = /* delims for atopcommands[] */
4806: { NULL, NULL, /* \\over has no delims */
4807: "\\left(", "\\right)", /* \\choose has ( ) delims*/
4808: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
4809: NULL, NULL, /* \\atop has no delims */
4810: #endif
4811: NULL, NULL } ; /* --- end-of-atopdelims[] --- */
4812: /* ---
4813: * html special/escape chars converted to latex equivalents
4814: * -------------------------------------------------------- */
4815: char *htmlsym=NULL; /* symbols[isymbol].html */
4816: static struct { char *html; char *args; char *latex; } symbols[] =
4817: { /* ---------------------------------------
4818: user-supplied newcommands
4819: --------------------------------------- */
4820: #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */
4821: #include NEWCOMMANDS
4822: #endif
4823: /* ------------------------------------------
4824: LaTeX Macro #args,default template...
4825: ------------------------------------------ */
4826: { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
1.2 ! albertel 4827: { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
! 4828: { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
! 4829: { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
! 4830: { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
1.1 albertel 4831: { "\\overset", NULL, "\\stackrel" }, /* just an alias */
4832: { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
4833: /* ---------------------------------------
1.2 ! albertel 4834: html char termchar LaTeX equivalent...
1.1 albertel 4835: --------------------------------------- */
4836: { """, ";", "\"" }, /* " is first, " */
4837: { "&", ";", "&" },
4838: { "<", ";", "<" },
4839: { ">", ";", ">" },
4840: { " ", ";", "~" },
4841: { "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
4842: { "¦", ";", "|" },
4843: { "±", ";", "\\pm" },
4844: { "²", ";", "{{}^2}" },
4845: { "³", ";", "{{}^3}" },
4846: { "µ", ";", "\\mu" },
4847: { "¹", ";", "{{}^1}" },
4848: { "¼", ";", "{\\frac14}" },
4849: { "½", ";", "{\\frac12}" },
4850: { "¾", ";", "{\\frac34}" },
4851: { "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
4852: { "Â", ";", "{\\rm~\\hat~A}" },
4853: { "Ã", ";", "{\\rm~\\tilde~A}" },
4854: { "Ä", ";", "{\\rm~\\ddot~A}" },
4855: { "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
4856: { "ã", ";", "{\\rm~\\tilde~a}" },
4857: { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
4858: /* ---------------------------------------
1.2 ! albertel 4859: html tag termchar LaTeX equivalent...
! 4860: --------------------------------------- */
! 4861: { "<br>", NULL, "\\\\" },
! 4862: { "<br/>", NULL, "\\\\" },
! 4863: { "<Br>", NULL, "\\\\" },
! 4864: { "<Br/>", NULL, "\\\\" },
! 4865: { "<BR>", NULL, "\\\\" },
! 4866: { "<BR/>", NULL, "\\\\" },
! 4867: /* ---------------------------------------
1.1 albertel 4868: LaTeX termchar mimeTeX equivalent...
4869: --------------------------------------- */
4870: { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
4871: { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
4872: { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
4873: { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
1.2 ! albertel 4874: { "\\dots", NULL, "{\\cdots}" },
1.1 albertel 4875: { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
4876: { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
4877: { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
4878: { "\\notin", NULL, "{\\not\\in}" },
4879: { "\\neq", NULL, "{\\not=}" },
1.2 ! albertel 4880: { "\\ne", NULL, "{\\not=}" },
1.1 albertel 4881: { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
4882: { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
1.2 ! albertel 4883: { "\\textcelsius", NULL, "{\\textdegree C}"},
! 4884: { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
1.1 albertel 4885: { "\\cr", NULL, "\\\\" },
1.2 ! albertel 4886: { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
1.1 albertel 4887: { "\\iint", NULL, "{\\int\\int}\\limits" },
4888: { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
1.2 ! albertel 4889: { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
1.1 albertel 4890: { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
4891: { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
1.2 ! albertel 4892: { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
! 4893: { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
! 4894: { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
! 4895: { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
! 4896: { "\'", "embed","^{\\fs{-1}\\prime}" },
1.1 albertel 4897: { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
1.2 ! albertel 4898: { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
1.1 albertel 4899: { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
4900: { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
4901: { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
4902: { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
4903: { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
1.2 ! albertel 4904: { "\\cancel",NULL, "\\Not" },
1.1 albertel 4905: { "\\hhline",NULL, "\\Hline" },
4906: { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
4907: /* ---------------------------------------------------------
4908: "Algebra Syntax" termchar mimeTeX/LaTeX equivalent...
4909: ------------------------------------------------------------ */
4910: { "sqrt", "1", "{\\sqrt{#1}}" },
4911: { "sin", "1", "{\\sin{#1}}" },
4912: { "cos", "1", "{\\cos{#1}}" },
4913: { "asin", "1", "{\\sin^{-1}{#1}}" },
4914: { "acos", "1", "{\\cos^{-1}{#1}}" },
1.2 ! albertel 4915: { "exp", "1", "{{\\rm~e}^{#1}}" },
1.1 albertel 4916: { "det", "1", "{\\left|{#1}\\right|}" },
4917: /* ---------------------------------------
4918: LaTeX Constant termchar value...
4919: --------------------------------------- */
4920: { "\\thinspace", NULL, "2" },
4921: { "\\thinmathspace", NULL, "2" },
4922: { "\\textwidth", NULL, "400" },
4923: { NULL, NULL, NULL }
4924: } ; /* --- end-of-symbols[] --- */
4925: /* -------------------------------------------------------------------------
4926: first remove comments
4927: -------------------------------------------------------------------------- */
4928: expptr = expression; /* start search at beginning */
4929: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
4930: {
4931: char *rightsym=NULL; /* rightcomment[isymbol] */
4932: expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
4933: /* --- check for any closing rightcomment, in given precedent order --- */
4934: if ( *expptr != '\000' ) /*have chars after this leftcomment*/
4935: for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
4936: if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
4937: { tokptr += strlen(rightsym); /* first char after rightcomment */
4938: if ( *tokptr == '\000' ) /*nothing after this rightcomment*/
4939: { *leftptr = '\000'; /*so terminate expr at leftcomment*/
4940: break; } /* and stop looking for comments */
4941: *leftptr = '~'; /* replace entire comment by ~ */
4942: strcpy(leftptr+1,tokptr); /* and squeeze out comment */
4943: goto next_comment; } /* stop looking for rightcomment */
4944: /* --- no rightcomment after opening leftcomment --- */
4945: *leftptr = '\000'; /* so terminate expression */
4946: /* --- resume search past squeezed-out comment --- */
4947: next_comment:
4948: if ( *leftptr == '\000' ) break; /* reached end of expression */
4949: expptr = leftptr+1; /*resume search after this comment*/
4950: } /* --- end-of-while(leftptr!=NULL) --- */
4951: /* -------------------------------------------------------------------------
4952: convert \left( to \( and \right) to \), etc.
4953: -------------------------------------------------------------------------- */
4954: if ( xlateleft ) /* \left...\right xlation wanted */
4955: for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left and 1 for \right */
4956: {
4957: char *lrstr = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
4958: int lrlen = (idelim==0?5:6); /* strlen() of \left or \right */
4959: char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
4960: **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
4961: **lrto = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
4962: *lrsym = NULL; /* lrfrom[isymbol] */
4963: expptr = expression; /* start search at beginning */
4964: while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
4965: {
4966: if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
4967: { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/
4968: expptr = tokptr+2; } /* and resume search past brace */
4969: else /* may be a "long" brace like \| */
4970: {
4971: expptr = tokptr+lrlen; /*init to resume search past\left\rt*/
4972: for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
4973: { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
4974: if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
4975: { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
4976: *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
4977: expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
4978: break; } /* no need to check more lrsym's */
4979: } /* --- end-of-for(isymbol) --- */
4980: } /* --- end-of-if/else(isthischar()) --- */
4981: } /* --- end-of-while(tokptr!=NULL) --- */
4982: } /* --- end-of-for(idelim) --- */
4983: /* -------------------------------------------------------------------------
4984: run thru table, converting all occurrences of each macro to its expansion
4985: -------------------------------------------------------------------------- */
4986: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
4987: {
4988: int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
4989: int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
1.2 ! albertel 4990: int isembedded = 0; /* true to xlate even if embedded */
1.1 albertel 4991: char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
4992: char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
4993: *htmlterm = args, /*if *args nonumeric, then html term*/
4994: *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
4995: char abuff[8192]; int iarg,nargs=0; /* macro expansion params */
4996: if ( args != NULL ) /*we have args (or htmlterm) param*/
4997: if ( *args != '\000' ) /* and it's not an empty string */
4998: if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
4999: { htmlterm = NULL; /* if so, then we have no htmlterm */
5000: *abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */
5001: nargs = atoi(abuff); } /* interpret #args to numeric */
1.2 ! albertel 5002: else if ( strncmp(args,"embed",5) == 0 ) /* xlate even if embedded */
! 5003: { htmlterm = NULL; /* if so, then we have no htmlterm */
! 5004: isembedded = 1 ; } /* turn on embedded flag */
1.1 albertel 5005: expptr = expression; /* re-start search at beginning */
5006: while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */
1.2 ! albertel 5007: { char termchar = *(tokptr+htmllen), /* char terminating html sequence */
! 5008: prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
1.1 albertel 5009: int escapelen = htmllen; /* total length of escape sequence */
5010: *abuff = '\000'; /* default to empty string */
5011: if ( latexsym != NULL ) /* table has .latex xlation */
5012: if ( *latexsym != '\000' ) /* and it's not an empty string */
5013: strcpy(abuff,latexsym); /* so get local copy */
5014: if ( htmlterm != NULL ) /* sequence may have terminator */
5015: escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
1.2 ! albertel 5016: if ( !isembedded ) /* don't xlate embedded sequence */
! 5017: if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
1.1 albertel 5018: { expptr = tokptr+htmllen; /* just resume search after prefix */
5019: continue; } /* but don't replace it */
1.2 ! albertel 5020: if ( isembedded ) /* for embedded sequence */
! 5021: if ( isthischar(prevchar,ESCAPE) ) /* don't xlate escaped char */
! 5022: { expptr = tokptr+htmllen; /*just resume search after literal*/
! 5023: continue; } /* but don't replace it */
1.1 albertel 5024: if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */
1.2 ! albertel 5025: && isalpha(*htmlsym) /* and our symbol starts with alpha*/
1.1 albertel 5026: && !isthischar(*htmlsym,"&") ) /* and not an &html; special char */
5027: if ( tokptr != expression ) /* then if we're past beginning */
5028: if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
5029: || (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */
5030: { expptr = tokptr+escapelen; /*just resume search after literal*/
5031: continue; } /* but don't replace it */
5032: if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/
5033: {
5034: char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
5035: char *optarg = args+1; /* ptr 1 char past #args digit 0-9 */
5036: expptr = arg1ptr; /* ptr to beginning of next arg */
5037: for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
5038: {
5039: char argsignal[32] = "#1", /* #1...#9 signals arg replacement */
5040: *argsigptr = NULL; /* ptr to argsignal in abuff[] */
5041: /* --- get argument value --- */
5042: *argval = '\000'; /* init arg as empty string */
5043: skipwhite(expptr); /* and skip leading white space */
5044: if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
5045: && !isalgebra ) /* but not in "algebra syntax" */
5046: { strcpy(argval,optarg); /* init with default value */
5047: if ( *expptr == '[' ) /* but user gave us [argval] */
5048: expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
5049: else /* not optional, so get {argval} */
5050: if ( *expptr != '\000' ) /* check that some argval provided */
5051: if ( !isalgebra ) /* only { } delims for latex macro */
5052: expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/
5053: else /*any delim for algebra syntax macro*/
5054: { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
5055: if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
5056: if ( *argval != '{' ) /* and it's not { }-enclosed */
5057: { strchange(0,argval,"\\left"); /* insert opening \left, */
5058: strchange(0,argval+strlen(argval)-1,"\\right"); } /*\right*/
5059: } /* --- end-of-if/else(!isalgebra) --- */
5060: /* --- replace #`iarg` in macro with argval --- */
5061: sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */
5062: while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
5063: strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/
5064: while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
5065: strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
5066: } /* --- end-of-for(iarg) --- */
5067: escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
5068: } /* --- end-of-if(nargs>0) --- */
5069: strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
5070: expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
5071: } /* --- end-of-while(tokptr!=NULL) --- */
5072: } /* --- end-of-for(isymbol) --- */
5073: /* -------------------------------------------------------------------------
5074: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
5075: -------------------------------------------------------------------------- */
5076: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
5077: {
5078: int atoplen = strlen(atopsym); /* #chars in \atop */
5079: expptr = expression; /* re-start search at beginning */
5080: while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
5081: { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
5082: char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
5083: if ( msgfp!=NULL && msglevel>=999 )
5084: { fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
5085: (int)(tokptr-expression),tokptr);
5086: fflush(msgfp); }
5087: if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
5088: { expptr = tokptr+atoplen; /* just resume search after prefix */
5089: continue; } /* but don't process it */
5090: leftbrace = findbraces(expression,tokptr); /* find left { */
5091: rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
5092: if ( leftbrace==NULL || rightbrace==NULL )
5093: { expptr += atoplen; continue; } /* skip command if didn't find */
5094: else /* we have bracketed { \atop } */
5095: {
5096: int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
5097: rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
5098: totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
5099: char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
5100: char arg[8192], command[8192]; /* left/right args, new \atop{}{} */
5101: *command = '\000'; /* start with null string */
5102: if (open!=NULL) strcat(command,open); /* add open delim if needed */
5103: strcat(command,atopsym); /* add command with \atop */
5104: arg[0] = '{'; /* arg starts with { */
5105: memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
5106: arg[leftlen+1] = '\000'; /* and null terminate it */
5107: strcat(command,arg); /* concatanate {left-arg to \atop */
5108: strcat(command,"}{"); /* close left-arg, open right-arg */
5109: memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
5110: arg[rightlen] = '}'; /* add closing } */
5111: arg[rightlen+1] = '\000'; /* and null terminate it */
5112: if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
5113: strcpy(arg,arg+1); /* so squeeze it out */
5114: strcat(command,arg); /* concatanate right-arg} */
5115: if (close!=NULL) strcat(command,close); /* add close delim if needed*/
5116: strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
5117: expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
5118: }
5119: } /* --- end-of-while(tokptr!=NULL) --- */
5120: } /* --- end-of-for(isymbol) --- */
5121: /* -------------------------------------------------------------------------
5122: back to caller with preprocessed expression
5123: -------------------------------------------------------------------------- */
5124: if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */
5125: { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
5126: fflush(msgfp); }
5127: return ( expression );
5128: } /* --- end-of-function mimeprep() --- */
5129:
5130:
5131: /* ==========================================================================
5132: * Function: strchange ( int nfirst, char *from, char *to )
5133: * Purpose: Changes the nfirst leading chars of `from` to `to`.
5134: * For example, to change char x[99]="12345678" to "123ABC5678"
5135: * call strchange(1,x+3,"ABC")
5136: * --------------------------------------------------------------------------
5137: * Arguments: nfirst (I) int containing #leading chars of `from`
5138: * that will be replace by `to`
5139: * from (I/O) char * to null-terminated string whose nfirst
5140: * leading chars will be replaced by `to`
5141: * to (I) char * to null-terminated string that will
5142: * replace the nfirst leading chars of `from`
5143: * --------------------------------------------------------------------------
5144: * Returns: ( char * ) ptr to first char of input `from`
5145: * or NULL for any error.
5146: * --------------------------------------------------------------------------
5147: * Notes: o If strlen(to)>nfirst, from must have memory past its null
5148: * (i.e., we don't do a realloc)
5149: * ======================================================================= */
5150: /* --- entry point --- */
5151: char *strchange ( int nfirst, char *from, char *to )
5152: {
5153: /* -------------------------------------------------------------------------
5154: Allocations and Declarations
5155: -------------------------------------------------------------------------- */
5156: int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
5157: nshift = abs(tolen-nfirst); /*need to shift from left or right*/
5158: /* -------------------------------------------------------------------------
5159: shift from left or right to accommodate replacement of its nfirst chars by to
5160: -------------------------------------------------------------------------- */
5161: if ( tolen < nfirst ) /* shift left is easy */
5162: strcpy(from,from+nshift); /* because memory doesn't overlap */
5163: if ( tolen > nfirst ) /* need more room at start of from */
5164: { char *pfrom = from+strlen(from); /* ptr to null terminating from */
5165: for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
5166: *(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */
5167: /* -------------------------------------------------------------------------
5168: from has exactly the right number of free leading chars, so just put to there
5169: -------------------------------------------------------------------------- */
5170: if ( tolen != 0 ) /* make sure to not empty or null */
5171: memcpy(from,to,tolen); /* chars moved into place */
5172: return ( from ); /* changed string back to caller */
5173: } /* --- end-of-function strchange() --- */
5174:
5175:
5176: /* ==========================================================================
5177: * Function: strreplace (char *string, char *from, char *to, int nreplace)
5178: * Purpose: Changes the first nreplace occurrences of 'from' to 'to'
5179: * in string, or all occurrences if nreplace=0.
5180: * --------------------------------------------------------------------------
5181: * Arguments: string (I/0) char * to null-terminated string in which
5182: * occurrence of 'from' will be replaced by 'to'
5183: * from (I) char * to null-terminated string
5184: * to be replaced by 'to'
5185: * to (I) char * to null-terminated string that will
5186: * replace 'from'
5187: * nreplace (I) int containing (maximum) number of
5188: * replacements, or 0 to replace all.
5189: * --------------------------------------------------------------------------
5190: * Returns: ( int ) number of replacements performed,
5191: * or 0 for no replacements or -1 for any error.
5192: * --------------------------------------------------------------------------
5193: * Notes: o
5194: * ======================================================================= */
5195: /* --- entry point --- */
5196: int strreplace ( char *string, char *from, char *to, int nreplace )
5197: {
5198: /* -------------------------------------------------------------------------
5199: Allocations and Declarations
5200: -------------------------------------------------------------------------- */
5201: int fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
5202: tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
5203: char *pfrom = (char *)NULL, /*ptr to 1st char of from in string*/
5204: *pstring = string, /*ptr past previously replaced from*/
5205: *strchange(); /* change 'from' to 'to' */
5206: int nreps = 0; /* #replacements returned to caller*/
5207: /* -------------------------------------------------------------------------
5208: repace occurrences of 'from' in string to 'to'
5209: -------------------------------------------------------------------------- */
5210: if ( string == (char *)NULL /* no input string */
5211: || (fromlen<1 && nreplace<=0) ) /* replacing empty string forever */
5212: nreps = (-1); /* so signal error */
5213: else /* args okay */
5214: while (nreplace<1 || nreps<nreplace) /* up to #replacements requested */
5215: {
5216: if ( fromlen > 0 ) /* have 'from' string */
5217: pfrom = strstr(pstring,from); /*ptr to 1st char of from in string*/
5218: else pfrom = pstring; /*or empty from at start of string*/
5219: if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
5220: if ( strchange(fromlen,pfrom,to) /* leading 'from' changed to 'to' */
5221: == (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
5222: nreps++; /* count another replacement */
5223: pstring = pfrom+tolen; /* pick up search after 'to' */
5224: if ( *pstring == '\000' ) break; /* but quit at end of string */
5225: } /* --- end-of-while() --- */
5226: return ( nreps ); /* #replacements back to caller */
5227: } /* --- end-of-function strreplace() --- */
5228:
5229:
5230: /* ==========================================================================
5231: * Function: strtexchr (char *string, char *texchr )
5232: * Purpose: Find first texchr in string, but texchr must be followed
5233: * by non-alpha
5234: * --------------------------------------------------------------------------
5235: * Arguments: string (I) char * to null-terminated string in which
5236: * firstoccurrence of delim will be found
5237: * texchr (I) char * to null-terminated string that
5238: * will be searched for
5239: * --------------------------------------------------------------------------
5240: * Returns: ( char * ) ptr to first char of texchr in string
5241: * or NULL if not found or for any error.
5242: * --------------------------------------------------------------------------
5243: * Notes: o texchr should contain its leading \, e.g., "\\left"
5244: * ======================================================================= */
5245: /* --- entry point --- */
5246: char *strtexchr ( char *string, char *texchr )
5247: {
5248: /* -------------------------------------------------------------------------
5249: Allocations and Declarations
5250: -------------------------------------------------------------------------- */
1.2 ! albertel 5251: char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
1.1 albertel 5252: char *pstring = string; /* start or continue up search here*/
5253: int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
5254: /* -------------------------------------------------------------------------
5255: locate texchr in string
5256: -------------------------------------------------------------------------- */
5257: if ( string != (char *)NULL /* check that we got input string */
5258: && texchrlen > 0 ) /* and a texchr to search for */
5259: while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
5260: != (char *)NULL ) /* found it */
5261: if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */
5262: == '\000' ) break; /* texchr at very end of string */
5263: else /* if there are chars after texchr */
5264: if ( isalpha(delim) /*texchr is prefix of longer symbol*/
5265: || 0 ) /* other tests to be determined */
5266: pstring = ptexchr + texchrlen; /* continue search after texchr */
5267: else /* passed all tests */
5268: break; /*so return ptr to texchr to caller*/
5269: return ( ptexchr ); /* ptr to texchar back to caller */
5270: } /* --- end-of-function strtexchr() --- */
5271:
5272:
5273: /* ==========================================================================
5274: * Function: findbraces ( char *expression, char *command )
5275: * Purpose: If expression!=NULL, finds opening left { preceding command;
5276: * if expression==NULL, finds closing right } after command.
5277: * For example, to parse out {a+b\over c+d} call findbraces()
5278: * twice.
5279: * --------------------------------------------------------------------------
5280: * Arguments: expression (I) NULL to find closing right } after command,
5281: * or char * to null-terminated string to find
5282: * left opening { preceding command.
5283: * command (I) char * to null-terminated string whose
5284: * first character is usually the \ of \command
5285: * --------------------------------------------------------------------------
5286: * Returns: ( char * ) ptr to either opening { or closing },
5287: * or NULL for any error.
5288: * --------------------------------------------------------------------------
5289: * Notes: o
5290: * ======================================================================= */
5291: /* --- entry point --- */
5292: char *findbraces ( char *expression, char *command )
5293: {
5294: /* -------------------------------------------------------------------------
5295: Allocations and Declarations
5296: -------------------------------------------------------------------------- */
5297: int isopen = (expression==NULL?0:1); /* true to find left opening { */
5298: char *left="{", *right="}", /* delims bracketing {x\command y} */
5299: *delim = (isopen?left:right), /* delim we want, { if isopen */
5300: *match = (isopen?right:left), /* matching delim, } if isopen */
5301: *brace = NULL; /* ptr to delim returned to caller */
5302: int inc = (isopen?-1:+1); /* pointer increment */
5303: int level = 1; /* nesting level, for {{}\command} */
5304: char *ptr = command; /* start search here */
5305: int setbrace = 1; /* true to set {}'s if none found */
5306: /* -------------------------------------------------------------------------
5307: search for left opening { before command, or right closing } after command
5308: -------------------------------------------------------------------------- */
5309: while ( 1 ) /* search for brace, or until end */
5310: {
5311: /* --- next char to check for delim --- */
5312: ptr += inc; /* bump ptr left or right */
5313: /* --- check for beginning or end of expression --- */
5314: if ( isopen ) /* going left, check for beginning */
5315: { if ( ptr < expression ) break; } /* went before start of string */
5316: else { if ( *ptr == '\000' ) break; } /* went past end of string */
5317: /* --- don't check this char if it's escaped --- */
5318: if ( !isopen || ptr>expression ) /* very first char can't be escaped*/
5319: if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */
5320: continue; /* so don't check this char */
5321: /* --- check for delim --- */
5322: if ( isthischar(*ptr,delim) ) /* found delim */
5323: if ( --level == 0 ) /* and it's not "internally" nested*/
5324: { brace = ptr; /* set ptr to brace */
5325: goto end_of_job; } /* and return it to caller */
5326: /* --- check for matching delim --- */
5327: if ( isthischar(*ptr,match) ) /* found matching delim */
5328: level++; /* so bump nesting level */
5329: } /* --- end-of-while(1) --- */
5330: end_of_job:
5331: if ( brace == (char *)NULL ) /* open{ or close} not found */
5332: if ( setbrace ) /* want to force one at start/end? */
5333: brace = ptr; /* { before expressn, } after cmmnd*/
5334: return ( brace ); /*back to caller with delim or NULL*/
5335: } /* --- end-of-function findbraces() --- */
5336: #endif /* PART2 */
5337:
5338: /* ---
5339: * PART3
5340: * ------ */
5341: #if !defined(PARTS) || defined(PART3)
5342: /* ==========================================================================
5343: * Function: rasterize ( expression, size )
5344: * Purpose: returns subraster corresponding to (a valid LaTeX) expression
5345: * at font size
5346: * --------------------------------------------------------------------------
5347: * Arguments: expression (I) char * to first char of null-terminated
5348: * string containing valid LaTeX expression
5349: * to be rasterized
5350: * size (I) int containing 0-4 default font size
5351: * --------------------------------------------------------------------------
5352: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
5353: * or NULL for any parsing error.
5354: * --------------------------------------------------------------------------
5355: * Notes: o This is mimeTeX's "main" reusable entry point. Easy to use:
5356: * just call it with a LaTeX expression, and get back a bitmap
5357: * of that expression. Then do what you want with the bitmap.
5358: * ======================================================================= */
5359: /* --- entry point --- */
5360: subraster *rasterize ( char *expression, int size )
5361: {
5362: /* -------------------------------------------------------------------------
5363: Allocations and Declarations
5364: -------------------------------------------------------------------------- */
5365: char *preamble(), pretext[256]; /* process preamble, if present */
5366: char chartoken[8192], *texsubexpr(), /*get subexpression from expression*/
5367: *subexpr = chartoken; /* token may be parenthesized expr */
5368: int isbrace(); /* check subexpr for braces */
5369: mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/
5370: int natoms=0; /* #atoms/tokens processed so far */
5371: int type_raster(); /* display debugging output */
5372: subraster *rasterize(), /* recurse */
5373: *rastparen(), /* handle parenthesized subexpr's */
5374: *rastlimits(); /* handle sub/superscripted expr's */
5375: subraster *rastcat(), /* concatanate atom subrasters */
5376: *subrastcpy(), /* copy final result if a charaster*/
5377: *new_subraster(); /* new subraster for isstring mode */
5378: subraster *get_charsubraster(), /* character subraster */
5379: *sp=NULL, *prevsp=NULL, /* raster for current, prev char */
5380: *expraster = (subraster *)NULL; /* raster returned to caller */
5381: int delete_subraster(); /* free everything before returning*/
5382: /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
5383: /* --- global values saved/restored at each recursive iteration --- */
1.2 ! albertel 5384: int wasstring = isstring, /* initial isstring mode flag */
1.1 albertel 5385: wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
1.2 ! albertel 5386: oldfontnum = fontnum, /* initial font family */
1.1 albertel 5387: oldfontsize = fontsize, /* initial fontsize */
5388: olddisplaysize = displaysize, /* initial \displaystyle size */
5389: oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
1.2 ! albertel 5390: oldsmashmargin = smashmargin, /* initial smashmargin */
! 5391: oldissmashdelta = issmashdelta, /* initial issmashdelta */
1.1 albertel 5392: *oldworkingparam = workingparam; /* initial working parameter */
5393: subraster *oldworkingbox = workingbox, /* initial working box */
5394: *oldleftexpression = leftexpression; /*left half rasterized so far*/
5395: double oldunitlength = unitlength; /* initial unitlength */
5396: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
5397: /* -------------------------------------------------------------------------
5398: initialization
5399: -------------------------------------------------------------------------- */
5400: recurlevel++; /* wind up one more recursion level*/
5401: leftexpression = NULL; /* no leading left half yet */
5402: isreplaceleft = 0; /* reset replaceleft flag */
5403: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
5404: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
5405: if ( msgfp!=NULL && msglevel >= 29 ) /*display expression for debugging*/
5406: { fprintf(msgfp,
5407: "rasterize> recursion level=%d, size=%d,\n\texpression=\"%s\"\n",
5408: recurlevel,size,(expression==NULL?"null":expression)); fflush(msgfp); }
5409: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
5410: /* -------------------------------------------------------------------------
5411: preocess optional $-terminated preamble preceding expression
5412: -------------------------------------------------------------------------- */
5413: expression = preamble(expression,&size,pretext); /* size may be modified */
5414: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
5415: fontsize = size; /* start at requested size */
5416: if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/
5417: if ( !ispreambledollars ) /* style fixed by $$...$$'s */
5418: isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
5419: /* -------------------------------------------------------------------------
5420: build up raster one character (or subexpression) at a time
5421: -------------------------------------------------------------------------- */
5422: while ( 1 )
5423: {
5424: /* --- get next character/token or subexpression --- */
5425: expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
5426: subexpr = chartoken; /* "local" copy of chartoken ptr */
5427: leftsymdef = NULL; /* no character identified yet */
5428: sp = NULL; /* no subraster yet */
5429: size = fontsize; /* in case reset by \tiny, etc */
5430: /* --- debugging output --- */
5431: if ( msgfp!=NULL && msglevel >= 999 ) /* display chartoken for debugging */
5432: { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d = \"%s\"\n",
5433: recurlevel,natoms+1,chartoken); fflush(msgfp); }
5434: if ( expression == NULL /* no more tokens */
5435: && *subexpr == '\000' ) break; /* and this token empty */
5436: if ( *subexpr == '\000' ) break; /* enough if just this token empty */
5437: /* --- check for parenthesized subexpression --- */
5438: if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */
5439: { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
5440: == NULL ) continue; } /* flush it if failed to rasterize */
5441: else /* --- single-character atomic token --- */
5442: if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
5443: {
5444: /* --- first look up mathchardef for atomic token in table --- */
5445: if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
5446: == NULL ) /* lookup failed */
5447: { char literal[512] = "[?]"; /*display for unrecognized literal*/
1.2 ! albertel 5448: int oldfontnum = fontnum; /* error display in default mode */
1.1 albertel 5449: if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */
5450: { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
5451: chartoken); fflush(msgfp); }
5452: sp = (subraster *)NULL; /* init to signal failure */
5453: if ( warninglevel < 1 ) continue; /* warnings not wanted */
1.2 ! albertel 5454: fontnum = 0; /* reset from \mathbb, etc */
1.1 albertel 5455: if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */
5456: { /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
5457: strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
5458: strcat(literal,chartoken+1); /* add chars following leading \ */
5459: strcat(literal,"?]}"); } /* add closing brace */
5460: sp = rasterize(literal,size-1); /* rasterize literal token */
1.2 ! albertel 5461: fontnum = oldfontnum; /* reset font family */
1.1 albertel 5462: if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/
5463: else /* --- check if we have special handler to process this token --- */
5464: if ( symdef->handler != NULL ) /* have a handler for this token */
5465: { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
5466: if ( (sp = (subraster *) /* returned void* is subraster* */
5467: (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))== NULL )
5468: continue; } /* flush token if handler failed */
5469: else /* --- no handler, so just get subraster for this character --- */
5470: if ( !isstring ) /* rasterizing */
5471: { if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
5472: == NULL ) continue; } /* flush token if failed */
5473: else /* constructing ascii string */
5474: { char *symbol = symdef->symbol; /* symbol for ascii string */
5475: int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
5476: if ( symlen < 1 ) continue; /* no symbol for ascii string */
5477: if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
5478: == NULL ) continue; /* flush token if malloc failed */
5479: sp->type = ASCIISTRING; /* set subraster type */
5480: sp->symdef = symdef; /* and set symbol definition */
5481: sp->baseline = 1; /* default (should be unused) */
5482: strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
5483: /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
5484: } /* --- end-of-if/else ... if/else --- */
5485: /* --- handle any super/subscripts following symbol or subexpression --- */
5486: sp = rastlimits(&expression,size,sp);
5487: /* --- debugging output --- */
5488: if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
5489: { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d%s\n",
5490: recurlevel,natoms+1,(sp==NULL?" = null":"..."));
5491: if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
5492: fflush(msgfp); } /* flush msgfp buffer */
5493: /* --- accumulate atom or parenthesized subexpression --- */
5494: if ( natoms < 1 /* nothing previous to concat */
5495: || expraster == NULL /* or previous was complete error */
5496: || isreplaceleft ) /* or we're replacing previous */
1.2 ! albertel 5497: { if ( 1 && expraster!=NULL ) /* probably replacing left */
! 5498: delete_subraster(expraster); /* so first free original left */
! 5499: expraster = subrastcpy(sp); /* copy static CHARASTER or left */
1.1 albertel 5500: isreplaceleft = 0; } /* reset replacement flag */
5501: else /*we've already built up atoms so...*/
5502: if ( sp != NULL ) /* ...if we have a new component */
5503: expraster = rastcat(expraster,sp,1); /* concat new one, free previous */
5504: delete_subraster(prevsp); /* free prev (if not a CHARASTER) */
5505: prevsp = sp; /* current becomes previous */
5506: leftexpression = expraster; /* left half rasterized so far */
5507: /* --- bump count --- */
5508: natoms++; /* bump #atoms count */
5509: } /* --- end-of-while(expression!=NULL) --- */
5510: /* -------------------------------------------------------------------------
5511: back to caller with rasterized expression
5512: -------------------------------------------------------------------------- */
5513: end_of_job:
5514: delete_subraster(prevsp); /* free last (if not a CHARASTER) */
5515: /* --- debugging output --- */
5516: if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
5517: { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
5518: recurlevel,natoms);
5519: if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
5520: type_raster(expraster->image,msgfp); /* display completed raster */
5521: fflush(msgfp); } /* flush msgfp buffer */
1.2 ! albertel 5522: /* --- set final raster buffer --- */
! 5523: if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
! 5524: { expraster->type = IMAGERASTER; /* set type to constructed image */
! 5525: if ( istextmode ) /* but in text mode */
! 5526: expraster->type = blanksignal; /* set type to avoid smash */
! 5527: expraster->size = fontsize; } /* set original input font size */
1.1 albertel 5528: /* --- restore flags/values to original saved values --- */
5529: isstring = wasstring; /* string mode reset */
5530: isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
1.2 ! albertel 5531: fontnum = oldfontnum; /* font family reset */
1.1 albertel 5532: fontsize = oldfontsize; /* fontsize reset */
5533: displaysize = olddisplaysize; /* \displaystyle size reset */
5534: shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
1.2 ! albertel 5535: smashmargin = oldsmashmargin; /* smashmargin reset */
! 5536: issmashdelta = oldissmashdelta; /* issmashdelta reset */
1.1 albertel 5537: workingparam = oldworkingparam; /* working parameter reset */
5538: workingbox = oldworkingbox; /* working box reset */
5539: leftexpression = oldleftexpression; /* leftexpression reset */
5540: leftsymdef = oldleftsymdef; /* leftsymdef reset */
5541: unitlength = oldunitlength; /* unitlength reset */
5542: recurlevel--; /* unwind one recursion level */
5543: /* --- return final subraster to caller --- */
5544: return ( expraster );
5545: } /* --- end-of-function rasterize() --- */
5546:
5547:
5548: /* ==========================================================================
5549: * Function: rastparen ( subexpr, size, basesp )
5550: * Purpose: parentheses handler, returns a subraster corresponding to
5551: * parenthesized subexpression at font size
5552: * --------------------------------------------------------------------------
5553: * Arguments: subexpr (I) char ** to first char of null-terminated
5554: * string beginning with a LEFTBRACES
5555: * to be rasterized
5556: * size (I) int containing 0-5 default font size
5557: * basesp (I) subraster * to character (or subexpression)
5558: * immediately preceding leading left{
5559: * (unused, but passed for consistency)
5560: * --------------------------------------------------------------------------
5561: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
5562: * or NULL for any parsing error
5563: * --------------------------------------------------------------------------
5564: * Notes: o This "handler" isn't in the mathchardef symbol table,
5565: * but is called directly from rasterize(), as necessary.
5566: * o Though subexpr is returned unchanged, it's passed as char **
5567: * for consistency with other handlers. Ditto, basesp is unused
5568: * but passed for consistency
5569: * ======================================================================= */
5570: /* --- entry point --- */
5571: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
5572: {
5573: /* -------------------------------------------------------------------------
5574: Allocations and Declarations
5575: -------------------------------------------------------------------------- */
5576: char *expression = *subexpr; /* dereference subexpr to get char* */
5577: int explen = strlen(expression); /* total #chars, including parens */
5578: int isescape = 0, /* true if parens \escaped */
5579: isrightdot = 0, /* true if right paren is \right. */
5580: isleftdot = 0; /* true if left paren is \left. */
5581: char left[16], right[16]; /* parens enclosing expresion */
5582: char noparens[8192]; /* get subexpr without parens */
5583: subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */
5584: int isheight = 1; /*true=full height, false=baseline*/
5585: int height, /* height of rasterized noparens[] */
5586: baseline; /* and its baseline */
1.2 ! albertel 5587: int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
1.1 albertel 5588: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
5589: subraster *rastcat(); /* concatanate subrasters */
5590: int delete_subraster(); /*in case of error after allocation*/
5591: /* -------------------------------------------------------------------------
5592: rasterize "interior" of expression, i.e., without enclosing parens
5593: -------------------------------------------------------------------------- */
5594: /* --- first see if enclosing parens are \escaped --- */
5595: if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */
5596: isescape = 1; /* so set flag accordingly */
5597: /* --- get expression *without* enclosing parens --- */
5598: strcpy(noparens,expression); /* get local copy of expression */
5599: noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
5600: strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */
5601: /* --- rasterize it --- */
5602: if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
5603: == NULL ) goto end_of_job; /* quit if failed */
5604: /* --- no need to add parentheses for unescaped { --- */
5605: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
5606: goto end_of_job; /* just return sp to caller */
5607: /* -------------------------------------------------------------------------
5608: obtain paren characters to enclose noparens[] raster with
5609: -------------------------------------------------------------------------- */
5610: /* --- first get left and right parens from expression --- */
5611: memset(left,0,16); memset(right,0,16); /* init parens with nulls */
5612: left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */
5613: right[0] = *(expression+explen-1); /* right} is always last char */
5614: isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */
5615: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
5616: /* --- need height of noparens[] raster as minimum parens height --- */
5617: height = (sp->image)->height; /* height of noparens[] raster */
5618: baseline = sp->baseline; /* baseline of noparens[] raster */
5619: if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */
5620: /* --- get best-fit parentheses characters --- */
5621: if ( !isleftdot ) /* if not \left. */
5622: lp = get_delim(left,height+1,family); /* get left paren char */
5623: if ( !isrightdot ) /* and if not \right. */
5624: rp = get_delim(right,height+1,family); /* get right paren char */
5625: if ( (lp==NULL && !isleftdot) /* check that we got left( */
5626: || (rp==NULL && !isrightdot) ) /* and right) if needed */
5627: { delete_subraster(sp); /* if failed, free subraster */
5628: if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
5629: if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
5630: sp = (subraster *)NULL; /* signal error to caller */
5631: goto end_of_job; } /* and quit */
5632: /* -------------------------------------------------------------------------
5633: set paren baselines to center on noparens[] raster, and concat components
5634: -------------------------------------------------------------------------- */
5635: /* --- set baselines to center paren chars on raster --- */
5636: if ( lp != NULL ) /* ignore for \left. */
5637: lp->baseline = baseline + ((lp->image)->height - height)/2;
5638: if ( rp != NULL ) /* ignore for \right. */
5639: rp->baseline = baseline + ((rp->image)->height - height)/2;
5640: /* --- concat lp||sp||rp to obtain final result --- */
5641: if ( lp != NULL ) /* ignore \left. */
5642: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
5643: if ( sp != NULL ) /* succeeded or ignored \left. */
5644: if ( rp != NULL ) /* ignore \right. */
5645: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
5646: /* --- back to caller --- */
5647: end_of_job:
5648: return ( sp );
5649: } /* --- end-of-function rastparen() --- */
5650:
5651:
5652: /* ==========================================================================
5653: * Function: rastlimits ( expression, size, basesp )
5654: * Purpose: \limits, \nolimts, _ and ^ handler,
5655: * dispatches call to rastscripts() or to rastdispmath()
5656: * as necessary, to handle sub/superscripts following symbol
5657: * --------------------------------------------------------------------------
5658: * Arguments: expression (I) char ** to first char of null-terminated
5659: * LaTeX expression (unused/unchanged)
5660: * size (I) int containing base font size (not used,
5661: * just stored in subraster)
5662: * basesp (I) subraster * to current character (or
5663: * subexpression) immediately preceding script
5664: * indicator
5665: * --------------------------------------------------------------------------
5666: * Returns: ( subraster * ) ptr to subraster returned by rastscripts()
5667: * or rastdispmath(), or NULL for any error
5668: * --------------------------------------------------------------------------
5669: * Notes: o
5670: * ======================================================================= */
5671: /* --- entry point --- */
5672: subraster *rastlimits ( char **expression, int size, subraster *basesp )
5673: {
5674: /* -------------------------------------------------------------------------
5675: Allocations and Declarations
5676: -------------------------------------------------------------------------- */
5677: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
5678: *rastcat(), /* may need to concat scripts */
5679: *scriptsp = basesp; /* and this will become the result */
5680: int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
1.2 ! albertel 5681: int oldsmashmargin = smashmargin; /* save original smashmargin */
1.1 albertel 5682: int type_raster(); /* display debugging output */
5683: /* --- to check for \limits or \nolimits preceding scripts --- */
5684: char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
5685: int toklen=0; /* strlen(limtoken) */
5686: mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */
5687: int class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
5688: /* -------------------------------------------------------------------------
5689: determine whether or not to use displaymath
5690: -------------------------------------------------------------------------- */
5691: scriptlevel++; /* first, increment subscript level*/
5692: *limtoken = '\000'; /* no token yet */
5693: if ( msgfp!=NULL && msglevel>=999 )
5694: { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
5695: scriptlevel,(exprptr==NULL?"null":exprptr)); fflush(msgfp); }
5696: if ( isstring ) goto end_of_job; /* no scripts for ascii string */
5697: /* --- check for \limits or \nolimits --- */
5698: skipwhite(exprptr); /* skip white space before \limits */
5699: if ( exprptr != NULL ) /* expression ptr supplied */
5700: if ( *exprptr != '\000' ) /* something in expression */
5701: exprptr = texchar(exprptr,limtoken); /* retrieve next token */
5702: if ( *limtoken != '\000' ) /* have token */
5703: if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
5704: if ( memcmp("\\limits",limtoken,toklen) == 0 /* may be \limits */
5705: || memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
5706: if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */
5707: != NULL ) /* found token in table */
5708: if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */
5709: isdisplay = 1; /* so explicitly set displaymath */
5710: else /* wasn't \limits */
5711: if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
5712: isdisplay = 0; /* so explicitly reset displaymath */
5713: /* --- see if we found \[no]limits --- */
5714: if ( isdisplay != (-1) ) /* explicit directive found */
5715: *expression = exprptr; /* so bump expression past it */
5716: else /* noexplicit directive */
5717: { isdisplay = 0; /* init displaymath flag off */
5718: if ( isdisplaystyle ) /* we're in displaystyle math mode */
5719: if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */
5720: { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
5721: isdisplay = 1; } /* set flag if mode forced true */
5722: else
5723: if ( isdisplaystyle >= 2 ) /*or mode forced conditionally true*/
5724: { if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
5725: && class!=OPENING && class!=CLOSING /*don't force ('s and )'s*/
5726: && class!=BINARYOP /* don't force binary operators */
5727: && class!=NOVALUE ) /* finally, don't force "images" */
5728: isdisplay = 1; } /* set flag if mode forced true */
5729: else /* determine mode from base symbol */
5730: if ( class == DISPOPER ) /* it's a displaystyle operator */
5731: isdisplay = 1; } /* so set flag */
5732: /* -------------------------------------------------------------------------
5733: dispatch call to create sub/superscripts
5734: -------------------------------------------------------------------------- */
5735: if ( isdisplay ) /* scripts above/below base symbol */
5736: scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
5737: else /* scripts alongside base symbol */
5738: if ( (scriptsp=rastscripts(expression,size,basesp)) == NULL ) /*no scripts*/
5739: scriptsp = basesp; /* so just return unscripted symbol*/
5740: else /* symbols followed by scripts */
5741: if ( basesp != NULL ) /* have base symbol */
1.2 ! albertel 5742: { smashmargin = 0; /* don't smash script */
! 5743: /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
! 5744: scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
1.1 albertel 5745: scriptsp->type = IMAGERASTER; /* flip type of composite object */
5746: scriptsp->size = size; } /* and set font size */
5747: end_of_job:
1.2 ! albertel 5748: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.1 albertel 5749: if ( msgfp!=NULL && msglevel>=99 )
5750: { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
5751: scriptlevel,(scriptsp==NULL?"null":"..."));
5752: if ( scriptsp != NULL ) /* have a constructed raster */
5753: type_raster(scriptsp->image,msgfp); /*display constructed raster*/
5754: fflush(msgfp); }
5755: scriptlevel--; /*lastly, decrement subscript level*/
5756: return ( scriptsp );
5757: } /* --- end-of-function rastlimits() --- */
5758:
5759:
5760: /* ==========================================================================
5761: * Function: rastscripts ( expression, size, basesp )
5762: * Purpose: super/subscript handler, returns subraster for the leading
5763: * scripts in expression, whose base symbol is at font size
5764: * --------------------------------------------------------------------------
5765: * Arguments: expression (I/O) char ** to first char of null-terminated
5766: * string beginning with a super/subscript,
5767: * and returning ptr immediately following
5768: * last script character processed.
5769: * size (I) int containing 0-4 default font size
5770: * basesp (I) subraster * to character (or subexpression)
5771: * immediately preceding leading script
5772: * (scripts will be placed relative to base)
5773: * --------------------------------------------------------------------------
5774: * Returns: ( subraster * ) ptr to subraster corresponding to scripts,
5775: * or NULL for any parsing error
5776: * --------------------------------------------------------------------------
5777: * Notes: o This "handler" isn't in the mathchardef symbol table,
5778: * but is called directly from rasterize(), as necessary.
5779: * ======================================================================= */
5780: /* --- entry point --- */
5781: subraster *rastscripts ( char **expression, int size, subraster *basesp )
5782: {
5783: /* -------------------------------------------------------------------------
5784: Allocations and Declarations
5785: -------------------------------------------------------------------------- */
5786: char *texscripts(), /* parse expression for scripts */
5787: subscript[512], supscript[512]; /* scripts parsed from expression */
5788: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
5789: subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */
5790: *rastack(); /*sets scripts in displaymath mode*/
5791: raster *rp=NULL; /* image raster embedded in sp */
5792: int height=0, width=0, baseline=0, /* height,width,baseline of sp */
5793: subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */
5794: supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */
5795: baseht=0, baseln=0; /* height,baseline of base */
5796: int bdescend=0, sdescend=0; /* descender of base, subscript */
5797: int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */
5798: isbase=0; /* true if we have base symbol */
5799: int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
5800: vbetween = 2, /* vertical space between scripts */
5801: vabove = szval+1, /*sup's top/bot above base's top/bot*/
5802: vbelow = szval+1, /*sub's top/bot below base's top/bot*/
5803: vbottom = szval+1; /*sup's bot above (sub's below) bsln*/
5804: /*int istweak = 1;*/ /* true to tweak script positioning */
5805: int rastput(); /*put scripts in constructed raster*/
5806: int delete_subraster(); /* free work areas */
5807: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
5808: /* -------------------------------------------------------------------------
5809: Obtain subscript and/or superscript expressions, and rasterize them/it
5810: -------------------------------------------------------------------------- */
5811: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
5812: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
5813: if ( *expression == NULL ) goto end_of_job; /* no expression given */
5814: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
5815: *expression = texscripts(*expression,subscript,supscript,3);
5816: /* --- rasterize scripts --- */
5817: if ( *subscript != '\000' ) /* have a subscript */
5818: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
5819: if ( *supscript != '\000' ) /* have a superscript */
5820: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
5821: /* --- set flags for convenience --- */
5822: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
5823: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
5824: isboth = (issub && issup); /* true if we have both */
5825: if (!issub && !issup) goto end_of_job; /* quit if we have neither */
5826: /* -------------------------------------------------------------------------
5827: get height, width, baseline of scripts, and height, baseline of base symbol
5828: -------------------------------------------------------------------------- */
5829: /* --- get height and width of components --- */
5830: if ( issub ) /* we have a subscript */
5831: { subht = (subsp->image)->height; /* so get its height */
5832: subwidth = (subsp->image)->width; /* and width */
5833: subln = subsp->baseline; } /* and baseline */
5834: if ( issup ) /* we have a superscript */
5835: { supht = (supsp->image)->height; /* so get its height */
5836: supwidth = (supsp->image)->width; /* and width */
5837: supln = supsp->baseline; } /* and baseline */
5838: /* --- get height and baseline of base, and descender of base and sub --- */
5839: if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */
5840: basesp = leftexpression; /* try using left side thus far */
5841: if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */
5842: { baseht = (basesp->image)->height; /* height of base symbol */
5843: baseln = basesp->baseline; /* and its baseline */
5844: bdescend = baseht-(baseln+1); /* and base symbol descender */
5845: sdescend = bdescend + vbelow; /*sub must descend by at least this*/
5846: if ( baseht > 0 ) isbase = 1; } /* set flag */
5847: /* -------------------------------------------------------------------------
5848: determine width of constructed raster
5849: -------------------------------------------------------------------------- */
5850: width = max2(subwidth,supwidth); /*widest component is overall width*/
5851: /* -------------------------------------------------------------------------
5852: determine height and baseline of constructed raster
5853: -------------------------------------------------------------------------- */
5854: /* --- both super/subscript --- */
5855: if ( isboth ) /*we have subscript and superscript*/
5856: { height = max2(subht+vbetween+supht, /* script heights + space bewteen */
5857: vbelow+baseht+vabove); /*sub below base bot, sup above top*/
5858: baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
5859: /* --- superscript only --- */
5860: if ( !issub ) /* we only have a superscript */
5861: { height = max3(baseln+1+vabove, /* sup's top above base symbol top */
5862: supht+vbottom, /* sup's bot above baseln */
5863: supht+vabove-bdescend); /* sup's bot above base symbol bot */
5864: baseline = height-1; } /*sup's baseline at bottom of raster*/
5865: /* --- subscript only --- */
5866: if ( !issup ) /* we only have a subscript */
5867: if ( subht > sdescend ) /*sub can descend below base bot...*/
5868: { height = subht; /* ...without extra space on top */
5869: baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
5870: baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
5871: else /* sub's top will be below baseln */
5872: { height = sdescend+1; /* sub's bot below base symbol bot */
5873: baseline = 0; } /* sub's baseline at top of raster */
5874: /* -------------------------------------------------------------------------
5875: construct raster with superscript over subscript
5876: -------------------------------------------------------------------------- */
5877: /* --- allocate subraster containing constructed raster --- */
5878: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
5879: == NULL ) /* and if we fail to allocate */
5880: goto end_of_job; /* quit */
5881: /* --- initialize subraster parameters --- */
5882: sp->type = IMAGERASTER; /* set type as constructed image */
5883: sp->size = size; /* set given size */
5884: sp->baseline = baseline; /* composite scripts baseline */
5885: rp = sp->image; /* raster embedded in subraster */
5886: /* --- place super/subscripts in new raster --- */
5887: if ( issup ) /* we have a superscript */
5888: rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */
5889: if ( issub ) /* we have a subscript */
5890: rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
5891: /* -------------------------------------------------------------------------
5892: free unneeded component subrasters and return final result to caller
5893: -------------------------------------------------------------------------- */
5894: end_of_job:
5895: if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
5896: if ( issup ) delete_subraster(supsp); /* and superscript */
5897: return ( sp );
5898: } /* --- end-of-function rastscripts() --- */
5899:
5900:
5901: /* ==========================================================================
5902: * Function: rastdispmath ( expression, size, sp )
5903: * Purpose: displaymath handler, returns sp along with
5904: * its immediately following super/subscripts
5905: * --------------------------------------------------------------------------
5906: * Arguments: expression (I/O) char ** to first char of null-terminated
5907: * string immediately following sp to be
5908: * rasterized along with its super/subscripts,
5909: * and returning ptr immediately following last
5910: * character processed.
5911: * size (I) int containing 0-4 default font size
5912: * sp (I) subraster * to display math operator
5913: * to which super/subscripts will be added
5914: * --------------------------------------------------------------------------
5915: * Returns: ( subraster * ) ptr to subraster corresponding to sp
5916: * plus its scripts, or NULL for any error
5917: * --------------------------------------------------------------------------
5918: * Notes: o sp returned unchanged if no super/subscript(s) follow it.
5919: * ======================================================================= */
5920: /* --- entry point --- */
5921: subraster *rastdispmath ( char **expression, int size, subraster *sp )
5922: {
5923: /* -------------------------------------------------------------------------
5924: Allocations and Declarations
5925: -------------------------------------------------------------------------- */
5926: char *texscripts(), /* parse expression for scripts */
5927: subscript[512], supscript[512]; /* scripts parsed from expression */
5928: int issub=0, issup=0; /* true if we have sub,sup */
5929: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
5930: *rastack(), /* stack operator with scripts */
5931: *new_subraster(); /* for dummy base sp, if needed */
5932: int vspace = 1; /* vertical space between scripts */
5933: /* -------------------------------------------------------------------------
5934: Obtain subscript and/or superscript expressions, and rasterize them/it
5935: -------------------------------------------------------------------------- */
5936: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
5937: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
5938: if ( *expression == NULL ) goto end_of_job; /* no expression given */
5939: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
5940: *expression = texscripts(*expression,subscript,supscript,3);
5941: /* --- rasterize scripts --- */
5942: if ( *subscript != '\000' ) /* have a subscript */
5943: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
5944: if ( *supscript != '\000' ) /* have a superscript */
5945: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
5946: /* --- set flags for convenience --- */
5947: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
5948: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
5949: if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/
5950: /* -------------------------------------------------------------------------
5951: stack operator and its script(s)
5952: -------------------------------------------------------------------------- */
5953: /* --- stack superscript atop operator --- */
5954: if ( issup ) /* we have a superscript */
5955: if ( sp == NULL ) /* but no base expression */
5956: sp = supsp; /* so just use superscript */
5957: else /* have base and superscript */
5958: if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
5959: == NULL ) goto end_of_job; /* and quit if failed */
5960: /* --- stack operator+superscript atop subscript --- */
5961: if ( issub ) /* we have a subscript */
5962: if ( sp == NULL ) /* but no base expression */
5963: sp = subsp; /* so just use subscript */
5964: else /* have base and subscript */
5965: if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
5966: == NULL ) goto end_of_job; /* and quit if failed */
5967: sp->type = IMAGERASTER; /* flip type of composite object */
5968: sp->size = size; /* and set font size */
5969: /* -------------------------------------------------------------------------
5970: free unneeded component subrasters and return final result to caller
5971: -------------------------------------------------------------------------- */
5972: end_of_job:
5973: return ( sp );
5974: } /* --- end-of-function rastdispmath() --- */
5975:
5976:
5977: /* ==========================================================================
5978: * Function: rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
5979: * Purpose: \left...\right handler, returns a subraster corresponding to
5980: * delimited subexpression at font size
5981: * --------------------------------------------------------------------------
5982: * Arguments: expression (I) char ** to first char of null-terminated
5983: * string beginning with a \left
5984: * to be rasterized
5985: * size (I) int containing 0-5 default font size
5986: * basesp (I) subraster * to character (or subexpression)
5987: * immediately preceding leading left{
5988: * (unused, but passed for consistency)
1.2 ! albertel 5989: * ildelim (I) int containing ldelims[] index of
1.1 albertel 5990: * left delimiter
5991: * arg2 (I) int unused
5992: * arg3 (I) int unused
5993: * --------------------------------------------------------------------------
5994: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
5995: * or NULL for any parsing error
5996: * --------------------------------------------------------------------------
5997: * Notes: o
5998: * ======================================================================= */
5999: /* --- entry point --- */
6000: subraster *rastleft ( char **expression, int size, subraster *basesp,
6001: int ildelim, int arg2, int arg3 )
6002: {
6003: /* -------------------------------------------------------------------------
6004: Allocations and Declarations
6005: -------------------------------------------------------------------------- */
6006: subraster *rasterize(), *sp=NULL; /*rasterize between \left...\right*/
6007: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
6008: subraster *rastlimits(); /*handle sub/super scripts on lp,rp*/
6009: subraster *rastcat(); /* concat lp||sp||rp subrasters */
6010: int family=CMSYEX, /* get_delim() family */
6011: height=0, rheight=0, /* subexpr, right delim height */
6012: margin=(size+1), /* delim height margin over subexpr*/
6013: opmargin=(5); /* extra margin for \int,\sum,\etc */
1.2 ! albertel 6014: char /* *texleft(),*/ subexpr[8192]; /* chars between \left...\right */
1.1 albertel 6015: char *texchar(), /* get delims after \left,\right */
6016: ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
6017: char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
6018: int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
6019: int sublen=0; /* strlen(subexpr) */
6020: int idelim=0; /* 1=left,2=right */
1.2 ! albertel 6021: /* int gotldelim = 0; */ /* true if ildelim given by caller */
1.1 albertel 6022: int delete_subraster(); /* free subraster if rastleft fails*/
6023: int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
1.2 ! albertel 6024: int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
1.1 albertel 6025: /* --- recognized delimiters --- */
6026: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
6027: static char *ldelims[] = {
6028: "unused", ".", /* 1 for \left., \right. */
6029: "(", ")", /* 2,3 for \left(, \right) */
6030: "\\{","\\}", /* 4,5 for \left\{, \right\} */
6031: "[", "]", /* 6,7 for \left[, \right] */
6032: "<", ">", /* 8,9 for \left<, \right> */
6033: "|", "\\|", /* 10,11 for \left,\right |,\|*/
6034: NULL };
6035: /* --- recognized operator delimiters --- */
6036: static char *opdelims[] = { /* operator delims from cmex10 */
6037: "int", "sum", "prod",
6038: "cup", "cap", "dot",
6039: "plus", "times", "wedge",
6040: "vee",
6041: NULL }; /* --- end-of-opdelims[] --- */
6042: /* --- delimiter xlation --- */
6043: static char *xfrom[] = /* xlate any delim suffix... */
6044: { "\\|", /* \| */
6045: "\\{", /* \{ */
6046: "\\}", /* \} */
6047: "\\lbrace", /* \lbrace */
6048: "\\rbrace", /* \rbrace */
6049: "\\langle", /* \langle */
6050: "\\rangle", /* \rangle */
6051: NULL } ; /* --- end-of-xfrom[] --- */
6052: static char *xto[] = /* ...to this instead */
6053: { "=", /* \| to = */
6054: "{", /* \{ to { */
6055: "}", /* \} to } */
6056: "{", /* \lbrace to { */
6057: "}", /* \rbrace to } */
6058: "<", /* \langle to < */
6059: ">", /* \rangle to > */
6060: NULL } ; /* --- end-of-xto[] --- */
1.2 ! albertel 6061: /* --- non-displaystyle delimiters --- */
! 6062: static char *textdelims[] = /* these delims _aren't_ display */
! 6063: { "|", "=",
! 6064: "(", ")",
! 6065: "[", "]",
! 6066: "<", ">",
! 6067: "{", "}",
! 6068: "dbl", /* \lbrackdbl and \rbrackdbl */
! 6069: NULL } ; /* --- end-of-textdelims[] --- */
1.1 albertel 6070: /* -------------------------------------------------------------------------
6071: initialization
6072: -------------------------------------------------------------------------- */
6073: /* --- check args --- */
6074: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
6075: /* --- determine left delimiter, and set default \right. delimiter --- */
6076: if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
1.2 ! albertel 6077: { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
! 6078: /* gotldelim = 1; */ } /* and set flag that we got it */
1.1 albertel 6079: else /* trapped \left without delim */
6080: { skipwhite(*expression); /* interpret \left ( as \left( */
1.2 ! albertel 6081: if ( *(*expression) == '\000' ) /* end-of-string after \left */
! 6082: goto end_of_job; /* so return NULL */
! 6083: *expression = texchar(*expression,ldelim); /*pull delim from expression*/
! 6084: if ( *expression == NULL /* probably invalid end-of-string */
! 6085: || *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
1.1 albertel 6086: strcpy(rdelim,"."); /* init default \right. delim */
6087: /* -------------------------------------------------------------------------
6088: locate \right balancing our opening \left
6089: -------------------------------------------------------------------------- */
6090: /* --- first \right following \left --- */
6091: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
6092: != NULL ) { /* found it */
6093: /* --- find matching \right by pushing past any nested \left's --- */
6094: pleft = *expression; /* start after first \left( */
6095: while ( 1 ) { /*break when matching \right found*/
6096: /* -- locate next nested \left if there is one --- */
6097: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
6098: == NULL ) break; /*no more, so matching \right found*/
6099: pleft += strlen(left); /* push ptr past \left token */
6100: if ( pleft >= pright ) break; /* not nested if \left after \right*/
6101: /* --- have nested \left, so push forward to next \right --- */
6102: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
6103: == NULL ) break; /* ran out of \right's */
6104: } /* --- end-of-while(1) --- */
6105: } /* --- end-of-if(pright!=NULL) --- */
6106: /* -------------------------------------------------------------------------
6107: push past \left(_a^b sub/superscripts, if present
6108: -------------------------------------------------------------------------- */
6109: pleft = *expression; /*reset pleft after opening \left( */
1.2 ! albertel 6110: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
! 6111: != NULL ) /* found actual _a^b scripts, too */
! 6112: { delete_subraster(lp); /* but we don't need them */
! 6113: lp = NULL; } /* reset pointer, too */
1.1 albertel 6114: /* -------------------------------------------------------------------------
6115: get \right delimiter and subexpression between \left...\right, xlate delims
6116: -------------------------------------------------------------------------- */
6117: /* --- get delimiter following \right --- */
6118: if ( pright == (char *)NULL ) { /* assume \right. at end of exprssn*/
6119: strcpy(rdelim,"."); /* set default \right. */
6120: sublen = strlen(*expression); /* use entire remaining expression */
6121: memcpy(subexpr,*expression,sublen); /* copy all remaining chars */
6122: *expression += sublen; } /* and push expression to its null */
6123: else { /* have explicit matching \right */
6124: sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
6125: memcpy(subexpr,*expression,sublen); /* copy chars preceding \right */
6126: *expression = pright+strlen(right); /* push expression past \right */
6127: skipwhite(*expression); /* interpret \right ) as \right) */
6128: *expression = texchar(*expression,rdelim); /*pull delim from expression*/
6129: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
6130: /* --- get subexpression between \left...\right --- */
6131: if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
6132: subexpr[sublen] = '\000'; /* and null-terminate it */
1.2 ! albertel 6133: /* --- adjust margin for expressions containing \middle's --- */
! 6134: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
! 6135: margin = 1; /* so don't "overwhelm" them */
1.1 albertel 6136: /* --- check for operator delimiter --- */
6137: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
6138: if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
6139: { margin += opmargin; /* extra height for operator */
6140: if ( *ldelim == '\\' ) /* have leading escape */
6141: strcpy(ldelim,ldelim+1); /* squeeze it out */
6142: break; } /* no need to check rest of table */
1.2 ! albertel 6143: /* --- xlate delimiters and check for textstyle --- */
1.1 albertel 6144: for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
6145: char *lrdelim = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
6146: int ix; char *xdelim; /* xfrom[] and xto[] index, delim */
6147: for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
6148: if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
6149: { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
6150: break; } /* no need to check further */
1.2 ! albertel 6151: for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
! 6152: if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
! 6153: { if ( idelim == 1 ) /* if it's the \left one */
! 6154: istextleft = 1; /* set left textstyle flag */
! 6155: else istextright = 1; /* else set right textstyle flag */
! 6156: break; } /* no need to check further */
1.1 albertel 6157: } /* --- end-of-for(idelim) --- */
6158: /* --- debugging --- */
6159: if ( msgfp!=NULL && msglevel>=99 )
6160: fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
6161: ldelim,rdelim,subexpr);
6162: /* -------------------------------------------------------------------------
6163: rasterize subexpression
6164: -------------------------------------------------------------------------- */
6165: /* --- rasterize subexpression --- */
6166: if ( (sp = rasterize(subexpr,size)) /* rasterize chars between delims */
6167: == NULL ) goto end_of_job; /* quit if failed */
6168: height = (sp->image)->height; /* height of subexpr raster */
6169: rheight = height+margin; /*default rheight as subexpr height*/
6170: /* -------------------------------------------------------------------------
6171: rasterize delimiters, reset baselines, and add sub/superscripts if present
6172: -------------------------------------------------------------------------- */
6173: /* --- check for dot delimiter --- */
6174: isleftdot = (strchr(ldelim,'.')!=NULL); /* true if \left. */
6175: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
6176: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
1.2 ! albertel 6177: isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
1.1 albertel 6178: if ( !isleftdot ) /* if not \left. */
6179: { /* --- first get requested \left delimiter --- */
6180: lp = get_delim(ldelim,rheight,family); /* get \left delim char */
6181: /* --- reset lp delim baseline to center delim on subexpr raster --- */
6182: if ( lp != NULL ) /* if get_delim() succeeded */
6183: { int lheight = (lp->image)->height; /* actual height of left delim */
6184: lp->baseline = sp->baseline + (lheight - height)/2;
6185: if ( lheight > rheight ) /* got bigger delim than requested */
6186: rheight = lheight-1; } /* make sure right delim matches */
6187: /* --- then add on any sub/superscripts attached to \left( --- */
6188: lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/
1.2 ! albertel 6189: isdisplaystyle = (istextright?0:9); /* force \displaystyle */
1.1 albertel 6190: if ( !isrightdot ) /* and if not \right. */
6191: { /* --- first get requested \right delimiter --- */
6192: rp = get_delim(rdelim,rheight,family); /* get \right delim char */
6193: /* --- reset rp delim baseline to center delim on subexpr raster --- */
6194: if ( rp != NULL ) /* if get_delim() succeeded */
6195: rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
6196: /* --- then add on any sub/superscripts attached to \right) --- */
6197: rp = rastlimits(expression,size,rp); } /*\right)_c^d, expression past d*/
6198: isdisplaystyle = wasdisplaystyle; /* original \displystyle default */
6199: /* --- check that we got delimiters --- */
6200: if ( 0 )
6201: if ( (lp==NULL && !isleftdot) /* check that we got left( */
6202: || (rp==NULL && !isrightdot) ) /* and right) if needed */
6203: { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
6204: if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
6205: if (0) { delete_subraster(sp); /* if failed, free subraster */
6206: sp = (subraster *)NULL; } /* signal error to caller */
6207: goto end_of_job; } /* and quit */
6208: /* -------------------------------------------------------------------------
6209: concat lp || sp || rp components
6210: -------------------------------------------------------------------------- */
6211: /* --- concat lp||sp||rp to obtain final result --- */
6212: if ( lp != NULL ) /* ignore \left. */
6213: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
6214: if ( sp != NULL ) /* succeeded or ignored \left. */
6215: if ( rp != NULL ) /* ignore \right. */
6216: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
6217: /* --- back to caller --- */
6218: end_of_job:
6219: return ( sp );
6220: } /* --- end-of-function rastleft() --- */
6221:
6222:
6223: /* ==========================================================================
1.2 ! albertel 6224: * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
! 6225: * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
! 6226: * --------------------------------------------------------------------------
! 6227: * Arguments: expression (I) char ** to first char of null-terminated
! 6228: * string beginning with a \right
! 6229: * to be rasterized
! 6230: * size (I) int containing 0-5 default font size
! 6231: * basesp (I) subraster * to character (or subexpression)
! 6232: * immediately preceding leading left{
! 6233: * (unused, but passed for consistency)
! 6234: * ildelim (I) int containing rdelims[] index of
! 6235: * right delimiter
! 6236: * arg2 (I) int unused
! 6237: * arg3 (I) int unused
! 6238: * --------------------------------------------------------------------------
! 6239: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
! 6240: * or NULL for any parsing error
! 6241: * --------------------------------------------------------------------------
! 6242: * Notes: o
! 6243: * ======================================================================= */
! 6244: /* --- entry point --- */
! 6245: subraster *rastright ( char **expression, int size, subraster *basesp,
! 6246: int ildelim, int arg2, int arg3 )
! 6247: {
! 6248: /* -------------------------------------------------------------------------
! 6249: Allocations and Declarations
! 6250: -------------------------------------------------------------------------- */
! 6251: subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
! 6252: if ( sp != NULL ) /* returning entire expression */
! 6253: {
! 6254: isreplaceleft = 1; /* set flag to replace left half*/
! 6255: }
! 6256: return ( sp );
! 6257: } /* --- end-of-function rastright() --- */
! 6258:
! 6259:
! 6260: /* ==========================================================================
! 6261: * Function: rastmiddle ( expression, size, basesp, arg1, arg2, arg3 )
! 6262: * Purpose: \middle handler, returns subraster corresponding to
! 6263: * entire expression with \middle delimiter(s) sized to fit.
! 6264: * --------------------------------------------------------------------------
! 6265: * Arguments: expression (I/O) char ** to first char of null-terminated
! 6266: * string immediately following \middle to be
! 6267: * rasterized, and returning ptr immediately
! 6268: * to terminating null.
! 6269: * size (I) int containing 0-5 default font size
! 6270: * basesp (I) subraster * to character (or subexpression)
! 6271: * immediately preceding \middle
! 6272: * (unused, but passed for consistency)
! 6273: * arg1 (I) int unused
! 6274: * arg2 (I) int unused
! 6275: * arg3 (I) int unused
! 6276: * --------------------------------------------------------------------------
! 6277: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
! 6278: * or NULL for any parsing error
! 6279: * (expression ptr unchanged if error occurs)
! 6280: * --------------------------------------------------------------------------
! 6281: * Notes: o
! 6282: * ======================================================================= */
! 6283: /* --- entry point --- */
! 6284: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
! 6285: int arg1, int arg2, int arg3 )
! 6286: {
! 6287: /* -------------------------------------------------------------------------
! 6288: Allocations and Declarations
! 6289: -------------------------------------------------------------------------- */
! 6290: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
! 6291: char *exprptr = *expression, /* local copy of ptr to expression */
! 6292: *texchar(), delim[32][132], /* delimiters following \middle's */
! 6293: *strtexchr(), /* locate \middle's */
! 6294: subexpr[8193], *subptr=NULL; /* subexpression between \middle's */
! 6295: int height=0, habove=0, hbelow=0; /* height, above & below baseline */
! 6296: int idelim, ndelims=0, /* \middle count (max 32) */
! 6297: family = CMSYEX; /* delims from CMSY10 or CMEX10 */
! 6298: subraster *subrastcpy(), /* copy subraster */
! 6299: *rastcat(), /* concatanate subraster */
! 6300: *get_delim(); /* get rasterized delimiter */
! 6301: int delete_subraster(); /* free work area subsp[]'s at eoj */
! 6302: /* -------------------------------------------------------------------------
! 6303: initialization
! 6304: -------------------------------------------------------------------------- */
! 6305: subsp[0] = leftexpression; /* expressn preceding 1st \middle */
! 6306: subsp[1] = NULL; /* set first null */
! 6307: /* -------------------------------------------------------------------------
! 6308: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
! 6309: -------------------------------------------------------------------------- */
! 6310: while ( ndelims < 30 ) /* max of 31 \middle's */
! 6311: {
! 6312: /* --- maintain max height above,below baseline --- */
! 6313: if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
! 6314: { int baseline = (subsp[ndelims])->baseline; /* #rows above baseline */
! 6315: height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
! 6316: habove = max2(habove,baseline); /* max #rows above baseline */
! 6317: hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
! 6318: /* --- get delimter after \middle --- */
! 6319: skipwhite(exprptr); /*skip space betwn \middle & \delim*/
! 6320: exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
! 6321: if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
! 6322: break; /* ignore it and consider job done */
! 6323: ndelims++; /* count another \middle\delim */
! 6324: /* --- get subexpression between \delim and next \middle --- */
! 6325: subsp[ndelims] = NULL; /* no subexpresion yet */
! 6326: if ( *exprptr == '\000' ) /* end-of-expression after \delim */
! 6327: break; /* so we have all subexpressions */
! 6328: if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
! 6329: == NULL ) /* no more \middle's */
! 6330: { strncpy(subexpr,exprptr,8192); /* get entire remaining expression */
! 6331: subexpr[8192] = '\000'; /* make sure it's null-terminated */
! 6332: exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
! 6333: else /* have another \middle */
! 6334: { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
! 6335: memcpy(subexpr,exprptr,min2(sublen,8192)); /* get subexpression */
! 6336: subexpr[min2(sublen,8192)] = '\000'; /* and null-terminate it */
! 6337: exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
! 6338: /* --- rasterize subexpression --- */
! 6339: subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
! 6340: } /* --- end-of-while(1) --- */
! 6341: /* -------------------------------------------------------------------------
! 6342: construct \middle\delim's and concatanate them between subexpressions
! 6343: -------------------------------------------------------------------------- */
! 6344: if ( ndelims < 1 /* no delims */
! 6345: || (height=habove+hbelow) < 1 ) /* or no subexpressions? */
! 6346: goto end_of_job; /* just flush \middle directive */
! 6347: for ( idelim=0; idelim<=ndelims; idelim++ )
! 6348: {
! 6349: /* --- first add on subexpression preceding delim --- */
! 6350: if ( subsp[idelim] != NULL ) /* have subexpr preceding delim */
! 6351: if ( sp == NULL ) /* this is first piece */
! 6352: { sp = subsp[idelim]; /* so just use it */
! 6353: if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
! 6354: else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); /* or concat it */
! 6355: /* --- now construct delimiter --- */
! 6356: if ( *(delim[idelim]) != '\000' ) /* have delimter */
! 6357: { subraster *delimsp = get_delim(delim[idelim],height,family);
! 6358: if ( delimsp != NULL ) /* rasterized delim */
! 6359: { delimsp->baseline = habove; /* set baseline */
! 6360: if ( sp == NULL ) /* this is first piece */
! 6361: sp = delimsp; /* so just use it */
! 6362: else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
! 6363: } /* --- end-of-for(idelim) --- */
! 6364: /* --- back to caller --- */
! 6365: end_of_job:
! 6366: if ( 0 ) /* now handled above */
! 6367: for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
! 6368: if ( subsp[idelim] != NULL ) /* have allocated subraster */
! 6369: delete_subraster(subsp[idelim]); /* so free it */
! 6370: if ( sp != NULL ) /* returning entire expression */
! 6371: { int newht = (sp->image)->height; /* height of returned subraster */
! 6372: sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
! 6373: isreplaceleft = 1; /* set flag to replace left half*/
! 6374: *expression += strlen(*expression); } /* and push to terminating null*/
! 6375: return ( sp );
! 6376: } /* --- end-of-function rastmiddle() --- */
! 6377:
! 6378:
! 6379: /* ==========================================================================
1.1 albertel 6380: * Function: rastflags ( expression, size, basesp, flag, value, arg3 )
6381: * Purpose: sets an internal flag, e.g., for \rm, or sets an internal
6382: * value, e.g., for \unitlength=<value>, and returns NULL
6383: * so nothing is displayed
6384: * --------------------------------------------------------------------------
6385: * Arguments: expression (I) char ** to first char of null-terminated
6386: * LaTeX expression (unused/unchanged)
6387: * size (I) int containing base font size (not used,
6388: * just stored in subraster)
6389: * basesp (I) subraster * to character (or subexpression)
1.2 ! albertel 6390: * immediately preceding "flags" directive
! 6391: * (unused but passed for consistency)
1.1 albertel 6392: * flag (I) int containing #define'd symbol specifying
6393: * internal flag to be set
6394: * value (I) int containing new value of flag
6395: * arg3 (I) int unused
6396: * --------------------------------------------------------------------------
6397: * Returns: ( subraster * ) NULL so nothing is displayed
6398: * --------------------------------------------------------------------------
6399: * Notes: o
6400: * ======================================================================= */
6401: /* --- entry point --- */
6402: subraster *rastflags ( char **expression, int size, subraster *basesp,
6403: int flag, int value, int arg3 )
6404: {
6405: /* -------------------------------------------------------------------------
6406: Allocations and Declarations
6407: -------------------------------------------------------------------------- */
6408: char *texsubexpr(), /* parse expression for... */
6409: valuearg[1024]="NOVALUE"; /* value from expression, if needed */
6410: int argvalue=NOVALUE, /* atoi(valuearg) */
6411: isdelta=0, /* true if + or - precedes valuearg */
6412: valuelen=0; /* strlen(valuearg) */
6413: double strtod(); /*convert ascii {valuearg} to double*/
6414: static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */
6415: /* -------------------------------------------------------------------------
6416: set flag or value
6417: -------------------------------------------------------------------------- */
6418: switch ( flag )
6419: {
6420: default: break; /* unrecognized flag */
1.2 ! albertel 6421: case ISFONTFAM:
1.1 albertel 6422: if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
6423: (*expression)++; /* skip leading ~ after \rm */
1.2 ! albertel 6424: fontnum = value; /* set font family */
1.1 albertel 6425: break;
6426: case ISSTRING: isstring=value; break; /* set string/image mode */
6427: case ISDISPLAYSTYLE: /* set \displaystyle mode */
6428: displaystylelevel = recurlevel; /* \displaystyle set at recurlevel */
6429: isdisplaystyle=value; break;
6430: case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */
6431: case ISREVERSE: /* reverse video */
6432: if ( value==1 || value==NOVALUE )
6433: { fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
6434: if ( value==2 || value==NOVALUE )
6435: { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
6436: if ( value==2 || value==NOVALUE )
6437: isblackonwhite = !isblackonwhite;
6438: break;
6439: case ISSUPER: /* set supersampling/lowpass flag */
6440: #ifndef SSFONTS /* don't have ss fonts loaded */
6441: value = 0; /* so force lowpass */
6442: #endif
6443: isss = issupersampling = value;
6444: fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
6445: break;
6446: case ISFONTSIZE: /* set fontsize */
6447: case ISDISPLAYSIZE: /* set displaysize */
6448: case ISSHRINK: /* set shrinkfactor */
6449: case ISAAALGORITHM: /* set anti-aliasing algorithm */
6450: case ISWEIGHT: /* set font weight */
6451: case ISCENTERWT: /* set lowpass center pixel weight */
6452: case ISADJACENTWT: /* set lowpass adjacent weight */
6453: case ISCORNERWT: /* set lowpass corner weight */
6454: case ISCOLOR: /* set red(1),green(2),blue(3) */
1.2 ! albertel 6455: case ISSMASH: /* set (minimum) "smash" margin */
1.1 albertel 6456: if ( value != NOVALUE ) /* passed a fixed value to be set */
6457: argvalue = value; /* set given fixed value */
6458: else /* get value from expression */
6459: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
6460: if ( *valuearg != '\000' ) /* guard against empty string */
6461: if ( !isalpha(*valuearg) ) /* and against alpha string args */
6462: if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
6463: { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
6464: if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
6465: { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */
6466: argvalue = atoi(valuearg); } } /* convert to int */
6467: switch ( flag )
6468: {
6469: default: break;
6470: case ISCOLOR: /* set color */
6471: slower(valuearg); /* convert arg to lower case */
6472: if ( argvalue==1 || strstr(valuearg,"red") )
6473: { fggreen = fgblue = (isblackonwhite?0:255);
6474: fgred = (isblackonwhite?255:0); }
6475: if ( argvalue==2 || strstr(valuearg,"green") )
6476: { fgred = fgblue = (isblackonwhite?0:255);
6477: fggreen = (isblackonwhite?255:0); }
6478: if ( argvalue==3 || strstr(valuearg,"blue") )
6479: { fgred = fggreen = (isblackonwhite?0:255);
6480: fgblue = (isblackonwhite?255:0); }
6481: if ( argvalue==0 || strstr(valuearg,"black") )
6482: fgred = fggreen = fgblue = (isblackonwhite?0:255);
6483: if ( argvalue==7 || strstr(valuearg,"white") )
6484: fgred = fggreen = fgblue = (isblackonwhite?255:0);
6485: break;
6486: case ISFONTSIZE: /* set fontsize */
6487: if ( argvalue != NOVALUE ) /* got a value */
6488: { int largestsize = (issupersampling?16:LARGESTSIZE);
6489: fontsize = (isdelta? fontsize+argvalue : argvalue);
6490: fontsize = max2(0,min2(fontsize,largestsize));
6491: shrinkfactor = shrinkfactors[fontsize];
1.2 ! albertel 6492: if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
! 6493: || (1 && isdisplaystyle==2) /* displaystyle enabled and set */
! 6494: || (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
1.1 albertel 6495: if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
6496: if ( !ispreambledollars ) /* respect $$...$$'s */
1.2 ! albertel 6497: if ( fontsize >= displaysize )
! 6498: isdisplaystyle = 2; /* forced */
! 6499: else isdisplaystyle = 1;
1.1 albertel 6500: /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
6501: else /* embed font size in expression */
6502: { sprintf(valuearg,"%d",fontsize); /* convert size */
6503: valuelen = strlen(valuearg); /* ought to be 1 */
6504: if ( *expression != '\000' ) /* ill-formed expression */
6505: { *expression = (char *)(*expression-valuelen); /*back up buff*/
6506: memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
6507: break;
6508: case ISDISPLAYSIZE: /* set displaysize */
6509: if ( argvalue != NOVALUE ) /* got a value */
6510: displaysize = (isdelta? displaysize+argvalue : argvalue);
6511: break;
1.2 ! albertel 6512: case ISSMASH: /* set (minimum) "smash" margin */
1.1 albertel 6513: if ( argvalue != NOVALUE ) /* got a value */
1.2 ! albertel 6514: { smashmargin = argvalue; /* set value */
1.1 albertel 6515: if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
1.2 ! albertel 6516: issmashdelta = (isdelta?1:0); } /* and set delta flag */
! 6517: smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
1.1 albertel 6518: break;
6519: case ISSHRINK: /* set shrinkfactor */
6520: if ( argvalue != NOVALUE ) /* got a value */
6521: shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
6522: shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
6523: break;
6524: case ISAAALGORITHM: /* set anti-aliasing algorithm */
6525: if ( argvalue != NOVALUE ) /* got a value */
6526: aaalgorithm = argvalue; /* set algorithm number */
6527: aaalgorithm = max2(0,min2(aaalgorithm,3)); /* bounds check */
6528: break;
6529: case ISWEIGHT: /* set font weight number */
6530: value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
6531: (isdelta? weightnum+argvalue : argvalue));
6532: if ( value>=0 && value<maxaaparams ) /* in range */
6533: { weightnum = value; /* reset weightnum index */
6534: minadjacent = aaparams[weightnum].minadjacent;
6535: maxadjacent = aaparams[weightnum].maxadjacent;
6536: cornerwt = aaparams[weightnum].cornerwt;
6537: adjacentwt = aaparams[weightnum].adjacentwt;
6538: centerwt = aaparams[weightnum].centerwt;
6539: fgalias = aaparams[weightnum].fgalias;
6540: fgonly = aaparams[weightnum].fgonly;
6541: bgalias = aaparams[weightnum].bgalias;
6542: bgonly = aaparams[weightnum].bgonly; }
6543: break;
6544: case ISCENTERWT: /* set lowpass center pixel weight */
6545: if ( argvalue != NOVALUE ) /* got a value */
6546: centerwt = argvalue; /* set lowpass center weight */
6547: break;
6548: case ISADJACENTWT: /* set lowpass adjacent weight */
6549: if ( argvalue != NOVALUE ) /* got a value */
6550: adjacentwt = argvalue; /* set lowpass adjacent weight */
6551: break;
6552: case ISCORNERWT: /* set lowpass corner weight */
6553: if ( argvalue != NOVALUE ) /* got a value */
6554: cornerwt = argvalue; /* set lowpass corner weight */
6555: break;
6556: } /* --- end-of-switch() --- */
6557: break;
6558: case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/
6559: *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
6560: valuelen = strlen(valuearg); /* ought to be 1-4 */
6561: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
6562: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
6563: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
6564: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
6565: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
6566: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
6567: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
6568: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
6569: break;
6570: case UNITLENGTH:
6571: if ( value != NOVALUE ) /* passed a fixed value to be set */
6572: unitlength = (double)(value); /* set given fixed value */
6573: else /* get value from expression */
6574: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
6575: if ( *valuearg != '\000' ) /* guard against empty string */
6576: unitlength = strtod(valuearg,NULL); } /* convert to double */
6577: break;
6578: } /* --- end-of-switch(flag) --- */
6579: return ( NULL ); /*just set value, nothing to display*/
6580: } /* --- end-of-function rastflags() --- */
6581:
6582:
6583: /* ==========================================================================
6584: * Function: rastspace(expression, size, basesp, width, isfill, isheight)
6585: * Purpose: returns a blank/space subraster width wide,
6586: * with baseline and height corresponding to basep
6587: * --------------------------------------------------------------------------
6588: * Arguments: expression (I) char ** to first char of null-terminated
6589: * LaTeX expression (unused/unchanged)
6590: * size (I) int containing base font size (not used,
6591: * just stored in subraster)
6592: * basesp (I) subraster * to character (or subexpression)
6593: * immediately preceding space, whose baseline
6594: * and height params are transferred to space
6595: * width (I) int containing #bits/pixels for space width
6596: * isfill (I) int containing true to \hfill complete
6597: * expression out to width
6598: * isheight (I) int containing true (but not NOVALUE)
6599: * to treat width arg as height
6600: * --------------------------------------------------------------------------
6601: * Returns: ( subraster * ) ptr to empty/blank subraster
6602: * or NULL for any error
6603: * --------------------------------------------------------------------------
6604: * Notes: o
6605: * ======================================================================= */
6606: /* --- entry point --- */
6607: subraster *rastspace ( char **expression, int size, subraster *basesp,
6608: int width, int isfill, int isheight )
6609: {
6610: /* -------------------------------------------------------------------------
6611: Allocations and Declarations
6612: -------------------------------------------------------------------------- */
6613: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
6614: int baseht=1, baseln=0; /* height,baseline of base symbol */
6615: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
6616: char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
6617: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
6618: subraster *rastcat(); /* cat rightsp after \hfill */
6619: /* -------------------------------------------------------------------------
6620: initialization
6621: -------------------------------------------------------------------------- */
6622: if ( isfill == NOVALUE ) isfill=0; /* novalue means false */
6623: if ( isheight == NOVALUE ) isheight=0; /* novalue means false */
6624: /* -------------------------------------------------------------------------
6625: determine width if not given (e.g., \hspace{width}, \hfill{width})
6626: -------------------------------------------------------------------------- */
6627: if ( width <= 0 ) /* width specified in expression */
6628: { int widthval; /* test {width} before using it */
6629: width = 1; /* set default width */
6630: *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
6631: widthval = /* convert {width} to integer */
6632: (int)((unitlength*strtod(widtharg,NULL))+0.5);
6633: if ( widthval>=2 && widthval<=600 ) /* sanity check */
6634: width = widthval; } /* replace deafault width */
6635: /* -------------------------------------------------------------------------
6636: see if width is "absolute" or fill width
6637: -------------------------------------------------------------------------- */
6638: if ( isfill /* called as \hfill{} */
6639: && !isheight ) /* parameter conflict */
6640: { if ( leftexpression != NULL ) /* if we have left half */
6641: width -= (leftexpression->image)->width; /*reduce left width from total*/
6642: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
6643: != NULL ) /* succeeded */
6644: width -= (rightsp->image)->width; } /* reduce right width from total */
6645: /* -------------------------------------------------------------------------
6646: construct blank subraster, and return it to caller
6647: -------------------------------------------------------------------------- */
6648: /* --- get parameters from base symbol --- */
6649: if ( basesp != (subraster *)NULL ) /* we have base symbol for space */
6650: { baseht = (basesp->image)->height; /* height of base symbol */
6651: baseln = basesp->baseline; } /* and its baseline */
6652: /* --- flip params for height --- */
6653: if ( isheight ) /* width is actually height */
6654: { baseht = width; /* use given width as height */
6655: width = 1; } /* and set default width */
6656: /* --- generate and init space subraster --- */
6657: if ( width > 0 ) /*make sure we have positive width*/
6658: if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
6659: != NULL ) /* and if we succeed... */
6660: { /* --- ...re-init subraster parameters --- */
6661: spacesp->size = size; /*propagate base font size forward*/
6662: spacesp->baseline = baseln; } /* ditto baseline */
6663: /* -------------------------------------------------------------------------
6664: concat right half if \hfill-ing
6665: -------------------------------------------------------------------------- */
6666: if ( rightsp != NULL ) /* we have a right half after fill */
6667: { spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/
6668: rastcat(spacesp,rightsp,3)); /* or cat right half after space */
6669: spacesp->type = blanksignal; /* need to propagate blanks */
6670: *expression += strlen((*expression)); } /* push expression to its null */
6671: return ( spacesp );
6672: } /* --- end-of-function rastspace() --- */
6673:
6674:
6675: /* ==========================================================================
6676: * Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 )
6677: * Purpose: \\ handler, returns subraster corresponding to
6678: * left-hand expression preceding \\ above right-hand expression
6679: * --------------------------------------------------------------------------
6680: * Arguments: expression (I/O) char ** to first char of null-terminated
6681: * string immediately following \\ to be
6682: * rasterized, and returning ptr immediately
6683: * to terminating null.
1.2 ! albertel 6684: * size (I) int containing 0-5 default font size
1.1 albertel 6685: * basesp (I) subraster * to character (or subexpression)
1.2 ! albertel 6686: * immediately preceding \\
1.1 albertel 6687: * (unused, but passed for consistency)
6688: * arg1 (I) int unused
6689: * arg2 (I) int unused
6690: * arg3 (I) int unused
6691: * --------------------------------------------------------------------------
6692: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
6693: * or NULL for any parsing error
6694: * (expression ptr unchanged if error occurs)
6695: * --------------------------------------------------------------------------
6696: * Notes: o
6697: * ======================================================================= */
6698: /* --- entry point --- */
6699: subraster *rastnewline ( char **expression, int size, subraster *basesp,
6700: int arg1, int arg2, int arg3 )
6701: {
6702: /* -------------------------------------------------------------------------
6703: Allocations and Declarations
6704: -------------------------------------------------------------------------- */
6705: subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
6706: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
6707: char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
6708: double strtod(); /* convert ascii param to double */
6709: int vspace = size+2; /* #pixels between lines */
6710: /* -------------------------------------------------------------------------
6711: obtain optional [vspace] argument immediately following \\ command
6712: -------------------------------------------------------------------------- */
6713: /* --- check if [vspace] given --- */
6714: if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/
6715: {
6716: /* ---parse [vspace] and bump expression past it, interpret as double--- */
6717: *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
6718: if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
6719: vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
6720: } /* --- end-of-if(*(*expression)=='[') --- */
6721: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
6722: /* -------------------------------------------------------------------------
6723: rasterize right half of expression and stack left half above it
6724: -------------------------------------------------------------------------- */
6725: /* --- rasterize right half --- */
6726: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
6727: == NULL ) goto end_of_job; /* quit if failed */
6728: /* --- stack left half above it --- */
1.2 ! albertel 6729: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
! 6730: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
1.1 albertel 6731: /* --- back to caller --- */
6732: end_of_job:
6733: if ( newlsp != NULL ) /* returning entire expression */
6734: { int newht = (newlsp->image)->height; /* height of returned subraster */
6735: newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
6736: isreplaceleft = 1; /* so set flag to replace left half*/
6737: *expression += strlen(*expression); } /* and push to terminating null*/
6738: return ( newlsp ); /* 1st line over 2nd, or null=error*/
6739: } /* --- end-of-function rastnewline() --- */
6740:
6741:
6742: /* ==========================================================================
6743: * Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 )
6744: * Purpose: returns left/right arrow subraster (e.g., for \longrightarrow)
6745: * --------------------------------------------------------------------------
6746: * Arguments: expression (I) char ** to first char of null-terminated
6747: * LaTeX expression (unused/unchanged)
6748: * size (I) int containing base font size (not used,
6749: * just stored in subraster)
6750: * basesp (I) subraster * to character (or subexpression)
6751: * immediately preceding space, whose baseline
6752: * and height params are transferred to space
6753: * drctn (I) int containing +1 for right, -1 for left,
6754: * or 0 for leftright
6755: * isBig (I) int containing 0 for ---> or 1 for ===>
6756: * arg3 (I) int unused
6757: * --------------------------------------------------------------------------
6758: * Returns: ( subraster * ) ptr to left/right arrow subraster
6759: * or NULL for any error
6760: * --------------------------------------------------------------------------
6761: * Notes: o An optional argument [width] may *immediately* follow
6762: * the \longxxx to explicitly set the arrow's width in pixels.
6763: * For example, \longrightarrow calculates a default width
6764: * (as usual in LaTeX), whereas \longrightarrow[50] explicitly
6765: * draws a 50-pixel long arrow. This can be used, e.g.,
6766: * to draw commutative diagrams in conjunction with
6767: * \array (and maybe with \stackrel and/or \relstack, too).
6768: * o In case you really want to render, say, [f]---->[g], just
6769: * use an intervening space, i.e., [f]\longrightarrow~[g].
6770: * In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
6771: * ======================================================================= */
6772: /* --- entry point --- */
6773: subraster *rastarrow ( char **expression, int size, subraster *basesp,
6774: int drctn, int isBig, int arg3 )
6775: {
6776: /* -------------------------------------------------------------------------
6777: Allocations and Declarations
6778: -------------------------------------------------------------------------- */
6779: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
6780: char *texsubexpr(), widtharg[256]; /* parse for optional [width] */
6781: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
6782: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
6783: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
6784: int delete_subraster(); /*free work areas in case of error*/
6785: double strtod(); /* convert ascii [width] to value */
6786: int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
6787: int islimits = 1; /*true to handle limits internally*/
6788: int limsize = size-1; /* font size for limits */
6789: int vspace = 1; /* #empty rows below arrow */
6790: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
6791: /* -------------------------------------------------------------------------
6792: construct longleft/rightarrow subraster, with limits, and return it to caller
6793: -------------------------------------------------------------------------- */
6794: /* --- check for optional width arg and replace default width --- */
6795: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
6796: { int widthval; /* test [width] before using it */
6797: *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
6798: widthval = /* convert [width] to integer */
6799: (int)((unitlength*strtod(widtharg,NULL))+0.5);
6800: if ( widthval>=2 && widthval<=600 ) /* sanity check */
6801: width = widthval; } /* replace deafault width */
6802: /* --- now parse for limits, and bump expression past it(them) --- */
6803: if ( islimits ) /* handling limits internally */
6804: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
6805: if ( *sub != '\000' ) /*have a subscript following arrow*/
6806: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
6807: if ( *super != '\000' ) /*have superscript following arrow*/
6808: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
6809: /* --- set height based on width --- */
6810: height = min2(17,max2(9,(width+2)/6)); /* height based on width */
6811: height = 1 + (height/2)*2; /* always force odd height */
6812: /* --- generate arrow subraster --- */
6813: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
6814: == NULL ) goto end_of_job; /* and quit if we failed */
6815: /* --- add space below arrow --- */
6816: if ( vspace > 0 ) /* if we have space below arrow */
6817: if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
6818: != NULL ) /* and if we succeeded */
6819: if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
6820: == NULL ) goto end_of_job; /* and quit if we failed */
6821: /* --- init arrow subraster parameters --- */
6822: arrowsp->size = size; /*propagate base font size forward*/
6823: arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */
6824: /* --- add limits above/below arrow, as necessary --- */
6825: if ( subsp != NULL ) /* stack subscript below arrow */
6826: if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
6827: == NULL ) goto end_of_job; /* quit if failed */
6828: if ( supsp != NULL ) /* stack superscript above arrow */
6829: if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
6830: == NULL ) goto end_of_job; /* quit if failed */
6831: /* --- return arrow (or NULL) to caller --- */
6832: end_of_job:
6833: return ( arrowsp );
6834: } /* --- end-of-function rastarrow() --- */
6835:
6836:
6837: /* ==========================================================================
6838: * Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 )
6839: * Purpose: returns an up/down arrow subraster (e.g., for \longuparrow)
6840: * --------------------------------------------------------------------------
6841: * Arguments: expression (I) char ** to first char of null-terminated
6842: * LaTeX expression (unused/unchanged)
6843: * size (I) int containing base font size (not used,
6844: * just stored in subraster)
6845: * basesp (I) subraster * to character (or subexpression)
6846: * immediately preceding space, whose baseline
6847: * and height params are transferred to space
6848: * drctn (I) int containing +1 for up, -1 for down,
6849: * or 0 for updown
6850: * isBig (I) int containing 0 for ---> or 1 for ===>
6851: * arg3 (I) int unused
6852: * --------------------------------------------------------------------------
6853: * Returns: ( subraster * ) ptr to up/down arrow subraster
6854: * or NULL for any error
6855: * --------------------------------------------------------------------------
6856: * Notes: o An optional argument [height] may *immediately* follow
6857: * the \longxxx to explicitly set the arrow's height in pixels.
6858: * For example, \longuparrow calculates a default height
6859: * (as usual in LaTeX), whereas \longuparrow[25] explicitly
6860: * draws a 25-pixel high arrow. This can be used, e.g.,
6861: * to draw commutative diagrams in conjunction with
6862: * \array (and maybe with \stackrel and/or \relstack, too).
6863: * o In case you really want to render, say, [f]---->[g], just
6864: * use an intervening space, i.e., [f]\longuparrow~[g].
6865: * In text use two spaces {\rm~[f]\longuparrow~~[g]}.
6866: * ======================================================================= */
6867: /* --- entry point --- */
6868: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
6869: int drctn, int isBig, int arg3 )
6870: {
6871: /* -------------------------------------------------------------------------
6872: Allocations and Declarations
6873: -------------------------------------------------------------------------- */
6874: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
6875: char *texsubexpr(), heightarg[256]; /* parse for optional [height] */
6876: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
6877: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
6878: subraster *rastcat(); /* cat superscript left, sub right */
6879: double strtod(); /* convert ascii [height] to value */
6880: int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
6881: int islimits = 1; /*true to handle limits internally*/
6882: int limsize = size-1; /* font size for limits */
6883: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
6884: /* -------------------------------------------------------------------------
6885: construct blank subraster, and return it to caller
6886: -------------------------------------------------------------------------- */
6887: /* --- check for optional height arg and replace default height --- */
6888: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
6889: { int heightval; /* test height before using it */
6890: *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
6891: heightval = /* convert [height] to integer */
6892: (int)((unitlength*strtod(heightarg,NULL))+0.5);
6893: if ( heightval>=2 && heightval<=600 ) /* sanity check */
6894: height = heightval; } /* replace deafault height */
6895: /* --- now parse for limits, and bump expression past it(them) --- */
6896: if ( islimits ) /* handling limits internally */
6897: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
6898: if ( *sub != '\000' ) /*have a subscript following arrow*/
6899: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
6900: if ( *super != '\000' ) /*have superscript following arrow*/
6901: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
6902: /* --- set width based on height --- */
6903: width = min2(17,max2(9,(height+2)/4)); /* width based on height */
6904: width = 1 + (width/2)*2; /* always force odd width */
6905: /* --- generate arrow subraster --- */
6906: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
6907: == NULL ) goto end_of_job; /* and quit if we failed */
6908: /* --- init arrow subraster parameters --- */
6909: arrowsp->size = size; /*propagate base font size forward*/
6910: arrowsp->baseline = height-1; /* set baseline at bottom of arrow */
6911: /* --- add limits above/below arrow, as necessary --- */
6912: if ( supsp != NULL ) /* cat superscript to left of arrow*/
6913: { int supht = (supsp->image)->height, /* superscript height */
6914: deltab = (1+abs(height-supht))/2; /* baseline difference to center */
6915: supsp->baseline = supht-1; /* force script baseline to bottom */
6916: if ( supht <= height ) /* arrow usually taller than script*/
6917: arrowsp->baseline -= deltab; /* so bottom of script goes here */
6918: else supsp->baseline -= deltab; /* else bottom of arrow goes here */
6919: if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
6920: == NULL ) goto end_of_job; } /* quit if failed */
6921: if ( subsp != NULL ) /* cat subscript to right of arrow */
6922: { int subht = (subsp->image)->height, /* subscript height */
6923: deltab = (1+abs(height-subht))/2; /* baseline difference to center */
6924: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
6925: subsp->baseline = subht-1; /* force script baseline to bottom */
6926: if ( subht <= height ) /* arrow usually taller than script*/
6927: arrowsp->baseline -= deltab; /* so bottom of script goes here */
6928: else subsp->baseline -= deltab; /* else bottom of arrow goes here */
6929: if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
6930: == NULL ) goto end_of_job; } /* quit if failed */
6931: /* --- return arrow (or NULL) to caller --- */
6932: end_of_job:
6933: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
6934: return ( arrowsp );
6935: } /* --- end-of-function rastuparrow() --- */
6936:
6937:
6938: /* ==========================================================================
6939: * Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3)
6940: * Purpose: overlays one raster on another
6941: * --------------------------------------------------------------------------
6942: * Arguments: expression (I/O) char ** to first char of null-terminated
6943: * string immediately following overlay \cmd to
6944: * be rasterized, and returning ptr immediately
6945: * following last character processed.
6946: * size (I) int containing 0-5 default font size
6947: * basesp (I) subraster * to character (or subexpression)
6948: * immediately preceding overlay \cmd
6949: * (unused, but passed for consistency)
6950: * overlay (I) int containing 1 to overlay / (e.g., \not)
6951: * or NOVALUE to pick up 2nd arg from expression
6952: * offset2 (I) int containing #pixels to horizontally offset
6953: * overlay relative to underlying symbol,
6954: * positive(right) or negative or 0,
6955: * or NOVALUE to pick up optional [offset] arg
6956: * arg3 (I) int unused
6957: * --------------------------------------------------------------------------
6958: * Returns: ( subraster * ) ptr to subraster corresponding to composite,
6959: * or NULL for any parsing error
6960: * --------------------------------------------------------------------------
6961: * Notes: o
6962: * ======================================================================= */
6963: /* --- entry point --- */
6964: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
6965: int overlay, int offset2, int arg3 )
6966: {
6967: /* -------------------------------------------------------------------------
6968: Allocations and Declarations
6969: -------------------------------------------------------------------------- */
6970: char *texsubexpr(), /*parse expression for base,overlay*/
6971: expr1[512], expr2[512]; /* base, overlay */
6972: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
6973: *new_subraster(); /*explicitly alloc sp2 if necessary*/
6974: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
6975: int line_raster(); /* draw diagonal for \Not */
6976: /* -------------------------------------------------------------------------
6977: Obtain base, and maybe overlay, and rasterize them
6978: -------------------------------------------------------------------------- */
6979: /* --- check for optional offset2 arg --- */
6980: if ( offset2 == NOVALUE ) /* only if not explicitly specified*/
6981: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
6982: { int offsetval; /* test before using it */
6983: *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
6984: offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
6985: if ( abs(offsetval) <= 25 ) /* sanity check */
6986: offset2 = offsetval; } /* replace deafault */
6987: if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
6988: /* --- parse for base, bump expression past it, and rasterize it --- */
6989: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
6990: if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
6991: if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
6992: == NULL ) goto end_of_job; /* quit if failed to rasterize */
6993: overlaysp = sp1; /*in case we return with no overlay*/
6994: /* --- get overlay expression, and rasterize it --- */
6995: if ( overlay == NOVALUE ) /* get overlay from input stream */
6996: { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
6997: if ( *expr2 != '\000' ) /* have an overlay */
6998: sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */
6999: else /* specific overlay */
7000: switch ( overlay )
7001: {
7002: default: break;
7003: case 1: /* e.g., \not overlays slash */
7004: sp2 = rasterize("/",size+1); /* rasterize overlay expression */
7005: offset2 = max2(1,size-3); /* push / right a bit */
7006: offset2 = 0;
7007: break;
7008: case 2: /* e.g., \Not draws diagonal */
7009: sp2 = NULL; /* no overlay required */
7010: if ( overlaysp != NULL ) /* check that we have raster */
7011: { raster *rp = overlaysp->image; /* raster to be \Not-ed */
7012: int width=rp->width, height=rp->height; /* raster dimensions */
7013: if ( 0 ) /* diagonal within bounding box */
7014: line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
7015: else /* construct "wide" diagonal */
7016: { int margin=3; /* desired extra margin width */
7017: sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
7018: if ( sp2 != NULL ) /* allocated successfully */
7019: line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
7020: break;
7021: case 3: /* e.g., \sout for strikeout */
7022: sp2 = NULL; /* no overlay required */
7023: if ( overlaysp != NULL ) /* check that we have raster */
7024: { raster *rp = overlaysp->image; /* raster to be \Not-ed */
7025: int width=rp->width, height=rp->height; /* raster dimensions */
7026: int baseline = overlaysp->baseline; /* we'll ignore descenders */
7027: int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
7028: if ( 1 ) /* strikeout within bounding box */
7029: line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
7030: break;
7031: } /* --- end-of-switch(overlay) --- */
7032: if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/
7033: /* -------------------------------------------------------------------------
7034: construct composite overlay
7035: -------------------------------------------------------------------------- */
7036: overlaysp = rastcompose(sp1,sp2,offset2,0,3);
7037: end_of_job:
7038: return ( overlaysp );
7039: } /* --- end-of-function rastoverlay() --- */
7040:
7041:
7042: /* ==========================================================================
7043: * Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 )
7044: * Purpose: \frac,\atop handler, returns a subraster corresponding to
7045: * expression (immediately following \frac,\atop) at font size
7046: * --------------------------------------------------------------------------
7047: * Arguments: expression (I/O) char ** to first char of null-terminated
7048: * string immediately following \frac to be
7049: * rasterized, and returning ptr immediately
7050: * following last character processed.
7051: * size (I) int containing 0-5 default font size
7052: * basesp (I) subraster * to character (or subexpression)
7053: * immediately preceding \frac
7054: * (unused, but passed for consistency)
7055: * isfrac (I) int containing true to draw horizontal line
7056: * between numerator and denominator,
7057: * or false not to draw it (for \atop).
7058: * arg2 (I) int unused
7059: * arg3 (I) int unused
7060: * --------------------------------------------------------------------------
7061: * Returns: ( subraster * ) ptr to subraster corresponding to fraction,
7062: * or NULL for any parsing error
7063: * --------------------------------------------------------------------------
7064: * Notes: o
7065: * ======================================================================= */
7066: /* --- entry point --- */
7067: subraster *rastfrac ( char **expression, int size, subraster *basesp,
7068: int isfrac, int arg2, int arg3 )
7069: {
7070: /* -------------------------------------------------------------------------
7071: Allocations and Declarations
7072: -------------------------------------------------------------------------- */
7073: char *texsubexpr(), /*parse expression for numer,denom*/
7074: numer[8192], denom[8192]; /*numer,denom parsed from expression*/
7075: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
7076: subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */
7077: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
7078: int width=0, /* width of constructed raster */
7079: numheight=0; /* height of numerator */
7080: int baseht=0, baseln=0; /* height,baseline of base symbol */
7081: /*int istweak = 1;*/ /*true to tweak baseline alignment*/
7082: int rule_raster(), /* draw horizontal line for frac */
7083: lineheight = 1; /* thickness of fraction line */
1.2 ! albertel 7084: int vspace = (size>2?2:1); /*vertical space between components*/
1.1 albertel 7085: int delete_subraster(); /*free work areas in case of error*/
7086: int type_raster(); /* display debugging output */
7087: /* -------------------------------------------------------------------------
7088: Obtain numerator and denominator, and rasterize them
7089: -------------------------------------------------------------------------- */
7090: /* --- parse for numerator,denominator and bump expression past them --- */
7091: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
7092: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
7093: if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
7094: goto end_of_job; /* nothing to do, so quit */
7095: /* --- rasterize numerator, denominator --- */
7096: if ( *numer != '\000' ) /* have a numerator */
7097: if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
7098: == NULL ) goto end_of_job; /* and quit if failed */
7099: if ( *denom != '\000' ) /* have a denominator */
7100: if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
7101: == NULL ) /* failed */
7102: { if ( numsp != NULL ) /* already rasterized numerator */
7103: delete_subraster(numsp); /* so free now-unneeded numerator */
7104: goto end_of_job; } /* and quit */
7105: /* --- if one componenet missing, use a blank space for it --- */
7106: if ( numsp == NULL ) /* no numerator given */
7107: numsp = rasterize("[?]",size-1); /* missing numerator */
7108: if ( densp == NULL ) /* no denominator given */
7109: densp = rasterize("[?]",size-1); /* missing denominator */
7110: /* --- check that we got both components --- */
7111: if ( numsp==NULL || densp==NULL ) /* some problem */
7112: { delete_subraster(numsp); /*delete numerator (if it existed)*/
7113: delete_subraster(densp); /*delete denominator (if it existed)*/
7114: goto end_of_job; } /* and quit */
7115: /* --- get height of numerator (to determine where line belongs) --- */
7116: numheight = (numsp->image)->height; /* get numerator's height */
7117: /* -------------------------------------------------------------------------
7118: construct raster with numerator stacked over denominator
7119: -------------------------------------------------------------------------- */
7120: /* --- construct raster with numer/denom --- */
7121: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
7122: == NULL ) /* failed to construct numer/denom */
7123: { delete_subraster(numsp); /* so free now-unneeded numerator */
7124: delete_subraster(densp); /* and now-unneeded denominator */
7125: goto end_of_job; } /* and then quit */
7126: /* --- determine width of constructed raster --- */
7127: width = (fracsp->image)->width; /*just get width of embedded image*/
7128: /* --- initialize subraster parameters --- */
7129: fracsp->size = size; /* propagate font size forward */
7130: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
7131: if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */
7132: { baseht = (basesp->image)->height; /* height of base symbol */
7133: baseln = basesp->baseline; /* and its baseline */
7134: } /* --- end-of-if(basesp!=NULL) --- */
7135: /* -------------------------------------------------------------------------
7136: draw horizontal line between numerator and denominator
7137: -------------------------------------------------------------------------- */
7138: if ( isfrac ) /*line for \frac, but not for \atop*/
7139: rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0);
7140: /* -------------------------------------------------------------------------
7141: return final result to caller
7142: -------------------------------------------------------------------------- */
7143: end_of_job:
7144: if ( msgfp!=NULL && msglevel>=99 )
7145: { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
7146: if ( fracsp != NULL ) /* have a constructed raster */
7147: type_raster(fracsp->image,msgfp); } /* display constructed raster */
7148: return ( fracsp );
7149: } /* --- end-of-function rastfrac() --- */
7150:
7151:
7152: /* ==========================================================================
7153: * Function: rastackrel ( expression, size, basesp, base, arg2, arg3 )
7154: * Purpose: \stackrel handler, returns a subraster corresponding to
7155: * stacked relation
7156: * --------------------------------------------------------------------------
7157: * Arguments: expression (I/O) char ** to first char of null-terminated
7158: * string immediately following \stackrel to be
7159: * rasterized, and returning ptr immediately
7160: * following last character processed.
7161: * size (I) int containing 0-4 default font size
7162: * basesp (I) subraster * to character (or subexpression)
7163: * immediately preceding \stackrel
7164: * (unused, but passed for consistency)
7165: * base (I) int containing 1 if upper/first subexpression
7166: * is base relation, or 2 if lower/second is
7167: * arg2 (I) int unused
7168: * arg3 (I) int unused
7169: * --------------------------------------------------------------------------
7170: * Returns: ( subraster * ) ptr to subraster corresponding to stacked
7171: * relation, or NULL for any parsing error
7172: * --------------------------------------------------------------------------
7173: * Notes: o
7174: * ======================================================================= */
7175: /* --- entry point --- */
7176: subraster *rastackrel ( char **expression, int size, subraster *basesp,
7177: int base, int arg2, int arg3 )
7178: {
7179: /* -------------------------------------------------------------------------
7180: Allocations and Declarations
7181: -------------------------------------------------------------------------- */
7182: char *texsubexpr(), /*parse expression for numer,denom*/
7183: upper[8192], lower[8192]; /*upper,lower parsed from expression*/
7184: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
7185: subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */
7186: int upsize = (base==1? size:size-1), /* font size for upper component */
7187: lowsize = (base==2? size:size-1); /* font size for lower component */
7188: int vspace = 1; /*vertical space between components*/
7189: int delete_subraster(); /*free work areas in case of error*/
7190: /* -------------------------------------------------------------------------
7191: Obtain numerator and denominator, and rasterize them
7192: -------------------------------------------------------------------------- */
7193: /* --- parse for numerator,denominator and bump expression past them --- */
7194: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
7195: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
7196: if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
7197: goto end_of_job; /* nothing to do, so quit */
7198: /* --- rasterize upper, lower --- */
7199: if ( *upper != '\000' ) /* have upper component */
7200: if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */
7201: == NULL ) goto end_of_job; /* and quit if failed */
7202: if ( *lower != '\000' ) /* have lower component */
7203: if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
7204: == NULL ) /* failed */
7205: { if ( upsp != NULL ) /* already rasterized upper */
7206: delete_subraster(upsp); /* so free now-unneeded upper */
7207: goto end_of_job; } /* and quit */
7208: /* -------------------------------------------------------------------------
7209: construct stacked relation raster
7210: -------------------------------------------------------------------------- */
7211: /* --- construct stacked relation --- */
7212: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
7213: == NULL ) goto end_of_job; /* quit if failed */
7214: /* --- initialize subraster parameters --- */
7215: relsp->size = size; /* propagate font size forward */
7216: /* -------------------------------------------------------------------------
7217: return final result to caller
7218: -------------------------------------------------------------------------- */
7219: end_of_job:
7220: return ( relsp );
7221: } /* --- end-of-function rastackrel() --- */
7222:
7223:
7224: /* ==========================================================================
7225: * Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 )
7226: * Purpose: \log, \lim, etc handler, returns a subraster corresponding
7227: * to math functions
7228: * --------------------------------------------------------------------------
7229: * Arguments: expression (I/O) char ** to first char of null-terminated
7230: * string immediately following \mathfunc to be
7231: * rasterized, and returning ptr immediately
7232: * following last character processed.
7233: * size (I) int containing 0-4 default font size
7234: * basesp (I) subraster * to character (or subexpression)
7235: * immediately preceding \mathfunc
7236: * (unused, but passed for consistency)
7237: * mathfunc (I) int containing 1=arccos, 2=arcsin, etc.
7238: * islimits (I) int containing 1 if function may have
7239: * limits underneath, e.g., \lim_{n\to\infty}
7240: * arg3 (I) int unused
7241: * --------------------------------------------------------------------------
7242: * Returns: ( subraster * ) ptr to subraster corresponding to mathfunc,
7243: * or NULL for any parsing error
7244: * --------------------------------------------------------------------------
7245: * Notes: o
7246: * ======================================================================= */
7247: /* --- entry point --- */
7248: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
7249: int mathfunc, int islimits, int arg3 )
7250: {
7251: /* -------------------------------------------------------------------------
7252: Allocations and Declarations
7253: -------------------------------------------------------------------------- */
7254: char *texscripts(), /* parse expression for _limits */
7255: func[4096], limits[8192]; /* func as {\rm func}, limits */
7256: char *texsubexpr(), /* parse expression for arg */
7257: funcarg[2048]; /* optional func arg */
7258: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
7259: subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
7260: int limsize = size-1; /* font size for limits */
7261: int vspace = 1; /*vertical space between components*/
7262: int delete_subraster(); /*free work areas in case of error*/
7263: /* --- table of function names by mathfunc number --- */
7264: static int numnames = 34; /* number of names in table */
7265: static char *funcnames[] = {
7266: "error", /* 0 index is illegal/error bucket*/
7267: "arccos", "arcsin", "arctan", /* 1 - 3 */
7268: "arg", "cos", "cosh", /* 4 - 6 */
7269: "cot", "coth", "csc", /* 7 - 9 */
7270: "deg", "det", "dim", /* 10 - 12 */
7271: "exp", "gcd", "hom", /* 13 - 15 */
7272: "inf", "ker", "lg", /* 16 - 18 */
7273: "lim", "liminf", "limsup", /* 19 - 21 */
7274: "ln", "log", "max", /* 22 - 24 */
7275: "min", "Pr", "sec", /* 25 - 27 */
7276: "sin", "sinh", "sup", /* 28 - 30 */
7277: "tan", "tanh", /* 31 - 32 */
7278: /* --- extra mimetex funcnames --- */
7279: "tr", /* 33 */
7280: "pmod" /* 34 */
7281: } ;
7282: /* -------------------------------------------------------------------------
7283: set up and rasterize function name in \rm
7284: -------------------------------------------------------------------------- */
7285: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
7286: switch ( mathfunc ) /* check for special processing */
7287: {
7288: default: /* no special processing */
7289: strcpy(func,"{\\rm~"); /* init string with {\rm~ */
7290: strcat(func,funcnames[mathfunc]); /* concat function name */
7291: strcat(func,"}"); /* and add terminating } */
7292: break;
7293: case 34: /* \pmod{x} --> (mod x) */
7294: /* --- parse for \pmod{arg} argument --- */
7295: *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
7296: strcpy(func,"{\\({\\rm~mod}"); /* init with {\left({\rm~mod} */
7297: strcat(func,"\\hspace2"); /* concat space */
7298: strcat(func,funcarg); /* and \pmodargument */
7299: strcat(func,"\\)}"); /* and add terminating \right)} */
7300: break;
7301: } /* --- end-of-switch(mathfunc) --- */
7302: if ( (funcsp = rasterize(func,size)) /* rasterize function name */
7303: == NULL ) goto end_of_job; /* and quit if failed */
7304: mathfuncsp = funcsp; /* just return funcsp if no limits */
7305: if ( !islimits ) goto end_of_job; /* treat any subscript normally */
7306: /* -------------------------------------------------------------------------
7307: Obtain limits, if permitted and if provided, and rasterize them
7308: -------------------------------------------------------------------------- */
7309: /* --- parse for subscript limits, and bump expression past it(them) --- */
7310: *expression = texscripts(*expression,limits,limits,1);
7311: if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */
7312: /* --- rasterize limits --- */
7313: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
7314: == NULL ) goto end_of_job; /* and quit if failed */
7315: /* -------------------------------------------------------------------------
7316: construct func atop limits
7317: -------------------------------------------------------------------------- */
7318: /* --- construct func atop limits --- */
7319: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
7320: == NULL ) goto end_of_job; /* quit if failed */
7321: /* --- initialize subraster parameters --- */
7322: mathfuncsp->size = size; /* propagate font size forward */
7323: /* -------------------------------------------------------------------------
7324: return final result to caller
7325: -------------------------------------------------------------------------- */
7326: end_of_job:
7327: return ( mathfuncsp );
7328: } /* --- end-of-function rastmathfunc() --- */
7329:
7330:
7331: /* ==========================================================================
7332: * Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 )
7333: * Purpose: \sqrt handler, returns a subraster corresponding to
7334: * expression (immediately following \sqrt) at font size
7335: * --------------------------------------------------------------------------
7336: * Arguments: expression (I/O) char ** to first char of null-terminated
7337: * string immediately following \sqrt to be
7338: * rasterized, and returning ptr immediately
7339: * following last character processed.
7340: * size (I) int containing 0-4 default font size
7341: * basesp (I) subraster * to character (or subexpression)
7342: * immediately preceding \accent
7343: * (unused, but passed for consistency)
7344: * arg1 (I) int unused
7345: * arg2 (I) int unused
7346: * arg3 (I) int unused
7347: * --------------------------------------------------------------------------
7348: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
7349: * or NULL for any parsing error
7350: * (expression ptr unchanged if error occurs)
7351: * --------------------------------------------------------------------------
7352: * Notes: o
7353: * ======================================================================= */
7354: /* --- entry point --- */
7355: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
7356: int arg1, int arg2, int arg3 )
7357: {
7358: /* -------------------------------------------------------------------------
7359: Allocations and Declarations
7360: -------------------------------------------------------------------------- */
7361: char *texsubexpr(), subexpr[8192], /* parse subexpr to be sqrt-ed */
7362: rootarg[8192]; /* optional \sqrt[rootarg]{...} */
7363: subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */
7364: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
7365: *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
7366: int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
7367: rootheight=0, rootwidth=0, /* height,width of rootarg raster */
7368: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
7369: int rastput(); /* put subexpr in constructed sqrt */
7370: int overspace = 2; /*space between subexpr and overbar*/
7371: int delete_subraster(); /* free work areas */
7372: /* -------------------------------------------------------------------------
7373: Obtain subexpression to be sqrt-ed, and rasterize it
7374: -------------------------------------------------------------------------- */
7375: /* --- first check for optional \sqrt[rootarg]{...} --- */
7376: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
7377: { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
7378: if ( *rootarg != '\000' ) /* got rootarg */
7379: if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
7380: != NULL ) /* rasterized successfully */
7381: { rootheight = (rootsp->image)->height; /* get height of rootarg */
7382: rootwidth = (rootsp->image)->width; } /* and its width */
7383: } /* --- end-of-if(**expression=='[') --- */
7384: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
7385: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
7386: if ( *subexpr == '\000' ) /* couldn't get subexpression */
7387: goto end_of_job; /* nothing to do, so quit */
7388: /* --- rasterize subexpression to be accented --- */
7389: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
7390: == NULL ) goto end_of_job; /* quit if failed */
7391: /* -------------------------------------------------------------------------
7392: determine height and width of sqrt raster to be constructed
7393: -------------------------------------------------------------------------- */
7394: /* --- first get height and width of subexpr --- */
7395: subheight = (subsp->image)->height; /* height of subexpr */
7396: subwidth = (subsp->image)->width; /* and its width */
7397: pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
7398: /* --- determine height and width of sqrt to contain subexpr --- */
7399: sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
7400: surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */
7401: sqrtwidth = subwidth + surdwidth + 1; /* total width */
7402: /* -------------------------------------------------------------------------
7403: construct sqrt (with room to move in subexpr) and embed subexpr in it
7404: -------------------------------------------------------------------------- */
7405: /* --- construct sqrt --- */
7406: if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
7407: == NULL ) goto end_of_job; /* quit if failed to build sqrt */
7408: /* --- embed subexpr in sqrt at lower-right corner--- */
7409: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
7410: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
7411: /* --- "embed" rootarg at upper-left --- */
7412: if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/
7413: {
7414: /* --- allocate full raster to contain sqrtsp and rootsp --- */
7415: int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
7416: fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
7417: subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
7418: if ( fullsp != NULL ) /* allocated successfully */
7419: { /* --- embed sqrtsp exactly at lower-right corner --- */
7420: rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
7421: fullheight-sqrtheight,fullwidth-sqrtwidth,1);
7422: /* --- embed rootsp near upper-left, nestled above leading surd --- */
7423: rastput(fullsp->image,rootsp->image,
7424: 0,max2(0,surdwidth-rootwidth-2-size),0);
7425: /* --- replace sqrtsp with fullsp --- */
7426: delete_subraster(sqrtsp); /* free original sqrtsp */
7427: sqrtsp = fullsp; /* and repoint it to fullsp instead*/
7428: sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
7429: } /* --- end-of-if(rootsp!=NULL) --- */
7430: /* --- initialize subraster parameters --- */
7431: sqrtsp->size = size; /* propagate font size forward */
7432: /* -------------------------------------------------------------------------
7433: free unneeded component subrasters and return final result to caller
7434: -------------------------------------------------------------------------- */
7435: end_of_job:
7436: if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
7437: return ( sqrtsp );
7438: } /* --- end-of-function rastsqrt() --- */
7439:
7440:
7441: /* ==========================================================================
7442: * Function: rastaccent (expression,size,basesp,accent,isabove,isscript)
7443: * Purpose: \hat, \vec, \etc handler, returns a subraster corresponding
7444: * to expression (immediately following \accent) at font size
7445: * --------------------------------------------------------------------------
7446: * Arguments: expression (I/O) char ** to first char of null-terminated
7447: * string immediately following \accent to be
7448: * rasterized, and returning ptr immediately
7449: * following last character processed.
7450: * size (I) int containing 0-4 default font size
7451: * basesp (I) subraster * to character (or subexpression)
7452: * immediately preceding \accent
7453: * (unused, but passed for consistency)
7454: * accent (I) int containing HATACCENT or VECACCENT, etc,
7455: * between numerator and denominator,
7456: * or false not to draw it (for \over).
7457: * isabove (I) int containing true if accent is above
7458: * expression to be accented, or false
7459: * if accent is below (e.g., underbrace)
7460: * isscript (I) int containing true if sub/superscripts
7461: * allowed (for under/overbrace), or 0 if not.
7462: * --------------------------------------------------------------------------
7463: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
7464: * or NULL for any parsing error
7465: * (expression ptr unchanged if error occurs)
7466: * --------------------------------------------------------------------------
7467: * Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way
7468: * of isabove and isscript args.
7469: * ======================================================================= */
7470: /* --- entry point --- */
7471: subraster *rastaccent ( char **expression, int size, subraster *basesp,
7472: int accent, int isabove, int isscript )
7473: {
7474: /* -------------------------------------------------------------------------
7475: Allocations and Declarations
7476: -------------------------------------------------------------------------- */
7477: char *texsubexpr(), subexpr[8192]; /* parse subexpr to be accented */
7478: char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
7479: subscript[512], supscript[512]; /* scripts parsed from expression */
7480: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
7481: subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
7482: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
7483: int accheight=0, accwidth=0, /* height, width of accent */
7484: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
7485: int delete_subraster(); /*free work areas in case of error*/
7486: int vspace = 0; /*vertical space between accent,sub*/
7487: /* -------------------------------------------------------------------------
7488: Obtain subexpression to be accented, and rasterize it
7489: -------------------------------------------------------------------------- */
7490: /* --- parse for subexpr to be accented, and bump expression past it --- */
7491: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
7492: if ( *subexpr=='\000' ) /* couldn't get subexpression */
7493: goto end_of_job; /* nothing to do, so quit */
7494: /* --- rasterize subexpression to be accented --- */
7495: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
7496: == NULL ) goto end_of_job; /* quit if failed */
7497: /* -------------------------------------------------------------------------
7498: determine desired accent width and height
7499: -------------------------------------------------------------------------- */
7500: /* --- first get height and width of subexpr --- */
7501: subheight = (subsp->image)->height; /* height of subexpr */
7502: subwidth = (subsp->image)->width; /* and its width is overall width */
7503: pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
7504: /* --- determine desired width, height of accent --- */
7505: accwidth = subwidth; /* same width as subexpr */
1.2 ! albertel 7506: accheight = 4; /* default for bars */
1.1 albertel 7507: switch ( accent )
7508: { default: break; /* default okay */
7509: case DOTACCENT: case DDOTACCENT:
1.2 ! albertel 7510: accheight = (size<4? 3:4); /* default for dots */
1.1 albertel 7511: break;
1.2 ! albertel 7512: case VECACCENT:
! 7513: vspace = 1; /* set 1-pixel vertical space */
! 7514: case HATACCENT:
1.1 albertel 7515: accheight = 7; /* default */
7516: if ( subwidth < 10 ) accheight = 5; /* unless small width */
7517: else if ( subwidth > 25 ) accheight = 9; /* or large */
7518: break;
7519: } /* --- end-of-switch(accent) --- */
7520: accheight = min2(accheight,subheight); /*never higher than accented subexpr*/
7521: /* -------------------------------------------------------------------------
7522: construct accent, and construct subraster with accent over (or under) subexpr
7523: -------------------------------------------------------------------------- */
7524: /* --- first construct accent --- */
7525: if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
7526: == NULL ) goto end_of_job; /* quit if failed to build accent */
7527: /* --- now stack accent above (or below) subexpr, and free both args --- */
7528: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
7529: : rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/
7530: if ( accsubsp == NULL ) /* failed to stack accent */
7531: { delete_subraster(subsp); /* free unneeded subsp */
7532: delete_subraster(accsp); /* and unneeded accsp */
7533: goto end_of_job; } /* and quit */
7534: /* -------------------------------------------------------------------------
7535: look for super/subscript (annotation for over/underbrace)
7536: -------------------------------------------------------------------------- */
7537: /* --- first check whether accent permits accompanying annotations --- */
7538: if ( !isscript ) goto end_of_job; /* no annotations for this accent */
7539: /* --- now get scripts if there actually are any --- */
7540: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
7541: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
7542: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
7543: /* --- rasterize script annotation at size-2 --- */
7544: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
7545: == NULL ) goto end_of_job; /* quit if failed */
7546: /* --- stack annotation above (or below) accent, and free both args --- */
7547: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
7548: : rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */
7549: /* -------------------------------------------------------------------------
7550: return final result to caller
7551: -------------------------------------------------------------------------- */
7552: end_of_job:
7553: if ( accsubsp != NULL ) /* initialize subraster parameters */
7554: accsubsp->size = size; /* propagate font size forward */
7555: return ( accsubsp );
7556: } /* --- end-of-function rastaccent() --- */
7557:
7558:
7559: /* ==========================================================================
1.2 ! albertel 7560: * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
1.1 albertel 7561: * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
7562: * to char(s) within {}'s rendered at size
7563: * --------------------------------------------------------------------------
7564: * Arguments: expression (I/O) char ** to first char of null-terminated
7565: * string immediately following \font to be
7566: * rasterized, and returning ptr immediately
7567: * following last character processed.
7568: * size (I) int containing 0-5 default font size
7569: * basesp (I) subraster * to character (or subexpression)
7570: * immediately preceding \accent
7571: * (unused, but passed for consistency)
1.2 ! albertel 7572: * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
1.1 albertel 7573: * arg2 (I) int unused
7574: * arg3 (I) int unused
7575: * --------------------------------------------------------------------------
7576: * Returns: ( subraster * ) ptr to subraster corresponding to chars
7577: * between {}'s, or NULL for any parsing error
7578: * --------------------------------------------------------------------------
7579: * Notes: o
7580: * ======================================================================= */
7581: /* --- entry point --- */
7582: subraster *rastfont ( char **expression, int size, subraster *basesp,
1.2 ! albertel 7583: int ifontnum, int arg2, int arg3 )
1.1 albertel 7584: {
7585: /* -------------------------------------------------------------------------
7586: Allocations and Declarations
7587: -------------------------------------------------------------------------- */
7588: char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/
7589: subexpr[8192]; /* turn \cal{AB} into \calA\calB */
7590: char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
1.2 ! albertel 7591: char *name = NULL; /* fontinfo[ifontnum].name */
! 7592: int family = 0, /* fontinfo[ifontnum].family */
! 7593: istext = 0, /* fontinfo[ifontnum].istext */
! 7594: class = 0; /* fontinfo[ifontnum].class */
1.1 albertel 7595: subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
7596: *rastflags(); /* or just set flag to switch font */
1.2 ! albertel 7597: int oldsmashmargin = smashmargin; /* turn off smash in text mode */
! 7598: #if 0
1.1 albertel 7599: /* --- fonts recognized by rastfont --- */
1.2 ! albertel 7600: static int nfonts = 6; /* legal font #'s are 1...nfonts */
1.1 albertel 7601: static struct {char *name; int class;}
7602: fonts[] =
7603: { /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
1.2 ! albertel 7604: { "\\math", 0 },
! 7605: { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
! 7606: { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
! 7607: { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
! 7608: { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
! 7609: { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
! 7610: { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
1.1 albertel 7611: { NULL, 0 }
7612: } ; /* --- end-of-fonts[] --- */
1.2 ! albertel 7613: #endif
1.1 albertel 7614: /* -------------------------------------------------------------------------
7615: first get font name and class to determine type of conversion desired
7616: -------------------------------------------------------------------------- */
1.2 ! albertel 7617: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
! 7618: name = fontinfo[ifontnum].name; /* font name */
! 7619: family = fontinfo[ifontnum].family; /* font family */
! 7620: istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
! 7621: class = fontinfo[ifontnum].class; /* font class */
! 7622: if ( istext ) /* text (respect blanks) */
! 7623: smashmargin = 0; /* don't smash internal blanks */
1.1 albertel 7624: /* -------------------------------------------------------------------------
7625: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
7626: -------------------------------------------------------------------------- */
1.2 ! albertel 7627: if ( 1 || class<0 ) /* not character-by-character */
1.1 albertel 7628: {
7629: /* ---
7630: if \font not immediately followed by { then it has no arg, so just set flag
7631: ------------------------------------------------------------------------ */
7632: if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
7633: {
7634: if ( msgfp!=NULL && msglevel>=99 )
1.2 ! albertel 7635: fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
! 7636: fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
1.1 albertel 7637: goto end_of_job;
7638: } /* --- end-of-if(*(*expression)!='{') --- */
7639: /* ---
7640: convert \font{abc} --> {\font~abc}
7641: ---------------------------------- */
7642: /* --- parse for {fontchars} arg, and bump expression past it --- */
7643: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
7644: if ( msgfp!=NULL && msglevel>=99 )
7645: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
7646: /* --- convert all fontchars at the same time --- */
7647: strcpy(subexpr,"{"); /* start off with opening { */
7648: strcat(subexpr,name); /* followed by font name */
7649: strcat(subexpr,"~"); /* followed by whitespace */
7650: strcat(subexpr,fontchars); /* followed by all the chars */
7651: strcat(subexpr,"}"); /* terminate with closing } */
7652: } /* --- end-of-if(class<0) --- */
7653: else /* character-by-character */
7654: {
7655: /* ---
7656: convert ABC to \calA\calB\calC
7657: ------------------------------ */
7658: int isprevchar=0; /* true if prev char converted */
7659: /* --- parse for {fontchars} arg, and bump expression past it --- */
7660: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
7661: if ( msgfp!=NULL && msglevel>=99 )
7662: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
7663: /* --- convert fontchars one at a time --- */
7664: strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */
7665: strcpy(subexpr,"{"); /* nope, just start off with { */
7666: for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
7667: {
7668: if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
1.2 ! albertel 7669: { if ( 0 || istext ) /* and we're in a text mode font */
1.1 albertel 7670: strcat(subexpr,"\\;"); } /* so respect whitespace */
7671: else /* char to be displayed in font */
7672: { int exprlen = 0; /* #chars in subexpr before fchar */
7673: int isinclass = 0; /* set true if fchar in font class */
1.2 ! albertel 7674: /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
1.1 albertel 7675: switch ( class ) /* check if fchar is in font class */
7676: { default: break; /* no chars in unrecognized class */
7677: case 1: if ( isupper((int)fchar) ) isinclass=1; break;
7678: case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
7679: case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
7680: case 4: if ( islower((int)fchar) ) isinclass=1; break;
7681: case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
7682: case 9: isinclass=1; break; }
7683: if ( isinclass ) /* convert current char to \font */
7684: { strcat(subexpr,name); /* by prefixing it with font name */
7685: isprevchar = 1; } /* and set flag to signal separator*/
7686: else /* current char not in \font */
7687: { if ( isprevchar ) /* extra separator only after \font*/
7688: if ( isalpha(fchar) ) /* separator only before alpha */
7689: strcat(subexpr,"~"); /* need separator after \font */
7690: isprevchar = 0; } /* reset flag for next char */
7691: exprlen = strlen(subexpr); /* #chars so far */
7692: subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/
7693: subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */
7694: } /* --- end-of-for(pfchars) --- */
7695: strcat(subexpr,"}"); /* add closing } */
7696: } /* --- end-of-if/else(class<0) --- */
7697: /* -------------------------------------------------------------------------
7698: rasterize subexpression containing chars to be rendered at font
7699: -------------------------------------------------------------------------- */
7700: if ( msgfp!=NULL && msglevel>=99 )
7701: fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
7702: if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
7703: == NULL ) goto end_of_job; /* and quit if failed */
7704: /* -------------------------------------------------------------------------
7705: back to caller with chars rendered in font
7706: -------------------------------------------------------------------------- */
7707: end_of_job:
1.2 ! albertel 7708: smashmargin = oldsmashmargin; /* restore smash */
! 7709: if ( istext && fontsp!=NULL ) /* raster contains text mode font */
! 7710: fontsp->type = blanksignal; /* signal nosmash */
1.1 albertel 7711: return ( fontsp ); /* chars rendered in font */
7712: } /* --- end-of-function rastfont() --- */
7713:
7714:
7715: /* ==========================================================================
7716: * Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
7717: * Purpose: \begin{}...\end{} handler, returns a subraster corresponding
7718: * to array expression within environment, i.e., rewrites
7719: * \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
7720: * --------------------------------------------------------------------------
7721: * Arguments: expression (I/O) char ** to first char of null-terminated
7722: * string immediately following \begin to be
7723: * rasterized, and returning ptr immediately
7724: * following last character processed.
7725: * size (I) int containing 0-4 default font size
7726: * basesp (I) subraster * to character (or subexpression)
7727: * immediately preceding \begin
7728: * (unused, but passed for consistency)
7729: * arg1 (I) int unused
7730: * arg2 (I) int unused
7731: * arg3 (I) int unused
7732: * --------------------------------------------------------------------------
7733: * Returns: ( subraster * ) ptr to subraster corresponding to array
7734: * expression, or NULL for any parsing error
7735: * --------------------------------------------------------------------------
7736: * Notes: o
7737: * ======================================================================= */
7738: /* --- entry point --- */
7739: subraster *rastbegin ( char **expression, int size, subraster *basesp,
7740: int arg1, int arg2, int arg3 )
7741: {
7742: /* -------------------------------------------------------------------------
7743: Allocations and Declarations
7744: -------------------------------------------------------------------------- */
7745: char *texsubexpr(), subexpr[8210], /* \begin{} environment paramaters */
7746: *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
7747: char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
7748: int strreplace(); /* replace substring in string */
7749: char *strchange(); /*\begin...\end --> {\begin...\end}*/
7750: char *delims = (char *)NULL; /* mdelims[ienviron] */
7751: subraster *rasterize(), *sp=NULL; /* rasterize environment */
7752: int ienviron = 0; /* environs[] index */
7753: int nbegins = 0; /* #\begins nested beneath this one*/
7754: int envlen=0, sublen=0; /* #chars in environ, subexpr */
7755: static int blevel = 0; /* \begin...\end nesting level */
7756: static char *mdelims[] = { NULL, NULL, NULL, NULL,
7757: "()","[]","{}","||","==", /* for pbBvVmatrix */
7758: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7759: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
7760: static char *environs[] = { /* types of environments we process*/
7761: "eqnarray", /* 0 eqnarray environment */
7762: "array", /* 1 array environment */
7763: "matrix", /* 2 array environment */
7764: "tabular", /* 3 array environment */
7765: "pmatrix", /* 4 ( ) */
7766: "bmatrix", /* 5 [ ] */
7767: "Bmatrix", /* 6 { } */
7768: "vmatrix", /* 7 | | */
7769: "Vmatrix", /* 8 || || */
7770: "gather", /* 9 gather environment */
7771: "align", /* 10 align environment */
7772: "verbatim", /* 11 verbatim environment */
7773: "picture", /* 12 picture environment */
7774: NULL }; /* trailer */
7775: /* -------------------------------------------------------------------------
7776: determine type of environment we're beginning
7777: -------------------------------------------------------------------------- */
7778: /* --- first bump nesting level --- */
7779: blevel++; /* count \begin...\begin...'s */
7780: /* --- \begin must be followed by {type_of_environment} --- */
7781: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
7782: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
7783: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
7784: strcpy(delims,delims+1); /* treat it as environment */
7785: /* --- look up environment in our table --- */
7786: for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
7787: if ( environs[ienviron] == NULL ) /* found NULL before match */
7788: goto end_of_job; /* so quit */
7789: else /* see if we have an exact match */
7790: if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
7791: break; /* leave loop with ienviron index */
7792: /* --- accumulate any additional params for this environment --- */
7793: *subexpr = '\000'; /* reset subexpr to empty string */
7794: delims = mdelims[ienviron]; /* mdelims[] string for ienviron */
7795: if ( delims != NULL ) /* add appropriate opening delim */
7796: { strcpy(subexpr,"\\"); /* start with \ for (,[,{,|,= */
7797: strcat(subexpr,delims); /* then add opening delim */
7798: subexpr[2] = '\000'; } /* remove extraneous closing delim */
7799: switch ( ienviron )
7800: {
7801: default: goto end_of_job; /* environ not implemented yet */
7802: case 0: /* \begin{eqnarray} */
7803: strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */
7804: break;
7805: case 1: case 2: case 3: /* \begin{array} followed by {lcr} */
7806: strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
7807: skipwhite(exprptr); /* bump to next non-white char */
7808: if ( *exprptr == '{' ) /* assume we have {lcr} argument */
7809: { exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
7810: if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
7811: strcat(subexpr,"$"); } /* add terminating $ to lcr */
7812: break;
7813: case 4: case 5: case 6: /* \begin{pmatrix} or b,B,v,Vmatrix */
7814: case 7: case 8:
7815: strcat(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
7816: break;
7817: case 9: /* gather */
7818: strcat(subexpr,"\\array{c$"); /* center equations */
7819: break;
7820: case 10: /* align */
7821: strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
7822: break;
7823: case 11: /* verbatim */
7824: strcat(subexpr,"{\\rm "); /* {\rm ...} */
7825: /*strcat(subexpr,"\\\\{\\rm ");*/ /* \\{\rm } doesn't work in context */
7826: break;
7827: case 12: /* picture */
7828: strcat(subexpr,"\\picture"); /* picture environment */
7829: skipwhite(exprptr); /* bump to next non-white char */
7830: if ( *exprptr == '(' ) /*assume we have (width,height) arg*/
7831: { exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
7832: if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
7833: strcat(subexpr,"{"); /* opening { after (width,height) */
7834: break;
7835: } /* --- end-of-switch(ienviron) --- */
7836: /* -------------------------------------------------------------------------
7837: locate matching \end{...}
7838: -------------------------------------------------------------------------- */
7839: /* --- first \end following \begin --- */
7840: if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */
7841: == NULL ) goto end_of_job; /* and quit if no \end found */
7842: /* --- find matching endptr by pushing past any nested \begin's --- */
7843: begptr = exprptr; /* start after first \begin{...} */
7844: while ( 1 ) /*break when we find matching \end*/
7845: {
7846: /* --- first, set ptr to closing } terminating current \end{...} --- */
7847: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
7848: == NULL ) goto end_of_job; /* and quit if no } found */
7849: /* -- locate next nested \begin --- */
7850: if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
7851: == NULL ) break; /*no more, so we have matching \end*/
7852: begptr += strlen(begtoken); /* push ptr past token */
7853: if ( begptr >= endptr ) break; /* past endptr, so not nested */
7854: /* --- have nested \begin, so push forward to next \end --- */
7855: nbegins++; /* count another nested \begin */
7856: if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
7857: == NULL ) goto end_of_job; /* and quit if none found */
7858: } /* --- end-of-while(1) --- */
7859: /* --- push expression past closing } of \end{} --- */
7860: *expression = braceptr+1; /* resume processing after } */
7861: /* -------------------------------------------------------------------------
7862: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
7863: -------------------------------------------------------------------------- */
7864: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
7865: sublen = strlen(subexpr); /* #chars in "preamble" */
7866: envlen = (int)(endptr-exprptr); /* #chars between \begin{}{}...\end */
7867: memcpy(subexpr+sublen,exprptr,envlen); /*concatanate environ after subexpr*/
7868: subexpr[sublen+envlen] = '\000'; /* and null-terminate */
7869: if ( 2 > 1 ) /* always... */
7870: strcat(subexpr,"}"); /* ...followed by terminating } */
7871: /* --- add terminating \right), etc, if necessary --- */
7872: if ( delims != (char *)NULL ) /* need closing delim */
7873: { strcat(subexpr,"\\"); /* start with \ for ),],},|,= */
7874: strcat(subexpr,delims+1); } /* add appropriate closing delim */
7875: /* -------------------------------------------------------------------------
7876: change nested \begin...\end to {\begin...\end} so \array{} can handle them
7877: -------------------------------------------------------------------------- */
7878: if ( nbegins > 0 ) /* have nested begins */
7879: if ( blevel < 2 ) /* only need to do this once */
7880: {
7881: begptr = subexpr; /* start at beginning of subexpr */
7882: while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
7883: { strchange(0,begptr,"{"); /* \begin --> {\begin */
7884: begptr += strlen(begtoken); } /* continue past {\begin */
7885: endptr = subexpr; /* start at beginning of subexpr */
7886: while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
7887: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
7888: == NULL ) goto end_of_job; /* and quit if no } found */
7889: else /* found terminating } */
7890: { strchange(0,braceptr,"}"); /* \end{...} --> \end{...}} */
7891: endptr = braceptr+1; } /* continue past \end{...} */
7892: } /* --- end-of-if(nbegins>0) --- */
7893: /* -------------------------------------------------------------------------
7894: post process as necessary
7895: -------------------------------------------------------------------------- */
7896: switch ( ienviron )
7897: {
7898: default: break; /* no post-processing required */
7899: case 10: /* align */
7900: strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
7901: strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
7902: strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
7903: strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
7904: strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
7905: strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
7906: strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
7907: if ( nbegins < 1 ) /* don't modify nested arrays */
7908: strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
7909: strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
7910: strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
7911: strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
7912: strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
7913: strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
7914: break;
7915: case 11: /* verbatim */
7916: strreplace(subexpr,"\n","\\\\",0); /* xlate \n newline to latex \\ */
7917: /*strcat(subexpr,"\\\\");*/ /* add final latex \\ newline */
7918: break;
7919: case 12: /* picture */
7920: strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
7921: strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
7922: strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
7923: break;
7924: } /* --- end-of-switch(ienviron) --- */
7925: /* -------------------------------------------------------------------------
7926: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
7927: -------------------------------------------------------------------------- */
7928: /* --- debugging output --- */
7929: if ( msgfp!=NULL && msglevel>=99 )
7930: fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
7931: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
7932: sp = rasterize(subexpr,size); /* rasterize subexpr */
7933: end_of_job:
7934: blevel--; /* decrement \begin nesting level */
7935: return ( sp ); /* back to caller with sp or NULL */
7936: } /* --- end-of-function rastbegin() --- */
7937:
7938:
7939: /* ==========================================================================
7940: * Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 )
7941: * Purpose: \array handler, returns a subraster corresponding to array
7942: * expression (immediately following \array) at font size
7943: * --------------------------------------------------------------------------
7944: * Arguments: expression (I/O) char ** to first char of null-terminated
7945: * string immediately following \array to be
7946: * rasterized, and returning ptr immediately
7947: * following last character processed.
7948: * size (I) int containing 0-4 default font size
7949: * basesp (I) subraster * to character (or subexpression)
7950: * immediately preceding \array
7951: * (unused, but passed for consistency)
7952: * arg1 (I) int unused
7953: * arg2 (I) int unused
7954: * arg3 (I) int unused
7955: * --------------------------------------------------------------------------
7956: * Returns: ( subraster * ) ptr to subraster corresponding to array
7957: * expression, or NULL for any parsing error
7958: * --------------------------------------------------------------------------
7959: * Notes: o Summary of syntax...
7960: * \array{3,lcrBC$a&b&c\\d&e&f\\etc}
7961: * o The 3,lcrBC$ part is an optional "preamble". The lcr means
7962: * what you think, i.e., "horizontal" left,center,right
7963: * justification down corresponding column. The new BC means
7964: * "vertical" baseline,center justification across corresponding
7965: * row. The leading 3 specifies the font size 0-4 to be used.
7966: * You may also specify +1,-1,+2,-2, etc, which is used as an
7967: * increment to the current font size, e.g., -1,lcr$ uses
7968: * one font size smaller than current. Without a leading
7969: * + or -, the font size is "absolute".
7970: * o The preamble can also be just lcrBC$ without a leading
7971: * size-part, or just 3$ without a trailing lcrBC-part.
7972: * The default size is whatever is current, and the
7973: * default justification is c(entered) and B(aseline).
7974: * ======================================================================= */
7975: /* --- entry point --- */
7976: subraster *rastarray ( char **expression, int size, subraster *basesp,
7977: int arg1, int arg2, int arg3 )
7978: {
7979: /* -------------------------------------------------------------------------
7980: Allocations and Declarations
7981: -------------------------------------------------------------------------- */
7982: char *texsubexpr(), subexpr[8210], *exprptr, /* parse array subexpr */
7983: subtok[4096], *subptr=subtok, /* & or \\ inside { }'s not a delim*/
7984: token[4096], *tokptr=token, /* token from subexpr to rasterize */
7985: *preamble(), *preptr=token; /*process optional size,lcr preamble*/
7986: char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
7987: int maxarraysz = 64; /* max #rows, cols */
7988: int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
7989: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7990: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
7991: hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
7992: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7993: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
7994: vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
7995: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7996: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
7997: colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
7998: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7999: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8000: rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
8001: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8002: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8003: fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
8004: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8005: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8006: fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
8007: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8008: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8009: rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
8010: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8011: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8012: rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
8013: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8014: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
8015: static int /* --- propagate global values across arrays --- */
8016: gjustify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
8017: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8018: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8019: gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
8020: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8021: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8022: growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
8023: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8024: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8025: gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
8026: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8027: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8028: gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
8029: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8030: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8031: growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
8032: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8033: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
8034: int rowglobal=0, colglobal=0, /* true to set global values */
8035: rowpropagate=0, colpropagate=0; /* true if propagating values */
8036: int irow,nrows=0, icol,ncols[65], /*#rows in array, #cols in each row*/
8037: maxcols=0; /* max# cols in any single row */
8038: int itoken, ntokens=0, /* index, total #tokens in array */
8039: subtoklen=0, /* strlen of {...} subtoken */
8040: istokwhite=1, /* true if token all whitespace */
8041: nnonwhite=0; /* #non-white tokens */
8042: int isescape=0,wasescape=0, /* current,prev chars escape? */
8043: ischarescaped=0, /* is current char escaped? */
8044: nescapes=0; /* #consecutive escapes */
8045: subraster *rasterize(), *toksp[1025], /* rasterize tokens */
8046: *new_subraster(), *arraysp=NULL; /* subraster for entire array */
8047: raster *arrayrp=NULL; /* raster for entire array */
8048: int delete_subraster(); /* free toksp[] workspace at eoj */
8049: int rowspace=2, colspace=4, /* blank space between rows, cols */
8050: hspace=1, vspace=1; /*space to accommodate hline,vline*/
8051: int width=0, height=0, /* width,height of array */
8052: leftcol=0, toprow=0; /*upper-left corner for cell in it*/
8053: int rastput(); /* embed tokens/cells in array */
8054: int rule_raster(); /* draw hlines and vlines in array */
8055: char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
8056: char *texchar(), hltoken[1025]; /* extract \hline from token */
8057: int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
8058: int isnewrow=1; /* true for new row */
8059: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
8060: /* -------------------------------------------------------------------------
8061: Macros to determine extra raster space required for vline/hline
8062: -------------------------------------------------------------------------- */
8063: #define vlinespace(icol) \
8064: ( vline[icol] == 0? 0 : /* no vline so no space needed */ \
8065: ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
8066: #define hlinespace(irow) \
8067: ( hline[irow] == 0? 0 : /* no hline so no space needed */ \
8068: ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
8069: /* -------------------------------------------------------------------------
8070: Obtain array subexpression
8071: -------------------------------------------------------------------------- */
8072: /* --- parse for array subexpression, and bump expression past it --- */
8073: subexpr[1] = *subexpr = ' '; /* set two leading blanks */
8074: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
8075: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display array */
8076: fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
8077: if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */
8078: goto end_of_job; /* nothing to do, so quit */
8079: /* -------------------------------------------------------------------------
8080: process optional size,lcr preamble if present
8081: -------------------------------------------------------------------------- */
8082: /* --- reset size, get lcr's, and push exprptr past preamble --- */
8083: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
8084: /* --- init with global values --- */
8085: for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
8086: justify[icol] = gjustify[icol]; /* -1,0,+1 = l,c,r */
8087: colwidth[icol] = gcolwidth[icol]; /* column width */
8088: rowheight[icol] = growheight[icol]; /* row height */
8089: fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
8090: fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
8091: rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
8092: /* --- process lcr's, etc in preamble --- */
8093: itoken = 0; /* debugging flag */
8094: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display preamble */
8095: if ( *preptr != '\000' ) /* if we have one */
8096: fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
8097: preptr);
8098: irow = icol = 0; /* init lcr counts */
8099: while ( *preptr != '\000' ) /* check preamble text for lcr */
8100: {
8101: char prepchar = *preptr; /* current preamble character */
8102: int prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
8103: if ( irow<maxarraysz && icol<maxarraysz )
8104: switch ( /*tolower*/(prepchar) )
8105: { default: break; /* just flush unrecognized chars */
8106: case 'l': justify[icol] = (-1); /*left-justify this column*/
8107: if (colglobal) gjustify[irow] = justify[irow]; break;
8108: case 'c': justify[icol] = (0); /* center this column */
8109: if (colglobal) gjustify[irow] = justify[irow]; break;
8110: case 'r': justify[icol] = (+1); /* right-justify this col */
8111: if (colglobal) gjustify[irow] = justify[irow]; break;
8112: case '|': vline[icol] += 1; break; /* solid vline left of col */
8113: case '.': vline[icol] = (-1); break; /*dashed vline left of col */
8114: case 'b': prepchar='B'; prepcase=2; /* alias for B */
8115: case 'B': break; /* baseline-justify row */
8116: case 'v': prepchar='C'; prepcase=2; /* alias for C */
8117: case 'C': rowcenter[irow] = 1; /* vertically center row */
8118: if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
8119: case 'g': colglobal=1; prepcase=0; break; /* set global col values */
8120: case 'G': rowglobal=1; prepcase=0; break; /* set global row values */
8121: case '#': colglobal=rowglobal=1; break; } /* set global col,row vals */
8122: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8123: fprintf(msgfp," %c[%d]",prepchar,
8124: (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
8125: preptr++; /* check next char for lcr */
8126: itoken++; /* #lcr's processed (debugging only)*/
8127: /* --- check for number or +number specifying colwidth or rowheight --- */
8128: if ( prepcase != 0 ) /* only check upper,lowercase */
8129: {
8130: int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
8131: if ( ispropagate ) /* set row or col propagation */
8132: if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
8133: else if ( prepcase == 2 ) rowpropagate = 1; /* propagating row values */
8134: if ( !colpropagate && prepcase == 1 )
8135: { colwidth[icol] = 0; /* reset colwidth */
8136: fixcolsize[icol] = 0; } /* reset width flag */
8137: if ( !rowpropagate && prepcase == 2 )
8138: { rowheight[irow] = 0; /* reset row height */
8139: fixrowsize[irow] = 0; } /* reset height flag */
8140: if ( ispropagate ) preptr++; /* bump past leading + */
8141: if ( isdigit(*preptr) ) /* digit follows character */
8142: { char *endptr = NULL; /* preptr set to 1st char after num*/
8143: int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
8144: char *whchars="?wh"; /* debugging width/height labels */
8145: preptr = endptr; /* skip over all digits */
8146: if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
8147: int index; /* icol,irow...maxarraysz index */
8148: if ( prepcase == 1 ) /* lowercase signifies colwidth */
8149: for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
8150: colwidth[index] = size; /* set colwidth to fixed size */
8151: fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
8152: justify[index] = justify[icol]; /* and propagate justification */
8153: if ( colglobal ) { /* set global values */
8154: gcolwidth[index] = colwidth[index]; /* set global col width */
8155: gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
8156: gjustify[index] = justify[icol]; } /* set global col justify */
8157: if ( !ispropagate ) break; } /* don't propagate */
8158: else /* uppercase signifies rowheight */
8159: for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
8160: rowheight[index] = size; /* set rowheight to size */
8161: fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
8162: rowcenter[index] = rowcenter[irow]; /* and propagate row center */
8163: if ( rowglobal ) { /* set global values */
8164: growheight[index] = rowheight[index]; /* set global row height */
8165: gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
8166: growcenter[index] = rowcenter[irow]; } /*set global row center*/
8167: if ( !ispropagate ) break; } /* don't propagate */
8168: } /* --- end-of-if(size>=3&&size<=500) --- */
8169: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8170: fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
8171: (prepcase==1?colwidth[icol]:rowheight[irow]),
8172: (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
8173: } /* --- end-of-if(isdigit()) --- */
8174: } /* --- end-of-if(prepcase!=0) --- */
8175: if ( prepcase == 1 ) icol++; /* bump col if lowercase lcr */
8176: else if ( prepcase == 2 ) irow++; /* bump row if uppercase BC */
8177: } /* --- end-of-while(*preptr!='\000') --- */
8178: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, emit final newline */
8179: if ( itoken > 0 ) /* if we have preamble */
8180: fprintf(msgfp,"\n");
8181: /* -------------------------------------------------------------------------
8182: tokenize and rasterize components a & b \\ c & d \\ etc of subexpr
8183: -------------------------------------------------------------------------- */
8184: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
8185: ncols[nrows] = 0; /* no tokens/cols in top row yet */
8186: while ( 1 ) /* scan chars till end */
8187: {
8188: /* --- local control flags --- */
8189: int iseox = (*exprptr == '\000'), /* null signals end-of-expression */
8190: iseor = iseox, /* \\ or eox signals end-of-row */
8191: iseoc = iseor; /* & or eor signals end-of-col */
8192: /* --- check for escapes --- */
8193: isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
8194: wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
8195: nescapes = (wasescape?nescapes+1:0); /* # preceding consecutive escapes */
8196: ischarescaped = (nescapes%2==0?0:1); /* is current char escaped? */
8197: /* -----------------------------------------------------------------------
8198: check for {...} subexpression starting from where we are now
8199: ------------------------------------------------------------------------ */
8200: if ( *exprptr == '{' /* start of {...} subexpression */
8201: && !ischarescaped ) /* if not escaped \{ */
8202: {
8203: subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
8204: subtoklen = strlen(subtok); /* #chars in {...} */
8205: memcpy(tokptr,exprptr,subtoklen); /* copy {...} to accumulated token */
8206: tokptr += subtoklen; /* bump tokptr to end of token */
8207: exprptr += subtoklen; /* and bump exprptr past {...} */
8208: istokwhite = 0; /* signal non-empty token */
8209: continue; /* continue with char after {...} */
8210: } /* --- end-of-if(*exprptr=='{') --- */
8211: /* -----------------------------------------------------------------------
8212: check for end-of-row(\\) and/or end-of-col(&)
8213: ------------------------------------------------------------------------ */
8214: /* --- check for (escaped) end-of-row delimiter --- */
8215: if ( isescape && !ischarescaped ) /* current char is escaped */
8216: if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
8217: || *(exprptr+1) == '\000' ) /* or a pathological null */
8218: { iseor = 1; /* so set end-of-row flag */
8219: wasescape=isescape=nescapes = 0; } /* reset flags for new row */
8220: /* --- check for end-of-col delimiter --- */
8221: if (iseor /* end-of-row signals end-of-col */
8222: || (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
8223: iseoc = 1; /* so set end-of-col flag */
8224: /* -----------------------------------------------------------------------
8225: rasterize completed token
8226: ------------------------------------------------------------------------ */
8227: if ( iseoc ) /* we have a completed token */
8228: {
8229: *tokptr = '\000'; /* first, null-terminate token */
8230: /* --- check first token in row for \hline or \hdash --- */
8231: ishonly = 0; /*init for token not only an \hline*/
8232: if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/
8233: {
8234: tokptr=token; skipwhite(tokptr); /* skip whitespace after // */
8235: tokptr = texchar(tokptr,hltoken); /* extract first char from token */
8236: hltoklen = strlen(hltoken); /* length of first char */
8237: if ( hltoklen >= minhltoklen ) /*token must be at least \hl or \hd*/
8238: if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
8239: hline[nrows] += 1; /* bump \hline count for row */
8240: else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
8241: hline[nrows] = (-1); /* set \hdash flag for row */
8242: if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
8243: { skipwhite(tokptr); /* flush whitespace after \hline */
8244: if ( *tokptr == '\000' /* end-of-expression after \hline */
8245: || isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
1.2 ! albertel 8246: { istokwhite = 1; /* so token contains \hline only */
! 8247: if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
1.1 albertel 8248: else /* token contains more than \hline */
8249: strcpy(token,tokptr); } /* so flush \hline from token */
8250: } /* --- end-of-if(ncols[nrows]==0) --- */
8251: /* --- rasterize completed token --- */
8252: toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
8253: rasterize(token,size)); /* rasterize non-empty token */
8254: if ( toksp[ntokens] != NULL ) /* have a rasterized token */
8255: nnonwhite++; /* bump rasterized token count */
8256: /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
8257: if ( toksp[ntokens] != NULL ) /* we have a rasterized token */
8258: {
8259: /* --- update max token "height" in current row, and baseline --- */
8260: int twidth = ((toksp[ntokens])->image)->width, /* width of token */
8261: theight = ((toksp[ntokens])->image)->height, /* height of token */
8262: tbaseln = (toksp[ntokens])->baseline, /* baseline of token */
8263: rheight = rowheight[nrows], /* current max height for row */
8264: rbaseln = rowbaseln[nrows]; /* current baseline for max height */
8265: if ( 0 || fixrowsize[nrows]==0 ) /* rowheight not fixed */
8266: rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
8267: max2(rbaseln+1,tbaseln+1) /* max height above baseline */
8268: + max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
8269: rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
8270: /* --- update max token width in current column --- */
8271: icol = ncols[nrows]; /* current column index */
8272: if ( 0 || fixcolsize[icol]==0 ) /* colwidth not fixed */
8273: colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
8274: } /* --- end-of-if(toksp[]!=NULL) --- */
8275: /* --- bump counters --- */
8276: if ( !ishonly ) /* don't count only an \hline */
8277: { ntokens++; /* bump total token count */
8278: ncols[nrows] += 1; } /* and bump #cols in current row */
8279: /* --- get ready for next token --- */
8280: tokptr = token; /* reset ptr for next token */
8281: istokwhite = 1; /* next token starts all white */
8282: } /* --- end-of-if(iseoc) --- */
8283: /* -----------------------------------------------------------------------
8284: bump row as necessary
8285: ------------------------------------------------------------------------ */
8286: if ( iseor ) /* we have a completed row */
8287: {
8288: maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
8289: if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
8290: nrows++; /* bump row count */
8291: ncols[nrows] = 0; /* no cols in this row yet */
8292: if ( !iseox ) /* don't have a null yet */
8293: { exprptr++; /* bump past extra \ in \\ delim */
8294: iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
8295: isnewrow = 1; /* signal start of new row */
8296: } /* --- end-of-if(iseor) --- */
8297: else
8298: isnewrow = 0; /* no longer first col of new row */
8299: /* -----------------------------------------------------------------------
8300: quit when done, or accumulate char in token and proceed to next char
8301: ------------------------------------------------------------------------ */
8302: /* --- quit when done --- */
8303: if ( iseox ) break; /* null terminator signalled done */
8304: /* --- accumulate chars in token --- */
8305: if ( !iseoc ) /* don't accumulate delimiters */
8306: { *tokptr++ = *exprptr; /* accumulate non-delim char */
8307: if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
8308: istokwhite = 0; } /* so reset flag to rasterize it */
8309: /* --- ready for next char --- */
8310: exprptr++; /* bump ptr */
8311: } /* --- end-of-while(*exprptr!='\000') --- */
8312: /* --- make sure we got something to do --- */
8313: if ( nnonwhite < 1 ) /* completely empty array */
8314: goto end_of_job; /* NULL back to caller */
8315: /* -------------------------------------------------------------------------
8316: determine dimensions of array raster and allocate it
8317: -------------------------------------------------------------------------- */
8318: /* --- adjust colspace --- */
8319: colspace = 2 + 2*size; /* temp kludge */
8320: /* --- reset propagated sizes at boundaries of array --- */
8321: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
8322: /* --- determine width of array raster --- */
8323: width = colspace*(maxcols-1); /* empty space between cols */
8324: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8325: fprintf(msgfp,"rastarray> %d cols, widths: ",maxcols);
8326: for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */
8327: { width += colwidth[icol]; /*width of this col (0 for maxcols)*/
8328: width += vlinespace(icol); /*plus space for vline, if present*/
8329: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8330: fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
8331: /* --- determine height of array raster --- */
8332: height = rowspace*(nrows-1); /* empty space between rows */
8333: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8334: fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
8335: for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
8336: { height += rowheight[irow]; /*height of this row (0 for nrows)*/
8337: height += hlinespace(irow); /*plus space for hline, if present*/
8338: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8339: fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
8340: /* --- allocate subraster and raster for array --- */
8341: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8342: fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
8343: width,colspace, height,rowspace);
8344: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
8345: == NULL ) goto end_of_job; /* quit if failed */
8346: /* --- initialize subraster parameters --- */
8347: arraysp->type = IMAGERASTER; /* image */
8348: arraysp->symdef = NULL; /* not applicable for image */
8349: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
8350: arraysp->size = size; /* size (probably unneeded) */
8351: arrayrp = arraysp->image; /* raster embedded in subraster */
8352: /* -------------------------------------------------------------------------
8353: embed tokens/cells in array
8354: -------------------------------------------------------------------------- */
8355: itoken = 0; /* start with first token */
8356: toprow = 0; /* start at top row of array */
8357: for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/
8358: {
8359: /* --- initialization for row --- */
8360: int baseline = rowbaseln[irow]; /* baseline for this row */
8361: if ( hline[irow] != 0 ) /* need hline above this row */
8362: { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
8363: if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
8364: rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
8365: if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
8366: toprow += hlinespace(irow); /* space for hline above irow */
8367: leftcol = 0; /* start at leftmost column */
8368: for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
8369: {
8370: subraster *tsp = toksp[itoken]; /* token that belongs in this cell */
1.2 ! albertel 8371: /* --- first adjust leftcol for vline to left of icol, if present ---- */
! 8372: leftcol += vlinespace(icol); /* space for vline to left of col */
! 8373: /* --- now rasterize cell ---- */
1.1 albertel 8374: if ( tsp != NULL ) /* have a rasterized cell token */
8375: {
8376: /* --- local parameters --- */
8377: int cwidth = colwidth[icol], /* total column width */
8378: twidth = (tsp->image)->width, /* token width */
8379: theight= (tsp->image)->height, /* token height */
8380: tokencol = 0, /*H offset (init for left justify)*/
8381: tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
8382: /* --- adjust leftcol for vline to left of icol, if present ---- */
1.2 ! albertel 8383: /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
1.1 albertel 8384: /* --- reset justification (if not left-justified) --- */
8385: if ( justify[icol] == 0 ) /* but user wants it centered */
8386: tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
8387: else if ( justify[icol] == 1 ) /* or user wants right-justify */
8388: tokencol = cwidth-twidth; /* so put entire margin at left */
8389: /* --- reset vertical centering (if not baseline-aligned) --- */
8390: if ( rowcenter[irow] ) /* center cells in row vertically */
8391: tokenrow = (rowheight[irow]-theight)/2; /* center row */
8392: /* --- embed token raster at appropriate place in array raster --- */
8393: rastput(arrayrp,tsp->image, /* overlay cell token in array */
8394: toprow+ tokenrow, /*with aligned baseline or centered*/
8395: leftcol+tokencol, 1); /* and justified as requested */
8396: } /* --- end-of-if(tsp!=NULL) --- */
8397: itoken++; /* bump index for next cell */
8398: leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
8399: /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
8400: } /* --- end-of-for(icol) --- */
8401: toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
8402: } /* --- end-of-for(irow) --- */
8403: /* -------------------------------------------------------------------------
8404: draw vlines as necessary
8405: -------------------------------------------------------------------------- */
8406: leftcol = 0; /* start at leftmost column */
8407: for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */
8408: {
8409: if ( vline[icol] != 0 ) /* need vline to left of this col */
8410: { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
8411: if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
8412: rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
8413: leftcol += vlinespace(icol); /* space for vline to left of col */
8414: if ( icol < maxcols ) /* don't address past end of array */
8415: leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
8416: } /* --- end-of-for(icol) --- */
8417: /* -------------------------------------------------------------------------
8418: free workspace and return final result to caller
8419: -------------------------------------------------------------------------- */
8420: end_of_job:
8421: /* --- free workspace --- */
8422: if ( ntokens > 0 ) /* if we have workspace to free */
8423: while ( --ntokens >= 0 ) /* free each token subraster */
8424: if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */
8425: delete_subraster(toksp[ntokens]); /* then free it */
8426: /* --- return final result to caller --- */
8427: return ( arraysp );
8428: } /* --- end-of-function rastarray() --- */
8429:
8430:
8431: /* ==========================================================================
8432: * Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
8433: * Purpose: \picture handler, returns subraster corresponding to picture
8434: * expression (immediately following \picture) at font size
8435: * --------------------------------------------------------------------------
8436: * Arguments: expression (I/O) char ** to first char of null-terminated
8437: * string immediately following \picture to be
8438: * rasterized, and returning ptr immediately
8439: * following last character processed.
8440: * size (I) int containing 0-4 default font size
8441: * basesp (I) subraster * to character (or subexpression)
8442: * immediately preceding \picture
8443: * (unused, but passed for consistency)
8444: * arg1 (I) int unused
8445: * arg2 (I) int unused
8446: * arg3 (I) int unused
8447: * --------------------------------------------------------------------------
8448: * Returns: ( subraster * ) ptr to subraster corresponding to picture
8449: * expression, or NULL for any parsing error
8450: * --------------------------------------------------------------------------
8451: * Notes: o Summary of syntax...
8452: * \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
8453: * o
8454: * ======================================================================= */
8455: /* --- entry point --- */
8456: subraster *rastpicture ( char **expression, int size, subraster *basesp,
8457: int arg1, int arg2, int arg3 )
8458: {
8459: /* -------------------------------------------------------------------------
8460: Allocations and Declarations
8461: -------------------------------------------------------------------------- */
8462: char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
8463: putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
8464: pream[64], *preptr, /* optional put preamble */
8465: picelem[1025]; /* picture element following put */
8466: subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
8467: *new_subraster(), *picturesp=NULL, /* subraster for entire picture */
8468: *oldworkingbox = workingbox; /* save working box on entry */
8469: raster *picturerp=NULL; /* raster for entire picture */
8470: int delete_subraster(); /* free picelemsp[] workspace */
8471: int pixsz = 1; /* pixels are one bit each */
8472: double strtod(), /* convert ascii params to doubles */
8473: x=0.0,y=0.0, /* x,y-coords for put,multiput*/
8474: xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
8475: int width=0, height=0, /* #pixels width,height of picture */
8476: ewidth=0, eheight=0, /* pic element width,height */
8477: ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
8478: num=1, inum; /* number reps, index of element */
8479: int iscenter=0; /* center or lowerleft put position*/
8480: int *oldworkingparam = workingparam, /* save working param on entry */
8481: origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
8482: int rastput(); /* embed elements in picture */
8483: int type_raster(); /* display debugging output */
8484: /* -------------------------------------------------------------------------
8485: First obtain (width,height) arguments immediately following \picture command
8486: -------------------------------------------------------------------------- */
8487: /* --- parse for (width,height) arguments, and bump expression past it --- */
8488: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
8489: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
8490: /* --- now interpret width,height returned in putexpr --- */
8491: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
8492: *putptr = '\000'; /* found it, so replace ',' by '\0'*/
8493: width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
8494: if ( putptr != NULL ) /* 2nd arg, if present, is height */
8495: height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
8496: /* -------------------------------------------------------------------------
8497: Then obtain entire picture {...} subexpression following (width,height)
8498: -------------------------------------------------------------------------- */
8499: /* --- parse for picture subexpression, and bump expression past it --- */
8500: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
8501: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
8502: /* -------------------------------------------------------------------------
8503: allocate subraster and raster for complete picture
8504: -------------------------------------------------------------------------- */
8505: /* --- sanity check on width,height args --- */
8506: if ( width < 2 || width > 600
8507: || height < 2 || height > 600 ) goto end_of_job;
8508: /* --- allocate and initialize subraster for constructed picture --- */
8509: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
8510: == NULL ) goto end_of_job; /* quit if failed */
8511: workingbox = picturesp; /* set workingbox to our picture */
8512: /* --- initialize picture subraster parameters --- */
8513: picturesp->type = IMAGERASTER; /* image */
8514: picturesp->symdef = NULL; /* not applicable for image */
8515: picturesp->baseline = height/2 + 2; /* is a little above center good? */
8516: picturesp->size = size; /* size (probably unneeded) */
8517: picturerp = picturesp->image; /* raster embedded in subraster */
8518: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8519: fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
8520: /* -------------------------------------------------------------------------
8521: parse out each picture element, rasterize it, and place it in picture
8522: -------------------------------------------------------------------------- */
8523: while ( *picptr != '\000' ) /* until we run out of pic_elems */
8524: {
8525: /* -----------------------------------------------------------------------
8526: first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
8527: ------------------------------------------------------------------------ */
8528: /* --- init default values in case not explicitly supplied in args --- */
8529: x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */
8530: iscenter = origin = 0; /* center, origin */
8531: /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
8532: while ( *picptr != '\000' ) /* skip invalid chars preceding ( */
8533: if ( *picptr == '(' ) break; /* found opening ( */
8534: else picptr++; /* else skip invalid char */
8535: picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
8536: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
8537: /* --- first look for $-terminated or for any non-digit preamble --- */
8538: *pream = '\000'; /* init preamble as empty string */
8539: if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
8540: { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
8541: strcpy(pream,putexpr); } /* copy leading preamble from put */
8542: else /* look for any non-digit preamble */
8543: { for ( preptr=pream,putptr=putexpr; ; putptr++ )
8544: if ( *putptr == '\000' /* end-of-putdata signalled */
8545: || !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
8546: else *preptr++ = *putptr; /* copy alpha char to preamble */
8547: *preptr = '\000'; } /* null-terminate preamble */
8548: /* --- interpret preamble --- */
8549: for ( preptr=pream; ; preptr++ ) /* examine each preamble char */
8550: if ( *preptr == '\000' ) break; /* end-of-preamble signalled */
8551: else switch ( tolower(*preptr) ) /* check lowercase preamble char */
8552: {
8553: default: break; /* unrecognized flag */
8554: case 'c': iscenter=1; break; /* center pic_elem at x,y coords */
8555: } /* --- end-of-switch --- */
8556: /* --- interpret x,y;xinc,yinc;num following preamble --- */
8557: if ( *putptr != '\000' ) /*check for put data after preamble*/
8558: {
8559: /* --- first squeeze preamble out of put expression --- */
8560: if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
8561: /* --- interpret x,y --- */
8562: if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
8563: *multptr = '\000'; /* replace semicolon by '\0' */
8564: if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
8565: *putptr = '\000'; /* replace comma by '\0' */
8566: if ( *putexpr != '\000' ) /* leading , may be placeholder */
8567: x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
8568: if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
8569: y = unitlength*strtod(putptr+1,NULL); /* in pixels */
8570: /* --- interpret xinc,yinc,num if we have a multiput --- */
8571: if ( multptr != NULL ) /* found ';' signalling multiput */
8572: {
8573: if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
8574: *preptr = '\000'; /* replace ';' by '\0' */
8575: if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
8576: *putptr = '\000'; /* replace ',' by '\0' */
8577: if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
8578: xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
8579: if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
8580: yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
8581: num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
8582: } /* --- end-of-if(multptr!=NULL) --- */
8583: } /* --- end-of-if(*preptr!='\000') --- */
8584: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8585: fprintf(msgfp,
8586: "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
8587: pream,x,y,xinc,yinc,num);
8588: /* -----------------------------------------------------------------------
8589: now obtain {...} picture element following [multi]put, and rasterize it
8590: ------------------------------------------------------------------------ */
8591: /* --- parse for {...} picture element and bump picptr past it --- */
8592: picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
8593: if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
8594: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8595: fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
8596: /* --- rasterize picture element --- */
8597: origin = 0; /* init origin as working param */
8598: workingparam = &origin; /* and point working param to it */
8599: picelemsp = rasterize(picelem,size); /* rasterize picture element */
8600: if ( picelemsp == NULL ) continue; /* failed to rasterize, skip elem */
8601: ewidth = (picelemsp->image)->width; /* width of element, in pixels */
8602: eheight = (picelemsp->image)->height; /* height of element, in pixels */
8603: if ( origin == 55 ) iscenter = 1; /* origin set to (.5,.5) for center*/
8604: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8605: { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
8606: ewidth,eheight,origin,num);
8607: if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
8608: /* -----------------------------------------------------------------------
8609: embed element in picture (once, or multiple times if requested)
8610: ------------------------------------------------------------------------ */
8611: for ( inum=0; inum<num; inum++ ) /* once, or for num repetitions */
8612: {
8613: /* --- set x,y-coords for this iteration --- */
8614: ix = iround(x); iy = iround(y); /* round x,y to nearest integer */
8615: if ( iscenter ) /* place center of element at x,y */
8616: { xpos = ix - ewidth/2; /* x picture coord to center elem */
8617: ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
8618: else /* default places lower-left at x,y*/
8619: { xpos = ix; /* set x pixel coord for left */
8620: if ( origin==10 || origin==11 ) /* x,yinc's are -+ or -- */
8621: xpos = ix - ewidth; /* so set for right instead */
8622: ypos = height - iy - eheight; /* set y pixel coord for lower */
8623: if ( origin==1 || origin==11 ) /* x,yinc's are +- or -- */
8624: ypos = height - iy; } /* so set for upper instead */
8625: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8626: fprintf(msgfp,
8627: "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
8628: inum,x,y,ix,iy,xpos,ypos);
8629: /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
8630: if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
8631: /* --- apply increment --- */
8632: if ( xinc==0. && yinc==0. ) break; /* quit if both increments zero */
8633: x += xinc; y += yinc; /* increment coords for next iter */
8634: } /* --- end-of-for(inum) --- */
8635: /* --- free picture element subraster after embedding it in picture --- */
8636: delete_subraster(picelemsp); /* done with subraster, so free it */
8637: } /* --- end-of-while(*picptr!=0) --- */
8638: /* -------------------------------------------------------------------------
8639: return picture constructed from pic_elements to caller
8640: -------------------------------------------------------------------------- */
8641: end_of_job:
8642: workingbox = oldworkingbox; /* restore original working box */
8643: workingparam = oldworkingparam; /* restore original working param */
8644: return ( picturesp ); /* return our picture to caller */
8645: } /* --- end-of-function rastpicture() --- */
8646:
8647:
8648: /* ==========================================================================
8649: * Function: rastline ( expression, size, basesp, arg1, arg2, arg3 )
8650: * Purpose: \line handler, returns subraster corresponding to line
8651: * parameters (xinc,yinc){xlen}
8652: * --------------------------------------------------------------------------
8653: * Arguments: expression (I/O) char ** to first char of null-terminated
8654: * string immediately following \line to be
8655: * rasterized, and returning ptr immediately
8656: * following last character processed.
8657: * size (I) int containing 0-4 default font size
8658: * basesp (I) subraster * to character (or subexpression)
8659: * immediately preceding \line
8660: * (unused, but passed for consistency)
8661: * arg1 (I) int unused
8662: * arg2 (I) int unused
8663: * arg3 (I) int unused
8664: * --------------------------------------------------------------------------
8665: * Returns: ( subraster * ) ptr to subraster corresponding to line
8666: * requested, or NULL for any parsing error
8667: * --------------------------------------------------------------------------
8668: * Notes: o Summary of syntax...
8669: * \line(xinc,yinc){xlen}
8670: * o if {xlen} not given, then it's assumed xlen = |xinc|
8671: * ======================================================================= */
8672: /* --- entry point --- */
8673: subraster *rastline ( char **expression, int size, subraster *basesp,
8674: int arg1, int arg2, int arg3 )
8675: {
8676: /* -------------------------------------------------------------------------
8677: Allocations and Declarations
8678: -------------------------------------------------------------------------- */
8679: char *texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
8680: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
8681: /*char *origexpression = *expression;*/ /*original expression after \line*/
8682: int pixsz = 1; /* pixels are one bit each */
1.2 ! albertel 8683: int thickness = 1; /* line thickness */
1.1 albertel 8684: double strtod(), /* convert ascii params to doubles */
8685: xinc=0.0, yinc=0.0, /* x,y-increments for line, */
8686: xlen=0.0, ylen=0.0; /* x,y lengths for line */
1.2 ! albertel 8687: int width=0, height=0, /* #pixels width,height of line */
! 8688: rwidth=0, rheight=0; /*alloc width,height plus thickness*/
1.1 albertel 8689: int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
8690: origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
8691: int line_raster(); /* draw line in linesp->image */
8692: /* -------------------------------------------------------------------------
8693: obtain (xinc,yinc) arguments immediately following \line command
8694: -------------------------------------------------------------------------- */
8695: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
8696: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
8697: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
1.2 ! albertel 8698: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
! 8699: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
! 8700: { *xptr = '\000'; /* terminate linexpr at ; */
! 8701: thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */
1.1 albertel 8702: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
8703: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
8704: if ( *linexpr != '\000' ) /* check against missing 1st arg */
8705: xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */
8706: if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
8707: yinc = ylen = strtod(xptr+1,NULL); /* in user units */
8708: /* -------------------------------------------------------------------------
8709: obtain optional {xlen} following (xinc,yinc), and calculate ylen
8710: -------------------------------------------------------------------------- */
8711: /* --- check if {xlen} given --- */
8712: if ( *(*expression) == '{' ) /*have {xlen} if leading char is { */
8713: {
8714: /* --- parse {xlen} and bump expression past it, interpret as double --- */
8715: *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
8716: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
8717: xlen = strtod(linexpr,NULL); /* xlen in user units */
8718: /* --- set other values accordingly --- */
8719: if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
8720: if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
8721: else xlen = 0.0; /* can't have xlen if xinc=0 */
8722: } /* --- end-of-if(*(*expression)=='{') --- */
8723: /* -------------------------------------------------------------------------
8724: calculate width,height, etc, based on xlen,ylen, etc
8725: -------------------------------------------------------------------------- */
8726: /* --- force lengths positive --- */
8727: xlen = absval(xlen); /* force xlen positive */
8728: ylen = absval(ylen); /* force ylen positive */
8729: /* --- calculate corresponding lengths in pixels --- */
1.2 ! albertel 8730: width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
! 8731: height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
! 8732: rwidth = width + (ylen<0.001?0:max2(0,thickness-1));
! 8733: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
1.1 albertel 8734: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
8735: if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
8736: if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
8737: origin = isright*10 + istop; /* interpret 0=(0,0), 11=(1,1), etc*/
8738: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8739: fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
8740: width,height,origin,xinc,yinc);
8741: /* -------------------------------------------------------------------------
8742: allocate subraster and raster for complete picture
8743: -------------------------------------------------------------------------- */
1.2 ! albertel 8744: /* --- sanity check on width,height,thickness args --- */
1.1 albertel 8745: if ( width < 1 || width > 600
1.2 ! albertel 8746: || height < 1 || height > 600
! 8747: || thickness<1||thickness>25 ) goto end_of_job;
1.1 albertel 8748: /* --- allocate and initialize subraster for constructed line --- */
1.2 ! albertel 8749: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
1.1 albertel 8750: == NULL ) goto end_of_job; /* quit if failed */
8751: /* --- initialize line subraster parameters --- */
8752: linesp->type = IMAGERASTER; /* image */
8753: linesp->symdef = NULL; /* not applicable for image */
1.2 ! albertel 8754: linesp->baseline = height/2 + 2 /* is a little above center good? */
! 8755: + (rheight-height)/2; /* account for line thickness too */
1.1 albertel 8756: linesp->size = size; /* size (probably unneeded) */
8757: /* -------------------------------------------------------------------------
8758: draw the line
8759: -------------------------------------------------------------------------- */
8760: line_raster ( linesp->image, /* embedded raster image */
8761: (istop? 0 : height-1), /* row0, from bottom or top */
8762: (isright? width-1 : 0), /* col0, from left or right */
8763: (istop? height-1 : 0), /* row1, to top or bottom */
8764: (isright? 0 : width-1), /* col1, to right or left */
1.2 ! albertel 8765: thickness ); /* line thickness (usually 1 pixel)*/
1.1 albertel 8766: /* -------------------------------------------------------------------------
8767: return constructed line to caller
8768: -------------------------------------------------------------------------- */
8769: end_of_job:
8770: if ( workingparam != NULL ) /* caller wants origin */
8771: *workingparam = origin; /* return origin corner to caller */
8772: return ( linesp ); /* return line to caller */
8773: } /* --- end-of-function rastline() --- */
8774:
8775:
8776: /* ==========================================================================
8777: * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
8778: * Purpose: \circle handler, returns subraster corresponding to ellipse
8779: * parameters (xdiam[,ydiam])
8780: * --------------------------------------------------------------------------
8781: * Arguments: expression (I/O) char ** to first char of null-terminated
8782: * string immediately following \circle to be
8783: * rasterized, and returning ptr immediately
8784: * following last character processed.
8785: * size (I) int containing 0-4 default font size
8786: * basesp (I) subraster * to character (or subexpression)
8787: * immediately preceding \circle
8788: * (unused, but passed for consistency)
8789: * arg1 (I) int unused
8790: * arg2 (I) int unused
8791: * arg3 (I) int unused
8792: * --------------------------------------------------------------------------
8793: * Returns: ( subraster * ) ptr to subraster corresponding to ellipse
8794: * requested, or NULL for any parsing error
8795: * --------------------------------------------------------------------------
8796: * Notes: o Summary of syntax...
8797: * \circle(xdiam[,ydiam])
8798: * o
8799: * ======================================================================= */
8800: /* --- entry point --- */
8801: subraster *rastcircle ( char **expression, int size, subraster *basesp,
8802: int arg1, int arg2, int arg3 )
8803: {
8804: /* -------------------------------------------------------------------------
8805: Allocations and Declarations
8806: -------------------------------------------------------------------------- */
8807: char *texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
8808: char *qptr=NULL, quads[256]="1234"; /* default to draw all quadrants */
8809: double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/
8810: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
8811: int pixsz = 1; /* pixels are one bit each */
8812: double strtod(), /* convert ascii params to doubles */
8813: xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
8814: int width=0, height=0; /* #pixels width,height of ellipse */
8815: int thickness = 1; /* drawn lines are one pixel thick */
8816: int origin = 55; /* force origin centered */
8817: int circle_raster(), /* draw ellipse in circsp->image */
8818: circle_recurse(); /* for theta0,theta1 args */
8819: /* -------------------------------------------------------------------------
8820: obtain (xdiam[,ydiam]) arguments immediately following \circle command
8821: -------------------------------------------------------------------------- */
8822: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
8823: *expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);
8824: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
8825: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
8826: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
8827: { *qptr = '\000'; /* replace semicolon by '\0' */
8828: strcpy(quads,qptr+1); /* save user-requested quads */
8829: if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
8830: { *qptr = '\000'; /* replace , with null */
8831: theta0 = strtod(quads,NULL); /* theta0 precedes , */
8832: theta1 = strtod(qptr+1,NULL); /* theta1 follows , */
8833: qptr = NULL; } /* signal thetas instead of quads */
8834: else
8835: qptr = quads; } /* set qptr arg for circle_raster()*/
8836: else /* no ;quads at all */
8837: qptr = quads; /* default to all 4 quadrants */
8838: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
8839: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
8840: xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */
8841: if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
8842: ydiam = strtod(xptr+1,NULL); /* in user units */
8843: /* -------------------------------------------------------------------------
8844: calculate width,height, etc
8845: -------------------------------------------------------------------------- */
8846: /* --- calculate width,height in pixels --- */
8847: width = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
8848: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
8849: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8850: fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
8851: width,height,(qptr==NULL?"default":qptr));
8852: /* -------------------------------------------------------------------------
8853: allocate subraster and raster for complete picture
8854: -------------------------------------------------------------------------- */
8855: /* --- sanity check on width,height args --- */
8856: if ( width < 1 || width > 600
8857: || height < 1 || height > 600 ) goto end_of_job;
8858: /* --- allocate and initialize subraster for constructed ellipse --- */
8859: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
8860: == NULL ) goto end_of_job; /* quit if failed */
8861: /* --- initialize ellipse subraster parameters --- */
8862: circsp->type = IMAGERASTER; /* image */
8863: circsp->symdef = NULL; /* not applicable for image */
8864: circsp->baseline = height/2 + 2; /* is a little above center good? */
8865: circsp->size = size; /* size (probably unneeded) */
8866: /* -------------------------------------------------------------------------
8867: draw the ellipse
8868: -------------------------------------------------------------------------- */
8869: if ( qptr != NULL ) /* have quads */
8870: circle_raster ( circsp->image, /* embedded raster image */
8871: 0, 0, /* row0,col0 are upper-left corner */
8872: height-1, width-1, /* row1,col1 are lower-right */
8873: thickness, /* line thickness is 1 pixel */
8874: qptr ); /* "1234" quadrants to be drawn */
8875: else /* have theta0,theta1 */
8876: circle_recurse ( circsp->image, /* embedded raster image */
8877: 0, 0, /* row0,col0 are upper-left corner */
8878: height-1, width-1, /* row1,col1 are lower-right */
8879: thickness, /* line thickness is 1 pixel */
8880: theta0,theta1 ); /* theta0,theta1 arc to be drawn */
8881: /* -------------------------------------------------------------------------
8882: return constructed ellipse to caller
8883: -------------------------------------------------------------------------- */
8884: end_of_job:
8885: if ( workingparam != NULL ) /* caller wants origin */
8886: *workingparam = origin; /* return center origin to caller */
8887: return ( circsp ); /* return ellipse to caller */
8888: } /* --- end-of-function rastcircle() --- */
8889:
8890:
8891: /* ==========================================================================
8892: * Function: rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
8893: * Purpose: \bezier handler, returns subraster corresponding to bezier
8894: * parameters (col0,row0)(col1,row1)(colt,rowt)
8895: * --------------------------------------------------------------------------
8896: * Arguments: expression (I/O) char ** to first char of null-terminated
8897: * string immediately following \bezier to be
8898: * rasterized, and returning ptr immediately
8899: * following last character processed.
8900: * size (I) int containing 0-5 default font size
8901: * basesp (I) subraster * to character (or subexpression)
8902: * immediately preceding \bezier
8903: * (unused, but passed for consistency)
8904: * arg1 (I) int unused
8905: * arg2 (I) int unused
8906: * arg3 (I) int unused
8907: * --------------------------------------------------------------------------
8908: * Returns: ( subraster * ) ptr to subraster corresponding to bezier
8909: * requested, or NULL for any parsing error
8910: * --------------------------------------------------------------------------
8911: * Notes: o Summary of syntax...
8912: * \bezier(col1,row1)(colt,rowt)
8913: * o col0=0,row0=0 assumed, i.e., given by
8914: * \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
8915: * ======================================================================= */
8916: /* --- entry point --- */
8917: subraster *rastbezier ( char **expression, int size, subraster *basesp,
8918: int arg1, int arg2, int arg3 )
8919: {
8920: /* -------------------------------------------------------------------------
8921: Allocations and Declarations
8922: -------------------------------------------------------------------------- */
8923: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
8924: char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
8925: double strtod(); /* convert ascii params to doubles */
8926: double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
8927: rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
8928: rmin=0.0, cmin=0.0, /* minimum r,c */
8929: rmax=0.0, cmax=0.0, /* maximum r,c */
8930: rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
8931: r=0.0, c=0.0; /* some point */
8932: int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
8933: int width=0, height=0; /* dimensions of bezier raster */
8934: int pixsz = 1; /* pixels are one bit each */
8935: /*int thickness = 1;*/ /* drawn lines are one pixel thick */
8936: int origin = 0; /*c's,r's reset to lower-left origin*/
8937: int bezier_raster(); /* draw bezier in bezsp->image */
8938: /* -------------------------------------------------------------------------
8939: obtain (c1,r1)(ct,rt) args immediately following \bezier command
8940: -------------------------------------------------------------------------- */
8941: for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0,r0 1=c1,r1 2=ct,rt */
8942: {
8943: /* --- parse for (r,c) args, and bump expression past them all --- */
8944: *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
8945: if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
8946: /* --- now interpret (r,c) returned in bezexpr --- */
8947: c = r = 0.0; /* init x-coord=col, y-coord=row */
8948: if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
8949: { *xptr = '\000'; /* found it, so replace ',' by '\0'*/
8950: r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
8951: c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */
8952: /* --- store r,c --- */
8953: switch ( iarg )
8954: { case 0: r0=r; c0=c; break;
8955: case 1: r1=r; c1=c; break;
8956: case 2: rt=r; ct=c; break; }
8957: } /* --- end-of-for(iarg) --- */
8958: /* --- determine midpoint and maximum,minimum points --- */
8959: rmid = 0.5*(rt + 0.5*(r0+r1)); /* y-coord at middle of bezier */
8960: cmid = 0.5*(ct + 0.5*(c0+c1)); /* x-coord at middle of bezier */
8961: rmin = min3(r0,r1,rmid); /* lowest row */
8962: cmin = min3(c0,c1,cmid); /* leftmost col */
8963: rmax = max3(r0,r1,rmid); /* highest row */
8964: cmax = max3(c0,c1,cmid); /* rightmost col */
8965: rdelta = rmax-rmin; /* height */
8966: cdelta = cmax-cmin; /* width */
8967: /* --- rescale coords so we start at 0,0 --- */
8968: r0 -= rmin; c0 -= cmin; /* rescale r0,c0 */
8969: r1 -= rmin; c1 -= cmin; /* rescale r1,c1 */
8970: rt -= rmin; ct -= cmin; /* rescale rt,ct */
8971: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
8972: r0 = rdelta - r0 + 1; /* map 0-->height-1, height-1-->0 */
8973: r1 = rdelta - r1 + 1;
8974: rt = rdelta - rt + 1;
8975: /* --- determine width,height of raster needed for bezier --- */
8976: width = (int)(cdelta + 0.9999) + 1; /* round width up */
8977: height = (int)(rdelta + 0.9999) + 1; /* round height up */
8978: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
8979: fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
8980: "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
8981: width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
8982: /* -------------------------------------------------------------------------
8983: allocate raster
8984: -------------------------------------------------------------------------- */
8985: /* --- sanity check on width,height args --- */
8986: if ( width < 1 || width > 600
8987: || height < 1 || height > 600 ) goto end_of_job;
8988: /* --- allocate and initialize subraster for constructed bezier --- */
8989: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
8990: == NULL ) goto end_of_job; /* quit if failed */
8991: /* --- initialize bezier subraster parameters --- */
8992: bezsp->type = IMAGERASTER; /* image */
8993: bezsp->symdef = NULL; /* not applicable for image */
8994: bezsp->baseline = height/2 + 2; /* is a little above center good? */
8995: bezsp->size = size; /* size (probably unneeded) */
8996: /* -------------------------------------------------------------------------
8997: draw the bezier
8998: -------------------------------------------------------------------------- */
8999: bezier_raster ( bezsp->image, /* embedded raster image */
9000: r0, c0, /* row0,col0 are lower-left corner */
9001: r1, c1, /* row1,col1 are upper-right */
9002: rt, ct ); /* bezier tangent point */
9003: /* -------------------------------------------------------------------------
9004: return constructed bezier to caller
9005: -------------------------------------------------------------------------- */
9006: end_of_job:
9007: if ( workingparam != NULL ) /* caller wants origin */
9008: *workingparam = origin; /* return center origin to caller */
9009: return ( bezsp ); /* return bezier to caller */
9010: } /* --- end-of-function rastbezier() --- */
9011:
9012:
9013: /* ==========================================================================
9014: * Function: rastraise ( expression, size, basesp, arg1, arg2, arg3 )
9015: * Purpose: \raisebox{lift}{subexpression} handler, returns subraster
9016: * containing subexpression with its baseline "lifted" by lift
9017: * pixels, scaled by \unitlength, or "lowered" if lift arg
9018: * negative
9019: * --------------------------------------------------------------------------
9020: * Arguments: expression (I/O) char ** to first char of null-terminated
9021: * string immediately following \raisebox to be
9022: * rasterized, and returning ptr immediately
9023: * following last character processed.
9024: * size (I) int containing 0-5 default font size
9025: * basesp (I) subraster * to character (or subexpression)
9026: * immediately preceding \rotatebox
9027: * (unused, but passed for consistency)
9028: * arg1 (I) int unused
9029: * arg2 (I) int unused
9030: * arg3 (I) int unused
9031: * --------------------------------------------------------------------------
9032: * Returns: ( subraster * ) ptr to subraster corresponding to \raisebox
9033: * requested, or NULL for any parsing error
9034: * --------------------------------------------------------------------------
9035: * Notes: o Summary of syntax...
9036: * \raisebox{lift}{subexpression}
9037: * o
9038: * ======================================================================= */
9039: /* --- entry point --- */
9040: subraster *rastraise ( char **expression, int size, subraster *basesp,
9041: int arg1, int arg2, int arg3 )
9042: {
9043: /* -------------------------------------------------------------------------
9044: Allocations and Declarations
9045: -------------------------------------------------------------------------- */
9046: char *texsubexpr(), subexpr[8192], *liftexpr=subexpr; /* args */
9047: subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
9048: int lift=0; /* amount to raise/lower baseline */
9049: /* -------------------------------------------------------------------------
9050: obtain {lift} argument immediately following \raisebox command
9051: -------------------------------------------------------------------------- */
9052: /* --- parse for {lift} arg, and bump expression past it --- */
9053: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
9054: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
9055: lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/
9056: if ( abs(lift) > 200 ) lift=0; /* sanity check */
9057: /* -------------------------------------------------------------------------
9058: obtain {subexpr} argument after {lift}, and rasterize it
9059: -------------------------------------------------------------------------- */
9060: /* --- parse for {subexpr} arg, and bump expression past it --- */
9061: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9062: /* --- rasterize subexpression to be raised/lowered --- */
9063: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
9064: == NULL ) goto end_of_job; /* and quit if failed */
9065: /* -------------------------------------------------------------------------
9066: raise/lower baseline and return it to caller
9067: -------------------------------------------------------------------------- */
9068: /* --- raise/lower baseline --- */
9069: raisesp->baseline += lift; /* new baseline (no height checks) */
9070: /* --- return raised subexpr to caller --- */
9071: end_of_job:
9072: return ( raisesp ); /* return raised subexpr to caller */
9073: } /* --- end-of-function rastraise() --- */
9074:
9075:
9076: /* ==========================================================================
9077: * Function: rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
9078: * Purpose: \rotatebox{degrees}{subexpression} handler, returns subraster
9079: * containing subexpression rotated by degrees (counterclockwise
9080: * if degrees positive)
9081: * --------------------------------------------------------------------------
9082: * Arguments: expression (I/O) char ** to first char of null-terminated
9083: * string immediately following \rotatebox to be
9084: * rasterized, and returning ptr immediately
9085: * following last character processed.
9086: * size (I) int containing 0-5 default font size
9087: * basesp (I) subraster * to character (or subexpression)
9088: * immediately preceding \rotatebox
9089: * (unused, but passed for consistency)
9090: * arg1 (I) int unused
9091: * arg2 (I) int unused
9092: * arg3 (I) int unused
9093: * --------------------------------------------------------------------------
9094: * Returns: ( subraster * ) ptr to subraster corresponding to \rotatebox
9095: * requested, or NULL for any parsing error
9096: * --------------------------------------------------------------------------
9097: * Notes: o Summary of syntax...
9098: * \rotatebox{degrees}{subexpression}
9099: * o
9100: * ======================================================================= */
9101: /* --- entry point --- */
9102: subraster *rastrotate ( char **expression, int size, subraster *basesp,
9103: int arg1, int arg2, int arg3 )
9104: {
9105: /* -------------------------------------------------------------------------
9106: Allocations and Declarations
9107: -------------------------------------------------------------------------- */
9108: char *texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */
9109: subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */
9110: raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
9111: int delete_raster(); /* delete intermediate rasters */
9112: int baseline=0; /* baseline of rasterized image */
9113: double strtod(), /* convert ascii params to doubles */
9114: degrees=0.0, ipart,fpart; /* degrees to be rotated */
9115: int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
9116: int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
9117: /* -------------------------------------------------------------------------
9118: obtain {degrees} argument immediately following \rotatebox command
9119: -------------------------------------------------------------------------- */
9120: /* --- parse for {degrees} arg, and bump expression past it --- */
9121: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
9122: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
9123: degrees = strtod(degexpr,NULL); /* degrees to be rotated */
9124: if ( degrees < 0.0 ) /* clockwise rotation desired */
9125: { degrees = -degrees; /* flip sign so degrees positive */
9126: isneg = 1; } /* and set flag to indicate flip */
9127: fpart = modf(degrees,&ipart); /* integer and fractional parts */
9128: ipart = (double)(((int)degrees)%360); /* degrees mod 360 */
9129: degrees = ipart + fpart; /* restore fractional part */
9130: if ( isneg ) /* if clockwise rotation requested */
9131: degrees = 360.0 - degrees; /* do equivalent counterclockwise */
9132: idegrees = (int)(degrees+0.5); /* integer degrees */
9133: n90 = idegrees/90; /* degrees is n90 multiples of 90 */
9134: isn90 = (90*n90==idegrees); /*true if degrees is multiple of 90*/
9135: isn90 = 1; /* forced true for time being */
9136: /* -------------------------------------------------------------------------
9137: obtain {subexpr} argument after {degrees}, and rasterize it
9138: -------------------------------------------------------------------------- */
9139: /* --- parse for {subexpr} arg, and bump expression past it --- */
9140: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9141: /* --- rasterize subexpression to be rotated --- */
9142: if ( (rotsp = rasterize(subexpr,size)) /* rasterize subexpression */
9143: == NULL ) goto end_of_job; /* and quit if failed */
9144: /* --- return unmodified image if no rotation requested --- */
9145: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
9146: /* --- extract params for image to be rotated --- */
9147: rotrp = rotsp->image; /* unrotated rasterized image */
9148: baseline = rotsp->baseline; /* and baseline of that image */
9149: /* -------------------------------------------------------------------------
9150: rotate by multiples of 90 degrees
9151: -------------------------------------------------------------------------- */
9152: if ( isn90 ) /* rotation by multiples of 90 */
9153: if ( n90 > 0 ) /* do nothing for 0 degrees */
9154: {
9155: n90 = 4-n90; /* rasrot() rotates clockwise */
9156: while ( n90 > 0 ) /* still have remaining rotations */
9157: { raster *nextrp = rastrot(rotrp); /* rotate raster image */
9158: if ( nextrp == NULL ) break; /* something's terribly wrong */
9159: delete_raster(rotrp); /* free previous raster image */
9160: rotrp = nextrp; /* and replace it with rotated one */
9161: n90--; } /* decrement remaining count */
9162: } /* --- end-of-if(isn90) --- */
9163: /* -------------------------------------------------------------------------
9164: requested rotation not multiple of 90 degrees
9165: -------------------------------------------------------------------------- */
9166: if ( !isn90 ) /* explicitly construct rotation */
9167: { ; } /* not yet implemented */
9168: /* -------------------------------------------------------------------------
9169: re-populate subraster envelope with rotated image
9170: -------------------------------------------------------------------------- */
9171: /* --- re-init various subraster parameters, embedding raster in it --- */
9172: if ( rotrp != NULL ) /* rotated raster constructed okay */
9173: { rotsp->type = IMAGERASTER; /* signal constructed image */
9174: rotsp->image = rotrp; /* raster we just constructed */
9175: /* --- now try to guess pleasing baseline --- */
9176: if ( idegrees > 2 ) /* leave unchanged if unrotated */
9177: if ( strlen(subexpr) < 3 /* we rotated a short expression */
9178: || abs(idegrees-180) < 3 ) /* or just turned it upside-down */
9179: baseline = rotrp->height - 1; /* so set with nothing descending */
9180: else /* rotated a long expression */
9181: baseline = (65*(rotrp->height-1))/100; /* roughly center long expr */
9182: rotsp->baseline = baseline; } /* set baseline as calculated above*/
9183: /* --- return rotated subexpr to caller --- */
9184: end_of_job:
9185: return ( rotsp ); /*return rotated subexpr to caller*/
9186: } /* --- end-of-function rastrotate() --- */
9187:
9188:
9189: /* ==========================================================================
9190: * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
9191: * Purpose: \fbox{subexpression} handler, returns subraster
9192: * containing subexpression with frame box drawn around it
9193: * --------------------------------------------------------------------------
9194: * Arguments: expression (I/O) char ** to first char of null-terminated
9195: * string immediately following \fbox to be
9196: * rasterized, and returning ptr immediately
9197: * following last character processed.
9198: * size (I) int containing 0-5 default font size
9199: * basesp (I) subraster * to character (or subexpression)
9200: * immediately preceding \fbox
9201: * (unused, but passed for consistency)
9202: * arg1 (I) int unused
9203: * arg2 (I) int unused
9204: * arg3 (I) int unused
9205: * --------------------------------------------------------------------------
9206: * Returns: ( subraster * ) ptr to subraster corresponding to \fbox
9207: * requested, or NULL for any parsing error
9208: * --------------------------------------------------------------------------
9209: * Notes: o Summary of syntax...
9210: * \fbox[width][height]{subexpression}
9211: * o
9212: * ======================================================================= */
9213: /* --- entry point --- */
9214: subraster *rastfbox ( char **expression, int size, subraster *basesp,
9215: int arg1, int arg2, int arg3 )
9216: {
9217: /* -------------------------------------------------------------------------
9218: Allocations and Declarations
9219: -------------------------------------------------------------------------- */
9220: char *texsubexpr(), subexpr[8192], widtharg[512]; /* args */
9221: subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
9222: raster *border_raster(), *bp=NULL; /* framed image raster */
9223: double strtod(); /* interpret [width][height] */
9224: int fwidth=6, fthick=1; /*extra frame width, line thickness*/
9225: int width=(-1), height=(-1), /* optional [width][height] args */
9226: iscompose = 0; /* set true if optional args given */
9227: /* -------------------------------------------------------------------------
9228: obtain optional [width][height] arguments immediately following \fbox
9229: -------------------------------------------------------------------------- */
9230: /* --- first check for optional \fbox[width] --- */
9231: if ( *(*expression) == '[' ) /* check for []-enclosed width arg */
9232: { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
9233: if ( *widtharg != '\000' ) /* got widtharg */
9234: { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
9235: height = 1; fwidth = 2; iscompose = 1; }
9236: } /* --- end-of-if(**expression=='[') --- */
9237: if ( width > 0 ) /* found leading [width], so... */
9238: if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
9239: { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
9240: if ( *widtharg != '\000' ) /* got widtharg */
9241: { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
9242: fwidth = 0; } /* no extra border */
9243: } /* --- end-of-if(**expression=='[') --- */
9244: /* -------------------------------------------------------------------------
9245: obtain {subexpr} argument
9246: -------------------------------------------------------------------------- */
9247: /* --- parse for {subexpr} arg, and bump expression past it --- */
9248: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9249: /* --- rasterize subexpression to be framed --- */
9250: if ( width<0 || height<0 ) /* no explicit dimensions given */
9251: { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
9252: == NULL ) goto end_of_job; } /* and quit if failed */
9253: else
9254: { char composexpr[8192]; /* compose subexpr with empty box */
9255: sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
9256: width,height,subexpr);
9257: if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
9258: == NULL ) goto end_of_job; } /* and quit if failed */
9259: /* -------------------------------------------------------------------------
9260: draw frame, reset params, and return it to caller
9261: -------------------------------------------------------------------------- */
9262: /* --- draw border --- */
9263: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
9264: == NULL ) goto end_of_job; /* draw border and quit if failed */
9265: /* --- replace original image and raise baseline to accommodate frame --- */
9266: framesp->image = bp; /* replace image with framed one */
9267: if ( !iscompose ) /* simple border around subexpr */
9268: framesp->baseline += fwidth; /* so just raise baseline */
9269: else
9270: framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
9271: /* --- return framed subexpr to caller --- */
9272: end_of_job:
9273: return ( framesp ); /* return framed subexpr to caller */
9274: } /* --- end-of-function rastfbox() --- */
9275:
9276:
9277: /* ==========================================================================
9278: * Function: rastinput ( expression, size, basesp, arg1, arg2, arg3 )
9279: * Purpose: \input{filename} handler, reads filename and returns
9280: * subraster containing image of expression read from filename
9281: * --------------------------------------------------------------------------
9282: * Arguments: expression (I/O) char ** to first char of null-terminated
9283: * string immediately following \input to be
9284: * rasterized, and returning ptr immediately
9285: * following last character processed.
9286: * size (I) int containing 0-5 default font size
9287: * basesp (I) subraster * to character (or subexpression)
9288: * immediately preceding \input
9289: * (unused, but passed for consistency)
9290: * arg1 (I) int unused
9291: * arg2 (I) int unused
9292: * arg3 (I) int unused
9293: * --------------------------------------------------------------------------
9294: * Returns: ( subraster * ) ptr to subraster corresponding to expression
9295: * in filename, or NULL for any parsing error
9296: * --------------------------------------------------------------------------
9297: * Notes: o Summary of syntax...
9298: * \input{filename}
9299: * o
9300: * ======================================================================= */
9301: /* --- entry point --- */
9302: subraster *rastinput ( char **expression, int size, subraster *basesp,
9303: int arg1, int arg2, int arg3 )
9304: {
9305: /* -------------------------------------------------------------------------
9306: Allocations and Declarations
9307: -------------------------------------------------------------------------- */
9308: char *texsubexpr(), tag[512]="\000", filename[1024]="\000"; /* args */
9309: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
9310: int status, rastreadfile(); /* read input file */
9311: int format=0, npts=0; /* don't reformat (numerical) input */
1.2 ! albertel 9312: char subexpr[8192] = "\000", /* concatanated lines from input file */
1.1 albertel 9313: *mimeprep(), /* preprocess inputted data */
1.2 ! albertel 9314: *dbltoa(), *reformat=NULL; /* reformat numerical input */
1.1 albertel 9315: /* -------------------------------------------------------------------------
9316: obtain [tag]{filename} argument
9317: -------------------------------------------------------------------------- */
9318: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
9319: if ( *(*expression) == '[' ) /* check for []-enclosed value */
9320: { char argfld[2048]; /* optional argument field */
9321: *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);
1.2 ! albertel 9322: if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
! 9323: { format = 1; /* signal dtoa()/dbltoa() format */
1.1 albertel 9324: if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
9325: npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
9326: if ( format == 0 ) /* reformat not requested */
9327: strcpy(tag,argfld); } /* so interpret arg as tag */
9328: /* --- parse for {filename} arg, and bump expression past it --- */
9329: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
9330: /* --- check for alternate filename:tag --- */
9331: if ( *filename != '\000' /* got filename */
9332: /*&& *tag == '\000'*/ ) /* but no [tag] */
9333: { char *delim = strchr(filename,':'); /* look for : in filename:tag */
9334: if ( delim != (char *)NULL ) /* found it */
9335: { *delim = '\000'; /* null-terminate filename at : */
9336: strcpy(tag,delim+1); } } /* and stuff after : is tag */
9337: /* --------------------------------------------------------------------------
9338: Read file and rasterize constructed subexpression
9339: -------------------------------------------------------------------------- */
1.2 ! albertel 9340: status = rastreadfile(filename,0,tag,subexpr); /* read file */
1.1 albertel 9341: if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
9342: /* --- rasterize input subexpression --- */
9343: mimeprep(subexpr); /* preprocess subexpression */
1.2 ! albertel 9344: if ( format == 1 ) /* dtoa()/dbltoa() */
1.1 albertel 9345: { double d = strtod(subexpr,NULL); /* interpret subexpr as double */
9346: if ( d != 0.0 ) /* conversion to double successful */
1.2 ! albertel 9347: if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
1.1 albertel 9348: strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
9349: inputsp = rasterize(subexpr,size); /* rasterize subexpression */
9350: /* --- return input image to caller --- */
9351: end_of_job:
9352: return ( inputsp ); /* return input image to caller */
9353: } /* --- end-of-function rastinput() --- */
9354:
9355:
9356: /* ==========================================================================
9357: * Function: rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
9358: * Purpose: \counter[value]{filename} handler, returns subraster
9359: * containing image of counter value read from filename
9360: * (or optional [value]), and increments counter
9361: * --------------------------------------------------------------------------
9362: * Arguments: expression (I/O) char ** to first char of null-terminated
9363: * string immediately following \counter to be
9364: * rasterized, and returning ptr immediately
9365: * following last character processed.
9366: * size (I) int containing 0-5 default font size
9367: * basesp (I) subraster * to character (or subexpression)
9368: * immediately preceding \counter
9369: * (unused, but passed for consistency)
9370: * arg1 (I) int unused
9371: * arg2 (I) int unused
9372: * arg3 (I) int unused
9373: * --------------------------------------------------------------------------
9374: * Returns: ( subraster * ) ptr to subraster corresponding to \counter
9375: * requested, or NULL for any parsing error
9376: * --------------------------------------------------------------------------
9377: * Notes: o Summary of syntax...
1.2 ! albertel 9378: * \counter[value][logfile]{filename:tag}
! 9379: * o :tag is optional
1.1 albertel 9380: * ======================================================================= */
9381: /* --- entry point --- */
9382: subraster *rastcounter ( char **expression, int size, subraster *basesp,
9383: int arg1, int arg2, int arg3 )
9384: {
9385: /* -------------------------------------------------------------------------
9386: Allocations and Declarations
9387: -------------------------------------------------------------------------- */
9388: char *texsubexpr(), filename[1024]="\000", /* counter file */
1.2 ! albertel 9389: logfile[1024]="\000", tag[512]="\000"; /*optional log file and tag*/
1.1 albertel 9390: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
9391: FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
1.2 ! albertel 9392: int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
! 9393: isstrict = 1; /* true to only write to existing files */
! 9394: char text[8192] = "1_", /* only line in counter file without tags */
1.1 albertel 9395: *delim = NULL, /* delimiter in text */
1.2 ! albertel 9396: utext[128] = "1_", /* default delimiter */
1.1 albertel 9397: *udelim = utext+1; /* underscore delimiter */
1.2 ! albertel 9398: char *rasteditfilename(), /* edit log file name */
! 9399: *timestamp(), /* timestamp for logging */
! 9400: *dbltoa(); /* double to comma-separated ascii */
1.1 albertel 9401: int counter = 1, /* atoi(text) (after _ removed, if present) */
1.2 ! albertel 9402: value = 1, /* optional [value] argument */
! 9403: gotvalue = 0, /* set true if [value] supplied */
! 9404: isdelta = 0, /* set true if [+value] or [-value] is delta*/
1.1 albertel 9405: ordindex = (-1); /* ordinal[] index to append ordinal suffix */
9406: /*--- ordinal suffixes based on units digit of counter ---*/
9407: static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
9408: static char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
9409: static int commentvar = 1; /* logvars[commentvar] replaced by comment */
9410: /* -------------------------------------------------------------------------
9411: first obtain optional [value][logfile] args immediately following \counter
9412: -------------------------------------------------------------------------- */
9413: /* --- first check for optional \counter[value] --- */
9414: if ( *(*expression) == '[' ) /* check for []-enclosed value */
1.2 ! albertel 9415: { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
! 9416: if ( *text != '\000' ) /* got counter value (or logfile) */
1.1 albertel 9417: if ( strlen(text) >= 1 ) /* and it's not an empty string */
1.2 ! albertel 9418: if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
! 9419: gotvalue = 1; /* signal we got optional value */
! 9420: else /* not +-digit, so must be logfile */
! 9421: strcpy(logfile,text); /* so just copy it */
1.1 albertel 9422: } /* --- end-of-if(**expression=='[') --- */
9423: /* --- next check for optional \counter[][logfile] --- */
9424: if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
9425: { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
1.2 ! albertel 9426: if ( *filename != '\000' ) /* got logfile (or counter value) */
1.1 albertel 9427: if ( strlen(filename) >= 1 ) /* and it's not an empty string */
1.2 ! albertel 9428: if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
! 9429: || gotvalue ) /* or we already got counter value */
! 9430: strcpy(logfile,filename); /* so just copy it */
! 9431: else /* leading +-digit must be value */
! 9432: { strcpy(text,filename); /* copy value to text line */
! 9433: gotvalue = 1; } /* and signal we got optional value*/
1.1 albertel 9434: } /* --- end-of-if(**expression=='[') --- */
1.2 ! albertel 9435: /* --- evaluate [value] if present --- */
! 9436: if ( gotvalue ) { /*leading +-digit should be in text*/
! 9437: if ( *text == '+' ) isdelta = (+1); /* signal adding */
! 9438: if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
! 9439: value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
! 9440: if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
! 9441: counter = value; /* re-init counter */
! 9442: } /* --- end-of-if(gotvalue) --- */
1.1 albertel 9443: /* -------------------------------------------------------------------------
9444: obtain counter {filename} argument
9445: -------------------------------------------------------------------------- */
9446: /* --- parse for {filename} arg, and bump expression past it --- */
9447: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
9448: /* --- check for counter filename:tag --- */
9449: if ( *filename != '\000' ) /* got filename */
9450: if ( (delim=strchr(filename,':')) /* look for : in filename:tag */
9451: != (char *)NULL ) /* found it */
9452: { *delim = '\000'; /* null-terminate filename at : */
9453: strcpy(tag,delim+1); } /* and stuff after : is tag */
9454: /* --------------------------------------------------------------------------
9455: Read and parse file, increment and rewrite counter (with optional underscore)
9456: -------------------------------------------------------------------------- */
9457: if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
9458: {
9459: /* --- read and interpret first (and only) line from counter file --- */
1.2 ! albertel 9460: if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
! 9461: if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
! 9462: { char *vdelim = NULL; /* underscore delim from file */
! 9463: double fileval = strtod(text,&vdelim); /* value and delim from file */
! 9464: counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
! 9465: counter += value; /* bump count by 1 or add/sub delta*/
! 9466: if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
1.1 albertel 9467: /* --- check for ordinal suffix --- */
9468: if ( udelim != (char *)NULL ) /* have some delim after value */
9469: if ( *udelim == '_' ) /* underscore signals ordinal */
1.2 ! albertel 9470: { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
! 9471: ordindex = abscount%10; /* least significant digit */
! 9472: if ( abscount >= 10 ) /* counter is 10 or greater */
! 9473: if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
1.1 albertel 9474: ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
9475: /* --- rewrite counter file --- */
1.2 ! albertel 9476: if ( status >= 0 ) /* file was read okay */
! 9477: { sprintf(text,"%d",counter); /*build image of incremented counter*/
! 9478: if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
! 9479: if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
! 9480: status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
1.1 albertel 9481: } /* --- end-of-if(strlen(filename)>1) --- */
9482: /* --------------------------------------------------------------------------
9483: log counter request
9484: -------------------------------------------------------------------------- */
9485: if ( strlen(logfile) > 1 ) /* optional [logfile] given */
9486: {
9487: char comment[1024] = "\000", /* embedded comment, logfile:comment*/
9488: *commptr = strchr(logfile,':'); /* check for : signalling comment */
1.2 ! albertel 9489: int islogokay = 1; /* logfile must exist if isstrict */
1.1 albertel 9490: if ( commptr != NULL ) /* have embedded comment */
9491: { strcpy(comment,commptr+1); /* comment follows : */
9492: *commptr = '\000'; } /* null-terminate actual logfile */
1.2 ! albertel 9493: strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
! 9494: if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
! 9495: else if ( isstrict ) { /*okay, but only write if it exists*/
! 9496: if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
! 9497: islogokay = 0; /* so don't write log file */
! 9498: else fclose(logfp); } /* close file opened for test read */
! 9499: if ( islogokay ) /* okay to write logfile */
! 9500: if ( (logfp = fopen(logfile,"a")) /* open logfile */
! 9501: != (FILE *)NULL ) { /* opened successfully for append */
! 9502: int ilog=0; /* logvars[] index */
! 9503: fprintf(logfp,"%s ",timestamp(TZDELTA,0)); /* first emit timestamp */
! 9504: if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
! 9505: else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
! 9506: fprintf(logfp,"=%d",counter); /* emit counter value */
! 9507: if ( status < 1 ) /* read or re-write failed */
! 9508: fprintf(logfp,"(%s %d)","error status",status); /* emit error */
! 9509: for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
! 9510: if ( ilog == commentvar /* replace with comment... */
! 9511: && commptr != NULL ) /* ...if available */
! 9512: fprintf(logfp," %.256s",comment); /* log embedded comment */
! 9513: else
! 9514: { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
! 9515: fprintf(logfp," %.64s", /* log variable */
1.1 albertel 9516: (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
1.2 ! albertel 9517: fprintf(logfp,"\n"); /* terminating newline */
! 9518: fclose(logfp); /* close logfile */
! 9519: } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
1.1 albertel 9520: } /* --- end-of-if(strlen(logfile)>1) --- */
9521: /* --------------------------------------------------------------------------
9522: construct counter expression and rasterize it
9523: -------------------------------------------------------------------------- */
9524: /* --- construct expression --- */
9525: /*sprintf(text,"%d",counter);*/ /* start with counter */
1.2 ! albertel 9526: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
1.1 albertel 9527: if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
9528: { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
9529: strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
9530: strcat(text,"}}"); } /* finish with }} */
9531: /* --- rasterize it --- */
9532: countersp = rasterize(text,size); /* rasterize counter subexpression */
9533: /* --- return counter image to caller --- */
9534: /*end_of_job:*/
9535: return ( countersp ); /* return counter image to caller */
9536: } /* --- end-of-function rastcounter() --- */
9537:
9538:
9539: /* ==========================================================================
1.2 ! albertel 9540: * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
! 9541: * Purpose: handle \today
! 9542: * --------------------------------------------------------------------------
! 9543: * Arguments: expression (I/O) char ** to first char of null-terminated
! 9544: * string immediately following \today,
! 9545: * and returning ptr immediately
! 9546: * following last character processed.
! 9547: * size (I) int containing 0-5 default font size
! 9548: * basesp (I) subraster * to character (or subexpression)
! 9549: * immediately preceding \today
! 9550: * (unused, but passed for consistency)
! 9551: * arg1 (I) int unused
! 9552: * arg2 (I) int unused
! 9553: * arg3 (I) int unused
! 9554: * --------------------------------------------------------------------------
! 9555: * Returns: ( subraster * ) subraster ptr to date stamp
! 9556: * --------------------------------------------------------------------------
! 9557: * Notes: o
! 9558: * ======================================================================= */
! 9559: /* --- entry point --- */
! 9560: subraster *rasttoday ( char **expression, int size, subraster *basesp,
! 9561: int arg1, int arg2, int arg3 )
! 9562: {
! 9563: /* -------------------------------------------------------------------------
! 9564: Allocations and Declarations
! 9565: -------------------------------------------------------------------------- */
! 9566: char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
! 9567: char *timestamp(), *today=optarg; /* timestamp to be rasterized */
! 9568: subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
! 9569: int ifmt=1, tzdelta=0; /* default timestamp() args */
! 9570: /* -------------------------------------------------------------------------
! 9571: Get optional args \today[+/-tzdelta,ifmt]
! 9572: -------------------------------------------------------------------------- */
! 9573: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
! 9574: if ( *(*expression) == '[' ) /* check for []-enclosed value */
! 9575: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
! 9576: if ( *optarg != '\000' ) /* got optional arg */
! 9577: { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
! 9578: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
! 9579: if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
! 9580: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
! 9581: { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
! 9582: if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
! 9583: tzdelta = atoi(arg); /* so interpret arg as tzdelta */
! 9584: else ifmt = atoi(arg); } /* else interpret args as ifmt */
! 9585: } /* --- end-of-if(*optarg!='\0') --- */
! 9586: } /* --- end-of-if(**expression=='[') --- */
! 9587: /* -------------------------------------------------------------------------
! 9588: Get timestamp and rasterize it
! 9589: -------------------------------------------------------------------------- */
! 9590: strcpy(today,"\\text{"); /* rasterize timestamp as text */
! 9591: strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
! 9592: strcat(today,"}"); /* terminate \text{} braces */
! 9593: todaysp = rasterize(today,size); /* rasterize timestamp */
! 9594: /* --- return timestamp raster to caller --- */
! 9595: /*end_of_job:*/
! 9596: return ( todaysp ); /* return timestamp to caller */
! 9597: } /* --- end-of-function rasttoday() --- */
! 9598:
! 9599:
! 9600: /* ==========================================================================
! 9601: * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
! 9602: * Purpose: handle \calendar
! 9603: * --------------------------------------------------------------------------
! 9604: * Arguments: expression (I/O) char ** to first char of null-terminated
! 9605: * string immediately following \calendar
! 9606: * and returning ptr immediately
! 9607: * following last character processed.
! 9608: * size (I) int containing 0-5 default font size
! 9609: * basesp (I) subraster * to character (or subexpression)
! 9610: * immediately preceding \calendar
! 9611: * (unused, but passed for consistency)
! 9612: * arg1 (I) int unused
! 9613: * arg2 (I) int unused
! 9614: * arg3 (I) int unused
! 9615: * --------------------------------------------------------------------------
! 9616: * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
! 9617: * --------------------------------------------------------------------------
! 9618: * Notes: o
! 9619: * ======================================================================= */
! 9620: /* --- entry point --- */
! 9621: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
! 9622: int arg1, int arg2, int arg3 )
! 9623: {
! 9624: /* -------------------------------------------------------------------------
! 9625: Allocations and Declarations
! 9626: -------------------------------------------------------------------------- */
! 9627: char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
! 9628: char *calendar(), *calstr=NULL; /* calendar to be rasterized */
! 9629: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
! 9630: int year=0,month=0,day=0, argval=0; /* default calendar() args */
! 9631: /* -------------------------------------------------------------------------
! 9632: Get optional args \today[+/-tzdelta,ifmt]
! 9633: -------------------------------------------------------------------------- */
! 9634: /* --- check for optional \calendar[year,month] --- */
! 9635: if ( *(*expression) == '[' ) /* check for []-enclosed value */
! 9636: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
! 9637: if ( *optarg != '\000' ) /* got optional arg */
! 9638: { char *comma = strchr(optarg,','), /* comma between year,month */
! 9639: *comma2 = NULL; /* second comma before day */
! 9640: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
! 9641: if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
! 9642: if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
! 9643: { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
! 9644: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
! 9645: { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
! 9646: argval = atoi(arg); /* interpret arg as integer */
! 9647: if ( iarg < 3 ) /* first two args are month,year */
! 9648: {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
! 9649: else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
! 9650: else /* only 3rd arg can be day */
! 9651: if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
! 9652: } /* --- end-of-if(*optarg!='\0') --- */
! 9653: } /* --- end-of-if(**expression=='[') --- */
! 9654: /* -------------------------------------------------------------------------
! 9655: Get calendar string and rasterize it
! 9656: -------------------------------------------------------------------------- */
! 9657: if ( msgfp!= NULL && msglevel>=9 )
! 9658: fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
! 9659: year,month,day);
! 9660: calstr = calendar(year,month,day); /* get calendar string */
! 9661: calendarsp = rasterize(calstr,size); /* rasterize calendar string */
! 9662: /* --- return calendar raster to caller --- */
! 9663: /*end_of_job:*/
! 9664: return ( calendarsp ); /* return calendar to caller */
! 9665: } /* --- end-of-function rastcalendar() --- */
! 9666:
! 9667:
! 9668: /* ==========================================================================
1.1 albertel 9669: * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
9670: * Purpose: no op -- flush \escape without error
9671: * --------------------------------------------------------------------------
9672: * Arguments: expression (I/O) char ** to first char of null-terminated
9673: * string immediately following \escape to be
9674: * flushed, and returning ptr immediately
9675: * following last character processed.
9676: * size (I) int containing 0-5 default font size
9677: * basesp (I) subraster * to character (or subexpression)
1.2 ! albertel 9678: * immediately preceding \escape
1.1 albertel 9679: * (unused, but passed for consistency)
9680: * nargs (I) int containing number of {}-args after
9681: * \escape to be flushed along with it
9682: * arg2 (I) int unused
9683: * arg3 (I) int unused
9684: * --------------------------------------------------------------------------
9685: * Returns: ( subraster * ) NULL subraster ptr
9686: * --------------------------------------------------------------------------
9687: * Notes: o
9688: * ======================================================================= */
9689: /* --- entry point --- */
9690: subraster *rastnoop ( char **expression, int size, subraster *basesp,
9691: int nargs, int arg2, int arg3 )
9692: {
9693: /* -------------------------------------------------------------------------
9694: Allocations and Declarations
9695: -------------------------------------------------------------------------- */
9696: char *texsubexpr(), subexpr[8192]; /* flush dummy args eaten by \escape*/
9697: subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */
9698: /* --- flush accompanying args if necessary --- */
9699: if ( nargs != NOVALUE /* not unspecified */
9700: && nargs > 0 ) /* and args to be flushed */
9701: while ( --nargs >= 0 ) /* count down */
9702: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
9703: /* --- return null ptr to caller --- */
9704: /*end_of_job:*/
9705: return ( noopsp ); /* return NULL ptr to caller */
9706: } /* --- end-of-function rastnoop() --- */
9707:
9708:
9709: /* ==========================================================================
9710: * Function: rastopenfile ( filename, mode )
9711: * Purpose: Opens filename[.tex] in mode, returning FILE *
9712: * --------------------------------------------------------------------------
9713: * Arguments: filename (I/O) char * to null-terminated string containing
9714: * name of file to open (preceded by path
9715: * relative to mimetex executable)
9716: * If fopen() fails, .tex appeneded,
9717: * and returned if that fopen() succeeds
9718: * mode (I) char * to null-terminated string containing
9719: * fopen() mode
9720: * --------------------------------------------------------------------------
9721: * Returns: ( FILE * ) pointer to opened file, or NULL if error
9722: * --------------------------------------------------------------------------
9723: * Notes: o
9724: * ======================================================================= */
9725: /* --- entry point --- */
9726: FILE *rastopenfile ( char *filename, char *mode )
9727: {
9728: /* -------------------------------------------------------------------------
9729: Allocations and Declarations
9730: -------------------------------------------------------------------------- */
9731: FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
1.2 ! albertel 9732: char texfile[2048] = "\000", /* local, edited copy of filename */
! 9733: *rasteditfilename(), /* prepend pathprefix if necessary */
1.1 albertel 9734: amode[128] = "r"; /* test open mode if arg mode=NULL */
1.2 ! albertel 9735: int ismode = 0; /* true of mode!=NULL */
1.1 albertel 9736: /* --------------------------------------------------------------------------
9737: Check mode and open file
9738: -------------------------------------------------------------------------- */
1.2 ! albertel 9739: /* --- edit filename --- */
! 9740: strcpy(texfile,rasteditfilename(filename)); /*edited copy of input filename*/
1.1 albertel 9741: /* --- check mode --- */
9742: if ( mode != (char *)NULL ) /* caller passed mode arg */
9743: if ( *mode != '\000' ) /* and it's not an empty string */
9744: { ismode = 1; /* so flip mode flag true */
9745: strcpy(amode,mode); /* and replace "r" with caller's */
9746: compress(amode,' '); } /* remove embedded blanks */
9747: /* --- open filename or filename.tex --- */
9748: if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/
9749: if ( (fp = fopen(texfile,amode)) /* try opening given filename */
9750: == NULL ) /* failed to open given filename */
9751: { strcpy(filename,texfile); /* signal possible filename error */
9752: strcat(texfile,".tex"); /* but first try adding .tex */
9753: if ( (fp = fopen(texfile,amode)) /* now try opening filename.tex */
9754: != NULL ) /* filename.tex succeeded */
9755: strcpy(filename,texfile); } /* replace caller's filename */
9756: /* --- close file if only opened to check name --- */
9757: if ( !ismode && fp!=NULL ) /* no mode, so just checking */
9758: fclose(fp); /* close file, fp signals success */
9759: /* --- return fp or NULL to caller --- */
9760: /*end_of_job:*/
9761: if ( msglevel>=9 && msgfp!=NULL ) /* debuging */
9762: { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
9763: filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
9764: return ( fp ); /* return fp or NULL to caller */
9765: } /* --- end-of-function rastopenfile() --- */
9766:
9767:
9768: /* ==========================================================================
1.2 ! albertel 9769: * Function: rasteditfilename ( filename )
! 9770: * Purpose: edits filename to remove security problems,
! 9771: * e.g., removes all ../'s and ..\'s.
! 9772: * --------------------------------------------------------------------------
! 9773: * Arguments: filename (I) char * to null-terminated string containing
! 9774: * name of file to be edited
! 9775: * --------------------------------------------------------------------------
! 9776: * Returns: ( char * ) pointer to edited filename,
! 9777: * or empty string "\000" if any problem
! 9778: * --------------------------------------------------------------------------
! 9779: * Notes: o
! 9780: * ======================================================================= */
! 9781: /* --- entry point --- */
! 9782: char *rasteditfilename ( char *filename )
! 9783: {
! 9784: /* -------------------------------------------------------------------------
! 9785: Allocations and Declarations
! 9786: -------------------------------------------------------------------------- */
! 9787: static char editname[2048]; /*edited filename returned to caller*/
! 9788: char *strchange(); /* prepend pathprefix if necessary */
! 9789: int strreplace(), /* remove ../'s and ..\'s */
! 9790: isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
! 9791: /* --------------------------------------------------------------------------
! 9792: edit filename
! 9793: -------------------------------------------------------------------------- */
! 9794: /* --- first check filename arg --- */
! 9795: *editname = '\000'; /* init edited name as empty string*/
! 9796: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
! 9797: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
! 9798: /* --- init edited filename --- */
! 9799: strcpy(editname,filename); /* init edited name as input name */
! 9800: compress(editname,' '); /* remove embedded blanks */
! 9801: /* --- remove leading or embedded ....'s --- */
! 9802: while ( strreplace(editname,"....",NULL,0) > 0 ) ; /* squeeze out ....'s */
! 9803: /* --- remove leading / and \ and dots (and blanks) --- */
! 9804: if ( *editname != '\000' ) /* still have chars in filename */
! 9805: while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
! 9806: strcpy(editname,editname+1); /* so flush leading / or \ (or ' ')*/
! 9807: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
! 9808: /* --- remove leading or embedded ../'s and ..\'s --- */
! 9809: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */
! 9810: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
! 9811: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* and ../'s again */
! 9812: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
! 9813: if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
! 9814: strchange(0,editname,pathprefix); /* so prepend prefix */
! 9815: end_of_job:
! 9816: return ( editname ); /* back with edited filename */
! 9817: } /* --- end-of-function rasteditfilename() --- */
! 9818:
! 9819:
! 9820: /* ==========================================================================
! 9821: * Function: rastreadfile ( filename, islock, tag, value )
1.1 albertel 9822: * Purpose: Read filename, returning value as string
9823: * between <tag>...</tag> or entire file if tag=NULL passed.
9824: * --------------------------------------------------------------------------
9825: * Arguments: filename (I) char * to null-terminated string containing
9826: * name of file to read (preceded by path
9827: * relative to mimetex executable)
1.2 ! albertel 9828: * islock (I) int containing 1 to lock file while reading
! 9829: * (hopefully done by opening in "r+" mode)
1.1 albertel 9830: * tag (I) char * to null-terminated string containing
9831: * html-like tagname. File contents between
9832: * <tag> and </tag> will be returned, or
9833: * entire file if tag=NULL passed.
9834: * value (O) char * returning value between <tag>...</tag>
9835: * or entire file if tag=NULL.
9836: * --------------------------------------------------------------------------
9837: * Returns: ( int ) 1=okay, 0=some error
9838: * --------------------------------------------------------------------------
9839: * Notes: o
9840: * ======================================================================= */
9841: /* --- entry point --- */
1.2 ! albertel 9842: int rastreadfile ( char *filename, int islock, char *tag, char *value )
1.1 albertel 9843: {
9844: /* -------------------------------------------------------------------------
9845: Allocations and Declarations
9846: -------------------------------------------------------------------------- */
9847: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
9848: char texfile[2048] = "\000", /* local copy of input filename */
9849: text[4096]; /* line from input file */
9850: char *tagp, tag1[512], tag2[512]; /* left <tag> and right <tag/> */
9851: int vallen=0, maxvallen=8000; /* #chars in value, max allowed */
1.2 ! albertel 9852: int status = (-1); /* status returned, 1=okay */
1.1 albertel 9853: int tagnum = 0; /* tag we're looking for */
1.2 ! albertel 9854: /*int islock = 1;*/ /* true to lock file */
1.1 albertel 9855: /* --------------------------------------------------------------------------
9856: Open file
9857: -------------------------------------------------------------------------- */
9858: /* --- first check output arg --- */
9859: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
9860: *value = '\000'; /* init buffer with empty string */
9861: /* --- open filename or filename.tex --- */
9862: if ( filename != (char *)NULL ) /* make sure we got filename arg */
9863: { strcpy(texfile,filename); /* local copy of filename */
1.2 ! albertel 9864: fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
1.1 albertel 9865: /* --- check that file opened --- */
9866: if ( fp == (FILE *)NULL ) /* failed to open file */
9867: { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
9868: goto end_of_job; } /* return error message to caller */
1.2 ! albertel 9869: status = 0; /* file opened successfully */
! 9870: if ( islock ) rewind(fp); /* start at beginning of file */
1.1 albertel 9871: /* --------------------------------------------------------------------------
9872: construct <tag>'s
9873: -------------------------------------------------------------------------- */
9874: if ( tag != (char *)NULL ) /* caller passed tag arg */
9875: if ( *tag != '\000' ) /* and it's not an empty string */
9876: { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
9877: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
9878: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
9879: compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
9880: tagnum = 1; } /* signal that we have tag */
9881: /* --------------------------------------------------------------------------
9882: Read file, concatnate lines
9883: -------------------------------------------------------------------------- */
9884: while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */
9885: switch ( tagnum ) { /* look for left- or right-tag */
1.2 ! albertel 9886: case 0: status = 1; break; /* no tag to look for */
1.1 albertel 9887: case 1: /* looking for opening left <tag> */
9888: if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
9889: strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */
9890: tagnum = 2; /*now looking for closing right tag*/
9891: case 2: /* looking for closing right </tag> */
9892: if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
9893: *tagp = '\000'; /* terminate line at tag */
9894: tagnum = 3; /* done after this line */
1.2 ! albertel 9895: status = 1; /* successfully read tag */
1.1 albertel 9896: break;
9897: } /* ---end-of-switch(tagnum) --- */
9898: if ( tagnum != 1 ) { /* no tag or left tag already found*/
9899: int textlen = strlen(text); /* #chars in current line */
9900: if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
9901: strcat(value,text); /* concat line to end of value */
9902: vallen += textlen; /* bump length */
9903: if ( tagnum > 2 ) break; } /* found right tag, so we're done */
9904: } /* --- end-of-while(fgets()!=NULL) --- */
9905: if ( tagnum<1 || tagnum>2 ) status=1; /* okay if no tag or we found tag */
9906: fclose ( fp ); /* close input file after reading */
9907: /* --- return value and status to caller --- */
9908: end_of_job:
9909: return ( status ); /* return status to caller */
9910: } /* --- end-of-function rastreadfile() --- */
9911:
9912:
9913: /* ==========================================================================
9914: * Function: rastwritefile ( filename, tag, value, isstrict )
9915: * Purpose: Re/writes filename, replacing string between <tag>...</tag>
9916: * with value, or writing entire file as value if tag=NULL.
9917: * --------------------------------------------------------------------------
9918: * Arguments: filename (I) char * to null-terminated string containing
9919: * name of file to write (preceded by path
9920: * relative to mimetex executable)
9921: * tag (I) char * to null-terminated string containing
9922: * html-like tagname. File contents between
9923: * <tag> and </tag> will be replaced, or
9924: * entire file written if tag=NULL passed.
9925: * value (I) char * containing string replacing value
9926: * between <tag>...</tag> or replacing entire
9927: * file if tag=NULL.
9928: * isstrict (I) int containing 1 to only rewrite existing
9929: * files, or 0 to create new file if necessary.
9930: * --------------------------------------------------------------------------
9931: * Returns: ( int ) 1=okay, 0=some error
9932: * --------------------------------------------------------------------------
9933: * Notes: o
9934: * ======================================================================= */
9935: /* --- entry point --- */
9936: int rastwritefile( char *filename, char *tag, char *value, int isstrict )
9937: {
9938: /* -------------------------------------------------------------------------
9939: Allocations and Declarations
9940: -------------------------------------------------------------------------- */
9941: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
9942: char texfile[2048] = "\000", /* local copy of input filename */
9943: filebuff[16384] = "\000", /* entire contents of file */
9944: tag1[512], tag2[512], /* left <tag> and right <tag/> */
9945: *strchange(), /* put value between <tag>...</tag>*/
9946: *timestamp(); /* log modification time */
9947: int istag=0, rastreadfile(), /* read file if tag!=NULL */
9948: /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
9949: isnewfile = 0, /* true if writing new file */
9950: status = 0; /* status returned, 1=okay */
1.2 ! albertel 9951: int istimestamp = 0; /* true to update <timestamp> tag */
1.1 albertel 9952: /* --------------------------------------------------------------------------
9953: check args
9954: -------------------------------------------------------------------------- */
9955: /* --- check filename and value --- */
9956: if ( filename == (char *)NULL /* quit if no filename arg supplied*/
9957: || value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
9958: if ( strlen(filename) < 2 /* quit if unreasonable filename */
9959: || *value == '\000' ) goto end_of_job; /* or empty value string supplied */
9960: /* --- establish filename[.tex] --- */
9961: strcpy(texfile,filename); /* local copy of input filename */
9962: if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */
9963: == (FILE *)NULL ) /* can't open, so write new file */
9964: { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */
9965: isnewfile = 1; } /* signal we're writing new file */
9966: /* --- check whether tag supplied by caller --- */
9967: if ( tag != (char *)NULL ) /* caller passed tag argument */
9968: if ( *tag != '\000' ) /* and it's not an empty string */
9969: { istag = 1; /* so flip tag flag true */
9970: strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin tags with < and </ */
9971: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
9972: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
9973: compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
9974: /* --------------------------------------------------------------------------
9975: read existing file if just rewriting a single tag
9976: -------------------------------------------------------------------------- */
9977: /* --- read original file if only replacing a tag within it --- */
9978: *filebuff = '\000'; /* init as empty file */
9979: if ( !isnewfile ) /* if file already exists */
9980: if ( istag ) /* and just rewriting one tag */
1.2 ! albertel 9981: if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
! 9982: <= 0 ) goto end_of_job; /* signal error if failed to read */
1.1 albertel 9983: /* --------------------------------------------------------------------------
9984: construct new file data if needed (entire file replaced by value if no tag)
9985: -------------------------------------------------------------------------- */
9986: if ( istag ) /* only replacing tag in file */
9987: {
9988: /* --- find <tag> and </tag> in file --- */
9989: int tlen1=strlen(tag1), tlen2=strlen(tag2), flen; /*tag,buff lengths*/
9990: char *tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
9991: *tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
9992: /* --- if adding new <tag> just concatanate at end of file --- */
9993: if ( tagp1 == (char *)NULL ) /* add new tag to file */
9994: {
9995: /* --- preprocess filebuff --- */
9996: if ( tagp2 != (char *)NULL ) /* apparently have ...</tag> */
9997: strcpy(filebuff,tagp2+tlen2); /* so get rid of leading ...</tag> */
9998: if ( (flen = strlen(filebuff)) /* #chars currently in buffer */
9999: > 0 ) /* we have non-empty buffer */
10000: if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
10001: if(0)strcat(filebuff,"\n"); /* so add one before new tag */
10002: /* --- add new tag --- */
10003: strcat(filebuff,tag1); /* add opening <tag> */
10004: strcat(filebuff,value); /* then value */
10005: strcat(filebuff,tag2); /* finally closing </tag> */
10006: strcat(filebuff,"\n"); /* newline at end of file */
10007: } /* --- end-of-if(tagp1==NULL) --- */
10008: else /* found existing opening <tag> */
10009: {
10010: if ( tagp2 == NULL ) /* apparently have <tag>... */
10011: { *(tagp1+tlen1) = '\000'; /* so get rid of trailing ... */
10012: strcat(filebuff,value); /* then concatanate value */
10013: strcat(filebuff,tag2); } /* and finally closing </tag> */
10014: else /* else have <tag>...<tag/> */
10015: if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
10016: >= 0 ) /* usually <tag> precedes </tag> */
10017: strchange(flen,tagp1+tlen1,value); /* change ...'s to value */
10018: else /* weirdly, </tag> precedes <tag> */
10019: { char fbuff[2048]; /* field buff for <tag>value</tag> */
10020: if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
10021: <= 0 ) goto end_of_job; /* must be internal error */
10022: strcpy(fbuff,tag1); /* set opening <tag> */
10023: strcat(fbuff,value); /* then value */
10024: strcat(fbuff,tag2); /* finally closing </tag> */
10025: strchange(flen,tagp2,fbuff); } /* replace original </tag>...<tag> */
10026: } /* --- end-of-if/else(tagp1==NULL) --- */
10027: } /* --- end-of-if(istag) --- */
10028: /* --------------------------------------------------------------------------
10029: rewrite file and return to caller
10030: -------------------------------------------------------------------------- */
10031: /* --- first open file for write --- */
10032: if ( (fp=rastopenfile(texfile,"w")) /* open for write */
10033: == (FILE *)NULL ) goto end_of_job; /* signal error if can't open */
10034: /* --- rewrite and close file --- */
10035: if ( fputs((istag?filebuff:value),fp) /* write filebuff or value */
10036: != EOF ) status = 1; /* signal success if succeeded */
10037: fclose ( fp ); /* close output file after writing */
10038: /* --- modify timestamp --- */
1.2 ! albertel 10039: if ( status > 0 ) /*forget timestamp if write failed*/
! 10040: if ( istimestamp ) /* if we're updating timestamp */
! 10041: if ( istag ) /* only log time in tagged file */
! 10042: if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
! 10043: { char fbuff[2048]; /* field buff <timestamp> value */
! 10044: strcpy(fbuff,tag); /* tag modified */
! 10045: strcat(fbuff," modified at "); /* spacer */
! 10046: strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
! 10047: status = rastwritefile(filename,"timestamp",fbuff,1); }
1.1 albertel 10048: /* --- return status to caller --- */
10049: end_of_job:
10050: return ( status ); /* return status to caller */
10051: } /* --- end-of-function rastwritefile() --- */
10052:
10053:
10054: /* ==========================================================================
1.2 ! albertel 10055: * Function: calendar ( year, month, day )
! 10056: * Purpose: returns null-terminated character string containing
! 10057: * \begin{array}...\end{array} for the one-month calendar
! 10058: * specified by year=1973...2099 and month=1...12.
! 10059: * If either arg out-of-range, today's value is used.
! 10060: * --------------------------------------------------------------------------
! 10061: * Arguments: year (I) int containing 1973...2099 or 0 for current
! 10062: * year
! 10063: * month (I) int containing 1...12 or 0 for current month
! 10064: * day (I) int containing day to emphasize or 0
! 10065: * --------------------------------------------------------------------------
! 10066: * Returns: ( char * ) char ptr to null-terminated buffer
! 10067: * containing \begin{array}...\end{array}
! 10068: * string that will render calendar for
! 10069: * requested month, or NULL for any error.
! 10070: * --------------------------------------------------------------------------
! 10071: * Notes: o
! 10072: * ======================================================================= */
! 10073: /* --- entry point --- */
! 10074: char *calendar( int year, int month, int day )
! 10075: {
! 10076: /* -------------------------------------------------------------------------
! 10077: Allocations and Declarations
! 10078: -------------------------------------------------------------------------- */
! 10079: static char calbuff[4096]; /* calendar returned to caller */
! 10080: time_t time_val = (time_t)(0); /* binary value returned by time() */
! 10081: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
! 10082: int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
! 10083: int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
! 10084: char aval[64]; /* ascii day or 4-digit year */
! 10085: /* --- calendar data --- */
! 10086: static char *monthnames[] = { "?", "January", "February", "March", "April",
! 10087: "May", "June", "July", "August", "September", "October",
! 10088: "November", "December", "?" } ;
! 10089: static int modays[] =
! 10090: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
! 10091: /* -------------------------------------------------------------------------
! 10092: initialization
! 10093: -------------------------------------------------------------------------- */
! 10094: /* --- get current date/time --- */
! 10095: time((time_t *)(&time_val)); /* get date and time */
! 10096: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
! 10097: yy = 1900 + (int)(tmstruct->tm_year); /* current four-digit year */
! 10098: mm = 1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
! 10099: dd = (int)(tmstruct->tm_mday); /* current day, 1-31 */
! 10100: /* --- check args --- */
! 10101: if ( year<1973 || year>2099 ) year = yy; /* current year if out-of-bounds */
! 10102: if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
! 10103: if ( month==mm && year==yy && day==0 ) /* current month and default day */
! 10104: day = dd; /* emphasize current day */
! 10105: modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
! 10106: /* --- initialize calendar string --- */
! 10107: strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
! 10108: strcat(calbuff,"\\small\\text{"); /* month set in roman */
! 10109: strcat(calbuff,monthnames[month]); /* insert month name */
! 10110: strcat(calbuff," }"); /* add a space */
! 10111: sprintf(aval,"%d",year); /* convert year to ascii */
! 10112: strcat(calbuff,aval); /* add year */
! 10113: strcat(calbuff,"\\\\"); /* end top row */
! 10114: strcat(calbuff, /* now begin calendar arrayr */
! 10115: "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
! 10116: "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
! 10117: "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
! 10118: "\\tiny\\text{Sat} \\\\ \\hline " );
! 10119: /* -------------------------------------------------------------------------
! 10120: generate calendar
! 10121: -------------------------------------------------------------------------- */
! 10122: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
! 10123: {
! 10124: /* --- get day-of-week for this day --- */
! 10125: iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
! 10126: if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
! 10127: /* --- may need empty cells at beginning of month --- */
! 10128: if ( idd == 1 ) /* first day of month */
! 10129: if ( iday > 0 ) /* need to skip cells */
! 10130: { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
! 10131: aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
! 10132: strcat(calbuff,aval); } /* add skip string to buffer */
! 10133: /* --- add idd to current cell --- */
! 10134: sprintf(aval,"%d",idd); /* convert idd to ascii */
! 10135: if ( idd == day /* emphasize today's date */
! 10136: /*&& month==mm && year==yy*/ ) /* only if this month's calendar */
! 10137: { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
! 10138: strcat(calbuff,aval); /* put in idd */
! 10139: strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
! 10140: else /* not today's date */
! 10141: strcat(calbuff,aval); /* so just put in idd */
! 10142: /* --- terminate cell --- */
! 10143: if ( idd < modays[month] ) /* not yet end-of-month */
! 10144: if ( iday < 6 ) /* still have days left in week */
! 10145: strcat(calbuff,"&"); /* new cell in same week */
! 10146: else /* reached end-of-week */
! 10147: strcat(calbuff,"\\\\ \\hline"); /* so start new week */
! 10148: } /* --- end-of-for(idd) --- */
! 10149: strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
! 10150: /* --- return calendar to caller --- */
! 10151: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
! 10152: return ( calbuff ); /* back to caller with calendar */
! 10153: } /* --- end-of-function calendar() --- */
! 10154:
! 10155:
! 10156: /* ==========================================================================
! 10157: * Function: timestamp ( tzdelta, ifmt )
1.1 albertel 10158: * Purpose: returns null-terminated character string containing
10159: * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
10160: * --------------------------------------------------------------------------
1.2 ! albertel 10161: * Arguments: tzdelta (I) integer, positive or negative, containing
! 10162: * containing number of hours to be added or
! 10163: * subtracted from system time (to accommodate
! 10164: * your desired time zone).
! 10165: * ifmt (I) integer containing 0 for default format
1.1 albertel 10166: * --------------------------------------------------------------------------
10167: * Returns: ( char * ) ptr to null-terminated buffer
10168: * containing current date:time stamp
10169: * --------------------------------------------------------------------------
10170: * Notes: o
10171: * ======================================================================= */
10172: /* --- entry point --- */
1.2 ! albertel 10173: char *timestamp( int tzdelta, int ifmt )
1.1 albertel 10174: {
10175: /* -------------------------------------------------------------------------
10176: Allocations and Declarations
10177: -------------------------------------------------------------------------- */
1.2 ! albertel 10178: static char timebuff[256]; /* date:time buffer back to caller */
1.1 albertel 10179: /*long time_val = 0L;*/ /* binary value returned by time() */
10180: time_t time_val = (time_t)(0); /* binary value returned by time() */
10181: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
1.2 ! albertel 10182: int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
! 10183: month=0, day=0; /* adjust day and month for delta */
! 10184: int tzadjust(); /* time zone adjustment function */
! 10185: int daynumber(); /* #days since Jan 1, 1973 */
! 10186: static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
! 10187: "Thursday", "Friday", "Saturday", "Sunday" } ;
! 10188: static char *monthnames[] = { "?", "January", "February", "March", "April",
! 10189: "May", "June", "July", "August", "September", "October",
! 10190: "November", "December", "?" } ;
1.1 albertel 10191: /* -------------------------------------------------------------------------
10192: get current date:time, adjust values, and and format stamp
10193: -------------------------------------------------------------------------- */
1.2 ! albertel 10194: /* --- first init returned timebuff in case of any error --- */
! 10195: *timebuff = '\000';
1.1 albertel 10196: /* --- get current date:time --- */
10197: time((time_t *)(&time_val)); /* get date and time */
10198: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
1.2 ! albertel 10199: /* --- extract fields --- */
! 10200: year = (int)(tmstruct->tm_year); /* local copy of year, 0=1900 */
! 10201: month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
! 10202: day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */
! 10203: hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */
! 10204: /* --- adjust year --- */
1.1 albertel 10205: year += 1900; /* set century in year */
1.2 ! albertel 10206: /* --- adjust for timezone --- */
! 10207: tzadjust(tzdelta,&year,&month,&day,&hour);
! 10208: /* --- check params --- */
! 10209: if ( hour<0 || hour>23
! 10210: || day<1 || day>31
! 10211: || month<1 || month>12
! 10212: || year<1973 ) goto end_of_job;
! 10213: /* --- adjust hour for am/pm --- */
! 10214: switch ( ifmt )
! 10215: {
! 10216: default:
! 10217: case 0:
! 10218: if ( hour < 12 ) /* am check */
! 10219: { ispm=0; /* reset pm flag */
! 10220: if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
! 10221: if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
! 10222: break;
! 10223: } /* --- end-of-switch(ifmt) --- */
1.1 albertel 10224: /* --- format date:time stamp --- */
1.2 ! albertel 10225: switch ( ifmt )
! 10226: {
! 10227: default:
! 10228: case 0: /* --- 2005-03-05:11:49:59am --- */
! 10229: sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
! 10230: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
! 10231: break;
! 10232: case 1: /* --- Saturday, March 5, 2005 --- */
! 10233: sprintf(timebuff,"%s, %s %d, %d",
! 10234: daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
! 10235: break;
! 10236: case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
! 10237: sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
! 10238: daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
! 10239: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
! 10240: break;
! 10241: case 3: /* --- 11:49:59am --- */
! 10242: sprintf(timebuff,"%d:%02d:%02d%s",
! 10243: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
! 10244: break;
! 10245: } /* --- end-of-switch(ifmt) --- */
! 10246: end_of_job:
! 10247: return ( timebuff ); /* return stamp to caller */
1.1 albertel 10248: } /* --- end-of-function timestamp() --- */
10249:
10250:
10251: /* ==========================================================================
1.2 ! albertel 10252: * Function: tzadjust ( tzdelta, year, month, day, hour )
! 10253: * Purpose: Adjusts hour, and day,month,year if necessary,
! 10254: * by delta increment to accommodate your time zone.
! 10255: * --------------------------------------------------------------------------
! 10256: * Arguments: tzdelta (I) integer, positive or negative, containing
! 10257: * containing number of hours to be added or
! 10258: * subtracted from given time (to accommodate
! 10259: * your desired time zone).
! 10260: * year (I) addr of int containing 4-digit year
! 10261: * month (I) addr of int containing month 1=Jan - 12=Dec.
! 10262: * day (I) addr of int containing day 1-31 for Jan.
! 10263: * hour (I) addr of int containing hour 0-23
! 10264: * Returns: ( int ) 1 for success, or 0 for error
! 10265: * --------------------------------------------------------------------------
! 10266: * Notes: o
! 10267: * ======================================================================= */
! 10268: /* --- entry point --- */
! 10269: int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
! 10270: {
! 10271: /* --------------------------------------------------------------------------
! 10272: Allocations and Declarations
! 10273: -------------------------------------------------------------------------- */
! 10274: int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
! 10275: /* --- calendar data --- */
! 10276: static int modays[] =
! 10277: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
! 10278: /* --------------------------------------------------------------------------
! 10279: check args
! 10280: -------------------------------------------------------------------------- */
! 10281: if ( mm<1 || mm>12 ) return(-1); /* bad month */
! 10282: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
! 10283: if ( hh<0 || hh>23 ) return(-1); /* bad hour */
! 10284: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
! 10285: /* --------------------------------------------------------------------------
! 10286: make adjustments
! 10287: -------------------------------------------------------------------------- */
! 10288: /* --- adjust hour --- */
! 10289: hh += tzdelta; /* apply caller's delta */
! 10290: /* --- adjust for feb 29 --- */
! 10291: modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
! 10292: /* --- adjust day --- */
! 10293: if ( hh < 0 ) /* went to preceding day */
! 10294: { dd--; hh += 24; }
! 10295: if ( hh > 23 ) /* went to next day */
! 10296: { dd++; hh -= 24; }
! 10297: /* --- adjust month --- */
! 10298: if ( dd < 1 ) /* went to preceding month */
! 10299: { mm--; dd = modays[mm]; }
! 10300: if ( dd > modays[mm] ) /* went to next month */
! 10301: { mm++; dd = 1; }
! 10302: /* --- adjust year --- */
! 10303: if ( mm < 1 ) /* went to preceding year */
! 10304: { yy--; mm = 12; dd = modays[mm]; }
! 10305: if ( mm > 12 ) /* went to next year */
! 10306: { yy++; mm = 1; dd = 1; }
! 10307: /* --- back to caller --- */
! 10308: *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
! 10309: return ( 1 );
! 10310: } /* --- end-of-function tzadjust() --- */
! 10311:
! 10312:
! 10313: /* ==========================================================================
! 10314: * Function: daynumber ( year, month, day )
! 10315: * Purpose: Returns number of actual calendar days from Jan 1, 1973
! 10316: * to the given date (e.g., bvdaynumber(1974,1,1)=365).
! 10317: * --------------------------------------------------------------------------
! 10318: * Arguments: year (I) int containing year -- may be either 1995 or
! 10319: * 95, or may be either 2010 or 110 for those
! 10320: * years.
! 10321: * month (I) int containing month, 1=Jan thru 12=Dec.
! 10322: * day (I) int containing day of month, 1-31 for Jan, etc.
! 10323: * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
! 10324: * or -1 for error (e.g., year<1973).
! 10325: * --------------------------------------------------------------------------
! 10326: * Notes: o
! 10327: * ======================================================================= */
! 10328: /* --- entry point --- */
! 10329: int daynumber ( int year, int month, int day )
! 10330: {
! 10331: /* --------------------------------------------------------------------------
! 10332: Allocations and Declarations
! 10333: -------------------------------------------------------------------------- */
! 10334: /* --- returned value (note: returned as a default "int") --- */
! 10335: int ndays; /* #days since jan 1, year0 */
! 10336: /* --- initial conditions --- */
! 10337: static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
! 10338: days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
! 10339: days1yr = 365;
! 10340: /* --- table of accumulated days per month (last index not used) --- */
! 10341: static int modays[] =
! 10342: { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
! 10343: /* --- variables for #days since day0 --- */
! 10344: int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
! 10345: /* --------------------------------------------------------------------------
! 10346: Check input
! 10347: -------------------------------------------------------------------------- */
! 10348: if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
! 10349: return ( -1 ); /* otherwise, forget it */
! 10350: if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
! 10351: /* --------------------------------------------------------------------------
! 10352: Find #days since jan 1, 1973
! 10353: -------------------------------------------------------------------------- */
! 10354: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
! 10355: nyears = year - year0; /* #years since year0 */
! 10356: if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
! 10357: nfouryrs = nyears/4; /* #complete four-year periods */
! 10358: nyears -= (4*nfouryrs); /* remainder excluding current year*/
! 10359: /* --- #days from jan 1, year0 till jan 1, this year --- */
! 10360: ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
! 10361: + (days1yr*nyears); /* +remaining days */
! 10362: /*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
! 10363: /* --- add #days within current year --- */
! 10364: ndays += (modays[month-1] + (day-1));
! 10365: /* --- may need an extra day if current year is a leap year --- */
! 10366: if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
! 10367: { if ( month > 2 ) /* past feb so need an extra day */
! 10368: /*if ( year != 100 )*/ /* unless it's 2000AD */
! 10369: ndays++; } /* so add it in */
! 10370: return ( (int)(ndays) ); /* #days back to caller */
! 10371: } /* --- end-of-function daynumber() --- */
! 10372:
! 10373:
! 10374: /* ==========================================================================
! 10375: * Function: dbltoa ( dblval, npts )
1.1 albertel 10376: * Purpose: Converts double to ascii, in financial format
10377: * (e.g., comma-separated and negatives enclosed in ()'s).
10378: * -------------------------------------------------------------------------
10379: * Arguments: dblval (I) double containing value to be converted.
10380: * npts (I) int containing #places after decimal point
10381: * to be displayed in returned string.
10382: * Returns: ( char * ) null-terminated string containing
10383: * double converted to financial format.
10384: * -------------------------------------------------------------------------
10385: * Notes: o
10386: * ======================================================================= */
10387: /* --- entry point --- */
1.2 ! albertel 10388: char *dbltoa ( double dblval, int npts )
! 10389: /* double dblval;
! 10390: int npts; */
1.1 albertel 10391: {
10392: /* -------------------------------------------------------------------------
10393: Allocations and Declarations
10394: ------------------------------------------------------------------------- */
10395: static char finval[128]; /* buffer returned to caller */
10396: static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
10397: char *finptr = finval; /* ptr to next char being converted*/
10398: double floor(); /* integer which is glb(double) */
10399: double dbldigit; /* to shift out digits from dblval */
10400: int digit; /* one digit from dblval */
10401: int isneg = 0; /* reset true if dblval negative */
10402: int ifrac = 0; /* npts fractional digits of dblval*/
10403: char digits[64]; int ndigits=0; /* all the digits [0]=least signif */
10404: /* -------------------------------------------------------------------------
10405: Check sign
10406: ------------------------------------------------------------------------- */
10407: if ( dblval < 0.0 ) /* got a negative value to convert */
10408: { isneg=1; dblval=(-dblval); } /* set flag and make it positive */
10409: /* -------------------------------------------------------------------------
10410: Get fractional part of dblval if required
10411: ------------------------------------------------------------------------- */
10412: if ( npts > 0 )
10413: { int ipts = npts; /* loop index */
10414: dbldigit = dblval-floor(dblval); /* fractional part as double */
10415: digit = 1; /* check if rounded frac > 1 */
10416: while ( --ipts >= 0 ) /* count down */
10417: { dbldigit *= 10.0; /* shift left one digit at a time */
10418: digit *= 10; } /* and keep max up-to-date */
10419: ifrac = (int)(dbldigit + 0.5); /* store fractional part as integer*/
10420: if ( ifrac >= digit ) /* round to next whole number */
10421: { dblval++; ifrac=0; } /* bump val, reset frac to zero */
10422: } /* --- end-of-if(npts>0) --- */
10423: else dblval += 0.5; /* no frac, round to nearest whole */
10424: /* -------------------------------------------------------------------------
10425: Get whole digits
10426: ------------------------------------------------------------------------- */
10427: dblval = floor(dblval); /* get rid of fractional part */
10428: while ( dblval > 0.0 ) /* still have data digits remaining*/
10429: { dbldigit = floor(dblval/10.0); /* shift out next digit */
10430: digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
10431: if ( digit<0 || digit>9 ) digit=10; /* index check */
10432: digits[ndigits++] = digittbl[digit]; /* store ascii digit */
10433: dblval = dbldigit; } /* ready for next digit */
10434: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
10435: /* -------------------------------------------------------------------------
10436: Format whole part from digits[] array
10437: ------------------------------------------------------------------------- */
10438: if ( isneg ) *finptr++ = '('; /* leading paren for negative value*/
10439: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
10440: { *finptr++ = digits[digit]; /* store digit */
10441: if ( digit>0 && digit%3==0 ) /* need a comma */
10442: *finptr++ = ','; } /* put in separating comma */
10443: /* -------------------------------------------------------------------------
10444: Format fractional part using ifrac
10445: ------------------------------------------------------------------------- */
10446: if ( npts > 0 )
10447: { *finptr++ = '.'; /* start with decimal point */
10448: sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
10449: finptr += npts; } /* bump ptr past fractional digits */
10450: /* -------------------------------------------------------------------------
10451: End-of-Job
10452: ------------------------------------------------------------------------- */
10453: if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
10454: *finptr = '\000'; /* null-terminate converted double */
10455: return ( finval ); /* converted double back to caller */
1.2 ! albertel 10456: } /* --- end-of-function dbltoa() --- */
1.1 albertel 10457:
10458:
10459: /* ==========================================================================
10460: * Function: aalowpass ( rp, bytemap, grayscale )
10461: * Purpose: calculates a lowpass anti-aliased bytemap
10462: * for rp->bitmap, with each byte 0...grayscale-1
10463: * --------------------------------------------------------------------------
10464: * Arguments: rp (I) raster * to raster whose bitmap
10465: * is to be anti-aliased
10466: * bytemap (O) intbyte * to bytemap, calculated
10467: * by applying lowpass filter to rp->bitmap,
10468: * and returned (as you'd expect) in 1-to-1
10469: * addressing correspondence with rp->bitmap
10470: * grayscale (I) int containing number of grayscales
10471: * to be calculated, 0...grayscale-1
10472: * (should typically be given as 256)
10473: * --------------------------------------------------------------------------
10474: * Returns: ( int ) 1=success, 0=any error
10475: * --------------------------------------------------------------------------
10476: * Notes: o If the center point of the box being averaged is black,
10477: * then the entire "average" is forced black (grayscale-1)
10478: * regardless of the surrounding points. This is my attempt
10479: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
10480: * lines, which would otherwise turn from black to a gray shade.
10481: * o Also, while the weights for neighbor points are fixed,
10482: * you may adjust the center point weight on the compile line.
10483: * A higher weight sharpens the resulting anti-aliased image;
10484: * lower weights blur it out more (but keep the "center" black
10485: * as per the preceding note).
10486: * ======================================================================= */
10487: /* --- entry point --- */
10488: int aalowpass (raster *rp, intbyte *bytemap, int grayscale)
10489: {
10490: /* -------------------------------------------------------------------------
10491: Allocations and Declarations
10492: -------------------------------------------------------------------------- */
10493: int status = 1; /* 1=success, 0=failure to caller */
10494: pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
10495: int irow=0, icol=0; /* rp->height, rp->width indexes */
10496: int weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
10497: int adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
10498: int totwts = 0; /* sum of all weights in matrix */
10499: int isforceavg = 1, /*force avg black if center black?*/
10500: isminmaxwts = 1, /*use wts or #pts for min/max test */
10501: blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
10502: /* -------------------------------------------------------------------------
10503: Initialization
10504: -------------------------------------------------------------------------- */
10505: /* --- calculate total weights --- */
10506: weights[4]= centerwt; /* weight for center point */
10507: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
10508: totwts = centerwt + 4*(1+adjacentwt); /* tot is center plus neighbors */
10509: /* -------------------------------------------------------------------------
10510: Calculate bytemap as 9-point weighted average over bitmap
10511: -------------------------------------------------------------------------- */
10512: for ( irow=0; irow<rp->height; irow++ )
10513: for ( icol=0; icol<rp->width; icol++ )
10514: {
10515: int ipixel = icol + irow*(rp->width); /* center pixel index */
10516: int jrow=0, jcol=0, /* box around ipixel */
10517: bitval = 0, /* value of bit/pixel at jrow,jcol */
10518: iscenter = 0, /* set true if center pixel black */
10519: nadjacent=0, wadjacent=0, /* #adjacent black pixels, their wts*/
10520: ngaps = 0, /* #gaps in 8 pixels around center */
10521: iwt=(-1), sumwts=0; /* weights index, sum in-bound wts */
10522: char adjmatrix[8]; /* adjacency "matrix" */
10523: memset(adjmatrix,0,8); /* zero out adjacency matrix */
10524: bytemap[ipixel] = 0; /* init pixel white */
10525: /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
10526: for ( jrow=irow-1; jrow<=irow+1; jrow++ ) /* jrow = irow-1...irow+1 */
10527: for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
10528: {
10529: int jpixel = jcol + jrow*(rp->width); /* averaging index */
10530: iwt++; /*always bump weight index*/
10531: if ( jrow<0 || jrow>=rp->height /* if row out pf bounds */
10532: || jcol<0 || jcol>=rp->width ) /* or col out of bounds */
10533: continue; /* ignore this point */
10534: bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
10535: if ( bitval ) /* this is a black pixel */
10536: { if ( jrow==irow && jcol==icol ) /* and this is center point */
10537: iscenter = 1; /* set flag for center point black */
10538: else /* adjacent point black */
10539: { nadjacent++; /* bump adjacent black count */
10540: adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
10541: wadjacent += weights[iwt]; } /* sum weights for black pixels */
10542: sumwts += weights[iwt]; /* and sum weights for all pixels */
10543: } /* --- end-of-for(jrow,jcol) --- */
10544: /* --- count gaps --- */
10545: ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
10546: for ( iwt=0; iwt<7; iwt++ ) /* clockwise around adjacency */
10547: if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
10548: ngaps /= 2; /*each gap has 2 black/white flips*/
10549: /* --- anti-alias pixel, but leave it black if it was already black --- */
10550: if ( isforceavg && iscenter ) /* force avg if center point black */
10551: bytemap[ipixel] = grayscale-1; /* so force grayscale-1=black */
10552: else /* center point not black */
10553: if ( ngaps <= 2 ) /*don't darken checkerboarded pixel*/
10554: { bytemap[ipixel] = /* 0=white ... grayscale-1=black */
10555: ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
10556: if ( blackscale > 0 /* blackscale kludge turned on */
10557: && bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
10558: bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
10559: /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
10560: if ( !iscenter ) /* apply min/maxadjacent test */
10561: if ( isminmaxwts ) /* min/max refer to adjacent weights*/
10562: { if ( wadjacent < minadjacent /* wts of adjacent points too low */
10563: || wadjacent > maxadjacent ) /* or too high */
10564: bytemap[ipixel] = 0; } /* so leave point white */
10565: else /* min/max refer to #adjacent points*/
10566: { if ( nadjacent < minadjacent /* too few adjacent points black */
10567: || nadjacent > maxadjacent ) /* or too many */
10568: bytemap[ipixel] = 0; } /* so leave point white */
10569: } /* --- end-of-for(irow,icol) --- */
10570: /* -------------------------------------------------------------------------
10571: Back to caller with gray-scale anti-aliased bytemap
10572: -------------------------------------------------------------------------- */
10573: /*end_of_job:*/
10574: return ( status );
10575: } /* --- end-of-function aalowpass() --- */
10576:
10577:
10578: /* ==========================================================================
10579: * Function: aapnm ( rp, bytemap, grayscale )
10580: * Purpose: calculates a lowpass anti-aliased bytemap
10581: * for rp->bitmap, with each byte 0...grayscale-1,
10582: * based on the pnmalias.c algorithm
10583: * --------------------------------------------------------------------------
10584: * Arguments: rp (I) raster * to raster whose bitmap
10585: * is to be anti-aliased
10586: * bytemap (O) intbyte * to bytemap, calculated
10587: * by applying pnm-based filter to rp->bitmap,
10588: * and returned (as you'd expect) in 1-to-1
10589: * addressing correspondence with rp->bitmap
10590: * grayscale (I) int containing number of grayscales
10591: * to be calculated, 0...grayscale-1
10592: * (should typically be given as 256)
10593: * --------------------------------------------------------------------------
10594: * Returns: ( int ) 1=success, 0=any error
10595: * --------------------------------------------------------------------------
10596: * Notes: o Based on the pnmalias.c algorithm in the netpbm package
10597: * on sourceforge.
10598: * ======================================================================= */
10599: /* --- entry point --- */
10600: int aapnm (raster *rp, intbyte *bytemap, int grayscale)
10601: {
10602: /* -------------------------------------------------------------------------
10603: Allocations and Declarations
10604: -------------------------------------------------------------------------- */
10605: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
10606: int width=rp->width, height=rp->height, /* width, height of raster */
10607: icol = 0, irow = 0, /* width, height indexes */
10608: imap = (-1); /* pixel index = icol + irow*width */
10609: int bgbitval=0, fgbitval=1; /* background, foreground bitval */
10610: #if 0
10611: int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
10612: int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
10613: #endif
10614: int totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
10615: int isresetparams = 1, /* true to set antialiasing params */
10616: isfgalias = 1, /* true to antialias fg bits */
10617: isfgonly = 0, /* true to only antialias fg bits */
10618: isbgalias = 0, /* true to antialias bg bits */
10619: isbgonly = 0; /* true to only antialias bg bits */
10620: /* -------------------------------------------------------------------------
10621: Initialization
10622: -------------------------------------------------------------------------- */
10623: /* --- check for bold light --- */
10624: if ( 0 )
10625: { if ( weightnum > 2 ) { isbgalias=1; } /* simulate bold */
10626: if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
10627: /* --- reset wts[], etc, and calculate total weights --- */
10628: if ( isresetparams ) /* wts[], etc taken from params */
10629: { int iwt=0; /* wts[iwt] index */
10630: wts[4]= centerwt; /* weight for center point */
10631: wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
10632: wts[0]=wts[2]=wts[6]=wts[8] = cornerwt; /* and corner points */
10633: for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
10634: isfgalias = fgalias; /* set isfgalias */
10635: isfgonly = fgonly; /* set isfgonly */
10636: isbgalias = bgalias; /* set isbgalias */
10637: isbgonly = bgonly; } /* set isbgonly */
10638: /* -------------------------------------------------------------------------
10639: Calculate bytemap as 9-point weighted average over bitmap
10640: -------------------------------------------------------------------------- */
10641: for ( irow=0; irow<height; irow++ )
10642: for ( icol=0; icol<width; icol++ )
10643: {
10644: /* --- local allocations and declarations --- */
10645: int bitval=0, /* value of rp bit at irow,icol */
10646: nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
10647: ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0; /*compass pt names*/
10648: int isbgedge=0, isfgedge=0; /*does pixel border a bg or fg edge*/
10649: int aabyteval=0; /* antialiased (or unchanged) value*/
10650: /* --- bump imap index and get center bit value --- */
10651: imap++; /* imap = icol + irow*width */
10652: bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
10653: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
10654: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
10655: /* --- check if we're antialiasing this pixel --- */
10656: if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
10657: || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
10658: continue; /* leave default and do next bit */
10659: /* --- get surrounding bits --- */
10660: if ( irow > 0 ) /* nn (north) bit available */
10661: nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
10662: if ( irow < height-1 ) /* ss (south) bit available */
10663: ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
10664: if ( icol > 0 ) /* ww (west) bit available */
10665: { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
10666: if ( irow > 0 ) /* nw bit available */
10667: nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
10668: if ( irow < height-1 ) /* sw bit available */
10669: swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
10670: if ( icol < width-1 ) /* ee (east) bit available */
10671: { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
10672: if ( irow > 0 ) /* ne bit available */
10673: nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
10674: if ( irow < height-1 ) /* se bit available */
10675: sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
10676: /* --- check for edges --- */
10677: isbgedge = /* current pixel borders a bg edge */
10678: (nnbitval==bgbitval && eebitval==bgbitval) || /*upper-right edge*/
10679: (eebitval==bgbitval && ssbitval==bgbitval) || /*lower-right edge*/
10680: (ssbitval==bgbitval && wwbitval==bgbitval) || /*lower-left edge*/
10681: (wwbitval==bgbitval && nnbitval==bgbitval) ; /*upper-left edge*/
10682: isfgedge = /* current pixel borders an fg edge*/
10683: (nnbitval==fgbitval && eebitval==fgbitval) || /*upper-right edge*/
10684: (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/
10685: (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left edge*/
10686: (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left edge*/
1.2 ! albertel 10687: /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
! 10688: if ( 1 ) { /* true to perform test */
! 10689: int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
! 10690: isbghorz = /* top or bottom edge is all bg */
! 10691: (nwbitval+nnbitval+nebitval == 3*bgbitval) || /* top edge bg */
! 10692: (swbitval+ssbitval+sebitval == 3*bgbitval) ; /* bottom edge bg */
! 10693: isfghorz = /* top or bottom edge is all fg */
! 10694: (nwbitval+nnbitval+nebitval == 3*fgbitval) || /* top edge fg */
! 10695: (swbitval+ssbitval+sebitval == 3*fgbitval) ; /* bottom edge fg */
! 10696: isbgvert = /* left or right edge is all bg */
! 10697: (nwbitval+wwbitval+swbitval == 3*bgbitval) || /* left edge bg */
! 10698: (nebitval+eebitval+sebitval == 3*bgbitval) ; /* right edge bg */
! 10699: isfgvert = /* left or right edge is all bg */
! 10700: (nwbitval+wwbitval+swbitval == 3*fgbitval) || /* left edge fg */
! 10701: (nebitval+eebitval+sebitval == 3*fgbitval) ; /* right edge fg */
! 10702: if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
! 10703: || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
! 10704: continue; /* don't antialias */
! 10705: } /* --- end-of-if(1) --- */
! 10706: /* --- check #gaps for checkerboard (added by j.forkosh) --- */
! 10707: if ( 0 ) { /* true to perform test */
! 10708: int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
! 10709: if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
! 10710: if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
! 10711: if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
! 10712: if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
! 10713: if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
! 10714: if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
! 10715: if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
! 10716: if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
! 10717: if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
! 10718: if ( ngaps<mingaps || ngaps>maxgaps ) continue;
! 10719: } /* --- end-of-if(1) --- */
1.1 albertel 10720: /* --- antialias if necessary --- */
10721: if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
10722: || (isfgalias && isfgedge) /* alias pixel surrounding fg */
10723: || (isbgedge && isfgedge) ) /* neighboring fg and bg pixel */
10724: {
10725: int aasumval = /* sum wts[]*bitmap[] */
10726: wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
10727: wts[3]*wwbitval + wts[4]*bitval + wts[5]*eebitval +
10728: wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
10729: double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
10730: aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
10731: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
10732: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* debugging */
10733: "aapnm> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
10734: irow,icol,imap, aawtval,aabyteval);
10735: } /* --- end-of-if(isedge) --- */
10736: } /* --- end-of-for(irow,icol) --- */
10737: /* -------------------------------------------------------------------------
10738: Back to caller with gray-scale anti-aliased bytemap
10739: -------------------------------------------------------------------------- */
10740: /*end_of_job:*/
10741: return ( 1 );
10742: } /* --- end-of-function aapnm() --- */
10743:
10744:
10745: /* ==========================================================================
10746: * Function: aasupsamp ( rp, aa, sf, grayscale )
10747: * Purpose: calculates a supersampled anti-aliased bytemap
10748: * for rp->bitmap, with each byte 0...grayscale-1
10749: * --------------------------------------------------------------------------
10750: * Arguments: rp (I) raster * to raster whose bitmap
10751: * is to be anti-aliased
10752: * aa (O) address of raster * to supersampled bytemap,
10753: * calculated by supersampling rp->bitmap
10754: * sf (I) int containing supersampling shrinkfactor
10755: * grayscale (I) int containing number of grayscales
10756: * to be calculated, 0...grayscale-1
10757: * (should typically be given as 256)
10758: * --------------------------------------------------------------------------
10759: * Returns: ( int ) 1=success, 0=any error
10760: * --------------------------------------------------------------------------
10761: * Notes: o If the center point of the box being averaged is black,
10762: * then the entire "average" is forced black (grayscale-1)
10763: * regardless of the surrounding points. This is my attempt
10764: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
10765: * lines, which would otherwise turn from black to a gray shade.
10766: * ======================================================================= */
10767: /* --- entry point --- */
10768: int aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
10769: {
10770: /* -------------------------------------------------------------------------
10771: Allocations and Declarations
10772: -------------------------------------------------------------------------- */
10773: int status = 0; /* 1=success, 0=failure to caller */
10774: int rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
10775: heightrem=0, widthrem=0, /* rp+rem is a multiple of shrinkf */
10776: aaheight=0, aawidth=0, /* supersampled dimensions */
10777: aapixsz=8; /* output pixels are 8-bit bytes */
10778: int maxaaval=(-9999), /* max grayscale val set in matrix */
10779: isrescalemax=1; /* 1=rescale maxaaval to grayscale */
10780: int irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
10781: raster *aap=NULL, *new_raster(); /* raster for supersampled image */
10782: raster *aaweights(); /* get weight matrix applied to rp */
10783: static raster *aawts = NULL; /* aaweights() resultant matrix */
10784: static int prevshrink = NOVALUE, /* shrinkfactor from previous call */
10785: sumwts = 0; /* sum of weights */
10786: static int blackfrac = 40, /* force black if this many pts are */
10787: /*grayfrac = 20,*/
10788: maxwt = 10, /* max weight in weight matrix */
10789: minwtfrac=10, maxwtfrac=70; /* force light pts white, dark black*/
10790: int type_raster(), type_bytemap(); /* debugging display routines */
10791: int delete_raster(); /* delete old rasters */
10792: /* -------------------------------------------------------------------------
10793: Initialization
10794: -------------------------------------------------------------------------- */
10795: /* --- check args --- */
10796: if ( aa == NULL ) goto end_of_job; /* no ptr for return output arg */
10797: *aa = NULL; /* init null ptr for error return */
10798: if ( rp == NULL /* no ptr to input arg */
10799: || sf < 1 /* invalid shrink factor */
10800: || grayscale < 2 ) goto end_of_job; /* invalid grayscale */
10801: /* --- get weight matrix (or use current one) --- */
10802: if ( sf != prevshrink ) /* have new shrink factor */
10803: { if ( aawts != NULL ) /* have unneeded weight matrix */
10804: delete_raster(aawts); /* so free it */
10805: sumwts = 0; /* reinitialize sum of weights */
10806: aawts = aaweights(sf,sf); /* get new weight matrix */
10807: if ( aawts != NULL ) /* got weight matrix okay*/
10808: for ( jwt=0; jwt<sf; jwt++ ) /* for each row */
10809: for ( iwt=0; iwt<sf; iwt++ ) /* and each column */
10810: { int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
10811: if ( wt > maxwt ) /* don't overweight center pts */
10812: { wt = maxwt; /* scale it back */
10813: setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
10814: sumwts += wt; } /* add weight to sum */
10815: prevshrink = sf; } /* save new shrink factor */
10816: if ( msgfp!=NULL && msglevel>=999 )
10817: { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
10818: type_bytemap((intbyte *)aawts->pixmap,grayscale,
10819: aawts->width,aawts->height,msgfp); }
10820: /* --- calculate supersampled height,width and allocate output raster */
10821: heightrem = rpheight%sf; /* remainder after division... */
10822: widthrem = rpwidth%sf; /* ...by shrinkfactor */
10823: aaheight = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
10824: aawidth = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
10825: if ( msgfp!=NULL && msglevel>=999 )
10826: { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
10827: rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
10828: fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
10829: type_raster(rp,msgfp); } /* ascii image of rp raster */
10830: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
10831: == NULL ) goto end_of_job; /* quit if alloc fails */
10832: /* -------------------------------------------------------------------------
10833: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
10834: -------------------------------------------------------------------------- */
10835: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
10836: {
10837: for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
10838: {
10839: int aaval=0; /* weighted rpvals */
10840: int nrp=0, mrp=0; /* #rp bits set, #within matrix */
10841: for ( jwt=0; jwt<sf; jwt++ )
10842: for ( iwt=0; iwt<sf; iwt++ )
10843: {
10844: int i=irp+iwt, j=jrp+jwt; /* rp->pixmap point */
10845: int rpval = 0; /* rp->pixmap value at i,j */
10846: if ( i>=0 && i<rpwidth /* i within actual pixmap */
10847: && j>=0 && j<rpheight ) /* ditto j */
10848: { mrp++; /* count another bit within matrix */
10849: rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
10850: if ( rpval != 0 )
10851: { nrp++; /* count another bit set */
10852: aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
10853: } /* --- end-of-for(iwt,jwt) --- */
10854: if ( aaval > 0 ) /*normalize and rescale non-zero val*/
10855: { int aafrac = (100*aaval)/sumwts; /* weighted percent of black points */
10856: /*if((100*nrp)/mrp >=blackfrac)*/ /* many black interior pts */
10857: if( aafrac >= maxwtfrac ) /* high weight of sampledblack pts */
10858: aaval = grayscale-1; /* so set supersampled pt black */
10859: else if( aafrac <= minwtfrac ) /* low weight of sampledblack pts */
10860: aaval = 0; /* so set supersampled pt white */
10861: else /* rescale calculated weight */
10862: aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
10863: maxaaval = max2(maxaaval,aaval); /* largest aaval so far */
10864: if ( msgfp!=NULL && msglevel>=999 )
10865: fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
10866: " mrp,nrp=%d,%d aaval=%d\n",
10867: jrp,irp, jaa,iaa, mrp,nrp, aaval);
10868: if ( jaa<aaheight && iaa<aawidth ) /* bounds check */
10869: setpixel(aap,jaa,iaa,aaval); /*weighted val in supersamp raster*/
10870: else if( msgfp!=NULL && msglevel>=9 ) /* emit error if out-of-bounds */
10871: fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
10872: aaheight,aawidth, jaa,iaa);
10873: iaa++; /* bump aa col index */
10874: } /* --- end-of-for(irp) --- */
10875: jaa++; /* bump aa row index */
10876: } /* --- end-of-for(jrp) --- */
10877: /* --- rescale supersampled image so darkest points become black --- */
10878: if ( isrescalemax ) /* flag set to rescale maxaaval */
10879: {
10880: double scalef = ((double)(grayscale-1))/((double)maxaaval);
10881: for ( jaa=0; jaa<aaheight; jaa++ ) /* height */
10882: for ( iaa=0; iaa<aawidth; iaa++ ) /* width */
10883: { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
10884: aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
10885: aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
10886: if( aafrac >= blackfrac ) /* high weight of sampledblack pts */
10887: aaval = grayscale-1; /* so set supersampled pt black */
10888: else if( 0&&aafrac <= minwtfrac ) /* low weight of sampledblack pts */
10889: aaval = 0; /* so set supersampled pt white */
10890: setpixel(aap,jaa,iaa,aaval); } /* replace rescaled val in raster */
10891: } /* --- end-of-if(isrescalemax) --- */
10892: *aa = aap; /* return supersampled image*/
10893: status = 1; /* set successful status */
10894: if ( msgfp!=NULL && msglevel>=999 )
10895: { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
10896: type_bytemap((intbyte *)aap->pixmap,grayscale,
10897: aap->width,aap->height,msgfp); fflush(msgfp); }
10898: /* -------------------------------------------------------------------------
10899: Back to caller with gray-scale anti-aliased bytemap
10900: -------------------------------------------------------------------------- */
10901: end_of_job:
10902: return ( status );
10903: } /* --- end-of-function aasupsamp() --- */
10904:
10905:
10906: /* ==========================================================================
10907: * Function: aacolormap ( bytemap, nbytes, colors, colormap )
10908: * Purpose: searches bytemap, returning a list of its discrete values
10909: * in ascending order in colors[], and returning an "image"
10910: * of bytemap (where vales are replaced by colors[]
10911: * indexes) in colormap[].
10912: * --------------------------------------------------------------------------
10913: * Arguments: bytemap (I) intbyte * to bytemap containing
10914: * grayscale values (usually 0=white
10915: * through 255=black) for which colors[]
10916: * and colormap[] will be constructed.
10917: * nbytes (I) int containing #bytes in bytemap
10918: * (usually just #rows * #cols)
10919: * colors (O) intbyte * (to be interpreted as ints)
10920: * returning a list of the discrete/different
10921: * values in bytemap, in ascending value order
10922: * colormap (O) intbyte * returning a bytemap "image",
10923: * i.e., in one-to-one pixel correspondence
10924: * with bytemap, but where the values have been
10925: * replaced with corresponding colors[] indexes.
10926: * --------------------------------------------------------------------------
10927: * Returns: ( int ) #colors in colors[], or 0 for any error
10928: * --------------------------------------------------------------------------
10929: * Notes: o
10930: * ======================================================================= */
10931: /* --- entry point --- */
10932: int aacolormap ( intbyte *bytemap, int nbytes,
10933: intbyte *colors, intbyte *colormap )
10934: {
10935: /* -------------------------------------------------------------------------
10936: Allocations and Declarations
10937: -------------------------------------------------------------------------- */
10938: int ncolors = 0, /* #different values in bytemap */
10939: igray, grayscale = 256; /* bytemap contains intbyte's */
10940: intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/
10941: int ibyte; /* bytemap/colormap index */
10942: int isscale = 0; /* true to scale largest val to 255*/
10943: int maxcolors = 0; /* maximum ncolors */
10944: /* -------------------------------------------------------------------------
10945: Accumulate colors[] from values occurring in bytemap
10946: -------------------------------------------------------------------------- */
10947: /* --- initialization --- */
10948: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
10949: == NULL ) goto end_of_job; /* signal error if malloc() failed */
10950: memset(bytevalues,0,grayscale); /* zero out bytevalues */
10951: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
10952: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
10953: bytevalues[(int)bytemap[ibyte]] = 1; /*use its value to index bytevalues*/
10954: /* --- collect the 1's indexes in colors[] --- */
10955: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
10956: if ( (int)bytevalues[igray] ) /*bytemap contains igray somewheres*/
10957: { colors[ncolors] = (intbyte)igray; /* so store igray in colors */
10958: bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
10959: if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
10960: bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
10961: ncolors++; } /* and bump #colors */
10962: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
10963: if ( isscale ) /* only rescale if requested */
10964: if ( ncolors > 1 ) /* and if not a "blank" raster */
10965: if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
10966: {
10967: /* --- multiply each colors[] by factor that scales largest to 255 --- */
10968: double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
10969: for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
10970: { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
10971: if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
10972: } /* --- end-of-if(isscale) --- */
10973: /* -------------------------------------------------------------------------
10974: Construct colormap
10975: -------------------------------------------------------------------------- */
10976: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
10977: colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
10978: /* -------------------------------------------------------------------------
10979: back to caller with #colors, or 0 for any error
10980: -------------------------------------------------------------------------- */
10981: end_of_job:
10982: if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
10983: if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
10984: ncolors = maxcolors; /* return maximum to caller */
10985: return ( ncolors ); /* back with #colors, or 0=error */
10986: } /* --- end-of-function aacolormap() --- */
10987:
10988:
10989: /* ==========================================================================
10990: * Function: aaweights ( width, height )
10991: * Builds "canonical" weight matrix, width x height, in a raster
10992: * (see Notes below for discussion).
10993: * --------------------------------------------------------------------------
10994: * Arguments: width (I) int containing width (#cols) of returned
10995: * raster/matrix of weights
10996: * height (I) int containing height (#rows) of returned
10997: * raster/matrix of weights
10998: * --------------------------------------------------------------------------
10999: * Returns: ( raster * ) ptr to raster containing width x height
11000: * weight matrix, or NULL for any error
11001: * --------------------------------------------------------------------------
11002: * Notes: o For example, given width=7, height=5, builds the matrix
11003: * 1 2 3 4 3 2 1
11004: * 2 4 6 8 6 4 2
11005: * 3 6 9 12 9 6 3
11006: * 2 4 6 8 6 4 2
11007: * 1 2 3 4 3 2 1
11008: * If an even dimension given, the two center numbers stay
11009: * the same, e.g., 123321 for the top row if width=6.
11010: * o For an odd square n x n matrix, the sum of all n^2
11011: * weights will be ((n+1)/2)^4.
11012: * o The largest weight (in the allocated pixsz=8 raster) is 255,
11013: * so the largest square matrix is 31 x 31. Any weight that
11014: * tries to grow beyond 255 is held constant at 255.
11015: * o A new_raster(), pixsz=8, is allocated for the caller.
11016: * To avoid memory leaks, be sure to delete_raster() when done.
11017: * ======================================================================= */
11018: /* --- entry point --- */
11019: raster *aaweights ( int width, int height )
11020: {
11021: /* -------------------------------------------------------------------------
11022: Allocations and Declarations
11023: -------------------------------------------------------------------------- */
11024: raster *new_raster(), *weights=NULL; /* raster of weights returned */
11025: int irow=0, icol=0, /* height, width indexes */
11026: weight = 0; /*running weight, as per Notes above*/
11027: /* -------------------------------------------------------------------------
11028: Initialization
11029: -------------------------------------------------------------------------- */
11030: /* --- allocate raster for weights --- */
11031: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
11032: == NULL ) goto end_of_job; /* return NULL error if failed */
11033: /* -------------------------------------------------------------------------
11034: Fill weight matrix, as per Notes above
11035: -------------------------------------------------------------------------- */
11036: for ( irow=0; irow<height; irow++ ) /* outer loop over rows */
11037: for ( icol=0; icol<width; icol++ ) /* inner loop over cols */
11038: {
11039: int jrow = height-irow-1, /* backwards irow, height-1,...,0 */
11040: jcol = width-icol-1; /* backwards icol, width-1,...,0 */
11041: weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
11042: if ( aaalgorithm == 1 ) weight=1; /* force equal weights */
11043: setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
11044: } /* --- end-of-for(irow,icol) --- */
11045: end_of_job:
11046: return ( weights ); /* back with weights or NULL=error */
11047: } /* --- end-of-function aaweights() --- */
11048:
11049:
11050: /* ==========================================================================
11051: * Function: aawtpixel ( image, ipixel, weights, rotate )
11052: * Purpose: Applies matrix of weights to the pixels
11053: * surrounding ipixel in image, rotated clockwise
11054: * by rotate degrees (typically 0 or 30).
11055: * --------------------------------------------------------------------------
11056: * Arguments: image (I) raster * to bitmap (though it can be bytemap)
11057: * containing image with pixels to be averaged.
11058: * ipixel (I) int containing index (irow*width+icol) of
11059: * center pixel of image for weighted average.
11060: * weights (I) raster * to bytemap of relative weights
11061: * (0-255), whose dimensions (usually odd width
11062: * and odd height) determine the "subgrid" of
11063: * image surrounding ipixel to be averaged.
11064: * rotate (I) int containing degrees clockwise rotation
11065: * (typically 0 or 30), i.e., imagine weights
11066: * rotated clockwise and then averaging the
11067: * image pixels "underneath" it now.
11068: * --------------------------------------------------------------------------
11069: * Returns: ( int ) 0-255 weighted average, or -1 for any error
11070: * --------------------------------------------------------------------------
11071: * Notes: o The rotation matrix used (when requested) is
11072: * / x' \ / cos(theta) sin(theta)/a \ / x \
11073: * | | = | | | |
11074: * \ y' / \ -a*sin(theta) cos(theta) / \ y /
11075: * where a=1 (current default) is the pixel (not screen)
11076: * aspect ratio width:height, and theta is rotate (converted
11077: * from degrees to radians). Then x=col,y=row are the integer
11078: * pixel coords relative to the input center ipixel, and
11079: * x',y' are rotated coords which aren't necessarily integer.
11080: * The actual pixel used is the one nearest x',y'.
11081: * ======================================================================= */
11082: /* --- entry point --- */
11083: int aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
11084: {
11085: /* -------------------------------------------------------------------------
11086: Allocations and Declarations
11087: -------------------------------------------------------------------------- */
11088: int aaimgval = 0, /* weighted avg returned to caller */
11089: totwts=0, sumwts=0; /* total of all wts, sum wts used */
11090: int pixsz = image->pixsz, /* #bits per image pixel */
11091: black1=1, black8=255, /* black for 1-bit, 8-bit pixels */
11092: black = (pixsz==1? black1:black8), /* black value for our image */
11093: scalefactor = (black1+black8-black), /* only scale 1-bit images */
11094: iscenter = 0; /* set true if center pixel black */
11095: /* --- grid dimensions and indexes --- */
11096: int wtheight = weights->height, /* #rows in weight matrix */
11097: wtwidth = weights->width, /* #cols in weight matrix */
11098: imgheight = image->height, /* #rows in image */
11099: imgwidth = image->width; /* #cols in image */
11100: int wtrow, wtrow0 = wtheight/2, /* center row index for weights */
11101: wtcol, wtcol0 = wtwidth/2, /* center col index for weights */
11102: imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
11103: imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
11104: /* --- rotated grid variables --- */
11105: static int prevrotate = 0; /* rotate from previous call */
11106: static double costheta = 1.0, /* cosine for previous rotate */
11107: sintheta = 0.0; /* and sine for previous rotate */
11108: double a = 1.0; /* default aspect ratio */
11109: /* -------------------------------------------------------------------------
11110: Initialization
11111: -------------------------------------------------------------------------- */
11112: /* --- refresh trig functions for rotate when it changes --- */
11113: if ( rotate != prevrotate ) /* need new sine/cosine */
11114: { costheta = cos(((double)rotate)/57.29578); /*cos of rotate in radians*/
11115: sintheta = sin(((double)rotate)/57.29578); /*sin of rotate in radians*/
11116: prevrotate = rotate; } /* save current rotate as prev */
11117: /* -------------------------------------------------------------------------
11118: Calculate aapixel as weighted average over image points around ipixel
11119: -------------------------------------------------------------------------- */
11120: for ( wtrow=0; wtrow<wtheight; wtrow++ )
11121: for ( wtcol=0; wtcol<wtwidth; wtcol++ )
11122: {
11123: /* --- allocations and declarations --- */
11124: int wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
11125: int drow = wtrow - wtrow0, /* delta row offset from center */
11126: dcol = wtcol - wtcol0; /* delta col offset from center */
11127: int iscenter = 0; /* set true if center point black */
11128: /* --- initialization --- */
11129: totwts += wt; /* sum all weights */
11130: /* --- rotate (if requested) --- */
11131: if ( rotate != 0 ) /* non-zero rotation */
11132: {
11133: /* --- apply rotation matrix to (x=dcol,y=drow) --- */
11134: double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
11135: dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
11136: dy = -a*dx*sintheta + dy*costheta; /* dy becomes new dy' */
11137: dx = dtemp; /* just for notational convenience */
11138: /* --- replace original (drow,dcol) with nearest rotated point --- */
11139: drow = (int)(dy+0.5); /* round dy for nearest row */
11140: dcol = (int)(dx+0.5); /* round dx for nearest col */
11141: } /* --- end-of-if(rotate!=0) --- */
11142: /* --- select image pixel to be weighted --- */
11143: imgrow = imgrow0 + drow; /*apply displacement to center row*/
11144: imgcol = imgcol0 + dcol; /*apply displacement to center col*/
11145: /* --- if pixel in bounds, accumulate weighted average --- */
11146: if ( imgrow>=0 && imgrow<imgheight ) /* row is in bounds */
11147: if ( imgcol>=0 && imgcol<imgwidth ) /* and col is in bounds */
11148: {
11149: /* --- accumulate weighted average --- */
11150: int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
11151: aaimgval += wt*imgval; /* weighted sum of image values */
11152: sumwts += wt; /* and also sum weights used */
11153: /* --- check if center image pixel black --- */
11154: if ( drow==0 && dcol==0 ) /* this is center ipixel */
11155: if ( imgval==black ) /* and it's black */
11156: iscenter = 1; /* so set black center flag true */
11157: } /* --- end-of-if(bounds checks ok) --- */
11158: } /* --- end-of-for(irow,icol) --- */
11159: if ( 0 && iscenter ) /* center point is black */
11160: aaimgval = black8; /* so force average black */
11161: else /* center point not black */
11162: aaimgval = /* 0=white ... black */
11163: ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
11164: /*end_of_job:*/
11165: return ( aaimgval );
11166: } /* --- end-of-function aawtpixel() --- */
11167: #endif /* PART3 */
11168:
11169: /* ---
11170: * PART1
11171: * ------ */
11172: #if !defined(PARTS) || defined(PART1)
11173: #ifdef DRIVER
11174: /* ==========================================================================
11175: * Function: main() driver for mimetex.c
11176: * Purpose: emits a mime xbitmap or gif image of a LaTeX math expression
11177: * entered either as
11178: * (1) html query string from a browser (most typical), or
11179: * (2) a query string from an html <form method="get">
11180: * whose <input name="formdata"> (mostly for demo), or
11181: * (3) command-line arguments (mostly to test).
11182: * If no input supplied, expression defaults to "f(x)=x^2",
11183: * treated as test (input method 3).
11184: * If args entered on command-line (or if no input supplied),
11185: * output is (usually) human-viewable ascii raster images on
11186: * stdout rather than the usual mime xbitmaps or gif images.
11187: * --------------------------------------------------------------------------
11188: * Command-Line Arguments:
11189: * When running mimeTeX from the command-line, rather than
11190: * from a browser, syntax is
11191: * ./mimetex [-d ] dump gif to stdout
11192: * [expression expression, e.g., x^2+y^2,
11193: * |-f input_file] or read expression from file
11194: * [-m msglevel] verbosity of debugging output
11195: * [-s fontsize] default fontsize, 0-5
11196: * -d Rather than ascii debugging output, mimeTeX dumps the
11197: * actual gif (or xbitmap) to stdout, e.g.,
11198: * ./mimetex -d x^2+y^2 > expression.gif
11199: * creates a gif file containing an image of x^2+y^2
11200: * -f Reads expression from input_file, and automatically
11201: * assumes -d switch. The input_file may contain the
11202: * expression on one line or spread out over many lines.
11203: * MimeTeX will concatanate all lines from input_file
11204: * to construct one long expression. Blanks, tabs, and
11205: * newlines will just be ignored.
11206: * -m 0-99, controls verbosity level for debugging output
11207: * (usually used only while testing code).
11208: * -s Font size, 0-5. As usual, the font size can
11209: * also be specified in the expression by a leading
11210: * preamble terminated by $, e.g., 3$f(x)=x^2 displays
11211: * f(x)=x^2 at font size 3. Default font size is 2.
11212: * --------------------------------------------------------------------------
11213: * Exits: 0=success, 1=some error
11214: * --------------------------------------------------------------------------
11215: * Notes: o For an executable that emits mime xbitmaps, compile as
11216: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
11217: * or, alternatively, for an executable that emits gif images
11218: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
11219: * or for gif images with anti-aliasing
11220: * cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
11221: * See Notes at top of file for other compile-line -D options.
11222: * o Move executable to your cgi-bin directory and either
11223: * point your browser to it directly in the form
11224: * http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
11225: * or put a tag in your html document of the form
11226: * <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
11227: * border=0 align=absmiddle>
11228: * where f(x)=x^2 (or any other expression) will be displayed
11229: * either as a mime xbitmap or gif image (as per -D flag).
11230: * ======================================================================= */
11231:
11232: /* -------------------------------------------------------------------------
11233: header files and other data
11234: -------------------------------------------------------------------------- */
11235: /* --- (additional) standard headers --- */
11236: /* --- other data --- */
11237: #ifdef DUMPENVIRON
11238: extern char **environ; /* environment information */
11239: #endif
11240:
11241: /* -------------------------------------------------------------------------
11242: globals for gif and png callback functions
11243: -------------------------------------------------------------------------- */
11244: GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */
11245: GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */
11246: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
11247: #ifdef AA /* if anti-aliasing requested */
11248: #define ISAAVALUE 1 /* turn flag on */
11249: #else
11250: #define ISAAVALUE 0 /* else turn flag off */
11251: #endif
11252: GLOBAL(int,isaa,ISAAVALUE); /* set anti-aliasing flag */
11253:
11254: /* -------------------------------------------------------------------------
11255: logging data structure, and default data to be logged
11256: -------------------------------------------------------------------------- */
11257: /* --- logging data structure --- */
11258: #define logdata struct logdata_struct /* "typedef" for logdata_struct*/
11259: logdata
11260: {
11261: /* -----------------------------------------------------------------------
11262: environment variable name, max #chars to display, min msglevel to display
11263: ------------------------------------------------------------------------ */
11264: char *name; /* environment variable name */
11265: int maxlen; /* max #chars to display */
11266: int msglevel; /* min msglevel to display data */
11267: } ; /* --- end-of-logdata_struct --- */
11268: /* --- data logged by mimeTeX --- */
11269: STATIC logdata mimelog[]
11270: #ifdef INITVALS
11271: =
11272: {
11273: /* ------ variable ------ maxlen msglevel ----- */
11274: { "QUERY_STRING", 999, 4 },
11275: { "REMOTE_ADDR", 999, 3 },
11276: { "HTTP_REFERER", 999, 3 },
11277: { "REQUEST_URI", 999, 5 },
11278: { "HTTP_USER_AGENT", 999, 3 },
11279: { "HTTP_X_FORWARDED_FOR", 999, 3 },
11280: { NULL, -1, -1 } /* trailer record */
11281: } /* --- end-of-mimelog[] --- */
11282: #endif
11283: ;
11284:
11285: /* -------------------------------------------------------------------------
11286: messages
11287: -------------------------------------------------------------------------- */
11288: static char *copyright = /* copyright, gnu/gpl notice */
11289: "+-----------------------------------------------------------------------+\n"
1.2 ! albertel 11290: "|mimeTeX vers 1.63, Copyright(c) 2002-2006, John Forkosh Associates, Inc|\n"
1.1 albertel 11291: "+-----------------------------------------------------------------------+\n"
11292: "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
11293: "| and comes with absolutely no warranty whatsoever. |\n"
11294: "+-----------------------------------------------------------------------+";
11295: static int maxmsgnum = 2; /* maximum msgtable[] index */
11296: static char *msgtable[] = { /* messages referenced by [index] */
11297: "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
11298: "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
11299: "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
11300: "\\red\\small\\rm\\fbox{\\array{" /* [1] */
11301: "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
11302: "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
11303: "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
11304: "\\red\\small\\rm\\fbox{\\array{" /* [2] */
11305: "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
11306: "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
11307: "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
11308: NULL } ; /* trailer */
11309:
11310:
11311: /* --- entry point --- */
11312: int main ( int argc, char *argv[]
11313: #ifdef DUMPENVP
11314: , char *envp[]
11315: #endif
11316: )
11317: {
11318: /* -------------------------------------------------------------------------
11319: Allocations and Declarations
11320: -------------------------------------------------------------------------- */
11321: /* --- expression to be emitted --- */
11322: static char exprbuffer[16385] = "f(x)=x^2"; /* expression to be processed */
11323: char *expression = exprbuffer; /* ptr to expression */
11324: int size = NORMALSIZE; /* default font size */
11325: char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
11326: char *mimeprep(); /* preprocess expression */
11327: int unescape_url(); /* convert %xx's to ascii chars */
11328: int emitcache(); /* emit cached image if it exists */
11329: int isquery = 0, /* true if input from QUERY_STRING */
11330: isqempty = 0, /* true if query string empty */
1.2 ! albertel 11331: isqforce = 0, /* true to force query emulation */
1.1 albertel 11332: isqlogging = 0, /* true if logging in query mode */
11333: isformdata = 0, /* true if input from html form */
1.2 ! albertel 11334: isinmemory = 1, /* true to generate image in memory*/
! 11335: isdumpimage = 0, /* true to dump image on stdout */
! 11336: isdumpbuffer = 0; /* true to dump to memory buffer */
1.1 albertel 11337: /* --- rasterization --- */
1.2 ! albertel 11338: subraster *rasterize(), *sp=NULL; /* rasterize expression */
! 11339: raster *border_raster(), *bp=NULL; /* put a border around raster */
! 11340: int delete_subraster(); /* for clean-up at end-of-job */
1.1 albertel 11341: int type_raster(), type_bytemap(), /* screen dump function prototypes */
11342: xbitmap_raster(); /* mime xbitmap output function */
11343: /* --- http_referer --- */
11344: char *referer = REFERER; /* http_referer must contain this */
11345: struct { char *referer; int msgnum; } /* http_referer can't contain this */
11346: denyreferer[] = { /* referer table to deny access to */
11347: #ifdef DENYREFERER
11348: #include DENYREFERER /* e.g., {"",1}, for no referer */
11349: #endif
11350: { NULL, -999 } }; /* trailer */
11351: char *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
11352: int ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));
11353: int isstrstr(); /* search http_referer for referer */
11354: int isinvalidreferer = 0; /* true for inavlid referer */
11355: int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/
11356: /* --- gif --- */
11357: #if defined(GIF)
11358: int GetPixel(); /* feed pixels to gifsave library */
11359: int GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
11360: void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
11361: #endif
11362: char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
1.2 ! albertel 11363: gif_buffer[64000] = "\000", /* or gif written in memory buffer */
1.1 albertel 11364: cachefile[256] = "\000", /* full path and name to cache file*/
11365: *md5str(); /* md5 has of expression */
11366: int maxage = 7200; /* max-age is two hours */
1.2 ! albertel 11367: /* --- pbm/pgm (-g switch) --- */
! 11368: int ispbmpgm = 0; /* true to write pbm/pgm file */
! 11369: int type_pbmpgm(), ptype=0; /* entry point, graphic format */
! 11370: char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
1.1 albertel 11371: /* --- anti-aliasing --- */
11372: intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
11373: colors[256]; /* grayscale vals in bytemap */
11374: int aalowpass(), aapnm(), /*lowpass filters for anti-aliasing*/
11375: grayscale = 256; /* 0-255 grayscales in 8-bit bytes */
11376: int ncolors=2, /* #colors (2=b&w) */
11377: aacolormap(); /* build colormap from bytemap */
11378: /* --- messages --- */
11379: char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/
11380: cachelog[256] = CACHELOG; /* cached image log in cachepath/ */
11381: char *timestamp(); /* time stamp for logged messages */
11382: int logger(); /* logs environ variables */
11383: int ismonth(); /* check argv[0] for current month */
11384: char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */
11385: char *dashes = /* separates logfile entries */
11386: "--------------------------------------------------------------------------";
11387: char *invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */
11388: /* -------------------------------------------------------------------------
11389: initialization
11390: -------------------------------------------------------------------------- */
11391: /* --- run optional system command string --- */
11392: #ifdef SYSTEM
11393: system(SYSTEM);
11394: #endif
11395: /* --- set global variables --- */
11396: msgfp = stdout; /* for comamnd-line mode output */
11397: isss = issupersampling; /* set supersampling flag */
1.2 ! albertel 11398: gifSize = 0; /* signal that image not in memory */
1.1 albertel 11399: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
11400: /* ---
11401: * check QUERY_STRING query for expression overriding command-line arg
11402: * ------------------------------------------------------------------- */
11403: if ( query != NULL ) /* check query string from environ */
11404: if ( strlen(query) >= 1 ) /* caller gave us a query string */
11405: { strncpy(expression,query,16384); /* so use it as expression */
11406: expression[16384] = '\000'; /* make sure it's null terminated */
11407: isquery = 1; } /* and set isquery flag */
11408: if ( !isquery ) /* empty query string */
11409: { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
11410: *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
11411: if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
11412: { isquery = 1; /* set flag to signal query */
1.2 ! albertel 11413: strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
1.1 albertel 11414: isqempty = 1; /* signal empty query string */
11415: } /* --- end-of-if(!isquery) --- */
11416: /* ---
11417: * process command-line input args (if not a query)
11418: * ------------------------------------------------ */
11419: if ( !isquery /* don't have an html query string */
11420: || ( /*isqempty &&*/ argc>1) ) /* or have command-line args */
11421: {
11422: char *argsignal = ARGSIGNAL, /* signals start of mimeTeX args */
11423: stopsignal[32] = "--"; /* default Unix end-of-args signal */
11424: int iarg=0, argnum=0, /*argv[] index for command-line args*/
11425: exprarg = 0, /* argv[] index for expression */
11426: infilearg = 0, /* argv[] index for infile */
11427: nswitches = 0, /* number of -switches */
11428: isstopsignal = 0, /* true after stopsignal found */
11429: isstrict = 1/*iswindows*/, /* true for strict arg checking */
11430: /*nb, windows has apache "x -3" bug*/
11431: nargs=0, nbadargs=0, /* number of arguments, bad ones */
11432: maxbadargs = (isstrict?0:1), /*assume query if too many bad args*/
11433: isgoodargs = 0; /* true to accept command-line args*/
11434: if ( argsignal != NULL ) /* if compiled with -DARGSIGNAL */
11435: while ( argc > ++iarg ) /* check each argv[] for argsignal */
11436: if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
11437: { argnum = iarg; /* got it, start parsing next arg */
11438: break; } /* stop looking for argsignal */
11439: while ( argc > ++argnum ) /* check for switches and values, */
11440: {
11441: nargs++; /* count another command-line arg */
11442: if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
11443: { isstopsignal = 1; /* so set stopsignal flag */
11444: continue; } /* and get expression after it */
11445: if ( !isstopsignal /* haven't seen stopsignal switch */
11446: && *argv[argnum] == '-' ) /* and have some '-' switch */
11447: {
1.2 ! albertel 11448: char *field = argv[argnum] + 1; /* ptr to char(s) following - */
! 11449: char flag = tolower(*field); /* single char following '-' */
! 11450: int arglen = strlen(field); /* #chars following - */
1.1 albertel 11451: argnum++; /* arg following flag/switch is usually its value */
11452: nswitches++; /* another switch on command line */
1.2 ! albertel 11453: if ( isstrict && /* if strict checking then... */
! 11454: !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
1.1 albertel 11455: { nbadargs++; argnum--; } /* so ignore longer -xxx switch */
11456: else /* process single-char -x switch */
11457: switch ( flag ) { /* see what user wants to tell us */
11458: /* --- ignore uninterpreted flag --- */
11459: default: nbadargs++; argnum--; break;
11460: /* --- adjustable program parameters (not checking input) --- */
1.2 ! albertel 11461: case 'b': isdumpimage++; isdumpbuffer++; argnum--; break;
1.1 albertel 11462: case 'd': isdumpimage++; argnum--; break;
11463: case 'e': isdumpimage++; gif_outfile=argv[argnum]; break;
11464: case 'f': isdumpimage++; infilearg=argnum; break;
1.2 ! albertel 11465: case 'g': ispbmpgm++;
! 11466: if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
! 11467: if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
! 11468: else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
1.1 albertel 11469: case 'm': msglevel = atoi(argv[argnum]); break;
11470: case 'o': istransparent = 0; argnum--; break;
1.2 ! albertel 11471: case 'q': isqforce = 1; argnum--; break;
1.1 albertel 11472: case 's': size = atoi(argv[argnum]); break;
11473: } /* --- end-of-switch(flag) --- */
11474: } /* --- end-of-if(*argv[argnum]=='-') --- */
11475: else /* expression if arg not a -flag */
11476: if ( infilearg == 0 ) /* no infile arg yet */
11477: { if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
11478: exprarg = argnum; /* but take last expression */
11479: /*infilearg = (-1);*/ } /* and set infilearg */
11480: else nbadargs++; /* infile and expression invalid */
11481: } /* --- end-of-while(argc>++argnum) --- */
11482: if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
1.2 ! albertel 11483: { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
! 11484: argc,progname,nargs,nbadargs);
! 11485: fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
! 11486: cachepath,pathprefix); }
1.1 albertel 11487: /* ---
11488: * decide whether command-line input overrides query_string
11489: * -------------------------------------------------------- */
11490: if ( isdumpimage > 2 ) nbadargs++; /* duplicate/conflicting -switch */
11491: isgoodargs = ( !isstrict /*good if not doing strict checking*/
11492: || !isquery /* or if no query, must use args */
11493: || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
11494: /* ---
11495: * take expression from command-line args
11496: * -------------------------------------- */
11497: if ( isgoodargs && exprarg > 0 /* good expression on command line */
11498: && infilearg <= 0 ) /* and not given in input file */
11499: if ( !isquery /* no conflict if no query_string */
11500: || nswitches > 0 ) /* explicit -switch(es) also given */
11501: { strncpy(expression,argv[exprarg],16384); /*expression from command-line*/
11502: expression[16384] = '\000'; /* make sure it's null terminated */
11503: isquery = 0; } /* and not from a query_string */
11504: /* ---
11505: * or read expression from input file
11506: * ---------------------------------- */
11507: if ( isgoodargs && infilearg > 0 ) /* have a good -f arg */
11508: {
11509: FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
11510: if ( infile != (FILE *)NULL ) /* opened input file successfully */
11511: { char instring[2049]; /* line from file */
11512: isquery = 0; /* file input, not a query_string */
11513: *expression = '\000'; /* start expresion as empty string */
11514: while ( fgets(instring,2048,infile) != (char *)NULL ) /* read till eof*/
11515: strcat(expression,instring); /* concat line to end of expression*/
11516: fclose ( infile ); } /*close input file after reading expression*/
11517: } /* --- end-of-if(infilearg>0) --- */
1.2 ! albertel 11518: /* ---
! 11519: * check if emulating query (for testing)
! 11520: * -------------------------------------- */
! 11521: if ( isqforce ) isquery = 1; /* emulate query string processing */
! 11522: /* ---
! 11523: * check if emitting pbm/pgm graphic
! 11524: * --------------------------------- */
! 11525: if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
! 11526: if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
! 11527: if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
! 11528: { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
! 11529: gif_outfile = (char *)NULL; /* reset gif output file */
! 11530: /*isdumpimage--;*/ } /* and decrement -e count */
1.1 albertel 11531: } /* --- end-of-if(!isquery) --- */
11532: /* ---
11533: * check for <form> input
11534: * ---------------------- */
11535: if ( isquery ) /* must be <form method="get"> */
11536: if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
11537: { char *delim=strchr(expression,'='); /* find equal following formdata */
11538: if ( delim != (char *)NULL ) /* found unescaped equal sign */
11539: strcpy(expression,delim+1); /* so shift name= out of expression*/
11540: while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
11541: *delim = ' '; /* is "shorthand" for blank space */
11542: /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */
11543: unescape_url(expression,0); /* convert all %xx's to chars */
11544: unescape_url(expression,0); /* repeat */
11545: msglevel = FORMLEVEL; /* msglevel for forms */
11546: isformdata = 1; } /* set flag to signal form data */
11547: else /* --- query, but not <form> input --- */
11548: unescape_url(expression,0); /* convert _all_ %xx's to chars */
11549: /* ---
11550: * check queries for embedded prefixes signalling special processing
11551: * ----------------------------------------------------------------- */
11552: if ( isquery ) /* only check queries */
11553: {
11554: /* --- check for msglevel=###$ prefix --- */
11555: if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
11556: { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
11557: if ( delim != (char *)NULL ) /* check that we found delim */
11558: { *delim = '\000'; /* replace delim with null */
11559: if ( seclevel <= 9 ) /* permit msglevel specification */
11560: msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
11561: strcpy(expression,delim+1); } } /* shift out prefix and delim */
11562: /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
11563: if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
11564: { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
11565: if ( delim != (char *)NULL ) /* check that we found delim */
11566: { *delim = '\000'; /* replace delim with null */
11567: if ( seclevel <= 3 ) /* permit logfile specification */
11568: strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */
11569: strcpy(expression,delim+1); } } /* shift out prefix and delim */
11570: } /* --- end-of-if(isquery) --- */
11571: /* ---
11572: * log query (e.g., for debugging)
11573: * ------------------------------- */
11574: if ( isquery ) /* only log query_string's */
11575: if ( msglevel >= LOGLEVEL /* check if logging */
11576: && seclevel <= 5 ) /* and if logging permitted */
11577: if ( logfile != NULL ) /* if a logfile is given */
11578: if ( *logfile != '\000' ) /*and if it's not an empty string*/
11579: if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */
11580: != NULL ) /* ignore logging if can't open */
11581: {
11582: /* --- default logging --- */
11583: logger(msgfp,msglevel,expression,mimelog); /* log query */
11584: /* --- additional debug logging (argv and environment) --- */
11585: if ( msglevel >= 9 ) /* log environment */
11586: { int i; /*char name[999],*value;*/
11587: fprintf(msgfp,"Command-line arguments...\n");
11588: if ( argc < 1 ) /* no command-line args */
11589: fprintf(msgfp," ...argc=%d, no argv[] variables\n",argc);
11590: else
11591: for ( i=0; i<argc; i++ ) /* display all argv[]'s */
11592: fprintf(msgfp," argv[%d] = \"%s\"\n",i,argv[i]);
11593: #ifdef DUMPENVP /* char *envp[] available for dump */
11594: fprintf(msgfp,"Environment variables (using envp[])...\n");
11595: if ( envp == (char **)NULL ) /* envp not provided */
11596: fprintf(msgfp," ...envp[] environment variables not available\n");
11597: else
11598: for ( i=0; ; i++ ) /* display all envp[]'s */
11599: if ( envp[i] == (char *)NULL ) break;
11600: else fprintf(msgfp," envp[%d] = \"%s\"\n",i,envp[i]);
11601: #endif /* --- DUMPENVP ---*/
11602: #ifdef DUMPENVIRON /* skip what should be redundant output */
11603: fprintf(msgfp,"Environment variables (using environ)...\n");
11604: if ( environ == (char **)NULL ) /* environ not provided */
11605: fprintf(msgfp," ...extern environ variables not available\n");
11606: else
11607: for ( i=0; ; i++ ) /*display environ[] and getenv()'s*/
11608: if ( environ[i] == (char *)NULL ) break;
11609: else {
11610: strcpy(name,environ[i]); /* set up name for getenv() arg */
11611: if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
11612: { *value = '\000'; /* got it, so null-terminate name */
11613: value = getenv(name); } /* and look up name using getenv() */
11614: else strcpy(name,"NULL"); /* missing = delim in environ[i] */
11615: fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
11616: i,environ[i],name,(value==NULL?"NULL":value));
11617: } /* --- end-of-if/else --- */
11618: #endif /* --- DUMPENVIRON ---*/
11619: } /* --- end-of-if(msglevel>=9) --- */
11620: /* --- close log file if no longer needed --- */
11621: if ( msglevel < DBGLEVEL ) /* logging, but not debugging */
11622: { fprintf(msgfp,"%s\n",dashes); /* so log separator line, */
11623: fclose(msgfp); /* close logfile immediately, */
11624: msgfp = NULL; } /* and reset msgfp pointer */
11625: else
11626: isqlogging = 1; /* set query logging flag */
11627: } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
11628: else /* couldn't open logfile */
11629: msglevel = 0; /* can't emit messages */
11630: /* ---
11631: * prepend prefix to submitted expression
11632: * -------------------------------------- */
11633: if ( 1 || isquery ) /* queries or command-line */
11634: if ( *exprprefix != '\000' ) /* we have a prefix string */
11635: { int npref = strlen(exprprefix); /* #chars in prefix */
11636: memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/
11637: memcpy(expression,exprprefix,npref); /* copy prefix into expression */
11638: expression[npref] = '{'; /* followed by { */
11639: strcat(expression,"}"); } /* and terminating } to balance { */
11640: /* ---
11641: * check if http_referer is allowed to use this image
11642: * -------------------------------------------------- */
11643: if ( isquery ) /* not relevant if "interactive" */
11644: if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */
11645: if ( strcmp(referer,"month") != 0 ) /* nor if it's *only* "month" */
11646: if ( http_referer != NULL ) /* nor if called "standalone" */
11647: if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */
11648: { expression = invalid_referer_msg; /* so give user error message */
11649: isinvalidreferer = 1; } /* and signal invalid referer */
11650: /* ---
11651: * check if referer contains "month" signal
11652: * ---------------------------------------- */
11653: if ( isquery ) /* not relevant if "interactive" */
11654: if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */
11655: if ( !isinvalidreferer ) /* nor if already invalid referer */
11656: if ( strstr(referer,"month") != NULL ) /* month check requested */
11657: if ( !ismonth(progname) ) /* not executed as mimetexJan-Dec */
11658: { expression = invalid_referer_msg; /* so give user error message */
11659: isinvalidreferer = 1; } /* and signal invalid referer */
11660: /* ---
11661: * check if http_referer is to be denied access
11662: * -------------------------------------------- */
11663: if ( isquery ) /* not relevant if "interactive" */
11664: if ( !isinvalidreferer ) /* nor if already invalid referer */
11665: { int iref=0, msgnum=(-999); /* denyreferer index, message# */
11666: for ( iref=0; msgnum<0; iref++ ) { /* run through denyreferer[] table */
11667: char *deny = denyreferer[iref].referer; /* referer to be denied */
11668: if ( deny == NULL ) break; /* null signals end-of-table */
11669: if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
11670: {fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
11671: iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
11672: if ( *deny == '\000' ) /* signal to check for no referer */
11673: { if ( http_referer == NULL ) /* http_referer not supplied */
11674: msgnum = denyreferer[iref].msgnum; } /* so set message# */
11675: else /* have referer to check for */
11676: if ( http_referer != NULL ) /* and have referer to be checked */
11677: if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
11678: msgnum = denyreferer[iref].msgnum; /* so set message# */
11679: } /* --- end-of-for(iref) --- */
11680: if ( msgnum >= 0 ) /* deny access to this referer */
11681: { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
11682: expression = msgtable[msgnum]; /* set user error message */
11683: isinvalidreferer = 1; } /* and signal invalid referer */
11684: } /* --- end-of-if(!isinvalidreferer) --- */
11685: /* --- also check maximum query_string length if no http_referer given --- */
11686: if ( isquery ) /* not relevant if "interactive" */
11687: if ( !isinvalidreferer ) /* nor if already invalid referer */
11688: if ( !ishttpreferer ) /* no http_referer supplied */
11689: if ( strlen(expression) > norefmaxlen ) /* query_string too long */
11690: { expression = invalid_referer_msg; /* set invalid http_referer message*/
11691: isinvalidreferer = 1; } /* and signal invalid referer */
11692: /* ---
11693: * check for image caching
11694: * ----------------------- */
1.2 ! albertel 11695: if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */
! 11696: || strstr(expression,"\\input") != NULL /* can't cache \input{} */
! 11697: || strstr(expression,"\\today") != NULL /* can't cache \today */
! 11698: || strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
! 11699: || strstr(expression,"\\nocach") != NULL /* no caching requested */
! 11700: || isformdata /* don't cache user form input */
1.1 albertel 11701: ) { iscaching = 0; /* so turn caching off */
1.2 ! albertel 11702: maxage = 5; } /* and set max-age to 5 seconds */
1.1 albertel 11703: if ( isquery ) /* don't cache command-line images */
11704: if ( iscaching ) /* image caching enabled */
11705: {
11706: /* --- set up path to cached image file --- */
11707: char *md5hash = md5str(expression); /* md5 hash of expression */
11708: if ( md5hash == NULL ) /* failed for some reason */
11709: iscaching = 0; /* so turn off caching */
11710: else
11711: {
11712: strcpy(cachefile,cachepath); /* start with (relative) path */
11713: strcat(cachefile,md5hash); /* add md5 hash of expression */
11714: strcat(cachefile,".gif"); /* finish with .gif extension */
11715: gif_outfile = cachefile; /* signal GIF_Create() to cache */
1.2 ! albertel 11716: /* --- emit mime content-type line --- */
! 11717: if ( 0 ) /* now done in emitcache() */
! 11718: { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
! 11719: fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.1 albertel 11720: /* --- emit cached image if it already exists --- */
1.2 ! albertel 11721: if ( emitcache(cachefile,maxage,0) > 0 ) /* cached image emitted */
1.1 albertel 11722: goto end_of_job; /* so nothing else to do */
11723: /* --- log caching request --- */
11724: if ( msglevel >= 1 /* check if logging */
11725: /*&& seclevel <= 5*/ ) /* and if logging permitted */
11726: if ( cachelog != NULL ) /* if a logfile is given */
11727: if ( *cachelog != '\000' ) /*and if it's not an empty string*/
11728: { char filename[256]; /* construct cachepath/cachelog */
11729: FILE *filefp=NULL; /* fopen(filename) */
11730: strcpy(filename,cachepath); /* start with (relative) path */
11731: strcat(filename,cachelog); /* add cache log filename */
11732: if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
11733: != NULL ) /* ignore logging if can't open */
11734: { int isreflogged = 0; /* set true if http_referer logged */
11735: fprintf(filefp,"%s %s\n", /* timestamp, md5 file */
1.2 ! albertel 11736: timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
1.1 albertel 11737: fprintf(filefp,"%s\n",expression); /* expression in filename */
11738: if ( http_referer != NULL ) /* show referer if we have one */
11739: if ( *http_referer != '\000' ) /* and if not an empty string*/
11740: { int loglen = strlen(dashes); /* #chars on line in log file*/
11741: char *refp = http_referer; /* line to be printed */
11742: isreflogged = 1; /* signal http_referer logged*/
11743: while ( 1 ) { /* printed in parts if needed*/
11744: fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
11745: if ( strlen(refp) <= loglen ) break; /* no more parts */
11746: refp += loglen; } } /* bump ptr to next part */
11747: if ( !isreflogged ) /* http_referer not logged */
11748: fprintf(filefp,"http://none\n"); /* so log dummy referer line */
11749: fprintf(filefp,"%s\n",dashes); /* separator line */
11750: fclose(filefp); } /* close logfile immediately */
11751: } /* --- end-of-if(cachelog!=NULL) --- */
11752: } /* --- end-of-if/else(md5hash==NULL) --- */
11753: } /* --- end-of-if(iscaching) --- */
11754: /* ---
11755: * emit copyright, gnu/gpl notice (if "interactive")
11756: * ------------------------------------------------- */
11757: if ( !isdumpimage ) /* don't mix ascii with image dump */
11758: if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */
11759: fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */
11760: /* -------------------------------------------------------------------------
11761: rasterize expression and put a border around it
11762: -------------------------------------------------------------------------- */
11763: /* --- preprocess expression, converting LaTeX constructs for mimeTeX --- */
11764: expression = mimeprep(expression); /* preprocess expression */
11765: /* --- double-check that we actually have an expression to rasterize --- */
11766: if ( expression == NULL ) /* nothing to rasterize */
11767: { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
11768: fprintf(msgfp,"No expression to rasterize\n");
11769: goto end_of_job; } /* and then quit */
11770: /* --- rasterize expression --- */
11771: if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */
11772: { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
11773: fprintf(msgfp,"Failed to rasterize %s\n",expression);
11774: if ( isquery ) sp = rasterize( /* or emit error raster if query */
11775: "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1);
11776: if ( sp == NULL ) goto end_of_job; } /* re-check for failure */
11777: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
11778: if ( issupersampling ) /* no border needed for gifs */
11779: bp = sp->image; /* so just extract pixel map */
11780: else /* for mime xbitmaps must have... */
11781: bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
11782: sp->image = bitmap_raster = bp; /* global copy for gif,png output */
1.2 ! albertel 11783: if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
! 11784: type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
1.1 albertel 11785: /* -------------------------------------------------------------------------
11786: generate anti-aliased bytemap from (bordered) bitmap
11787: -------------------------------------------------------------------------- */
11788: if ( isaa ) /* we want anti-aliased bitmap */
11789: {
11790: /* ---
11791: * allocate bytemap and colormap as per width*height of bitmap
11792: * ----------------------------------------------------------- */
11793: int nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/
11794: if ( isss ) /* anti-aliasing by supersampling */
11795: bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
11796: else /* need to allocate bytemap */
11797: if ( aaalgorithm == 0 ) /* anti-aliasing not wanted */
11798: isaa = 0; /* so signal no anti-aliasing */
11799: else /* anti-aliasing wanted */
11800: if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
11801: == NULL ) isaa = 0; /* reset flag if malloc failed */
11802: if ( isaa ) /* have bytemap, so... */
11803: if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */
11804: == NULL ) isaa = 0; /* reset flag if malloc failed */
11805: /* ---
11806: * now generate anti-aliased bytemap and colormap from bitmap
11807: * ---------------------------------------------------------- */
11808: if ( isaa ) /*re-check that we're anti-aliasing*/
11809: {
11810: /* ---
11811: * select anti-aliasing algorithm
11812: * ------------------------------ */
11813: if ( !isss ) /* generate bytemap for lowpass */
11814: if ( aaalgorithm == 1 ) /* 1 for aalowpass() */
11815: { if ( aalowpass(bp,bytemap_raster,grayscale) /* my lowpass filter */
11816: == 0 ) isaa = 0; } /*failed, so turn off anti-aliasing*/
11817: else /* or 2 for aapnm() */
11818: if ( aaalgorithm == 2 ) /*2 for netpbm pnmalias.c algorithm*/
11819: { if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
11820: == 0 ) isaa = 0; } /*failed, so turn off anti-aliasing*/
11821: else isaa = 0; /* unrecognized algorithm */
11822: /* ---
11823: * finally, generate colors and colormap
11824: * ------------------------------------- */
11825: if ( isaa ) { /* we have bytemap_raster */
11826: ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
11827: if ( ncolors < 2 ) /* failed */
11828: { isaa = 0; /* so turn off anti-aliasing */
11829: ncolors = 2; } /* and reset for black&white */
11830: } /* --- end-of-if(isaa) --- */
1.2 ! albertel 11831: if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch */
! 11832: raster pbm_raster; /*construct arg for write_pbmpgm()*/
! 11833: pbm_raster.width = bp->width; pbm_raster.height = bp->height;
! 11834: pbm_raster.pixsz = 8; pbm_raster.pixmap = (pixbyte *)bytemap_raster;
! 11835: type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
1.1 albertel 11836: } /* --- end-of-if(isaa) --- */
11837: } /* --- end-of-if(isaa) --- */
11838: /* -------------------------------------------------------------------------
11839: display results on msgfp if called from command line (usually for testing)
11840: -------------------------------------------------------------------------- */
11841: if ( (!isquery||isqlogging) || msglevel >= 99 ) /*command line or debuging*/
11842: if ( !isdumpimage ) /* don't mix ascii with image dump */
11843: {
11844: /* ---
11845: * display ascii image of rasterize()'s rasterized bitmap
11846: * ------------------------------------------------------ */
11847: if ( !isss ) /* no bitmap for supersampling */
11848: { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
11849: type_raster(bp,msgfp); } /* emit ascii image of raster */
11850: /* ---
11851: * display anti-aliasing results applied to rasterized bitmap
11852: * ---------------------------------------------------------- */
11853: if ( isaa ) /* if anti-aliasing applied */
11854: {
11855: int igray; /* colors[] index */
11856: /* --- anti-aliased bytemap image --- */
11857: if ( msgfp!=NULL && msglevel>=9 ) /* don't usually emit raw bytemap */
11858: { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
11859: "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
11860: type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); }
11861: /* --- colormap image --- */
11862: fprintf(msgfp,"\nHex dump of colormap indexes, " /* emit colormap */
11863: "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
11864: type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp);
11865: /* --- rgb values corresponding to colormap indexes */
11866: fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
11867: for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
11868: fprintf(msgfp,"%s%2x-->%3d", (igray%5?" ":"\n"),
11869: igray,(int)(colors[ncolors-1]-colors[igray]));
11870: fprintf(msgfp,"\n"); /* always needs a final newline */
11871: } /* --- end-of-if(isaa) --- */
11872: } /* --- end-of-if(!isquery||msglevel>=9) --- */
11873: /* -------------------------------------------------------------------------
11874: emit xbitmap or gif image, and exit
11875: -------------------------------------------------------------------------- */
11876: if ( isquery /* called from browser (usual) */
1.2 ! albertel 11877: || (isdumpimage && !ispbmpgm) /* or to emit gif dump of image */
1.1 albertel 11878: || msglevel >= 99 ) /* or for debugging */
11879: {
11880: int igray = 0; /* grayscale index */
11881: #if defined(GIF) /* compiled to emit gif */
11882: /* ------------------------------------------------------------------------
11883: emit GIF image
11884: ------------------------------------------------------------------------- */
1.2 ! albertel 11885: /* --- don't use memory buffer if outout file given --- */
! 11886: if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
1.1 albertel 11887: /* --- emit mime content-type line --- */
11888: if ( !isdumpimage /* don't mix ascii with image dump */
1.2 ! albertel 11889: && !isinmemory /* done below if in memory */
! 11890: && !iscaching ) /* done by emitcache() if caching */
1.1 albertel 11891: { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
11892: /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
11893: /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/
11894: fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.2 ! albertel 11895: /* --- write output to memory buffer, possibly for testing --- */
! 11896: if ( isinmemory /* want gif written to memory */
! 11897: || isdumpbuffer ) /*or dump memory buffer for testing*/
! 11898: if ( gif_outfile == NULL ) /* and don't already have a file */
! 11899: { *gif_buffer = '\000'; /* init buffer as empty string */
! 11900: memset(gif_buffer,0,4096); /* zero out buffer */
! 11901: gif_outfile = gif_buffer; /* and point outfile to buffer */
! 11902: if ( isdumpbuffer ) /* buffer dump test requested */
! 11903: isdumpbuffer = 999; } /* so signal dumping to buffer */
1.1 albertel 11904: /* --- initialize gifsave library and colors --- */
11905: if ( msgfp!=NULL && msglevel>=999 )
1.2 ! albertel 11906: { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
! 11907: bp->width,bp->height,ncolors); fflush(msgfp); }
1.1 albertel 11908: while ( 1 ) /* init gifsave lib, and retry if caching fails */
11909: { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);
11910: if ( status == 0 ) break; /* continue if succeeded */
11911: if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
11912: iscaching = 0; /* retry without cache file */
1.2 ! albertel 11913: isdumpbuffer = 0; /* reset isdumpbuffer signal */
! 11914: if ( isquery ) isinmemory = 1; /* force in-memory image generation*/
! 11915: if ( isinmemory ) { /* using memory buffer */
! 11916: gif_outfile = gif_buffer; /* emit images to memory buffer */
! 11917: *gif_outfile = '\000'; } /* empty string signals buffer */
! 11918: else { /* or */
! 11919: gif_outfile = (char *)NULL; /* emit images to stdout */
! 11920: fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
! 11921: fprintf( stdout, "Content-type: image/gif\n\n" ); }
! 11922: } /* --- end-of-while(1) --- */
1.1 albertel 11923: GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */
11924: if ( !isaa ) /* just b&w if not anti-aliased */
11925: { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
11926: colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
11927: else /* set grayscales for anti-aliasing */
11928: /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
11929: for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
11930: {
11931: /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
11932: double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
11933: /* --- r,g,b components go from background to foreground color --- */
11934: int red = iround(((double)bgred) +gfrac*((double)(fgred-bgred))),
11935: green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
11936: blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
11937: /* --- set color index number igray to rgb values gray,gray,gray --- */
11938: GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
11939: } /* --- end-of-for(igray) --- */
11940: /* --- set gif color#0 (background) transparent --- */
11941: if ( istransparent ) /* transparent background wanted */
11942: GIF_SetTransparent(0); /* set transparent background */
11943: if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
11944: /* --- emit compressed gif image (to stdout or cache file) --- */
11945: GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
11946: GIF_Close(); /* close file */
1.2 ! albertel 11947: if ( msgfp!=NULL && msglevel>=9 )
! 11948: { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
! 11949: fflush(msgfp); }
! 11950: /* --- may need to emit image from cached file or from memory --- */
! 11951: if ( isquery /* have an actual query string */
! 11952: || isdumpimage /* or dumping image */
! 11953: || msglevel >= 99 ) { /* or debugging */
! 11954: int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
! 11955: if ( iscaching ) /* caching enabled */
! 11956: emitcache(cachefile,maxage2,0); /* cached image (hopefully) emitted*/
! 11957: else if ( isinmemory ) /* or emit image from memory buffer*/
! 11958: emitcache(gif_buffer,maxage2,1); } /* emitted from memory buffer */
! 11959: /* --- for testing, may need to write image buffer to file --- */
! 11960: if ( isdumpbuffer > 99 ) /* gif image in memory buffer */
! 11961: if ( gifSize > 0 ) /* and it's not an empty buffer */
! 11962: { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
! 11963: if ( dumpfp != NULL ) /* file opened successfully */
! 11964: { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
! 11965: fclose(dumpfp); } /* and close file */
! 11966: } /* --- end-of-if(isdumpbuffer>99) --- */
1.1 albertel 11967: #else
11968: /* ------------------------------------------------------------------------
11969: emit mime XBITMAP image
11970: ------------------------------------------------------------------------- */
11971: xbitmap_raster(bp,stdout); /* default emits mime xbitmap */
11972: #endif
11973: } /* --- end-of-if(isquery) --- */
11974: /* --- exit --- */
11975: end_of_job:
1.2 ! albertel 11976: if ( !isss ) /*bytemap raster in sp for supersamp*/
! 11977: if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
1.1 albertel 11978: if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
1.2 ! albertel 11979: if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
! 11980: if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */
1.1 albertel 11981: if ( msgfp != NULL /* have message/log file open */
11982: && msgfp != stdout ) /* and it's not stdout */
1.2 ! albertel 11983: { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
! 11984: timestamp(TZDELTA,0));
1.1 albertel 11985: fprintf(msgfp,"%s\n",dashes); /* so log separator line */
11986: fclose(msgfp); } /* and close logfile */
1.2 ! albertel 11987: /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
! 11988: #if defined(_CRTDBG_MAP_ALLOC)
! 11989: _CrtDumpMemoryLeaks();
! 11990: #endif
! 11991: /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
! 11992: #if !defined(_USRDLL)
! 11993: exit ( 0 );
! 11994: #endif
1.1 albertel 11995: } /* --- end-of-function main() --- */
11996:
11997: /* ==========================================================================
1.2 ! albertel 11998: * Function: CreateGifFromEq ( expression, gifFileName )
! 11999: * Purpose: shortcut method to create GIF file for expression,
! 12000: * with antialising and all other capabilities
! 12001: * --------------------------------------------------------------------------
! 12002: * Arguments: expression (I) char *ptr to null-terminated string
! 12003: * containing LaTeX expression to be rendred
! 12004: * gifFileName (I) char *ptr to null-terminated string
! 12005: * containing name of output gif file
! 12006: * --------------------------------------------------------------------------
! 12007: * Returns: ( int ) exit value from main (0 if successful)
! 12008: * --------------------------------------------------------------------------
! 12009: * Notes: o This function is the entry point when mimeTeX is built
! 12010: * as a Win32 DLL rather then a standalone app or CGI
! 12011: * o Contributed to mimeTeX by Shital Shah. See his homepage
! 12012: * http://www.shitalshah.com
! 12013: * o Shital discusses the mimeTeX Win32 DLL project at
! 12014: * http://www.codeproject.com/dotnet/Eq2Img.asp
! 12015: * and you can download his latest code from
! 12016: * http://www.shitalshah.com/dev/eq2img_all.zip
! 12017: * ======================================================================= */
! 12018: /* --- include function to expose Win32 DLL to outside world --- */
! 12019: #if defined(_USRDLL)
! 12020: extern _declspec(dllexport)int _cdecl
! 12021: CreateGifFromEq ( char *expression, char *gifFileName );
! 12022: #endif
! 12023: /* --- entry point --- */
! 12024: int CreateGifFromEq ( char *expression, char *gifFileName )
! 12025: {
! 12026: /* -------------------------------------------------------------------------
! 12027: Allocations and Declarations
! 12028: -------------------------------------------------------------------------- */
! 12029: int main(); /* main() akways returns an int */
! 12030: /* --- set constants --- */
! 12031: int argc = 4; /* count of args supplied to main() */
! 12032: char *argv[5] = /* command line args to run with -e option */
! 12033: { "MimeTeXWin32DLL", "-e", /* constant args */
! 12034: /*gifFileName, expression,*/ NULL, NULL, NULL };
! 12035: /* --- set argv[]'s not computable at load time --- */
! 12036: argv[2] = gifFileName; /* args are -e gifFileName */
! 12037: argv[3] = expression; /* and now -e gifFileName expression */
! 12038: /* -------------------------------------------------------------------------
! 12039: Run mimeTeX in command-line mode with -e (export) option, and then return
! 12040: -------------------------------------------------------------------------- */
! 12041: return main ( argc, argv
! 12042: #ifdef DUMPENVP
! 12043: , NULL
! 12044: #endif
! 12045: ) ;
! 12046: } /* --- end-of-function CreateGifFromEq() --- */
! 12047:
! 12048: /* ==========================================================================
1.1 albertel 12049: * Function: isstrstr ( char *string, char *snippets, int iscase )
12050: * Purpose: determine whether any substring of 'string'
12051: * matches any of the comma-separated list of 'snippets',
12052: * ignoring case if iscase=0.
12053: * --------------------------------------------------------------------------
12054: * Arguments: string (I) char * containing null-terminated
12055: * string that will be searched for
12056: * any one of the specified snippets
12057: * snippets (I) char * containing null-terminated,
12058: * comma-separated list of snippets
12059: * to be searched for in string
12060: * iscase (I) int containing 0 for case-insensitive
12061: * comparisons, or 1 for case-sensitive
12062: * --------------------------------------------------------------------------
12063: * Returns: ( int ) 1 if any snippet is a substring of
12064: * string, 0 if not
12065: * --------------------------------------------------------------------------
12066: * Notes: o
12067: * ======================================================================= */
12068: /* --- entry point --- */
12069: int isstrstr ( char *string, char *snippets, int iscase )
12070: {
12071: /* -------------------------------------------------------------------------
12072: Allocations and Declarations
12073: -------------------------------------------------------------------------- */
12074: int status = 0; /*1 if any snippet found in string*/
12075: char snip[99], *snipptr = snippets, /* munge through each snippet */
12076: delim = ',', *delimptr = NULL; /* separated by delim's */
12077: char stringcp[999], *cp = stringcp; /*maybe lowercased copy of string*/
12078: /* -------------------------------------------------------------------------
12079: initialization
12080: -------------------------------------------------------------------------- */
12081: /* --- arg check --- */
12082: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
12083: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
12084: /* --- copy string and lowercase it if case-insensitive --- */
12085: strcpy(stringcp,string); /* local copy of string */
12086: if ( !iscase ) /* want case-insensitive compares */
12087: for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
12088: if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
12089: /* -------------------------------------------------------------------------
12090: extract each snippet and see if it's a substring of string
12091: -------------------------------------------------------------------------- */
12092: while ( snipptr != NULL ) /* while we still have snippets */
12093: {
12094: /* --- extract next snippet --- */
12095: if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
12096: == NULL ) /*not found following last snippet*/
12097: { strcpy(snip,snipptr); /* local copy of last snippet */
12098: snipptr = NULL; } /* signal end-of-string */
12099: else /* snippet ends just before delim */
12100: { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */
12101: memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */
12102: snip[sniplen] = '\000'; /* null-terminated snippet */
12103: snipptr = delimptr + 1; } /* next snippet starts after delim */
12104: /* --- lowercase snippet if case-insensitive --- */
12105: if ( !iscase ) /* want case-insensitive compares */
12106: for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
12107: if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
12108: /* --- check if snippet in string --- */
12109: if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */
12110: { status = 1; /* so reset return status */
12111: break; } /* no need to check any further */
12112: } /* --- end-of-while(*snipptr!=0) --- */
12113: end_of_job: return ( status ); /*1 if snippet found in list, else 0*/
12114: } /* --- end-of-function isstrstr() --- */
12115:
12116: /* ==========================================================================
12117: * Function: ismonth ( char *month )
12118: * Purpose: returns 1 if month contains current month "jan"..."dec".
12119: * --------------------------------------------------------------------------
12120: * Arguments: month (I) char * containing null-terminated string
12121: * in which "jan"..."dec" is (putatively)
12122: * contained as a substring.
12123: * --------------------------------------------------------------------------
12124: * Returns: ( int ) 1 if month contains current month,
12125: * 0 otherwise
12126: * --------------------------------------------------------------------------
12127: * Notes: o There's a three day "grace period", e.g., Dec 3 mtaches Nov.
12128: * ======================================================================= */
12129: /* --- entry point --- */
12130: int ismonth ( char *month )
12131: {
12132: /* -------------------------------------------------------------------------
12133: Allocations and Declarations
12134: -------------------------------------------------------------------------- */
12135: int isokay = 0; /*1 if month contains current month*/
12136: /*long time_val = 0L;*/ /* binary value returned by time() */
12137: time_t time_val = (time_t)(0); /* binary value returned by time() */
12138: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
12139: int imonth, mday; /* current month 1-12 and day 1-31 */
12140: int ngrace = 3; /* grace period */
12141: char lcmonth[128]="\000"; int i=0; /* lowercase month */
12142: static char *months[] = /* month must contain current one */
12143: {"dec","jan","feb","mar","apr","may","jun",
12144: "jul","aug","sep","oct","nov","dec","jan"};
12145: /* -------------------------------------------------------------------------
12146: get current date:time info, and check month
12147: -------------------------------------------------------------------------- */
12148: /* --- lowercase input month --- */
12149: if ( month != NULL ) /* check that we got input */
12150: for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
12151: lcmonth[i] = tolower(*month); /* lowerase each char in month */
12152: if ( i < 2 ) goto end_of_job; /* must be invalid input */
12153: lcmonth[i] = '\000'; /* null-terminate lcmonth[] */
12154: /* --- get current date:time --- */
12155: time((time_t *)(&time_val)); /* get date and time */
12156: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
12157: /* --- month and day --- */
12158: imonth = 1 + (int)(tmstruct->tm_mon); /* 1=jan ... 12=dec */
12159: mday = (int)(tmstruct->tm_mday); /* 1-31 */
12160: if ( imonth<1 || imonth>12 /* quit if month out-of-range */
12161: || mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
12162: /* --- check input month against current date --- */
12163: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
12164: if ( mday <= ngrace ) /* 1-3 within grace period */
12165: if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
12166: if ( mday >= 31-ngrace ) /* 28-31 within grace period */
12167: if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
12168: end_of_job:
12169: return ( isokay ); /*1 if month contains current month*/
12170: } /* --- end-of-function ismonth() --- */
12171:
12172: /* ==========================================================================
12173: * Functions: int unescape_url ( char *url, int isescape )
12174: * char x2c ( char *what )
12175: * Purpose: unescape_url replaces 3-character sequences %xx in url
12176: * with the single character represented by hex xx.
12177: * x2c returns the single character represented by hex xx
12178: * passed as a 2-character sequence in what.
12179: * --------------------------------------------------------------------------
12180: * Arguments: url (I) char * containing null-terminated
12181: * string with embedded %xx sequences
12182: * to be converted.
12183: * isescape (I) int containing 1 to _not_ unescape
12184: * \% sequences (0 would be NCSA default)
12185: * what (I) char * whose first 2 characters are
12186: * interpreted as ascii representations
12187: * of hex digits.
12188: * --------------------------------------------------------------------------
12189: * Returns: ( int ) unescape_url always returns 0.
12190: * ( char ) x2c returns the single char
12191: * corresponding to hex xx passed in what.
12192: * --------------------------------------------------------------------------
12193: * Notes: o These two functions were taken verbatim from util.c in
12194: * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
12195: * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
12196: * so unescape_url() can be safely applied to input which may or
12197: * may not have been url-encoded.
12198: * ======================================================================= */
12199: /* --- entry point --- */
12200: int unescape_url(char *url, int isescape) {
12201: int x=0,y=0,prevescape=0,gotescape=0;
12202: char x2c();
12203: static char *hex="0123456789ABCDEFabcdef";
12204: for(;url[y];++x,++y) {
12205: gotescape = prevescape;
12206: prevescape = (url[x]=='\\');
12207: if((url[x] = url[y]) == '%')
12208: if(!isescape || !gotescape)
12209: if(isthischar(url[y+1],hex)
12210: && isthischar(url[y+2],hex))
12211: { url[x] = x2c(&url[y+1]);
12212: y+=2; }
12213: }
12214: url[x] = '\0';
12215: return 0;
12216: } /* --- end-of-function unescape_url() --- */
12217: /* --- entry point --- */
12218: char x2c(char *what) {
12219: char digit;
12220: digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
12221: digit *= 16;
12222: digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
12223: return(digit);
12224: } /* --- end-of-function x2c() --- */
12225:
12226: /* ==========================================================================
12227: * Function: logger ( fp, msglevel, message, logvars )
12228: * Purpose: Logs the environment variables specified in logvars
12229: * to fp if their msglevel is >= the passed msglevel.
12230: * --------------------------------------------------------------------------
12231: * Arguments: fp (I) FILE * to file containing log
12232: * msglevel (I) int containing logging message level
12233: * message (I) char * to optional message, or NULL
12234: * logvars (I) logdata * to array of environment variables
12235: * to be logged
12236: * --------------------------------------------------------------------------
12237: * Returns: ( int ) number of variables from logvars
12238: * that were actually logged
12239: * --------------------------------------------------------------------------
12240: * Notes: o
12241: * ======================================================================= */
12242: /* --- entry point --- */
12243: int logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
12244: {
12245: /* -------------------------------------------------------------------------
12246: Allocations and Declarations
12247: -------------------------------------------------------------------------- */
12248: int ilog=0, nlogged=0; /* logvars[] index, #vars logged */
12249: char *timestamp(); /* timestamp logged */
12250: char *value = NULL; /* getenv(name) to be logged */
12251: /* -------------------------------------------------------------------------
12252: Log each variable
12253: -------------------------------------------------------------------------- */
1.2 ! albertel 12254: fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
1.1 albertel 12255: if ( message != NULL ) /* optional message supplied */
12256: fprintf(fp," MESSAGE = %s\n",message); /* emit caller-supplied message */
12257: if ( logvars != (logdata *)NULL ) /* have logvars */
12258: for ( ilog=0; logvars[ilog].name != NULL; ilog++ ) /* till end-of-table */
12259: if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
12260: if ( (value=getenv(logvars[ilog].name)) /* getenv(name) to be logged */
12261: != NULL ) /* check that name exists */
12262: {
12263: fprintf(fp," %s = %.*s\n", /* emit variable name = value */
12264: logvars[ilog].name,logvars[ilog].maxlen,value);
12265: nlogged++; /* bump #vars logged */
12266: } /* --- end-of-for(ilog) --- */
12267: return ( nlogged ); /* back to caller */
12268: } /* --- end-of-function logger() --- */
12269:
12270: /* ==========================================================================
1.2 ! albertel 12271: * Function: emitcache ( cachefile, maxage, isbuffer )
1.1 albertel 12272: * Purpose: dumps bytes from cachefile to stdout
12273: * --------------------------------------------------------------------------
12274: * Arguments: cachefile (I) pointer to null-terminated char string
1.2 ! albertel 12275: * containing full path to file to be dumped,
! 12276: * or contains buffer of bytes to be dumped
! 12277: * maxage (I) int containing maxage. in seconds, for
! 12278: * http header, or -1 to not emit headers
! 12279: * isbuffer (I) 1 if cachefile is buffer of bytes to be
! 12280: * dumped
1.1 albertel 12281: * --------------------------------------------------------------------------
12282: * Returns: ( int ) #bytes dumped (0 signals error)
12283: * --------------------------------------------------------------------------
12284: * Notes: o
12285: * ======================================================================= */
12286: /* --- entry point --- */
1.2 ! albertel 12287: int emitcache ( char *cachefile, int maxage, int isbuffer )
1.1 albertel 12288: {
12289: /* -------------------------------------------------------------------------
12290: Allocations and Declarations
12291: -------------------------------------------------------------------------- */
1.2 ! albertel 12292: int nbytes=gifSize, readcachefile(); /* read cache file */
! 12293: FILE *emitptr = stdout; /* emit cachefile to stdout */
! 12294: unsigned char buffer[64000]; /* bytes from cachefile */
! 12295: unsigned char *buffptr = buffer; /* ptr to buffer */
1.1 albertel 12296: /* -------------------------------------------------------------------------
12297: initialization
12298: -------------------------------------------------------------------------- */
12299: /* --- check that files opened okay --- */
1.2 ! albertel 12300: if ( emitptr == (FILE *)NULL ) /* failed to open emit file */
1.1 albertel 12301: goto end_of_job; /* so return 0 bytes to caller */
1.2 ! albertel 12302: /* --- read the file if necessary --- */
! 12303: if ( isbuffer ) /* cachefile is buffer */
! 12304: buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */
! 12305: else /* cachefile is file name */
! 12306: if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
! 12307: < 1 ) goto end_of_job; /* quit if file not read */
! 12308: /* --- first emit http headers if requested --- */
! 12309: if ( maxage >= 0 ) /* caller wants http headers */
! 12310: { /* --- emit mime content-type line --- */
! 12311: fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
! 12312: fprintf( emitptr, "Content-Length: %d\n",nbytes );
! 12313: fprintf( emitptr, "Content-type: image/gif\n\n" ); }
! 12314: /* -------------------------------------------------------------------------
! 12315: set stdout to binary mode (for Windows)
! 12316: -------------------------------------------------------------------------- */
1.1 albertel 12317: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */ /* doesn't work portably, */
12318: #ifdef WINDOWS /* so instead... */
12319: #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */
12320: if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
12321: == -1 ) ; /* handle error */ /* sets stdout to binary mode */
12322: #else /* setmode() not available */
12323: #if 1
12324: freopen ("CON", "wb", stdout); /* freopen() stdout binary */
12325: #else
12326: stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
12327: #endif
12328: #endif
12329: #endif
12330: /* -------------------------------------------------------------------------
12331: emit bytes from cachefile
12332: -------------------------------------------------------------------------- */
1.2 ! albertel 12333: /* --- write bytes to stdout --- */
! 12334: if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
! 12335: < nbytes ) /* failed to write all bytes */
! 12336: nbytes = 0; /* reset total count to 0 */
! 12337: end_of_job:
! 12338: return ( nbytes ); /* back with #bytes emitted */
! 12339: } /* --- end-of-function emitcache() --- */
! 12340:
! 12341: /* ==========================================================================
! 12342: * Function: readcachefile ( cachefile, buffer )
! 12343: * Purpose: read cachefile into buffer
! 12344: * --------------------------------------------------------------------------
! 12345: * Arguments: cachefile (I) pointer to null-terminated char string
! 12346: * containing full path to file to be read
! 12347: * buffer (O) pointer to unsigned char string
! 12348: * returning contents of cachefile
! 12349: * (max 64000 bytes)
! 12350: * --------------------------------------------------------------------------
! 12351: * Returns: ( int ) #bytes read (0 signals error)
! 12352: * --------------------------------------------------------------------------
! 12353: * Notes: o
! 12354: * ======================================================================= */
! 12355: /* --- entry point --- */
! 12356: int readcachefile ( char *cachefile, unsigned char *buffer )
! 12357: {
! 12358: /* -------------------------------------------------------------------------
! 12359: Allocations and Declarations
! 12360: -------------------------------------------------------------------------- */
! 12361: FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
! 12362: unsigned char cachebuff[64]; /* bytes from cachefile */
! 12363: int buflen = 32, /* #bytes we try to read from file */
! 12364: nread = 0, /* #bytes actually read from file */
! 12365: maxbytes = 64000, /* max #bytes returned in buffer */
! 12366: nbytes = 0; /* total #bytes read */
! 12367: /* -------------------------------------------------------------------------
! 12368: initialization
! 12369: -------------------------------------------------------------------------- */
! 12370: /* --- check that files opened okay --- */
! 12371: if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
! 12372: /* --- check that output buffer provided --- */
! 12373: if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
! 12374: /* -------------------------------------------------------------------------
! 12375: read bytes from cachefile
! 12376: -------------------------------------------------------------------------- */
1.1 albertel 12377: while ( 1 )
12378: {
12379: /* --- read bytes from cachefile --- */
1.2 ! albertel 12380: nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
! 12381: if ( nbytes + nread > maxbytes ) /* block too big for buffer */
! 12382: nread = maxbytes - nbytes; /* so truncate it */
1.1 albertel 12383: if ( nread < 1 ) break; /* no bytes left in cachefile */
1.2 ! albertel 12384: /* --- store bytes in buffer --- */
! 12385: memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
! 12386: /* --- ready to read next block --- */
1.1 albertel 12387: nbytes += nread; /* bump total #bytes emitted */
12388: if ( nread < buflen ) break; /* no bytes left in cachefile */
1.2 ! albertel 12389: if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */
1.1 albertel 12390: } /* --- end-of-while(1) --- */
12391: end_of_job:
12392: if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
12393: return ( nbytes ); /* back with #bytes emitted */
1.2 ! albertel 12394: } /* --- end-of-function readcachefile() --- */
1.1 albertel 12395:
12396: /* ==========================================================================
12397: * Function: md5str ( instr )
12398: * Purpose: returns null-terminated character string containing
12399: * md5 hash of instr (input string)
12400: * --------------------------------------------------------------------------
12401: * Arguments: instr (I) pointer to null-terminated char string
12402: * containing input string whose md5 hash
12403: * is desired
12404: * --------------------------------------------------------------------------
12405: * Returns: ( char * ) ptr to null-terminated 32-character
12406: * md5 hash of instr
12407: * --------------------------------------------------------------------------
12408: * Notes: o Other md5 library functions are included below.
12409: * They're all taken from Christophe Devine's code,
12410: * which (as of 04-Aug-2004) is available from
12411: * http://www.cr0.net:8040/code/crypto/md5/
12412: * o The P,F,S macros in the original code are replaced
12413: * by four functions P1()...P4() to accommodate a problem
12414: * with Compaq's vax/vms C compiler.
12415: * ======================================================================= */
12416: /* --- #include "md5.h" --- */
12417: #ifndef uint8
12418: #define uint8 unsigned char
12419: #endif
12420: #ifndef uint32
12421: #define uint32 unsigned long int
12422: #endif
12423: typedef struct
12424: { uint32 total[2];
12425: uint32 state[4];
12426: uint8 buffer[64];
12427: } md5_context;
12428: void md5_starts( md5_context *ctx );
12429: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
12430: void md5_finish( md5_context *ctx, uint8 digest[16] );
12431: /* --- md5.h --- */
12432: #define GET_UINT32(n,b,i) \
12433: { (n) = ( (uint32) (b)[(i) ] ) \
12434: | ( (uint32) (b)[(i) + 1] << 8 ) \
12435: | ( (uint32) (b)[(i) + 2] << 16 ) \
12436: | ( (uint32) (b)[(i) + 3] << 24 ); }
12437: #define PUT_UINT32(n,b,i) \
12438: { (b)[(i) ] = (uint8) ( (n) ); \
12439: (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
12440: (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
12441: (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
12442: /* --- P,S,F macros defined as functions --- */
12443: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
12444: { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
12445: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
12446: return; }
12447: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
12448: { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
12449: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
12450: return; }
12451: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
12452: { *a += (uint32)(b ^ c ^ d) + X[k] + t;
12453: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
12454: return; }
12455: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
12456: { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
12457: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
12458: return; }
12459:
12460: /* --- entry point (this one little stub written by me)--- */
12461: char *md5str( char *instr )
12462: { static char outstr[64];
12463: unsigned char md5sum[16];
12464: md5_context ctx;
12465: int j;
12466: md5_starts( &ctx );
12467: md5_update( &ctx, (uint8 *)instr, strlen(instr) );
12468: md5_finish( &ctx, md5sum );
12469: for( j=0; j<16; j++ )
12470: sprintf( outstr + j*2, "%02x", md5sum[j] );
12471: outstr[32] = '\000';
12472: return ( outstr ); }
12473:
12474: /* --- entry point (all md5 functions below by Christophe Devine) --- */
12475: void md5_starts( md5_context *ctx )
12476: { ctx->total[0] = 0;
12477: ctx->total[1] = 0;
12478: ctx->state[0] = 0x67452301;
12479: ctx->state[1] = 0xEFCDAB89;
12480: ctx->state[2] = 0x98BADCFE;
12481: ctx->state[3] = 0x10325476; }
12482:
12483: void md5_process( md5_context *ctx, uint8 data[64] )
12484: { uint32 X[16], A, B, C, D;
12485: GET_UINT32( X[0], data, 0 );
12486: GET_UINT32( X[1], data, 4 );
12487: GET_UINT32( X[2], data, 8 );
12488: GET_UINT32( X[3], data, 12 );
12489: GET_UINT32( X[4], data, 16 );
12490: GET_UINT32( X[5], data, 20 );
12491: GET_UINT32( X[6], data, 24 );
12492: GET_UINT32( X[7], data, 28 );
12493: GET_UINT32( X[8], data, 32 );
12494: GET_UINT32( X[9], data, 36 );
12495: GET_UINT32( X[10], data, 40 );
12496: GET_UINT32( X[11], data, 44 );
12497: GET_UINT32( X[12], data, 48 );
12498: GET_UINT32( X[13], data, 52 );
12499: GET_UINT32( X[14], data, 56 );
12500: GET_UINT32( X[15], data, 60 );
12501: A = ctx->state[0];
12502: B = ctx->state[1];
12503: C = ctx->state[2];
12504: D = ctx->state[3];
12505: P1( X, &A, B, C, D, 0, 7, 0xD76AA478 );
12506: P1( X, &D, A, B, C, 1, 12, 0xE8C7B756 );
12507: P1( X, &C, D, A, B, 2, 17, 0x242070DB );
12508: P1( X, &B, C, D, A, 3, 22, 0xC1BDCEEE );
12509: P1( X, &A, B, C, D, 4, 7, 0xF57C0FAF );
12510: P1( X, &D, A, B, C, 5, 12, 0x4787C62A );
12511: P1( X, &C, D, A, B, 6, 17, 0xA8304613 );
12512: P1( X, &B, C, D, A, 7, 22, 0xFD469501 );
12513: P1( X, &A, B, C, D, 8, 7, 0x698098D8 );
12514: P1( X, &D, A, B, C, 9, 12, 0x8B44F7AF );
12515: P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
12516: P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
12517: P1( X, &A, B, C, D, 12, 7, 0x6B901122 );
12518: P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
12519: P1( X, &C, D, A, B, 14, 17, 0xA679438E );
12520: P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
12521: P2( X, &A, B, C, D, 1, 5, 0xF61E2562 );
12522: P2( X, &D, A, B, C, 6, 9, 0xC040B340 );
12523: P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
12524: P2( X, &B, C, D, A, 0, 20, 0xE9B6C7AA );
12525: P2( X, &A, B, C, D, 5, 5, 0xD62F105D );
12526: P2( X, &D, A, B, C, 10, 9, 0x02441453 );
12527: P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
12528: P2( X, &B, C, D, A, 4, 20, 0xE7D3FBC8 );
12529: P2( X, &A, B, C, D, 9, 5, 0x21E1CDE6 );
12530: P2( X, &D, A, B, C, 14, 9, 0xC33707D6 );
12531: P2( X, &C, D, A, B, 3, 14, 0xF4D50D87 );
12532: P2( X, &B, C, D, A, 8, 20, 0x455A14ED );
12533: P2( X, &A, B, C, D, 13, 5, 0xA9E3E905 );
12534: P2( X, &D, A, B, C, 2, 9, 0xFCEFA3F8 );
12535: P2( X, &C, D, A, B, 7, 14, 0x676F02D9 );
12536: P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
12537: P3( X, &A, B, C, D, 5, 4, 0xFFFA3942 );
12538: P3( X, &D, A, B, C, 8, 11, 0x8771F681 );
12539: P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
12540: P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
12541: P3( X, &A, B, C, D, 1, 4, 0xA4BEEA44 );
12542: P3( X, &D, A, B, C, 4, 11, 0x4BDECFA9 );
12543: P3( X, &C, D, A, B, 7, 16, 0xF6BB4B60 );
12544: P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
12545: P3( X, &A, B, C, D, 13, 4, 0x289B7EC6 );
12546: P3( X, &D, A, B, C, 0, 11, 0xEAA127FA );
12547: P3( X, &C, D, A, B, 3, 16, 0xD4EF3085 );
12548: P3( X, &B, C, D, A, 6, 23, 0x04881D05 );
12549: P3( X, &A, B, C, D, 9, 4, 0xD9D4D039 );
12550: P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
12551: P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
12552: P3( X, &B, C, D, A, 2, 23, 0xC4AC5665 );
12553: P4( X, &A, B, C, D, 0, 6, 0xF4292244 );
12554: P4( X, &D, A, B, C, 7, 10, 0x432AFF97 );
12555: P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
12556: P4( X, &B, C, D, A, 5, 21, 0xFC93A039 );
12557: P4( X, &A, B, C, D, 12, 6, 0x655B59C3 );
12558: P4( X, &D, A, B, C, 3, 10, 0x8F0CCC92 );
12559: P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
12560: P4( X, &B, C, D, A, 1, 21, 0x85845DD1 );
12561: P4( X, &A, B, C, D, 8, 6, 0x6FA87E4F );
12562: P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
12563: P4( X, &C, D, A, B, 6, 15, 0xA3014314 );
12564: P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
12565: P4( X, &A, B, C, D, 4, 6, 0xF7537E82 );
12566: P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
12567: P4( X, &C, D, A, B, 2, 15, 0x2AD7D2BB );
12568: P4( X, &B, C, D, A, 9, 21, 0xEB86D391 );
12569: ctx->state[0] += A;
12570: ctx->state[1] += B;
12571: ctx->state[2] += C;
12572: ctx->state[3] += D; }
12573:
12574: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
12575: { uint32 left, fill;
12576: if( length < 1 ) return;
12577: left = ctx->total[0] & 0x3F;
12578: fill = 64 - left;
12579: ctx->total[0] += length;
12580: ctx->total[0] &= 0xFFFFFFFF;
12581: if( ctx->total[0] < length )
12582: ctx->total[1]++;
12583: if( left && length >= fill )
12584: { memcpy( (void *) (ctx->buffer + left),
12585: (void *) input, fill );
12586: md5_process( ctx, ctx->buffer );
12587: length -= fill;
12588: input += fill;
12589: left = 0; }
12590: while( length >= 64 )
12591: { md5_process( ctx, input );
12592: length -= 64;
12593: input += 64; }
12594: if( length >= 1 )
12595: memcpy( (void *) (ctx->buffer + left),
12596: (void *) input, length ); }
12597:
12598: void md5_finish( md5_context *ctx, uint8 digest[16] )
12599: { static uint8 md5_padding[64] =
12600: { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12601: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12602: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12603: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
12604: uint32 last, padn;
12605: uint32 high, low;
12606: uint8 msglen[8];
12607: high = ( ctx->total[0] >> 29 )
12608: | ( ctx->total[1] << 3 );
12609: low = ( ctx->total[0] << 3 );
12610: PUT_UINT32( low, msglen, 0 );
12611: PUT_UINT32( high, msglen, 4 );
12612: last = ctx->total[0] & 0x3F;
12613: padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
12614: md5_update( ctx, md5_padding, padn );
12615: md5_update( ctx, msglen, 8 );
12616: PUT_UINT32( ctx->state[0], digest, 0 );
12617: PUT_UINT32( ctx->state[1], digest, 4 );
12618: PUT_UINT32( ctx->state[2], digest, 8 );
12619: PUT_UINT32( ctx->state[3], digest, 12 ); }
12620: /* --- end-of-function md5str() and "friends" --- */
12621:
12622: #if defined(GIF)
12623: /* ==========================================================================
12624: * Function: GetPixel ( int x, int y )
12625: * Purpose: callback for GIF_CompressImage() returning the
12626: * pixel at column x, row y
12627: * --------------------------------------------------------------------------
12628: * Arguments: x (I) int containing column=0...width-1
12629: * of desired pixel
12630: * y (I) int containing row=0...height-1
12631: * of desired pixel
12632: * --------------------------------------------------------------------------
12633: * Returns: ( int ) 0 or 1, if pixel at x,y is off or on
12634: * --------------------------------------------------------------------------
12635: * Notes: o
12636: * ======================================================================= */
12637: /* --- entry point --- */
12638: int GetPixel ( int x, int y )
12639: {
12640: int ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/
12641: int pixval =0; /* value of pixel */
12642: if ( !isaa ) /* use bitmap if not anti-aliased */
12643: pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
12644: else /* else use anti-aliased grayscale*/
12645: pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
12646: if ( msgfp!=NULL && msglevel>=9999 ) /* dump pixel */
12647: { fprintf(msgfp,"GetPixel> x=%d, y=%d pixel=%d\n",x,y,pixval);
12648: fflush(msgfp); }
12649: return pixval;
12650: } /* --- end-of-function GetPixel() --- */
12651: #endif /* gif */
12652: #endif /* driver */
12653: #endif /* PART1 */
12654: /* ======================= END-OF-FILE MIMETEX.C ========================= */
12655:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>