Annotation of loncom/cgi/mimeTeX/mimetex.c, revision 1.3
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.
1.3 ! albertel 39: * But image caching is available by using mimeTeX's
! 40: * -DCACHEPATH=\"path/\" compile option (see below).
1.1 albertel 41: * There's also no inherent need to repeatedly write the
42: * cumbersome <img> tag illustrated above. You can write
43: * your own custom tags, or write a wrapper script around
1.3 ! albertel 44: * mimeTeX to simplify the notation.
1.1 albertel 45: *
46: * Functions: ===================== Raster Functions ======================
47: * PART2 --- raster constructor functions ---
48: * new_raster(width,height,pixsz) allocation (and constructor)
49: * new_subraster(width,height,pixsz)allocation (and constructor)
50: * new_chardef() allocate chardef struct
51: * delete_raster(rp) deallocate raster (rp = raster ptr)
52: * delete_subraster(sp) deallocate subraster (sp=subraster ptr)
53: * delete_chardef(cp) deallocate chardef (cp = chardef ptr)
54: * --- primitive (sub)raster functions ---
55: * rastcpy(rp) allocate new copy of rp
56: * subrastcpy(sp) allocate new copy of sp
57: * rastrot(rp) new raster rotated right 90 degrees to rp
1.3 ! albertel 58: * rastref(rp,axis) new raster reflected (axis 1=horz,2=vert)
1.1 albertel 59: * rastput(target,source,top,left,isopaque) overlay src on trgt
60: * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
61: * rastcat(sp1,sp2,isfree) concatanate sp1||sp2
62: * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
63: * rastile(tiles,ntiles) create composite raster from tiles
1.2 albertel 64: * rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2
1.3 ! albertel 65: * rastsmashcheck(term) check if term is "safe" to smash
1.1 albertel 66: * --- raster "drawing" functions ---
67: * accent_subraster(accent,width,height) draw \hat\vec\etc
68: * arrow_subraster(width,height,drctn,isBig) left/right arrow
69: * uparrow_subraster(width,height,drctn,isBig) up/down arrow
70: * rule_raster(rp,top,left,width,height,type) draw rule in rp
71: * line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
72: * line_recurse(rp,row0,col0,row1,col1,thickness) recurse line
73: * circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
74: * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
75: * bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively
76: * border_raster(rp,ntop,nbot,isline,isfree)put border around rp
1.3 ! albertel 77: * backspace_raster(rp,nback,pback,minspace,isfree) neg space
1.1 albertel 78: * --- raster (and chardef) output functions ---
79: * type_raster(rp,fp) emit ascii dump of rp on file ptr fp
80: * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
81: * xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp
1.2 albertel 82: * type_pbmpgm(rp,ptype,file) pbm or pgm image of rp to file
1.1 albertel 83: * cstruct_chardef(cp,fp,col1) emit C struct of cp on fp
84: * cstruct_raster(rp,fp,col1) emit C struct of rp on fp
85: * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
86: * --- ancillary output functions ---
87: * emit_string(fp,col1,string,comment) emit string and C comment
1.2 albertel 88: * gftobitmap(rp) convert .gf-like pixmap to bitmap image
1.1 albertel 89: * ====================== Font Functions =======================
90: * --- font lookup functions ---
1.3 ! albertel 91: * get_symdef(symbol) return mathchardef for symbol
! 92: * get_ligature(expr,family) return symtable index for ligature
! 93: * get_chardef(symdef,size) return chardef for symdef,size
1.1 albertel 94: * get_charsubraster(symdef,size) wrap subraster around chardef
1.2 albertel 95: * get_symsubraster(symbol,size) returns subraster for symbol
1.1 albertel 96: * --- ancillary font functions ---
97: * get_baseline(gfdata) determine baseline (in our coords)
98: * get_delim(symbol,height,family) delim just larger than height
99: * make_delim(symbol,height) construct delim exactly height size
100: * ================= Tokenize/Parse Functions ==================
101: * texchar(expression,chartoken) retruns next char or \sequence
102: * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
1.2 albertel 103: * texleft(expr,subexpr,maxsubsz,ldelim,rdelim) \left...\right
1.1 albertel 104: * texscripts(expression,subscript,superscript,which)get scripts
105: * --- ancillary parse functions ---
106: * isbrace(expression,braces,isescape) check for leading brace
107: * preamble(expression,size,subexpr) parse preamble
108: * mimeprep(expression) preprocessor converts \left( to \(, etc.
109: * strchange(nfirst,from,to) change nfirst chars of from to to
110: * strreplace(string,from,to,nreplace) change from to to in str
1.3 ! albertel 111: * strwstr(string,substr,white,sublen) find substr in string
1.1 albertel 112: * strtexchr(string,texchr) find texchr in string
113: * findbraces(expression,command) find opening { or closing }
114: * PART3 =========== Rasterize an Expression (recursively) ===========
115: * --- here's the primary entry point for all of mimeTeX ---
116: * rasterize(expression,size) parse and rasterize expression
117: * --- explicitly called handlers that rasterize... ---
118: * rastparen(subexpr,size,basesp) parenthesized subexpr
119: * rastlimits(expression,size,basesp) dispatch super/sub call
120: * rastscripts(expression,size,basesp) super/subscripted exprssn
121: * rastdispmath(expression,size,sp) scripts for displaymath
122: * --- table-driven handlers that rasterize... ---
123: * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
1.2 albertel 124: * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
125: * rastmiddle(expression,size,basesp,arg1,arg2,arg3) \middle
1.1 albertel 126: * rastflags(expression,size,basesp,flag,value,arg3) set flag
127: * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
128: * rastnewline(expression,size,basesp,arg1,arg2,arg3) \\
129: * rastarrow(expression,size,basesp,width,height,drctn) \longarr
130: * rastuparrow(expression,size,basesp,width,height,drctn)up/down
131: * rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not
132: * rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
133: * rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel
134: * rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
135: * rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt
136: * rastaccent(expression,size,basesp,accent,isabove,isscript)
137: * rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
138: * rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{}
139: * rastarray(expression,size,basesp,arg1,arg2,arg3) \array
140: * rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture
141: * rastline(expression,size,basesp,arg1,arg2,arg3) \line
1.3 ! albertel 142: * rastrule(expression,size,basesp,arg1,arg2,arg3) \rule
1.1 albertel 143: * rastcircle(expression,size,basesp,arg1,arg2,arg3) \circle
144: * rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier
145: * rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox
146: * rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox
1.3 ! albertel 147: * rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
1.1 albertel 148: * rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
149: * rastinput(expression,size,basesp,arg1,arg2,arg3) \input
150: * rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter
1.2 albertel 151: * rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
152: * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
1.1 albertel 153: * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
154: * --- helper functions for handlers ---
155: * rastopenfile(filename,mode) opens filename[.tex] in mode
1.2 albertel 156: * rasteditfilename(filename) edit filename (for security)
157: * rastreadfile(filename,islock,tag,value) read <tag>...</tag>
1.1 albertel 158: * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
1.2 albertel 159: * calendar(year,month,day) formats one-month calendar string
160: * timestamp(tzdelta,ifmt) formats timestamp string
161: * tzadjust(tzdelta,year,month,day,hour) adjust date/time
162: * daynumber(year,month,day) #days since Monday, Jan 1, 1973
163: * dbltoa(d,npts) double to comma-separated ascii
1.1 albertel 164: * === Anti-alias completed raster (lowpass) or symbols (ss) ===
165: * aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap
166: * aapnm(rp,bytemap,grayscale) lowpass based on pnmalias.c
1.3 ! albertel 167: * aapnmlookup(rp,bytemap,grayscale) aapnm based on aagridnum()
! 168: * aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
! 169: * aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
! 170: * aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
! 171: * aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
! 172: * aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
! 173: * aafollowline(rp,irow,icol,direction) looks for a "turn"
! 174: * aagridnum(rp,irow,icol) calculates gridnum, 0-511
! 175: * aapatternnum(gridnum) looks up pattern#, 1-51, for gridnum
! 176: * aalookup(gridnum) table lookup for all possible 3x3 grids
! 177: * aalowpasslookup(rp,bytemap,grayscale) driver for aalookup()
1.1 albertel 178: * aasupsamp(rp,aa,sf,grayscale) or by supersampling
179: * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
180: * aaweights(width,height) builds "canonical" weight matrix
181: * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
1.3 ! albertel 182: * === miscellaneous ===
! 183: * mimetexsetmsg(newmsglevel,newmsgfp) set msglevel and msgfp
1.1 albertel 184: * PART1 ========================== Driver ===========================
185: * main(argc,argv) parses math expression and emits mime xbitmap
1.2 albertel 186: * CreateGifFromEq(expression,gifFileName) entry pt for win dll
1.1 albertel 187: * isstrstr(string,snippets,iscase) are any snippets in string?
188: * ismonth(month) is month current month ("jan"-"dec")?
189: * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
190: * logger(fp,msglevel,logvars) logs environment variables
1.2 albertel 191: * emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
192: * readcachefile(cachefile,buffer) read cachefile into buffer
1.1 albertel 193: * md5str(instr) md5 hash library functions
194: * GetPixel(x,y) callback function for gifsave library
195: *
196: * Source: mimetex.c (needs mimetex.h and texfonts.h to compile,
197: * and also needs gifsave.c if compiled with -DAA or -DGIF)
198: *
199: * --------------------------------------------------------------------------
200: * Notes o See bottom of file for main() driver (and "friends"),
201: * and compile as
202: * cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
203: * to produce an executable that emits gif images with
204: * anti-aliasing (see Notes below). You may also compile
205: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
206: * to produce an executable that emits gif images without
207: * anti-aliasing. Alternatively, compile mimeTeX as
208: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
209: * to produce an executable that just emits mime xbitmaps.
210: * In either case you'll need mimetex.h and texfonts.h,
211: * and with -DAA or -DGIF you'll also need gifsave.c
212: * o For gif images, the gifsave.c library by Sverre H. Huseby
213: * <http://shh.thathost.com> slightly modified by me to allow
214: * (a)sending output to stdout and (b)specifying a transparent
215: * background color index, is included with mimeTeX,
216: * and it's documented in mimetex.html#gifsave .
217: * o Optional compile-line -D defined symbols are documented
1.3 ! albertel 218: * in mimetex.html#options . They include (additional -D
! 219: * switches are discussed in mimetex.html#options)...
1.1 albertel 220: * -DAA
221: * Turns on gif anti-aliasing with default values
222: * (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
223: * for the following anti-aliasing parameters...
224: * -DCENTERWT=n
225: * -DADJACENTWT=j
226: * -DCORNERWT=k
227: * MimeTeX currently provides a lowpass filtering
228: * algorithm for anti-aliasing, which is applied to the
229: * existing set of bitmap fonts. This lowpass filter
230: * applies default weights
1.3 ! albertel 231: * 1 2 1
! 232: * 2 8 2
! 233: * 1 2 1
1.1 albertel 234: * to neighboring pixels. The defaults weights are
1.3 ! albertel 235: * CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
1.1 albertel 236: * which you can adjust to control anti-aliasing.
237: * Lower CENTERWT values will blur/spread out lines
238: * while higher values will tend to sharpen lines.
239: * Experimentation is recommended to determine
240: * what value works best for you.
241: * -DCACHEPATH=\"path/\"
242: * This option saves each rendered image to a file
243: * in directory path/ which mimeTeX reads rather than
244: * re-rendering the same image every time it's given
245: * the same LaTeX expression. Sometimes mimeTeX disables
246: * caching, e.g., expressions containing \input{ } are
247: * re-rendered since the contents of the inputted file
248: * may have changed. If compiled without -DCACHEPATH
249: * mimeTeX always re-renders expressions. This usually
250: * isn't too cpu intensive, but if you have unusually
251: * high hit rates then image caching may be helpful.
252: * The path/ is relative to mimetex.cgi, and must
253: * be writable by it. Files created under path/ are
254: * named filename.gif, where filename is the 32-character
255: * MD5 hash of the LaTeX expression.
256: * -DDISPLAYSIZE=n
257: * By default, operator limits like \int_a^b are rendered
258: * \textstyle at font sizes \normalsize and smaller,
259: * and rendered \displaystyle at font sizes \large and
260: * larger. This default corresponds to -DDISPLAYSIZE=3,
261: * which you can adjust; e.g., -DDISPLAYSIZE=0 always
262: * defaults to \displaystyle, and 99 (or any large number)
263: * always defaults to \textstyle. Note that explicit
264: * \textstyle, \displaystyle, \limits or \nolimits
265: * directives in an expression always override
266: * the DISPLAYSIZE default.
267: * -NORMALSIZE=n
268: * MimeTeX currently has six font sizes numbered 0-5,
269: * and always starts in NORMALSIZE whose default value
270: * is 2. Specify -DNORMALSIZE=3 on the compile line if
271: * you prefer mimeTeX to start in default size 3, etc.
272: * -DREFERER=\"domain\" -or-
273: * -DREFERER=\"domain1,domain2,etc\"
274: * Blocks mimeTeX requests from unauthorized domains that
275: * may be using your server's mimetex.cgi without permission.
276: * If REFERER is defined, mimeTeX checks for the environment
277: * variable HTTP_REFERER and, if it exists, performs a
278: * case-insensitive test to make sure it contains 'domain'
279: * as a substring. If given several 'domain's (second form)
280: * then HTTP_REFERER must contain either 'domain1' or
281: * 'domain2', etc, as a (case-insensitive) substring.
282: * If HTTP_REFERER fails to contain a substring matching
283: * any of these domain(s), mimeTeX emits an error message
284: * image corresponding to the expression specified by
285: * the invalid_referer_msg string defined in main().
286: * Note: if HTTP_REFERER is not an environment variable,
287: * mimeTeX correctly generates the requested expression
288: * (i.e., no referer error).
289: * -DWARNINGS=n -or-
290: * -DNOWARNINGS
291: * If an expression submitted to mimeTeX contains an
292: * unrecognzied escape sequence, e.g., "y=x+\abc+1", then
293: * mimeTeX generates a gif image containing an embedded
294: * warning in the form "y=x+[\abc?]+1". If you want these
295: * warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
296: * mimeTeX to ignore unrecognized symbols, and the rendered
297: * image is "y=x++1" instead.
298: * -DWHITE
299: * MimeTeX usually renders black symbols on a white
300: * background. This option renders white symbols on
301: * a black background instead.
302: * o See individual function entry points for further comments.
303: * o The font information in texfonts.h was produced by multiple
304: * runs of gfuntype, one run per struct (i.e., one run per font
305: * family at a particular size). See gfuntype.c, and also
306: * mimetex.html#fonts, for details.
307: * o mimetex.c contains library functions implementing a raster
308: * datatype, functions to manipulate rasterized .mf fonts
309: * (see gfuntype.c which rasterizes .mf fonts), functions
310: * to parse LaTeX expressions, etc. A complete list of
311: * mimetex.c functions is above. See their individual entry
312: * points below for further comments.
313: * All these functions eventually belong in several
314: * different modules, possibly along the lines suggested
315: * by the divisions above. But until the best decomposition
316: * becomes clear, it seems better to keep mimetex.c
317: * neatly together, avoiding a bad decomposition that
318: * becomes permanent by default.
319: * o The "main" reusable function is rasterize(),
320: * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
321: * and returns a (sub)raster representing it as a bit or bytemap.
322: * Your application can do anything it likes with this pixel map.
323: * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
324: * --------------------------------------------------------------------------
325: * Revision History:
326: * 09/18/02 J.Forkosh Installation.
327: * 12/11/02 J.Forkosh Version 1.00 released.
328: * 07/04/03 J.Forkosh Version 1.01 released.
329: * 10/17/03 J.Forkosh Version 1.20 released.
330: * 12/21/03 J.Forkosh Version 1.30 released.
331: * 02/01/04 J.Forkosh Version 1.40 released.
332: * 10/02/04 J.Forkosh Version 1.50 released.
333: * 11/30/04 J.Forkosh Version 1.60 released.
1.3 ! albertel 334: * 10/11/05 J.Forkosh Version 1.64 released.
! 335: * 11/30/06 J.Forkosh most recent changes
1.1 albertel 336: *
337: ****************************************************************************/
338:
339: /* -------------------------------------------------------------------------
340: header files and macros
341: -------------------------------------------------------------------------- */
342: /* --- standard headers --- */
343: #include <stdio.h>
344: #include <stdlib.h>
345: /*#include <unistd.h>*/
346: #include <string.h>
347: #include <ctype.h>
348: #include <math.h>
349: #include <time.h>
350:
351: /* --- windows-specific header info --- */
352: #ifndef WINDOWS /* -DWINDOWS not supplied by user */
1.2 albertel 353: #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
354: || defined(DJGPP) /* try to recognize windows compilers */ \
355: || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
1.1 albertel 356: #define WINDOWS /* signal windows */
357: #endif
358: #endif
359: #ifdef WINDOWS /* Windows opens stdout in char mode, and */
360: #include <fcntl.h> /* precedes every 0x0A with spurious 0x0D.*/
361: #include <io.h> /* So emitcache() issues a Win _setmode() */
362: /* call to put stdout in binary mode. */
363: #if defined(_O_BINARY) && !defined(O_BINARY) /* only have _O_BINARY */
364: #define O_BINARY _O_BINARY /* make O_BINARY available, etc... */
365: #define setmode _setmode
366: #define fileno _fileno
367: #endif
368: #if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */
369: #define HAVE_SETMODE /* so we'll use setmode() */
370: #endif
1.2 albertel 371: #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
372: /* to show source file and line numbers where memory leaks occur... */
373: #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
374: #include <crtdbg.h> /* and this debug library */
375: #endif
1.1 albertel 376: #define ISWINDOWS 1
377: #else
378: #define ISWINDOWS 0
379: #endif
380:
381: /* --- check for supersampling or low-pass anti-aliasing --- */
382: #ifdef SS
383: #define ISSUPERSAMPLING 1
384: #ifndef AAALGORITHM
385: #define AAALGORITHM 1 /* default supersampling algorithm */
386: #endif
387: #ifndef AA /* anti-aliasing not explicitly set */
388: #define AA /* so define it ourselves */
389: #endif
390: #ifndef SSFONTS /* need supersampling fonts */
391: #define SSFONTS
392: #endif
393: #else
394: #define ISSUPERSAMPLING 0
395: #ifndef AAALGORITHM
1.3 ! albertel 396: #define AAALGORITHM 3 /*2*/ /* default lowpass algorithm */
1.1 albertel 397: #endif
398: #endif
1.3 ! albertel 399: #ifndef MAXFOLLOW
! 400: #define MAXFOLLOW 8 /* aafollowline() maxturn default */
! 401: #endif
1.1 albertel 402:
403: /* --- set aa (and default gif) if any anti-aliasing options specified --- */
404: #if defined(AA) || defined(GIF) || defined(PNG) \
405: || defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
406: || defined(MINADJACENT) || defined(MAXADJACENT)
407: #if !defined(GIF) && !defined(AA) /* aa not explicitly specified */
408: #define AA /* so define it ourselves */
409: #endif
410: #if !defined(GIF) && !defined(PNG) /* neither gif nor png specified */
411: #define GIF /* so default to gif */
412: #endif
413: #endif
414: /* --- resolve output option inconsistencies --- */
415: #if defined(XBITMAP) /* xbitmap supercedes gif and png */
416: #ifdef AA
417: #undef AA
418: #endif
419: #ifdef GIF
420: #undef GIF
421: #endif
422: #ifdef PNG
423: #undef PNG
424: #endif
425: #endif
426:
427: /* --- decide whether to compile main() --- */
428: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
429: #define DRIVER /* driver will be compiled */
430: /* --- check whether or not to perform http_referer check --- */
431: #ifndef REFERER /* all http_referer's allowed */
432: #define REFERER NULL
433: #endif
434: /* --- max query_string length if no http_referer supplied --- */
435: #ifndef NOREFMAXLEN
436: #define NOREFMAXLEN 9999 /* default to any length query */
437: #endif
438: #else
1.3 ! albertel 439: #ifndef TEXFONTS
! 440: #define NOTEXFONTS /* texfonts not required */
! 441: #endif
1.1 albertel 442: #endif
443:
444: /* --- application headers --- */
445: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
446: #define TEXFONTS /* to include texfonts.h */
447: #endif
448: #include "mimetex.h"
1.2 albertel 449: /* --- info needed when gif image returned in memory buffer --- */
450: #ifdef GIF /* compiling along with gifsave.c */
451: extern int gifSize;
452: extern int maxgifSize;
453: #else /* or just set dummy values */
454: static int gifSize=0, maxgifSize=0;
455: #endif
1.3 ! albertel 456: /* --- gamma correction --- */
! 457: #ifndef GAMMA
! 458: #define GAMMA 1.25 /*1.75*/ /*2.2*/
! 459: #endif
! 460: #ifndef REVERSEGAMMA
! 461: #define REVERSEGAMMA 0.5 /* for \reverse white-on-black */
! 462: #endif
! 463: /* --- opaque background (default to transparent) --- */
! 464: #ifndef OPAQUE
! 465: #define ISTRANSPARENT 1
! 466: #else
! 467: #define ISTRANSPARENT 0
! 468: #endif
! 469: /* --- internal buffer sizes --- */
! 470: #if !defined(MAXEXPRSZ)
! 471: #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/
! 472: #endif
! 473: #if !defined(MAXSUBXSZ)
! 474: #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
! 475: #endif
! 476: #if !defined(MAXTOKNSZ)
! 477: #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
! 478: #endif
! 479: #if !defined(MAXFILESZ)
! 480: #define MAXFILESZ (65536-1) /*max #bytes in input (output) file*/
! 481: #endif
! 482: #if !defined(MAXLINESZ)
! 483: #define MAXLINESZ (4096-1) /* max #chars in line from file */
! 484: #endif
! 485: #if !defined(MAXGIFSZ)
! 486: #define MAXGIFSZ 131072 /* max #bytes in output GIF image */
! 487: #endif
1.1 albertel 488:
489: /* -------------------------------------------------------------------------
490: adjustable default values
491: -------------------------------------------------------------------------- */
492: /* --- anti-aliasing parameters --- */
493: #ifndef CENTERWT
1.2 albertel 494: /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
1.1 albertel 495: /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
1.2 albertel 496: #define CENTERWT 8 /* anti-aliasing centerwt default */
1.1 albertel 497: #endif
498: #ifndef ADJACENTWT
499: /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
1.2 albertel 500: #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
1.1 albertel 501: #endif
502: #ifndef CORNERWT
503: #define CORNERWT 1 /* anti-aliasing cornerwt default*/
504: #endif
505: #ifndef MINADJACENT
506: #define MINADJACENT 6 /*anti-aliasing minadjacent default*/
507: #endif
508: #ifndef MAXADJACENT
509: #define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/
510: #endif
511: /* --- variables for anti-aliasing parameters --- */
512: GLOBAL(int,centerwt,CENTERWT); /*lowpass matrix center pixel wt */
513: GLOBAL(int,adjacentwt,ADJACENTWT); /*lowpass matrix adjacent pixel wt*/
514: GLOBAL(int,cornerwt,CORNERWT); /*lowpass matrix corner pixel wt */
515: GLOBAL(int,minadjacent,MINADJACENT); /* darken if>=adjacent pts black*/
516: GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */
517: GLOBAL(int,weightnum,1); /* font wt, */
518: GLOBAL(int,maxaaparams,4); /* #entries in table */
1.3 ! albertel 519: /* --- anti-aliasing parameter values by font weight --- */
1.1 albertel 520: #define aaparameters struct aaparameters_struct /* typedef */
521: aaparameters
522: { int centerwt; /* lowpass matrix center pixel wt*/
523: int adjacentwt; /* lowpass matrix adjacent pixel wt*/
524: int cornerwt; /* lowpass matrix corner pixel wt*/
525: int minadjacent; /* darken if >= adjacent pts black */
526: int maxadjacent; /* darken if <= adjacent pts black */
527: int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
528: STATIC aaparameters aaparams[] /* set params by weight */
529: #ifdef INITVALS
530: =
531: { /* ----------------------------------------------------
532: centerwt adj corner minadj max fgalias,only,bgalias,only
533: ------------------------------------------------------- */
534: { 64, 1, 1, 6, 8, 1,0,0,0 }, /* 0 = light */
535: { CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
536: { 8, 1, 1, 5, 8, 1,0,0,0 }, /* 2 = semibold */
537: { 8, 2, 1, 4, 9, 1,0,0,0 } /* 3 = bold */
538: } /* --- end-of-aaparams[] --- */
539: #endif
540: ;
1.3 ! albertel 541: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
! 542: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
! 543: ispatternnumcount = 1; /* true to accumulate counts */
1.1 albertel 544:
545: /* -------------------------------------------------------------------------
546: other variables
547: -------------------------------------------------------------------------- */
548: /* --- black on white background (default), or white on black --- */
549: #ifdef WHITE
550: #define ISBLACKONWHITE 0 /* white on black background */
551: #else
552: #define ISBLACKONWHITE 1 /* black on white background */
553: #endif
554: /* --- colors --- */
555: #define BGRED (ISBLACKONWHITE?255:0)
556: #define BGGREEN (ISBLACKONWHITE?255:0)
557: #define BGBLUE (ISBLACKONWHITE?255:0)
558: #ifndef FGRED
559: #define FGRED (ISBLACKONWHITE?0:255)
560: #endif
561: #ifndef FGGREEN
562: #define FGGREEN (ISBLACKONWHITE?0:255)
563: #endif
564: #ifndef FGBLUE
565: #define FGBLUE (ISBLACKONWHITE?0:255)
566: #endif
1.2 albertel 567: /* --- "smash" margin (0 means no smashing) --- */
568: #ifndef SMASHMARGIN
569: #ifdef NOSMASH
570: #define SMASHMARGIN 0
1.1 albertel 571: #else
1.2 albertel 572: #define SMASHMARGIN 3
1.1 albertel 573: #endif
574: #endif
1.3 ! albertel 575: #ifndef SMASHCHECK
! 576: #define SMASHCHECK 0
! 577: #endif
1.1 albertel 578: /* --- textwidth --- */
579: #ifndef TEXTWIDTH
580: #define TEXTWIDTH (400)
581: #endif
582: /* --- font "combinations" --- */
1.2 albertel 583: #define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
1.1 albertel 584: /* --- prefix prepended to all expressions --- */
585: #ifndef PREFIX
586: #define PREFIX "\000" /* default no prepended prefix */
587: #endif
588: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
589: #ifdef NOARGSIGNAL
590: #define ARGSIGNAL NULL
591: #endif
592: #ifndef ARGSIGNAL
593: #define ARGSIGNAL "++"
594: #endif
595: /* --- security and logging (inhibit message logging, etc) --- */
596: #ifndef SECURITY
597: #define SECURITY 999 /* default highest security level */
598: #endif
599: #ifndef LOGFILE
600: #define LOGFILE "mimetex.log" /* default log file */
601: #endif
602: #ifndef CACHELOG
603: #define CACHELOG "mimetex.log" /* default caching log file */
604: #endif
605: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
606: #define DUMPENVP /* assume char *envp[] available */
607: #endif
608: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
609: #ifndef CACHEPATH
610: #define ISCACHING 0 /* no caching */
611: #define CACHEPATH "\000" /* same directory as mimetex.cgi */
612: #else
613: #define ISCACHING 1 /* caching if -DCACHEPATH="path" */
614: #endif
615: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
616: #ifndef PATHPREFIX
617: #define PATHPREFIX "\000" /* paths relative mimetex.cgi */
618: #endif
1.2 albertel 619: /* --- time zone delta t (in hours) --- */
620: #ifndef TZDELTA
621: #define TZDELTA 0
622: #endif
1.3 ! albertel 623: /* --- treat +'s in query string as blanks? --- */
! 624: #ifdef PLUSBLANK /* + always interpreted as blank */
! 625: #define ISPLUSBLANK 1
! 626: #else
! 627: #ifdef PLUSNOTBLANK /* + never interpreted as blank */
! 628: #define ISPLUSBLANK 0
! 629: #else /* program tries to determine */
! 630: #define ISPLUSBLANK (-1)
! 631: #endif
! 632: #endif
1.1 albertel 633:
634: /* -------------------------------------------------------------------------
635: debugging and logging / error reporting
636: -------------------------------------------------------------------------- */
637: /* --- debugging and error reporting --- */
638: #ifndef MSGLEVEL
639: #define MSGLEVEL 1
640: #endif
641: #define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */
642: #define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */
643: #ifndef FORMLEVEL
644: #define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/
645: #endif
646: GLOBAL(int,seclevel,SECURITY); /* security level */
647: GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */
648: STATIC FILE *msgfp; /* output in command-line mode */
649: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
650: #ifdef WARNINGS
651: #define WARNINGLEVEL WARNINGS
652: #else
653: #ifdef NOWARNINGS
654: #define WARNINGLEVEL 0
655: #else
656: #define WARNINGLEVEL 1
657: #endif
658: #endif
659: GLOBAL(int,warninglevel,WARNINGLEVEL); /* warning level */
660:
661: /* -------------------------------------------------------------------------
662: control flags and values
663: -------------------------------------------------------------------------- */
664: GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
665: GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
1.2 albertel 666: GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
1.3 ! albertel 667: GLOBAL(int,isligature,0); /* true if ligature found */
! 668: GLOBAL(char,*subexprptr,(char *)NULL); /* ptr within expression to subexpr*/
1.2 albertel 669: /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
1.1 albertel 670: GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
671: GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
1.3 ! albertel 672: GLOBAL(int,isemitcontenttype,1); /* true to emit mime content-type */
1.2 albertel 673: GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
1.1 albertel 674: GLOBAL(int,fontsize,NORMALSIZE); /* current size */
675: GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
676: GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
677: GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
678: /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
1.3 ! albertel 679: GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/
1.2 albertel 680: GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
1.3 ! albertel 681: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
1.2 albertel 682: GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
1.3 ! albertel 683: GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */
! 684: GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */
! 685: GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/
! 686: GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */
! 687: GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/
! 688: #define BLANKSIGNAL (-991234) /*rastsmash signal right-hand blank*/
! 689: GLOBAL(int,blanksignal,BLANKSIGNAL); /*rastsmash signal right-hand blank*/
! 690: GLOBAL(int,blanksymspace,0); /* extra (or too much) space wanted*/
! 691: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
1.1 albertel 692: GLOBAL(int,fgred,FGRED);
693: GLOBAL(int,fggreen,FGGREEN);
694: GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */
695: GLOBAL(int,bgred,BGRED);
696: GLOBAL(int,bggreen,BGGREEN);
697: GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */
1.3 ! albertel 698: GLOBAL(double,gammacorrection,GAMMA); /* gamma correction */
! 699: GLOBAL(int,isplusblank,ISPLUSBLANK); /*interpret +'s in query as blanks?*/
1.1 albertel 700: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
701: GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */
702: GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */
1.3 ! albertel 703: GLOBAL(int,maxfollow,MAXFOLLOW); /* aafollowline() maxturn parameter*/
1.1 albertel 704: GLOBAL(int,fgalias,1);
705: GLOBAL(int,fgonly,0);
706: GLOBAL(int,bgalias,0);
707: GLOBAL(int,bgonly,0); /* aapnm() params */
708: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
709: GLOBAL(int,isss,ISSUPERSAMPLING); /* supersampling flag for main() */
710: GLOBAL(int,*workingparam,(int *)NULL); /* working parameter */
711: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
712: GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */
713: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
714: GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/
1.3 ! albertel 715: GLOBAL(int,fraccenterline,NOVALUE); /* baseline for punct. after \frac */
! 716: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
1.1 albertel 717: GLOBAL(int,iscaching,ISCACHING); /* true if caching images */
718: GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */
719: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
720: /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */
721:
722: /* -------------------------------------------------------------------------
723: miscellaneous macros
724: -------------------------------------------------------------------------- */
725: #define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */
726: #define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
727: #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
728: #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
729: #define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
730: #define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
731: #define dmod(x,y) ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
732: #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \
733: { char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else
734: #define slower(s) if ((s)!=NULL) /* lowercase all chars in s */ \
735: { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
1.3 ! albertel 736: /*subraster *subrastcpy();*/ /* need global module declaration */
! 737: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \*/
! 738: /* sp->type=blanksignal*/
1.1 albertel 739:
740: /* ---
741: * PART2
742: * ------ */
743: #if !defined(PARTS) || defined(PART2)
744: /* ==========================================================================
745: * Function: new_raster ( width, height, pixsz )
746: * Purpose: Allocation and constructor for raster.
747: * mallocs and initializes memory for width*height pixels,
748: * and returns raster struct ptr to caller.
749: * --------------------------------------------------------------------------
750: * Arguments: width (I) int containing width, in bits,
751: * of raster pixmap to be allocated
752: * height (I) int containing height, in bits/scans,
753: * of raster pixmap to be allocated
754: * pixsz (I) int containing #bits per pixel, 1 or 8
755: * --------------------------------------------------------------------------
756: * Returns: ( raster * ) ptr to allocated and initialized
757: * raster struct, or NULL for any error.
758: * --------------------------------------------------------------------------
759: * Notes:
760: * ======================================================================= */
761: /* --- entry point --- */
762: raster *new_raster ( int width, int height, int pixsz )
763: {
764: /* -------------------------------------------------------------------------
765: Allocations and Declarations
766: -------------------------------------------------------------------------- */
767: raster *rp = (raster *)NULL; /* raster ptr returned to caller */
768: pixbyte *pixmap = NULL; /* raster pixel map to be malloced */
769: int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
770: int filler = (isstring?' ':0); /* pixmap filler */
771: int delete_raster(); /* in case pixmap malloc() fails */
772: int npadding = (0&&issupersampling?8+256:0); /* padding bytes */
773: /* -------------------------------------------------------------------------
774: allocate and initialize raster struct and embedded bitmap
775: -------------------------------------------------------------------------- */
776: if ( msgfp!=NULL && msglevel>=9999 )
777: { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
778: width,height,pixsz); fflush(msgfp); }
779: /* --- allocate and initialize raster struct --- */
780: rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */
781: if ( msgfp!=NULL && msglevel>=9999 )
782: { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
783: sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
784: if ( rp == (raster *)NULL ) /* malloc failed */
785: goto end_of_job; /* return error to caller */
786: rp->width = width; /* store width in raster struct */
787: rp->height = height; /* and store height */
1.2 albertel 788: rp->format = 1; /* initialize as bitmap format */
1.1 albertel 789: rp->pixsz = pixsz; /* store #bits per pixel */
790: rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
791: /* --- allocate and initialize bitmap array --- */
792: if ( msgfp!=NULL && msglevel>=9999 )
793: { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
794: nbytes); fflush(msgfp); }
795: if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/
796: pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
797: if ( msgfp!=NULL && msglevel>=9999 )
798: { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
799: nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
800: if ( pixmap == (pixbyte *)NULL ) /* malloc failed */
801: { delete_raster(rp); /* so free everything */
802: rp = (raster *)NULL; /* reset pointer */
803: goto end_of_job; } /* and return error to caller */
804: memset((void *)pixmap,filler,nbytes); /* init bytes to binary 0's or ' 's*/
805: *pixmap = (pixbyte)0; /* and first byte alwasy 0 */
806: rp->pixmap = pixmap; /* store ptr to malloced memory */
807: /* -------------------------------------------------------------------------
808: Back to caller with address of raster struct, or NULL ptr for any error.
809: -------------------------------------------------------------------------- */
810: end_of_job:
811: if ( msgfp!=NULL && msglevel>=9999 )
812: { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
813: width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
814: return ( rp ); /* back to caller with raster */
815: } /* --- end-of-function new_raster() --- */
816:
817:
818: /* ==========================================================================
819: * Function: new_subraster ( width, height, pixsz )
820: * Purpose: Allocate a new subraster along with
821: * an embedded raster of width x height.
822: * --------------------------------------------------------------------------
823: * Arguments: width (I) int containing width of embedded raster
824: * height (I) int containing height of embedded raster
825: * pixsz (I) int containing #bits per pixel, 1 or 8
826: * --------------------------------------------------------------------------
827: * Returns: ( subraster * ) ptr to newly-allocated subraster,
828: * or NULL for any error.
829: * --------------------------------------------------------------------------
830: * Notes: o if width or height <=0, embedded raster not allocated
831: * ======================================================================= */
832: /* --- entry point --- */
833: subraster *new_subraster ( int width, int height, int pixsz )
834: {
835: /* -------------------------------------------------------------------------
836: Allocations and Declarations
837: -------------------------------------------------------------------------- */
838: subraster *sp=NULL; /* subraster returned to caller */
839: raster *new_raster(), *rp=NULL; /* image raster embedded in sp */
840: int delete_subraster(); /* in case new_raster() fails */
841: int size = NORMALSIZE, /* default size */
842: baseline = height-1; /* and baseline */
843: /* -------------------------------------------------------------------------
844: allocate and initialize subraster struct
845: -------------------------------------------------------------------------- */
846: if ( msgfp!=NULL && msglevel>=9999 )
847: { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
848: width,height,pixsz); fflush(msgfp); }
849: /* --- allocate subraster struct --- */
850: sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */
851: if ( sp == (subraster *)NULL ) /* malloc failed */
852: goto end_of_job; /* return error to caller */
853: /* --- initialize subraster struct --- */
854: sp->type = NOVALUE; /* character or image raster */
855: sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */
856: sp->baseline = baseline; /*0 if image is entirely descending*/
857: sp->size = size; /* font size 0-4 */
858: sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */
859: sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/
860: /* -------------------------------------------------------------------------
861: allocate raster and embed it in subraster, and return to caller
862: -------------------------------------------------------------------------- */
863: /* --- allocate raster struct if desired --- */
864: if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */
865: { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
866: != NULL ) /* if allocate succeeded */
867: sp->image = rp; /* embed raster in subraster */
868: else /* or if allocate failed */
869: { delete_subraster(sp); /* free non-unneeded subraster */
870: sp = NULL; } } /* signal error */
871: /* --- back to caller with new subraster or NULL --- */
872: end_of_job:
873: if ( msgfp!=NULL && msglevel>=9999 )
874: { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
875: width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
876: return ( sp );
877: } /* --- end-of-function new_subraster() --- */
878:
879:
880: /* ==========================================================================
881: * Function: new_chardef ( )
882: * Purpose: Allocates and initializes a chardef struct,
883: * but _not_ the embedded raster struct.
884: * --------------------------------------------------------------------------
885: * Arguments: none
886: * --------------------------------------------------------------------------
887: * Returns: ( chardef * ) ptr to allocated and initialized
888: * chardef struct, or NULL for any error.
889: * --------------------------------------------------------------------------
890: * Notes:
891: * ======================================================================= */
892: /* --- entry point --- */
893: chardef *new_chardef ( )
894: {
895: /* -------------------------------------------------------------------------
896: Allocations and Declarations
897: -------------------------------------------------------------------------- */
898: chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */
899: /* -------------------------------------------------------------------------
900: allocate and initialize chardef struct
901: -------------------------------------------------------------------------- */
902: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
903: if ( cp == (chardef *)NULL ) /* malloc failed */
904: goto end_of_job; /* return error to caller */
905: cp->charnum = cp->location = 0; /* init character description */
906: cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
907: cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
908: cp->image.width = cp->image.height = 0; /* init raster dimensions */
1.2 albertel 909: cp->image.format = 0; /* init raster format */
1.1 albertel 910: cp->image.pixsz = 0; /* and #bits per pixel */
911: cp->image.pixmap = NULL; /* init raster pixmap as null */
912: /* -------------------------------------------------------------------------
913: Back to caller with address of chardef struct, or NULL ptr for any error.
914: -------------------------------------------------------------------------- */
915: end_of_job:
916: return ( cp );
917: } /* --- end-of-function new_chardef() --- */
918:
919:
920: /* ==========================================================================
921: * Function: delete_raster ( rp )
922: * Purpose: Destructor for raster.
923: * Frees memory for raster bitmap and struct.
924: * --------------------------------------------------------------------------
925: * Arguments: rp (I) ptr to raster struct to be deleted.
926: * --------------------------------------------------------------------------
927: * Returns: ( int ) 1 if completed successfully,
928: * or 0 otherwise (for any error).
929: * --------------------------------------------------------------------------
930: * Notes:
931: * ======================================================================= */
932: /* --- entry point --- */
933: int delete_raster ( raster *rp )
934: {
935: /* -------------------------------------------------------------------------
936: free raster bitmap and struct
937: -------------------------------------------------------------------------- */
938: if ( rp != (raster *)NULL ) /* can't free null ptr */
939: {
940: if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */
941: free((void *)rp->pixmap); /* free pixmap within raster */
942: free((void *)rp); /* lastly, free raster struct */
943: } /* --- end-of-if(rp!=NULL) --- */
944: return ( 1 ); /* back to caller, 1=okay 0=failed */
945: } /* --- end-of-function delete_raster() --- */
946:
947:
948: /* ==========================================================================
949: * Function: delete_subraster ( sp )
950: * Purpose: Deallocates a subraster (and embedded raster)
951: * --------------------------------------------------------------------------
952: * Arguments: sp (I) ptr to subraster struct to be deleted.
953: * --------------------------------------------------------------------------
954: * Returns: ( int ) 1 if completed successfully,
955: * or 0 otherwise (for any error).
956: * --------------------------------------------------------------------------
957: * Notes:
958: * ======================================================================= */
959: /* --- entry point --- */
960: int delete_subraster ( subraster *sp )
961: {
962: /* -------------------------------------------------------------------------
963: free subraster struct
964: -------------------------------------------------------------------------- */
965: int delete_raster(); /* to delete embedded raster */
966: if ( sp != (subraster *)NULL ) /* can't free null ptr */
967: {
968: if ( sp->type != CHARASTER ) /* not static character data */
969: if ( sp->image != NULL ) /*raster allocated within subraster*/
970: delete_raster(sp->image); /* so free embedded raster */
971: free((void *)sp); /* and free subraster struct itself*/
972: } /* --- end-of-if(sp!=NULL) --- */
973: return ( 1 ); /* back to caller, 1=okay 0=failed */
974: } /* --- end-of-function delete_subraster() --- */
975:
976:
977: /* ==========================================================================
978: * Function: delete_chardef ( cp )
979: * Purpose: Deallocates a chardef (and bitmap of embedded raster)
980: * --------------------------------------------------------------------------
981: * Arguments: cp (I) ptr to chardef struct to be deleted.
982: * --------------------------------------------------------------------------
983: * Returns: ( int ) 1 if completed successfully,
984: * or 0 otherwise (for any error).
985: * --------------------------------------------------------------------------
986: * Notes:
987: * ======================================================================= */
988: /* --- entry point --- */
989: int delete_chardef ( chardef *cp )
990: {
991: /* -------------------------------------------------------------------------
992: free chardef struct
993: -------------------------------------------------------------------------- */
994: if ( cp != (chardef *)NULL ) /* can't free null ptr */
995: {
996: if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */
997: free((void *)cp->image.pixmap); /* so free embedded pixmap */
998: free((void *)cp); /* and free chardef struct itself */
999: } /* --- end-of-if(cp!=NULL) --- */
1000: /* -------------------------------------------------------------------------
1001: Back to caller with 1=okay, 0=failed.
1002: -------------------------------------------------------------------------- */
1003: return ( 1 );
1004: } /* --- end-of-function delete_chardef() --- */
1005:
1006:
1007: /* ==========================================================================
1008: * Function: rastcpy ( rp )
1009: * Purpose: makes duplicate copy of rp
1010: * --------------------------------------------------------------------------
1011: * Arguments: rp (I) ptr to raster struct to be copied
1012: * --------------------------------------------------------------------------
1013: * Returns: ( raster * ) ptr to new copy rp,
1014: * or NULL for any error.
1015: * --------------------------------------------------------------------------
1016: * Notes: o
1017: * ======================================================================= */
1018: /* --- entry point --- */
1019: raster *rastcpy ( raster *rp )
1020: {
1021: /* -------------------------------------------------------------------------
1022: Allocations and Declarations
1023: -------------------------------------------------------------------------- */
1024: raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/
1025: int height= (rp==NULL?0:rp->height), /* original and copied height */
1026: width = (rp==NULL?0:rp->width), /* original and copied width */
1027: pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
1028: nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
1029: /* -------------------------------------------------------------------------
1030: allocate rotated raster and fill it
1031: -------------------------------------------------------------------------- */
1032: /* --- allocate copied raster with same width,height, and copy bitmap --- */
1033: if ( rp != NULL ) /* nothing to copy if ptr null */
1034: if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
1035: != NULL ) /* check that allocate succeeded */
1036: memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
1037: return ( newrp ); /* return copied raster to caller */
1038: } /* --- end-of-function rastcpy() --- */
1039:
1040:
1041: /* ==========================================================================
1042: * Function: subrastcpy ( sp )
1043: * Purpose: makes duplicate copy of sp
1044: * --------------------------------------------------------------------------
1045: * Arguments: sp (I) ptr to subraster struct to be copied
1046: * --------------------------------------------------------------------------
1047: * Returns: ( subraster * ) ptr to new copy sp,
1048: * or NULL for any error.
1049: * --------------------------------------------------------------------------
1050: * Notes: o
1051: * ======================================================================= */
1052: /* --- entry point --- */
1053: subraster *subrastcpy ( subraster *sp )
1054: {
1055: /* -------------------------------------------------------------------------
1056: Allocations and Declarations
1057: -------------------------------------------------------------------------- */
1058: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
1059: raster *rastcpy(), *newrp=NULL; /* and new raster image within it */
1060: int delete_subraster(); /* dealloc newsp if rastcpy() fails*/
1061: /* -------------------------------------------------------------------------
1062: make copy, and return it to caller
1063: -------------------------------------------------------------------------- */
1064: if ( sp == NULL ) goto end_of_job; /* nothing to copy */
1065: /* --- allocate new subraster "envelope" for copy --- */
1066: if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
1067: == NULL ) goto end_of_job; /* and quit if we fail to allocate */
1068: /* --- transparently copy original envelope to new one --- */
1069: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
1070: /* --- make a copy of the rasterized image itself, if there is one --- */
1071: if ( sp->image != NULL ) /* there's an image embedded in sp */
1072: if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */
1073: == NULL ) /* failed to copy successfully */
1074: { delete_subraster(newsp); /* won't need newsp any more */
1075: newsp = NULL; /* because we're returning error */
1076: goto end_of_job; } /* back to caller with error signal*/
1077: /* --- set new params in new envelope --- */
1078: newsp->image = newrp; /* new raster image we just copied */
1079: switch ( sp->type ) /* set new raster image type */
1080: { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
1081: case ASCIISTRING: newsp->type = ASCIISTRING; break;
1.3 ! albertel 1082: case FRACRASTER: newsp->type = FRACRASTER; break;
! 1083: case BLANKSIGNAL: newsp->type = blanksignal; break;
1.1 albertel 1084: case IMAGERASTER: default: newsp->type = IMAGERASTER; break; }
1085: /* --- return copy of sp to caller --- */
1086: end_of_job:
1087: return ( newsp ); /* copy back to caller */
1088: } /* --- end-of-function subrastcpy() --- */
1089:
1090:
1091: /* ==========================================================================
1092: * Function: rastrot ( rp )
1093: * Purpose: rotates rp image 90 degrees right/clockwise
1094: * --------------------------------------------------------------------------
1095: * Arguments: rp (I) ptr to raster struct to be rotated
1096: * --------------------------------------------------------------------------
1.3 ! albertel 1097: * Returns: ( raster * ) ptr to new raster rotated relative to rp,
1.1 albertel 1098: * or NULL for any error.
1099: * --------------------------------------------------------------------------
1100: * Notes: o An underbrace is } rotated 90 degrees clockwise,
1101: * a hat is <, etc.
1102: * ======================================================================= */
1103: /* --- entry point --- */
1104: raster *rastrot ( raster *rp )
1105: {
1106: /* -------------------------------------------------------------------------
1107: Allocations and Declarations
1108: -------------------------------------------------------------------------- */
1109: raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/
1110: int height = rp->height, irow, /* original height, row index */
1111: width = rp->width, icol, /* original width, column index */
1112: pixsz = rp->pixsz; /* #bits per pixel */
1113: /* -------------------------------------------------------------------------
1114: allocate rotated raster and fill it
1115: -------------------------------------------------------------------------- */
1116: /* --- allocate rotated raster with flipped width<-->height --- */
1117: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
1118: != NULL ) /* check that allocation succeeded */
1119: /* --- fill rotated raster --- */
1120: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
1121: for ( icol=0; icol<width; icol++ ) /* and each column of rp */
1122: { int value = getpixel(rp,irow,icol);
1123: /* setpixel(rotated,icol,irow,value); } */
1124: setpixel(rotated,icol,(height-1-irow),value); }
1125: return ( rotated ); /* return rotated raster to caller */
1126: } /* --- end-of-function rastrot() --- */
1127:
1128:
1129: /* ==========================================================================
1.3 ! albertel 1130: * Function: rastref ( rp, axis )
! 1131: * Purpose: reflects rp, horizontally about y-axis |_ becomes _| if axis=1
! 1132: * or vertically about x-axis M becomes W if axis=2.
! 1133: * --------------------------------------------------------------------------
! 1134: * Arguments: rp (I) ptr to raster struct to be reflected
! 1135: * axis (I) int containing 1 for horizontal reflection,
! 1136: * or 2 for vertical
! 1137: * --------------------------------------------------------------------------
! 1138: * Returns: ( raster * ) ptr to new raster reflected relative to rp,
! 1139: * or NULL for any error.
! 1140: * --------------------------------------------------------------------------
! 1141: * Notes: o
! 1142: * ======================================================================= */
! 1143: /* --- entry point --- */
! 1144: raster *rastref ( raster *rp, int axis )
! 1145: {
! 1146: /* -------------------------------------------------------------------------
! 1147: Allocations and Declarations
! 1148: -------------------------------------------------------------------------- */
! 1149: raster *new_raster(), *reflected=NULL; /* reflected raster back to caller */
! 1150: int height = rp->height, irow, /* height, row index */
! 1151: width = rp->width, icol, /* width, column index */
! 1152: pixsz = rp->pixsz; /* #bits per pixel */
! 1153: /* -------------------------------------------------------------------------
! 1154: allocate reflected raster and fill it
! 1155: -------------------------------------------------------------------------- */
! 1156: /* --- allocate reflected raster with same width, height --- */
! 1157: if ( axis==1 || axis==2 ) /* first validate axis arg */
! 1158: if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
! 1159: != NULL ) /* check that allocation succeeded */
! 1160: /* --- fill reflected raster --- */
! 1161: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
! 1162: for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
! 1163: int value = getpixel(rp,irow,icol);
! 1164: if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
! 1165: if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
! 1166: return ( reflected ); /*return reflected raster to caller*/
! 1167: } /* --- end-of-function rastref() --- */
! 1168:
! 1169:
! 1170: /* ==========================================================================
1.1 albertel 1171: * Function: rastput ( target, source, top, left, isopaque )
1172: * Purpose: Overlays source onto target,
1173: * with the 0,0-bit of source onto the top,left-bit of target.
1174: * --------------------------------------------------------------------------
1175: * Arguments: target (I) ptr to target raster struct
1176: * source (I) ptr to source raster struct
1177: * top (I) int containing 0 ... target->height - 1
1178: * left (I) int containing 0 ... target->width - 1
1179: * isopaque (I) int containing false (zero) to allow
1180: * original 1-bits of target to "show through"
1181: * 0-bits of source.
1182: * --------------------------------------------------------------------------
1183: * Returns: ( int ) 1 if completed successfully,
1184: * or 0 otherwise (for any error).
1185: * --------------------------------------------------------------------------
1186: * Notes:
1187: * ======================================================================= */
1188: /* --- entry point --- */
1189: int rastput ( raster *target, raster *source,
1190: int top, int left, int isopaque )
1191: {
1192: /* -------------------------------------------------------------------------
1193: Allocations and Declarations
1194: -------------------------------------------------------------------------- */
1195: int irow, icol, /* indexes over source raster */
1196: twidth=target->width, theight=target->height, /*target width,height*/
1197: tpix, ntpix = twidth*theight; /* #pixels in target */
1198: int isfatal = 0, /* true to abend on out-of-bounds error */
1199: isstrict = 0/*1*/, /* true for strict bounds check - no "wrap"*/
1200: isokay = 1; /* true if no pixels out-of-bounds */
1201: /* -------------------------------------------------------------------------
1202: superimpose source onto target, one bit at a time
1203: -------------------------------------------------------------------------- */
1204: if ( isstrict && (top<0 || left<0) ) /* args fail strict test */
1205: isokay = 0; /* so just return error */
1206: else
1207: for ( irow=0; irow<source->height; irow++ ) /* for each scan line */
1208: {
1209: tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/
1210: for ( icol=0; icol<source->width; icol++ ) /* each pixel in scan line */
1211: {
1212: int svalue = getpixel(source,irow,icol); /* source pixel value */
1213: ++tpix; /* bump target pixel */
1214: if ( msgfp!=NULL && msglevel>=9999 ) /* debugging output */
1215: { fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
1216: "left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
1217: left,icol,twidth); fflush(msgfp); }
1218: if ( tpix >= ntpix /* bounds check failed */
1219: || (isstrict && (irow+top>=theight || icol+left>=twidth)) )
1220: { isokay = 0; /* reset okay flag */
1221: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
1222: else break; } /*or just go on to next row*/
1223: if ( tpix >= 0 ) /* bounds check okay */
1.3 ! albertel 1224: if ( svalue!=0 || isopaque ) { /*got dark or opaque source*/
! 1225: setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
1.1 albertel 1226: } /* --- end-of-for(icol) --- */
1227: } /* --- end-of-for(irow) --- */
1228: /* -------------------------------------------------------------------------
1229: Back to caller with 1=okay, 0=failed.
1230: -------------------------------------------------------------------------- */
1231: end_of_job:
1232: return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
1233: } /* --- end-of-function rastput() --- */
1234:
1235:
1236: /* ==========================================================================
1237: * Function: rastcompose ( sp1, sp2, offset2, isalign, isfree )
1238: * Purpose: Overlays sp2 on top of sp1, leaving both unchanged
1239: * and returning a newly-allocated composite subraster.
1240: * Frees/deletes input sp1 and/or sp2 depending on value
1241: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1242: * --------------------------------------------------------------------------
1243: * Arguments: sp1 (I) subraster * to "underneath" subraster,
1244: * whose baseline is preserved
1245: * sp2 (I) subraster * to "overlaid" subraster
1246: * offset2 (I) int containing 0 or number of pixels
1247: * to horizontally shift sp2 relative to sp1,
1248: * either positive (right) or negative
1249: * isalign (I) int containing 1 to align baselines,
1250: * or 0 to vertically center sp2 over sp1
1251: * isfree (I) int containing 1=free sp1 before return,
1252: * 2=free sp2, 3=free both, 0=free none.
1253: * --------------------------------------------------------------------------
1254: * Returns: ( subraster * ) pointer to constructed subraster
1255: * or NULL for any error
1256: * --------------------------------------------------------------------------
1257: * Notes:
1258: * ======================================================================= */
1259: /* --- entry point --- */
1260: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
1261: int isalign, int isfree )
1262: {
1263: /* -------------------------------------------------------------------------
1264: Allocations and Declarations
1265: -------------------------------------------------------------------------- */
1266: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1267: raster *rp=(raster *)NULL; /* new composite raster in sp */
1268: int delete_subraster(); /* in case isfree non-zero */
1269: int rastput(); /*place sp1,sp2 in composite raster*/
1270: int base1 = sp1->baseline, /*baseline for underlying subraster*/
1271: height1 = (sp1->image)->height, /* height for underlying subraster */
1272: width1 = (sp1->image)->width, /* width for underlying subraster */
1273: pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */
1274: base2 = sp2->baseline, /*baseline for overlaid subraster */
1275: height2 = (sp2->image)->height, /* height for overlaid subraster */
1276: width2 = (sp2->image)->width, /* width for overlaid subraster */
1277: pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
1278: int height=0, width=0, pixsz=0, base=0; /* overlaid composite */
1279: /* -------------------------------------------------------------------------
1280: Initialization
1281: -------------------------------------------------------------------------- */
1282: /* --- determine height, width and baseline of composite raster --- */
1283: if ( isalign ) /* baselines of sp1,sp2 aligned */
1284: { height = max2(base1+1,base2+1) /* max height above baseline */
1285: + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1286: base = max2(base1,base2); } /* max space above baseline */
1287: else /* baselines not aligned */
1288: { height = max2(height1,height2); /* max height */
1289: base = base1 + (height-height1)/2; } /* baseline for sp1 */
1290: width = max2(width1,width2+abs(offset2)); /* max width */
1291: pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
1292: /* -------------------------------------------------------------------------
1293: allocate concatted composite subraster
1294: -------------------------------------------------------------------------- */
1295: /* --- allocate returned subraster (and then initialize it) --- */
1296: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1297: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1298: /* --- initialize subraster parameters --- */
1299: sp->type = IMAGERASTER; /* image */
1300: sp->baseline = base; /* composite baseline */
1301: sp->size = sp1->size; /* underlying char is sp1 */
1302: /* --- extract raster from subraster --- */
1303: rp = sp->image; /* raster allocated in subraster */
1304: /* -------------------------------------------------------------------------
1305: overlay sp1 and sp2 in new composite raster
1306: -------------------------------------------------------------------------- */
1307: if ( isalign )
1308: { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
1309: rastput (rp, sp2->image, base-base2, /*overlaid*/
1310: (width-width2)/2+offset2, 0); }
1311: else
1312: { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
1313: rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
1314: (width-width2)/2+offset2, 0); }
1315: /* -------------------------------------------------------------------------
1316: free input if requested
1317: -------------------------------------------------------------------------- */
1318: if ( isfree > 0 ) /* caller wants input freed */
1319: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1320: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
1321: /* -------------------------------------------------------------------------
1322: Back to caller with pointer to concatted subraster or with null for error
1323: -------------------------------------------------------------------------- */
1324: end_of_job:
1325: return ( sp ); /* back with subraster or null ptr */
1326: } /* --- end-of-function rastcompose() --- */
1327:
1328:
1329: /* ==========================================================================
1330: * Function: rastcat ( sp1, sp2, isfree )
1331: * Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged
1332: * and returning a newly-allocated subraster.
1333: * Frees/deletes input sp1 and/or sp2 depending on value
1334: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1335: * --------------------------------------------------------------------------
1336: * Arguments: sp1 (I) subraster * to left-hand subraster
1337: * sp2 (I) subraster * to right-hand subraster
1338: * isfree (I) int containing 1=free sp1 before return,
1339: * 2=free sp2, 3=free both, 0=free none.
1340: * --------------------------------------------------------------------------
1341: * Returns: ( subraster * ) pointer to constructed subraster sp1||sp2
1342: * or NULL for any error
1343: * --------------------------------------------------------------------------
1344: * Notes:
1345: * ======================================================================= */
1346: /* --- entry point --- */
1347: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
1348: {
1349: /* -------------------------------------------------------------------------
1350: Allocations and Declarations
1351: -------------------------------------------------------------------------- */
1352: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1353: raster *rp=(raster *)NULL; /* new concatted raster */
1354: int delete_subraster(); /* in case isfree non-zero */
1355: int rastput(); /*place sp1,sp2 in concatted raster*/
1356: int type_raster(); /* debugging display */
1357: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
1358: height1 = (sp1->image)->height, /* height for left-hand subraster */
1359: width1 = (sp1->image)->width, /* width for left-hand subraster */
1360: pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */
1361: type1 = sp1->type, /* image type for left-hand */
1362: base2 = sp2->baseline, /*baseline for right-hand subraster*/
1363: height2 = (sp2->image)->height, /* height for right-hand subraster */
1364: width2 = (sp2->image)->width, /* width for right-hand subraster */
1365: pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
1366: type2 = sp2->type; /* image type for right-hand */
1367: int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
1.2 albertel 1368: int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
1369: isopaque = (issmash?0:1), /* not oppaque if smashing */
1370: rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
1.3 ! albertel 1371: oldsmashmargin = smashmargin, /* save original smashmargin */
! 1372: oldblanksymspace = blanksymspace, /* save original blanksymspace */
! 1373: oldnocatspace = isnocatspace; /* save original isnocatspace */
1.1 albertel 1374: mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
1375: *symdef2 = sp2->symdef; /* mathchardef of right-hand char */
1376: int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
1377: class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
1378: smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
1379: class1==OPENING||class1==CLOSING||class1==PUNCTION),
1380: smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
1381: class2==OPENING||class2==CLOSING||class2==PUNCTION),
1382: space = fontsize/2+1; /* #cols between sp1 and sp2 */
1.3 ! albertel 1383: int isfrac = (type1 == FRACRASTER /* sp1 is a \frac */
! 1384: && class2 == PUNCTION); /* and sp2 is punctuation */
1.1 albertel 1385: /* -------------------------------------------------------------------------
1386: Initialization
1387: -------------------------------------------------------------------------- */
1388: /* --- determine inter-character space from character class --- */
1389: if ( !isstring )
1390: space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
1391: else space = 1; /* space for ascii string */
1.3 ! albertel 1392: if ( isnocatspace > 0 ) { /* spacing explicitly turned off */
! 1393: space = 0; /* reset space */
! 1394: isnocatspace--; } /* and decrement isnocatspace flag */
! 1395: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
! 1396: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
! 1397: space = 0; /* no extra space between spaces */
! 1398: if ( sp2->type != BLANKSIGNAL ) /* not a blank space signal */
! 1399: if ( blanksymspace != 0 ) { /* and we have a space adjustment */
! 1400: space = max2(0,space+blanksymspace); /* adjust as much as possible */
! 1401: blanksymspace = 0; } /* and reset adjustment */
! 1402: if ( msgfp!=NULL && msglevel>=999 ) /* display space results */
! 1403: { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
! 1404: space,oldblanksymspace,oldnocatspace); fflush(msgfp); }
1.2 albertel 1405: /* --- determine smash --- */
1.3 ! albertel 1406: if ( !isstring && !isfrac ) /* don't smash strings or \frac's */
1.2 albertel 1407: if ( issmash ) { /* raster smash wanted */
1408: int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
1409: margin = smashmargin; /* init margin without delta */
1.1 albertel 1410: if ( (1 && smash1 && smash2) /* concatanating two chars */
1.3 ! albertel 1411: || (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
! 1412: && type1!=FRACRASTER && type2!=FRACRASTER ) )
1.2 albertel 1413: /*maxsmash = 0;*/ /* turn off smash */
1414: margin = max2(space-1,0); /* force small smashmargin */
1.1 albertel 1415: else /* adjust for delta if images */
1.2 albertel 1416: if ( issmashdelta ) /* smashmargin is a delta value */
1.1 albertel 1417: margin += fontsize; /* add displaystyle base to margin */
1.2 albertel 1418: if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
1.1 albertel 1419: isblank = 1; /* set blank flag signal */
1420: else /* see how much extra space we have*/
1.2 albertel 1421: if ( maxsmash > margin ) /* enough space for adjustment */
1422: nsmash = maxsmash-margin; /* make adjustment */
1423: if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
1424: { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
1425: maxsmash,margin,nsmash);
1.1 albertel 1426: fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
1427: (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
1428: fflush(msgfp); }
1.2 albertel 1429: } /* --- end-of-if(issmash) --- */
1.1 albertel 1430: /* --- determine height, width and baseline of composite raster --- */
1431: if ( !isstring )
1432: { height = max2(base1+1,base2+1) /* max height above baseline */
1433: + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1.2 albertel 1434: width = width1+width2 + space-nsmash; /*add widths and space-smash*/
1435: width = max3(width,width1,width2); } /* don't "over-smash" composite */
1.1 albertel 1436: else /* ascii string */
1437: { height = 1; /* default */
1438: width = width1 + width2 + space - 1; } /* no need for two nulls */
1439: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
1440: base = max2(base1,base2); /* max space above baseline */
1441: if ( msgfp!=NULL && msglevel>=9999 ) /* display components */
1442: { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1443: height1,width1,pixsz1,base1);
1444: type_raster(sp1->image,msgfp); /* display left-hand raster */
1445: fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1446: height2,width2,pixsz2,base2);
1447: type_raster(sp2->image,msgfp); /* display right-hand raster */
1448: fprintf(msgfp,
1.2 albertel 1449: "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
1450: height,width,nsmash,pixsz,base);
1.1 albertel 1451: fflush(msgfp); } /* flush msgfp buffer */
1452: /* -------------------------------------------------------------------------
1453: allocate concatted composite subraster
1454: -------------------------------------------------------------------------- */
1455: /* --- allocate returned subraster (and then initialize it) --- */
1456: if ( msgfp!=NULL && msglevel>=9999 )
1457: { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
1458: width,height,pixsz); fflush(msgfp); }
1459: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1460: == (subraster *)NULL ) /* failed */
1461: { if ( msgfp!=NULL && msglevel>=1 ) /* report failure */
1462: { fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
1463: width,height,pixsz); fflush(msgfp); }
1464: goto end_of_job; } /* failed, so quit */
1465: /* --- initialize subraster parameters --- */
1466: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/
1467: if ( !isstring )
1.2 albertel 1468: sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
1.3 ! albertel 1469: (type2!=CHARASTER? type2 :
! 1470: (type1!=CHARASTER&&type1!=BLANKSIGNAL
! 1471: &&type1!=FRACRASTER?type1:IMAGERASTER));
1.1 albertel 1472: else
1473: sp->type = ASCIISTRING; /* concatted ascii string */
1474: sp->symdef = symdef2; /* rightmost char is sp2 */
1475: sp->baseline = base; /* composite baseline */
1476: sp->size = sp2->size; /* rightmost char is sp2 */
1477: if ( isblank ) /* need to propagate blanksignal */
1478: sp->type = blanksignal; /* may not be completely safe??? */
1479: /* --- extract raster from subraster --- */
1480: rp = sp->image; /* raster allocated in subraster */
1481: /* -------------------------------------------------------------------------
1482: overlay sp1 and sp2 in new composite raster
1483: -------------------------------------------------------------------------- */
1484: if ( msgfp!=NULL && msglevel>=9999 )
1485: { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
1486: fflush(msgfp); } /* flush msgfp buffer */
1487: if ( !isstring )
1488: rastput (rp, sp1->image, base-base1, /* overlay left-hand */
1.2 albertel 1489: max2(0,nsmash-width1), 1); /* plus any residual smash space */
1.1 albertel 1490: else
1491: memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1); /*init left string*/
1492: if ( msgfp!=NULL && msglevel>=9999 )
1493: { type_raster(sp->image,msgfp); /* display composite raster */
1494: fflush(msgfp); } /* flush msgfp buffer */
1495: if ( !isstring )
1.3 ! albertel 1496: { int fracbase = ( isfrac? /* baseline for punc after \frac */
! 1497: max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
! 1498: rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
! 1499: max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
! 1500: if ( 1 && type1 == FRACRASTER /* we're done with \frac image */
! 1501: && type2 != FRACRASTER ) /* unless we have \frac\frac */
! 1502: fraccenterline = NOVALUE; /* so reset centerline signal */
! 1503: if ( fraccenterline != NOVALUE ) /* sp2 is a fraction */
! 1504: fraccenterline += (base-base2); } /* so adjust its centerline */
1.1 albertel 1505: else
1506: { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
1507: ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
1508: if ( msgfp!=NULL && msglevel>=9999 )
1509: { type_raster(sp->image,msgfp); /* display composite raster */
1510: fflush(msgfp); } /* flush msgfp buffer */
1511: /* -------------------------------------------------------------------------
1512: free input if requested
1513: -------------------------------------------------------------------------- */
1514: if ( isfree > 0 ) /* caller wants input freed */
1515: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1516: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
1517: /* -------------------------------------------------------------------------
1518: Back to caller with pointer to concatted subraster or with null for error
1519: -------------------------------------------------------------------------- */
1520: end_of_job:
1.2 albertel 1521: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.1 albertel 1522: return ( sp ); /* back with subraster or null ptr */
1523: } /* --- end-of-function rastcat() --- */
1524:
1525:
1526: /* ==========================================================================
1527: * Function: rastack ( sp1, sp2, base, space, iscenter, isfree )
1528: * Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged
1529: * and returning a newly-allocated subraster,
1530: * whose baseline is sp1's if base=1, or sp2's if base=2.
1531: * Frees/deletes input sp1 and/or sp2 depending on value
1532: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1533: * --------------------------------------------------------------------------
1534: * Arguments: sp1 (I) subraster * to lower subraster
1535: * sp2 (I) subraster * to upper subraster
1536: * base (I) int containing 1 if sp1 is baseline,
1537: * or 2 if sp2 is baseline.
1538: * space (I) int containing #rows blank space inserted
1539: * between sp1's image and sp2's image.
1540: * iscenter (I) int containing 1 to center both sp1 and sp2
1541: * in stacked array, 0 to left-justify both
1542: * isfree (I) int containing 1=free sp1 before return,
1543: * 2=free sp2, 3=free both, 0=free none.
1544: * --------------------------------------------------------------------------
1545: * Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1
1546: * or NULL for any error
1547: * --------------------------------------------------------------------------
1548: * Notes:
1549: * ======================================================================= */
1550: /* --- entry point --- */
1551: subraster *rastack ( subraster *sp1, subraster *sp2,
1552: int base, int space, int iscenter, int isfree )
1553: {
1554: /* -------------------------------------------------------------------------
1555: Allocations and Declarations
1556: -------------------------------------------------------------------------- */
1557: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1558: raster *rp=(raster *)NULL; /* new stacked raster in sp */
1559: int delete_subraster(); /* in case isfree non-zero */
1560: int rastput(); /* place sp1,sp2 in stacked raster */
1561: int base1 = sp1->baseline, /* baseline for lower subraster */
1562: height1 = (sp1->image)->height, /* height for lower subraster */
1563: width1 = (sp1->image)->width, /* width for lower subraster */
1564: pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */
1565: base2 = sp2->baseline, /* baseline for upper subraster */
1566: height2 = (sp2->image)->height, /* height for upper subraster */
1567: width2 = (sp2->image)->width, /* width for upper subraster */
1568: pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */
1569: int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
1570: mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */
1571: *symdef2 = sp2->symdef; /* mathchardef of right upper char */
1572: /* -------------------------------------------------------------------------
1573: Initialization
1574: -------------------------------------------------------------------------- */
1575: /* --- determine height, width and baseline of composite raster --- */
1576: height = height1 + space + height2; /* sum of heights plus space */
1577: width = max2(width1,width2); /* max width is overall width */
1578: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
1579: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
1580: /* -------------------------------------------------------------------------
1581: allocate stacked composite subraster (with embedded raster)
1582: -------------------------------------------------------------------------- */
1583: /* --- allocate returned subraster (and then initialize it) --- */
1584: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1585: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1586: /* --- initialize subraster parameters --- */
1587: sp->type = IMAGERASTER; /* stacked rasters */
1588: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
1589: sp->baseline = baseline; /* composite baseline */
1590: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
1591: /* --- extract raster from subraster --- */
1592: rp = sp->image; /* raster embedded in subraster */
1593: /* -------------------------------------------------------------------------
1594: overlay sp1 and sp2 in new composite raster
1595: -------------------------------------------------------------------------- */
1596: if ( iscenter == 1 ) /* center both sp1 and sp2 */
1597: { rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */
1598: rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
1599: else /* left-justify both sp1 and sp2 */
1600: { rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */
1601: rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
1602: /* -------------------------------------------------------------------------
1603: free input if requested
1604: -------------------------------------------------------------------------- */
1605: if ( isfree > 0 ) /* caller wants input freed */
1606: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1607: if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
1608: /* -------------------------------------------------------------------------
1609: Back to caller with pointer to stacked subraster or with null for error
1610: -------------------------------------------------------------------------- */
1611: end_of_job:
1612: return ( sp ); /* back with subraster or null ptr */
1613: } /* --- end-of-function rastack() --- */
1614:
1615:
1616: /* ==========================================================================
1617: * Function: rastile ( tiles, ntiles )
1618: * Purpose: Allocate and build up a composite raster
1619: * from the ntiles components/characters supplied in tiles.
1620: * --------------------------------------------------------------------------
1621: * Arguments: tiles (I) subraster * to array of subraster structs
1622: * describing the components and their locations
1623: * ntiles (I) int containing number of subrasters in tiles[]
1624: * --------------------------------------------------------------------------
1625: * Returns: ( raster * ) ptr to composite raster,
1626: * or NULL for any error.
1627: * --------------------------------------------------------------------------
1628: * Notes: o The top,left corner of a raster is row=0,col=0
1629: * with row# increasing as you move down,
1630: * and col# increasing as you move right.
1631: * Metafont numbers rows with the baseline=0,
1632: * so the top row is a positive number that
1633: * decreases as you move down.
1634: * o rastile() is no longer used.
1635: * It was used by an earlier rasterize() algorithm,
1636: * and I've left it in place should it be needed again.
1637: * But recent changes haven't been tested/exercised.
1638: * ======================================================================= */
1639: /* --- entry point --- */
1640: raster *rastile ( subraster *tiles, int ntiles )
1641: {
1642: /* -------------------------------------------------------------------------
1643: Allocations and Declarations
1644: -------------------------------------------------------------------------- */
1645: raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/
1646: int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
1647: toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
1648: botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */
1649: int itile; /* tiles[] index */
1650: int rastput(); /* overlay each tile in composite raster */
1651: /* -------------------------------------------------------------------------
1652: run through tiles[] to determine dimensions for composite raster
1653: -------------------------------------------------------------------------- */
1654: /* --- determine row and column bounds of composite raster --- */
1655: for ( itile=0; itile<ntiles; itile++ )
1656: {
1657: subraster *tile = &(tiles[itile]); /* ptr to current tile */
1658: /* --- upper-left corner of composite --- */
1659: toprow = min2(toprow, tile->toprow);
1660: leftcol = min2(leftcol, tile->leftcol);
1661: /* --- lower-right corner of composite --- */
1662: botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
1663: rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1);
1664: /* --- pixsz of composite --- */
1665: pixsz = max2(pixsz,(tile->image)->pixsz);
1666: } /* --- end-of-for(itile) --- */
1667: /* --- calculate width and height from bounds --- */
1668: width = rightcol - leftcol + 1;
1669: height = botrow - toprow + 1;
1670: /* --- sanity check (quit if bad dimensions) --- */
1671: if ( width<1 || height<1 ) goto end_of_job;
1672: /* -------------------------------------------------------------------------
1673: allocate composite raster, and embed tiles[] within it
1674: -------------------------------------------------------------------------- */
1675: /* --- allocate composite raster --- */
1676: if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
1677: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
1678: /* --- embed tiles[] in composite --- */
1679: for ( itile=0; itile<ntiles; itile++ )
1680: { subraster *tile = &(tiles[itile]); /* ptr to current tile */
1681: rastput (composite, tile->image, /* overlay tile image at...*/
1682: tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
1683: /* -------------------------------------------------------------------------
1684: Back to caller with composite raster (or null for any error)
1685: -------------------------------------------------------------------------- */
1686: end_of_job:
1687: return ( composite ); /* back with composite or null ptr */
1688: } /* --- end-of-function rastile() --- */
1689:
1690:
1691: /* ==========================================================================
1.2 albertel 1692: * Function: rastsmash ( sp1, sp2 )
1.1 albertel 1693: * Purpose: When concatanating sp1||sp2, calculate #pixels
1.2 albertel 1694: * we can "smash sp2 left"
1.1 albertel 1695: * --------------------------------------------------------------------------
1696: * Arguments: sp1 (I) subraster * to left-hand raster
1697: * sp2 (I) subraster * to right-hand raster
1698: * --------------------------------------------------------------------------
1.2 albertel 1699: * Returns: ( int ) max #pixels we can smash sp1||sp2,
1.1 albertel 1700: * or "blanksignal" if sp2 intentionally blank,
1701: * or 0 for any error.
1702: * --------------------------------------------------------------------------
1703: * Notes: o
1704: * ======================================================================= */
1705: /* --- entry point --- */
1.2 albertel 1706: int rastsmash ( subraster *sp1, subraster *sp2 )
1.1 albertel 1707: {
1708: /* -------------------------------------------------------------------------
1709: Allocations and Declarations
1710: -------------------------------------------------------------------------- */
1.2 albertel 1711: int nsmash = 0; /* #pixels to smash sp1||sp2 */
1.1 albertel 1712: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
1713: height1 = (sp1->image)->height, /* height for left-hand subraster */
1714: width1 = (sp1->image)->width, /* width for left-hand subraster */
1715: base2 = sp2->baseline, /*baseline for right-hand subraster*/
1716: height2 = (sp2->image)->height, /* height for right-hand subraster */
1717: width2 = (sp2->image)->width; /* width for right-hand subraster */
1718: int base = max2(base1,base2), /* max ascenders - 1 above baseline*/
1719: top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
1720: bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
1721: height = max2(bot1,bot2)+1; /* total height */
1722: int irow1=0,irow2=0, icol=0; /* row,col indexes */
1723: int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
1724: firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
1725: int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
1726: int type_raster(); /* display debugging output */
1727: /* -------------------------------------------------------------------------
1728: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
1729: -------------------------------------------------------------------------- */
1730: /* --- check args --- */
1731: if ( isstring ) goto end_of_job; /* ignore string rasters */
1.2 albertel 1732: if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
1733: if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
1.1 albertel 1734: if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 albertel 1735: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 1736: /* --- init firstcol1[], firstcol2[] --- */
1737: for ( irow1=0; irow1<height; irow1++ ) /* for each row */
1738: firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
1739: /* --- set firstcol2[] indicating left edge of sp2 --- */
1740: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
1741: for ( icol=0; icol<width2; icol++ ) /* find first non-empty col in row */
1742: if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
1743: { firstcol2[irow2] = icol; /* icol is #cols from left edge */
1744: nfirst2++; /* bump #rows containing set pixels*/
1745: break; } /* and go on to next row */
1746: if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
1.2 albertel 1747: { nsmash = blanksignal; /* signal intentional blanks */
1748: goto end_of_job; } /* don't smash intentional blanks */
1.1 albertel 1749: /* --- now check if preceding image in sp1 was an intentional blank --- */
1750: if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 albertel 1751: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 1752: /* --- set firstcol1[] indicating right edge of sp1 --- */
1753: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
1754: for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
1755: if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
1756: { firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
1757: nfirst1++; /* bump #rows containing set pixels*/
1758: break; } /* and go on to next row */
1759: if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
1.2 albertel 1760: goto end_of_job; /* don't smash intentional blanks */
1.1 albertel 1761: /* -------------------------------------------------------------------------
1762: find minimum separation
1763: -------------------------------------------------------------------------- */
1764: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
1765: int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
1.3 ! albertel 1766: if ( margin2 != blanksignal ) { /* irow2 not an empty/blank row */
1.1 albertel 1767: for ( irow1=max2(irow2-smin,top1); ; irow1++ )
1768: if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
1769: else
1770: if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
1771: int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
1772: if ( ds >= smin ) continue; /* min unchanged */
1.2 albertel 1773: if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
1.1 albertel 1774: smin=ds; xmin=dx; ymin=dy; /* set new min */
1775: } /* --- end-of-if(margin1!=blanksignal) --- */
1.3 ! albertel 1776: } /* --- end-of-if(margin2!=blanksignal) --- */
1.2 albertel 1777: if ( smin<2 ) goto end_of_job; /* can't smash */
1.1 albertel 1778: } /* --- end-of-for(irow2) --- */
1.2 albertel 1779: /*nsmash = min2(xmin,width2);*/ /* permissible smash */
1780: nsmash = xmin; /* permissible smash */
1.1 albertel 1781: /* -------------------------------------------------------------------------
1.2 albertel 1782: Back to caller with #pixels to smash sp1||sp2
1.1 albertel 1783: -------------------------------------------------------------------------- */
1784: end_of_job:
1785: /* --- debugging output --- */
1786: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
1.2 albertel 1787: { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
1788: nsmash,smashmargin);
1.1 albertel 1789: if ( msglevel >= 999 ) /* also display rasters */
1.2 albertel 1790: { fprintf(msgfp,"rastsmash>left-hand image...\n");
1.1 albertel 1791: if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
1.2 albertel 1792: fprintf(msgfp,"rastsmash>right-hand image...\n");
1.1 albertel 1793: if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
1794: fflush(msgfp); }
1.2 albertel 1795: return ( nsmash ); /* back with #smash pixels */
1796: } /* --- end-of-function rastsmash() --- */
1.1 albertel 1797:
1798:
1799: /* ==========================================================================
1.3 ! albertel 1800: * Function: rastsmashcheck ( term )
! 1801: * Purpose: Check an exponent term to see if its leading symbol
! 1802: * would make smashing dangerous
! 1803: * --------------------------------------------------------------------------
! 1804: * Arguments: term (I) char * to null-terminated string
! 1805: * containing right-hand exponent term about to
! 1806: * be smashed against existing left-hand.
! 1807: * --------------------------------------------------------------------------
! 1808: * Returns: ( int ) 1 if it's okay to smash term, or
! 1809: * 0 if smash is dangerous.
! 1810: * --------------------------------------------------------------------------
! 1811: * Notes: o
! 1812: * ======================================================================= */
! 1813: /* --- entry point --- */
! 1814: int rastsmashcheck ( char *term )
! 1815: {
! 1816: /* -------------------------------------------------------------------------
! 1817: Allocations and Declarations
! 1818: -------------------------------------------------------------------------- */
! 1819: int isokay = 0; /* 1 to signal okay to caller */
! 1820: static char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
! 1821: static char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
! 1822: static char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
! 1823: "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
! 1824: char *expression = term; /* local ptr to beginning of expression */
! 1825: char *token = NULL; int i; /* token = nosmashstrs[i] or grayspace[i] */
! 1826: /* -------------------------------------------------------------------------
! 1827: see if smash check enabled
! 1828: -------------------------------------------------------------------------- */
! 1829: if ( smashcheck < 1 ) { /* no smash checking wanted */
! 1830: if ( smashcheck >= 0 ) /* -1 means check should always fail */
! 1831: isokay = 1; /* otherwise (if 0), signal okay to smash */
! 1832: goto end_of_job; } /* return to caller */
! 1833: /* -------------------------------------------------------------------------
! 1834: skip leading white and gray space
! 1835: -------------------------------------------------------------------------- */
! 1836: /* --- first check input --- */
! 1837: if ( term == NULL ) goto end_of_job; /* no input so return 0 to caller */
! 1838: if ( *term == '\000' ) goto end_of_job; /* ditto for empty string */
! 1839: /* --- skip leading white space --- */
! 1840: skipwhite(term); /* skip leading white sapce */
! 1841: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
! 1842: /* --- skip leading gray space --- */
! 1843: skipgray:
! 1844: for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
! 1845: if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
! 1846: term += strlen(token); /* skip past this grayspace token */
! 1847: if ( *term == '\000' ) { /* nothing left so quit */
! 1848: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
! 1849: fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
! 1850: goto end_of_job; }
! 1851: goto skipgray; } /* restart grayspace check from beginning */
! 1852: /* -------------------------------------------------------------------------
! 1853: check for leading no-smash single char
! 1854: -------------------------------------------------------------------------- */
! 1855: /* --- don't smash if term begins with a "nosmash" char --- */
! 1856: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
! 1857: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
! 1858: fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
! 1859: goto end_of_job; }
! 1860: /* -------------------------------------------------------------------------
! 1861: check for leading no-smash token
! 1862: -------------------------------------------------------------------------- */
! 1863: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
! 1864: if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
! 1865: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
! 1866: fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
! 1867: goto end_of_job; } /* so don't smash term */
! 1868: /* -------------------------------------------------------------------------
! 1869: back to caller
! 1870: -------------------------------------------------------------------------- */
! 1871: isokay = 1; /* no problem, so signal okay to smash */
! 1872: end_of_job:
! 1873: if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */
! 1874: fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
! 1875: isokay,(expression==NULL?"<no input>":expression));
! 1876: return ( isokay ); /* back to caller with 1 if okay to smash */
! 1877: } /* --- end-of-function rastsmashcheck() --- */
! 1878:
! 1879:
! 1880: /* ==========================================================================
1.1 albertel 1881: * Function: accent_subraster ( accent, width, height, pixsz )
1882: * Purpose: Allocate a new subraster of width x height
1883: * (or maybe different dimensions, depending on accent),
1884: * and draw an accent (\hat or \vec or \etc) that fills it
1885: * --------------------------------------------------------------------------
1886: * Arguments: accent (I) int containing either HATACCENT or VECACCENT,
1887: * etc, indicating the type of accent desired
1888: * width (I) int containing desired width of accent (#cols)
1889: * height (I) int containing desired height of accent(#rows)
1890: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
1891: * --------------------------------------------------------------------------
1892: * Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
1893: * or NULL for any error.
1894: * --------------------------------------------------------------------------
1895: * Notes: o Some accents have internally-determined dimensions,
1896: * and caller should check dimensions in returned subraster
1897: * ======================================================================= */
1898: /* --- entry point --- */
1899: subraster *accent_subraster ( int accent, int width, int height, int pixsz )
1900: {
1901: /* -------------------------------------------------------------------------
1902: Allocations and Declarations
1903: -------------------------------------------------------------------------- */
1904: /* --- general info --- */
1905: raster *new_raster(), *rp=NULL; /*raster containing desired accent*/
1906: subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
1907: int delete_raster(), delete_subraster(); /*free allocated raster on err*/
1908: int line_raster(), /* draws lines */
1.2 albertel 1909: rule_raster(), /* draw solid boxes */
1.1 albertel 1910: thickness = 1; /* line thickness */
1911: /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
1912: /* --- other working info --- */
1913: int col0, col1, /* cols for line */
1914: row0, row1; /* rows for line */
1915: subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/
1916: /* --- info for under/overbraces, tildes, etc --- */
1917: char brace[16]; /*"{" for over, "}" for under, etc*/
1918: raster *rastrot(), /* rotate { for overbrace, etc */
1919: *rastcpy(); /* may need copy of original */
1920: subraster *arrow_subraster(); /* rightarrow for vec */
1921: subraster *rastack(); /* stack accent atop extra space */
1922: /* -------------------------------------------------------------------------
1923: outer switch() traps accents that may change caller's height,width
1924: -------------------------------------------------------------------------- */
1925: switch ( accent )
1926: {
1927: default:
1928: /* -----------------------------------------------------------------------
1929: inner switch() first allocates fixed-size raster for accents that don't
1930: ------------------------------------------------------------------------ */
1931: if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
1932: != NULL ) /* and if we succeeded... */
1933: switch ( accent ) /* ...draw requested accent in it */
1934: {
1935: /* --- unrecognized request --- */
1936: default: delete_raster(rp); /* unrecognized accent requested */
1937: rp = NULL; break; /* so free raster and signal error */
1938: /* --- bar request --- */
1939: case UNDERBARACCENT:
1940: case BARACCENT:
1.2 albertel 1941: thickness = 1; /*height-1;*/ /* adjust thickness */
1.1 albertel 1942: if ( accent == BARACCENT ) /* bar is above expression */
1.2 albertel 1943: { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
1944: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
1.1 albertel 1945: else /* underbar is below expression */
1.2 albertel 1946: { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
1947: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
1.1 albertel 1948: break;
1949: /* --- dot request --- */
1950: case DOTACCENT:
1951: thickness = height-1; /* adjust thickness */
1.2 albertel 1952: /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
1953: rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
1.1 albertel 1954: break;
1955: /* --- ddot request --- */
1956: case DDOTACCENT:
1957: thickness = height-1; /* adjust thickness */
1.2 albertel 1958: col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
1959: col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
1960: if ( col0+thickness >= col1 ) /* dots overlap */
1961: { col0 = max2(col0-1,0); /* try moving left dot more left */
1962: col1 = min2(col1+1,width-thickness); } /* and right dot right */
1963: if ( col0+thickness >= col1 ) /* dots _still_ overlap */
1964: thickness = max2(thickness-1,1); /* so try reducing thickness */
1965: /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
1966: /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
1967: rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
1968: rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
1.1 albertel 1969: break;
1970: /* --- hat request --- */
1971: case HATACCENT:
1.2 albertel 1972: thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
1.1 albertel 1973: line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/
1974: line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
1975: break;
1976: /* --- sqrt request --- */
1977: case SQRTACCENT:
1978: col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */
1979: col0 = (col1+2)/3; /* midpoint col of sqrt */
1980: row0 = (height+1)/2; /* midpoint row of sqrt */
1981: row1 = height-1; /* bottom row of sqrt */
1982: line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
1983: line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
1984: line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
1985: break;
1986: } /* --- end-of-inner-switch(accent) --- */
1987: break; /* break from outer accent switch */
1988: /* --- underbrace, overbrace request --- */
1989: case UNDERBRACE:
1990: case OVERBRACE:
1991: if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
1992: if ( accent == OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
1993: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
1994: != NULL ) /* found desired brace */
1995: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
1996: delete_subraster(accsp); } /* and free subraster "envelope" */
1997: break;
1998: /* --- hat request --- */
1999: case HATACCENT:
2000: if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
2001: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2002: != NULL ) /* found desired brace */
2003: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
2004: delete_subraster(accsp); } /* and free subraster "envelope" */
2005: break;
2006: /* --- vec request --- */
2007: case VECACCENT:
2008: height = 2*(height/2) + 1; /* force height odd */
2009: if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
2010: != NULL ) /* succeeded */
2011: { rp = accsp->image; /* "extract" raster with bitmap */
2012: free((void *)accsp); } /* and free subraster "envelope" */
2013: break;
2014: /* --- tilde request --- */
2015: case TILDEACCENT:
2016: accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
2017: get_delim("~",-width,CMEX10)); /*width search for tilde*/
2018: if ( accsp != NULL ) /* found desired tilde */
2019: if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
2020: != NULL ) /* have tilde with space below it */
2021: { rp = sp->image; /* "extract" raster with bitmap */
1.2 albertel 2022: free((void *)sp); /* and free subraster "envelope" */
2023: leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
1.1 albertel 2024: break;
2025: } /* --- end-of-outer-switch(accent) --- */
2026: /* -------------------------------------------------------------------------
2027: if we constructed accent raster okay, embed it in a subraster and return it
2028: -------------------------------------------------------------------------- */
2029: /* --- if all okay, allocate subraster to contain constructed raster --- */
1.3 ! albertel 2030: if ( rp != NULL ) { /* accent raster constructed okay */
1.1 albertel 2031: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
2032: == NULL ) /* and if we fail to allocate */
2033: delete_raster(rp); /* free now-unneeded raster */
2034: else /* subraster allocated okay */
2035: { /* --- init subraster parameters, embedding raster in it --- */
2036: sp->type = IMAGERASTER; /* constructed image */
2037: sp->image = rp; /* raster we just constructed */
2038: sp->size = (-1); /* can't set font size here */
2039: sp->baseline = 0; } /* can't set baseline here */
1.3 ! albertel 2040: } /* --- end-of-if(rp!=NULL) --- */
1.1 albertel 2041: /* --- return subraster containing desired accent to caller --- */
2042: return ( sp ); /* return accent or NULL to caller */
2043: } /* --- end-of-function accent_subraster() --- */
2044:
2045:
2046: /* ==========================================================================
2047: * Function: arrow_subraster ( width, height, pixsz, drctn, isBig )
2048: * Purpose: Allocate a raster/subraster and draw left/right arrow in it
2049: * --------------------------------------------------------------------------
2050: * Arguments: width (I) int containing number of cols for arrow
2051: * height (I) int containing number of rows for arrow
2052: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
2053: * drctn (I) int containing +1 for right arrow,
2054: * or -1 for left, 0 for leftright
2055: * isBig (I) int containing 1/true for \Long arrows,
2056: * or false for \long arrows, i.e.,
2057: * true for ===> or false for --->.
2058: * --------------------------------------------------------------------------
2059: * Returns: ( subraster * ) ptr to constructed left/right arrow
2060: * or NULL for any error.
2061: * --------------------------------------------------------------------------
2062: * Notes: o
2063: * ======================================================================= */
2064: /* --- entry point --- */
2065: subraster *arrow_subraster ( int width, int height, int pixsz,
2066: int drctn, int isBig )
2067: {
2068: /* -------------------------------------------------------------------------
2069: Allocations and Declarations
2070: -------------------------------------------------------------------------- */
2071: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2072: int rule_raster(); /* draw arrow line */
2073: int irow, midrow=height/2; /* index, midrow is arrowhead apex */
1.2 albertel 2074: int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
1.1 albertel 2075: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2076: int ipix, /* raster pixmap[] index */
2077: npix = width*height; /* #pixels malloced in pixmap[] */
2078: /* -------------------------------------------------------------------------
2079: allocate raster/subraster and draw arrow line
2080: -------------------------------------------------------------------------- */
2081: if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */
2082: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2083: == NULL ) goto end_of_job; /* and quit if failed */
2084: if ( !isBig ) /* single line */
2085: rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
2086: else
2087: { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
2088: rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
2089: rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
2090: /* -------------------------------------------------------------------------
2091: construct arrowhead(s)
2092: -------------------------------------------------------------------------- */
2093: for ( irow=0; irow<height; irow++ ) /* for each row of arrow */
2094: {
2095: int delta = abs(irow-midrow); /*arrowhead offset for irow*/
2096: /* --- right arrowhead --- */
2097: if ( drctn >= 0 ) /* right arrowhead wanted */
2098: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
2099: { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
1.3 ! albertel 2100: if ( ipix >= 0 ) { /* bounds check */
1.1 albertel 2101: if ( pixsz == 1 ) /* have a bitmap */
2102: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2103: else /* should have a bytemap */
2104: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 ! albertel 2105: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2106: /* --- left arrowhead (same as right except for ipix calculation) --- */
2107: if ( drctn <= 0 ) /* left arrowhead wanted */
2108: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
2109: { ipix = irow*width + delta + icol; /* leftmost bit+delta+icol */
1.3 ! albertel 2110: if ( ipix < npix ) { /* bounds check */
1.1 albertel 2111: if ( pixsz == 1 ) /* have a bitmap */
2112: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2113: else /* should have a bytemap */
2114: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 ! albertel 2115: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2116: } /* --- end-of-for(irow) --- */
2117: end_of_job:
2118: return ( arrowsp ); /*back to caller with arrow or NULL*/
2119: } /* --- end-of-function arrow_subraster() --- */
2120:
2121:
2122: /* ==========================================================================
2123: * Function: uparrow_subraster ( width, height, pixsz, drctn, isBig )
2124: * Purpose: Allocate a raster/subraster and draw up/down arrow in it
2125: * --------------------------------------------------------------------------
2126: * Arguments: width (I) int containing number of cols for arrow
2127: * height (I) int containing number of rows for arrow
2128: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
2129: * drctn (I) int containing +1 for up arrow,
2130: * or -1 for down, or 0 for updown
2131: * isBig (I) int containing 1/true for \Long arrows,
2132: * or false for \long arrows, i.e.,
2133: * true for ===> or false for --->.
2134: * --------------------------------------------------------------------------
2135: * Returns: ( subraster * ) ptr to constructed up/down arrow
2136: * or NULL for any error.
2137: * --------------------------------------------------------------------------
2138: * Notes: o
2139: * ======================================================================= */
2140: /* --- entry point --- */
2141: subraster *uparrow_subraster ( int width, int height, int pixsz,
2142: int drctn, int isBig )
2143: {
2144: /* -------------------------------------------------------------------------
2145: Allocations and Declarations
2146: -------------------------------------------------------------------------- */
2147: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2148: int rule_raster(); /* draw arrow line */
2149: int icol, midcol=width/2; /* index, midcol is arrowhead apex */
1.2 albertel 2150: int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
1.1 albertel 2151: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2152: int ipix, /* raster pixmap[] index */
2153: npix = width*height; /* #pixels malloced in pixmap[] */
2154: /* -------------------------------------------------------------------------
2155: allocate raster/subraster and draw arrow line
2156: -------------------------------------------------------------------------- */
2157: if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */
2158: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2159: == NULL ) goto end_of_job; /* and quit if failed */
2160: if ( !isBig ) /* single line */
2161: rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
2162: else
2163: { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
2164: rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
2165: rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
2166: /* -------------------------------------------------------------------------
2167: construct arrowhead(s)
2168: -------------------------------------------------------------------------- */
2169: for ( icol=0; icol<width; icol++ ) /* for each col of arrow */
2170: {
2171: int delta = abs(icol-midcol); /*arrowhead offset for icol*/
2172: /* --- up arrowhead --- */
2173: if ( drctn >= 0 ) /* up arrowhead wanted */
2174: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
2175: { ipix = (irow+delta)*width + icol; /* leftmost+icol */
1.3 ! albertel 2176: if ( ipix < npix ) { /* bounds check */
1.1 albertel 2177: if ( pixsz == 1 ) /* have a bitmap */
2178: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2179: else /* should have a bytemap */
2180: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 ! albertel 2181: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2182: /* --- down arrowhead (same as up except for ipix calculation) --- */
2183: if ( drctn <= 0 ) /* down arrowhead wanted */
2184: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
2185: { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
1.3 ! albertel 2186: if ( ipix > 0 ) { /* bounds check */
1.1 albertel 2187: if ( pixsz == 1 ) /* have a bitmap */
2188: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2189: else /* should have a bytemap */
2190: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 ! albertel 2191: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.2 albertel 2192: } /* --- end-of-for(icol) --- */
1.1 albertel 2193: end_of_job:
2194: return ( arrowsp ); /*back to caller with arrow or NULL*/
2195: } /* --- end-of-function uparrow_subraster() --- */
2196:
2197:
2198: /* ==========================================================================
2199: * Function: rule_raster ( rp, top, left, width, height, type )
2200: * Purpose: Draw a solid or dashed line (or box) in existing raster rp,
2201: * starting at top,left with dimensions width,height.
2202: * --------------------------------------------------------------------------
2203: * Arguments: rp (I) raster * to raster in which rule
2204: * will be drawn
2205: * top (I) int containing row at which top-left corner
2206: * of rule starts (0 is topmost)
2207: * left (I) int containing col at which top-left corner
2208: * of rule starts (0 is leftmost)
2209: * width (I) int containing number of cols for rule
2210: * height (I) int containing number of rows for rule
2211: * type (I) int containing 0 for solid rule,
2212: * 1 for horizontal dashes, 2 for vertical
1.3 ! albertel 2213: * 3 for solid rule with corners removed (bevel)
! 2214: * 4 for strut (nothing drawn)
1.1 albertel 2215: * --------------------------------------------------------------------------
2216: * Returns: ( int ) 1 if rule drawn okay,
2217: * or 0 for any error.
2218: * --------------------------------------------------------------------------
2219: * Notes: o Rule line is implicitly "horizontal" or "vertical" depending
2220: * on relative width,height dimensions. It's a box if they're
2221: * more or less comparable.
2222: * ======================================================================= */
2223: /* --- entry point --- */
2224: int rule_raster ( raster *rp, int top, int left,
2225: int width, int height, int type )
2226: {
2227: /* -------------------------------------------------------------------------
2228: Allocations and Declarations
2229: -------------------------------------------------------------------------- */
1.2 albertel 2230: int irow=0, icol=0; /* indexes over rp raster */
2231: int ipix = 0, /* raster pixmap[] index */
1.1 albertel 2232: npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
2233: int isfatal = 0; /* true to abend on out-of-bounds error */
1.3 ! albertel 2234: int hdash=1, vdash=2, /* type for horizontal, vertical dashes */
! 2235: bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */
1.1 albertel 2236: int dashlen=3, spacelen=2, /* #pixels for dash followed by space */
2237: isdraw=1; /* true when drawing dash (init for solid) */
2238: /* -------------------------------------------------------------------------
2239: Check args
2240: -------------------------------------------------------------------------- */
1.3 ! albertel 2241: if ( rp == (raster *)NULL ) { /* no raster arg supplied */
1.1 albertel 2242: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
2243: rp = workingbox->image; /* use workingbox if possible */
1.3 ! albertel 2244: else return ( 0 ); } /* otherwise signal error to caller */
! 2245: if ( type == bevel ) /* remove corners of solid box */
1.2 albertel 2246: if ( width<3 || height<3 ) type=0; /* too small to remove corners */
1.1 albertel 2247: /* -------------------------------------------------------------------------
2248: Fill line/box
2249: -------------------------------------------------------------------------- */
1.3 ! albertel 2250: if ( width > 0 ) /* zero width implies strut*/
! 2251: for ( irow=top; irow<top+height; irow++ ) /* for each scan line */
1.1 albertel 2252: {
1.3 ! albertel 2253: if ( type == strut ) isdraw = 0; /* draw nothing for strut */
1.1 albertel 2254: if ( type == vdash ) /*set isdraw for vert dash*/
2255: isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
2256: ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/
2257: for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */
2258: {
1.3 ! albertel 2259: if ( type == bevel ) { /* remove corners of box */
1.2 albertel 2260: if ( (irow==top && icol==left) /* top-left corner */
2261: || (irow==top && icol>=left+width-1) /* top-right corner */
2262: || (irow>=top+height-1 && icol==left) /* bottom-left corner */
2263: || (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
1.3 ! albertel 2264: isdraw = 0; else isdraw = 1; } /*set isdraw to skip corner*/
1.1 albertel 2265: if ( type == hdash ) /*set isdraw for horiz dash*/
2266: isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
2267: if ( ++ipix >= npix ) /* bounds check failed */
2268: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2269: else break; /*or just go on to next row*/
2270: else /*ibit is within rp bounds*/
1.3 ! albertel 2271: if ( isdraw ) { /*and we're drawing this bit*/
1.1 albertel 2272: if ( rp->pixsz == 1 ) /* have a bitmap */
2273: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2274: else /* should have a bytemap */
2275: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
1.3 ! albertel 2276: ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
1.1 albertel 2277: } /* --- end-of-for(icol) --- */
2278: } /* --- end-of-for(irow) --- */
2279: end_of_job:
2280: return ( isfatal? (ipix<npix? 1:0) : 1 );
2281: } /* --- end-of-function rule_raster() --- */
2282:
2283:
2284: /* ==========================================================================
2285: * Function: line_raster ( rp, row0, col0, row1, col1, thickness )
2286: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2287: * in existing raster rp.
2288: * --------------------------------------------------------------------------
2289: * Arguments: rp (I) raster * to raster in which a line
2290: * will be drawn
2291: * row0 (I) int containing row at which
2292: * line will start (0 is topmost)
2293: * col0 (I) int containing col at which
2294: * line will start (0 is leftmost)
2295: * row1 (I) int containing row at which
2296: * line will end (rp->height-1 is bottom-most)
2297: * col1 (I) int containing col at which
2298: * line will end (rp->width-1 is rightmost)
2299: * thickness (I) int containing number of pixels/bits
2300: * thick the line will be
2301: * --------------------------------------------------------------------------
2302: * Returns: ( int ) 1 if line drawn okay,
2303: * or 0 for any error.
2304: * --------------------------------------------------------------------------
2305: * Notes: o if row0==row1, a horizontal line is drawn
2306: * between col0 and col1, with row0(==row1) the top row
2307: * and row0+(thickness-1) the bottom row
2308: * o if col0==col1, a vertical bar is drawn
2309: * between row0 and row1, with col0(==col1) the left col
2310: * and col0+(thickness-1) the right col
2311: * o if both the above, you get a square thickness x thickness
2312: * whose top-left corner is row0,col0.
2313: * ======================================================================= */
2314: /* --- entry point --- */
2315: int line_raster ( raster *rp, int row0, int col0,
2316: int row1, int col1, int thickness )
2317: {
2318: /* -------------------------------------------------------------------------
2319: Allocations and Declarations
2320: -------------------------------------------------------------------------- */
1.2 albertel 2321: int irow=0, icol=0, /* indexes over rp raster */
1.1 albertel 2322: locol=col0, hicol=col1, /* col limits at irow */
2323: lorow=row0, hirow=row1; /* row limits at icol */
1.2 albertel 2324: int width=rp->width, height=rp->height; /* dimensions of input raster */
2325: int ipix = 0, /* raster pixmap[] index */
2326: npix = width*height; /* #pixels malloced in rp->pixmap[] */
1.1 albertel 2327: int isfatal = 0; /* true to abend on out-of-bounds error */
2328: int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
2329: double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
2330: dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
2331: a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
1.2 albertel 2332: xcol=0, xrow=0; /* calculated col at irow, or row at icol */
1.1 albertel 2333: double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
2334: xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
2335: ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
2336: xheight = 1.0;
2337: int line_recurse(), isrecurse=1; /* true to draw line recursively */
2338: /* -------------------------------------------------------------------------
2339: Check args
2340: -------------------------------------------------------------------------- */
1.3 ! albertel 2341: if ( rp == (raster *)NULL ) { /* no raster arg supplied */
1.1 albertel 2342: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
2343: rp = workingbox->image; /* use workingbox if possible */
1.3 ! albertel 2344: else return ( 0 ); } /* otherwise signal error to caller */
1.1 albertel 2345: /* -------------------------------------------------------------------------
2346: Initialization
2347: -------------------------------------------------------------------------- */
1.3 ! albertel 2348: if ( msgfp!=NULL && msglevel>=29 ) { /* debugging */
1.1 albertel 2349: fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
2350: "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
1.3 ! albertel 2351: row0,col0, row1,col1, thickness, dy,dx, a, xwidth); fflush(msgfp); }
1.1 albertel 2352: /* --- check for recursive line drawing --- */
1.2 albertel 2353: if ( isrecurse ) { /* drawing lines recursively */
2354: for ( irow=0; irow<thickness; irow++ ) /* each line 1 pixel thick */
2355: { double xrow0=(double)row0, xcol0=(double)col0,
2356: xrow1=(double)row1, xcol1=(double)col1;
2357: if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
2358: else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
2359: if( xrow0>(-0.001) && xcol0>(-0.001) /*check line inside raster*/
2360: && xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
2361: line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
2362: return ( 1 ); }
1.1 albertel 2363: /* --- set params for horizontal line or vertical bar --- */
2364: if ( isline ) /*interpret row as top row*/
2365: row1 = row0 + (thickness-1); /* set bottom row for line */
2366: if ( 0&&isbar ) /*interpret col as left col*/
2367: hicol = col0 + (thickness-1); /* set right col for bar */
2368: /* -------------------------------------------------------------------------
2369: draw line one row at a time
2370: -------------------------------------------------------------------------- */
2371: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
2372: {
2373: if ( !isbar && !isline ) /* neither vert nor horiz */
2374: { xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */
2375: locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
2376: hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
2377: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2378: fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
2379: irow,xcol,locol,hicol);
2380: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2381: for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
2382: if ( ++ipix >= npix ) /* bounds check failed */
2383: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2384: else break; /*or just go on to next row*/
2385: else /* turn on pixel in line */
2386: if ( rp->pixsz == 1 ) /* have a pixel bitmap */
2387: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2388: else /* should have a bytemap */
2389: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
2390: ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
2391: } /* --- end-of-for(irow) --- */
2392: /* -------------------------------------------------------------------------
2393: now _redraw_ line one col at a time to avoid "gaps"
2394: -------------------------------------------------------------------------- */
2395: if ( 1 )
2396: for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
2397: {
2398: if ( !isbar && !isline ) /* neither vert nor horiz */
2399: { xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */
2400: lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
2401: hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
2402: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2403: fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
2404: icol,xrow,lorow,hirow);
2405: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2406: for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
2407: if ( irow<0 || irow>=rp->height
2408: || icol<0 || icol>=rp->width ) /* bounds check */
2409: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2410: else continue; /*or just go on to next row*/
2411: else
2412: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2413: } /* --- end-of-for(irow) --- */
2414: /* -------------------------------------------------------------------------
2415: Back to caller with 1=okay, 0=failed.
2416: -------------------------------------------------------------------------- */
2417: end_of_job:
2418: return ( isfatal? (ipix<npix? 1:0) : 1 );
2419: } /* --- end-of-function line_raster() --- */
2420:
2421:
2422: /* ==========================================================================
2423: * Function: line_recurse ( rp, row0, col0, row1, col1, thickness )
2424: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2425: * in existing raster rp.
2426: * --------------------------------------------------------------------------
2427: * Arguments: rp (I) raster * to raster in which a line
2428: * will be drawn
2429: * row0 (I) double containing row at which
2430: * line will start (0 is topmost)
2431: * col0 (I) double containing col at which
2432: * line will start (0 is leftmost)
2433: * row1 (I) double containing row at which
2434: * line will end (rp->height-1 is bottom-most)
2435: * col1 (I) double containing col at which
2436: * line will end (rp->width-1 is rightmost)
2437: * thickness (I) int containing number of pixels/bits
2438: * thick the line will be
2439: * --------------------------------------------------------------------------
2440: * Returns: ( int ) 1 if line drawn okay,
2441: * or 0 for any error.
2442: * --------------------------------------------------------------------------
2443: * Notes: o Recurses, drawing left- and right-halves of line
2444: * until a horizontal or vertical segment is found
2445: * ======================================================================= */
2446: /* --- entry point --- */
2447: int line_recurse ( raster *rp, double row0, double col0,
2448: double row1, double col1, int thickness )
2449: {
2450: /* -------------------------------------------------------------------------
2451: Allocations and Declarations
2452: -------------------------------------------------------------------------- */
2453: double delrow = fabs(row1-row0), /* 0 if line horizontal */
2454: delcol = fabs(col1-col0), /* 0 if line vertical */
2455: tolerance = 0.5; /* draw line when it goes to point */
2456: double midrow = 0.5*(row0+row1), /* midpoint row */
2457: midcol = 0.5*(col0+col1); /* midpoint col */
2458: /* -------------------------------------------------------------------------
2459: recurse if either delta > tolerance
2460: -------------------------------------------------------------------------- */
2461: if ( delrow > tolerance /* row hasn't converged */
2462: || delcol > tolerance ) /* col hasn't converged */
2463: { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
2464: line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
2465: return ( 1 ); }
2466: /* -------------------------------------------------------------------------
2467: draw converged point
2468: -------------------------------------------------------------------------- */
2469: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
2470: return ( 1 );
2471: } /* --- end-of-function line_recurse() --- */
2472:
2473:
2474: /* ==========================================================================
2475: * Function: circle_raster ( rp, row0, col0, row1, col1,
2476: * thickness, quads )
2477: * Purpose: Draw quad(rant)s of an ellipse in box determined by
2478: * diagonally opposite corner points (row0,col0) and
2479: * (row1,col1), of thickness pixels in existing raster rp.
2480: * --------------------------------------------------------------------------
2481: * Arguments: rp (I) raster * to raster in which an ellipse
2482: * will be drawn
2483: * row0 (I) int containing 1st corner row bounding ellipse
2484: * (0 is topmost)
2485: * col0 (I) int containing 1st corner col bounding ellipse
2486: * (0 is leftmost)
2487: * row1 (I) int containing 2nd corner row bounding ellipse
2488: * (rp->height-1 is bottom-most)
2489: * col1 (I) int containing 2nd corner col bounding ellipse
2490: * (rp->width-1 is rightmost)
2491: * thickness (I) int containing number of pixels/bits
2492: * thick the ellipse arc line will be
2493: * quads (I) char * to null-terminated string containing
2494: * any subset/combination of "1234" specifying
2495: * which quadrant(s) of ellipse to draw.
2496: * NULL ptr draws all four quadrants;
2497: * otherwise 1=upper-right quadrant,
2498: * 2=uper-left, 3=lower-left, 4=lower-right,
2499: * i.e., counterclockwise from 1=positive quad.
2500: * --------------------------------------------------------------------------
2501: * Returns: ( int ) 1 if ellipse drawn okay,
2502: * or 0 for any error.
2503: * --------------------------------------------------------------------------
2504: * Notes: o row0==row1 or col0==col1 are errors
2505: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
2506: * ======================================================================= */
2507: /* --- entry point --- */
2508: int circle_raster ( raster *rp, int row0, int col0,
2509: int row1, int col1, int thickness, char *quads )
2510: {
2511: /* -------------------------------------------------------------------------
2512: Allocations and Declarations
2513: -------------------------------------------------------------------------- */
2514: /* --- lower-left and upper-right bounding points (in our coords) --- */
2515: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
2516: locol = min2(col0,col1), /* lower bounding col (left of box)*/
2517: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
2518: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
2519: /* --- a and b ellipse params --- */
2520: int width = hicol-locol+1, /* width of bounding box */
2521: height= hirow-lorow+1, /* height of bounding box */
2522: islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
2523: double a = ((double)width)/2.0, /* x=a when y=0 */
2524: b = ((double)height)/2.0, /* y=b when x=0 */
2525: abmajor = (islandscape? a : b), /* max2(a,b) */
2526: abminor = (islandscape? b : a), /* min2(a,b) */
2527: abmajor2 = abmajor*abmajor, /* abmajor^2 */
2528: abminor2 = abminor*abminor; /* abminor^2 */
2529: /* --- other stuff --- */
2530: int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
2531: iminor=0, nminor=min2(width,height); /* solved index on minor axis */
2532: int irow, icol, /* raster indexes at circumference */
2533: rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/
2534: double midrow= ((double)(row0+row1))/2.0, /* center row */
2535: midcol= ((double)(col0+col1))/2.0; /* center col */
2536: double xy, xy2, /* major axis ellipse coord */
2537: yx2, yx; /* solved minor ellipse coord */
2538: int isokay = 1; /* true if no pixels out-of-bounds */
2539: char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */
2540: int circle_recurse(), isrecurse=1; /* true to draw ellipse recursively*/
2541: /* -------------------------------------------------------------------------
2542: pixel-by-pixel along positive major axis, quit when it goes negative
2543: -------------------------------------------------------------------------- */
2544: if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */
2545: if ( msgfp!=NULL && msglevel>=39 ) /* debugging */
2546: fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
2547: width,height,quads);
2548: if ( nmajor < 1 ) isokay = 0; /* problem with input args */
2549: else
2550: {
2551: if ( isrecurse ) /* use recursive algorithm */
2552: {
2553: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2554: {
2555: double theta0=0.0, theta1=0.0; /* set thetas based on quadrant */
2556: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2557: { default: /* unrecognized, assume quadrant 1 */
2558: case '1': theta0= 0.0; theta1= 90.0; break; /* first quadrant */
2559: case '2': theta0= 90.0; theta1=180.0; break; /* second quadrant */
2560: case '3': theta0=180.0; theta1=270.0; break; /* third quadrant */
2561: case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
2562: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
2563: } /* --- end-of-for(qptr) --- */
2564: return ( 1 );
2565: } /* --- end-of-if(isrecurse) --- */
2566: for ( imajor=(nmajor+1)/2; ; imajor-- )
2567: {
2568: /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
2569: xy = ((double)imajor); /* xy = abmajor ... 0 */
2570: if ( xy < 0.0 ) break; /* negative side symmetrical */
2571: yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
2572: yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */
2573: iminor = iround(yx); /* nearest integer */
2574: /* --- set pixels for each requested quadrant --- */
2575: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2576: {
2577: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
2578: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2579: { default: break; /* unrecognized, assume quadrant 1 */
2580: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
2581: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
2582: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
2583: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
2584: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
2585: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
2586: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
2587: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
2588: fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
2589: imajor,iminor,*qptr,irow,icol);
2590: if ( irow<0 || irow>=rp->height /* row outside raster */
2591: || icol<0 || icol>=rp->width ) /* col outside raster */
2592: { isokay = 0; /* signal out-of-bounds pixel */
2593: continue; } /* but still try remaining points */
2594: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2595: } /* --- end-of-for(qptr) --- */
2596: } /* --- end-of-for(imajor) --- */
2597: /* ------------------------------------------------------------------------
2598: now do it _again_ along minor axis to avoid "gaps"
2599: ------------------------------------------------------------------------- */
2600: if ( 1 && iminor>0 )
2601: for ( iminor=(nminor+1)/2; ; iminor-- )
2602: {
2603: /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
2604: yx = ((double)iminor); /* yx = abminor ... 0 */
2605: if ( yx < 0.0 ) break; /* negative side symmetrical */
2606: xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
2607: xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */
2608: imajor = iround(xy); /* nearest integer */
2609: /* --- set pixels for each requested quadrant --- */
2610: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
2611: {
2612: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
2613: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
2614: { default: break; /* unrecognized, assume quadrant 1 */
2615: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
2616: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
2617: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
2618: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
2619: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
2620: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
2621: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
2622: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
2623: fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
2624: iminor,imajor,*qptr,irow,icol);
2625: if ( irow<0 || irow>=rp->height /* row outside raster */
2626: || icol<0 || icol>=rp->width ) /* col outside raster */
2627: { isokay = 0; /* signal out-of-bounds pixel */
2628: continue; } /* but still try remaining points */
2629: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2630: } /* --- end-of-for(qptr) --- */
2631: } /* --- end-of-for(iminor) --- */
2632: } /* --- end-of-if/else(nmajor<1) --- */
2633: return ( isokay );
2634: } /* --- end-of-function circle_raster() --- */
2635:
2636:
2637: /* ==========================================================================
2638: * Function: circle_recurse ( rp, row0, col0, row1, col1,
2639: * thickness, theta0, theta1 )
2640: * Purpose: Recursively draws arc theta0<=theta<=theta1 of the ellipse
2641: * in box determined by diagonally opposite corner points
2642: * (row0,col0) and (row1,col1), of thickness pixels in raster rp.
2643: * --------------------------------------------------------------------------
2644: * Arguments: rp (I) raster * to raster in which an ellipse
2645: * will be drawn
2646: * row0 (I) int containing 1st corner row bounding ellipse
2647: * (0 is topmost)
2648: * col0 (I) int containing 1st corner col bounding ellipse
2649: * (0 is leftmost)
2650: * row1 (I) int containing 2nd corner row bounding ellipse
2651: * (rp->height-1 is bottom-most)
2652: * col1 (I) int containing 2nd corner col bounding ellipse
2653: * (rp->width-1 is rightmost)
2654: * thickness (I) int containing number of pixels/bits
2655: * thick the ellipse arc line will be
2656: * theta0 (I) double containing first angle -360 -> +360
2657: * theta1 (I) double containing second angle -360 -> +360
2658: * 0=x-axis, positive moving counterclockwise
2659: * --------------------------------------------------------------------------
2660: * Returns: ( int ) 1 if ellipse drawn okay,
2661: * or 0 for any error.
2662: * --------------------------------------------------------------------------
2663: * Notes: o row0==row1 or col0==col1 are errors
2664: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
2665: * Then, with x=r*cos(theta), y=r*sin(theta), ellipse
2666: * equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
2667: * ======================================================================= */
2668: /* --- entry point --- */
2669: int circle_recurse ( raster *rp, int row0, int col0,
2670: int row1, int col1, int thickness, double theta0, double theta1 )
2671: {
2672: /* -------------------------------------------------------------------------
2673: Allocations and Declarations
2674: -------------------------------------------------------------------------- */
2675: /* --- lower-left and upper-right bounding points (in our coords) --- */
2676: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
2677: locol = min2(col0,col1), /* lower bounding col (left of box)*/
2678: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
2679: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
2680: /* --- a and b ellipse params --- */
2681: int width = hicol-locol+1, /* width of bounding box */
2682: height= hirow-lorow+1; /* height of bounding box */
2683: double a = ((double)width)/2.0, /* col x=a when row y=0 */
2684: b = ((double)height)/2.0, /* row y=b when col x=0 */
2685: ab=a*b, a2=a*a, b2=b*b; /* product and squares */
2686: /* --- arc parameters --- */
2687: double rads = 0.017453292, /* radians per degree = 1/57.29578 */
2688: lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
2689: hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
2690: locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
2691: hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
2692: rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
2693: rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
2694: xlo=rlo*locos, ylo=rlo*losin, /*col,row pixel coords for lotheta*/
2695: xhi=rhi*hicos, yhi=rhi*hisin, /*col,row pixel coords for hitheta*/
2696: xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
2697: tolerance = 0.5; /* convergence tolerance */
2698: /* -------------------------------------------------------------------------
2699: recurse if either delta > tolerance
2700: -------------------------------------------------------------------------- */
2701: if ( ydelta > tolerance /* row hasn't converged */
2702: || xdelta > tolerance ) /* col hasn't converged */
2703: { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
2704: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta); /*lo*/
2705: circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
2706: /* -------------------------------------------------------------------------
2707: draw converged point
2708: -------------------------------------------------------------------------- */
2709: else
2710: { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi), /* relative to center*/
2711: centerrow = 0.5*((double)(lorow+hirow)), /* ellipse y-center */
2712: centercol = 0.5*((double)(locol+hicol)), /* ellipse x-center */
2713: midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
2714: setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
2715: return ( 1 );
2716: } /* --- end-of-function circle_recurse() --- */
2717:
2718:
2719: /* ==========================================================================
2720: * Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
2721: * Purpose: Recursively draw bezier from r0,c0 to r1,c1
2722: * (with tangent point rt,ct) in existing raster rp.
2723: * --------------------------------------------------------------------------
2724: * Arguments: rp (I) raster * to raster in which a line
2725: * will be drawn
2726: * r0 (I) double containing row at which
2727: * bezier will start (0 is topmost)
2728: * c0 (I) double containing col at which
2729: * bezier will start (0 is leftmost)
2730: * r1 (I) double containing row at which
2731: * bezier will end (rp->height-1 is bottom-most)
2732: * c1 (I) double containing col at which
2733: * bezier will end (rp->width-1 is rightmost)
2734: * rt (I) double containing row for tangent point
2735: * ct (I) double containing col for tangent point
2736: * --------------------------------------------------------------------------
2737: * Returns: ( int ) 1 if line drawn okay,
2738: * or 0 for any error.
2739: * --------------------------------------------------------------------------
2740: * Notes: o Recurses, drawing left- and right-halves of bezier curve
2741: * until a point is found
2742: * ======================================================================= */
2743: /* --- entry point --- */
2744: int bezier_raster ( raster *rp, double r0, double c0,
2745: double r1, double c1, double rt, double ct )
2746: {
2747: /* -------------------------------------------------------------------------
2748: Allocations and Declarations
2749: -------------------------------------------------------------------------- */
2750: double delrow = fabs(r1-r0), /* 0 if same row */
2751: delcol = fabs(c1-c0), /* 0 if same col */
2752: tolerance = 0.5; /* draw curve when it goes to point*/
2753: double midrow = 0.5*(r0+r1), /* midpoint row */
2754: midcol = 0.5*(c0+c1); /* midpoint col */
2755: int irow=0, icol=0; /* point to be drawn */
2756: int status = 1; /* return status */
2757: /* -------------------------------------------------------------------------
2758: recurse if either delta > tolerance
2759: -------------------------------------------------------------------------- */
2760: if ( delrow > tolerance /* row hasn't converged */
2761: || delcol > tolerance ) /* col hasn't converged */
2762: { bezier_raster(rp, r0,c0, /* left half */
2763: 0.5*(rt+midrow), 0.5*(ct+midcol),
2764: 0.5*(r0+rt), 0.5*(c0+ct) );
2765: bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
2766: r1,c1,
2767: 0.5*(r1+rt), 0.5*(c1+ct) );
2768: return ( 1 ); }
2769: /* -------------------------------------------------------------------------
2770: draw converged point
2771: -------------------------------------------------------------------------- */
2772: /* --- get integer point --- */
2773: irow = iround(midrow); /* row pixel coord */
2774: icol = iround(midcol); /* col pixel coord */
2775: /* --- bounds check --- */
2776: if ( irow>=0 && irow<rp->height /* row in bounds */
2777: && icol>=0 && icol<rp->width ) /* col in bounds */
2778: setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/
2779: else status = 0; /* bad status if out-of-bounds */
2780: return ( status );
2781: } /* --- end-of-function bezier_raster() --- */
2782:
2783:
2784: /* ==========================================================================
2785: * Function: border_raster ( rp, ntop, nbot, isline, isfree )
2786: * Purpose: Allocate a new raster containing a copy of input rp,
2787: * along with ntop extra rows at top and nbot at bottom,
2788: * and whose width is either adjusted correspondingly,
2789: * or is automatically enlarged to a multiple of 8
2790: * with original bitmap centered
2791: * --------------------------------------------------------------------------
2792: * Arguments: rp (I) raster * to raster on which a border
2793: * is to be placed
2794: * ntop (I) int containing number extra rows at top.
2795: * if negative, abs(ntop) used, and same
2796: * number of extra cols added at left.
2797: * nbot (I) int containing number extra rows at bottom.
2798: * if negative, abs(nbot) used, and same
2799: * number of extra cols added at right.
2800: * isline (I) int containing 0 to leave border pixels clear
2801: * or >0 to draw a line around border of width
2802: * isline.
2803: * isfree (I) int containing true to free rp before return
2804: * --------------------------------------------------------------------------
2805: * Returns: ( raster * ) ptr to bordered raster,
2806: * or NULL for any error.
2807: * --------------------------------------------------------------------------
2808: * Notes: o
2809: * ======================================================================= */
2810: /* --- entry point --- */
2811: raster *border_raster ( raster *rp, int ntop, int nbot,
2812: int isline, int isfree )
2813: {
2814: /* -------------------------------------------------------------------------
2815: Allocations and Declarations
2816: -------------------------------------------------------------------------- */
2817: raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/
2818: int rastput(); /* overlay rp in new bordered raster */
1.3 ! albertel 2819: int width = (rp==NULL?0:rp->width), /* width of raster */
! 2820: height = (rp==NULL?0:rp->height), /* height of raster */
! 2821: istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
1.1 albertel 2822: leftmargin = 0; /* adjust width to whole number of bytes */
1.3 ! albertel 2823: int delete_raster(); /* free input rp if isfree is true */
1.1 albertel 2824: /* -------------------------------------------------------------------------
2825: Initialization
2826: -------------------------------------------------------------------------- */
2827: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
2828: if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
2829: { bp=rp; goto end_of_job; } /* return ascii string unchanged */
2830: /* --- check for negative args --- */
2831: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
2832: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
2833: /* --- adjust height for ntop and nbot margins --- */
2834: height += (ntop+nbot); /* adjust height for margins */
2835: /* --- adjust width for left and right margins --- */
2836: if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/
2837: { /* --- adjust width (and leftmargin) as requested by caller -- */
2838: if ( istopneg ) { width += ntop; leftmargin = ntop; }
2839: if ( isbotneg ) width += nbot; }
2840: else
2841: { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
2842: leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
2843: width += leftmargin; /* width now multiple of 8 */
2844: leftmargin /= 2; } /* center original raster */
2845: /* -------------------------------------------------------------------------
2846: allocate bordered raster, and embed rp within it
2847: -------------------------------------------------------------------------- */
2848: /* --- allocate bordered raster --- */
2849: if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/
2850: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
2851: /* --- embed rp in it --- */
2852: rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */
2853: /* -------------------------------------------------------------------------
2854: draw border if requested
2855: -------------------------------------------------------------------------- */
2856: if ( isline )
2857: { int irow, icol, nthick=isline; /*height,width index, line thickness*/
2858: /* --- draw left- and right-borders --- */
2859: for ( irow=0; irow<height; irow++ ) /* for each row of bp */
2860: for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
2861: { setpixel(bp,irow,icol,255); /* left border */
2862: setpixel(bp,irow,width-1-icol,255); } /* right border */
2863: /* --- draw top- and bottom-borders --- */
2864: for ( icol=0; icol<width; icol++ ) /* for each col of bp */
2865: for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
2866: { setpixel(bp,irow,icol,255); /* top border */
2867: setpixel(bp,height-1-irow,icol,255); } /* bottom border */
2868: } /* --- end-of-if(isline) --- */
2869: /* -------------------------------------------------------------------------
2870: free rp if no longer needed
2871: -------------------------------------------------------------------------- */
2872: if ( isfree ) /*caller no longer needs rp*/
2873: delete_raster(rp); /* so free it for him */
2874: /* -------------------------------------------------------------------------
2875: Back to caller with bordered raster (or null for any error)
2876: -------------------------------------------------------------------------- */
2877: end_of_job:
2878: return ( bp ); /* back with bordered or null ptr */
2879: } /* --- end-of-function border_raster() --- */
2880:
2881:
2882: /* ==========================================================================
1.3 ! albertel 2883: * Function: backspace_raster ( rp, nback, pback, minspace, isfree )
! 2884: * Purpose: Allocate a new raster containing a copy of input rp,
! 2885: * but with trailing nback columns removed.
! 2886: * If minspace>=0 then (at least) that many columns
! 2887: * of whitespace will be left in place, regardless of nback.
! 2888: * If minspace<0 then existing black pixels will be deleted
! 2889: * as required.
! 2890: * --------------------------------------------------------------------------
! 2891: * Arguments: rp (I) raster * to raster on which a border
! 2892: * is to be placed
! 2893: * nback (I) int containing number of columns to
! 2894: * backspace (>=0)
! 2895: * pback (O) ptr to int returning #pixels actually
! 2896: * backspaced (or NULL to not use)
! 2897: * minspace (I) int containing number of columns
! 2898: * of whitespace to be left in place
! 2899: * isfree (I) int containing true to free rp before return
! 2900: * --------------------------------------------------------------------------
! 2901: * Returns: ( raster * ) ptr to backspaced raster,
! 2902: * or NULL for any error.
! 2903: * --------------------------------------------------------------------------
! 2904: * Notes: o For \! negative space, for \hspace{-10}, etc.
! 2905: * ======================================================================= */
! 2906: /* --- entry point --- */
! 2907: raster *backspace_raster ( raster *rp, int nback, int *pback, int minspace,
! 2908: int isfree )
! 2909: {
! 2910: /* -------------------------------------------------------------------------
! 2911: Allocations and Declarations
! 2912: -------------------------------------------------------------------------- */
! 2913: raster *new_raster(), *bp=(raster *)NULL; /* raster returned to caller */
! 2914: int delete_raster(); /* free input rp if isfree is true */
! 2915: int width = (rp==NULL?0:rp->width), /* original width of raster */
! 2916: height = (rp==NULL?0:rp->height), /* height of raster */
! 2917: mback = nback, /* nback adjusted for minspace */
! 2918: newwidth = width, /* adjusted width after backspace */
! 2919: icol=0, irow=0; /* col,row index */
! 2920: if ( rp == NULL ) goto end_of_job; /* no input given */
! 2921: /* -------------------------------------------------------------------------
! 2922: locate rightmost column of rp containing ink, and determine backspaced width
! 2923: -------------------------------------------------------------------------- */
! 2924: /* --- locate rightmost column of rp containing ink --- */
! 2925: if ( minspace >= 0 ) /* only needed if given minspace */
! 2926: for ( icol=width-1; icol>=0; icol-- ) /* find first non-empty col in row */
! 2927: for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
! 2928: if ( getpixel(rp,irow,icol) != 0 ) { /* found a set pixel */
! 2929: int whitecols = (width-1) - icol; /* #cols containing white space */
! 2930: mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
! 2931: goto gotright; } /* no need to look further */
! 2932: /* --- determine width of new backspaced raster --- */
! 2933: gotright: /* found col with ink (or rp empty)*/
! 2934: if ( mback > width ) mback = width; /* can't backspace before beginning*/
! 2935: newwidth = max2(1,width-mback); /* #cols in backspaced raster */
! 2936: if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
! 2937: /* -------------------------------------------------------------------------
! 2938: allocate new raster and fill it with leftmost cols of rp
! 2939: -------------------------------------------------------------------------- */
! 2940: /* --- allocate backspaced raster --- */
! 2941: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
! 2942: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
! 2943: /* --- fill new raster --- */
! 2944: if ( width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/
! 2945: for ( icol=0; icol<newwidth; icol++ ) /* find first non-empty col in row */
! 2946: for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
! 2947: { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
! 2948: setpixel(bp,irow,icol,value); } /* saved in backspaced raster */
! 2949: /* -------------------------------------------------------------------------
! 2950: Back to caller with backspaced raster (or null for any error)
! 2951: -------------------------------------------------------------------------- */
! 2952: end_of_job:
! 2953: if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
! 2954: "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
! 2955: nback,minspace,mback,width,newwidth); fflush(msgfp); }
! 2956: if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
! 2957: return ( bp ); /* back with backspaced or null ptr*/
! 2958: } /* --- end-of-function backspace_raster() --- */
! 2959:
! 2960:
! 2961: /* ==========================================================================
1.1 albertel 2962: * Function: type_raster ( rp, fp )
2963: * Purpose: Emit an ascii dump representing rp, on fp.
2964: * --------------------------------------------------------------------------
2965: * Arguments: rp (I) ptr to raster struct for which an
2966: * ascii dump is to be constructed.
2967: * fp (I) File ptr to output device (defaults to
2968: * stdout if passed as NULL).
2969: * --------------------------------------------------------------------------
2970: * Returns: ( int ) 1 if completed successfully,
2971: * or 0 otherwise (for any error).
2972: * --------------------------------------------------------------------------
2973: * Notes:
2974: * ======================================================================= */
2975: /* --- entry point --- */
2976: int type_raster ( raster *rp, FILE *fp )
2977: {
2978: /* -------------------------------------------------------------------------
2979: Allocations and Declarations
2980: -------------------------------------------------------------------------- */
2981: static int display_width = 72; /* max columns for display */
2982: static char display_chars[16] = /* display chars for bytemap */
2983: { ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
2984: char scanline[133]; /* ascii image for one scan line */
2985: int scan_width; /* #chars in scan (<=display_width)*/
2986: int irow, locol,hicol=(-1); /* height index, width indexes */
1.2 albertel 2987: raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
2988: int delete_raster(); /*free bitmap converted for display*/
1.1 albertel 2989: /* --------------------------------------------------------------------------
2990: initialization
2991: -------------------------------------------------------------------------- */
2992: /* --- redirect null fp --- */
2993: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
1.3 ! albertel 2994: if ( msglevel >= 999 ) { fprintf(fp, /* debugging diagnostics */
! 2995: "type_raster> width=%d height=%d ...\n",
! 2996: rp->width,rp->height); fflush(fp); }
1.1 albertel 2997: /* --- check for ascii string --- */
2998: if ( isstring /* pixmap has string, not raster */
1.2 albertel 2999: || (0 && rp->height==1) ) /* infer input rp is a string */
1.1 albertel 3000: {
3001: char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
3002: int width = strlen(string); /* #chars in ascii string */
3003: while ( width > display_width-2 ) /* too big for one line */
3004: { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
3005: string += (display_width-2); /* bump string past displayed chars*/
3006: width -= (display_width-2); } /* decrement remaining width */
3007: fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
3008: return ( 1 );
3009: } /* --- end-of-if(isstring) --- */
3010: /* --------------------------------------------------------------------------
3011: display ascii dump of bitmap image (in segments if display_width < rp->width)
3012: -------------------------------------------------------------------------- */
1.2 albertel 3013: if ( rp->format == 2 /* input is .gf-formatted */
3014: || rp->format == 3 )
3015: bitmaprp = gftobitmap(rp); /* so convert it for display */
3016: if ( bitmaprp != NULL ) /* if we have image for display */
3017: while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
1.1 albertel 3018: {
3019: /* --- set hicol for this pass (locol set above) --- */
3020: hicol += display_width; /* show as much as display allows */
3021: if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
3022: scan_width = hicol-locol+1; /* #chars in this scan */
3023: if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
3024: /* ------------------------------------------------------------------------
3025: display all scan lines for this local...hicol segment range
3026: ------------------------------------------------------------------------ */
3027: for ( irow=0; irow<rp->height; irow++ ) /* all scan lines for col range */
3028: {
3029: /* --- allocations and declarations --- */
3030: int ipix, /* pixmap[] index for this scan */
3031: lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
3032: /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
3033: for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
1.2 albertel 3034: if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
3035: scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
1.1 albertel 3036: else /* should have a bytemap */
1.2 albertel 3037: if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
3038: { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
1.1 albertel 3039: ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
3040: scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
3041: /* --- display completed scan line --- */
3042: fprintf(fp,"%.*s\n",scan_width,scanline);
3043: } /* --- end-of-for(irow) --- */
3044: } /* --- end-of-while(hicol<rp->width) --- */
3045: /* -------------------------------------------------------------------------
3046: Back to caller with 1=okay, 0=failed.
3047: -------------------------------------------------------------------------- */
1.2 albertel 3048: if ( rp->format == 2 /* input was .gf-format */
3049: || rp->format == 3 )
3050: if ( bitmaprp != NULL ) /* and we converted it for display */
3051: delete_raster(bitmaprp); /* no longer needed, so free it */
1.1 albertel 3052: return ( 1 );
3053: } /* --- end-of-function type_raster() --- */
3054:
3055:
3056: /* ==========================================================================
3057: * Function: type_bytemap ( bp, grayscale, width, height, fp )
3058: * Purpose: Emit an ascii dump representing bp, on fp.
3059: * --------------------------------------------------------------------------
3060: * Arguments: bp (I) intbyte * to bytemap for which an
3061: * ascii dump is to be constructed.
3062: * grayscale (I) int containing #gray shades, 256 for 8-bit
3063: * width (I) int containing #cols in bytemap
3064: * height (I) int containing #rows in bytemap
3065: * fp (I) File ptr to output device (defaults to
3066: * stdout if passed as NULL).
3067: * --------------------------------------------------------------------------
3068: * Returns: ( int ) 1 if completed successfully,
3069: * or 0 otherwise (for any error).
3070: * --------------------------------------------------------------------------
3071: * Notes:
3072: * ======================================================================= */
3073: /* --- entry point --- */
3074: int type_bytemap ( intbyte *bp, int grayscale,
3075: int width, int height, FILE *fp )
3076: {
3077: /* -------------------------------------------------------------------------
3078: Allocations and Declarations
3079: -------------------------------------------------------------------------- */
3080: static int display_width = 72; /* max columns for display */
3081: int byte_width = 3, /* cols to display byte (ff+space) */
3082: maxbyte = 0; /* if maxbyte<16, set byte_width=2 */
3083: int white_byte = 0, /* show dots for white_byte's */
3084: black_byte = grayscale-1; /* show stars for black_byte's */
3085: char scanline[133]; /* ascii image for one scan line */
3086: int scan_width, /* #chars in scan (<=display_width)*/
3087: scan_cols; /* #cols in scan (hicol-locol+1) */
3088: int ibyte, /* bp[] index */
3089: irow, locol,hicol=(-1); /* height index, width indexes */
3090: /* --------------------------------------------------------------------------
3091: initialization
3092: -------------------------------------------------------------------------- */
3093: /* --- redirect null fp --- */
3094: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3095: /* --- check for ascii string --- */
3096: if ( isstring ) /* bp has ascii string, not raster */
3097: { width = strlen((char *)bp); /* #chars in ascii string */
3098: height = 1; } /* default */
3099: /* --- see if we can get away with byte_width=1 --- */
3100: for ( ibyte=0; ibyte<width*height; ibyte++ ) /* check all bytes */
3101: { int byteval = (int)bp[ibyte]; /* current byte value */
3102: if ( byteval < black_byte ) /* if it's less than black_byte */
3103: maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
3104: if ( maxbyte < 16 ) /* bytevals will fit in one column */
3105: byte_width = 1; /* so reset display byte_width */
3106: /* --------------------------------------------------------------------------
3107: display ascii dump of bitmap image (in segments if display_width < rp->width)
3108: -------------------------------------------------------------------------- */
3109: while ( (locol=hicol+1) < width ) /*start where prev segment left off*/
3110: {
3111: /* --- set hicol for this pass (locol set above) --- */
3112: hicol += display_width/byte_width; /* show as much as display allows */
3113: if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
3114: scan_cols = hicol-locol+1; /* #cols in this scan */
3115: scan_width = byte_width*scan_cols; /* #chars in this scan */
3116: if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
3117: /* ------------------------------------------------------------------------
3118: display all scan lines for this local...hicol segment range
3119: ------------------------------------------------------------------------ */
3120: for ( irow=0; irow<height; irow++ ) /* all scan lines for col range */
3121: {
3122: /* --- allocations and declarations --- */
3123: int lobyte = irow*width + locol; /* first bp[] byte in this scan */
3124: char scanbyte[32]; /* sprintf() buffer for byte */
3125: /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
3126: memset(scanline,' ',scan_width); /* blank out scanline */
3127: for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
3128: { int byteval = (int)bp[lobyte+ibyte]; /* value of current byte */
3129: memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
3130: if ( byteval == black_byte ) /* but if we have a black byte */
3131: memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
3132: if ( byte_width > 1 ) /* don't blank out single char */
3133: scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
3134: if ( byteval != white_byte /* format bytes that are non-white */
3135: && byteval != black_byte ) /* and that are non-black */
3136: sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
3137: memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
3138: /* --- display completed scan line --- */
3139: fprintf(fp,"%.*s\n",scan_width,scanline);
3140: } /* --- end-of-for(irow) --- */
3141: } /* --- end-of-while(hicol<width) --- */
3142: /* -------------------------------------------------------------------------
3143: Back to caller with 1=okay, 0=failed.
3144: -------------------------------------------------------------------------- */
3145: return ( 1 );
3146: } /* --- end-of-function type_bytemap() --- */
3147:
3148:
3149: /* ==========================================================================
3150: * Function: xbitmap_raster ( rp, fp )
3151: * Purpose: Emit a mime xbitmap representing rp, on fp.
3152: * --------------------------------------------------------------------------
3153: * Arguments: rp (I) ptr to raster struct for which a mime
3154: * xbitmap is to be constructed.
3155: * fp (I) File ptr to output device (defaults to
3156: * stdout if passed as NULL).
3157: * --------------------------------------------------------------------------
3158: * Returns: ( int ) 1 if completed successfully,
3159: * or 0 otherwise (for any error).
3160: * --------------------------------------------------------------------------
3161: * Notes:
3162: * ======================================================================= */
3163: /* --- entry point --- */
3164: int xbitmap_raster ( raster *rp, FILE *fp )
3165: {
3166: /* -------------------------------------------------------------------------
3167: Allocations and Declarations
3168: -------------------------------------------------------------------------- */
3169: char *title = "image"; /* dummy title */
3170: int hex_bitmap(); /* dump bitmap as hex bytes */
3171: /* --------------------------------------------------------------------------
3172: emit text to display mime xbitmap representation of rp->bitmap image
3173: -------------------------------------------------------------------------- */
3174: /* --- first redirect null fp --- */
3175: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3176: /* --- check for ascii string --- */
3177: if ( isstring ) /* pixmap has string, not raster */
3178: return ( 0 ); /* can't handle ascii string */
3179: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
3180: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
3181: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
3182: title,rp->width, title,rp->height );
3183: fprintf( fp, "static char %s_bits[] = {\n", title );
3184: hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */
3185: fprintf (fp,"};\n"); /* ending with "};" for C array */
3186: /* -------------------------------------------------------------------------
3187: Back to caller with 1=okay, 0=failed.
3188: -------------------------------------------------------------------------- */
3189: return ( 1 );
3190: } /* --- end-of-function xbitmap_raster() --- */
3191:
3192:
3193: /* ==========================================================================
1.2 albertel 3194: * Function: type_pbmpgm ( rp, ptype, file )
3195: * Purpose: Write pbm or pgm image of rp to file
3196: * --------------------------------------------------------------------------
3197: * Arguments: rp (I) ptr to raster struct for which
3198: * a pbm/pgm file is to be written.
3199: * ptype (I) int containing 1 for pbm, 2 for pgm, or
3200: * 0 to determine ptype from values in rp
3201: * file (I) ptr to null-terminated char string
3202: * containing name of fuke to be written
3203: * (see notes below).
3204: * --------------------------------------------------------------------------
3205: * Returns: ( int ) total #bytes written,
3206: * or 0 for any error.
3207: * --------------------------------------------------------------------------
3208: * Notes: o (a) If file==NULL, output is written to stdout;
3209: * (b) if *file=='\000' then file is taken as the
3210: * address of an output buffer to which output
3211: * is written (and is followed by a terminating
3212: * '\0' which is not counted in #bytes returned);
3213: * (c) otherwise file is the filename (opened and
3214: * closed internally) to which output is written,
3215: * except that any final .ext extension is replaced
3216: * by .pbm or .pgm depending on ptype.
3217: * ======================================================================= */
3218: /* --- entry point --- */
3219: int type_pbmpgm ( raster *rp, int ptype, char *file )
3220: {
3221: /* -------------------------------------------------------------------------
3222: Allocations and Declarations
3223: -------------------------------------------------------------------------- */
3224: int isokay=0, nbytes=0; /* completion flag, total #bytes written */
3225: int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
3226: int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
3227: ngray = 0; /* #gray scale values */
3228: FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
3229: char outline[1024], outfield[256], /* output line, field */
3230: cr[16] = "\n\000"; /* cr at end-of-line */
3231: int maxlinelen = 70; /* maximum allowed line length */
3232: int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
3233: static char
3234: *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
3235: *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
3236: *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
3237: /* -------------------------------------------------------------------------
3238: check input, determine grayscale, and set up output file if necessary
3239: -------------------------------------------------------------------------- */
3240: /* --- check input args --- */
3241: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
3242: if ( ptype != 0 ) /* we'll determine ptype below */
3243: if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
3244: /* --- determine largest (and smallest) value in pixmap --- */
3245: for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
3246: for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
3247: { int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
3248: pixmin = min2(pixmin,pixval); /* new minimum */
3249: pixmax = max2(pixmax,pixval); } /* new maximum */
3250: ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
3251: if ( ptype == 0 ) /* caller wants us to set ptype */
3252: ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
3253: /* --- open output file if necessary --- */
3254: if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
3255: else if ( *file != '\000' ) { /* explicit filename provided, so...*/
3256: char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
3257: strncpy(fname,file,255); /* local copy of file name */
3258: fname[255] = '\000'; /* make sure it's null terminated */
3259: if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
3260: strcat(fname,suffix[ptype]); /* so add extension */
3261: else /* we already have an extension */
3262: strcpy(pdot,suffix[ptype]); /* so replace original extension */
3263: if ( (fp = fopen(fname,mode[ptype])) /* open output file */
3264: == (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
3265: } /* --- ens-of-if(*file!='\0') --- */
3266: /* -------------------------------------------------------------------------
3267: format and write header
3268: -------------------------------------------------------------------------- */
3269: /* --- format header info --- */
3270: *outline = '\000'; /* initialize line buffer */
3271: strcat(outline,magic[ptype]); /* begin file with "magic number" */
3272: strcat(outline,cr); /* followed by cr to end line */
3273: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
3274: strcat(outline,outfield); /* add width and height to header */
3275: strcat(outline,cr); /* followed by cr to end line */
3276: if ( ptype == 2 ) /* need max grayscale value */
3277: { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
3278: strcat(outline,outfield); /* add max value to header */
3279: strcat(outline,cr); } /* followed by cr to end line */
3280: /* --- write header to file or memory buffer --- */
3281: if ( fp == NULL ) /* if we have no open file... */
3282: strcat(file,outline); /* add header to caller's buffer */
3283: else /* or if we have an open file... */
3284: if ( fputs(outline,fp) /* try writing header to open file */
3285: == EOF ) goto end_of_job; /* return with error if failed */
3286: nbytes += strlen(outline); /* bump output byte count */
3287: /* -------------------------------------------------------------------------
3288: format and write pixels
3289: -------------------------------------------------------------------------- */
3290: *outline = '\000'; /* initialize line buffer */
3291: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
3292: for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
3293: /* --- format value at irow,jcol--- */
3294: *outfield = '\000'; /* init empty field */
3295: if ( irow < rp->height ) { /* check row index */
3296: int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
3297: if ( ptype == 1 ) /* pixval must be 1 or 0 */
3298: pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
3299: sprintf(outfield,"%d ",pixval); } /* format pixel value */
3300: /* --- write line if this value won't fit on it (or last line) --- */
3301: if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
3302: || irow >= rp->height ) { /* force writing last line */
3303: strcat(outline,cr); /* add cr to end current line */
3304: if ( fp == NULL ) /* if we have no open file... */
3305: strcat(file,outline); /* add header to caller's buffer */
3306: else /* or if we have an open file... */
3307: if ( fputs(outline,fp) /* try writing header to open file */
3308: == EOF ) goto end_of_job; /* return with error if failed */
3309: nbytes += strlen(outline); /* bump output byte count */
3310: *outline = '\000'; /* re-initialize line buffer */
3311: } /* --- end-of-if(strlen>=maxlinelen) --- */
3312: if ( irow >= rp->height ) break; /* done after writing last line */
3313: /* --- concatanate value to line -- */
3314: strcat(outline,outfield); /* concatanate value to line */
3315: } /* --- end-of-for(jcol,irow) --- */
3316: isokay = 1; /* signal successful completion */
3317: /* -------------------------------------------------------------------------
3318: Back to caller with total #bytes written, or 0=failed.
3319: -------------------------------------------------------------------------- */
3320: end_of_job:
3321: if ( fp != NULL /* output written to an open file */
3322: && fp != stdout ) /* and it's not just stdout */
3323: fclose(fp); /* so close file before returning */
3324: return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
3325: } /* --- end-of-function type_pbmpgm() --- */
3326:
3327:
3328: /* ==========================================================================
1.1 albertel 3329: * Function: cstruct_chardef ( cp, fp, col1 )
3330: * Purpose: Emit a C struct of cp on fp, starting in col1.
3331: * --------------------------------------------------------------------------
3332: * Arguments: cp (I) ptr to chardef struct for which
3333: * a C struct is to be generated.
3334: * fp (I) File ptr to output device (defaults to
3335: * stdout if passed as NULL).
3336: * col1 (I) int containing 0...65; output lines
3337: * are preceded by col1 blanks.
3338: * --------------------------------------------------------------------------
3339: * Returns: ( int ) 1 if completed successfully,
3340: * or 0 otherwise (for any error).
3341: * --------------------------------------------------------------------------
3342: * Notes:
3343: * ======================================================================= */
3344: /* --- entry point --- */
3345: int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
3346: {
3347: /* -------------------------------------------------------------------------
3348: Allocations and Declarations
3349: -------------------------------------------------------------------------- */
3350: char field[64]; /* field within output line */
3351: int cstruct_raster(), /* emit a raster */
3352: emit_string(); /* emit a string and comment */
3353: /* -------------------------------------------------------------------------
3354: emit charnum, location, name / hirow, hicol, lorow, locol
3355: -------------------------------------------------------------------------- */
3356: /* --- charnum, location, name --- */
3357: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
3358: emit_string ( fp, col1, field, "character number, location");
3359: /* --- toprow, topleftcol, botrow, botleftcol --- */
1.2 albertel 3360: sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
1.1 albertel 3361: cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
3362: cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
3363: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
3364: /* -------------------------------------------------------------------------
3365: emit raster and chardef's closing brace, and then return to caller
3366: -------------------------------------------------------------------------- */
3367: cstruct_raster(&cp->image,fp,col1+4); /* emit raster */
3368: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3369: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3370: } /* --- end-of-function cstruct_chardef() --- */
3371:
3372:
3373: /* ==========================================================================
3374: * Function: cstruct_raster ( rp, fp, col1 )
3375: * Purpose: Emit a C struct of rp on fp, starting in col1.
3376: * --------------------------------------------------------------------------
3377: * Arguments: rp (I) ptr to raster struct for which
3378: * a C struct is to be generated.
3379: * fp (I) File ptr to output device (defaults to
3380: * stdout if passed as NULL).
3381: * col1 (I) int containing 0...65; output lines
3382: * are preceded by col1 blanks.
3383: * --------------------------------------------------------------------------
3384: * Returns: ( int ) 1 if completed successfully,
3385: * or 0 otherwise (for any error).
3386: * --------------------------------------------------------------------------
3387: * Notes:
3388: * ======================================================================= */
3389: /* --- entry point --- */
3390: int cstruct_raster ( raster *rp, FILE *fp, int col1 )
3391: {
3392: /* -------------------------------------------------------------------------
3393: Allocations and Declarations
3394: -------------------------------------------------------------------------- */
3395: char field[64]; /* field within output line */
3396: char typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
3397: int hex_bitmap(); /* to emit raster bitmap */
3398: int emit_string(); /* emit a string and comment */
3399: /* -------------------------------------------------------------------------
3400: emit width and height
3401: -------------------------------------------------------------------------- */
1.2 albertel 3402: sprintf(field,"{ %2d, %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
3403: rp->width,rp->height,rp->format,rp->pixsz,typecast);
3404: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
1.1 albertel 3405: /* -------------------------------------------------------------------------
3406: emit bitmap and closing brace, and return to caller
3407: -------------------------------------------------------------------------- */
3408: hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */
3409: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3410: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3411: } /* --- end-of-function cstruct_raster() --- */
3412:
3413:
3414: /* ==========================================================================
3415: * Function: hex_bitmap ( rp, fp, col1, isstr )
3416: * Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1.
3417: * If isstr (is string) is true, the dump is of the form
3418: * "\x01\x02\x03\x04\x05..."
3419: * Otherwise, if isstr is false, the dump is of the form
3420: * 0x01,0x02,0x03,0x04,0x05...
3421: * --------------------------------------------------------------------------
3422: * Arguments: rp (I) ptr to raster struct for which
3423: * a hex dump is to be constructed.
3424: * fp (I) File ptr to output device (defaults to
3425: * stdout if passed as NULL).
3426: * col1 (I) int containing 0...65; output lines
3427: * are preceded by col1 blanks.
3428: * isstr (I) int specifying dump format as described above
3429: * --------------------------------------------------------------------------
3430: * Returns: ( int ) 1 if completed successfully,
3431: * or 0 otherwise (for any error).
3432: * --------------------------------------------------------------------------
3433: * Notes:
3434: * ======================================================================= */
3435: /* --- entry point --- */
3436: int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
3437: {
3438: /* -------------------------------------------------------------------------
3439: Allocations and Declarations
3440: -------------------------------------------------------------------------- */
1.2 albertel 3441: int ibyte, /* pixmap[ibyte] index */
3442: nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
1.1 albertel 3443: char stub[64]=" ";/* col1 leading blanks */
3444: int linewidth = 64, /* (roughly) rightmost column */
3445: colwidth = (isstr? 4:5); /* #cols required for each byte */
3446: int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
3447: /* --------------------------------------------------------------------------
3448: initialization
3449: -------------------------------------------------------------------------- */
3450: /* --- redirect null fp --- */
3451: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3452: /* --- emit initial stub if wanted --- */
3453: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
3454: /* --------------------------------------------------------------------------
3455: emit hex dump of rp->bitmap image
3456: -------------------------------------------------------------------------- */
3457: if ( isstr ) fprintf(fp,"\""); /* opening " before first line */
3458: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* one byte at a time */
3459: {
3460: /* --- display a byte as hex char or number, depending on isstr --- */
3461: if ( isstr ) /* string format wanted */
3462: fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]); /*print byte as hex char*/
3463: else /* comma-separated format wanted */
3464: fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/
3465: /* --- add a separator and newline, etc, as necessary --- */
3466: if ( ibyte < nbytes-1) /* not the last byte yet */
3467: {
3468: if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
1.3 ! albertel 3469: if ( (ibyte+1)%ncols==0 ) { /* need new line after every ncols */
1.1 albertel 3470: if ( !isstr ) /* for hex numbers format ... */
3471: fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
3472: else /* for string format... */
1.3 ! albertel 3473: fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
1.1 albertel 3474: } /* --- end-of-if(ibyte<nbytes-1) --- */
3475: } /* --- end-of-for(ibyte) --- */
3476: if ( isstr ) fprintf(fp,"\""); /* closing " after last line */
3477: return ( 1 ); /* back with 1=okay, 0=failed */
3478: } /* --- end-of-function hex_bitmap() --- */
3479:
3480:
3481: /* ==========================================================================
3482: * Function: emit_string ( fp, col1, string, comment )
3483: * Purpose: Emit string on fp, starting in col1,
3484: * and followed by right-justified comment.
3485: * --------------------------------------------------------------------------
3486: * Arguments: fp (I) File ptr to output device (defaults to
3487: * stdout if passed as NULL).
3488: * col1 (I) int containing 0 or #blanks preceding string
3489: * string (I) char * containing string to be emitted.
3490: * If last char of string is '\n',
3491: * the emitted line ends with a newline,
3492: * otherwise not.
3493: * comment (I) NULL or char * containing right-justified
3494: * comment (we enclose between /star and star/)
3495: * --------------------------------------------------------------------------
3496: * Returns: ( int ) 1 if completed successfully,
3497: * or 0 otherwise (for any error).
3498: * --------------------------------------------------------------------------
3499: * Notes: o
3500: * ======================================================================= */
3501: /* --- entry point --- */
3502: int emit_string ( FILE *fp, int col1, char *string, char *comment )
3503: {
3504: /* -------------------------------------------------------------------------
3505: Allocations and Declarations
3506: -------------------------------------------------------------------------- */
3507: char line[256]; /* construct line with caller's fields */
3508: int fieldlen; /* #chars in one of caller's fields */
3509: int linelen = 72; /*line length (for right-justified comment)*/
3510: int isnewline = 0; /* true to emit \n at end of line */
3511: /* --------------------------------------------------------------------------
3512: construct line containing prolog, string, epilog, and finally comment
3513: -------------------------------------------------------------------------- */
3514: /* --- init line --- */
3515: memset(line,' ',255); /* start line with blanks */
3516: /* --- embed string into line --- */
3517: if ( string != NULL ) /* if caller gave us a string... */
3518: { fieldlen = strlen(string); /* #cols required for string */
3519: if ( string[fieldlen-1] == '\n' ) /* check last char for newline */
3520: { isnewline = 1; /* got it, so set flag */
3521: fieldlen--; } /* but don't print it yet */
3522: memcpy(line+col1,string,fieldlen); /* embid string starting at col1 */
3523: col1 += fieldlen; } /* bump col past epilog */
3524: /* --- embed comment into line --- */
3525: if ( comment != NULL ) /* if caller gave us a comment... */
3526: { fieldlen = 6 + strlen(comment); /* plus /star, star/, 2 spaces */
3527: if ( linelen-fieldlen < col1 ) /* comment won't fit */
3528: fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
3529: if ( fieldlen > 6 ) /* can fit all or part of comment */
3530: sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
3531: '*', fieldlen-6,comment, '*');
3532: col1 = linelen; } /* indicate line filled */
3533: /* --- line completed --- */
3534: line[col1] = '\000'; /* null-terminate completed line */
3535: /* -------------------------------------------------------------------------
3536: emit line, then back to caller with 1=okay, 0=failed.
3537: -------------------------------------------------------------------------- */
3538: /* --- first redirect null fp --- */
3539: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3540: /* --- emit line (and optional newline) --- */
3541: fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */
3542: if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/
3543: return ( 1 );
3544: } /* --- end-of-function emit_string() --- */
3545:
3546:
3547: /* ==========================================================================
1.2 albertel 3548: * Function: gftobitmap ( gf )
3549: * Purpose: convert .gf-like pixmap to bitmap image
3550: * --------------------------------------------------------------------------
3551: * Arguments: gf (I) raster * to struct in .gf-format
3552: * --------------------------------------------------------------------------
3553: * Returns: ( raster * ) image-format raster * if successful,
3554: * or NULL for any error.
3555: * --------------------------------------------------------------------------
3556: * Notes: o
3557: * ======================================================================= */
3558: /* --- entry point --- */
3559: raster *gftobitmap ( raster *gf )
3560: {
3561: /* -------------------------------------------------------------------------
3562: Allocations and Declarations
3563: -------------------------------------------------------------------------- */
3564: raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
3565: int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
3566: int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
3567: ibit=0, bitval=0; /* bitmap index, bit value */
3568: int isrepeat = 1, /* true to process repeat counts */
3569: repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
3570: nrepeats=0, irepeat=0, /* scan line repeat count,index */
3571: wbits = 0; /* count bits to width of scan line*/
3572: /* -------------------------------------------------------------------------
3573: initialization
3574: -------------------------------------------------------------------------- */
3575: /* --- check args --- */
3576: if ( gf == NULL ) goto end_of_job; /* input raster not provided */
3577: format = gf->format; /* 2 or 3 */
3578: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
3579: ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
3580: /* --- allocate output raster with proper dimensions for bitmap --- */
3581: width=gf->width; height=gf->height; /* dimensions of raster */
3582: if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
3583: == NULL ) goto end_of_job; /* quit if failed to allocate */
3584: totbits = width*height; /* total #bits in image */
3585: /* -------------------------------------------------------------------------
3586: fill bitmap
3587: -------------------------------------------------------------------------- */
3588: for ( icount=0,bitval=0; icount<ncounts; icount++ )
3589: {
3590: int nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
3591: if ( isrepeat /* we're proxessing repeat counts */
1.3 ! albertel 3592: && nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
1.2 albertel 3593: if ( nrepeats == 0 ) /* recursive repeat is error */
3594: { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
3595: nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
3596: icount += 2; } /* bump byte/nibble count */
3597: else /* some internal error occurred */
3598: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
1.3 ! albertel 3599: fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
1.2 albertel 3600: if ( 0 )
3601: fprintf(stdout,
3602: "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
3603: icount,bitval,nbits,ibit,totbits);
3604: for ( ; nbits>0; nbits-- ) /* count down */
3605: { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
3606: for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
3607: if ( bitval == 1 ) /* set pixel */
3608: { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
3609: else /* clear pixel */
3610: { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
3611: if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
3612: ibit++; } /* bump bit index */
3613: bitval = 1-bitval; /* flip bit value */
3614: if ( wbits >= width ) { /* completed repeats */
3615: ibit += nrepeats*width; /*bump bit count past repeated scans*/
3616: if ( wbits > width ) /* out-of alignment error */
3617: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
3618: fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
3619: wbits = nrepeats = 0; } /* reset repeat counts */
3620: } /* --- end-of-for(icount) --- */
3621: end_of_job:
3622: return ( rp ); /* back to caller with image */
3623: } /* --- end-of-function gftobitmap() --- */
3624:
3625:
3626: /* ==========================================================================
1.1 albertel 3627: * Function: get_symdef ( symbol )
3628: * Purpose: returns mathchardef struct for symbol
3629: * --------------------------------------------------------------------------
3630: * Arguments: symbol (I) char * containing symbol
3631: * whose corresponding mathchardef is wanted
3632: * --------------------------------------------------------------------------
3633: * Returns: ( mathchardef * ) pointer to struct defining symbol,
3634: * or NULL for any error
3635: * --------------------------------------------------------------------------
3636: * Notes: o Input symbol need only contain a leading substring to match,
3637: * e.g., \gam passed in symbol will match \gamma in the table.
3638: * If the table contains two or more possible matches,
3639: * the shortest is returned, e.g., input \e will return with
3640: * data for \eta rather than \epsilon. To get \epsilon,
3641: * you must pass a leading substring long enough to eliminate
3642: * shorter table matches, i.e., in this case \ep
3643: * ======================================================================= */
3644: /* --- entry point --- */
3645: mathchardef *get_symdef ( char *symbol )
3646: {
3647: /* -------------------------------------------------------------------------
3648: Allocations and Declarations
3649: -------------------------------------------------------------------------- */
3650: mathchardef *symdefs = symtable; /* table of mathchardefs */
1.3 ! albertel 3651: int ligdef=0, get_ligature(); /* or we may have a ligature */
1.1 albertel 3652: int idef = 0, /* symdefs[] index */
3653: bestdef = (-9999); /*index of shortest matching symdef*/
3654: int symlen = strlen(symbol), /* length of input symbol */
3655: deflen, minlen=9999; /*length of shortest matching symdef*/
3656: int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
3657: alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
1.2 albertel 3658: int family = fontinfo[fontnum].family; /* current font family */
1.1 albertel 3659: static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
1.2 albertel 3660: /* --- see table on page 536 in TLC2 --- */
1.1 albertel 3661: {"\\int", "\\Bigint"},
3662: {"\\oint", "\\Bigoint"},
3663: {"\\sum", "\\Bigsum"},
3664: {"\\prod", "\\Bigprod"},
3665: {"\\coprod", "\\Bigcoprod"},
1.2 albertel 3666: /* --- must be 'big' when related to similar binary operators --- */
3667: {"\\bigcup", "\\Bigcup"},
3668: {"\\bigsqcup", "\\Bigsqcup"},
3669: {"\\bigcap", "\\Bigcap"},
3670: /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
3671: {"\\bigodot", "\\Bigodot"},
3672: {"\\bigoplus", "\\Bigoplus"},
3673: {"\\bigominus", "\\ominus"},
3674: {"\\bigotimes", "\\Bigotimes"},
3675: {"\\bigoslash", "\\oslash"},
3676: {"\\biguplus", "\\Biguplus"},
3677: {"\\bigwedge", "\\Bigwedge"},
3678: {"\\bigvee", "\\Bigvee"},
1.1 albertel 3679: {NULL, NULL} };
3680: /* -------------------------------------------------------------------------
1.3 ! albertel 3681: First check for ligature
! 3682: -------------------------------------------------------------------------- */
! 3683: isligature = 0; /* init signal for no ligature */
! 3684: if ( family == CYR10 ) /*only check for cyrillic ligatures*/
! 3685: if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
! 3686: >= 0 ) /* found a ligature */
! 3687: { bestdef = ligdef; /* set bestdef for ligature */
! 3688: isligature = 1; /* signal we found a ligature */
! 3689: goto end_of_job; } /* so just give it to caller */
! 3690: /* -------------------------------------------------------------------------
1.1 albertel 3691: If in \displaystyle mode, first xlate int to Bigint, etc.
3692: -------------------------------------------------------------------------- */
3693: if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */
3694: for ( idef=0; ; idef++ ) { /* lookup symbol in displaysyms */
3695: char *fromsym = displaysyms[idef][0], /* look for this symbol */
3696: *tosym = displaysyms[idef][1]; /* and xlate it to this symbol */
3697: if ( fromsym == NULL ) break; /* end-of-table */
3698: if ( !strcmp(symbol,fromsym) ) /* found a match */
3699: { if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
3700: { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
3701: isdisplaystyle,symbol,tosym); fflush(msgfp); }
3702: symbol = tosym; /* so look up tosym instead */
3703: symlen = strlen(symbol); /* reset symbol length */
3704: break; } /* no need to search further */
3705: } /* --- end-of-for(idef) --- */
3706: /* -------------------------------------------------------------------------
3707: search symdefs[] in order for first occurrence of symbol
3708: -------------------------------------------------------------------------- */
3709: for ( idef=0; ;idef++ ) /* until trailer record found */
3710: if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
3711: else /* check against caller's symbol */
3712: if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
1.3 ! albertel 3713: if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */
1.2 albertel 3714: || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
3715: || symdefs[idef].handler!=NULL)) /* or text mode and directive */
3716: || (symdefs[idef].family==family /* have correct family */
3717: && symdefs[idef].handler==NULL) ) /* and not a handler collision */
3718: #if 0
3719: || (fontnum==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
3720: || (fontnum==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/
3721: || (fontnum==3 && symdefs[idef].family==BBOLD10 /*textmode && bb text*/
3722: && symdefs[idef].handler==NULL)
3723: || (fontnum==4 && symdefs[idef].family==CMMIB10 /*textmode && bf text*/
3724: && symdefs[idef].handler==NULL) )
3725: #endif
1.1 albertel 3726: if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
3727: { bestdef = idef; /* save index of new best match */
3728: if ( (minlen = deflen) /* and save its len for next test */
3729: == symlen ) break; } /*perfect match, so return with it*/
3730: if ( bestdef < 0 ) /* failed to look up symbol */
1.2 albertel 3731: if ( fontnum != 0 ) /* we're in a restricted font mode */
3732: { int oldfontnum = fontnum; /* save current font family */
3733: mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
3734: fontnum = 0; /*try to look up symbol in any font*/
3735: symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
3736: fontnum = oldfontnum; /* reset font family */
3737: return symdef; } /* caller gets fontnum=0 lookup */
1.3 ! albertel 3738: end_of_job:
! 3739: if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
! 3740: { fprintf(msgfp,
! 3741: "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
! 3742: symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
1.1 albertel 3743: fflush(msgfp); }
1.3 ! albertel 3744: return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
1.1 albertel 3745: } /* --- end-of-function get_symdef() --- */
3746:
3747:
3748: /* ==========================================================================
1.3 ! albertel 3749: * Function: get_ligature ( expression, family )
! 3750: * Purpose: returns symtable[] index for ligature
! 3751: * --------------------------------------------------------------------------
! 3752: * Arguments: expression (I) char * containing ligature
! 3753: * whose corresponding mathchardef is wanted
! 3754: * family (I) int containing NOVALUE for any family,
! 3755: * or, e.g., CYR10 for cyrillic, etc.
! 3756: * --------------------------------------------------------------------------
! 3757: * Returns: ( int ) symtable[] index defining ligature,
! 3758: * or -9999 if no ligature found or for any error
! 3759: * --------------------------------------------------------------------------
! 3760: * Notes: o
! 3761: * ======================================================================= */
! 3762: /* --- entry point --- */
! 3763: int get_ligature ( char *expression, int family )
! 3764: {
! 3765: /* -------------------------------------------------------------------------
! 3766: Allocations and Declarations
! 3767: -------------------------------------------------------------------------- */
! 3768: mathchardef *symdefs = symtable; /* table of mathchardefs */
! 3769: char *ligature = expression /*- 1*/, /* expression ptr */
! 3770: *symbol = NULL; /* symdefs[idef].symbol */
! 3771: int liglen = strlen(ligature); /* #chars remaining in expression */
! 3772: int iscyrfam = (family==CYR10); /* true for cyrillic families */
! 3773: int idef = 0, /* symdefs[] index */
! 3774: bestdef = (-9999), /*index of longest matching symdef*/
! 3775: maxlen=(-9999); /*length of longest matching symdef*/
! 3776: /* -------------------------------------------------------------------------
! 3777: search symdefs[] in order for first occurrence of symbol
! 3778: -------------------------------------------------------------------------- */
! 3779: if ( !isstring ) { /* no ligatures in "string" mode */
! 3780: for ( idef=0; ;idef++ ) /* until trailer record found */
! 3781: if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
! 3782: else { /* check against caller's ligature */
! 3783: int symlen = strlen(symbol); /* #chars in symbol */
! 3784: if ( ( symlen>1 || iscyrfam ) /*ligature >1 char long or cyrillic*/
! 3785: && symlen <= liglen /* and enough remaining chars */
! 3786: && ( *symbol!='\\' || iscyrfam ) /* not escaped or cyrillic */
! 3787: && symdefs[idef].handler == NULL ) /* and not a handler */
! 3788: if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
! 3789: if ( family < 0 /* no family specifies */
! 3790: || symdefs[idef].family == family ) /* or have correct family */
! 3791: if ( symlen > maxlen ) /* new longest ligature */
! 3792: { bestdef = idef; /* save index of new best match */
! 3793: maxlen = symlen; } /* and save its len for next test */
! 3794: } /* --- end-of-if/else(symbol==NULL) --- */
! 3795: if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
! 3796: { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
! 3797: ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
! 3798: fflush(msgfp); }
! 3799: } /* --- end-of-if(!isstring) --- */
! 3800: return ( bestdef ); /* -9999 or index of best symdef[] */
! 3801: } /* --- end-of-function get_ligature --- */
! 3802:
! 3803:
! 3804: /* ==========================================================================
1.1 albertel 3805: * Function: get_chardef ( symdef, size )
3806: * Purpose: returns chardef ptr containing data for symdef at given size
3807: * --------------------------------------------------------------------------
3808: * Arguments: symdef (I) mathchardef * corresponding to symbol
3809: * whose corresponding chardef is wanted
1.2 albertel 3810: * size (I) int containing 0-5 for desired size
1.1 albertel 3811: * --------------------------------------------------------------------------
3812: * Returns: ( chardef * ) pointer to struct defining symbol at size,
3813: * or NULL for any error
3814: * --------------------------------------------------------------------------
3815: * Notes: o if size unavailable, the next-closer-to-normalsize
3816: * is returned instead.
3817: * ======================================================================= */
3818: /* --- entry point --- */
3819: chardef *get_chardef ( mathchardef *symdef, int size )
3820: {
3821: /* -------------------------------------------------------------------------
3822: Allocations and Declarations
3823: -------------------------------------------------------------------------- */
3824: fontfamily *fonts = fonttable; /* table of font families */
3825: chardef **fontdef, /*tables for desired font, by size*/
3826: *gfdata = (chardef *)NULL; /* chardef for symdef,size */
3827: int ifont; /* fonts[] index */
3828: int family, charnum; /* indexes retrieved from symdef */
3829: int sizeinc = 0, /*+1 or -1 to get closer to normal*/
3830: normalsize = 2; /* this size always present */
3831: int isBig = 0; /*true if symbol's 1st char is upper*/
3832: char *symptr = NULL; /* look for 1st alpha of symbol */
3833: /* -------------------------------------------------------------------------
3834: initialization
3835: -------------------------------------------------------------------------- */
3836: /* --- check symdef --- */
1.3 ! albertel 3837: if ( symdef == NULL ) goto end_of_job; /* get_symdef() probably failed */
1.1 albertel 3838: /* --- get local copy of indexes from symdef --- */
3839: family = symdef->family; /* font family containing symbol */
3840: charnum = symdef->charnum; /* char# of symbol within font */
3841: /* --- check for supersampling --- */
3842: if ( issupersampling ) /* check for supersampling fonts */
3843: if ( fonts != ssfonttable ) /* uh oh--probably internal error */
3844: { fonts = ssfonttable; } /* force it */
3845: /* --- check requested size, and set size increment --- */
3846: if ( 0 && issupersampling ) /* set size index for supersampling */
3847: size = LARGESTSIZE+1; /* index 1 past largest size */
3848: else /* low pass indexes 0...LARGESTSIZE */
3849: {
3850: if( size<0 ) size = 0; /* size was definitely too small */
3851: if( size>LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */
3852: if( size<normalsize ) sizeinc = (+1); /*use next larger if size too small*/
3853: if( size>normalsize ) sizeinc = (-1); /*or next smaller if size too large*/
3854: }
3855: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
3856: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
3857: if ( isalpha(*symptr) ) /* found leading alpha char */
3858: { isBig = isupper(*symptr); /* is 1st char of name uppercase? */
3859: if ( !isBig /* 1st char lowercase */
3860: && strlen(symptr) >= 4 ) /* but followed by at least 3 chars */
3861: isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
3862: || !memcmp(symptr,"bigg",4); /* or with bigg */
3863: break; } /* don't check beyond 1st char */
3864: /* -------------------------------------------------------------------------
3865: find font family in table of fonts[]
3866: -------------------------------------------------------------------------- */
3867: /* --- look up font family --- */
3868: for ( ifont=0; ;ifont++ ) /* until trailer record found */
1.3 ! albertel 3869: if ( fonts[ifont].family < 0 ) { /* error, no such family */
! 3870: if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
! 3871: fprintf(msgfp,"get_chardef> failed to find font family %d\n",
! 3872: family); fflush(msgfp); }
! 3873: goto end_of_job; } /* quit if can't find font family*/
1.1 albertel 3874: else if ( fonts[ifont].family == family ) break; /* found font family */
3875: /* --- get local copy of table for this family by size --- */
3876: fontdef = fonts[ifont].fontdef; /* font by size */
3877: /* -------------------------------------------------------------------------
3878: get font in desired size, or closest available size, and return symbol
3879: -------------------------------------------------------------------------- */
3880: /* --- get font in desired size --- */
3881: while ( 1 ) /* find size or closest available */
3882: if ( fontdef[size] != NULL ) break; /* found available size */
3883: else /* adjust size closer to normal */
3884: if ( size == NORMALSIZE /* already normal so no more sizes,*/
1.3 ! albertel 3885: || sizeinc == 0 ) { /* or must be supersampling */
! 3886: if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
! 3887: fprintf(msgfp,"get_chardef> failed to find font size %d\n",
! 3888: size); fflush(msgfp); }
! 3889: goto end_of_job; } /* quit if can't find desired size */
1.1 albertel 3890: else /*bump size 1 closer to NORMALSIZE*/
3891: size += sizeinc; /* see if adjusted size available */
3892: /* --- ptr to chardef struct --- */
3893: gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/
3894: /* -------------------------------------------------------------------------
3895: kludge to tweak CMEX10 (which appears to have incorrect descenders)
3896: -------------------------------------------------------------------------- */
3897: if ( family == CMEX10 ) /* cmex10 needs tweak */
3898: { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
3899: gfdata->botrow = (isBig? (-height/3) : (-height/4));
3900: gfdata->toprow = gfdata->botrow + gfdata->image.height; }
3901: /* -------------------------------------------------------------------------
3902: return subraster containing chardef data for symbol in requested size
3903: -------------------------------------------------------------------------- */
1.3 ! albertel 3904: end_of_job:
! 3905: if ( msgfp!=NULL && msglevel>=999 )
! 3906: { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
! 3907: else
! 3908: fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d %s\n",
! 3909: symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
! 3910: fflush(msgfp); }
! 3911: return ( gfdata ); /*ptr to chardef for symbol in size*/
1.1 albertel 3912: } /* --- end-of-function get_chardef() --- */
3913:
3914:
3915: /* ==========================================================================
3916: * Function: get_charsubraster ( symdef, size )
3917: * Purpose: returns new subraster ptr containing
3918: * data for symdef at given size
3919: * --------------------------------------------------------------------------
1.2 albertel 3920: * Arguments: symdef (I) mathchardef * corresponding to symbol whose
3921: * corresponding chardef subraster is wanted
3922: * size (I) int containing 0-5 for desired size
1.1 albertel 3923: * --------------------------------------------------------------------------
3924: * Returns: ( subraster * ) pointer to struct defining symbol at size,
3925: * or NULL for any error
3926: * --------------------------------------------------------------------------
3927: * Notes: o just wraps a subraster envelope around get_chardef()
3928: * ======================================================================= */
3929: /* --- entry point --- */
3930: subraster *get_charsubraster ( mathchardef *symdef, int size )
3931: {
3932: /* -------------------------------------------------------------------------
3933: Allocations and Declarations
3934: -------------------------------------------------------------------------- */
3935: chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
3936: int get_baseline(); /* baseline of gfdata */
3937: subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
1.2 albertel 3938: raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
3939: int delete_subraster(); /* in case gftobitmap() fails */
1.1 albertel 3940: int aasupsamp(), /*antialias char with supersampling*/
3941: grayscale=256; /* aasupersamp() parameters */
3942: /* -------------------------------------------------------------------------
3943: look up chardef for symdef at size, and embed data (gfdata) in subraster
3944: -------------------------------------------------------------------------- */
3945: if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */
3946: != NULL ) /* and check that we found it */
3947: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
3948: != NULL ) /* and check that we succeeded */
3949: {
1.2 albertel 3950: raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
3951: int format = image->format; /* 1=bitmap, else .gf */
1.1 albertel 3952: sp->symdef = symdef; /* replace NULL with caller's arg */
3953: sp->size = size; /*replace default with caller's size*/
3954: sp->baseline = get_baseline(gfdata); /* get baseline of character */
1.2 albertel 3955: if ( format == 1 ) /* already a bitmap */
3956: { sp->type = CHARASTER; /* static char raster */
3957: sp->image = image; } /* store ptr to its bitmap */
3958: else /* need to convert .gf-to-bitmap */
3959: if ( (bitmaprp = gftobitmap(image)) /* convert */
3960: != (raster *)NULL ) /* successful */
3961: { sp->type = IMAGERASTER; /* allocated raster will be freed */
3962: sp->image = bitmaprp; } /* store ptr to converted bitmap */
3963: else /* conversion failed */
3964: { delete_subraster(sp); /* free unneeded subraster */
3965: sp = (subraster *)NULL; /* signal error to caller */
3966: goto end_of_job; } /* quit */
1.1 albertel 3967: if ( issupersampling ) /* antialias character right here */
3968: {
3969: raster *aa = NULL; /* antialiased char raster */
3970: int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
3971: if ( status ) /* supersampled successfully */
3972: { int baseline = sp->baseline; /* baseline before supersampling */
3973: int height = gfdata->image.height; /* #rows before supersampling */
3974: sp->image = aa; /* replace chardef with ss image */
3975: if ( baseline >= height-1 ) /* baseline at bottom of char */
3976: sp->baseline = aa->height -1; /* so keep it at bottom */
3977: else /* char has descenders */
3978: sp->baseline /= shrinkfactor; /* rescale baseline */
3979: sp->type = IMAGERASTER; } /* character is an image raster */
3980: } /* --- end-of-if(issupersampling) --- */
3981: } /* --- end-of-if(sp!=NULL) --- */
1.2 albertel 3982: end_of_job:
3983: if ( msgfp!=NULL && msglevel>=999 )
1.3 ! albertel 3984: { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
! 3985: " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
! 3986: (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
! 3987: fflush(msgfp); }
1.1 albertel 3988: return ( sp ); /* back to caller */
3989: } /* --- end-of-function get_charsubraster() --- */
3990:
3991:
3992: /* ==========================================================================
1.2 albertel 3993: * Function: get_symsubraster ( symbol, size )
3994: * Purpose: returns new subraster ptr containing
3995: * data for symbol at given size
3996: * --------------------------------------------------------------------------
3997: * Arguments: symbol (I) char * corresponding to symbol
3998: * whose corresponding subraster is wanted
3999: * size (I) int containing 0-5 for desired size
4000: * --------------------------------------------------------------------------
4001: * Returns: ( subraster * ) pointer to struct defining symbol at size,
4002: * or NULL for any error
4003: * --------------------------------------------------------------------------
4004: * Notes: o just combines get_symdef() and get_charsubraster()
4005: * ======================================================================= */
4006: /* --- entry point --- */
4007: subraster *get_symsubraster ( char *symbol, int size )
4008: {
4009: /* -------------------------------------------------------------------------
4010: Allocations and Declarations
4011: -------------------------------------------------------------------------- */
4012: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
4013: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
4014: /* -------------------------------------------------------------------------
4015: look up mathchardef for symbol
4016: -------------------------------------------------------------------------- */
4017: if ( symbol != NULL ) /* user supplied input symbol */
4018: symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
4019: /* -------------------------------------------------------------------------
4020: look up chardef for mathchardef and wrap a subraster structure around data
4021: -------------------------------------------------------------------------- */
4022: if ( symdef != NULL ) /* lookup succeeded */
4023: sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
4024: return ( sp ); /* back to caller with sp or NULL */
4025: } /* --- end-of-function get_symsubraster() --- */
4026:
4027:
4028: /* ==========================================================================
1.1 albertel 4029: * Function: get_baseline ( gfdata )
4030: * Purpose: returns baseline for a chardef struct
4031: * --------------------------------------------------------------------------
4032: * Arguments: gfdata (I) chardef * containing chardef for symbol
4033: * whose baseline is wanted
4034: * --------------------------------------------------------------------------
4035: * Returns: ( int ) baseline for symdef,
4036: * or -1 for any error
4037: * --------------------------------------------------------------------------
4038: * Notes: o Unlike TeX, the top-left corners of our rasters are (0,0),
4039: * with (row,col) increasing as you move down and right.
4040: * Baselines are calculated with respect to this scheme,
4041: * so 0 would mean the very top row is on the baseline
4042: * and everything else descends below the baseline.
4043: * ======================================================================= */
4044: /* --- entry point --- */
4045: int get_baseline ( chardef *gfdata )
4046: {
4047: /* -------------------------------------------------------------------------
4048: Allocations and Declarations
4049: -------------------------------------------------------------------------- */
4050: int /*toprow = gfdata->toprow,*/ /*TeX top row from .gf file info*/
4051: botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/
4052: height = gfdata->image.height; /* #rows comprising symbol */
4053: /* -------------------------------------------------------------------------
4054: give caller baseline
4055: -------------------------------------------------------------------------- */
4056: return ( (height-1) + botrow ); /* note: descenders have botrow<0 */
4057: } /* --- end-of-function get_baseline() --- */
4058:
4059:
4060: /* ==========================================================================
4061: * Function: get_delim ( char *symbol, int height, int family )
4062: * Purpose: returns subraster corresponding to the samllest
4063: * character containing symbol, but at least as large as height,
4064: * and in caller's family (if specified).
4065: * If no symbol character as large as height is available,
4066: * then the largest availabale character is returned instead.
4067: * --------------------------------------------------------------------------
4068: * Arguments: symbol (I) char * containing (substring of) desired
4069: * symbol, e.g., if symbol="(", then any
4070: * mathchardef like "(" or "\\(", etc, match.
4071: * height (I) int containing minimum acceptable height
4072: * for returned character
4073: * family (I) int containing -1 to consider all families,
4074: * or, e.g., CMEX10 for only that family
4075: * --------------------------------------------------------------------------
4076: * Returns: ( subraster * ) best matching character available,
4077: * or NULL for any error
4078: * --------------------------------------------------------------------------
4079: * Notes: o If height is passed as negative, its absolute value is used
4080: * but the best-fit width is searched for (rather than height)
4081: * ======================================================================= */
4082: /* --- entry point --- */
4083: subraster *get_delim ( char *symbol, int height, int family )
4084: {
4085: /* -------------------------------------------------------------------------
4086: Allocations and Declarations
4087: -------------------------------------------------------------------------- */
4088: mathchardef *symdefs = symtable; /* table of mathchardefs */
4089: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
4090: subraster *make_delim(); /* construct delim if can't find it*/
4091: chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */
4092: char lcsymbol[256], *symptr, /* lowercase symbol for comparison */
4093: *unescsymbol = symbol; /* unescaped symbol */
4094: int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
4095: deflen = 0; /* length of symdef (aka lcsymbol) */
4096: int idef = 0, /* symdefs[] index */
4097: bestdef = (-9999), /* index of best fit symdef */
4098: bigdef = (-9999); /*index of biggest (in case no best)*/
4099: int size = 0, /* size index 0...LARGESTSIZE */
4100: bestsize = (-9999), /* index of best fit size */
4101: bigsize = (-9999); /*index of biggest (in case no best)*/
4102: int defheight, bestheight=9999, /* height of best fit symdef */
4103: bigheight = (-9999); /*height of biggest(in case no best)*/
4104: int iswidth = 0; /* true if best-fit width desired */
4105: int isunesc = 0, /* true if leading escape removed */
4106: issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */
1.3 ! albertel 4107: int iscurly = 0; /* true for StMary's curly symbols */
1.1 albertel 4108: char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
4109: /* -------------------------------------------------------------------------
4110: determine if searching height or width, and search symdefs[] for best-fit
4111: -------------------------------------------------------------------------- */
4112: /* --- arg checks --- */
4113: if ( symlen < 1 ) return (sp); /* no input symbol suplied */
4114: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
1.3 ! albertel 4115: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
1.1 albertel 4116: /* --- ignore leading escapes for CMEX10 --- */
4117: if ( 1 ) /* ignore leading escape */
4118: if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
4119: if ( strstr(symbol,"sq") != NULL ) /* \sq symbol requested */
4120: issq = 1; /* seq \sq signal */
4121: if ( strstr(symbol,"oint") != NULL ) /* \oint symbol requested */
4122: isoint = 1; /* seq \oint signal */
4123: if ( *symbol=='\\' ) /* have leading \ */
4124: { unescsymbol = symbol+1; /* push past leading \ */
4125: if ( --symlen < 1 ) return(sp); /* one less char */
4126: if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
4127: unescsymbol = bigint; /* but big version looks better */
4128: if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
4129: unescsymbol = bigoint; /* but big version looks better */
4130: symlen = strlen(unescsymbol); /* explicitly recalculate length */
4131: isunesc = 1; } /* signal leading escape removed */
4132: } /* --- end-of-if(family) --- */
4133: /* --- determine whether searching for best-fit height or width --- */
4134: if ( height < 0 ) /* negative signals width search */
4135: { height = (-height); /* flip "height" positive */
4136: iswidth = 1; } /* set flag for width search */
4137: /* --- search symdefs[] for best-fit height (or width) --- */
4138: for ( idef=0; ;idef++ ) /* until trailer record found */
4139: {
4140: char *defsym = symdefs[idef].symbol; /* local copies */
4141: int deffam = symdefs[idef].family;
4142: if ( defsym == NULL ) break; /* reached end-of-table */
4143: else /* check against caller's symbol */
4144: if ( family<0 || deffam == family /* if explicitly in caller's family*/
1.2 albertel 4145: || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
1.1 albertel 4146: {
4147: strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
4148: if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
4149: strcpy(lcsymbol,lcsymbol+1); /* so squeeze it out of lcsymbol too*/
4150: if ( 0 ) /* don't ignore case */
4151: for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
4152: if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
4153: deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
4154: if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
4155: if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
4156: && (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
1.3 ! albertel 4157: if ( ( deffam == CMSY10 ? /* CMSY10 or not CMSY10 */
1.1 albertel 4158: symptr == lcsymbol /* caller's sym is a prefix */
4159: && deflen == symlen: /* and same length */
1.3 ! albertel 4160: (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
! 4161: (symptr == lcsymbol /* caller's sym is a prefix */
! 4162: || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
1.1 albertel 4163: for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
4164: if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
4165: { defheight = gfdata->image.height; /* height of this character */
4166: if ( iswidth ) /* width search wanted instead... */
4167: defheight = gfdata->image.width; /* ...so substitute width */
4168: leftsymdef = &(symdefs[idef]); /* set symbol class, etc */
4169: if ( defheight>=height && defheight<bestheight ) /*new best fit*/
4170: { bestdef=idef; bestsize=size; /* save indexes of best fit */
4171: bestheight = defheight; } /* and save new best height */
4172: if ( defheight >= bigheight ) /* new biggest character */
4173: { bigdef=idef; bigsize=size; /* save indexes of biggest */
4174: bigheight = defheight; } /* and save new big height */
4175: } /* --- end-of-if(gfdata!=NULL) --- */
4176: } /* --- end-of-if(family) --- */
4177: } /* --- end-of-for(idef) --- */
4178: /* -------------------------------------------------------------------------
4179: construct subraster for best fit character, and return it to caller
4180: -------------------------------------------------------------------------- */
4181: if ( bestdef >= 0 ) /* found a best fit for caller */
4182: sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
1.2 albertel 4183: if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
4184: || bigdef < 0 ) /* delim not in font tables */
1.1 albertel 4185: sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
4186: if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
4187: sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
1.2 albertel 4188: if ( msgfp!=NULL && msglevel>=99 )
4189: fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
4190: (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
1.1 albertel 4191: return ( sp );
4192: } /* --- end-of-function get_delim() --- */
4193:
4194:
4195: /* ==========================================================================
4196: * Function: make_delim ( char *symbol, int height )
4197: * Purpose: constructs subraster corresponding to symbol
4198: * exactly as large as height,
4199: * --------------------------------------------------------------------------
4200: * Arguments: symbol (I) char * containing, e.g., if symbol="("
4201: * for desired delimiter
4202: * height (I) int containing height
4203: * for returned character
4204: * --------------------------------------------------------------------------
4205: * Returns: ( subraster * ) constructed delimiter
4206: * or NULL for any error
4207: * --------------------------------------------------------------------------
4208: * Notes: o If height is passed as negative, its absolute value is used
4209: * and interpreted as width (rather than height)
4210: * ======================================================================= */
4211: /* --- entry point --- */
4212: subraster *make_delim ( char *symbol, int height )
4213: {
4214: /* -------------------------------------------------------------------------
4215: Allocations and Declarations
4216: -------------------------------------------------------------------------- */
4217: subraster *sp = (subraster *)NULL, /* subraster returned to caller */
4218: *new_subraster(); /* allocate subraster */
1.2 albertel 4219: subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
4220: *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
4221: *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
4222: *rastack(), *rastcat(); /* stack pieces, concat filler */
4223: int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
1.1 albertel 4224: raster *rasp = (raster *)NULL; /* sp->image */
4225: int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
1.2 albertel 4226: int pixsz = 1, /* pixels are one bit each */
4227: symsize = 0; /* size arg for get_symsubraster() */
1.1 albertel 4228: int thickness = 1; /* drawn lines are one pixel thick */
4229: int aspectratio = 8; /* default height/width for parens */
4230: int iswidth = 0, /*true if width specified by height*/
4231: width = height; /* #pixels width (e.g., of ellipse)*/
1.2 albertel 4232: char *lp=NULL, *rp=NULL, /* check symbol for left or right */
4233: *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
4234: *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
4235: *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
1.1 albertel 4236: int circle_raster(), /* ellipse for ()'s in sp->image */
4237: rule_rsater(), /* horizontal or vertical lines */
4238: line_raster(); /* line between two points */
1.2 albertel 4239: subraster *uparrow_subraster(); /* up/down arrows */
4240: int isprealloc = 1; /*pre-alloc subraster, except arrow*/
4241: int oldsmashmargin = smashmargin, /* save original smashmargin */
1.3 ! albertel 4242: wasnocatspace = isnocatspace; /* save original isnocatspace */
1.1 albertel 4243: /* -------------------------------------------------------------------------
4244: initialization
4245: -------------------------------------------------------------------------- */
4246: /* --- determine whether constructing height or width --- */
4247: if ( height < 0 ) /* negative "height" signals width */
4248: { width = height = (-height); /* flip height positive */
4249: iswidth = 1; } /* set flag for width */
4250: if ( height < 3 ) goto end_of_job; /* too small, must be error */
4251: /* --- set default width (or height) accordingly --- */
4252: if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
4253: else width = (height+(aspectratio+1)/2)/aspectratio;
4254: if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
1.2 albertel 4255: || strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
4256: || strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
4257: width = max2(width,6); /* need space between two |'s */
1.1 albertel 4258: if ( width < 2 ) width=2; /* set min width */
4259: if ( strchr(symbol,'(') != NULL /* if left ( */
4260: || strchr(symbol,')') != NULL ) /* or right ) paren wanted */
1.2 albertel 4261: { width = (3*width)/2; /* adjust width */
4262: if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
4263: if ( strchr(symbol,'/') != NULL /* left / */
4264: || strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
4265: || strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
4266: width = max2(height/3,5);
4267: if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
4268: { width = min2(height/3,20); /* adjust width */
4269: isprealloc = 0; } /* don't preallocate subraster */
4270: if ( strchr(symbol,'{') != NULL /* if left { */
4271: || strchr(symbol,'}') != NULL ) /* or right } brace wanted */
4272: { isprealloc = 0; } /* don't preallocate */
1.1 albertel 4273: /* --- allocate and initialize subraster for constructed delimiter --- */
1.2 albertel 4274: if ( isprealloc ) /* pre-allocation wanted */
4275: { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
4276: == NULL ) goto end_of_job; /* quit if failed */
4277: /* --- initialize delimiter subraster parameters --- */
4278: sp->type = IMAGERASTER; /* image */
4279: sp->symdef = NULL; /* not applicable for image */
4280: sp->baseline = height/2 + 2; /* is a little above center good? */
4281: sp->size = NORMALSIZE; /* size (probably unneeded) */
4282: rasp = sp->image; } /* pointer to image in subraster */
1.1 albertel 4283: /* -------------------------------------------------------------------------
4284: ( ) parens
4285: -------------------------------------------------------------------------- */
4286: if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
4287: || (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
4288: {
1.2 albertel 4289: if ( isdrawparen ) { /* draw the paren */
4290: int mywidth = min2(width,20); /* max width for ()'s */
4291: circle_raster ( rasp, /* embedded raster image */
1.1 albertel 4292: 0, 0, /* row0,col0 are upper-left corner */
4293: height-1, mywidth-1, /* row1,col1 are lower-right */
4294: thickness, /* line thickness is 1 pixel */
4295: (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
1.2 albertel 4296: isokay = 1; } /* set flag */
4297: else {
4298: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
4299: char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
4300: *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
4301: *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
4302: int baseht=0, barht=0, /* height of base=top+bot, bar */
4303: ibar=0, nbars=0; /* bar index, #bars between top&bot*/
4304: int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
4305: topfill=(isleft?0:0), botfill=(isleft?0:0),
4306: barfill=(isleft?0:7); /* alignment fillers */
4307: /* --- get pieces at largest size smaller than total height --- */
4308: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
4309: {
4310: /* --- get pieces at current test size --- */
4311: isokay = 1; /* check for all pieces */
4312: if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
4313: if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
4314: if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
4315: /* --- check sum of pieces against total desired height --- */
4316: if ( isokay ) { /* all pieces retrieved */
4317: baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
4318: barht = (symbar->image)->height; /* bar height */
4319: if ( baseht < height+5 ) break; /* largest base that's not too big */
4320: if ( symsize < 1 ) break; /* or smallest available base */
4321: } /* --- end-of-if(isokay) --- */
4322: /* --- free test pieces that were too big --- */
4323: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
4324: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
4325: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
4326: isokay = 0; /* nothing available */
4327: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
4328: } /* --- end-of-for(symsize) --- */
4329: /* --- construct brace from pieces --- */
4330: if ( isokay ) { /* we have the pieces */
4331: /* --- add alignment fillers --- */
1.3 ! albertel 4332: smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/
1.2 albertel 4333: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
4334: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
4335: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
4336: smashmargin = oldsmashmargin; /* reset smashmargin */
1.3 ! albertel 4337: isnocatspace = wasnocatspace; /* reset isnocatspace */
1.2 albertel 4338: /* --- #bars needed between top and bot --- */
4339: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
4340: /* --- stack pieces --- */
4341: sp = topsym; /* start with top piece */
4342: if ( nbars > 0 ) /* need nbars between top and bot */
4343: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4344: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
4345: delete_subraster(barsym); /* barsym no longer needed */
4346: } /* --- end-of-if(isokay) --- */
4347: } /* --- end-of-if/else(isdrawparen) --- */
1.1 albertel 4348: } /* --- end-of-if(left- or right-() paren wanted) --- */
4349: /* -------------------------------------------------------------------------
1.2 albertel 4350: { } braces
4351: -------------------------------------------------------------------------- */
4352: else
4353: if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
4354: || (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
4355: {
4356: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
4357: char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
4358: *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
4359: *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
4360: *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
4361: int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
4362: ibar=0, nbars=0; /* bar index, #bars above,below mid*/
4363: int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
4364: topfill=(isleft?4:0), botfill=(isleft?4:0),
4365: midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
4366: /* --- get pieces at largest size smaller than total height --- */
4367: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
4368: {
4369: /* --- get pieces at current test size --- */
4370: isokay = 1; /* check for all pieces */
4371: if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
4372: if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
4373: if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
4374: if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
4375: /* --- check sum of pieces against total desired height --- */
4376: if ( isokay ) { /* all pieces retrieved */
4377: baseht = (symtop->image)->height + (symbot->image)->height
4378: + (symmid->image)->height; /* top+bot+mid height */
4379: barht = (symbar->image)->height; /* bar height */
4380: if ( baseht < height+5 ) break; /* largest base that's not too big */
4381: if ( symsize < 1 ) break; /* or smallest available base */
4382: } /* --- end-of-if(isokay) --- */
4383: /* --- free test pieces that were too big --- */
4384: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
4385: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
4386: if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
4387: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
4388: isokay = 0; /* nothing available */
4389: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
4390: } /* --- end-of-for(symsize) --- */
4391: /* --- construct brace from pieces --- */
4392: if ( isokay ) { /* we have the pieces */
4393: /* --- add alignment fillers --- */
1.3 ! albertel 4394: smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/
1.2 albertel 4395: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
4396: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
4397: midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
4398: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
4399: smashmargin = oldsmashmargin; /* reset smashmargin */
1.3 ! albertel 4400: isnocatspace = wasnocatspace; /* reset isnocatspace */
1.2 albertel 4401: /* --- #bars needed on each side of mid piece --- */
4402: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
4403: /* --- stack pieces --- */
4404: sp = topsym; /* start with top piece */
4405: if ( nbars > 0 ) /* need nbars above middle */
4406: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4407: sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
4408: if ( nbars > 0 ) /* need nbars below middle */
4409: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4410: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
4411: delete_subraster(barsym); /* barsym no longer needed */
4412: } /* --- end-of-if(isokay) --- */
4413: } /* --- end-of-if(left- or right-{} brace wanted) --- */
4414: /* -------------------------------------------------------------------------
1.1 albertel 4415: [ ] brackets
4416: -------------------------------------------------------------------------- */
4417: else
4418: if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
1.2 albertel 4419: || (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
4420: || (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
4421: || (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
4422: || (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
4423: || (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
4424: || (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
4425: || (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
4426: {
4427: /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
4428: int mywidth = min2(width,12), /* max width for horizontal bars */
4429: wthick = 1; /* thickness of top.bottom bars */
4430: thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
4431: if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
4432: wthick = thickness; /* same thickness for top/bot bar */
4433: if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
4434: rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
4435: if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
4436: rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
4437: if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
1.1 albertel 4438: rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
1.2 albertel 4439: if ( lp4 != NULL ) /* 2nd left vertical bar needed */
4440: rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
4441: if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
1.1 albertel 4442: rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
1.2 albertel 4443: if ( rp4 != NULL ) /* 2nd right vertical bar needed */
4444: rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
1.1 albertel 4445: isokay = 1; /* set flag */
4446: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
4447: /* -------------------------------------------------------------------------
4448: < > brackets
4449: -------------------------------------------------------------------------- */
4450: else
4451: if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
4452: || (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
4453: {
1.2 albertel 4454: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
4455: int mywidth = min2(width,12), /* max width for brackets */
4456: mythick = 1; /* all lines one pixel thick */
4457: thickness = (height<25?1:2); /* set line pixel thickness */
1.1 albertel 4458: if ( lp != NULL ) /* left < bracket wanted */
1.2 albertel 4459: { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
4460: if ( thickness>1 )
4461: line_raster(rasp,height/2,1,0,mywidth-1,mythick);
4462: line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
4463: if ( thickness>1 )
4464: line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
1.1 albertel 4465: if ( rp != NULL ) /* right > bracket wanted */
1.2 albertel 4466: { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
4467: if ( thickness>1 )
4468: line_raster(rasp,height/2,mywidth-2,0,0,mythick);
4469: line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
4470: if ( thickness>1 )
4471: line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
1.1 albertel 4472: isokay = 1; /* set flag */
4473: } /* --- end-of-if(left- or right-<> bracket wanted) --- */
4474: /* -------------------------------------------------------------------------
1.2 albertel 4475: / \ delimiters
4476: -------------------------------------------------------------------------- */
4477: else
4478: if ( (lp=strchr(symbol,'/')) != NULL /* left / wanted */
4479: || (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
4480: || (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
4481: {
4482: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
4483: int mywidth = width; /* max width for / \ */
4484: thickness = 1; /* set line pixel thickness */
4485: if ( lp != NULL ) /* left / wanted */
4486: line_raster(rasp,0,mywidth-1,height-1,0,thickness);
4487: if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
4488: line_raster(rasp,0,0,height-1,mywidth-1,thickness);
4489: isokay = 1; /* set flag */
4490: } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
4491: /* -------------------------------------------------------------------------
4492: arrow delimiters
4493: -------------------------------------------------------------------------- */
4494: else
4495: if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
4496: {
4497: /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
4498: int mywidth = width; /* max width for / \ */
4499: int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
4500: || strstr(symbol,"Down")!=NULL); /* or a Down */
4501: int drctn = +1; /* init for uparrow */
4502: if ( strstr(symbol,"down")!=NULL /* down if we have down */
4503: || strstr(symbol,"Down")!=NULL ) /* or Down */
4504: { drctn = (-1); /* reset direction to down */
4505: if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
4506: || strstr(symbol,"Up")!=NULL ) /* and down or Down */
4507: drctn = 0; } /* reset direction to updown */
4508: sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
4509: if ( sp != NULL )
4510: { sp->type = IMAGERASTER; /* image */
4511: sp->symdef = NULL; /* not applicable for image */
4512: sp->baseline = height/2 + 2; /* is a little above center good? */
4513: sp->size = NORMALSIZE; /* size (probably unneeded) */
4514: isokay = 1; } /* set flag */
4515: } /* --- end-of-if(arrow delimiter wanted) --- */
4516: /* -------------------------------------------------------------------------
1.1 albertel 4517: \- for | | brackets or \= for || || brackets
4518: -------------------------------------------------------------------------- */
4519: else
4520: if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */
4521: || (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */
4522: || (rp=strchr(symbol,'=')) != NULL /* left or right || bracket wanted */
4523: || (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
4524: {
4525: /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
4526: int midcol = width/2; /* middle col, left of mid if even */
4527: if ( rp != NULL /* left or right || bracket wanted */
4528: || rp2 != NULL ) /* or || in standard tex notation */
1.2 albertel 4529: { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
1.1 albertel 4530: rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
4531: rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
4532: else /*nb, lp2 spuriously set if rp2 set*/
4533: if ( lp != NULL /* left or right | bracket wanted */
4534: || lp2 != NULL ) /* ditto for synomym */
1.2 albertel 4535: { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
1.1 albertel 4536: rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
4537: isokay = 1; /* set flag */
4538: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
4539: /* -------------------------------------------------------------------------
4540: back to caller
4541: -------------------------------------------------------------------------- */
4542: end_of_job:
1.2 albertel 4543: if ( msgfp!=NULL && msglevel>=99 )
4544: fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
4545: (symbol==NULL?"null":symbol),isokay);
1.1 albertel 4546: if ( !isokay ) /* don't have requested delimiter */
1.2 albertel 4547: { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
1.1 albertel 4548: sp = NULL; } /* and signal error to caller */
4549: return ( sp ); /*back to caller with delim or NULL*/
4550: } /* --- end-of-function make_delim() --- */
4551:
4552:
4553: /* ==========================================================================
4554: * Function: texchar ( expression, chartoken )
4555: * Purpose: scans expression, returning either its first character,
4556: * or the next \sequence if that first char is \,
4557: * and a pointer to the first expression char past that.
4558: * --------------------------------------------------------------------------
4559: * Arguments: expression (I) char * to first char of null-terminated
4560: * string containing valid LaTeX expression
4561: * to be scanned
4562: * chartoken (O) char * to null-terminated string returning
4563: * either the first (non-whitespace) character
4564: * of expression if that char isn't \, or else
4565: * the \ and everything following it up to
4566: * the next non-alphabetic character (but at
4567: * least one char following the \ even if
4568: * it's non-alpha)
4569: * --------------------------------------------------------------------------
4570: * Returns: ( char * ) ptr to the first char of expression
4571: * past returned chartoken,
4572: * or NULL for any parsing error.
4573: * --------------------------------------------------------------------------
4574: * Notes: o Does *not* skip leading whitespace, but simply
4575: * returns any whitespace character as the next character.
4576: * ======================================================================= */
4577: /* --- entry point --- */
4578: char *texchar ( char *expression, char *chartoken )
4579: {
4580: /* -------------------------------------------------------------------------
4581: Allocations and Declarations
4582: -------------------------------------------------------------------------- */
4583: int esclen = 0, /*length of escape sequence*/
4584: maxesclen = 128; /* max len of esc sequence */
4585: char *ptoken = chartoken; /* ptr into chartoken */
4586: int iprefix = 0; /* prefix index */
4587: static char *prefixes[] = /*e.g., \big followed by ( */
4588: { /* "\\left", "\\right", */
4589: "\\big", "\\Big", "\\bigg", "\\Bigg",
4590: "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
4591: "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
1.3 ! albertel 4592: static char *starred[] = /* may be followed by * */
! 4593: { "\\hspace", "\\!", NULL };
1.1 albertel 4594: /* -------------------------------------------------------------------------
4595: just return the next char if it's not \
4596: -------------------------------------------------------------------------- */
4597: /* --- error check for end-of-string --- */
4598: *ptoken = '\000'; /* init in case of error */
4599: if ( expression == NULL ) return(NULL); /* nothing to scan */
4600: if ( *expression == '\000' ) return(NULL); /* nothing to scan */
4601: /* --- always returning first character (either \ or some other char) --- */
4602: *ptoken++ = *expression++; /* here's first character */
4603: /* --- if first char isn't \, then just return it to caller --- */
4604: if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */
4605: { *ptoken = '\000'; /* add a null terminator */
4606: goto end_of_job; } /* ptr past returned char */
4607: if ( *expression == '\000' ) /* \ is very last char */
4608: { *chartoken = '\000'; /* flush bad trailing \ */
4609: return(NULL); } /* and signal end-of-job */
4610: /* -------------------------------------------------------------------------
4611: we have an escape sequence, so return all alpha chars following \
4612: -------------------------------------------------------------------------- */
4613: /* --- accumulate chars until first non-alpha char found --- */
4614: for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */
4615: { if ( esclen < maxesclen-3 ) /* more room in chartoken */
4616: *ptoken++ = *expression; /*copy alpha char, bump ptr*/
4617: expression++; } /* bump expression ptr */
4618: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
4619: *ptoken = '\000'; /* set null for compare */
4620: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
4621: if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
4622: { char nextchar[256]; int nextlen=0; /* texchar after prefix */
4623: skipwhite(expression); /* skip space after prefix*/
4624: expression = texchar(expression,nextchar); /* get nextchar */
4625: if ( (nextlen = strlen(nextchar)) > 0 ) /* #chars in nextchar */
4626: { strcpy(ptoken,nextchar); /* append nextchar */
4627: ptoken += strlen(nextchar); /* point to null terminator*/
4628: esclen += strlen(nextchar); } /* and bump escape length */
4629: break; } /* stop checking prefixes */
4630: /* --- every \ must be followed by at least one char, e.g., \[ --- */
4631: if ( esclen < 1 ) /* \ followed by non-alpha */
4632: *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
1.3 ! albertel 4633: *ptoken = '\000'; /* null-terminate token */
! 4634: /* --- check for \hspace* or other starred commands --- */
! 4635: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
! 4636: if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */
! 4637: if ( *expression == '*' ) /* follows by a * */
! 4638: { *ptoken++ = *expression++; /* copy * and bump ptr */
! 4639: *ptoken = '\000'; /* null-terminate token */
! 4640: break; } /* stop checking */
! 4641: /* --- respect spaces in text mode, except first space after \escape --- */
! 4642: if ( esclen >= 1 ) { /*only for alpha \sequences*/
1.2 albertel 4643: if ( istextmode ) /* in \rm or \it text mode */
4644: if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
4645: expression++; } /* so flush delim */
1.1 albertel 4646: /* --- back to caller --- */
4647: end_of_job:
4648: if ( msgfp!=NULL && msglevel>=999 )
4649: { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
4650: fflush(msgfp); }
4651: return ( expression ); /*ptr to 1st non-alpha char*/
4652: } /* --- end-of-function texchar() --- */
4653:
4654:
4655: /* ==========================================================================
4656: * Function: texsubexpr (expression,subexpr,maxsubsz,
4657: * left,right,isescape,isdelim)
4658: * Purpose: scans expression, returning everything between a balanced
4659: * left{...right} subexpression if the first non-whitespace
4660: * char of expression is an (escaped or unescaped) left{,
4661: * or just the next texchar() otherwise,
4662: * and a pointer to the first expression char past that.
4663: * --------------------------------------------------------------------------
4664: * Arguments: expression (I) char * to first char of null-terminated
4665: * string containing valid LaTeX expression
4666: * to be scanned
4667: * subexpr (O) char * to null-terminated string returning
4668: * either everything between a balanced {...}
4669: * subexpression if the first char is {,
4670: * or the next texchar() otherwise.
4671: * maxsubsz (I) int containing max #bytes returned
4672: * in subexpr buffer (0 means unlimited)
4673: * left (I) char * specifying allowable left delimiters
4674: * that begin subexpression, e.g., "{[(<"
4675: * right (I) char * specifying matching right delimiters
4676: * in the same order as left, e.g., "}])>"
4677: * isescape (I) int controlling whether escaped and/or
4678: * unescaped left,right are matched;
4679: * see isbrace() comments below for details.
4680: * isdelim (I) int containing true (non-zero) to return
4681: * the leading left and trailing right delims
4682: * (if any were found) along with subexpr,
4683: * or containing false=0 to return subexpr
4684: * without its delimiters
4685: * --------------------------------------------------------------------------
4686: * Returns: ( char * ) ptr to the first char of expression
4687: * past returned subexpr (see Notes),
4688: * or NULL for any parsing error.
4689: * --------------------------------------------------------------------------
4690: * Notes: o If subexpr is of the form left{...right},
4691: * the outer {}'s are returned as part of subexpr
4692: * if isdelim is true; if isdelim is false the {}'s aren't
4693: * returned. In either case the returned pointer is
4694: * *always* bumped past the closing right}, even if
4695: * that closing right} isn't returned in subexpr.
4696: * o If subexpr is not of the form left{...right},
4697: * the returned pointer is on the character immediately
4698: * following the last character returned in subexpr
4699: * o \. acts as LaTeX \right. and matches any \left(
4700: * And it also acts as a LaTeX \left. and matches any \right)
4701: * ======================================================================= */
4702: /* --- entry point --- */
4703: char *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
4704: char *left, char *right, int isescape, int isdelim )
4705: {
4706: /* -------------------------------------------------------------------------
4707: Allocations and Declarations
4708: -------------------------------------------------------------------------- */
4709: char *texchar(); /*next char (or \sequence) from expression*/
1.2 albertel 4710: char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
4711: rightdelim[256] = ")\000"; /* and matching right) */
1.1 albertel 4712: char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
1.2 albertel 4713: char *strtexchr(), *texleft(); /* check for \left, and get it */
1.1 albertel 4714: int gotescape = 0, /* true if leading char of expression is \ */
4715: prevescape = 0; /* while parsing, true if preceding char \ */
4716: int isbrace(); /* check for left,right braces */
4717: int isanyright = 1; /* true matches any right with left, (...] */
4718: int isleftdot = 0; /* true if left brace is a \. */
4719: int nestlevel = 1; /* current # of nested braces */
1.3 ! albertel 4720: int subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
1.1 albertel 4721: /* -------------------------------------------------------------------------
4722: skip leading whitespace and just return the next char if it's not {
4723: -------------------------------------------------------------------------- */
4724: /* --- skip leading whitespace and error check for end-of-string --- */
4725: *subexpr = '\000'; /* init in case of error */
4726: if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/
4727: skipwhite(expression); /* leading whitespace gone */
4728: if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
4729: /* --- set maxsubsz --- */
1.3 ! albertel 4730: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2; /* input 0 means unlimited */
1.1 albertel 4731: /* --- check for escape --- */
4732: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
4733: gotescape = 1; /* so set flag accordingly */
1.2 albertel 4734: /* --- check for \left...\right --- */
4735: if ( gotescape ) /* begins with \ */
4736: if ( memcmp(expression+1,"left",4) ) /* and followed by left */
4737: if ( strchr(left,'l') != NULL ) /* caller wants \left's */
4738: if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
4739: { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
4740: (isdelim?NULL:leftdelim),rightdelim);
4741: if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
4742: return ( pright ); /*back to caller past \right*/
4743: } /* --- end-of-if(expression=="\\left") --- */
1.1 albertel 4744: /* --- if first char isn't left{ or script, just return it to caller --- */
1.3 ! albertel 4745: if ( !isbrace(expression,left,isescape) ) { /* not a left{ */
1.1 albertel 4746: if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
4747: return ( texchar(expression,subexpr) ); /* next char to caller */
4748: else /* --- kludge for super/subscripts to accommodate texscripts() --- */
4749: { *subexpr++ = *expression; /* signal script */
4750: *subexpr = '\000'; /* null-terminate subexpr */
1.3 ! albertel 4751: return ( expression ); } } /* leave script in stream */
1.1 albertel 4752: /* --- extract left and find matching right delimiter --- */
4753: *leftdelim = *(expression+gotescape); /* the left( in expression */
4754: if ( (gotescape && *leftdelim == '.') /* we have a left \. */
4755: || (gotescape && isanyright) ) /*or are matching any right*/
4756: { isleftdot = 1; /* so just set flag */
4757: *leftdelim = '\000'; } /* and reset leftdelim */
4758: else /* find matching \right */
4759: if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
4760: *rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */
4761: else /* can't happen -- pgm bug */
4762: return ( NULL ); /*just signal eoj to caller*/
4763: /* -------------------------------------------------------------------------
4764: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
4765: -------------------------------------------------------------------------- */
4766: /* --- first initialize by bumping past left{ or \{ --- */
4767: if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/
4768: else expression++; /* always bump past left{ */
1.3 ! albertel 4769: if ( gotescape ) { /*need to bump another char*/
1.1 albertel 4770: if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
1.3 ! albertel 4771: else expression++; } /* else just bump past it */
1.1 albertel 4772: /* --- set maximum size for numerical arguments --- */
4773: if ( 0 ) /* check turned on or off? */
4774: if ( !isescape && !isdelim ) /*looking for numerical arg*/
4775: maxsubsz = 96; /* set max arg size */
4776: /* --- search for matching right} --- */
4777: while ( 1 ) /*until balanced right} */
4778: {
4779: /* --- error check for end-of-string --- */
4780: if ( *expression == '\000' ) /* premature end-of-string */
4781: { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/
4782: { expression = origexpression; /* so end-of-string is error*/
4783: subexpr = origsubexpr; } /* so reset all ptrs */
1.3 ! albertel 4784: if ( isdelim ) { /* generate fake right */
1.1 albertel 4785: if ( gotescape ) /* need escaped right */
4786: { *subexpr++ = '\\'; /* set escape char */
4787: *subexpr++ = '.'; } /* and fake \right. */
4788: else /* escape not wanted */
1.3 ! albertel 4789: *subexpr++ = *rightdelim; } /* so fake actual right */
1.1 albertel 4790: *subexpr = '\000'; /* null-terminate subexpr */
4791: return ( expression ); } /* back with final token */
4792: /* --- check preceding char for escape --- */
4793: if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */
4794: prevescape = 1-prevescape; /* so flip escape flag */
4795: else prevescape = 0; /* or turn flag off */
4796: /* --- check for { and } (un/escaped as per leading left) --- */
4797: if ( gotescape == prevescape ) /* escaped iff leading is */
4798: { /* --- check for (closing) right delim and see if we're done --- */
4799: if ( isthischar(*expression,rightdelim) /* found a right} */
4800: || (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
4801: || (prevescape && isthischar(*expression,".")) ) /*or found \right. */
4802: if ( --nestlevel < 1 ) /*\right balances 1st \left*/
4803: { if ( isdelim ) /*caller wants } in subexpr*/
4804: *subexpr++ = *expression; /* so end subexpr with } */
4805: else /*check for \ before right}*/
4806: if ( prevescape ) /* have unwanted \ */
4807: *(subexpr-1) = '\000'; /* so replace it with null */
4808: *subexpr = '\000'; /* null-terminate subexpr */
4809: return ( expression+1 ); } /* back with char after } */
4810: /* --- check for (another) left{ --- */
4811: if ( isthischar(*expression,leftdelim) /* found another left{ */
4812: || (isleftdot && isthischar(*expression,left)) ) /* any left{ */
4813: nestlevel++;
4814: } /* --- end-of-if(gotescape==prevescape) --- */
4815: /* --- not done, so copy char to subexpr and continue with next char --- */
4816: if ( ++subsz < maxsubsz-5 ) /* more room in subexpr */
4817: *subexpr++ = *expression; /* so copy char and bump ptr*/
4818: expression++; /* bump expression ptr */
4819: } /* --- end-of-while(1) --- */
4820: } /* --- end-of-function texsubexpr() --- */
4821:
4822:
4823: /* ==========================================================================
1.2 albertel 4824: * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
4825: * Purpose: scans expression, starting after opening \left,
4826: * and returning ptr after matching closing \right.
4827: * Everything between is returned in subexpr, if given.
4828: * Likewise, if given, ldelim returns delimiter after \left
4829: * and rdelim returns delimiter after \right.
4830: * If ldelim is given, the returned subexpr doesn't include it.
4831: * If rdelim is given, the returned pointer is after that delim.
4832: * --------------------------------------------------------------------------
4833: * Arguments: expression (I) char * to first char of null-terminated
4834: * string immediately following opening \left
4835: * subexpr (O) char * to null-terminated string returning
4836: * either everything between balanced
4837: * \left ... \right. If leftdelim given,
4838: * subexpr does _not_ contain that delimiter.
4839: * maxsubsz (I) int containing max #bytes returned
4840: * in subexpr buffer (0 means unlimited)
4841: * ldelim (O) char * returning delimiter following
4842: * opening \left
4843: * rdelim (O) char * returning delimiter following
4844: * closing \right
4845: * --------------------------------------------------------------------------
4846: * Returns: ( char * ) ptr to the first char of expression
4847: * past closing \right, or past closing
4848: * right delimiter if rdelim!=NULL,
4849: * or NULL for any error.
4850: * --------------------------------------------------------------------------
4851: * Notes: o
4852: * ======================================================================= */
4853: /* --- entry point --- */
4854: char *texleft ( char *expression, char *subexpr, int maxsubsz,
4855: char *ldelim, char *rdelim )
4856: {
4857: /* -------------------------------------------------------------------------
4858: Allocations and Declarations
4859: -------------------------------------------------------------------------- */
4860: char *texchar(), /* get delims after \left,\right */
4861: *strtexchr(), *pright=expression; /* locate matching \right */
4862: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
4863: int sublen = 0; /* #chars between \left...\right */
4864: /* -------------------------------------------------------------------------
4865: initialization
4866: -------------------------------------------------------------------------- */
4867: /* --- init output --- */
4868: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
4869: if ( ldelim != NULL ) *ldelim = '\000'; /* init ldelim, if given */
4870: if ( rdelim != NULL ) *rdelim = '\000'; /* init rdelim, if given */
4871: /* --- check args --- */
4872: if ( expression == NULL ) goto end_of_job; /* no input supplied */
4873: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
4874: /* --- determine left delimiter --- */
4875: if ( ldelim != NULL ) /* caller wants left delim */
4876: { skipwhite(expression); /* interpret \left ( as \left( */
4877: expression = texchar(expression,ldelim); } /*delim from expression*/
4878: /* -------------------------------------------------------------------------
4879: locate \right balancing opening \left
4880: -------------------------------------------------------------------------- */
4881: /* --- first \right following \left --- */
4882: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
4883: != NULL ) { /* found it */
4884: /* --- find matching \right by pushing past any nested \left's --- */
4885: char *pleft = expression; /* start after first \left( */
4886: while ( 1 ) { /*break when matching \right found*/
4887: /* -- locate next nested \left if there is one --- */
4888: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
4889: == NULL ) break; /*no more, so matching \right found*/
4890: pleft += strlen(left); /* push ptr past \left token */
4891: if ( pleft >= pright ) break; /* not nested if \left after \right*/
4892: /* --- have nested \left, so push forward to next \right --- */
4893: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
4894: == NULL ) break; /* ran out of \right's */
4895: } /* --- end-of-while(1) --- */
4896: } /* --- end-of-if(pright!=NULL) --- */
4897: /* --- set subexpression length, push pright past \right --- */
4898: if ( pright != (char *)NULL ) /* found matching \right */
4899: { sublen = (int)(pright-expression); /* #chars between \left...\right */
4900: pright += strlen(right); } /* so push pright past \right */
4901: /* -------------------------------------------------------------------------
4902: get rightdelim and subexpr between \left...\right
4903: -------------------------------------------------------------------------- */
4904: /* --- get delimiter following \right --- */
1.3 ! albertel 4905: if ( rdelim != NULL ) { /* caller wants right delim */
1.2 albertel 4906: if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
4907: { strcpy(rdelim,"."); /* set default \right. */
4908: sublen = strlen(expression); /* use entire remaining expression */
4909: pright = expression + sublen; } /* and push pright to end-of-string*/
4910: else /* have explicit matching \right */
4911: { skipwhite(pright); /* interpret \right ) as \right) */
4912: pright = texchar(pright,rdelim); /* pull delim from expression */
1.3 ! albertel 4913: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
1.2 albertel 4914: /* --- get subexpression between \left...\right --- */
4915: if ( sublen > 0 ) /* have subexpr */
4916: if ( subexpr != NULL ) { /* and caller wants it */
4917: if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
4918: memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
4919: subexpr[sublen] = '\000'; } /* null-terminate subexpr */
4920: end_of_job:
4921: if ( msglevel>=99 && msgfp!=NULL )
4922: { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
4923: (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
4924: (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
4925: return ( pright );
4926: } /* --- end-of-function texleft --- */
4927:
4928:
4929: /* ==========================================================================
1.1 albertel 4930: * Function: texscripts ( expression, subscript, superscript, which )
4931: * Purpose: scans expression, returning subscript and/or superscript
4932: * if expression is of the form _x^y or ^{x}_{y},
4933: * or any (valid LaTeX) permutation of the above,
4934: * and a pointer to the first expression char past "scripts"
4935: * --------------------------------------------------------------------------
4936: * Arguments: expression (I) char * to first char of null-terminated
4937: * string containing valid LaTeX expression
4938: * to be scanned
4939: * subscript (O) char * to null-terminated string returning
4940: * subscript (without _), if found, or "\000"
4941: * superscript (O) char * to null-terminated string returning
4942: * superscript (without ^), if found, or "\000"
4943: * which (I) int containing 1 for subscript only,
4944: * 2 for superscript only, >=3 for either/both
4945: * --------------------------------------------------------------------------
4946: * Returns: ( char * ) ptr to the first char of expression
4947: * past returned "scripts" (unchanged
4948: * except for skipped whitespace if
4949: * neither subscript nor superscript found),
4950: * or NULL for any parsing error.
4951: * --------------------------------------------------------------------------
4952: * Notes: o an input expression like ^a^b_c will return superscript="b",
4953: * i.e., totally ignoring all but the last "script" encountered
4954: * ======================================================================= */
4955: /* --- entry point --- */
4956: char *texscripts ( char *expression, char *subscript,
4957: char *superscript, int which )
4958: {
4959: /* -------------------------------------------------------------------------
4960: Allocations and Declarations
4961: -------------------------------------------------------------------------- */
4962: char *texsubexpr(); /* next subexpression from expression */
4963: int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
4964: /* -------------------------------------------------------------------------
1.2 albertel 4965: init "scripts"
1.1 albertel 4966: -------------------------------------------------------------------------- */
1.2 albertel 4967: if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
4968: if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
1.1 albertel 4969: /* -------------------------------------------------------------------------
4970: get subscript and/or superscript from expression
4971: -------------------------------------------------------------------------- */
1.2 albertel 4972: while ( expression != NULL ) {
4973: skipwhite(expression); /* leading whitespace gone */
4974: if ( *expression == '\000' ) return(expression); /* nothing left to scan */
1.1 albertel 4975: if ( isthischar(*expression,SUBSCRIPT) /* found _ */
4976: && (which==1 || which>2 ) ) /* and caller wants it */
4977: { if ( gotsub /* found 2nd subscript */
4978: || subscript == NULL ) break; /* or no subscript buffer */
4979: gotsub = 1; /* set subscript flag */
4980: expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
4981: else /* no _, check for ^ */
4982: if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */
4983: && which>=2 ) /* and caller wants it */
4984: { if ( gotsup /* found 2nd superscript */
4985: || superscript == NULL ) break; /* or no superscript buffer*/
4986: gotsup = 1; /* set superscript flag */
4987: expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
4988: else /* neither _ nor ^ */
4989: return ( expression ); /*return ptr past "scripts"*/
1.2 albertel 4990: } /* --- end-of-while(expression!=NULL) --- */
1.1 albertel 4991: return ( expression );
4992: } /* --- end-of-function texscripts() --- */
4993:
4994:
4995: /* ==========================================================================
4996: * Function: isbrace ( expression, braces, isescape )
4997: * Purpose: checks leading char(s) of expression for a brace,
4998: * either escaped or unescaped depending on isescape,
4999: * except that { and } are always matched, if they're
5000: * in braces, regardless of isescape.
5001: * --------------------------------------------------------------------------
5002: * Arguments: expression (I) char * to first char of null-terminated
5003: * string containing a valid LaTeX expression
5004: * whose leading char(s) are checked for braces
5005: * that begin subexpression, e.g., "{[(<"
5006: * braces (I) char * specifying matching brace delimiters
5007: * to be checked for, e.g., "{[(<" or "}])>"
5008: * isescape (I) int containing 0 to match only unescaped
5009: * braces, e.g., (...) or {...}, etc,
5010: * or containing 1 to match only escaped
5011: * braces, e.g., \(...\) or \[...\], etc,
5012: * or containing 2 to match either.
5013: * But note: if {,} are in braces
5014: * then they're *always* matched whether
5015: * escaped or not, regardless of isescape.
5016: * --------------------------------------------------------------------------
5017: * Returns: ( int ) 1 if the leading char(s) of expression
5018: * is a brace, or 0 if not.
5019: * --------------------------------------------------------------------------
5020: * Notes: o
5021: * ======================================================================= */
5022: /* --- entry point --- */
5023: int isbrace ( char *expression, char *braces, int isescape )
5024: {
5025: /* -------------------------------------------------------------------------
5026: Allocations and Declarations
5027: -------------------------------------------------------------------------- */
5028: int gotescape = 0, /* true if leading char is an escape */
5029: gotbrace = 0; /*true if first non-escape char is a brace*/
5030: /* -------------------------------------------------------------------------
5031: check for brace
5032: -------------------------------------------------------------------------- */
1.3 ! albertel 5033: /* --- first check for end-of-string or \= ligature --- */
! 5034: if ( *expression == '\000' /* nothing to check */
! 5035: || isligature ) goto end_of_job; /* have a \= ligature */
1.1 albertel 5036: /* --- check leading char for escape --- */
5037: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
5038: { gotescape = 1; /* so set flag accordingly */
5039: expression++; } /* and bump past escape */
5040: /* --- check (maybe next char) for brace --- */
5041: if ( isthischar(*expression,braces) ) /* expression is braced */
5042: gotbrace = 1; /* so set flag accordingly */
5043: if ( gotescape && *expression == '.' ) /* \. matches any brace */
5044: gotbrace = 1; /* set flag */
5045: /* --- check for TeX brace { or } --- */
5046: if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
5047: if ( isescape ) isescape = 2; /* reset escape flag */
5048: /* -------------------------------------------------------------------------
5049: back to caller
5050: -------------------------------------------------------------------------- */
1.3 ! albertel 5051: end_of_job:
! 5052: if ( msglevel>=999 && msgfp!=NULL )
! 5053: { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
! 5054: expression,gotbrace,isligature); fflush(msgfp); }
! 5055: if ( gotbrace && /* found a brace */
1.1 albertel 5056: ( isescape==2 || /* escape irrelevant */
5057: gotescape==isescape ) /* un/escaped as requested */
5058: ) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */
5059: } /* --- end-of-function isbrace() --- */
5060:
5061:
5062: /* ==========================================================================
5063: * Function: preamble ( expression, size, subexpr )
5064: * Purpose: parses $-terminated preamble, if present, at beginning
5065: * of expression, re-setting size if necessary, and
5066: * returning any other parameters besides size in subexpr.
5067: * --------------------------------------------------------------------------
5068: * Arguments: expression (I) char * to first char of null-terminated
5069: * string containing LaTeX expression possibly
5070: * preceded by $-terminated preamble
5071: * size (I/O) int * containing 0-4 default font size,
5072: * and returning size modified by first
5073: * preamble parameter (or unchanged)
5074: * subexpr(O) char * returning any remaining preamble
5075: * parameters past size
5076: * --------------------------------------------------------------------------
5077: * Returns: ( char * ) ptr to first char past preamble in expression
5078: * or NULL for any parsing error.
5079: * --------------------------------------------------------------------------
5080: * Notes: o size can be any number >=0. If preceded by + or -, it's
5081: * interpreted as an increment to input size; otherwise
5082: * it's interpreted as the size.
5083: * o if subexpr is passed as NULL ptr, then returned expression
5084: * ptr will have "flushed" and preamble parameters after size
5085: * ======================================================================= */
5086: /* --- entry point --- */
5087: char *preamble ( char *expression, int *size, char *subexpr )
5088: {
5089: /* -------------------------------------------------------------------------
5090: Allocations and Declarations
5091: -------------------------------------------------------------------------- */
5092: char pretext[512], *prep=expression, /*pream from expression, ptr into it*/
5093: *dollar, *comma; /* preamble delimiters */
5094: int prelen = 0, /* preamble length */
5095: sizevalue = 0, /* value of size parameter */
5096: isfontsize = 0, /*true if leading fontsize present*/
5097: isdelta = 0; /*true to increment passed size arg*/
5098: /* -------------------------------------------------------------------------
5099: initialization
5100: -------------------------------------------------------------------------- */
5101: if ( subexpr != NULL ) /* caller passed us an address */
5102: *subexpr = '\000'; /* so init assuming no preamble */
5103: if ( expression == NULL ) goto end_of_job; /* no input */
5104: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
5105: /* -------------------------------------------------------------------------
5106: process preamble if present
5107: -------------------------------------------------------------------------- */
5108: /*process_preamble:*/
5109: if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
1.3 ! albertel 5110: != NULL ) { /* found embedded $ */
1.1 albertel 5111: if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
5112: > 0 ) { /* must have preamble preceding $ */
5113: if ( prelen < 65 ) { /* too long for a prefix */
5114: memcpy(pretext,expression,prelen); /* local copy of preamble */
5115: pretext[prelen] = '\000'; /* null-terminated */
5116: if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
5117: && strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
5118: /* --- skip any leading whitespace --- */
5119: prep = pretext; /* start at beginning of preamble */
5120: skipwhite(prep); /* skip any leading white space */
5121: /* --- check for embedded , or leading +/- (either signalling size) --- */
5122: if ( isthischar(*prep,"+-") ) /* have leading + or - */
5123: isdelta = 1; /* so use size value as increment */
5124: comma = strchr(pretext,','); /* , signals leading size param */
5125: /* --- process leading size parameter if present --- */
5126: if ( comma != NULL /* size param explicitly signalled */
5127: || isdelta || isdigit(*prep) ) { /* or inferred implicitly */
5128: /* --- parse size parameter and reset size accordingly --- */
5129: if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
5130: sizevalue = atoi(prep); /* convert size string to integer */
5131: if ( size != NULL ) /* caller passed address for size */
5132: *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
5133: /* --- finally, set flag and shift size parameter out of preamble --- */
5134: isfontsize = 1; /*set flag showing font size present*/
5135: if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/
5136: } /* --- end-of-if(comma!=NULL||etc) --- */
5137: /* --- copy any preamble params following size to caller's subexpr --- */
5138: if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
5139: if ( subexpr != NULL ) /* caller passed us an address */
5140: strcpy(subexpr,pretext); /*so return extra params to caller*/
5141: /* --- finally, set prep to shift preamble out of expression --- */
5142: prep = expression + prelen+1; /* set prep past $ in expression */
5143: } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
5144: } /* --- end-of-if(prelen<65) --- */
5145: } /* --- end-of-if(prelen>0) --- */
5146: else { /* $ is first char of expression */
5147: int ndollars = 0; /* number of $...$ pairs removed */
5148: prep = expression; /* start at beginning of expression*/
5149: while ( *prep == '$' ) { /* remove all matching $...$'s */
5150: int explen = strlen(prep)-1; /* index of last char in expression*/
5151: if ( explen < 2 ) break; /* no $...$'s left to remove */
5152: if ( prep[explen] != '$' ) break; /* unmatched $ */
5153: prep[explen] = '\000'; /* remove trailing $ */
5154: prep++; /* and remove matching leading $ */
5155: ndollars++; /* count another pair removed */
5156: } /* --- end-of-while(*prep=='$') --- */
5157: ispreambledollars = ndollars; /* set flag to fix \displaystyle */
5158: if ( ndollars == 1 ) /* user submitted $...$ expression */
5159: isdisplaystyle = 0; /* so set \textstyle */
5160: if ( ndollars > 1 ) /* user submitted $$...$$ */
5161: isdisplaystyle = 2; /* so set \displaystyle */
5162: /*goto process_preamble;*/ /*check for preamble after leading $*/
5163: } /* --- end-of-if/else(prelen>0) --- */
1.3 ! albertel 5164: } /* --- end-of-if(dollar!=NULL) --- */
1.1 albertel 5165: /* -------------------------------------------------------------------------
5166: back to caller
5167: -------------------------------------------------------------------------- */
5168: end_of_job:
5169: return ( prep ); /*expression, or ptr past preamble*/
5170: } /* --- end-of-function preamble() --- */
5171:
5172:
5173: /* ==========================================================================
5174: * Function: mimeprep ( expression )
5175: * Purpose: preprocessor for mimeTeX input, e.g.,
5176: * (a) removes comments,
5177: * (b) converts \left( to \( and \right) to \),
5178: * (c) xlates &html; special chars to equivalent latex
5179: * Should only be called once (after unescape_url())
5180: * --------------------------------------------------------------------------
5181: * Arguments: expression (I/O) char * to first char of null-terminated
5182: * string containing mimeTeX/LaTeX expression,
5183: * and returning preprocessed string
5184: * --------------------------------------------------------------------------
5185: * Returns: ( char * ) ptr to input expression,
5186: * or NULL for any parsing error.
5187: * --------------------------------------------------------------------------
5188: * Notes: o
5189: * ======================================================================= */
5190: /* --- entry point --- */
5191: char *mimeprep ( char *expression )
5192: {
5193: /* -------------------------------------------------------------------------
5194: Allocations and Declarations
5195: -------------------------------------------------------------------------- */
5196: char *expptr=expression, /* ptr within expression */
5197: *tokptr=NULL, /*ptr to token found in expression*/
5198: *texsubexpr(), argval[8192]; /*parse for macro args after token*/
5199: char *strchange(); /* change leading chars of string */
1.3 ! albertel 5200: char *strwstr(); /*use strwstr() instead of strstr()*/
1.1 albertel 5201: char *findbraces(); /*find left { and right } for \atop*/
5202: int idelim=0, /* left- or right-index */
5203: isymbol=0; /*symbols[],rightcomment[],etc index*/
5204: int xlateleft = 0; /* true to xlate \left and \right */
5205: /* ---
5206: * comments
5207: * -------- */
5208: char *leftptr=NULL; /* find leftcomment in expression */
5209: static char *leftcomment = "%%", /* open comment */
5210: *rightcomment[] = {"\n", "%%", NULL}; /* close comments */
5211: /* ---
5212: * special long (more than 1-char) \left and \right delimiters
5213: * ----------------------------------------------------------- */
5214: static char *leftfrom[] = /* xlate any \left suffix... */
5215: { "\\|", /* \left\| */
5216: "\\{", /* \left\{ */
5217: "\\langle", /* \left\langle */
5218: NULL } ; /* --- end-of-leftfrom[] --- */
5219: static char *leftto[] = /* ...to this instead */
5220: { "=", /* = */
5221: "{", /* { */
5222: "<", /* < */
5223: NULL } ; /* --- end-of-leftto[] --- */
5224: static char *rightfrom[] = /* xlate any \right suffix... */
5225: { "\\|", /* \right\| */
5226: "\\}", /* \right\} */
5227: "\\rangle", /* \right\rangle */
5228: NULL } ; /* --- end-of-rightfrom[] --- */
5229: static char *rightto[] = /* ...to this instead */
5230: { "=", /* = */
5231: "}", /* } */
5232: ">", /* > */
5233: NULL } ; /* --- end-of-rightto[] --- */
5234: /* ---
5235: * { \atop }-like commands
5236: * ----------------------- */
5237: char *atopsym=NULL; /* atopcommands[isymbol] */
5238: static char *atopcommands[] = /* list of {a+b\command c+d}'s */
5239: { "\\over", /* plain tex for \frac */
5240: "\\choose", /* binomial coefficient */
5241: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
5242: "\\atop",
5243: #endif
5244: NULL } ; /* --- end-of-atopcommands[] --- */
5245: static char *atopdelims[] = /* delims for atopcommands[] */
5246: { NULL, NULL, /* \\over has no delims */
5247: "\\left(", "\\right)", /* \\choose has ( ) delims*/
5248: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
5249: NULL, NULL, /* \\atop has no delims */
5250: #endif
5251: NULL, NULL } ; /* --- end-of-atopdelims[] --- */
5252: /* ---
5253: * html special/escape chars converted to latex equivalents
5254: * -------------------------------------------------------- */
5255: char *htmlsym=NULL; /* symbols[isymbol].html */
5256: static struct { char *html; char *args; char *latex; } symbols[] =
5257: { /* ---------------------------------------
5258: user-supplied newcommands
5259: --------------------------------------- */
5260: #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */
5261: #include NEWCOMMANDS
5262: #endif
1.3 ! albertel 5263: /* ---------------------------------------
! 5264: Cyrillic termchar mimeTeX equivalent...
! 5265: --------------------------------------- */
! 5266: { "\\\'G", "embed\\","{\\acute{G}}" },
! 5267: { "\\\'g", "embed\\","{\\acute{g}}" },
! 5268: { "\\\'K", "embed\\","{\\acute{K}}" },
! 5269: { "\\\'k", "embed\\","{\\acute{k}}" },
! 5270: { "\\u U", "embed\\","{\\breve{U}}" },
! 5271: { "\\u u", "embed\\","{\\breve{u}}" },
! 5272: /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/
! 5273: /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
! 5274: { "\\\"I", "embed\\","{\\ddot{\\=I}}" },
! 5275: { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
1.1 albertel 5276: /* ------------------------------------------
5277: LaTeX Macro #args,default template...
5278: ------------------------------------------ */
5279: { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
1.2 albertel 5280: { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
5281: { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
5282: { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
5283: { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
1.3 ! albertel 5284: { "\\buildrel","3", "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
1.1 albertel 5285: { "\\overset", NULL, "\\stackrel" }, /* just an alias */
5286: { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
5287: /* ---------------------------------------
1.2 albertel 5288: html char termchar LaTeX equivalent...
1.1 albertel 5289: --------------------------------------- */
5290: { """, ";", "\"" }, /* " is first, " */
5291: { "&", ";", "&" },
5292: { "<", ";", "<" },
5293: { ">", ";", ">" },
1.3 ! albertel 5294: { "\", ";", "\\" }, /* backslash */
! 5295: { "&backslash",";", "\\" },
1.1 albertel 5296: { " ", ";", "~" },
5297: { "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
5298: { "¦", ";", "|" },
5299: { "±", ";", "\\pm" },
5300: { "²", ";", "{{}^2}" },
5301: { "³", ";", "{{}^3}" },
5302: { "µ", ";", "\\mu" },
5303: { "¹", ";", "{{}^1}" },
5304: { "¼", ";", "{\\frac14}" },
5305: { "½", ";", "{\\frac12}" },
5306: { "¾", ";", "{\\frac34}" },
5307: { "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
5308: { "Â", ";", "{\\rm~\\hat~A}" },
5309: { "Ã", ";", "{\\rm~\\tilde~A}" },
5310: { "Ä", ";", "{\\rm~\\ddot~A}" },
5311: { "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
5312: { "ã", ";", "{\\rm~\\tilde~a}" },
5313: { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
5314: /* ---------------------------------------
1.2 albertel 5315: html tag termchar LaTeX equivalent...
5316: --------------------------------------- */
1.3 ! albertel 5317: { "<br>", "embed\\i","\\\\" },
! 5318: { "<br/>", "embed\\i","\\\\" },
! 5319: /* ---------------------------------------
! 5320: garbage termchar LaTeX equivalent...
! 5321: --------------------------------------- */
! 5322: { "< TEX >", "embed\\i","\000" },
! 5323: { "< / TEX >","embed\\i","\000" },
! 5324: { "<br / >", "embed\\i","\000" },
1.2 albertel 5325: /* ---------------------------------------
1.1 albertel 5326: LaTeX termchar mimeTeX equivalent...
5327: --------------------------------------- */
5328: { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
5329: { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
5330: { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
5331: { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
1.2 albertel 5332: { "\\dots", NULL, "{\\cdots}" },
1.1 albertel 5333: { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
5334: { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
5335: { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
5336: { "\\notin", NULL, "{\\not\\in}" },
5337: { "\\neq", NULL, "{\\not=}" },
1.2 albertel 5338: { "\\ne", NULL, "{\\not=}" },
1.1 albertel 5339: { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
5340: { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
1.2 albertel 5341: { "\\textcelsius", NULL, "{\\textdegree C}"},
5342: { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
1.1 albertel 5343: { "\\cr", NULL, "\\\\" },
1.2 albertel 5344: { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
1.1 albertel 5345: { "\\iint", NULL, "{\\int\\int}\\limits" },
5346: { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
1.2 albertel 5347: { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
1.1 albertel 5348: { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
5349: { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
1.2 albertel 5350: { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
5351: { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
5352: { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
5353: { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
5354: { "\'", "embed","^{\\fs{-1}\\prime}" },
1.1 albertel 5355: { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
1.2 albertel 5356: { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
1.1 albertel 5357: { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
5358: { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
5359: { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
5360: { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
5361: { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
1.2 albertel 5362: { "\\cancel",NULL, "\\Not" },
1.1 albertel 5363: { "\\hhline",NULL, "\\Hline" },
5364: { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
5365: /* ---------------------------------------------------------
5366: "Algebra Syntax" termchar mimeTeX/LaTeX equivalent...
5367: ------------------------------------------------------------ */
5368: { "sqrt", "1", "{\\sqrt{#1}}" },
5369: { "sin", "1", "{\\sin{#1}}" },
5370: { "cos", "1", "{\\cos{#1}}" },
5371: { "asin", "1", "{\\sin^{-1}{#1}}" },
5372: { "acos", "1", "{\\cos^{-1}{#1}}" },
1.2 albertel 5373: { "exp", "1", "{{\\rm~e}^{#1}}" },
1.1 albertel 5374: { "det", "1", "{\\left|{#1}\\right|}" },
5375: /* ---------------------------------------
5376: LaTeX Constant termchar value...
5377: --------------------------------------- */
5378: { "\\thinspace", NULL, "2" },
5379: { "\\thinmathspace", NULL, "2" },
5380: { "\\textwidth", NULL, "400" },
5381: { NULL, NULL, NULL }
5382: } ; /* --- end-of-symbols[] --- */
5383: /* -------------------------------------------------------------------------
5384: first remove comments
5385: -------------------------------------------------------------------------- */
5386: expptr = expression; /* start search at beginning */
5387: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
5388: {
5389: char *rightsym=NULL; /* rightcomment[isymbol] */
5390: expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
5391: /* --- check for any closing rightcomment, in given precedent order --- */
5392: if ( *expptr != '\000' ) /*have chars after this leftcomment*/
5393: for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
5394: if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
5395: { tokptr += strlen(rightsym); /* first char after rightcomment */
5396: if ( *tokptr == '\000' ) /*nothing after this rightcomment*/
5397: { *leftptr = '\000'; /*so terminate expr at leftcomment*/
5398: break; } /* and stop looking for comments */
5399: *leftptr = '~'; /* replace entire comment by ~ */
5400: strcpy(leftptr+1,tokptr); /* and squeeze out comment */
5401: goto next_comment; } /* stop looking for rightcomment */
5402: /* --- no rightcomment after opening leftcomment --- */
5403: *leftptr = '\000'; /* so terminate expression */
5404: /* --- resume search past squeezed-out comment --- */
5405: next_comment:
5406: if ( *leftptr == '\000' ) break; /* reached end of expression */
5407: expptr = leftptr+1; /*resume search after this comment*/
5408: } /* --- end-of-while(leftptr!=NULL) --- */
5409: /* -------------------------------------------------------------------------
5410: run thru table, converting all occurrences of each macro to its expansion
5411: -------------------------------------------------------------------------- */
5412: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
5413: {
5414: int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
5415: int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
1.3 ! albertel 5416: int isembedded = 0, /* true to xlate even if embedded */
! 5417: isstrwstr = 0, /* true to use strwstr() */
! 5418: wstrlen = 0; /* length of strwstr() match */
1.1 albertel 5419: char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
1.3 ! albertel 5420: char embedkeywd[99] = "embed", /* keyword to signal embedded token*/
! 5421: embedterm = '\000'; /* char immediately after embed */
! 5422: int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */
1.1 albertel 5423: char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
5424: *htmlterm = args, /*if *args nonumeric, then html term*/
5425: *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
5426: char abuff[8192]; int iarg,nargs=0; /* macro expansion params */
1.3 ! albertel 5427: char wstrwhite[99]; /* whitespace chars for strwstr() */
1.1 albertel 5428: if ( args != NULL ) /*we have args (or htmlterm) param*/
1.3 ! albertel 5429: if ( *args != '\000' ) { /* and it's not an empty string */
1.1 albertel 5430: if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
5431: { htmlterm = NULL; /* if so, then we have no htmlterm */
5432: *abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */
5433: nargs = atoi(abuff); } /* interpret #args to numeric */
1.3 ! albertel 5434: else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
1.2 albertel 5435: { htmlterm = NULL; /* if so, then we have no htmlterm */
1.3 ! albertel 5436: isembedded = 1 ; /* turn on embedded flag */
! 5437: embedterm = args[embedlen]; /* char immediately after embed */
! 5438: if (strlen(args) > embedlen+1) { /* have embed,white for strwstr() */
! 5439: isstrwstr = 1; /* turn on strwtsr flag */
! 5440: strcpy(wstrwhite,args+6); } } /* and set its whitespace arg */
! 5441: } /* --- end-of-if(*args!='\000') --- */
1.1 albertel 5442: expptr = expression; /* re-start search at beginning */
1.3 ! albertel 5443: while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
! 5444: strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
! 5445: != NULL ) /* found another sym */
! 5446: { int toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
! 5447: char termchar = *(tokptr+toklen), /* char terminating html sequence */
1.2 albertel 5448: prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
1.3 ! albertel 5449: int escapelen = toklen; /* total length of escape sequence */
1.1 albertel 5450: *abuff = '\000'; /* default to empty string */
5451: if ( latexsym != NULL ) /* table has .latex xlation */
5452: if ( *latexsym != '\000' ) /* and it's not an empty string */
5453: strcpy(abuff,latexsym); /* so get local copy */
5454: if ( htmlterm != NULL ) /* sequence may have terminator */
5455: escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
1.2 albertel 5456: if ( !isembedded ) /* don't xlate embedded sequence */
5457: if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
1.3 ! albertel 5458: { expptr = tokptr+toklen; /* just resume search after prefix */
1.1 albertel 5459: continue; } /* but don't replace it */
1.2 albertel 5460: if ( isembedded ) /* for embedded sequence */
1.3 ! albertel 5461: if ( !isthischar(embedterm,ESCAPE) /* don't xlate escaped \token */
! 5462: && isthischar(prevchar,ESCAPE) ) /* and we have escaped \token */
! 5463: { expptr = tokptr+toklen; /*just resume search after literal*/
1.2 albertel 5464: continue; } /* but don't replace it */
1.1 albertel 5465: if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */
1.2 albertel 5466: && isalpha(*htmlsym) /* and our symbol starts with alpha*/
1.1 albertel 5467: && !isthischar(*htmlsym,"&") ) /* and not an &html; special char */
5468: if ( tokptr != expression ) /* then if we're past beginning */
5469: if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
5470: || (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */
5471: { expptr = tokptr+escapelen; /*just resume search after literal*/
5472: continue; } /* but don't replace it */
5473: if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/
5474: {
5475: char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
5476: char *optarg = args+1; /* ptr 1 char past #args digit 0-9 */
5477: expptr = arg1ptr; /* ptr to beginning of next arg */
5478: for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
5479: {
5480: char argsignal[32] = "#1", /* #1...#9 signals arg replacement */
5481: *argsigptr = NULL; /* ptr to argsignal in abuff[] */
5482: /* --- get argument value --- */
5483: *argval = '\000'; /* init arg as empty string */
5484: skipwhite(expptr); /* and skip leading white space */
5485: if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
5486: && !isalgebra ) /* but not in "algebra syntax" */
5487: { strcpy(argval,optarg); /* init with default value */
5488: if ( *expptr == '[' ) /* but user gave us [argval] */
5489: expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
5490: else /* not optional, so get {argval} */
1.3 ! albertel 5491: if ( *expptr != '\000' ) { /* check that some argval provided */
1.1 albertel 5492: if ( !isalgebra ) /* only { } delims for latex macro */
5493: expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/
5494: else /*any delim for algebra syntax macro*/
5495: { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
5496: if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
5497: if ( *argval != '{' ) /* and it's not { }-enclosed */
5498: { strchange(0,argval,"\\left"); /* insert opening \left, */
1.3 ! albertel 5499: strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
! 5500: } /* --- end-of-if(*expptr!='\000') --- */
1.1 albertel 5501: /* --- replace #`iarg` in macro with argval --- */
5502: sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */
5503: while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
5504: strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/
5505: while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
5506: strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
5507: } /* --- end-of-for(iarg) --- */
5508: escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
5509: } /* --- end-of-if(nargs>0) --- */
5510: strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
5511: expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
5512: } /* --- end-of-while(tokptr!=NULL) --- */
5513: } /* --- end-of-for(isymbol) --- */
5514: /* -------------------------------------------------------------------------
1.3 ! albertel 5515: convert \left( to \( and \right) to \), etc.
! 5516: -------------------------------------------------------------------------- */
! 5517: if ( xlateleft ) /* \left...\right xlation wanted */
! 5518: for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left and 1 for \right */
! 5519: {
! 5520: char *lrstr = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
! 5521: int lrlen = (idelim==0?5:6); /* strlen() of \left or \right */
! 5522: char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
! 5523: **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
! 5524: **lrto = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
! 5525: *lrsym = NULL; /* lrfrom[isymbol] */
! 5526: expptr = expression; /* start search at beginning */
! 5527: while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
! 5528: {
! 5529: if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
! 5530: { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/
! 5531: expptr = tokptr+2; } /* and resume search past brace */
! 5532: else /* may be a "long" brace like \| */
! 5533: {
! 5534: expptr = tokptr+lrlen; /*init to resume search past\left\rt*/
! 5535: for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
! 5536: { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
! 5537: if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
! 5538: { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
! 5539: *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
! 5540: expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
! 5541: break; } /* no need to check more lrsym's */
! 5542: } /* --- end-of-for(isymbol) --- */
! 5543: } /* --- end-of-if/else(isthischar()) --- */
! 5544: } /* --- end-of-while(tokptr!=NULL) --- */
! 5545: } /* --- end-of-for(idelim) --- */
! 5546: /* -------------------------------------------------------------------------
1.1 albertel 5547: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
5548: -------------------------------------------------------------------------- */
5549: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
5550: {
5551: int atoplen = strlen(atopsym); /* #chars in \atop */
5552: expptr = expression; /* re-start search at beginning */
5553: while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
5554: { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
5555: char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
5556: if ( msgfp!=NULL && msglevel>=999 )
5557: { fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
5558: (int)(tokptr-expression),tokptr);
5559: fflush(msgfp); }
5560: if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
5561: { expptr = tokptr+atoplen; /* just resume search after prefix */
5562: continue; } /* but don't process it */
5563: leftbrace = findbraces(expression,tokptr); /* find left { */
5564: rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
5565: if ( leftbrace==NULL || rightbrace==NULL )
5566: { expptr += atoplen; continue; } /* skip command if didn't find */
5567: else /* we have bracketed { \atop } */
5568: {
5569: int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
5570: rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
5571: totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
5572: char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
5573: char arg[8192], command[8192]; /* left/right args, new \atop{}{} */
5574: *command = '\000'; /* start with null string */
5575: if (open!=NULL) strcat(command,open); /* add open delim if needed */
5576: strcat(command,atopsym); /* add command with \atop */
5577: arg[0] = '{'; /* arg starts with { */
5578: memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
5579: arg[leftlen+1] = '\000'; /* and null terminate it */
5580: strcat(command,arg); /* concatanate {left-arg to \atop */
5581: strcat(command,"}{"); /* close left-arg, open right-arg */
5582: memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
5583: arg[rightlen] = '}'; /* add closing } */
5584: arg[rightlen+1] = '\000'; /* and null terminate it */
5585: if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
5586: strcpy(arg,arg+1); /* so squeeze it out */
5587: strcat(command,arg); /* concatanate right-arg} */
5588: if (close!=NULL) strcat(command,close); /* add close delim if needed*/
5589: strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
5590: expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
5591: }
5592: } /* --- end-of-while(tokptr!=NULL) --- */
5593: } /* --- end-of-for(isymbol) --- */
5594: /* -------------------------------------------------------------------------
5595: back to caller with preprocessed expression
5596: -------------------------------------------------------------------------- */
5597: if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */
5598: { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
5599: fflush(msgfp); }
5600: return ( expression );
5601: } /* --- end-of-function mimeprep() --- */
5602:
5603:
5604: /* ==========================================================================
5605: * Function: strchange ( int nfirst, char *from, char *to )
5606: * Purpose: Changes the nfirst leading chars of `from` to `to`.
5607: * For example, to change char x[99]="12345678" to "123ABC5678"
5608: * call strchange(1,x+3,"ABC")
5609: * --------------------------------------------------------------------------
5610: * Arguments: nfirst (I) int containing #leading chars of `from`
5611: * that will be replace by `to`
5612: * from (I/O) char * to null-terminated string whose nfirst
5613: * leading chars will be replaced by `to`
5614: * to (I) char * to null-terminated string that will
5615: * replace the nfirst leading chars of `from`
5616: * --------------------------------------------------------------------------
5617: * Returns: ( char * ) ptr to first char of input `from`
5618: * or NULL for any error.
5619: * --------------------------------------------------------------------------
5620: * Notes: o If strlen(to)>nfirst, from must have memory past its null
5621: * (i.e., we don't do a realloc)
5622: * ======================================================================= */
5623: /* --- entry point --- */
5624: char *strchange ( int nfirst, char *from, char *to )
5625: {
5626: /* -------------------------------------------------------------------------
5627: Allocations and Declarations
5628: -------------------------------------------------------------------------- */
5629: int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
5630: nshift = abs(tolen-nfirst); /*need to shift from left or right*/
5631: /* -------------------------------------------------------------------------
5632: shift from left or right to accommodate replacement of its nfirst chars by to
5633: -------------------------------------------------------------------------- */
5634: if ( tolen < nfirst ) /* shift left is easy */
5635: strcpy(from,from+nshift); /* because memory doesn't overlap */
5636: if ( tolen > nfirst ) /* need more room at start of from */
5637: { char *pfrom = from+strlen(from); /* ptr to null terminating from */
5638: for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
5639: *(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */
5640: /* -------------------------------------------------------------------------
5641: from has exactly the right number of free leading chars, so just put to there
5642: -------------------------------------------------------------------------- */
5643: if ( tolen != 0 ) /* make sure to not empty or null */
5644: memcpy(from,to,tolen); /* chars moved into place */
5645: return ( from ); /* changed string back to caller */
5646: } /* --- end-of-function strchange() --- */
5647:
5648:
5649: /* ==========================================================================
5650: * Function: strreplace (char *string, char *from, char *to, int nreplace)
5651: * Purpose: Changes the first nreplace occurrences of 'from' to 'to'
5652: * in string, or all occurrences if nreplace=0.
5653: * --------------------------------------------------------------------------
5654: * Arguments: string (I/0) char * to null-terminated string in which
5655: * occurrence of 'from' will be replaced by 'to'
5656: * from (I) char * to null-terminated string
5657: * to be replaced by 'to'
5658: * to (I) char * to null-terminated string that will
5659: * replace 'from'
5660: * nreplace (I) int containing (maximum) number of
5661: * replacements, or 0 to replace all.
5662: * --------------------------------------------------------------------------
5663: * Returns: ( int ) number of replacements performed,
5664: * or 0 for no replacements or -1 for any error.
5665: * --------------------------------------------------------------------------
5666: * Notes: o
5667: * ======================================================================= */
5668: /* --- entry point --- */
5669: int strreplace ( char *string, char *from, char *to, int nreplace )
5670: {
5671: /* -------------------------------------------------------------------------
5672: Allocations and Declarations
5673: -------------------------------------------------------------------------- */
5674: int fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
5675: tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
5676: char *pfrom = (char *)NULL, /*ptr to 1st char of from in string*/
5677: *pstring = string, /*ptr past previously replaced from*/
5678: *strchange(); /* change 'from' to 'to' */
5679: int nreps = 0; /* #replacements returned to caller*/
5680: /* -------------------------------------------------------------------------
5681: repace occurrences of 'from' in string to 'to'
5682: -------------------------------------------------------------------------- */
5683: if ( string == (char *)NULL /* no input string */
5684: || (fromlen<1 && nreplace<=0) ) /* replacing empty string forever */
5685: nreps = (-1); /* so signal error */
5686: else /* args okay */
5687: while (nreplace<1 || nreps<nreplace) /* up to #replacements requested */
5688: {
5689: if ( fromlen > 0 ) /* have 'from' string */
5690: pfrom = strstr(pstring,from); /*ptr to 1st char of from in string*/
5691: else pfrom = pstring; /*or empty from at start of string*/
5692: if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
5693: if ( strchange(fromlen,pfrom,to) /* leading 'from' changed to 'to' */
5694: == (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
5695: nreps++; /* count another replacement */
5696: pstring = pfrom+tolen; /* pick up search after 'to' */
5697: if ( *pstring == '\000' ) break; /* but quit at end of string */
5698: } /* --- end-of-while() --- */
5699: return ( nreps ); /* #replacements back to caller */
5700: } /* --- end-of-function strreplace() --- */
5701:
5702:
5703: /* ==========================================================================
1.3 ! albertel 5704: * Function: strwstr (char *string, char *substr, char *white, int *sublen)
! 5705: * Purpose: Find first substr in string, but wherever substr contains
! 5706: * a whitespace char (in white), string may contain any number
! 5707: * (including 0) of whitespace chars. If white contains I or i,
! 5708: * then match is case-insensitive (and I,i _not_ whitespace).
! 5709: * --------------------------------------------------------------------------
! 5710: * Arguments: string (I) char * to null-terminated string in which
! 5711: * first occurrence of substr will be found
! 5712: * substr (I) char * to null-terminated string containing
! 5713: * "template" that will be searched for
! 5714: * white (I) char * to null-terminated string containing
! 5715: * whitespace chars. If NULL or empty, then
! 5716: * "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
! 5717: * If white contains I or i, then match is
! 5718: * case-insensitive (and I,i _not_ considered
! 5719: * whitespace).
! 5720: * sublen (O) address of int returning "length" of substr
! 5721: * found in string (which may be longer or
! 5722: * shorter than substr itself).
! 5723: * --------------------------------------------------------------------------
! 5724: * Returns: ( char * ) ptr to first char of substr in string
! 5725: * or NULL if not found or for any error.
! 5726: * --------------------------------------------------------------------------
! 5727: * Notes: o Wherever a single whitespace char appears in substr,
! 5728: * the corresponding position in string may contain any
! 5729: * number (including 0) of whitespace chars, e.g.,
! 5730: * string="abc def" and string="abcdef" both match
! 5731: * substr="c d" at offset 2 of string.
! 5732: * o If substr="c d" (two spaces between c and d),
! 5733: * then string must have at least one space, so now "abcdef"
! 5734: * doesn't match. In general, the minimum number of spaces
! 5735: * in string is the number of spaces in substr minus 1
! 5736: * (so 1 space in substr permits 0 spaces in string).
! 5737: * o Embedded spaces are counted in sublen, e.g.,
! 5738: * string="c d" (three spaces) matches substr="c d"
! 5739: * with sublen=5 returned. But string="ab c d" will
! 5740: * also match substr=" c d" returning sublen=5 and
! 5741: * a ptr to the "c". That is, the mandatory preceding
! 5742: * space is _not_ counted as part of the match.
! 5743: * But all the embedded space is counted.
! 5744: * (An inconsistent bug/feature is that mandatory
! 5745: * terminating space is counted.)
! 5746: * o Moreover, string="c d" matches substr=" c d", i.e.,
! 5747: * the very beginning of a string is assumed to be preceded
! 5748: * by "virtual blanks".
! 5749: * ======================================================================= */
! 5750: /* --- entry point --- */
! 5751: char *strwstr ( char *string, char *substr, char *white, int *sublen )
! 5752: {
! 5753: /* -------------------------------------------------------------------------
! 5754: Allocations and Declarations
! 5755: -------------------------------------------------------------------------- */
! 5756: char *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
! 5757: *pfound = (char *)NULL; /*ptr to found substr back to caller*/
! 5758: char *pwhite=NULL, whitespace[256]; /* callers white whithout i,I */
! 5759: int iscase = (white==NULL?1: /* case-sensitive if i,I in white */
! 5760: strchr(white,'i')==NULL && strchr(white,'I')==NULL);
! 5761: int foundlen = 0; /* length of substr found in string*/
! 5762: int nstrwhite=0, nsubwhite=0, /* #leading white chars in str,sub */
! 5763: nminwhite=0; /* #mandatory leading white in str */
! 5764: int nstrchars=0, nsubchars=0, /* #non-white chars to be matched */
! 5765: isncmp=0; /*strncmp() or strncasecmp() result*/
! 5766: /* -------------------------------------------------------------------------
! 5767: Initialization
! 5768: -------------------------------------------------------------------------- */
! 5769: /* --- set up whitespace --- */
! 5770: strcpy(whitespace,WHITEMATH); /*default if no user input for white*/
! 5771: if ( white != NULL ) /*user provided ptr to white string*/
! 5772: if ( *white != '\000' ) { /*and it's not just an empty string*/
! 5773: strcpy(whitespace,white); /* so use caller's white spaces */
! 5774: while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
! 5775: strcpy(pwhite,pwhite+1); /* so squeeze it out */
! 5776: while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
! 5777: strcpy(pwhite,pwhite+1); /* so squeeze it out */
! 5778: if ( *whitespace == '\000' ) /* caller's white just had i,I */
! 5779: strcpy(whitespace,WHITEMATH); } /* so revert back to default */
! 5780: /* -------------------------------------------------------------------------
! 5781: Find first occurrence of substr in string
! 5782: -------------------------------------------------------------------------- */
! 5783: if ( string != NULL ) /* caller passed us a string ptr */
! 5784: while ( *pstring != '\000' ) { /* break when string exhausted */
! 5785: char *pstrptr = pstring; /* (re)start at next char in string*/
! 5786: int leadingwhite = 0; /* leading whitespace */
! 5787: psubstr = substr; /* start at beginning of substr */
! 5788: foundlen = 0; /* reset length of found substr */
! 5789: if ( substr != NULL ) /* caller passed us a substr ptr */
! 5790: while ( *psubstr != '\000' ) { /*see if pstring begins with substr*/
! 5791: /* --- check for end-of-string before finding match --- */
! 5792: if ( *pstrptr == '\000' ) /* end-of-string without a match */
! 5793: goto nextstrchar; /* keep trying with next char */
! 5794: /* --- actual amount of whitespace in string and substr --- */
! 5795: nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
! 5796: nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
! 5797: nminwhite = max2(0,nsubwhite-1); /* #mandatory leading white in str */
! 5798: /* --- check for mandatory leading whitespace in string --- */
! 5799: if ( pstrptr != string ) /*not mandatory at start of string*/
! 5800: if ( nstrwhite < nminwhite ) /* too little leading white space */
! 5801: goto nextstrchar; /* keep trying with next char */
! 5802: /* ---hold on to #whitespace chars in string preceding substr match--- */
! 5803: if ( pstrptr == pstring ) /* whitespace at start of substr */
! 5804: leadingwhite = nstrwhite; /* save it as leadingwhite */
! 5805: /* --- check for optional whitespace --- */
! 5806: if ( psubstr != substr ) /* always okay at start of substr */
! 5807: if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */
! 5808: goto nextstrchar; /* keep trying with next char */
! 5809: /* --- skip any leading whitespace in substr and string --- */
! 5810: psubstr += nsubwhite; /* push past leading sub whitespace*/
! 5811: pstrptr += nstrwhite; /* push past leading str whitespace*/
! 5812: /* --- now get non-whitespace chars that we have to match --- */
! 5813: nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
! 5814: nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
! 5815: if ( nstrchars < nsubchars ) /* too few chars for match */
! 5816: goto nextstrchar; /* keep trying with next char */
! 5817: /* --- see if next nsubchars are a match --- */
! 5818: isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
! 5819: strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
! 5820: if ( isncmp != 0 ) /* no match */
! 5821: goto nextstrchar; /* keep trying with next char */
! 5822: /* --- push past matched chars --- */
! 5823: psubstr += nsubchars; pstrptr += nsubchars; /*nsubchars were matched*/
! 5824: } /* --- end-of-while(*psubstr!='\000') --- */
! 5825: pfound = pstring + leadingwhite; /* found match starting at pstring */
! 5826: foundlen = (int)(pstrptr-pfound); /* consisting of this many chars */
! 5827: goto end_of_job; /* back to caller */
! 5828: /* ---failed to find substr, continue trying with next char in string--- */
! 5829: nextstrchar: /* continue outer loop */
! 5830: pstring++; /* bump to next char in string */
! 5831: } /* --- end-of-while(*pstring!='\000') --- */
! 5832: /* -------------------------------------------------------------------------
! 5833: Back to caller with ptr to first occurrence of substr in string
! 5834: -------------------------------------------------------------------------- */
! 5835: end_of_job:
! 5836: if ( msglevel>=999 && msgfp!=NULL) { /* debugging/diagnostic output */
! 5837: fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
! 5838: string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
! 5839: if ( sublen != NULL ) /*caller wants length of found substr*/
! 5840: *sublen = foundlen; /* give it to him along with ptr */
! 5841: return ( pfound ); /*ptr to first found substr, or NULL*/
! 5842: } /* --- end-of-function strwstr() --- */
! 5843:
! 5844:
! 5845: /* ==========================================================================
! 5846: * Function: strtexchr ( char *string, char *texchr )
1.1 albertel 5847: * Purpose: Find first texchr in string, but texchr must be followed
5848: * by non-alpha
5849: * --------------------------------------------------------------------------
5850: * Arguments: string (I) char * to null-terminated string in which
1.3 ! albertel 5851: * first occurrence of delim will be found
1.1 albertel 5852: * texchr (I) char * to null-terminated string that
5853: * will be searched for
5854: * --------------------------------------------------------------------------
5855: * Returns: ( char * ) ptr to first char of texchr in string
5856: * or NULL if not found or for any error.
5857: * --------------------------------------------------------------------------
5858: * Notes: o texchr should contain its leading \, e.g., "\\left"
5859: * ======================================================================= */
5860: /* --- entry point --- */
5861: char *strtexchr ( char *string, char *texchr )
5862: {
5863: /* -------------------------------------------------------------------------
5864: Allocations and Declarations
5865: -------------------------------------------------------------------------- */
1.2 albertel 5866: char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
1.1 albertel 5867: char *pstring = string; /* start or continue up search here*/
5868: int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
5869: /* -------------------------------------------------------------------------
5870: locate texchr in string
5871: -------------------------------------------------------------------------- */
5872: if ( string != (char *)NULL /* check that we got input string */
1.3 ! albertel 5873: && texchrlen > 0 ) { /* and a texchr to search for */
1.1 albertel 5874: while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
5875: != (char *)NULL ) /* found it */
5876: if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */
5877: == '\000' ) break; /* texchr at very end of string */
5878: else /* if there are chars after texchr */
5879: if ( isalpha(delim) /*texchr is prefix of longer symbol*/
5880: || 0 ) /* other tests to be determined */
5881: pstring = ptexchr + texchrlen; /* continue search after texchr */
5882: else /* passed all tests */
1.3 ! albertel 5883: break; } /*so return ptr to texchr to caller*/
1.1 albertel 5884: return ( ptexchr ); /* ptr to texchar back to caller */
5885: } /* --- end-of-function strtexchr() --- */
5886:
5887:
5888: /* ==========================================================================
5889: * Function: findbraces ( char *expression, char *command )
5890: * Purpose: If expression!=NULL, finds opening left { preceding command;
5891: * if expression==NULL, finds closing right } after command.
5892: * For example, to parse out {a+b\over c+d} call findbraces()
5893: * twice.
5894: * --------------------------------------------------------------------------
5895: * Arguments: expression (I) NULL to find closing right } after command,
5896: * or char * to null-terminated string to find
5897: * left opening { preceding command.
5898: * command (I) char * to null-terminated string whose
5899: * first character is usually the \ of \command
5900: * --------------------------------------------------------------------------
5901: * Returns: ( char * ) ptr to either opening { or closing },
5902: * or NULL for any error.
5903: * --------------------------------------------------------------------------
5904: * Notes: o
5905: * ======================================================================= */
5906: /* --- entry point --- */
5907: char *findbraces ( char *expression, char *command )
5908: {
5909: /* -------------------------------------------------------------------------
5910: Allocations and Declarations
5911: -------------------------------------------------------------------------- */
5912: int isopen = (expression==NULL?0:1); /* true to find left opening { */
5913: char *left="{", *right="}", /* delims bracketing {x\command y} */
5914: *delim = (isopen?left:right), /* delim we want, { if isopen */
5915: *match = (isopen?right:left), /* matching delim, } if isopen */
5916: *brace = NULL; /* ptr to delim returned to caller */
5917: int inc = (isopen?-1:+1); /* pointer increment */
5918: int level = 1; /* nesting level, for {{}\command} */
5919: char *ptr = command; /* start search here */
5920: int setbrace = 1; /* true to set {}'s if none found */
5921: /* -------------------------------------------------------------------------
5922: search for left opening { before command, or right closing } after command
5923: -------------------------------------------------------------------------- */
5924: while ( 1 ) /* search for brace, or until end */
5925: {
5926: /* --- next char to check for delim --- */
5927: ptr += inc; /* bump ptr left or right */
5928: /* --- check for beginning or end of expression --- */
5929: if ( isopen ) /* going left, check for beginning */
5930: { if ( ptr < expression ) break; } /* went before start of string */
5931: else { if ( *ptr == '\000' ) break; } /* went past end of string */
5932: /* --- don't check this char if it's escaped --- */
5933: if ( !isopen || ptr>expression ) /* very first char can't be escaped*/
5934: if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */
5935: continue; /* so don't check this char */
5936: /* --- check for delim --- */
5937: if ( isthischar(*ptr,delim) ) /* found delim */
5938: if ( --level == 0 ) /* and it's not "internally" nested*/
5939: { brace = ptr; /* set ptr to brace */
5940: goto end_of_job; } /* and return it to caller */
5941: /* --- check for matching delim --- */
5942: if ( isthischar(*ptr,match) ) /* found matching delim */
5943: level++; /* so bump nesting level */
5944: } /* --- end-of-while(1) --- */
5945: end_of_job:
5946: if ( brace == (char *)NULL ) /* open{ or close} not found */
5947: if ( setbrace ) /* want to force one at start/end? */
5948: brace = ptr; /* { before expressn, } after cmmnd*/
5949: return ( brace ); /*back to caller with delim or NULL*/
5950: } /* --- end-of-function findbraces() --- */
5951: #endif /* PART2 */
5952:
5953: /* ---
5954: * PART3
5955: * ------ */
5956: #if !defined(PARTS) || defined(PART3)
5957: /* ==========================================================================
5958: * Function: rasterize ( expression, size )
5959: * Purpose: returns subraster corresponding to (a valid LaTeX) expression
5960: * at font size
5961: * --------------------------------------------------------------------------
5962: * Arguments: expression (I) char * to first char of null-terminated
5963: * string containing valid LaTeX expression
5964: * to be rasterized
5965: * size (I) int containing 0-4 default font size
5966: * --------------------------------------------------------------------------
5967: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
5968: * or NULL for any parsing error.
5969: * --------------------------------------------------------------------------
5970: * Notes: o This is mimeTeX's "main" reusable entry point. Easy to use:
5971: * just call it with a LaTeX expression, and get back a bitmap
5972: * of that expression. Then do what you want with the bitmap.
5973: * ======================================================================= */
5974: /* --- entry point --- */
5975: subraster *rasterize ( char *expression, int size )
5976: {
5977: /* -------------------------------------------------------------------------
5978: Allocations and Declarations
5979: -------------------------------------------------------------------------- */
1.3 ! albertel 5980: char *preamble(), pretext[512]; /* process preamble, if present */
! 5981: char chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
1.1 albertel 5982: *subexpr = chartoken; /* token may be parenthesized expr */
5983: int isbrace(); /* check subexpr for braces */
5984: mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/
1.3 ! albertel 5985: int ligdef, get_ligature(); /*get symtable[] index for ligature*/
1.1 albertel 5986: int natoms=0; /* #atoms/tokens processed so far */
5987: int type_raster(); /* display debugging output */
5988: subraster *rasterize(), /* recurse */
5989: *rastparen(), /* handle parenthesized subexpr's */
5990: *rastlimits(); /* handle sub/superscripted expr's */
5991: subraster *rastcat(), /* concatanate atom subrasters */
5992: *subrastcpy(), /* copy final result if a charaster*/
5993: *new_subraster(); /* new subraster for isstring mode */
5994: subraster *get_charsubraster(), /* character subraster */
5995: *sp=NULL, *prevsp=NULL, /* raster for current, prev char */
5996: *expraster = (subraster *)NULL; /* raster returned to caller */
5997: int delete_subraster(); /* free everything before returning*/
1.3 ! albertel 5998: int family = fontinfo[fontnum].family; /* current font family */
! 5999: int isleftscript = 0, /* true if left-hand term scripted */
! 6000: wasscripted = 0, /* true if preceding token scripted*/
! 6001: wasdelimscript = 0; /* true if preceding delim scripted*/
1.1 albertel 6002: /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
6003: /* --- global values saved/restored at each recursive iteration --- */
1.2 albertel 6004: int wasstring = isstring, /* initial isstring mode flag */
1.1 albertel 6005: wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
1.2 albertel 6006: oldfontnum = fontnum, /* initial font family */
1.1 albertel 6007: oldfontsize = fontsize, /* initial fontsize */
6008: olddisplaysize = displaysize, /* initial \displaystyle size */
6009: oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
1.2 albertel 6010: oldsmashmargin = smashmargin, /* initial smashmargin */
6011: oldissmashdelta = issmashdelta, /* initial issmashdelta */
1.3 ! albertel 6012: oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
! 6013: oldisscripted = isscripted, /* initial isscripted */
1.1 albertel 6014: *oldworkingparam = workingparam; /* initial working parameter */
6015: subraster *oldworkingbox = workingbox, /* initial working box */
6016: *oldleftexpression = leftexpression; /*left half rasterized so far*/
6017: double oldunitlength = unitlength; /* initial unitlength */
6018: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
6019: /* -------------------------------------------------------------------------
6020: initialization
6021: -------------------------------------------------------------------------- */
6022: recurlevel++; /* wind up one more recursion level*/
6023: leftexpression = NULL; /* no leading left half yet */
6024: isreplaceleft = 0; /* reset replaceleft flag */
1.3 ! albertel 6025: if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */
1.1 albertel 6026: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
6027: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
1.3 ! albertel 6028: if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/
! 6029: fprintf(msgfp,
! 6030: "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
! 6031: recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
1.1 albertel 6032: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
6033: /* -------------------------------------------------------------------------
6034: preocess optional $-terminated preamble preceding expression
6035: -------------------------------------------------------------------------- */
6036: expression = preamble(expression,&size,pretext); /* size may be modified */
6037: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
6038: fontsize = size; /* start at requested size */
6039: if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/
6040: if ( !ispreambledollars ) /* style fixed by $$...$$'s */
6041: isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
6042: /* -------------------------------------------------------------------------
6043: build up raster one character (or subexpression) at a time
6044: -------------------------------------------------------------------------- */
6045: while ( 1 )
6046: {
1.3 ! albertel 6047: /* --- kludge for \= cyrillic ligature --- */
! 6048: isligature = 0; /* no ligature found yet */
! 6049: family = fontinfo[fontnum].family; /* current font family */
! 6050: if ( family == CYR10 ) /* may have cyrillic \= ligature */
! 6051: if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
! 6052: >= 0 ) /* got some ligature */
! 6053: if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
! 6054: isligature = 1; /* signal \= ligature */
1.1 albertel 6055: /* --- get next character/token or subexpression --- */
1.3 ! albertel 6056: subexprptr = expression; /* ptr within expression to subexpr*/
1.1 albertel 6057: expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
6058: subexpr = chartoken; /* "local" copy of chartoken ptr */
6059: leftsymdef = NULL; /* no character identified yet */
6060: sp = NULL; /* no subraster yet */
6061: size = fontsize; /* in case reset by \tiny, etc */
1.3 ! albertel 6062: /*isleftscript = isdelimscript;*/ /*was preceding term scripted delim*/
! 6063: wasscripted = isscripted; /* true if preceding token scripted*/
! 6064: wasdelimscript = isdelimscript; /* preceding \right delim scripted */
! 6065: if(1)isscripted = 0; /* no subscripted expression yet */
! 6066: isdelimscript = 0; /* reset \right delim scripted flag*/
1.1 albertel 6067: /* --- debugging output --- */
1.3 ! albertel 6068: if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */
! 6069: fprintf(msgfp,
! 6070: "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
! 6071: recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
1.1 albertel 6072: if ( expression == NULL /* no more tokens */
6073: && *subexpr == '\000' ) break; /* and this token empty */
6074: if ( *subexpr == '\000' ) break; /* enough if just this token empty */
6075: /* --- check for parenthesized subexpression --- */
6076: if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */
6077: { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
6078: == NULL ) continue; } /* flush it if failed to rasterize */
6079: else /* --- single-character atomic token --- */
6080: if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
6081: {
1.3 ! albertel 6082: /* --- first check for opening $ in \text{ if $n-m$ even} --- */
! 6083: if ( istextmode /* we're in \text mode */
! 6084: && *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
! 6085: char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
! 6086: int exprlen = 0; /* length of $expression$ */
! 6087: int textfontnum = fontnum; /* current text font number */
! 6088: /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
! 6089: if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
! 6090: exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
! 6091: else { /* no closing $ found */
! 6092: exprlen = strlen(expression); /* just assume entire expression */
! 6093: endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
! 6094: exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */
! 6095: if ( exprlen > 0 ) { /* have something between $$ */
! 6096: memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
! 6097: mathexpr[exprlen] = '\000'; /* null-terminate it */
! 6098: fontnum = 0; /* set math mode */
! 6099: sp = rasterize(mathexpr,size); /* and rasterize $expression$ */
! 6100: fontnum = textfontnum; } /* set back to text mode */
! 6101: expression = endptr+1; /* push expression past closing $ */
! 6102: } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
! 6103: else
! 6104: /* --- otherwise, look up mathchardef for atomic token in table --- */
! 6105: if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
! 6106: == NULL ) /* lookup failed */
! 6107: { char literal[512] = "[?]"; /*display for unrecognized literal*/
! 6108: int oldfontnum = fontnum; /* error display in default mode */
! 6109: if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
1.1 albertel 6110: { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
6111: chartoken); fflush(msgfp); }
1.3 ! albertel 6112: sp = (subraster *)NULL; /* init to signal failure */
! 6113: if ( warninglevel < 1 ) continue; /* warnings not wanted */
! 6114: fontnum = 0; /* reset from \mathbb, etc */
! 6115: if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
! 6116: { /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
! 6117: strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
! 6118: strcat(literal,chartoken+1); /* add chars following leading \ */
! 6119: strcat(literal,"?]}"); } /* add closing brace */
! 6120: sp = rasterize(literal,size-1); /* rasterize literal token */
! 6121: fontnum = oldfontnum; /* reset font family */
! 6122: if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
! 6123: else /* --- check if we have special handler to process this token --- */
! 6124: if ( symdef->handler != NULL ) /* have a handler for this token */
! 6125: { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
! 6126: if ( (sp = (subraster *) /* returned void* is subraster* */
! 6127: (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
! 6128: continue; } /* flush token if handler failed */
! 6129: else /* --- no handler, so just get subraster for this character --- */
! 6130: if ( !isstring ) /* rasterizing */
! 6131: { if ( isligature ) /* found a ligature */
! 6132: expression = subexprptr + strlen(symdef->symbol); /*push past it*/
! 6133: if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
1.1 albertel 6134: == NULL ) continue; } /* flush token if failed */
1.3 ! albertel 6135: else /* constructing ascii string */
1.1 albertel 6136: { char *symbol = symdef->symbol; /* symbol for ascii string */
6137: int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
6138: if ( symlen < 1 ) continue; /* no symbol for ascii string */
6139: if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
6140: == NULL ) continue; /* flush token if malloc failed */
6141: sp->type = ASCIISTRING; /* set subraster type */
6142: sp->symdef = symdef; /* and set symbol definition */
6143: sp->baseline = 1; /* default (should be unused) */
6144: strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
6145: /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
1.3 ! albertel 6146: } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
1.1 albertel 6147: /* --- handle any super/subscripts following symbol or subexpression --- */
6148: sp = rastlimits(&expression,size,sp);
1.3 ! albertel 6149: isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
1.1 albertel 6150: /* --- debugging output --- */
1.3 ! albertel 6151: if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */
! 6152: fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
! 6153: recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
! 6154: if ( msglevel >= 9 ) fprintf(msgfp,
! 6155: " isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
! 6156: isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
! 6157: if ( msglevel >= 99 )
! 6158: if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
! 6159: fflush(msgfp); } /* flush msgfp buffer */
1.1 albertel 6160: /* --- accumulate atom or parenthesized subexpression --- */
6161: if ( natoms < 1 /* nothing previous to concat */
6162: || expraster == NULL /* or previous was complete error */
6163: || isreplaceleft ) /* or we're replacing previous */
1.2 albertel 6164: { if ( 1 && expraster!=NULL ) /* probably replacing left */
6165: delete_subraster(expraster); /* so first free original left */
6166: expraster = subrastcpy(sp); /* copy static CHARASTER or left */
1.1 albertel 6167: isreplaceleft = 0; } /* reset replacement flag */
6168: else /*we've already built up atoms so...*/
1.3 ! albertel 6169: if ( sp != NULL ) { /* ...if we have a new term */
! 6170: int prevsmashmargin = smashmargin; /* save current smash margin */
! 6171: if ( isleftscript ) { /* don't smash against scripts */
! 6172: isdelimscript = 0; /* reset \right delim scripted flag*/
! 6173: if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
! 6174: expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
! 6175: smashmargin = prevsmashmargin; } /* restore current smash margin */
1.1 albertel 6176: delete_subraster(prevsp); /* free prev (if not a CHARASTER) */
6177: prevsp = sp; /* current becomes previous */
6178: leftexpression = expraster; /* left half rasterized so far */
6179: /* --- bump count --- */
6180: natoms++; /* bump #atoms count */
6181: } /* --- end-of-while(expression!=NULL) --- */
6182: /* -------------------------------------------------------------------------
6183: back to caller with rasterized expression
6184: -------------------------------------------------------------------------- */
6185: end_of_job:
6186: delete_subraster(prevsp); /* free last (if not a CHARASTER) */
6187: /* --- debugging output --- */
6188: if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
6189: { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
6190: recurlevel,natoms);
6191: if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
6192: type_raster(expraster->image,msgfp); /* display completed raster */
6193: fflush(msgfp); } /* flush msgfp buffer */
1.2 albertel 6194: /* --- set final raster buffer --- */
6195: if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
1.3 ! albertel 6196: { int type = expraster->type; /* type of constructed image */
! 6197: if ( type != FRACRASTER ) /* leave \frac alone */
! 6198: expraster->type = IMAGERASTER; /* set type to constructed image */
1.2 albertel 6199: if ( istextmode ) /* but in text mode */
6200: expraster->type = blanksignal; /* set type to avoid smash */
6201: expraster->size = fontsize; } /* set original input font size */
1.1 albertel 6202: /* --- restore flags/values to original saved values --- */
6203: isstring = wasstring; /* string mode reset */
6204: isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
1.2 albertel 6205: fontnum = oldfontnum; /* font family reset */
1.1 albertel 6206: fontsize = oldfontsize; /* fontsize reset */
6207: displaysize = olddisplaysize; /* \displaystyle size reset */
6208: shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
1.2 albertel 6209: smashmargin = oldsmashmargin; /* smashmargin reset */
6210: issmashdelta = oldissmashdelta; /* issmashdelta reset */
1.3 ! albertel 6211: isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */
! 6212: isscripted = oldisscripted; /* isscripted reset */
1.1 albertel 6213: workingparam = oldworkingparam; /* working parameter reset */
6214: workingbox = oldworkingbox; /* working box reset */
6215: leftexpression = oldleftexpression; /* leftexpression reset */
6216: leftsymdef = oldleftsymdef; /* leftsymdef reset */
6217: unitlength = oldunitlength; /* unitlength reset */
6218: recurlevel--; /* unwind one recursion level */
6219: /* --- return final subraster to caller --- */
6220: return ( expraster );
6221: } /* --- end-of-function rasterize() --- */
6222:
6223:
6224: /* ==========================================================================
6225: * Function: rastparen ( subexpr, size, basesp )
6226: * Purpose: parentheses handler, returns a subraster corresponding to
6227: * parenthesized subexpression at font size
6228: * --------------------------------------------------------------------------
6229: * Arguments: subexpr (I) char ** to first char of null-terminated
6230: * string beginning with a LEFTBRACES
6231: * to be rasterized
6232: * size (I) int containing 0-5 default font size
6233: * basesp (I) subraster * to character (or subexpression)
6234: * immediately preceding leading left{
6235: * (unused, but passed for consistency)
6236: * --------------------------------------------------------------------------
6237: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
6238: * or NULL for any parsing error
6239: * --------------------------------------------------------------------------
6240: * Notes: o This "handler" isn't in the mathchardef symbol table,
6241: * but is called directly from rasterize(), as necessary.
6242: * o Though subexpr is returned unchanged, it's passed as char **
6243: * for consistency with other handlers. Ditto, basesp is unused
6244: * but passed for consistency
6245: * ======================================================================= */
6246: /* --- entry point --- */
6247: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
6248: {
6249: /* -------------------------------------------------------------------------
6250: Allocations and Declarations
6251: -------------------------------------------------------------------------- */
6252: char *expression = *subexpr; /* dereference subexpr to get char* */
6253: int explen = strlen(expression); /* total #chars, including parens */
6254: int isescape = 0, /* true if parens \escaped */
6255: isrightdot = 0, /* true if right paren is \right. */
6256: isleftdot = 0; /* true if left paren is \left. */
1.3 ! albertel 6257: char left[32], right[32]; /* parens enclosing expresion */
! 6258: char noparens[MAXSUBXSZ+1]; /* get subexpr without parens */
1.1 albertel 6259: subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */
6260: int isheight = 1; /*true=full height, false=baseline*/
6261: int height, /* height of rasterized noparens[] */
6262: baseline; /* and its baseline */
1.2 albertel 6263: int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
1.1 albertel 6264: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
6265: subraster *rastcat(); /* concatanate subrasters */
6266: int delete_subraster(); /*in case of error after allocation*/
6267: /* -------------------------------------------------------------------------
6268: rasterize "interior" of expression, i.e., without enclosing parens
6269: -------------------------------------------------------------------------- */
6270: /* --- first see if enclosing parens are \escaped --- */
6271: if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */
6272: isescape = 1; /* so set flag accordingly */
6273: /* --- get expression *without* enclosing parens --- */
6274: strcpy(noparens,expression); /* get local copy of expression */
6275: noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
6276: strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */
6277: /* --- rasterize it --- */
6278: if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
6279: == NULL ) goto end_of_job; /* quit if failed */
6280: /* --- no need to add parentheses for unescaped { --- */
6281: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
6282: goto end_of_job; /* just return sp to caller */
6283: /* -------------------------------------------------------------------------
6284: obtain paren characters to enclose noparens[] raster with
6285: -------------------------------------------------------------------------- */
6286: /* --- first get left and right parens from expression --- */
6287: memset(left,0,16); memset(right,0,16); /* init parens with nulls */
6288: left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */
6289: right[0] = *(expression+explen-1); /* right} is always last char */
6290: isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */
6291: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
6292: /* --- need height of noparens[] raster as minimum parens height --- */
6293: height = (sp->image)->height; /* height of noparens[] raster */
6294: baseline = sp->baseline; /* baseline of noparens[] raster */
6295: if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */
6296: /* --- get best-fit parentheses characters --- */
6297: if ( !isleftdot ) /* if not \left. */
6298: lp = get_delim(left,height+1,family); /* get left paren char */
6299: if ( !isrightdot ) /* and if not \right. */
6300: rp = get_delim(right,height+1,family); /* get right paren char */
6301: if ( (lp==NULL && !isleftdot) /* check that we got left( */
6302: || (rp==NULL && !isrightdot) ) /* and right) if needed */
6303: { delete_subraster(sp); /* if failed, free subraster */
6304: if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
6305: if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
6306: sp = (subraster *)NULL; /* signal error to caller */
6307: goto end_of_job; } /* and quit */
6308: /* -------------------------------------------------------------------------
6309: set paren baselines to center on noparens[] raster, and concat components
6310: -------------------------------------------------------------------------- */
6311: /* --- set baselines to center paren chars on raster --- */
6312: if ( lp != NULL ) /* ignore for \left. */
6313: lp->baseline = baseline + ((lp->image)->height - height)/2;
6314: if ( rp != NULL ) /* ignore for \right. */
6315: rp->baseline = baseline + ((rp->image)->height - height)/2;
6316: /* --- concat lp||sp||rp to obtain final result --- */
6317: if ( lp != NULL ) /* ignore \left. */
6318: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
6319: if ( sp != NULL ) /* succeeded or ignored \left. */
6320: if ( rp != NULL ) /* ignore \right. */
6321: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
6322: /* --- back to caller --- */
6323: end_of_job:
6324: return ( sp );
6325: } /* --- end-of-function rastparen() --- */
6326:
6327:
6328: /* ==========================================================================
6329: * Function: rastlimits ( expression, size, basesp )
6330: * Purpose: \limits, \nolimts, _ and ^ handler,
6331: * dispatches call to rastscripts() or to rastdispmath()
6332: * as necessary, to handle sub/superscripts following symbol
6333: * --------------------------------------------------------------------------
6334: * Arguments: expression (I) char ** to first char of null-terminated
6335: * LaTeX expression (unused/unchanged)
6336: * size (I) int containing base font size (not used,
6337: * just stored in subraster)
6338: * basesp (I) subraster * to current character (or
6339: * subexpression) immediately preceding script
6340: * indicator
6341: * --------------------------------------------------------------------------
6342: * Returns: ( subraster * ) ptr to subraster returned by rastscripts()
6343: * or rastdispmath(), or NULL for any error
6344: * --------------------------------------------------------------------------
6345: * Notes: o
6346: * ======================================================================= */
6347: /* --- entry point --- */
6348: subraster *rastlimits ( char **expression, int size, subraster *basesp )
6349: {
6350: /* -------------------------------------------------------------------------
6351: Allocations and Declarations
6352: -------------------------------------------------------------------------- */
6353: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
6354: *rastcat(), /* may need to concat scripts */
1.3 ! albertel 6355: *rasterize(), /* may need to construct dummy base*/
! 6356: *scriptsp = basesp, /* and this will become the result */
! 6357: *dummybase = basesp; /* for {}_i construct a dummy base */
1.1 albertel 6358: int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
1.2 albertel 6359: int oldsmashmargin = smashmargin; /* save original smashmargin */
1.1 albertel 6360: int type_raster(); /* display debugging output */
1.3 ! albertel 6361: int delete_subraster(); /* free dummybase, if necessary */
! 6362: int rastsmashcheck(); /* check if okay to smash scripts */
1.1 albertel 6363: /* --- to check for \limits or \nolimits preceding scripts --- */
6364: char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
6365: int toklen=0; /* strlen(limtoken) */
6366: mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */
6367: int class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
6368: /* -------------------------------------------------------------------------
6369: determine whether or not to use displaymath
6370: -------------------------------------------------------------------------- */
6371: scriptlevel++; /* first, increment subscript level*/
6372: *limtoken = '\000'; /* no token yet */
1.3 ! albertel 6373: isscripted = 0; /* signal term not (text) scripted */
1.1 albertel 6374: if ( msgfp!=NULL && msglevel>=999 )
6375: { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
6376: scriptlevel,(exprptr==NULL?"null":exprptr)); fflush(msgfp); }
6377: if ( isstring ) goto end_of_job; /* no scripts for ascii string */
6378: /* --- check for \limits or \nolimits --- */
6379: skipwhite(exprptr); /* skip white space before \limits */
6380: if ( exprptr != NULL ) /* expression ptr supplied */
6381: if ( *exprptr != '\000' ) /* something in expression */
6382: exprptr = texchar(exprptr,limtoken); /* retrieve next token */
6383: if ( *limtoken != '\000' ) /* have token */
6384: if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
6385: if ( memcmp("\\limits",limtoken,toklen) == 0 /* may be \limits */
6386: || memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
6387: if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */
1.3 ! albertel 6388: != NULL ) { /* found token in table */
1.1 albertel 6389: if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */
6390: isdisplay = 1; /* so explicitly set displaymath */
6391: else /* wasn't \limits */
6392: if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
1.3 ! albertel 6393: isdisplay = 0; } /* so explicitly reset displaymath */
1.1 albertel 6394: /* --- see if we found \[no]limits --- */
6395: if ( isdisplay != (-1) ) /* explicit directive found */
6396: *expression = exprptr; /* so bump expression past it */
6397: else /* noexplicit directive */
6398: { isdisplay = 0; /* init displaymath flag off */
1.3 ! albertel 6399: if ( isdisplaystyle ) { /* we're in displaystyle math mode */
1.1 albertel 6400: if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */
6401: { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
6402: isdisplay = 1; } /* set flag if mode forced true */
6403: else
6404: if ( isdisplaystyle >= 2 ) /*or mode forced conditionally true*/
6405: { if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
6406: && class!=OPENING && class!=CLOSING /*don't force ('s and )'s*/
6407: && class!=BINARYOP /* don't force binary operators */
6408: && class!=NOVALUE ) /* finally, don't force "images" */
6409: isdisplay = 1; } /* set flag if mode forced true */
6410: else /* determine mode from base symbol */
6411: if ( class == DISPOPER ) /* it's a displaystyle operator */
1.3 ! albertel 6412: isdisplay = 1; } } /* so set flag */
1.1 albertel 6413: /* -------------------------------------------------------------------------
6414: dispatch call to create sub/superscripts
6415: -------------------------------------------------------------------------- */
6416: if ( isdisplay ) /* scripts above/below base symbol */
6417: scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
1.3 ! albertel 6418: else { /* scripts alongside base symbol */
! 6419: if ( dummybase == NULL ) /* no base symbol preceding scripts*/
! 6420: dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
! 6421: issmashokay = 1; /*haven't found a no-smash char yet*/
! 6422: if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
1.1 albertel 6423: scriptsp = basesp; /* so just return unscripted symbol*/
1.3 ! albertel 6424: else { /* symbols followed by scripts */
! 6425: isscripted = 1; /*signal current term text-scripted*/
1.1 albertel 6426: if ( basesp != NULL ) /* have base symbol */
1.3 ! albertel 6427: { /*if(0)smashmargin = 0;*/ /*don't smash script (doesn't work)*/
1.2 albertel 6428: /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
1.3 ! albertel 6429: /* --- smash (or just concat) script raster against base symbol --- */
! 6430: if ( !issmashokay ) /* don't smash leading - */
! 6431: if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
1.2 albertel 6432: scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
1.3 ! albertel 6433: if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
! 6434: /* --- smash (or just concat) scripted term to stuff to its left --- */
! 6435: issmashokay = 1; /* okay to smash base expression */
! 6436: if ( 0 && smashcheck > 1 ) /* smashcheck=2 to check base */
! 6437: /* note -- we _don't_ have base expression available to check */
! 6438: issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
! 6439: if ( !issmashokay ) /* don't smash leading - */
! 6440: if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
! 6441: scriptsp->size = size; } } } /* and set font size */
1.1 albertel 6442: end_of_job:
1.2 albertel 6443: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.3 ! albertel 6444: if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
1.1 albertel 6445: if ( msgfp!=NULL && msglevel>=99 )
6446: { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
6447: scriptlevel,(scriptsp==NULL?"null":"..."));
6448: if ( scriptsp != NULL ) /* have a constructed raster */
6449: type_raster(scriptsp->image,msgfp); /*display constructed raster*/
6450: fflush(msgfp); }
6451: scriptlevel--; /*lastly, decrement subscript level*/
6452: return ( scriptsp );
6453: } /* --- end-of-function rastlimits() --- */
6454:
6455:
6456: /* ==========================================================================
6457: * Function: rastscripts ( expression, size, basesp )
6458: * Purpose: super/subscript handler, returns subraster for the leading
6459: * scripts in expression, whose base symbol is at font size
6460: * --------------------------------------------------------------------------
6461: * Arguments: expression (I/O) char ** to first char of null-terminated
6462: * string beginning with a super/subscript,
6463: * and returning ptr immediately following
6464: * last script character processed.
6465: * size (I) int containing 0-4 default font size
6466: * basesp (I) subraster * to character (or subexpression)
6467: * immediately preceding leading script
6468: * (scripts will be placed relative to base)
6469: * --------------------------------------------------------------------------
6470: * Returns: ( subraster * ) ptr to subraster corresponding to scripts,
6471: * or NULL for any parsing error
6472: * --------------------------------------------------------------------------
6473: * Notes: o This "handler" isn't in the mathchardef symbol table,
6474: * but is called directly from rasterize(), as necessary.
6475: * ======================================================================= */
6476: /* --- entry point --- */
6477: subraster *rastscripts ( char **expression, int size, subraster *basesp )
6478: {
6479: /* -------------------------------------------------------------------------
6480: Allocations and Declarations
6481: -------------------------------------------------------------------------- */
6482: char *texscripts(), /* parse expression for scripts */
6483: subscript[512], supscript[512]; /* scripts parsed from expression */
6484: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
6485: subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */
6486: *rastack(); /*sets scripts in displaymath mode*/
6487: raster *rp=NULL; /* image raster embedded in sp */
6488: int height=0, width=0, baseline=0, /* height,width,baseline of sp */
6489: subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */
6490: supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */
6491: baseht=0, baseln=0; /* height,baseline of base */
6492: int bdescend=0, sdescend=0; /* descender of base, subscript */
6493: int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */
6494: isbase=0; /* true if we have base symbol */
6495: int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
6496: vbetween = 2, /* vertical space between scripts */
6497: vabove = szval+1, /*sup's top/bot above base's top/bot*/
6498: vbelow = szval+1, /*sub's top/bot below base's top/bot*/
6499: vbottom = szval+1; /*sup's bot above (sub's below) bsln*/
6500: /*int istweak = 1;*/ /* true to tweak script positioning */
6501: int rastput(); /*put scripts in constructed raster*/
6502: int delete_subraster(); /* free work areas */
1.3 ! albertel 6503: int rastsmashcheck(); /* check if okay to smash scripts */
1.1 albertel 6504: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
6505: /* -------------------------------------------------------------------------
6506: Obtain subscript and/or superscript expressions, and rasterize them/it
6507: -------------------------------------------------------------------------- */
6508: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
6509: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
6510: if ( *expression == NULL ) goto end_of_job; /* no expression given */
6511: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
6512: *expression = texscripts(*expression,subscript,supscript,3);
6513: /* --- rasterize scripts --- */
6514: if ( *subscript != '\000' ) /* have a subscript */
6515: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
6516: if ( *supscript != '\000' ) /* have a superscript */
6517: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
6518: /* --- set flags for convenience --- */
6519: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
6520: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
6521: isboth = (issub && issup); /* true if we have both */
6522: if (!issub && !issup) goto end_of_job; /* quit if we have neither */
1.3 ! albertel 6523: /* --- check for leading no-smash chars (if enabled) --- */
! 6524: issmashokay = 0; /* default, don't smash scripts */
! 6525: if ( smashcheck > 0 ) { /* smash checking wanted */
! 6526: issmashokay = 1; /*haven't found a no-smash char yet*/
! 6527: if ( issub ) /* got a subscript */
! 6528: issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
! 6529: if ( issmashokay ) /* clean sub, so check sup */
! 6530: if ( issup ) /* got a superscript */
! 6531: issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
! 6532: } /* --- end-of-if(smashcheck>0) --- */
1.1 albertel 6533: /* -------------------------------------------------------------------------
6534: get height, width, baseline of scripts, and height, baseline of base symbol
6535: -------------------------------------------------------------------------- */
6536: /* --- get height and width of components --- */
6537: if ( issub ) /* we have a subscript */
6538: { subht = (subsp->image)->height; /* so get its height */
6539: subwidth = (subsp->image)->width; /* and width */
6540: subln = subsp->baseline; } /* and baseline */
6541: if ( issup ) /* we have a superscript */
6542: { supht = (supsp->image)->height; /* so get its height */
6543: supwidth = (supsp->image)->width; /* and width */
6544: supln = supsp->baseline; } /* and baseline */
6545: /* --- get height and baseline of base, and descender of base and sub --- */
6546: if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */
6547: basesp = leftexpression; /* try using left side thus far */
6548: if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */
6549: { baseht = (basesp->image)->height; /* height of base symbol */
6550: baseln = basesp->baseline; /* and its baseline */
6551: bdescend = baseht-(baseln+1); /* and base symbol descender */
6552: sdescend = bdescend + vbelow; /*sub must descend by at least this*/
6553: if ( baseht > 0 ) isbase = 1; } /* set flag */
6554: /* -------------------------------------------------------------------------
6555: determine width of constructed raster
6556: -------------------------------------------------------------------------- */
6557: width = max2(subwidth,supwidth); /*widest component is overall width*/
6558: /* -------------------------------------------------------------------------
6559: determine height and baseline of constructed raster
6560: -------------------------------------------------------------------------- */
6561: /* --- both super/subscript --- */
6562: if ( isboth ) /*we have subscript and superscript*/
6563: { height = max2(subht+vbetween+supht, /* script heights + space bewteen */
6564: vbelow+baseht+vabove); /*sub below base bot, sup above top*/
6565: baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
6566: /* --- superscript only --- */
6567: if ( !issub ) /* we only have a superscript */
6568: { height = max3(baseln+1+vabove, /* sup's top above base symbol top */
6569: supht+vbottom, /* sup's bot above baseln */
6570: supht+vabove-bdescend); /* sup's bot above base symbol bot */
6571: baseline = height-1; } /*sup's baseline at bottom of raster*/
6572: /* --- subscript only --- */
1.3 ! albertel 6573: if ( !issup ) { /* we only have a subscript */
1.1 albertel 6574: if ( subht > sdescend ) /*sub can descend below base bot...*/
6575: { height = subht; /* ...without extra space on top */
6576: baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
6577: baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
6578: else /* sub's top will be below baseln */
6579: { height = sdescend+1; /* sub's bot below base symbol bot */
1.3 ! albertel 6580: baseline = 0; } } /* sub's baseline at top of raster */
1.1 albertel 6581: /* -------------------------------------------------------------------------
6582: construct raster with superscript over subscript
6583: -------------------------------------------------------------------------- */
6584: /* --- allocate subraster containing constructed raster --- */
6585: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
6586: == NULL ) /* and if we fail to allocate */
6587: goto end_of_job; /* quit */
6588: /* --- initialize subraster parameters --- */
6589: sp->type = IMAGERASTER; /* set type as constructed image */
6590: sp->size = size; /* set given size */
6591: sp->baseline = baseline; /* composite scripts baseline */
6592: rp = sp->image; /* raster embedded in subraster */
6593: /* --- place super/subscripts in new raster --- */
6594: if ( issup ) /* we have a superscript */
6595: rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */
6596: if ( issub ) /* we have a subscript */
6597: rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
6598: /* -------------------------------------------------------------------------
6599: free unneeded component subrasters and return final result to caller
6600: -------------------------------------------------------------------------- */
6601: end_of_job:
6602: if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
6603: if ( issup ) delete_subraster(supsp); /* and superscript */
6604: return ( sp );
6605: } /* --- end-of-function rastscripts() --- */
6606:
6607:
6608: /* ==========================================================================
6609: * Function: rastdispmath ( expression, size, sp )
6610: * Purpose: displaymath handler, returns sp along with
6611: * its immediately following super/subscripts
6612: * --------------------------------------------------------------------------
6613: * Arguments: expression (I/O) char ** to first char of null-terminated
6614: * string immediately following sp to be
6615: * rasterized along with its super/subscripts,
6616: * and returning ptr immediately following last
6617: * character processed.
6618: * size (I) int containing 0-4 default font size
6619: * sp (I) subraster * to display math operator
6620: * to which super/subscripts will be added
6621: * --------------------------------------------------------------------------
6622: * Returns: ( subraster * ) ptr to subraster corresponding to sp
6623: * plus its scripts, or NULL for any error
6624: * --------------------------------------------------------------------------
6625: * Notes: o sp returned unchanged if no super/subscript(s) follow it.
6626: * ======================================================================= */
6627: /* --- entry point --- */
6628: subraster *rastdispmath ( char **expression, int size, subraster *sp )
6629: {
6630: /* -------------------------------------------------------------------------
6631: Allocations and Declarations
6632: -------------------------------------------------------------------------- */
6633: char *texscripts(), /* parse expression for scripts */
6634: subscript[512], supscript[512]; /* scripts parsed from expression */
6635: int issub=0, issup=0; /* true if we have sub,sup */
6636: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
6637: *rastack(), /* stack operator with scripts */
6638: *new_subraster(); /* for dummy base sp, if needed */
6639: int vspace = 1; /* vertical space between scripts */
6640: /* -------------------------------------------------------------------------
6641: Obtain subscript and/or superscript expressions, and rasterize them/it
6642: -------------------------------------------------------------------------- */
6643: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
6644: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
6645: if ( *expression == NULL ) goto end_of_job; /* no expression given */
6646: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
6647: *expression = texscripts(*expression,subscript,supscript,3);
6648: /* --- rasterize scripts --- */
6649: if ( *subscript != '\000' ) /* have a subscript */
6650: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
6651: if ( *supscript != '\000' ) /* have a superscript */
6652: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
6653: /* --- set flags for convenience --- */
6654: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
6655: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
6656: if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/
6657: /* -------------------------------------------------------------------------
6658: stack operator and its script(s)
6659: -------------------------------------------------------------------------- */
6660: /* --- stack superscript atop operator --- */
1.3 ! albertel 6661: if ( issup ) { /* we have a superscript */
1.1 albertel 6662: if ( sp == NULL ) /* but no base expression */
6663: sp = supsp; /* so just use superscript */
6664: else /* have base and superscript */
6665: if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
1.3 ! albertel 6666: == NULL ) goto end_of_job; } /* and quit if failed */
1.1 albertel 6667: /* --- stack operator+superscript atop subscript --- */
1.3 ! albertel 6668: if ( issub ) { /* we have a subscript */
1.1 albertel 6669: if ( sp == NULL ) /* but no base expression */
6670: sp = subsp; /* so just use subscript */
6671: else /* have base and subscript */
6672: if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
1.3 ! albertel 6673: == NULL ) goto end_of_job; } /* and quit if failed */
1.1 albertel 6674: sp->type = IMAGERASTER; /* flip type of composite object */
6675: sp->size = size; /* and set font size */
6676: /* -------------------------------------------------------------------------
6677: free unneeded component subrasters and return final result to caller
6678: -------------------------------------------------------------------------- */
6679: end_of_job:
6680: return ( sp );
6681: } /* --- end-of-function rastdispmath() --- */
6682:
6683:
6684: /* ==========================================================================
6685: * Function: rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
6686: * Purpose: \left...\right handler, returns a subraster corresponding to
6687: * delimited subexpression at font size
6688: * --------------------------------------------------------------------------
6689: * Arguments: expression (I) char ** to first char of null-terminated
6690: * string beginning with a \left
6691: * to be rasterized
6692: * size (I) int containing 0-5 default font size
6693: * basesp (I) subraster * to character (or subexpression)
6694: * immediately preceding leading left{
6695: * (unused, but passed for consistency)
1.2 albertel 6696: * ildelim (I) int containing ldelims[] index of
1.1 albertel 6697: * left delimiter
6698: * arg2 (I) int unused
6699: * arg3 (I) int unused
6700: * --------------------------------------------------------------------------
6701: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
6702: * or NULL for any parsing error
6703: * --------------------------------------------------------------------------
6704: * Notes: o
6705: * ======================================================================= */
6706: /* --- entry point --- */
6707: subraster *rastleft ( char **expression, int size, subraster *basesp,
6708: int ildelim, int arg2, int arg3 )
6709: {
6710: /* -------------------------------------------------------------------------
6711: Allocations and Declarations
6712: -------------------------------------------------------------------------- */
6713: subraster *rasterize(), *sp=NULL; /*rasterize between \left...\right*/
6714: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
6715: subraster *rastlimits(); /*handle sub/super scripts on lp,rp*/
6716: subraster *rastcat(); /* concat lp||sp||rp subrasters */
6717: int family=CMSYEX, /* get_delim() family */
6718: height=0, rheight=0, /* subexpr, right delim height */
6719: margin=(size+1), /* delim height margin over subexpr*/
6720: opmargin=(5); /* extra margin for \int,\sum,\etc */
1.3 ! albertel 6721: char /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
1.1 albertel 6722: char *texchar(), /* get delims after \left,\right */
6723: ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
6724: char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
6725: int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
1.3 ! albertel 6726: int isleftscript=0, isrightscript=0; /* true if delims are scripted */
1.1 albertel 6727: int sublen=0; /* strlen(subexpr) */
6728: int idelim=0; /* 1=left,2=right */
1.2 albertel 6729: /* int gotldelim = 0; */ /* true if ildelim given by caller */
1.1 albertel 6730: int delete_subraster(); /* free subraster if rastleft fails*/
6731: int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
1.2 albertel 6732: int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
1.1 albertel 6733: /* --- recognized delimiters --- */
6734: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
6735: static char *ldelims[] = {
6736: "unused", ".", /* 1 for \left., \right. */
6737: "(", ")", /* 2,3 for \left(, \right) */
6738: "\\{","\\}", /* 4,5 for \left\{, \right\} */
6739: "[", "]", /* 6,7 for \left[, \right] */
6740: "<", ">", /* 8,9 for \left<, \right> */
6741: "|", "\\|", /* 10,11 for \left,\right |,\|*/
6742: NULL };
6743: /* --- recognized operator delimiters --- */
6744: static char *opdelims[] = { /* operator delims from cmex10 */
6745: "int", "sum", "prod",
6746: "cup", "cap", "dot",
6747: "plus", "times", "wedge",
6748: "vee",
6749: NULL }; /* --- end-of-opdelims[] --- */
6750: /* --- delimiter xlation --- */
6751: static char *xfrom[] = /* xlate any delim suffix... */
6752: { "\\|", /* \| */
6753: "\\{", /* \{ */
6754: "\\}", /* \} */
6755: "\\lbrace", /* \lbrace */
6756: "\\rbrace", /* \rbrace */
6757: "\\langle", /* \langle */
6758: "\\rangle", /* \rangle */
6759: NULL } ; /* --- end-of-xfrom[] --- */
6760: static char *xto[] = /* ...to this instead */
6761: { "=", /* \| to = */
6762: "{", /* \{ to { */
6763: "}", /* \} to } */
6764: "{", /* \lbrace to { */
6765: "}", /* \rbrace to } */
6766: "<", /* \langle to < */
6767: ">", /* \rangle to > */
6768: NULL } ; /* --- end-of-xto[] --- */
1.2 albertel 6769: /* --- non-displaystyle delimiters --- */
6770: static char *textdelims[] = /* these delims _aren't_ display */
6771: { "|", "=",
6772: "(", ")",
6773: "[", "]",
6774: "<", ">",
6775: "{", "}",
6776: "dbl", /* \lbrackdbl and \rbrackdbl */
6777: NULL } ; /* --- end-of-textdelims[] --- */
1.1 albertel 6778: /* -------------------------------------------------------------------------
6779: initialization
6780: -------------------------------------------------------------------------- */
6781: /* --- check args --- */
6782: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
6783: /* --- determine left delimiter, and set default \right. delimiter --- */
6784: if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
1.2 albertel 6785: { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
6786: /* gotldelim = 1; */ } /* and set flag that we got it */
1.1 albertel 6787: else /* trapped \left without delim */
6788: { skipwhite(*expression); /* interpret \left ( as \left( */
1.2 albertel 6789: if ( *(*expression) == '\000' ) /* end-of-string after \left */
6790: goto end_of_job; /* so return NULL */
6791: *expression = texchar(*expression,ldelim); /*pull delim from expression*/
6792: if ( *expression == NULL /* probably invalid end-of-string */
6793: || *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
1.1 albertel 6794: strcpy(rdelim,"."); /* init default \right. delim */
6795: /* -------------------------------------------------------------------------
6796: locate \right balancing our opening \left
6797: -------------------------------------------------------------------------- */
6798: /* --- first \right following \left --- */
6799: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
6800: != NULL ) { /* found it */
6801: /* --- find matching \right by pushing past any nested \left's --- */
6802: pleft = *expression; /* start after first \left( */
6803: while ( 1 ) { /*break when matching \right found*/
6804: /* -- locate next nested \left if there is one --- */
6805: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
6806: == NULL ) break; /*no more, so matching \right found*/
6807: pleft += strlen(left); /* push ptr past \left token */
6808: if ( pleft >= pright ) break; /* not nested if \left after \right*/
6809: /* --- have nested \left, so push forward to next \right --- */
6810: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
6811: == NULL ) break; /* ran out of \right's */
6812: } /* --- end-of-while(1) --- */
6813: } /* --- end-of-if(pright!=NULL) --- */
6814: /* -------------------------------------------------------------------------
6815: push past \left(_a^b sub/superscripts, if present
6816: -------------------------------------------------------------------------- */
6817: pleft = *expression; /*reset pleft after opening \left( */
1.2 albertel 6818: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
6819: != NULL ) /* found actual _a^b scripts, too */
6820: { delete_subraster(lp); /* but we don't need them */
6821: lp = NULL; } /* reset pointer, too */
1.1 albertel 6822: /* -------------------------------------------------------------------------
6823: get \right delimiter and subexpression between \left...\right, xlate delims
6824: -------------------------------------------------------------------------- */
6825: /* --- get delimiter following \right --- */
6826: if ( pright == (char *)NULL ) { /* assume \right. at end of exprssn*/
6827: strcpy(rdelim,"."); /* set default \right. */
6828: sublen = strlen(*expression); /* use entire remaining expression */
6829: memcpy(subexpr,*expression,sublen); /* copy all remaining chars */
6830: *expression += sublen; } /* and push expression to its null */
6831: else { /* have explicit matching \right */
6832: sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
6833: memcpy(subexpr,*expression,sublen); /* copy chars preceding \right */
6834: *expression = pright+strlen(right); /* push expression past \right */
6835: skipwhite(*expression); /* interpret \right ) as \right) */
6836: *expression = texchar(*expression,rdelim); /*pull delim from expression*/
6837: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
6838: /* --- get subexpression between \left...\right --- */
6839: if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
6840: subexpr[sublen] = '\000'; /* and null-terminate it */
1.2 albertel 6841: /* --- adjust margin for expressions containing \middle's --- */
6842: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
6843: margin = 1; /* so don't "overwhelm" them */
1.1 albertel 6844: /* --- check for operator delimiter --- */
6845: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
6846: if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
6847: { margin += opmargin; /* extra height for operator */
6848: if ( *ldelim == '\\' ) /* have leading escape */
6849: strcpy(ldelim,ldelim+1); /* squeeze it out */
6850: break; } /* no need to check rest of table */
1.2 albertel 6851: /* --- xlate delimiters and check for textstyle --- */
1.1 albertel 6852: for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
6853: char *lrdelim = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
6854: int ix; char *xdelim; /* xfrom[] and xto[] index, delim */
6855: for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
6856: if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
6857: { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
6858: break; } /* no need to check further */
1.2 albertel 6859: for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
6860: if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
6861: { if ( idelim == 1 ) /* if it's the \left one */
6862: istextleft = 1; /* set left textstyle flag */
6863: else istextright = 1; /* else set right textstyle flag */
6864: break; } /* no need to check further */
1.1 albertel 6865: } /* --- end-of-for(idelim) --- */
6866: /* --- debugging --- */
6867: if ( msgfp!=NULL && msglevel>=99 )
6868: fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
6869: ldelim,rdelim,subexpr);
6870: /* -------------------------------------------------------------------------
6871: rasterize subexpression
6872: -------------------------------------------------------------------------- */
6873: /* --- rasterize subexpression --- */
6874: if ( (sp = rasterize(subexpr,size)) /* rasterize chars between delims */
6875: == NULL ) goto end_of_job; /* quit if failed */
6876: height = (sp->image)->height; /* height of subexpr raster */
6877: rheight = height+margin; /*default rheight as subexpr height*/
6878: /* -------------------------------------------------------------------------
6879: rasterize delimiters, reset baselines, and add sub/superscripts if present
6880: -------------------------------------------------------------------------- */
6881: /* --- check for dot delimiter --- */
6882: isleftdot = (strchr(ldelim,'.')!=NULL); /* true if \left. */
6883: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
6884: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
1.2 albertel 6885: isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
1.1 albertel 6886: if ( !isleftdot ) /* if not \left. */
6887: { /* --- first get requested \left delimiter --- */
6888: lp = get_delim(ldelim,rheight,family); /* get \left delim char */
6889: /* --- reset lp delim baseline to center delim on subexpr raster --- */
6890: if ( lp != NULL ) /* if get_delim() succeeded */
6891: { int lheight = (lp->image)->height; /* actual height of left delim */
6892: lp->baseline = sp->baseline + (lheight - height)/2;
6893: if ( lheight > rheight ) /* got bigger delim than requested */
6894: rheight = lheight-1; } /* make sure right delim matches */
6895: /* --- then add on any sub/superscripts attached to \left( --- */
1.3 ! albertel 6896: lp = rastlimits(&pleft,size,lp); /*\left(_a^b and push pleft past b*/
! 6897: isleftscript = isscripted; } /* check if left delim scripted */
1.2 albertel 6898: isdisplaystyle = (istextright?0:9); /* force \displaystyle */
1.1 albertel 6899: if ( !isrightdot ) /* and if not \right. */
6900: { /* --- first get requested \right delimiter --- */
6901: rp = get_delim(rdelim,rheight,family); /* get \right delim char */
6902: /* --- reset rp delim baseline to center delim on subexpr raster --- */
6903: if ( rp != NULL ) /* if get_delim() succeeded */
6904: rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
6905: /* --- then add on any sub/superscripts attached to \right) --- */
1.3 ! albertel 6906: rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/
! 6907: isrightscript = isscripted; } /* check if right delim scripted */
1.1 albertel 6908: isdisplaystyle = wasdisplaystyle; /* original \displystyle default */
6909: /* --- check that we got delimiters --- */
6910: if ( 0 )
6911: if ( (lp==NULL && !isleftdot) /* check that we got left( */
6912: || (rp==NULL && !isrightdot) ) /* and right) if needed */
6913: { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
6914: if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
6915: if (0) { delete_subraster(sp); /* if failed, free subraster */
6916: sp = (subraster *)NULL; } /* signal error to caller */
6917: goto end_of_job; } /* and quit */
6918: /* -------------------------------------------------------------------------
6919: concat lp || sp || rp components
6920: -------------------------------------------------------------------------- */
6921: /* --- concat lp||sp||rp to obtain final result --- */
6922: if ( lp != NULL ) /* ignore \left. */
6923: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
6924: if ( sp != NULL ) /* succeeded or ignored \left. */
6925: if ( rp != NULL ) /* ignore \right. */
6926: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
6927: /* --- back to caller --- */
6928: end_of_job:
1.3 ! albertel 6929: isdelimscript = isrightscript; /* signal if right delim scripted */
1.1 albertel 6930: return ( sp );
6931: } /* --- end-of-function rastleft() --- */
6932:
6933:
6934: /* ==========================================================================
1.2 albertel 6935: * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
6936: * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
6937: * --------------------------------------------------------------------------
6938: * Arguments: expression (I) char ** to first char of null-terminated
6939: * string beginning with a \right
6940: * to be rasterized
6941: * size (I) int containing 0-5 default font size
6942: * basesp (I) subraster * to character (or subexpression)
6943: * immediately preceding leading left{
6944: * (unused, but passed for consistency)
6945: * ildelim (I) int containing rdelims[] index of
6946: * right delimiter
6947: * arg2 (I) int unused
6948: * arg3 (I) int unused
6949: * --------------------------------------------------------------------------
6950: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
6951: * or NULL for any parsing error
6952: * --------------------------------------------------------------------------
6953: * Notes: o
6954: * ======================================================================= */
6955: /* --- entry point --- */
6956: subraster *rastright ( char **expression, int size, subraster *basesp,
6957: int ildelim, int arg2, int arg3 )
6958: {
6959: /* -------------------------------------------------------------------------
6960: Allocations and Declarations
6961: -------------------------------------------------------------------------- */
6962: subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
6963: if ( sp != NULL ) /* returning entire expression */
6964: {
6965: isreplaceleft = 1; /* set flag to replace left half*/
6966: }
6967: return ( sp );
6968: } /* --- end-of-function rastright() --- */
6969:
6970:
6971: /* ==========================================================================
6972: * Function: rastmiddle ( expression, size, basesp, arg1, arg2, arg3 )
6973: * Purpose: \middle handler, returns subraster corresponding to
6974: * entire expression with \middle delimiter(s) sized to fit.
6975: * --------------------------------------------------------------------------
6976: * Arguments: expression (I/O) char ** to first char of null-terminated
6977: * string immediately following \middle to be
6978: * rasterized, and returning ptr immediately
6979: * to terminating null.
6980: * size (I) int containing 0-5 default font size
6981: * basesp (I) subraster * to character (or subexpression)
6982: * immediately preceding \middle
6983: * (unused, but passed for consistency)
6984: * arg1 (I) int unused
6985: * arg2 (I) int unused
6986: * arg3 (I) int unused
6987: * --------------------------------------------------------------------------
6988: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
6989: * or NULL for any parsing error
6990: * (expression ptr unchanged if error occurs)
6991: * --------------------------------------------------------------------------
6992: * Notes: o
6993: * ======================================================================= */
6994: /* --- entry point --- */
6995: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
6996: int arg1, int arg2, int arg3 )
6997: {
6998: /* -------------------------------------------------------------------------
6999: Allocations and Declarations
7000: -------------------------------------------------------------------------- */
7001: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
7002: char *exprptr = *expression, /* local copy of ptr to expression */
7003: *texchar(), delim[32][132], /* delimiters following \middle's */
7004: *strtexchr(), /* locate \middle's */
1.3 ! albertel 7005: subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
1.2 albertel 7006: int height=0, habove=0, hbelow=0; /* height, above & below baseline */
7007: int idelim, ndelims=0, /* \middle count (max 32) */
7008: family = CMSYEX; /* delims from CMSY10 or CMEX10 */
7009: subraster *subrastcpy(), /* copy subraster */
7010: *rastcat(), /* concatanate subraster */
7011: *get_delim(); /* get rasterized delimiter */
7012: int delete_subraster(); /* free work area subsp[]'s at eoj */
7013: /* -------------------------------------------------------------------------
7014: initialization
7015: -------------------------------------------------------------------------- */
7016: subsp[0] = leftexpression; /* expressn preceding 1st \middle */
7017: subsp[1] = NULL; /* set first null */
7018: /* -------------------------------------------------------------------------
7019: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
7020: -------------------------------------------------------------------------- */
7021: while ( ndelims < 30 ) /* max of 31 \middle's */
7022: {
7023: /* --- maintain max height above,below baseline --- */
7024: if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
7025: { int baseline = (subsp[ndelims])->baseline; /* #rows above baseline */
7026: height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
7027: habove = max2(habove,baseline); /* max #rows above baseline */
7028: hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
7029: /* --- get delimter after \middle --- */
7030: skipwhite(exprptr); /*skip space betwn \middle & \delim*/
7031: exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
7032: if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
7033: break; /* ignore it and consider job done */
7034: ndelims++; /* count another \middle\delim */
7035: /* --- get subexpression between \delim and next \middle --- */
7036: subsp[ndelims] = NULL; /* no subexpresion yet */
7037: if ( *exprptr == '\000' ) /* end-of-expression after \delim */
7038: break; /* so we have all subexpressions */
7039: if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
7040: == NULL ) /* no more \middle's */
1.3 ! albertel 7041: { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
! 7042: subexpr[MAXSUBXSZ] = '\000'; /* make sure it's null-terminated */
1.2 albertel 7043: exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
7044: else /* have another \middle */
7045: { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
1.3 ! albertel 7046: memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
! 7047: subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
1.2 albertel 7048: exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
7049: /* --- rasterize subexpression --- */
7050: subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
7051: } /* --- end-of-while(1) --- */
7052: /* -------------------------------------------------------------------------
7053: construct \middle\delim's and concatanate them between subexpressions
7054: -------------------------------------------------------------------------- */
7055: if ( ndelims < 1 /* no delims */
7056: || (height=habove+hbelow) < 1 ) /* or no subexpressions? */
7057: goto end_of_job; /* just flush \middle directive */
7058: for ( idelim=0; idelim<=ndelims; idelim++ )
7059: {
7060: /* --- first add on subexpression preceding delim --- */
1.3 ! albertel 7061: if ( subsp[idelim] != NULL ) { /* have subexpr preceding delim */
1.2 albertel 7062: if ( sp == NULL ) /* this is first piece */
7063: { sp = subsp[idelim]; /* so just use it */
7064: if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
1.3 ! albertel 7065: else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
1.2 albertel 7066: /* --- now construct delimiter --- */
7067: if ( *(delim[idelim]) != '\000' ) /* have delimter */
7068: { subraster *delimsp = get_delim(delim[idelim],height,family);
7069: if ( delimsp != NULL ) /* rasterized delim */
7070: { delimsp->baseline = habove; /* set baseline */
7071: if ( sp == NULL ) /* this is first piece */
7072: sp = delimsp; /* so just use it */
7073: else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
7074: } /* --- end-of-for(idelim) --- */
7075: /* --- back to caller --- */
7076: end_of_job:
7077: if ( 0 ) /* now handled above */
7078: for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
7079: if ( subsp[idelim] != NULL ) /* have allocated subraster */
7080: delete_subraster(subsp[idelim]); /* so free it */
7081: if ( sp != NULL ) /* returning entire expression */
7082: { int newht = (sp->image)->height; /* height of returned subraster */
7083: sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
7084: isreplaceleft = 1; /* set flag to replace left half*/
7085: *expression += strlen(*expression); } /* and push to terminating null*/
7086: return ( sp );
7087: } /* --- end-of-function rastmiddle() --- */
7088:
7089:
7090: /* ==========================================================================
1.1 albertel 7091: * Function: rastflags ( expression, size, basesp, flag, value, arg3 )
7092: * Purpose: sets an internal flag, e.g., for \rm, or sets an internal
7093: * value, e.g., for \unitlength=<value>, and returns NULL
7094: * so nothing is displayed
7095: * --------------------------------------------------------------------------
7096: * Arguments: expression (I) char ** to first char of null-terminated
7097: * LaTeX expression (unused/unchanged)
7098: * size (I) int containing base font size (not used,
7099: * just stored in subraster)
7100: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 7101: * immediately preceding "flags" directive
7102: * (unused but passed for consistency)
1.1 albertel 7103: * flag (I) int containing #define'd symbol specifying
7104: * internal flag to be set
7105: * value (I) int containing new value of flag
7106: * arg3 (I) int unused
7107: * --------------------------------------------------------------------------
7108: * Returns: ( subraster * ) NULL so nothing is displayed
7109: * --------------------------------------------------------------------------
7110: * Notes: o
7111: * ======================================================================= */
7112: /* --- entry point --- */
7113: subraster *rastflags ( char **expression, int size, subraster *basesp,
7114: int flag, int value, int arg3 )
7115: {
7116: /* -------------------------------------------------------------------------
7117: Allocations and Declarations
7118: -------------------------------------------------------------------------- */
7119: char *texsubexpr(), /* parse expression for... */
7120: valuearg[1024]="NOVALUE"; /* value from expression, if needed */
7121: int argvalue=NOVALUE, /* atoi(valuearg) */
7122: isdelta=0, /* true if + or - precedes valuearg */
7123: valuelen=0; /* strlen(valuearg) */
1.3 ! albertel 7124: double dblvalue=(-99.), strtod(); /*convert ascii {valuearg} to double*/
1.1 albertel 7125: static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */
7126: /* -------------------------------------------------------------------------
7127: set flag or value
7128: -------------------------------------------------------------------------- */
7129: switch ( flag )
7130: {
7131: default: break; /* unrecognized flag */
1.2 albertel 7132: case ISFONTFAM:
1.1 albertel 7133: if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
7134: (*expression)++; /* skip leading ~ after \rm */
1.2 albertel 7135: fontnum = value; /* set font family */
1.1 albertel 7136: break;
7137: case ISSTRING: isstring=value; break; /* set string/image mode */
7138: case ISDISPLAYSTYLE: /* set \displaystyle mode */
7139: displaystylelevel = recurlevel; /* \displaystyle set at recurlevel */
7140: isdisplaystyle=value; break;
7141: case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */
7142: case ISREVERSE: /* reverse video */
7143: if ( value==1 || value==NOVALUE )
7144: { fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
7145: if ( value==2 || value==NOVALUE )
7146: { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
7147: if ( value==2 || value==NOVALUE )
7148: isblackonwhite = !isblackonwhite;
1.3 ! albertel 7149: if ( gammacorrection > 0.0001 ) /* have gamma correction */
! 7150: gammacorrection = REVERSEGAMMA; /* use reverse video gamma instead */
1.1 albertel 7151: break;
7152: case ISSUPER: /* set supersampling/lowpass flag */
7153: #ifndef SSFONTS /* don't have ss fonts loaded */
7154: value = 0; /* so force lowpass */
7155: #endif
7156: isss = issupersampling = value;
7157: fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
7158: break;
7159: case ISFONTSIZE: /* set fontsize */
7160: case ISDISPLAYSIZE: /* set displaysize */
1.3 ! albertel 7161: case ISCONTENTTYPE: /*enable/disable content-type lines*/
1.1 albertel 7162: case ISSHRINK: /* set shrinkfactor */
7163: case ISAAALGORITHM: /* set anti-aliasing algorithm */
7164: case ISWEIGHT: /* set font weight */
7165: case ISCENTERWT: /* set lowpass center pixel weight */
7166: case ISADJACENTWT: /* set lowpass adjacent weight */
7167: case ISCORNERWT: /* set lowpass corner weight */
7168: case ISCOLOR: /* set red(1),green(2),blue(3) */
1.2 albertel 7169: case ISSMASH: /* set (minimum) "smash" margin */
1.3 ! albertel 7170: case ISGAMMA: /* set gamma correction */
1.1 albertel 7171: if ( value != NOVALUE ) /* passed a fixed value to be set */
1.3 ! albertel 7172: { argvalue = value; /* set given fixed int value */
! 7173: dblvalue = (double)value; } /* or maybe interpreted as double */
1.1 albertel 7174: else /* get value from expression */
7175: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
7176: if ( *valuearg != '\000' ) /* guard against empty string */
7177: if ( !isalpha(*valuearg) ) /* and against alpha string args */
7178: if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
7179: { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
7180: if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
7181: { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */
1.3 ! albertel 7182: switch ( flag ) { /* convert to double or int */
! 7183: default: argvalue = atoi(valuearg); break; /* convert to int */
! 7184: case ISGAMMA:
! 7185: dblvalue = strtod(valuearg,NULL); break; } /* or to double */
! 7186: } /* --- end-of-if(*valuearg!='?') --- */
! 7187: } /* --- end-of-if(value==NOVALUE) --- */
1.1 albertel 7188: switch ( flag )
7189: {
7190: default: break;
7191: case ISCOLOR: /* set color */
7192: slower(valuearg); /* convert arg to lower case */
7193: if ( argvalue==1 || strstr(valuearg,"red") )
7194: { fggreen = fgblue = (isblackonwhite?0:255);
7195: fgred = (isblackonwhite?255:0); }
7196: if ( argvalue==2 || strstr(valuearg,"green") )
7197: { fgred = fgblue = (isblackonwhite?0:255);
7198: fggreen = (isblackonwhite?255:0); }
7199: if ( argvalue==3 || strstr(valuearg,"blue") )
7200: { fgred = fggreen = (isblackonwhite?0:255);
7201: fgblue = (isblackonwhite?255:0); }
7202: if ( argvalue==0 || strstr(valuearg,"black") )
7203: fgred = fggreen = fgblue = (isblackonwhite?0:255);
7204: if ( argvalue==7 || strstr(valuearg,"white") )
7205: fgred = fggreen = fgblue = (isblackonwhite?255:0);
7206: break;
7207: case ISFONTSIZE: /* set fontsize */
7208: if ( argvalue != NOVALUE ) /* got a value */
7209: { int largestsize = (issupersampling?16:LARGESTSIZE);
7210: fontsize = (isdelta? fontsize+argvalue : argvalue);
7211: fontsize = max2(0,min2(fontsize,largestsize));
7212: shrinkfactor = shrinkfactors[fontsize];
1.2 albertel 7213: if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
7214: || (1 && isdisplaystyle==2) /* displaystyle enabled and set */
7215: || (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
1.1 albertel 7216: if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
1.3 ! albertel 7217: if ( !ispreambledollars ) { /* respect $$...$$'s */
1.2 albertel 7218: if ( fontsize >= displaysize )
7219: isdisplaystyle = 2; /* forced */
1.3 ! albertel 7220: else isdisplaystyle = 1; }
1.1 albertel 7221: /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
7222: else /* embed font size in expression */
7223: { sprintf(valuearg,"%d",fontsize); /* convert size */
7224: valuelen = strlen(valuearg); /* ought to be 1 */
7225: if ( *expression != '\000' ) /* ill-formed expression */
7226: { *expression = (char *)(*expression-valuelen); /*back up buff*/
7227: memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
7228: break;
7229: case ISDISPLAYSIZE: /* set displaysize */
7230: if ( argvalue != NOVALUE ) /* got a value */
7231: displaysize = (isdelta? displaysize+argvalue : argvalue);
7232: break;
1.3 ! albertel 7233: case ISCONTENTTYPE: /*enable/disable content-type lines*/
! 7234: if ( argvalue != NOVALUE ) /* got a value */
! 7235: isemitcontenttype = (argvalue>0?1:0);
! 7236: break;
1.2 albertel 7237: case ISSMASH: /* set (minimum) "smash" margin */
1.1 albertel 7238: if ( argvalue != NOVALUE ) /* got a value */
1.2 albertel 7239: { smashmargin = argvalue; /* set value */
1.1 albertel 7240: if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
1.2 albertel 7241: issmashdelta = (isdelta?1:0); } /* and set delta flag */
7242: smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
1.3 ! albertel 7243: isexplicitsmash = 1; /* signal explicit \smash directive*/
1.1 albertel 7244: break;
7245: case ISSHRINK: /* set shrinkfactor */
7246: if ( argvalue != NOVALUE ) /* got a value */
7247: shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
7248: shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
7249: break;
7250: case ISAAALGORITHM: /* set anti-aliasing algorithm */
1.3 ! albertel 7251: if ( argvalue != NOVALUE ) { /* got a value */
! 7252: if ( argvalue >= 0 ) { /* non-negative to set algorithm */
! 7253: aaalgorithm = argvalue; /* set algorithm number */
! 7254: aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
! 7255: else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
1.1 albertel 7256: break;
7257: case ISWEIGHT: /* set font weight number */
7258: value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
7259: (isdelta? weightnum+argvalue : argvalue));
7260: if ( value>=0 && value<maxaaparams ) /* in range */
7261: { weightnum = value; /* reset weightnum index */
7262: minadjacent = aaparams[weightnum].minadjacent;
7263: maxadjacent = aaparams[weightnum].maxadjacent;
7264: cornerwt = aaparams[weightnum].cornerwt;
7265: adjacentwt = aaparams[weightnum].adjacentwt;
7266: centerwt = aaparams[weightnum].centerwt;
7267: fgalias = aaparams[weightnum].fgalias;
7268: fgonly = aaparams[weightnum].fgonly;
7269: bgalias = aaparams[weightnum].bgalias;
7270: bgonly = aaparams[weightnum].bgonly; }
7271: break;
7272: case ISCENTERWT: /* set lowpass center pixel weight */
7273: if ( argvalue != NOVALUE ) /* got a value */
7274: centerwt = argvalue; /* set lowpass center weight */
7275: break;
7276: case ISADJACENTWT: /* set lowpass adjacent weight */
7277: if ( argvalue != NOVALUE ) /* got a value */
7278: adjacentwt = argvalue; /* set lowpass adjacent weight */
7279: break;
7280: case ISCORNERWT: /* set lowpass corner weight */
7281: if ( argvalue != NOVALUE ) /* got a value */
7282: cornerwt = argvalue; /* set lowpass corner weight */
7283: break;
1.3 ! albertel 7284: case ISGAMMA: /* set gamma correction */
! 7285: if ( dblvalue >= 0.0 ) /* got a value */
! 7286: gammacorrection = dblvalue; /* set gamma correction */
! 7287: break;
1.1 albertel 7288: } /* --- end-of-switch() --- */
7289: break;
7290: case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/
7291: *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
7292: valuelen = strlen(valuearg); /* ought to be 1-4 */
7293: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
7294: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
7295: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
7296: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
7297: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
7298: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
7299: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
7300: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
7301: break;
7302: case UNITLENGTH:
7303: if ( value != NOVALUE ) /* passed a fixed value to be set */
7304: unitlength = (double)(value); /* set given fixed value */
7305: else /* get value from expression */
7306: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
7307: if ( *valuearg != '\000' ) /* guard against empty string */
7308: unitlength = strtod(valuearg,NULL); } /* convert to double */
7309: break;
7310: } /* --- end-of-switch(flag) --- */
7311: return ( NULL ); /*just set value, nothing to display*/
7312: } /* --- end-of-function rastflags() --- */
7313:
7314:
7315: /* ==========================================================================
7316: * Function: rastspace(expression, size, basesp, width, isfill, isheight)
7317: * Purpose: returns a blank/space subraster width wide,
7318: * with baseline and height corresponding to basep
7319: * --------------------------------------------------------------------------
7320: * Arguments: expression (I) char ** to first char of null-terminated
7321: * LaTeX expression (unused/unchanged)
7322: * size (I) int containing base font size (not used,
7323: * just stored in subraster)
7324: * basesp (I) subraster * to character (or subexpression)
7325: * immediately preceding space, whose baseline
7326: * and height params are transferred to space
7327: * width (I) int containing #bits/pixels for space width
7328: * isfill (I) int containing true to \hfill complete
7329: * expression out to width
1.3 ! albertel 7330: * (Kludge: isfill=99 signals \hspace*
! 7331: * for negative space)
1.1 albertel 7332: * isheight (I) int containing true (but not NOVALUE)
7333: * to treat width arg as height
7334: * --------------------------------------------------------------------------
7335: * Returns: ( subraster * ) ptr to empty/blank subraster
7336: * or NULL for any error
7337: * --------------------------------------------------------------------------
7338: * Notes: o
7339: * ======================================================================= */
7340: /* --- entry point --- */
7341: subraster *rastspace ( char **expression, int size, subraster *basesp,
7342: int width, int isfill, int isheight )
7343: {
7344: /* -------------------------------------------------------------------------
7345: Allocations and Declarations
7346: -------------------------------------------------------------------------- */
7347: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
1.3 ! albertel 7348: raster *bp=NULL, *backspace_raster(); /* for negative space */
! 7349: int delete_subraster(); /* if fail, free unneeded subraster*/
1.1 albertel 7350: int baseht=1, baseln=0; /* height,baseline of base symbol */
7351: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
1.3 ! albertel 7352: int isstar=0, minspace=0; /* defaults for negative hspace */
1.1 albertel 7353: char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
7354: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
7355: subraster *rastcat(); /* cat rightsp after \hfill */
7356: /* -------------------------------------------------------------------------
7357: initialization
7358: -------------------------------------------------------------------------- */
1.3 ! albertel 7359: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
1.1 albertel 7360: if ( isfill == NOVALUE ) isfill=0; /* novalue means false */
7361: if ( isheight == NOVALUE ) isheight=0; /* novalue means false */
1.3 ! albertel 7362: minspace = (isstar?(-1):0); /* reset default minspace */
1.1 albertel 7363: /* -------------------------------------------------------------------------
7364: determine width if not given (e.g., \hspace{width}, \hfill{width})
7365: -------------------------------------------------------------------------- */
1.3 ! albertel 7366: if ( width == 0 ) { /* width specified in expression */
! 7367: double dwidth; int widthval; /* test {width} before using it */
! 7368: int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
! 7369: /* --- check if optional [minspace] given for negative \hspace --- */
! 7370: if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */
! 7371: /* ---parse [minspace], bump expression past it, interpret as double--- */
! 7372: *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
! 7373: if ( *widtharg != '\000' ) /* got [minspace] */
! 7374: minspace = iround(unitlength*strtod(widtharg,NULL)); /* in pixels */
! 7375: } /* --- end-of-if(*(*expression)=='[') --- */
! 7376: width = 1; /* set default width */
! 7377: *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
! 7378: dwidth = unitlength*strtod(widtharg,NULL); /* scaled width value */
! 7379: widthval = /* convert {width} to integer */
! 7380: (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
! 7381: if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
! 7382: width = widthval; /* replace deafault width */
! 7383: } /* --- end-of-if(width==0) --- */
! 7384: /* -------------------------------------------------------------------------
! 7385: first check for negative space
! 7386: -------------------------------------------------------------------------- */
! 7387: if ( width < 0 ) { /* have negative hspace */
! 7388: if ( leftexpression != (subraster *)NULL ) /* can't backspace */
! 7389: if ( (spacesp=new_subraster(0,0,0)) /* get new subraster for backspace */
! 7390: != NULL ) { /* and if we succeed... */
! 7391: int nback=(-width), pback; /*#pixels wanted,actually backspaced*/
! 7392: if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
! 7393: != NULL ) { /* and if backspace succeeds... */
! 7394: spacesp->image = bp; /* save backspaced image */
! 7395: /*spacesp->type = leftexpression->type;*/ /* copy original type */
! 7396: spacesp->type = blanksignal; /* need to propagate blanks */
! 7397: spacesp->size = leftexpression->size; /* copy original font size */
! 7398: spacesp->baseline = leftexpression->baseline; /* and baseline */
! 7399: blanksymspace += -(nback-pback); /* wanted more than we got */
! 7400: isreplaceleft = 1; } /*signal to replace entire expressn*/
! 7401: else { /* backspace failed */
! 7402: delete_subraster(spacesp); /* free unneeded envelope */
! 7403: spacesp = (subraster *)NULL; } } /* and signal failure */
! 7404: goto end_of_job;
! 7405: } /* --- end-of-if(width<0) --- */
1.1 albertel 7406: /* -------------------------------------------------------------------------
7407: see if width is "absolute" or fill width
7408: -------------------------------------------------------------------------- */
7409: if ( isfill /* called as \hfill{} */
7410: && !isheight ) /* parameter conflict */
7411: { if ( leftexpression != NULL ) /* if we have left half */
7412: width -= (leftexpression->image)->width; /*reduce left width from total*/
7413: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
7414: != NULL ) /* succeeded */
7415: width -= (rightsp->image)->width; } /* reduce right width from total */
7416: /* -------------------------------------------------------------------------
7417: construct blank subraster, and return it to caller
7418: -------------------------------------------------------------------------- */
7419: /* --- get parameters from base symbol --- */
7420: if ( basesp != (subraster *)NULL ) /* we have base symbol for space */
7421: { baseht = (basesp->image)->height; /* height of base symbol */
7422: baseln = basesp->baseline; } /* and its baseline */
7423: /* --- flip params for height --- */
7424: if ( isheight ) /* width is actually height */
7425: { baseht = width; /* use given width as height */
7426: width = 1; } /* and set default width */
7427: /* --- generate and init space subraster --- */
7428: if ( width > 0 ) /*make sure we have positive width*/
7429: if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
7430: != NULL ) /* and if we succeed... */
7431: { /* --- ...re-init subraster parameters --- */
7432: spacesp->size = size; /*propagate base font size forward*/
1.3 ! albertel 7433: if(1)spacesp->type = blanksignal; /* need to propagate blanks (???) */
1.1 albertel 7434: spacesp->baseline = baseln; } /* ditto baseline */
7435: /* -------------------------------------------------------------------------
7436: concat right half if \hfill-ing
7437: -------------------------------------------------------------------------- */
7438: if ( rightsp != NULL ) /* we have a right half after fill */
7439: { spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/
7440: rastcat(spacesp,rightsp,3)); /* or cat right half after space */
7441: spacesp->type = blanksignal; /* need to propagate blanks */
7442: *expression += strlen((*expression)); } /* push expression to its null */
1.3 ! albertel 7443: end_of_job:
! 7444: return ( spacesp );
1.1 albertel 7445: } /* --- end-of-function rastspace() --- */
7446:
7447:
7448: /* ==========================================================================
7449: * Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 )
7450: * Purpose: \\ handler, returns subraster corresponding to
7451: * left-hand expression preceding \\ above right-hand expression
7452: * --------------------------------------------------------------------------
7453: * Arguments: expression (I/O) char ** to first char of null-terminated
7454: * string immediately following \\ to be
7455: * rasterized, and returning ptr immediately
7456: * to terminating null.
1.2 albertel 7457: * size (I) int containing 0-5 default font size
1.1 albertel 7458: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 7459: * immediately preceding \\
1.1 albertel 7460: * (unused, but passed for consistency)
7461: * arg1 (I) int unused
7462: * arg2 (I) int unused
7463: * arg3 (I) int unused
7464: * --------------------------------------------------------------------------
7465: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
7466: * or NULL for any parsing error
7467: * (expression ptr unchanged if error occurs)
7468: * --------------------------------------------------------------------------
7469: * Notes: o
7470: * ======================================================================= */
7471: /* --- entry point --- */
7472: subraster *rastnewline ( char **expression, int size, subraster *basesp,
7473: int arg1, int arg2, int arg3 )
7474: {
7475: /* -------------------------------------------------------------------------
7476: Allocations and Declarations
7477: -------------------------------------------------------------------------- */
7478: subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
7479: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
7480: char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
7481: double strtod(); /* convert ascii param to double */
7482: int vspace = size+2; /* #pixels between lines */
7483: /* -------------------------------------------------------------------------
7484: obtain optional [vspace] argument immediately following \\ command
7485: -------------------------------------------------------------------------- */
7486: /* --- check if [vspace] given --- */
7487: if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/
7488: {
7489: /* ---parse [vspace] and bump expression past it, interpret as double--- */
7490: *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
7491: if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
7492: vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
7493: } /* --- end-of-if(*(*expression)=='[') --- */
7494: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
7495: /* -------------------------------------------------------------------------
7496: rasterize right half of expression and stack left half above it
7497: -------------------------------------------------------------------------- */
7498: /* --- rasterize right half --- */
7499: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
7500: == NULL ) goto end_of_job; /* quit if failed */
7501: /* --- stack left half above it --- */
1.2 albertel 7502: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
7503: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
1.1 albertel 7504: /* --- back to caller --- */
7505: end_of_job:
7506: if ( newlsp != NULL ) /* returning entire expression */
7507: { int newht = (newlsp->image)->height; /* height of returned subraster */
7508: newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
7509: isreplaceleft = 1; /* so set flag to replace left half*/
7510: *expression += strlen(*expression); } /* and push to terminating null*/
7511: return ( newlsp ); /* 1st line over 2nd, or null=error*/
7512: } /* --- end-of-function rastnewline() --- */
7513:
7514:
7515: /* ==========================================================================
7516: * Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 )
7517: * Purpose: returns left/right arrow subraster (e.g., for \longrightarrow)
7518: * --------------------------------------------------------------------------
7519: * Arguments: expression (I) char ** to first char of null-terminated
7520: * LaTeX expression (unused/unchanged)
7521: * size (I) int containing base font size (not used,
7522: * just stored in subraster)
7523: * basesp (I) subraster * to character (or subexpression)
7524: * immediately preceding space, whose baseline
7525: * and height params are transferred to space
7526: * drctn (I) int containing +1 for right, -1 for left,
7527: * or 0 for leftright
7528: * isBig (I) int containing 0 for ---> or 1 for ===>
7529: * arg3 (I) int unused
7530: * --------------------------------------------------------------------------
7531: * Returns: ( subraster * ) ptr to left/right arrow subraster
7532: * or NULL for any error
7533: * --------------------------------------------------------------------------
7534: * Notes: o An optional argument [width] may *immediately* follow
7535: * the \longxxx to explicitly set the arrow's width in pixels.
7536: * For example, \longrightarrow calculates a default width
7537: * (as usual in LaTeX), whereas \longrightarrow[50] explicitly
7538: * draws a 50-pixel long arrow. This can be used, e.g.,
7539: * to draw commutative diagrams in conjunction with
7540: * \array (and maybe with \stackrel and/or \relstack, too).
7541: * o In case you really want to render, say, [f]---->[g], just
7542: * use an intervening space, i.e., [f]\longrightarrow~[g].
7543: * In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
7544: * ======================================================================= */
7545: /* --- entry point --- */
7546: subraster *rastarrow ( char **expression, int size, subraster *basesp,
7547: int drctn, int isBig, int arg3 )
7548: {
7549: /* -------------------------------------------------------------------------
7550: Allocations and Declarations
7551: -------------------------------------------------------------------------- */
7552: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
7553: char *texsubexpr(), widtharg[256]; /* parse for optional [width] */
7554: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
7555: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
7556: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
7557: int delete_subraster(); /*free work areas in case of error*/
7558: double strtod(); /* convert ascii [width] to value */
7559: int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
7560: int islimits = 1; /*true to handle limits internally*/
7561: int limsize = size-1; /* font size for limits */
7562: int vspace = 1; /* #empty rows below arrow */
7563: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
7564: /* -------------------------------------------------------------------------
7565: construct longleft/rightarrow subraster, with limits, and return it to caller
7566: -------------------------------------------------------------------------- */
7567: /* --- check for optional width arg and replace default width --- */
7568: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
7569: { int widthval; /* test [width] before using it */
7570: *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
7571: widthval = /* convert [width] to integer */
7572: (int)((unitlength*strtod(widtharg,NULL))+0.5);
7573: if ( widthval>=2 && widthval<=600 ) /* sanity check */
7574: width = widthval; } /* replace deafault width */
7575: /* --- now parse for limits, and bump expression past it(them) --- */
7576: if ( islimits ) /* handling limits internally */
7577: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
7578: if ( *sub != '\000' ) /*have a subscript following arrow*/
7579: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
7580: if ( *super != '\000' ) /*have superscript following arrow*/
7581: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
7582: /* --- set height based on width --- */
7583: height = min2(17,max2(9,(width+2)/6)); /* height based on width */
7584: height = 1 + (height/2)*2; /* always force odd height */
7585: /* --- generate arrow subraster --- */
7586: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
7587: == NULL ) goto end_of_job; /* and quit if we failed */
7588: /* --- add space below arrow --- */
7589: if ( vspace > 0 ) /* if we have space below arrow */
7590: if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
7591: != NULL ) /* and if we succeeded */
7592: if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
7593: == NULL ) goto end_of_job; /* and quit if we failed */
7594: /* --- init arrow subraster parameters --- */
7595: arrowsp->size = size; /*propagate base font size forward*/
7596: arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */
7597: /* --- add limits above/below arrow, as necessary --- */
7598: if ( subsp != NULL ) /* stack subscript below arrow */
7599: if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
7600: == NULL ) goto end_of_job; /* quit if failed */
7601: if ( supsp != NULL ) /* stack superscript above arrow */
7602: if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
7603: == NULL ) goto end_of_job; /* quit if failed */
7604: /* --- return arrow (or NULL) to caller --- */
7605: end_of_job:
7606: return ( arrowsp );
7607: } /* --- end-of-function rastarrow() --- */
7608:
7609:
7610: /* ==========================================================================
7611: * Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 )
7612: * Purpose: returns an up/down arrow subraster (e.g., for \longuparrow)
7613: * --------------------------------------------------------------------------
7614: * Arguments: expression (I) char ** to first char of null-terminated
7615: * LaTeX expression (unused/unchanged)
7616: * size (I) int containing base font size (not used,
7617: * just stored in subraster)
7618: * basesp (I) subraster * to character (or subexpression)
7619: * immediately preceding space, whose baseline
7620: * and height params are transferred to space
7621: * drctn (I) int containing +1 for up, -1 for down,
7622: * or 0 for updown
7623: * isBig (I) int containing 0 for ---> or 1 for ===>
7624: * arg3 (I) int unused
7625: * --------------------------------------------------------------------------
7626: * Returns: ( subraster * ) ptr to up/down arrow subraster
7627: * or NULL for any error
7628: * --------------------------------------------------------------------------
7629: * Notes: o An optional argument [height] may *immediately* follow
7630: * the \longxxx to explicitly set the arrow's height in pixels.
7631: * For example, \longuparrow calculates a default height
7632: * (as usual in LaTeX), whereas \longuparrow[25] explicitly
7633: * draws a 25-pixel high arrow. This can be used, e.g.,
7634: * to draw commutative diagrams in conjunction with
7635: * \array (and maybe with \stackrel and/or \relstack, too).
7636: * o In case you really want to render, say, [f]---->[g], just
7637: * use an intervening space, i.e., [f]\longuparrow~[g].
7638: * In text use two spaces {\rm~[f]\longuparrow~~[g]}.
7639: * ======================================================================= */
7640: /* --- entry point --- */
7641: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
7642: int drctn, int isBig, int arg3 )
7643: {
7644: /* -------------------------------------------------------------------------
7645: Allocations and Declarations
7646: -------------------------------------------------------------------------- */
7647: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
7648: char *texsubexpr(), heightarg[256]; /* parse for optional [height] */
7649: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
7650: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
7651: subraster *rastcat(); /* cat superscript left, sub right */
7652: double strtod(); /* convert ascii [height] to value */
7653: int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
7654: int islimits = 1; /*true to handle limits internally*/
7655: int limsize = size-1; /* font size for limits */
7656: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
7657: /* -------------------------------------------------------------------------
7658: construct blank subraster, and return it to caller
7659: -------------------------------------------------------------------------- */
7660: /* --- check for optional height arg and replace default height --- */
7661: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
7662: { int heightval; /* test height before using it */
7663: *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
7664: heightval = /* convert [height] to integer */
7665: (int)((unitlength*strtod(heightarg,NULL))+0.5);
7666: if ( heightval>=2 && heightval<=600 ) /* sanity check */
7667: height = heightval; } /* replace deafault height */
7668: /* --- now parse for limits, and bump expression past it(them) --- */
7669: if ( islimits ) /* handling limits internally */
7670: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
7671: if ( *sub != '\000' ) /*have a subscript following arrow*/
7672: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
7673: if ( *super != '\000' ) /*have superscript following arrow*/
7674: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
7675: /* --- set width based on height --- */
7676: width = min2(17,max2(9,(height+2)/4)); /* width based on height */
7677: width = 1 + (width/2)*2; /* always force odd width */
7678: /* --- generate arrow subraster --- */
7679: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
7680: == NULL ) goto end_of_job; /* and quit if we failed */
7681: /* --- init arrow subraster parameters --- */
7682: arrowsp->size = size; /*propagate base font size forward*/
7683: arrowsp->baseline = height-1; /* set baseline at bottom of arrow */
7684: /* --- add limits above/below arrow, as necessary --- */
7685: if ( supsp != NULL ) /* cat superscript to left of arrow*/
7686: { int supht = (supsp->image)->height, /* superscript height */
7687: deltab = (1+abs(height-supht))/2; /* baseline difference to center */
7688: supsp->baseline = supht-1; /* force script baseline to bottom */
7689: if ( supht <= height ) /* arrow usually taller than script*/
7690: arrowsp->baseline -= deltab; /* so bottom of script goes here */
7691: else supsp->baseline -= deltab; /* else bottom of arrow goes here */
7692: if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
7693: == NULL ) goto end_of_job; } /* quit if failed */
7694: if ( subsp != NULL ) /* cat subscript to right of arrow */
7695: { int subht = (subsp->image)->height, /* subscript height */
7696: deltab = (1+abs(height-subht))/2; /* baseline difference to center */
7697: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
7698: subsp->baseline = subht-1; /* force script baseline to bottom */
7699: if ( subht <= height ) /* arrow usually taller than script*/
7700: arrowsp->baseline -= deltab; /* so bottom of script goes here */
7701: else subsp->baseline -= deltab; /* else bottom of arrow goes here */
7702: if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
7703: == NULL ) goto end_of_job; } /* quit if failed */
7704: /* --- return arrow (or NULL) to caller --- */
7705: end_of_job:
7706: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
7707: return ( arrowsp );
7708: } /* --- end-of-function rastuparrow() --- */
7709:
7710:
7711: /* ==========================================================================
7712: * Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3)
7713: * Purpose: overlays one raster on another
7714: * --------------------------------------------------------------------------
7715: * Arguments: expression (I/O) char ** to first char of null-terminated
7716: * string immediately following overlay \cmd to
7717: * be rasterized, and returning ptr immediately
7718: * following last character processed.
7719: * size (I) int containing 0-5 default font size
7720: * basesp (I) subraster * to character (or subexpression)
7721: * immediately preceding overlay \cmd
7722: * (unused, but passed for consistency)
7723: * overlay (I) int containing 1 to overlay / (e.g., \not)
7724: * or NOVALUE to pick up 2nd arg from expression
7725: * offset2 (I) int containing #pixels to horizontally offset
7726: * overlay relative to underlying symbol,
7727: * positive(right) or negative or 0,
7728: * or NOVALUE to pick up optional [offset] arg
7729: * arg3 (I) int unused
7730: * --------------------------------------------------------------------------
7731: * Returns: ( subraster * ) ptr to subraster corresponding to composite,
7732: * or NULL for any parsing error
7733: * --------------------------------------------------------------------------
7734: * Notes: o
7735: * ======================================================================= */
7736: /* --- entry point --- */
7737: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
7738: int overlay, int offset2, int arg3 )
7739: {
7740: /* -------------------------------------------------------------------------
7741: Allocations and Declarations
7742: -------------------------------------------------------------------------- */
7743: char *texsubexpr(), /*parse expression for base,overlay*/
7744: expr1[512], expr2[512]; /* base, overlay */
7745: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
7746: *new_subraster(); /*explicitly alloc sp2 if necessary*/
7747: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
7748: int line_raster(); /* draw diagonal for \Not */
7749: /* -------------------------------------------------------------------------
7750: Obtain base, and maybe overlay, and rasterize them
7751: -------------------------------------------------------------------------- */
7752: /* --- check for optional offset2 arg --- */
7753: if ( offset2 == NOVALUE ) /* only if not explicitly specified*/
7754: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
7755: { int offsetval; /* test before using it */
7756: *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
7757: offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
7758: if ( abs(offsetval) <= 25 ) /* sanity check */
7759: offset2 = offsetval; } /* replace deafault */
7760: if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
7761: /* --- parse for base, bump expression past it, and rasterize it --- */
7762: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
7763: if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
7764: if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
7765: == NULL ) goto end_of_job; /* quit if failed to rasterize */
7766: overlaysp = sp1; /*in case we return with no overlay*/
7767: /* --- get overlay expression, and rasterize it --- */
7768: if ( overlay == NOVALUE ) /* get overlay from input stream */
7769: { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
7770: if ( *expr2 != '\000' ) /* have an overlay */
7771: sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */
7772: else /* specific overlay */
7773: switch ( overlay )
7774: {
7775: default: break;
7776: case 1: /* e.g., \not overlays slash */
7777: sp2 = rasterize("/",size+1); /* rasterize overlay expression */
7778: offset2 = max2(1,size-3); /* push / right a bit */
7779: offset2 = 0;
7780: break;
7781: case 2: /* e.g., \Not draws diagonal */
7782: sp2 = NULL; /* no overlay required */
7783: if ( overlaysp != NULL ) /* check that we have raster */
7784: { raster *rp = overlaysp->image; /* raster to be \Not-ed */
7785: int width=rp->width, height=rp->height; /* raster dimensions */
7786: if ( 0 ) /* diagonal within bounding box */
7787: line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
7788: else /* construct "wide" diagonal */
7789: { int margin=3; /* desired extra margin width */
7790: sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
7791: if ( sp2 != NULL ) /* allocated successfully */
7792: line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
7793: break;
7794: case 3: /* e.g., \sout for strikeout */
7795: sp2 = NULL; /* no overlay required */
7796: if ( overlaysp != NULL ) /* check that we have raster */
7797: { raster *rp = overlaysp->image; /* raster to be \Not-ed */
7798: int width=rp->width, height=rp->height; /* raster dimensions */
7799: int baseline = overlaysp->baseline; /* we'll ignore descenders */
7800: int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
7801: if ( 1 ) /* strikeout within bounding box */
7802: line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
7803: break;
7804: } /* --- end-of-switch(overlay) --- */
7805: if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/
7806: /* -------------------------------------------------------------------------
7807: construct composite overlay
7808: -------------------------------------------------------------------------- */
7809: overlaysp = rastcompose(sp1,sp2,offset2,0,3);
7810: end_of_job:
7811: return ( overlaysp );
7812: } /* --- end-of-function rastoverlay() --- */
7813:
7814:
7815: /* ==========================================================================
7816: * Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 )
7817: * Purpose: \frac,\atop handler, returns a subraster corresponding to
7818: * expression (immediately following \frac,\atop) at font size
7819: * --------------------------------------------------------------------------
7820: * Arguments: expression (I/O) char ** to first char of null-terminated
7821: * string immediately following \frac to be
7822: * rasterized, and returning ptr immediately
7823: * following last character processed.
7824: * size (I) int containing 0-5 default font size
7825: * basesp (I) subraster * to character (or subexpression)
7826: * immediately preceding \frac
7827: * (unused, but passed for consistency)
7828: * isfrac (I) int containing true to draw horizontal line
7829: * between numerator and denominator,
7830: * or false not to draw it (for \atop).
7831: * arg2 (I) int unused
7832: * arg3 (I) int unused
7833: * --------------------------------------------------------------------------
7834: * Returns: ( subraster * ) ptr to subraster corresponding to fraction,
7835: * or NULL for any parsing error
7836: * --------------------------------------------------------------------------
7837: * Notes: o
7838: * ======================================================================= */
7839: /* --- entry point --- */
7840: subraster *rastfrac ( char **expression, int size, subraster *basesp,
7841: int isfrac, int arg2, int arg3 )
7842: {
7843: /* -------------------------------------------------------------------------
7844: Allocations and Declarations
7845: -------------------------------------------------------------------------- */
7846: char *texsubexpr(), /*parse expression for numer,denom*/
1.3 ! albertel 7847: numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
1.1 albertel 7848: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
7849: subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */
7850: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
7851: int width=0, /* width of constructed raster */
7852: numheight=0; /* height of numerator */
7853: int baseht=0, baseln=0; /* height,baseline of base symbol */
7854: /*int istweak = 1;*/ /*true to tweak baseline alignment*/
7855: int rule_raster(), /* draw horizontal line for frac */
7856: lineheight = 1; /* thickness of fraction line */
1.2 albertel 7857: int vspace = (size>2?2:1); /*vertical space between components*/
1.1 albertel 7858: int delete_subraster(); /*free work areas in case of error*/
7859: int type_raster(); /* display debugging output */
7860: /* -------------------------------------------------------------------------
7861: Obtain numerator and denominator, and rasterize them
7862: -------------------------------------------------------------------------- */
7863: /* --- parse for numerator,denominator and bump expression past them --- */
7864: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
7865: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
7866: if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
7867: goto end_of_job; /* nothing to do, so quit */
7868: /* --- rasterize numerator, denominator --- */
7869: if ( *numer != '\000' ) /* have a numerator */
7870: if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
7871: == NULL ) goto end_of_job; /* and quit if failed */
7872: if ( *denom != '\000' ) /* have a denominator */
7873: if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
7874: == NULL ) /* failed */
7875: { if ( numsp != NULL ) /* already rasterized numerator */
7876: delete_subraster(numsp); /* so free now-unneeded numerator */
7877: goto end_of_job; } /* and quit */
7878: /* --- if one componenet missing, use a blank space for it --- */
7879: if ( numsp == NULL ) /* no numerator given */
7880: numsp = rasterize("[?]",size-1); /* missing numerator */
7881: if ( densp == NULL ) /* no denominator given */
7882: densp = rasterize("[?]",size-1); /* missing denominator */
7883: /* --- check that we got both components --- */
7884: if ( numsp==NULL || densp==NULL ) /* some problem */
7885: { delete_subraster(numsp); /*delete numerator (if it existed)*/
7886: delete_subraster(densp); /*delete denominator (if it existed)*/
7887: goto end_of_job; } /* and quit */
7888: /* --- get height of numerator (to determine where line belongs) --- */
7889: numheight = (numsp->image)->height; /* get numerator's height */
7890: /* -------------------------------------------------------------------------
7891: construct raster with numerator stacked over denominator
7892: -------------------------------------------------------------------------- */
7893: /* --- construct raster with numer/denom --- */
7894: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
7895: == NULL ) /* failed to construct numer/denom */
7896: { delete_subraster(numsp); /* so free now-unneeded numerator */
7897: delete_subraster(densp); /* and now-unneeded denominator */
7898: goto end_of_job; } /* and then quit */
7899: /* --- determine width of constructed raster --- */
7900: width = (fracsp->image)->width; /*just get width of embedded image*/
7901: /* --- initialize subraster parameters --- */
7902: fracsp->size = size; /* propagate font size forward */
7903: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
1.3 ! albertel 7904: fracsp->type = FRACRASTER; /* signal \frac image */
1.1 albertel 7905: if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */
7906: { baseht = (basesp->image)->height; /* height of base symbol */
7907: baseln = basesp->baseline; /* and its baseline */
7908: } /* --- end-of-if(basesp!=NULL) --- */
7909: /* -------------------------------------------------------------------------
7910: draw horizontal line between numerator and denominator
7911: -------------------------------------------------------------------------- */
1.3 ! albertel 7912: fraccenterline = numheight+vspace; /* signal that we have a \frac */
1.1 albertel 7913: if ( isfrac ) /*line for \frac, but not for \atop*/
1.3 ! albertel 7914: rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
1.1 albertel 7915: /* -------------------------------------------------------------------------
7916: return final result to caller
7917: -------------------------------------------------------------------------- */
7918: end_of_job:
7919: if ( msgfp!=NULL && msglevel>=99 )
7920: { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
7921: if ( fracsp != NULL ) /* have a constructed raster */
7922: type_raster(fracsp->image,msgfp); } /* display constructed raster */
7923: return ( fracsp );
7924: } /* --- end-of-function rastfrac() --- */
7925:
7926:
7927: /* ==========================================================================
7928: * Function: rastackrel ( expression, size, basesp, base, arg2, arg3 )
7929: * Purpose: \stackrel handler, returns a subraster corresponding to
7930: * stacked relation
7931: * --------------------------------------------------------------------------
7932: * Arguments: expression (I/O) char ** to first char of null-terminated
7933: * string immediately following \stackrel to be
7934: * rasterized, and returning ptr immediately
7935: * following last character processed.
7936: * size (I) int containing 0-4 default font size
7937: * basesp (I) subraster * to character (or subexpression)
7938: * immediately preceding \stackrel
7939: * (unused, but passed for consistency)
7940: * base (I) int containing 1 if upper/first subexpression
7941: * is base relation, or 2 if lower/second is
7942: * arg2 (I) int unused
7943: * arg3 (I) int unused
7944: * --------------------------------------------------------------------------
7945: * Returns: ( subraster * ) ptr to subraster corresponding to stacked
7946: * relation, or NULL for any parsing error
7947: * --------------------------------------------------------------------------
7948: * Notes: o
7949: * ======================================================================= */
7950: /* --- entry point --- */
7951: subraster *rastackrel ( char **expression, int size, subraster *basesp,
7952: int base, int arg2, int arg3 )
7953: {
7954: /* -------------------------------------------------------------------------
7955: Allocations and Declarations
7956: -------------------------------------------------------------------------- */
1.3 ! albertel 7957: char *texsubexpr(), /*parse expression for upper,lower*/
! 7958: upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */
1.1 albertel 7959: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
7960: subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */
7961: int upsize = (base==1? size:size-1), /* font size for upper component */
7962: lowsize = (base==2? size:size-1); /* font size for lower component */
7963: int vspace = 1; /*vertical space between components*/
7964: int delete_subraster(); /*free work areas in case of error*/
7965: /* -------------------------------------------------------------------------
7966: Obtain numerator and denominator, and rasterize them
7967: -------------------------------------------------------------------------- */
7968: /* --- parse for numerator,denominator and bump expression past them --- */
7969: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
7970: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
7971: if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
7972: goto end_of_job; /* nothing to do, so quit */
7973: /* --- rasterize upper, lower --- */
7974: if ( *upper != '\000' ) /* have upper component */
7975: if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */
7976: == NULL ) goto end_of_job; /* and quit if failed */
7977: if ( *lower != '\000' ) /* have lower component */
7978: if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
7979: == NULL ) /* failed */
7980: { if ( upsp != NULL ) /* already rasterized upper */
7981: delete_subraster(upsp); /* so free now-unneeded upper */
7982: goto end_of_job; } /* and quit */
7983: /* -------------------------------------------------------------------------
7984: construct stacked relation raster
7985: -------------------------------------------------------------------------- */
7986: /* --- construct stacked relation --- */
7987: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
7988: == NULL ) goto end_of_job; /* quit if failed */
7989: /* --- initialize subraster parameters --- */
7990: relsp->size = size; /* propagate font size forward */
7991: /* -------------------------------------------------------------------------
7992: return final result to caller
7993: -------------------------------------------------------------------------- */
7994: end_of_job:
7995: return ( relsp );
7996: } /* --- end-of-function rastackrel() --- */
7997:
7998:
7999: /* ==========================================================================
8000: * Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 )
8001: * Purpose: \log, \lim, etc handler, returns a subraster corresponding
8002: * to math functions
8003: * --------------------------------------------------------------------------
8004: * Arguments: expression (I/O) char ** to first char of null-terminated
8005: * string immediately following \mathfunc to be
8006: * rasterized, and returning ptr immediately
8007: * following last character processed.
8008: * size (I) int containing 0-4 default font size
8009: * basesp (I) subraster * to character (or subexpression)
8010: * immediately preceding \mathfunc
8011: * (unused, but passed for consistency)
8012: * mathfunc (I) int containing 1=arccos, 2=arcsin, etc.
8013: * islimits (I) int containing 1 if function may have
8014: * limits underneath, e.g., \lim_{n\to\infty}
8015: * arg3 (I) int unused
8016: * --------------------------------------------------------------------------
8017: * Returns: ( subraster * ) ptr to subraster corresponding to mathfunc,
8018: * or NULL for any parsing error
8019: * --------------------------------------------------------------------------
8020: * Notes: o
8021: * ======================================================================= */
8022: /* --- entry point --- */
8023: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
8024: int mathfunc, int islimits, int arg3 )
8025: {
8026: /* -------------------------------------------------------------------------
8027: Allocations and Declarations
8028: -------------------------------------------------------------------------- */
8029: char *texscripts(), /* parse expression for _limits */
1.3 ! albertel 8030: func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
1.1 albertel 8031: char *texsubexpr(), /* parse expression for arg */
1.3 ! albertel 8032: funcarg[MAXTOKNSZ+1]; /* optional func arg */
1.1 albertel 8033: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
8034: subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
8035: int limsize = size-1; /* font size for limits */
8036: int vspace = 1; /*vertical space between components*/
8037: int delete_subraster(); /*free work areas in case of error*/
8038: /* --- table of function names by mathfunc number --- */
8039: static int numnames = 34; /* number of names in table */
8040: static char *funcnames[] = {
8041: "error", /* 0 index is illegal/error bucket*/
8042: "arccos", "arcsin", "arctan", /* 1 - 3 */
8043: "arg", "cos", "cosh", /* 4 - 6 */
8044: "cot", "coth", "csc", /* 7 - 9 */
8045: "deg", "det", "dim", /* 10 - 12 */
8046: "exp", "gcd", "hom", /* 13 - 15 */
8047: "inf", "ker", "lg", /* 16 - 18 */
8048: "lim", "liminf", "limsup", /* 19 - 21 */
8049: "ln", "log", "max", /* 22 - 24 */
8050: "min", "Pr", "sec", /* 25 - 27 */
8051: "sin", "sinh", "sup", /* 28 - 30 */
8052: "tan", "tanh", /* 31 - 32 */
8053: /* --- extra mimetex funcnames --- */
8054: "tr", /* 33 */
8055: "pmod" /* 34 */
8056: } ;
8057: /* -------------------------------------------------------------------------
8058: set up and rasterize function name in \rm
8059: -------------------------------------------------------------------------- */
8060: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
8061: switch ( mathfunc ) /* check for special processing */
8062: {
8063: default: /* no special processing */
8064: strcpy(func,"{\\rm~"); /* init string with {\rm~ */
8065: strcat(func,funcnames[mathfunc]); /* concat function name */
8066: strcat(func,"}"); /* and add terminating } */
8067: break;
8068: case 34: /* \pmod{x} --> (mod x) */
8069: /* --- parse for \pmod{arg} argument --- */
8070: *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
8071: strcpy(func,"{\\({\\rm~mod}"); /* init with {\left({\rm~mod} */
8072: strcat(func,"\\hspace2"); /* concat space */
8073: strcat(func,funcarg); /* and \pmodargument */
8074: strcat(func,"\\)}"); /* and add terminating \right)} */
8075: break;
8076: } /* --- end-of-switch(mathfunc) --- */
8077: if ( (funcsp = rasterize(func,size)) /* rasterize function name */
8078: == NULL ) goto end_of_job; /* and quit if failed */
8079: mathfuncsp = funcsp; /* just return funcsp if no limits */
8080: if ( !islimits ) goto end_of_job; /* treat any subscript normally */
8081: /* -------------------------------------------------------------------------
8082: Obtain limits, if permitted and if provided, and rasterize them
8083: -------------------------------------------------------------------------- */
8084: /* --- parse for subscript limits, and bump expression past it(them) --- */
8085: *expression = texscripts(*expression,limits,limits,1);
8086: if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */
8087: /* --- rasterize limits --- */
8088: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
8089: == NULL ) goto end_of_job; /* and quit if failed */
8090: /* -------------------------------------------------------------------------
8091: construct func atop limits
8092: -------------------------------------------------------------------------- */
8093: /* --- construct func atop limits --- */
8094: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
8095: == NULL ) goto end_of_job; /* quit if failed */
8096: /* --- initialize subraster parameters --- */
8097: mathfuncsp->size = size; /* propagate font size forward */
8098: /* -------------------------------------------------------------------------
8099: return final result to caller
8100: -------------------------------------------------------------------------- */
8101: end_of_job:
8102: return ( mathfuncsp );
8103: } /* --- end-of-function rastmathfunc() --- */
8104:
8105:
8106: /* ==========================================================================
8107: * Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 )
8108: * Purpose: \sqrt handler, returns a subraster corresponding to
8109: * expression (immediately following \sqrt) at font size
8110: * --------------------------------------------------------------------------
8111: * Arguments: expression (I/O) char ** to first char of null-terminated
8112: * string immediately following \sqrt to be
8113: * rasterized, and returning ptr immediately
8114: * following last character processed.
8115: * size (I) int containing 0-4 default font size
8116: * basesp (I) subraster * to character (or subexpression)
8117: * immediately preceding \accent
8118: * (unused, but passed for consistency)
8119: * arg1 (I) int unused
8120: * arg2 (I) int unused
8121: * arg3 (I) int unused
8122: * --------------------------------------------------------------------------
8123: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
8124: * or NULL for any parsing error
8125: * (expression ptr unchanged if error occurs)
8126: * --------------------------------------------------------------------------
8127: * Notes: o
8128: * ======================================================================= */
8129: /* --- entry point --- */
8130: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
8131: int arg1, int arg2, int arg3 )
8132: {
8133: /* -------------------------------------------------------------------------
8134: Allocations and Declarations
8135: -------------------------------------------------------------------------- */
1.3 ! albertel 8136: char *texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
! 8137: rootarg[MAXSUBXSZ+1]; /* optional \sqrt[rootarg]{...} */
1.1 albertel 8138: subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */
8139: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
8140: *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
8141: int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
8142: rootheight=0, rootwidth=0, /* height,width of rootarg raster */
8143: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
8144: int rastput(); /* put subexpr in constructed sqrt */
8145: int overspace = 2; /*space between subexpr and overbar*/
8146: int delete_subraster(); /* free work areas */
8147: /* -------------------------------------------------------------------------
8148: Obtain subexpression to be sqrt-ed, and rasterize it
8149: -------------------------------------------------------------------------- */
8150: /* --- first check for optional \sqrt[rootarg]{...} --- */
8151: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
8152: { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
8153: if ( *rootarg != '\000' ) /* got rootarg */
8154: if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
8155: != NULL ) /* rasterized successfully */
8156: { rootheight = (rootsp->image)->height; /* get height of rootarg */
8157: rootwidth = (rootsp->image)->width; } /* and its width */
8158: } /* --- end-of-if(**expression=='[') --- */
8159: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
8160: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
8161: if ( *subexpr == '\000' ) /* couldn't get subexpression */
8162: goto end_of_job; /* nothing to do, so quit */
8163: /* --- rasterize subexpression to be accented --- */
8164: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
8165: == NULL ) goto end_of_job; /* quit if failed */
8166: /* -------------------------------------------------------------------------
8167: determine height and width of sqrt raster to be constructed
8168: -------------------------------------------------------------------------- */
8169: /* --- first get height and width of subexpr --- */
8170: subheight = (subsp->image)->height; /* height of subexpr */
8171: subwidth = (subsp->image)->width; /* and its width */
8172: pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
8173: /* --- determine height and width of sqrt to contain subexpr --- */
8174: sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
8175: surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */
8176: sqrtwidth = subwidth + surdwidth + 1; /* total width */
8177: /* -------------------------------------------------------------------------
8178: construct sqrt (with room to move in subexpr) and embed subexpr in it
8179: -------------------------------------------------------------------------- */
8180: /* --- construct sqrt --- */
8181: if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
8182: == NULL ) goto end_of_job; /* quit if failed to build sqrt */
8183: /* --- embed subexpr in sqrt at lower-right corner--- */
8184: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
8185: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
8186: /* --- "embed" rootarg at upper-left --- */
8187: if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/
8188: {
8189: /* --- allocate full raster to contain sqrtsp and rootsp --- */
8190: int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
8191: fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
8192: subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
8193: if ( fullsp != NULL ) /* allocated successfully */
8194: { /* --- embed sqrtsp exactly at lower-right corner --- */
8195: rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
8196: fullheight-sqrtheight,fullwidth-sqrtwidth,1);
8197: /* --- embed rootsp near upper-left, nestled above leading surd --- */
8198: rastput(fullsp->image,rootsp->image,
8199: 0,max2(0,surdwidth-rootwidth-2-size),0);
8200: /* --- replace sqrtsp with fullsp --- */
8201: delete_subraster(sqrtsp); /* free original sqrtsp */
8202: sqrtsp = fullsp; /* and repoint it to fullsp instead*/
8203: sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
8204: } /* --- end-of-if(rootsp!=NULL) --- */
8205: /* --- initialize subraster parameters --- */
8206: sqrtsp->size = size; /* propagate font size forward */
8207: /* -------------------------------------------------------------------------
8208: free unneeded component subrasters and return final result to caller
8209: -------------------------------------------------------------------------- */
8210: end_of_job:
8211: if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
8212: return ( sqrtsp );
8213: } /* --- end-of-function rastsqrt() --- */
8214:
8215:
8216: /* ==========================================================================
8217: * Function: rastaccent (expression,size,basesp,accent,isabove,isscript)
8218: * Purpose: \hat, \vec, \etc handler, returns a subraster corresponding
8219: * to expression (immediately following \accent) at font size
8220: * --------------------------------------------------------------------------
8221: * Arguments: expression (I/O) char ** to first char of null-terminated
8222: * string immediately following \accent to be
8223: * rasterized, and returning ptr immediately
8224: * following last character processed.
8225: * size (I) int containing 0-4 default font size
8226: * basesp (I) subraster * to character (or subexpression)
8227: * immediately preceding \accent
8228: * (unused, but passed for consistency)
8229: * accent (I) int containing HATACCENT or VECACCENT, etc,
8230: * between numerator and denominator,
8231: * or false not to draw it (for \over).
8232: * isabove (I) int containing true if accent is above
8233: * expression to be accented, or false
8234: * if accent is below (e.g., underbrace)
8235: * isscript (I) int containing true if sub/superscripts
8236: * allowed (for under/overbrace), or 0 if not.
8237: * --------------------------------------------------------------------------
8238: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
8239: * or NULL for any parsing error
8240: * (expression ptr unchanged if error occurs)
8241: * --------------------------------------------------------------------------
8242: * Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way
8243: * of isabove and isscript args.
8244: * ======================================================================= */
8245: /* --- entry point --- */
8246: subraster *rastaccent ( char **expression, int size, subraster *basesp,
8247: int accent, int isabove, int isscript )
8248: {
8249: /* -------------------------------------------------------------------------
8250: Allocations and Declarations
8251: -------------------------------------------------------------------------- */
1.3 ! albertel 8252: char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
1.1 albertel 8253: char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
1.3 ! albertel 8254: subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */
1.1 albertel 8255: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
8256: subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
8257: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
8258: int accheight=0, accwidth=0, /* height, width of accent */
8259: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
8260: int delete_subraster(); /*free work areas in case of error*/
8261: int vspace = 0; /*vertical space between accent,sub*/
8262: /* -------------------------------------------------------------------------
8263: Obtain subexpression to be accented, and rasterize it
8264: -------------------------------------------------------------------------- */
8265: /* --- parse for subexpr to be accented, and bump expression past it --- */
8266: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
8267: if ( *subexpr=='\000' ) /* couldn't get subexpression */
8268: goto end_of_job; /* nothing to do, so quit */
8269: /* --- rasterize subexpression to be accented --- */
8270: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
8271: == NULL ) goto end_of_job; /* quit if failed */
8272: /* -------------------------------------------------------------------------
8273: determine desired accent width and height
8274: -------------------------------------------------------------------------- */
8275: /* --- first get height and width of subexpr --- */
8276: subheight = (subsp->image)->height; /* height of subexpr */
8277: subwidth = (subsp->image)->width; /* and its width is overall width */
8278: pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
8279: /* --- determine desired width, height of accent --- */
8280: accwidth = subwidth; /* same width as subexpr */
1.2 albertel 8281: accheight = 4; /* default for bars */
1.1 albertel 8282: switch ( accent )
8283: { default: break; /* default okay */
8284: case DOTACCENT: case DDOTACCENT:
1.2 albertel 8285: accheight = (size<4? 3:4); /* default for dots */
1.1 albertel 8286: break;
1.2 albertel 8287: case VECACCENT:
8288: vspace = 1; /* set 1-pixel vertical space */
8289: case HATACCENT:
1.1 albertel 8290: accheight = 7; /* default */
8291: if ( subwidth < 10 ) accheight = 5; /* unless small width */
8292: else if ( subwidth > 25 ) accheight = 9; /* or large */
8293: break;
8294: } /* --- end-of-switch(accent) --- */
8295: accheight = min2(accheight,subheight); /*never higher than accented subexpr*/
8296: /* -------------------------------------------------------------------------
8297: construct accent, and construct subraster with accent over (or under) subexpr
8298: -------------------------------------------------------------------------- */
8299: /* --- first construct accent --- */
8300: if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
8301: == NULL ) goto end_of_job; /* quit if failed to build accent */
8302: /* --- now stack accent above (or below) subexpr, and free both args --- */
8303: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
8304: : rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/
8305: if ( accsubsp == NULL ) /* failed to stack accent */
8306: { delete_subraster(subsp); /* free unneeded subsp */
8307: delete_subraster(accsp); /* and unneeded accsp */
8308: goto end_of_job; } /* and quit */
8309: /* -------------------------------------------------------------------------
8310: look for super/subscript (annotation for over/underbrace)
8311: -------------------------------------------------------------------------- */
8312: /* --- first check whether accent permits accompanying annotations --- */
8313: if ( !isscript ) goto end_of_job; /* no annotations for this accent */
8314: /* --- now get scripts if there actually are any --- */
8315: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
8316: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
8317: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
8318: /* --- rasterize script annotation at size-2 --- */
8319: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
8320: == NULL ) goto end_of_job; /* quit if failed */
8321: /* --- stack annotation above (or below) accent, and free both args --- */
8322: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
8323: : rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */
8324: /* -------------------------------------------------------------------------
8325: return final result to caller
8326: -------------------------------------------------------------------------- */
8327: end_of_job:
8328: if ( accsubsp != NULL ) /* initialize subraster parameters */
8329: accsubsp->size = size; /* propagate font size forward */
8330: return ( accsubsp );
8331: } /* --- end-of-function rastaccent() --- */
8332:
8333:
8334: /* ==========================================================================
1.2 albertel 8335: * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
1.1 albertel 8336: * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
8337: * to char(s) within {}'s rendered at size
8338: * --------------------------------------------------------------------------
8339: * Arguments: expression (I/O) char ** to first char of null-terminated
8340: * string immediately following \font to be
8341: * rasterized, and returning ptr immediately
8342: * following last character processed.
8343: * size (I) int containing 0-5 default font size
8344: * basesp (I) subraster * to character (or subexpression)
8345: * immediately preceding \accent
8346: * (unused, but passed for consistency)
1.2 albertel 8347: * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
1.1 albertel 8348: * arg2 (I) int unused
8349: * arg3 (I) int unused
8350: * --------------------------------------------------------------------------
8351: * Returns: ( subraster * ) ptr to subraster corresponding to chars
8352: * between {}'s, or NULL for any parsing error
8353: * --------------------------------------------------------------------------
8354: * Notes: o
8355: * ======================================================================= */
8356: /* --- entry point --- */
8357: subraster *rastfont ( char **expression, int size, subraster *basesp,
1.2 albertel 8358: int ifontnum, int arg2, int arg3 )
1.1 albertel 8359: {
8360: /* -------------------------------------------------------------------------
8361: Allocations and Declarations
8362: -------------------------------------------------------------------------- */
1.3 ! albertel 8363: char *texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
! 8364: subexpr[MAXSUBXSZ+1]; /* turn \cal{AB} into \calA\calB */
1.1 albertel 8365: char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
1.2 albertel 8366: char *name = NULL; /* fontinfo[ifontnum].name */
8367: int family = 0, /* fontinfo[ifontnum].family */
8368: istext = 0, /* fontinfo[ifontnum].istext */
8369: class = 0; /* fontinfo[ifontnum].class */
1.1 albertel 8370: subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
8371: *rastflags(); /* or just set flag to switch font */
1.2 albertel 8372: int oldsmashmargin = smashmargin; /* turn off smash in text mode */
8373: #if 0
1.1 albertel 8374: /* --- fonts recognized by rastfont --- */
1.2 albertel 8375: static int nfonts = 6; /* legal font #'s are 1...nfonts */
1.1 albertel 8376: static struct {char *name; int class;}
8377: fonts[] =
8378: { /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
1.2 albertel 8379: { "\\math", 0 },
8380: { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
8381: { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
8382: { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
8383: { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
8384: { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
8385: { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
1.3 ! albertel 8386: { "\\mathrm", -1 }, /*(7) \mathrm */
! 8387: { "\\cyr", -1 }, /*(8) \cyr */
1.1 albertel 8388: { NULL, 0 }
8389: } ; /* --- end-of-fonts[] --- */
1.2 albertel 8390: #endif
1.1 albertel 8391: /* -------------------------------------------------------------------------
8392: first get font name and class to determine type of conversion desired
8393: -------------------------------------------------------------------------- */
1.2 albertel 8394: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
8395: name = fontinfo[ifontnum].name; /* font name */
8396: family = fontinfo[ifontnum].family; /* font family */
8397: istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
8398: class = fontinfo[ifontnum].class; /* font class */
8399: if ( istext ) /* text (respect blanks) */
1.3 ! albertel 8400: { mathsmashmargin = smashmargin; /* needed for \text{if $n-m$ even} */
! 8401: smashmargin = 0; } /* don't smash internal blanks */
1.1 albertel 8402: /* -------------------------------------------------------------------------
8403: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
8404: -------------------------------------------------------------------------- */
1.2 albertel 8405: if ( 1 || class<0 ) /* not character-by-character */
1.1 albertel 8406: {
8407: /* ---
8408: if \font not immediately followed by { then it has no arg, so just set flag
8409: ------------------------------------------------------------------------ */
8410: if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
8411: {
8412: if ( msgfp!=NULL && msglevel>=99 )
1.2 albertel 8413: fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
8414: fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
1.1 albertel 8415: goto end_of_job;
8416: } /* --- end-of-if(*(*expression)!='{') --- */
8417: /* ---
8418: convert \font{abc} --> {\font~abc}
8419: ---------------------------------- */
8420: /* --- parse for {fontchars} arg, and bump expression past it --- */
8421: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
8422: if ( msgfp!=NULL && msglevel>=99 )
8423: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
8424: /* --- convert all fontchars at the same time --- */
8425: strcpy(subexpr,"{"); /* start off with opening { */
8426: strcat(subexpr,name); /* followed by font name */
8427: strcat(subexpr,"~"); /* followed by whitespace */
8428: strcat(subexpr,fontchars); /* followed by all the chars */
8429: strcat(subexpr,"}"); /* terminate with closing } */
8430: } /* --- end-of-if(class<0) --- */
8431: else /* character-by-character */
8432: {
8433: /* ---
8434: convert ABC to \calA\calB\calC
8435: ------------------------------ */
8436: int isprevchar=0; /* true if prev char converted */
8437: /* --- parse for {fontchars} arg, and bump expression past it --- */
8438: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
8439: if ( msgfp!=NULL && msglevel>=99 )
8440: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
8441: /* --- convert fontchars one at a time --- */
8442: strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */
8443: strcpy(subexpr,"{"); /* nope, just start off with { */
8444: for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
8445: {
8446: if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
1.2 albertel 8447: { if ( 0 || istext ) /* and we're in a text mode font */
1.1 albertel 8448: strcat(subexpr,"\\;"); } /* so respect whitespace */
8449: else /* char to be displayed in font */
8450: { int exprlen = 0; /* #chars in subexpr before fchar */
8451: int isinclass = 0; /* set true if fchar in font class */
1.2 albertel 8452: /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
1.1 albertel 8453: switch ( class ) /* check if fchar is in font class */
8454: { default: break; /* no chars in unrecognized class */
8455: case 1: if ( isupper((int)fchar) ) isinclass=1; break;
8456: case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
8457: case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
8458: case 4: if ( islower((int)fchar) ) isinclass=1; break;
8459: case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
8460: case 9: isinclass=1; break; }
8461: if ( isinclass ) /* convert current char to \font */
8462: { strcat(subexpr,name); /* by prefixing it with font name */
8463: isprevchar = 1; } /* and set flag to signal separator*/
8464: else /* current char not in \font */
8465: { if ( isprevchar ) /* extra separator only after \font*/
8466: if ( isalpha(fchar) ) /* separator only before alpha */
8467: strcat(subexpr,"~"); /* need separator after \font */
8468: isprevchar = 0; } /* reset flag for next char */
8469: exprlen = strlen(subexpr); /* #chars so far */
8470: subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/
8471: subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */
8472: } /* --- end-of-for(pfchars) --- */
8473: strcat(subexpr,"}"); /* add closing } */
8474: } /* --- end-of-if/else(class<0) --- */
8475: /* -------------------------------------------------------------------------
8476: rasterize subexpression containing chars to be rendered at font
8477: -------------------------------------------------------------------------- */
8478: if ( msgfp!=NULL && msglevel>=99 )
8479: fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
8480: if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
8481: == NULL ) goto end_of_job; /* and quit if failed */
8482: /* -------------------------------------------------------------------------
8483: back to caller with chars rendered in font
8484: -------------------------------------------------------------------------- */
8485: end_of_job:
1.2 albertel 8486: smashmargin = oldsmashmargin; /* restore smash */
1.3 ! albertel 8487: mathsmashmargin = SMASHMARGIN; /* this one probably not necessary */
1.2 albertel 8488: if ( istext && fontsp!=NULL ) /* raster contains text mode font */
8489: fontsp->type = blanksignal; /* signal nosmash */
1.1 albertel 8490: return ( fontsp ); /* chars rendered in font */
8491: } /* --- end-of-function rastfont() --- */
8492:
8493:
8494: /* ==========================================================================
8495: * Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
8496: * Purpose: \begin{}...\end{} handler, returns a subraster corresponding
8497: * to array expression within environment, i.e., rewrites
8498: * \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
8499: * --------------------------------------------------------------------------
8500: * Arguments: expression (I/O) char ** to first char of null-terminated
8501: * string immediately following \begin to be
8502: * rasterized, and returning ptr immediately
8503: * following last character processed.
8504: * size (I) int containing 0-4 default font size
8505: * basesp (I) subraster * to character (or subexpression)
8506: * immediately preceding \begin
8507: * (unused, but passed for consistency)
8508: * arg1 (I) int unused
8509: * arg2 (I) int unused
8510: * arg3 (I) int unused
8511: * --------------------------------------------------------------------------
8512: * Returns: ( subraster * ) ptr to subraster corresponding to array
8513: * expression, or NULL for any parsing error
8514: * --------------------------------------------------------------------------
8515: * Notes: o
8516: * ======================================================================= */
8517: /* --- entry point --- */
8518: subraster *rastbegin ( char **expression, int size, subraster *basesp,
8519: int arg1, int arg2, int arg3 )
8520: {
8521: /* -------------------------------------------------------------------------
8522: Allocations and Declarations
8523: -------------------------------------------------------------------------- */
1.3 ! albertel 8524: char *texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
1.1 albertel 8525: *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
8526: char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
8527: int strreplace(); /* replace substring in string */
8528: char *strchange(); /*\begin...\end --> {\begin...\end}*/
8529: char *delims = (char *)NULL; /* mdelims[ienviron] */
8530: subraster *rasterize(), *sp=NULL; /* rasterize environment */
8531: int ienviron = 0; /* environs[] index */
8532: int nbegins = 0; /* #\begins nested beneath this one*/
8533: int envlen=0, sublen=0; /* #chars in environ, subexpr */
8534: static int blevel = 0; /* \begin...\end nesting level */
8535: static char *mdelims[] = { NULL, NULL, NULL, NULL,
8536: "()","[]","{}","||","==", /* for pbBvVmatrix */
1.3 ! albertel 8537: NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
1.1 albertel 8538: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
8539: static char *environs[] = { /* types of environments we process*/
8540: "eqnarray", /* 0 eqnarray environment */
8541: "array", /* 1 array environment */
8542: "matrix", /* 2 array environment */
8543: "tabular", /* 3 array environment */
8544: "pmatrix", /* 4 ( ) */
8545: "bmatrix", /* 5 [ ] */
8546: "Bmatrix", /* 6 { } */
8547: "vmatrix", /* 7 | | */
8548: "Vmatrix", /* 8 || || */
8549: "gather", /* 9 gather environment */
8550: "align", /* 10 align environment */
8551: "verbatim", /* 11 verbatim environment */
8552: "picture", /* 12 picture environment */
1.3 ! albertel 8553: "cases", /* 13 cases environment */
! 8554: "equation", /* 14 for \begin{equation} */
1.1 albertel 8555: NULL }; /* trailer */
8556: /* -------------------------------------------------------------------------
8557: determine type of environment we're beginning
8558: -------------------------------------------------------------------------- */
8559: /* --- first bump nesting level --- */
8560: blevel++; /* count \begin...\begin...'s */
8561: /* --- \begin must be followed by {type_of_environment} --- */
8562: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
8563: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
8564: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
8565: strcpy(delims,delims+1); /* treat it as environment */
8566: /* --- look up environment in our table --- */
8567: for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
8568: if ( environs[ienviron] == NULL ) /* found NULL before match */
8569: goto end_of_job; /* so quit */
8570: else /* see if we have an exact match */
8571: if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
8572: break; /* leave loop with ienviron index */
8573: /* --- accumulate any additional params for this environment --- */
8574: *subexpr = '\000'; /* reset subexpr to empty string */
8575: delims = mdelims[ienviron]; /* mdelims[] string for ienviron */
8576: if ( delims != NULL ) /* add appropriate opening delim */
8577: { strcpy(subexpr,"\\"); /* start with \ for (,[,{,|,= */
8578: strcat(subexpr,delims); /* then add opening delim */
8579: subexpr[2] = '\000'; } /* remove extraneous closing delim */
8580: switch ( ienviron )
8581: {
8582: default: goto end_of_job; /* environ not implemented yet */
8583: case 0: /* \begin{eqnarray} */
8584: strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */
8585: break;
8586: case 1: case 2: case 3: /* \begin{array} followed by {lcr} */
8587: strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
8588: skipwhite(exprptr); /* bump to next non-white char */
8589: if ( *exprptr == '{' ) /* assume we have {lcr} argument */
8590: { exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
8591: if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
8592: strcat(subexpr,"$"); } /* add terminating $ to lcr */
8593: break;
8594: case 4: case 5: case 6: /* \begin{pmatrix} or b,B,v,Vmatrix */
8595: case 7: case 8:
8596: strcat(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
8597: break;
8598: case 9: /* gather */
8599: strcat(subexpr,"\\array{c$"); /* center equations */
8600: break;
8601: case 10: /* align */
8602: strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
8603: break;
8604: case 11: /* verbatim */
8605: strcat(subexpr,"{\\rm "); /* {\rm ...} */
8606: /*strcat(subexpr,"\\\\{\\rm ");*/ /* \\{\rm } doesn't work in context */
8607: break;
8608: case 12: /* picture */
8609: strcat(subexpr,"\\picture"); /* picture environment */
8610: skipwhite(exprptr); /* bump to next non-white char */
8611: if ( *exprptr == '(' ) /*assume we have (width,height) arg*/
8612: { exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
8613: if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
8614: strcat(subexpr,"{"); /* opening { after (width,height) */
8615: break;
1.3 ! albertel 8616: case 13: /* cases */
! 8617: strcat(subexpr,"\\array{ll$"); /* a&b \\ c&d etc */
! 8618: break;
! 8619: case 14: /* \begin{equation} */
! 8620: strcat(subexpr,"{"); /* just enclose expression in {}'s */
! 8621: break;
1.1 albertel 8622: } /* --- end-of-switch(ienviron) --- */
8623: /* -------------------------------------------------------------------------
8624: locate matching \end{...}
8625: -------------------------------------------------------------------------- */
8626: /* --- first \end following \begin --- */
8627: if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */
8628: == NULL ) goto end_of_job; /* and quit if no \end found */
8629: /* --- find matching endptr by pushing past any nested \begin's --- */
8630: begptr = exprptr; /* start after first \begin{...} */
8631: while ( 1 ) /*break when we find matching \end*/
8632: {
8633: /* --- first, set ptr to closing } terminating current \end{...} --- */
8634: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
8635: == NULL ) goto end_of_job; /* and quit if no } found */
8636: /* -- locate next nested \begin --- */
8637: if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
8638: == NULL ) break; /*no more, so we have matching \end*/
8639: begptr += strlen(begtoken); /* push ptr past token */
8640: if ( begptr >= endptr ) break; /* past endptr, so not nested */
8641: /* --- have nested \begin, so push forward to next \end --- */
8642: nbegins++; /* count another nested \begin */
8643: if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
8644: == NULL ) goto end_of_job; /* and quit if none found */
8645: } /* --- end-of-while(1) --- */
8646: /* --- push expression past closing } of \end{} --- */
8647: *expression = braceptr+1; /* resume processing after } */
8648: /* -------------------------------------------------------------------------
8649: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
8650: -------------------------------------------------------------------------- */
8651: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
8652: sublen = strlen(subexpr); /* #chars in "preamble" */
8653: envlen = (int)(endptr-exprptr); /* #chars between \begin{}{}...\end */
8654: memcpy(subexpr+sublen,exprptr,envlen); /*concatanate environ after subexpr*/
8655: subexpr[sublen+envlen] = '\000'; /* and null-terminate */
8656: if ( 2 > 1 ) /* always... */
8657: strcat(subexpr,"}"); /* ...followed by terminating } */
8658: /* --- add terminating \right), etc, if necessary --- */
8659: if ( delims != (char *)NULL ) /* need closing delim */
8660: { strcat(subexpr,"\\"); /* start with \ for ),],},|,= */
8661: strcat(subexpr,delims+1); } /* add appropriate closing delim */
8662: /* -------------------------------------------------------------------------
8663: change nested \begin...\end to {\begin...\end} so \array{} can handle them
8664: -------------------------------------------------------------------------- */
8665: if ( nbegins > 0 ) /* have nested begins */
8666: if ( blevel < 2 ) /* only need to do this once */
8667: {
8668: begptr = subexpr; /* start at beginning of subexpr */
8669: while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
8670: { strchange(0,begptr,"{"); /* \begin --> {\begin */
8671: begptr += strlen(begtoken); } /* continue past {\begin */
8672: endptr = subexpr; /* start at beginning of subexpr */
8673: while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
8674: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
8675: == NULL ) goto end_of_job; /* and quit if no } found */
8676: else /* found terminating } */
8677: { strchange(0,braceptr,"}"); /* \end{...} --> \end{...}} */
8678: endptr = braceptr+1; } /* continue past \end{...} */
8679: } /* --- end-of-if(nbegins>0) --- */
8680: /* -------------------------------------------------------------------------
8681: post process as necessary
8682: -------------------------------------------------------------------------- */
8683: switch ( ienviron )
8684: {
8685: default: break; /* no post-processing required */
8686: case 10: /* align */
8687: strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
8688: strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
8689: strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
8690: strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
8691: strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
8692: strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
8693: strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
8694: if ( nbegins < 1 ) /* don't modify nested arrays */
8695: strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
8696: strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
8697: strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
8698: strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
8699: strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
8700: strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
8701: break;
8702: case 11: /* verbatim */
8703: strreplace(subexpr,"\n","\\\\",0); /* xlate \n newline to latex \\ */
8704: /*strcat(subexpr,"\\\\");*/ /* add final latex \\ newline */
8705: break;
8706: case 12: /* picture */
8707: strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
8708: strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
8709: strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
8710: break;
8711: } /* --- end-of-switch(ienviron) --- */
8712: /* -------------------------------------------------------------------------
8713: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
8714: -------------------------------------------------------------------------- */
8715: /* --- debugging output --- */
8716: if ( msgfp!=NULL && msglevel>=99 )
8717: fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
8718: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
8719: sp = rasterize(subexpr,size); /* rasterize subexpr */
8720: end_of_job:
8721: blevel--; /* decrement \begin nesting level */
8722: return ( sp ); /* back to caller with sp or NULL */
8723: } /* --- end-of-function rastbegin() --- */
8724:
8725:
8726: /* ==========================================================================
8727: * Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 )
8728: * Purpose: \array handler, returns a subraster corresponding to array
8729: * expression (immediately following \array) at font size
8730: * --------------------------------------------------------------------------
8731: * Arguments: expression (I/O) char ** to first char of null-terminated
8732: * string immediately following \array to be
8733: * rasterized, and returning ptr immediately
8734: * following last character processed.
8735: * size (I) int containing 0-4 default font size
8736: * basesp (I) subraster * to character (or subexpression)
8737: * immediately preceding \array
8738: * (unused, but passed for consistency)
8739: * arg1 (I) int unused
8740: * arg2 (I) int unused
8741: * arg3 (I) int unused
8742: * --------------------------------------------------------------------------
8743: * Returns: ( subraster * ) ptr to subraster corresponding to array
8744: * expression, or NULL for any parsing error
8745: * --------------------------------------------------------------------------
8746: * Notes: o Summary of syntax...
8747: * \array{3,lcrBC$a&b&c\\d&e&f\\etc}
8748: * o The 3,lcrBC$ part is an optional "preamble". The lcr means
8749: * what you think, i.e., "horizontal" left,center,right
8750: * justification down corresponding column. The new BC means
8751: * "vertical" baseline,center justification across corresponding
8752: * row. The leading 3 specifies the font size 0-4 to be used.
8753: * You may also specify +1,-1,+2,-2, etc, which is used as an
8754: * increment to the current font size, e.g., -1,lcr$ uses
8755: * one font size smaller than current. Without a leading
8756: * + or -, the font size is "absolute".
8757: * o The preamble can also be just lcrBC$ without a leading
8758: * size-part, or just 3$ without a trailing lcrBC-part.
8759: * The default size is whatever is current, and the
8760: * default justification is c(entered) and B(aseline).
8761: * ======================================================================= */
8762: /* --- entry point --- */
8763: subraster *rastarray ( char **expression, int size, subraster *basesp,
8764: int arg1, int arg2, int arg3 )
8765: {
8766: /* -------------------------------------------------------------------------
8767: Allocations and Declarations
8768: -------------------------------------------------------------------------- */
1.3 ! albertel 8769: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
! 8770: subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
! 8771: token[MAXTOKNSZ+1], *tokptr=token, /* subexpr token to rasterize */
1.1 albertel 8772: *preamble(), *preptr=token; /*process optional size,lcr preamble*/
8773: char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
8774: int maxarraysz = 64; /* max #rows, cols */
8775: 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 */
8776: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8777: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8778: hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
8779: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8780: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8781: vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
8782: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8783: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8784: colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
8785: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8786: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8787: rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
8788: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8789: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8790: fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
8791: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8792: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8793: fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
8794: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8795: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8796: rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
8797: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8798: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8799: rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
8800: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8801: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
8802: static int /* --- propagate global values across arrays --- */
8803: 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 */
8804: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8805: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8806: gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
8807: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8808: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8809: growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
8810: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8811: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8812: gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
8813: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8814: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8815: gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
8816: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8817: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
8818: growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
8819: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8820: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
8821: int rowglobal=0, colglobal=0, /* true to set global values */
8822: rowpropagate=0, colpropagate=0; /* true if propagating values */
8823: int irow,nrows=0, icol,ncols[65], /*#rows in array, #cols in each row*/
8824: maxcols=0; /* max# cols in any single row */
8825: int itoken, ntokens=0, /* index, total #tokens in array */
8826: subtoklen=0, /* strlen of {...} subtoken */
8827: istokwhite=1, /* true if token all whitespace */
8828: nnonwhite=0; /* #non-white tokens */
8829: int isescape=0,wasescape=0, /* current,prev chars escape? */
8830: ischarescaped=0, /* is current char escaped? */
8831: nescapes=0; /* #consecutive escapes */
8832: subraster *rasterize(), *toksp[1025], /* rasterize tokens */
8833: *new_subraster(), *arraysp=NULL; /* subraster for entire array */
8834: raster *arrayrp=NULL; /* raster for entire array */
8835: int delete_subraster(); /* free toksp[] workspace at eoj */
8836: int rowspace=2, colspace=4, /* blank space between rows, cols */
8837: hspace=1, vspace=1; /*space to accommodate hline,vline*/
8838: int width=0, height=0, /* width,height of array */
8839: leftcol=0, toprow=0; /*upper-left corner for cell in it*/
8840: int rastput(); /* embed tokens/cells in array */
8841: int rule_raster(); /* draw hlines and vlines in array */
8842: char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
8843: char *texchar(), hltoken[1025]; /* extract \hline from token */
8844: int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
8845: int isnewrow=1; /* true for new row */
8846: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
8847: /* -------------------------------------------------------------------------
8848: Macros to determine extra raster space required for vline/hline
8849: -------------------------------------------------------------------------- */
8850: #define vlinespace(icol) \
8851: ( vline[icol] == 0? 0 : /* no vline so no space needed */ \
8852: ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
8853: #define hlinespace(irow) \
8854: ( hline[irow] == 0? 0 : /* no hline so no space needed */ \
8855: ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
8856: /* -------------------------------------------------------------------------
8857: Obtain array subexpression
8858: -------------------------------------------------------------------------- */
8859: /* --- parse for array subexpression, and bump expression past it --- */
8860: subexpr[1] = *subexpr = ' '; /* set two leading blanks */
8861: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
8862: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display array */
8863: fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
8864: if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */
8865: goto end_of_job; /* nothing to do, so quit */
8866: /* -------------------------------------------------------------------------
8867: process optional size,lcr preamble if present
8868: -------------------------------------------------------------------------- */
8869: /* --- reset size, get lcr's, and push exprptr past preamble --- */
8870: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
8871: /* --- init with global values --- */
8872: for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
8873: justify[icol] = gjustify[icol]; /* -1,0,+1 = l,c,r */
8874: colwidth[icol] = gcolwidth[icol]; /* column width */
8875: rowheight[icol] = growheight[icol]; /* row height */
8876: fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
8877: fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
8878: rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
8879: /* --- process lcr's, etc in preamble --- */
8880: itoken = 0; /* debugging flag */
8881: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display preamble */
8882: if ( *preptr != '\000' ) /* if we have one */
8883: fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
8884: preptr);
8885: irow = icol = 0; /* init lcr counts */
8886: while ( *preptr != '\000' ) /* check preamble text for lcr */
8887: {
8888: char prepchar = *preptr; /* current preamble character */
8889: int prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
8890: if ( irow<maxarraysz && icol<maxarraysz )
8891: switch ( /*tolower*/(prepchar) )
8892: { default: break; /* just flush unrecognized chars */
8893: case 'l': justify[icol] = (-1); /*left-justify this column*/
8894: if (colglobal) gjustify[irow] = justify[irow]; break;
8895: case 'c': justify[icol] = (0); /* center this column */
8896: if (colglobal) gjustify[irow] = justify[irow]; break;
8897: case 'r': justify[icol] = (+1); /* right-justify this col */
8898: if (colglobal) gjustify[irow] = justify[irow]; break;
8899: case '|': vline[icol] += 1; break; /* solid vline left of col */
8900: case '.': vline[icol] = (-1); break; /*dashed vline left of col */
8901: case 'b': prepchar='B'; prepcase=2; /* alias for B */
8902: case 'B': break; /* baseline-justify row */
8903: case 'v': prepchar='C'; prepcase=2; /* alias for C */
8904: case 'C': rowcenter[irow] = 1; /* vertically center row */
8905: if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
8906: case 'g': colglobal=1; prepcase=0; break; /* set global col values */
8907: case 'G': rowglobal=1; prepcase=0; break; /* set global row values */
8908: case '#': colglobal=rowglobal=1; break; } /* set global col,row vals */
8909: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8910: fprintf(msgfp," %c[%d]",prepchar,
8911: (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
8912: preptr++; /* check next char for lcr */
8913: itoken++; /* #lcr's processed (debugging only)*/
8914: /* --- check for number or +number specifying colwidth or rowheight --- */
8915: if ( prepcase != 0 ) /* only check upper,lowercase */
8916: {
8917: int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
1.3 ! albertel 8918: if ( ispropagate ) { /* set row or col propagation */
1.1 albertel 8919: if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
1.3 ! albertel 8920: else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
1.1 albertel 8921: if ( !colpropagate && prepcase == 1 )
8922: { colwidth[icol] = 0; /* reset colwidth */
8923: fixcolsize[icol] = 0; } /* reset width flag */
8924: if ( !rowpropagate && prepcase == 2 )
8925: { rowheight[irow] = 0; /* reset row height */
8926: fixrowsize[irow] = 0; } /* reset height flag */
8927: if ( ispropagate ) preptr++; /* bump past leading + */
8928: if ( isdigit(*preptr) ) /* digit follows character */
8929: { char *endptr = NULL; /* preptr set to 1st char after num*/
8930: int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
8931: char *whchars="?wh"; /* debugging width/height labels */
8932: preptr = endptr; /* skip over all digits */
8933: if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
8934: int index; /* icol,irow...maxarraysz index */
8935: if ( prepcase == 1 ) /* lowercase signifies colwidth */
8936: for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
8937: colwidth[index] = size; /* set colwidth to fixed size */
8938: fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
8939: justify[index] = justify[icol]; /* and propagate justification */
8940: if ( colglobal ) { /* set global values */
8941: gcolwidth[index] = colwidth[index]; /* set global col width */
8942: gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
8943: gjustify[index] = justify[icol]; } /* set global col justify */
8944: if ( !ispropagate ) break; } /* don't propagate */
8945: else /* uppercase signifies rowheight */
8946: for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
8947: rowheight[index] = size; /* set rowheight to size */
8948: fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
8949: rowcenter[index] = rowcenter[irow]; /* and propagate row center */
8950: if ( rowglobal ) { /* set global values */
8951: growheight[index] = rowheight[index]; /* set global row height */
8952: gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
8953: growcenter[index] = rowcenter[irow]; } /*set global row center*/
8954: if ( !ispropagate ) break; } /* don't propagate */
8955: } /* --- end-of-if(size>=3&&size<=500) --- */
8956: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
8957: fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
8958: (prepcase==1?colwidth[icol]:rowheight[irow]),
8959: (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
8960: } /* --- end-of-if(isdigit()) --- */
8961: } /* --- end-of-if(prepcase!=0) --- */
8962: if ( prepcase == 1 ) icol++; /* bump col if lowercase lcr */
8963: else if ( prepcase == 2 ) irow++; /* bump row if uppercase BC */
8964: } /* --- end-of-while(*preptr!='\000') --- */
8965: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, emit final newline */
8966: if ( itoken > 0 ) /* if we have preamble */
8967: fprintf(msgfp,"\n");
8968: /* -------------------------------------------------------------------------
8969: tokenize and rasterize components a & b \\ c & d \\ etc of subexpr
8970: -------------------------------------------------------------------------- */
8971: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
8972: ncols[nrows] = 0; /* no tokens/cols in top row yet */
8973: while ( 1 ) /* scan chars till end */
8974: {
8975: /* --- local control flags --- */
8976: int iseox = (*exprptr == '\000'), /* null signals end-of-expression */
8977: iseor = iseox, /* \\ or eox signals end-of-row */
8978: iseoc = iseor; /* & or eor signals end-of-col */
8979: /* --- check for escapes --- */
8980: isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
8981: wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
8982: nescapes = (wasescape?nescapes+1:0); /* # preceding consecutive escapes */
8983: ischarescaped = (nescapes%2==0?0:1); /* is current char escaped? */
8984: /* -----------------------------------------------------------------------
8985: check for {...} subexpression starting from where we are now
8986: ------------------------------------------------------------------------ */
8987: if ( *exprptr == '{' /* start of {...} subexpression */
8988: && !ischarescaped ) /* if not escaped \{ */
8989: {
8990: subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
8991: subtoklen = strlen(subtok); /* #chars in {...} */
8992: memcpy(tokptr,exprptr,subtoklen); /* copy {...} to accumulated token */
8993: tokptr += subtoklen; /* bump tokptr to end of token */
8994: exprptr += subtoklen; /* and bump exprptr past {...} */
8995: istokwhite = 0; /* signal non-empty token */
8996: continue; /* continue with char after {...} */
8997: } /* --- end-of-if(*exprptr=='{') --- */
8998: /* -----------------------------------------------------------------------
8999: check for end-of-row(\\) and/or end-of-col(&)
9000: ------------------------------------------------------------------------ */
9001: /* --- check for (escaped) end-of-row delimiter --- */
9002: if ( isescape && !ischarescaped ) /* current char is escaped */
9003: if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
9004: || *(exprptr+1) == '\000' ) /* or a pathological null */
9005: { iseor = 1; /* so set end-of-row flag */
9006: wasescape=isescape=nescapes = 0; } /* reset flags for new row */
9007: /* --- check for end-of-col delimiter --- */
9008: if (iseor /* end-of-row signals end-of-col */
9009: || (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
9010: iseoc = 1; /* so set end-of-col flag */
9011: /* -----------------------------------------------------------------------
9012: rasterize completed token
9013: ------------------------------------------------------------------------ */
9014: if ( iseoc ) /* we have a completed token */
9015: {
9016: *tokptr = '\000'; /* first, null-terminate token */
9017: /* --- check first token in row for \hline or \hdash --- */
9018: ishonly = 0; /*init for token not only an \hline*/
9019: if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/
9020: {
9021: tokptr=token; skipwhite(tokptr); /* skip whitespace after // */
9022: tokptr = texchar(tokptr,hltoken); /* extract first char from token */
9023: hltoklen = strlen(hltoken); /* length of first char */
1.3 ! albertel 9024: if ( hltoklen >= minhltoklen ) { /*token must be at least \hl or \hd*/
1.1 albertel 9025: if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
9026: hline[nrows] += 1; /* bump \hline count for row */
9027: else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
1.3 ! albertel 9028: hline[nrows] = (-1); } /* set \hdash flag for row */
1.1 albertel 9029: if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
9030: { skipwhite(tokptr); /* flush whitespace after \hline */
9031: if ( *tokptr == '\000' /* end-of-expression after \hline */
9032: || isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
1.2 albertel 9033: { istokwhite = 1; /* so token contains \hline only */
9034: if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
1.1 albertel 9035: else /* token contains more than \hline */
9036: strcpy(token,tokptr); } /* so flush \hline from token */
9037: } /* --- end-of-if(ncols[nrows]==0) --- */
9038: /* --- rasterize completed token --- */
9039: toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
9040: rasterize(token,size)); /* rasterize non-empty token */
9041: if ( toksp[ntokens] != NULL ) /* have a rasterized token */
9042: nnonwhite++; /* bump rasterized token count */
9043: /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
9044: if ( toksp[ntokens] != NULL ) /* we have a rasterized token */
9045: {
9046: /* --- update max token "height" in current row, and baseline --- */
9047: int twidth = ((toksp[ntokens])->image)->width, /* width of token */
9048: theight = ((toksp[ntokens])->image)->height, /* height of token */
9049: tbaseln = (toksp[ntokens])->baseline, /* baseline of token */
9050: rheight = rowheight[nrows], /* current max height for row */
9051: rbaseln = rowbaseln[nrows]; /* current baseline for max height */
9052: if ( 0 || fixrowsize[nrows]==0 ) /* rowheight not fixed */
9053: rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
9054: max2(rbaseln+1,tbaseln+1) /* max height above baseline */
9055: + max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
9056: rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
9057: /* --- update max token width in current column --- */
9058: icol = ncols[nrows]; /* current column index */
9059: if ( 0 || fixcolsize[icol]==0 ) /* colwidth not fixed */
9060: colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
9061: } /* --- end-of-if(toksp[]!=NULL) --- */
9062: /* --- bump counters --- */
9063: if ( !ishonly ) /* don't count only an \hline */
9064: { ntokens++; /* bump total token count */
9065: ncols[nrows] += 1; } /* and bump #cols in current row */
9066: /* --- get ready for next token --- */
9067: tokptr = token; /* reset ptr for next token */
9068: istokwhite = 1; /* next token starts all white */
9069: } /* --- end-of-if(iseoc) --- */
9070: /* -----------------------------------------------------------------------
9071: bump row as necessary
9072: ------------------------------------------------------------------------ */
9073: if ( iseor ) /* we have a completed row */
9074: {
9075: maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
9076: if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
9077: nrows++; /* bump row count */
9078: ncols[nrows] = 0; /* no cols in this row yet */
9079: if ( !iseox ) /* don't have a null yet */
9080: { exprptr++; /* bump past extra \ in \\ delim */
9081: iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
9082: isnewrow = 1; /* signal start of new row */
9083: } /* --- end-of-if(iseor) --- */
9084: else
9085: isnewrow = 0; /* no longer first col of new row */
9086: /* -----------------------------------------------------------------------
9087: quit when done, or accumulate char in token and proceed to next char
9088: ------------------------------------------------------------------------ */
9089: /* --- quit when done --- */
9090: if ( iseox ) break; /* null terminator signalled done */
9091: /* --- accumulate chars in token --- */
9092: if ( !iseoc ) /* don't accumulate delimiters */
9093: { *tokptr++ = *exprptr; /* accumulate non-delim char */
9094: if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
9095: istokwhite = 0; } /* so reset flag to rasterize it */
9096: /* --- ready for next char --- */
9097: exprptr++; /* bump ptr */
9098: } /* --- end-of-while(*exprptr!='\000') --- */
9099: /* --- make sure we got something to do --- */
9100: if ( nnonwhite < 1 ) /* completely empty array */
9101: goto end_of_job; /* NULL back to caller */
9102: /* -------------------------------------------------------------------------
9103: determine dimensions of array raster and allocate it
9104: -------------------------------------------------------------------------- */
9105: /* --- adjust colspace --- */
9106: colspace = 2 + 2*size; /* temp kludge */
9107: /* --- reset propagated sizes at boundaries of array --- */
9108: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
9109: /* --- determine width of array raster --- */
9110: width = colspace*(maxcols-1); /* empty space between cols */
9111: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
9112: fprintf(msgfp,"rastarray> %d cols, widths: ",maxcols);
9113: for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */
9114: { width += colwidth[icol]; /*width of this col (0 for maxcols)*/
9115: width += vlinespace(icol); /*plus space for vline, if present*/
9116: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
9117: fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
9118: /* --- determine height of array raster --- */
9119: height = rowspace*(nrows-1); /* empty space between rows */
9120: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
9121: fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
9122: for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
9123: { height += rowheight[irow]; /*height of this row (0 for nrows)*/
9124: height += hlinespace(irow); /*plus space for hline, if present*/
9125: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
9126: fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
9127: /* --- allocate subraster and raster for array --- */
9128: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
9129: fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
9130: width,colspace, height,rowspace);
9131: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
9132: == NULL ) goto end_of_job; /* quit if failed */
9133: /* --- initialize subraster parameters --- */
9134: arraysp->type = IMAGERASTER; /* image */
9135: arraysp->symdef = NULL; /* not applicable for image */
9136: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
9137: arraysp->size = size; /* size (probably unneeded) */
9138: arrayrp = arraysp->image; /* raster embedded in subraster */
9139: /* -------------------------------------------------------------------------
9140: embed tokens/cells in array
9141: -------------------------------------------------------------------------- */
9142: itoken = 0; /* start with first token */
9143: toprow = 0; /* start at top row of array */
9144: for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/
9145: {
9146: /* --- initialization for row --- */
9147: int baseline = rowbaseln[irow]; /* baseline for this row */
9148: if ( hline[irow] != 0 ) /* need hline above this row */
9149: { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
9150: if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
9151: rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
9152: if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
9153: toprow += hlinespace(irow); /* space for hline above irow */
9154: leftcol = 0; /* start at leftmost column */
9155: for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
9156: {
9157: subraster *tsp = toksp[itoken]; /* token that belongs in this cell */
1.2 albertel 9158: /* --- first adjust leftcol for vline to left of icol, if present ---- */
9159: leftcol += vlinespace(icol); /* space for vline to left of col */
9160: /* --- now rasterize cell ---- */
1.1 albertel 9161: if ( tsp != NULL ) /* have a rasterized cell token */
9162: {
9163: /* --- local parameters --- */
9164: int cwidth = colwidth[icol], /* total column width */
9165: twidth = (tsp->image)->width, /* token width */
9166: theight= (tsp->image)->height, /* token height */
9167: tokencol = 0, /*H offset (init for left justify)*/
9168: tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
9169: /* --- adjust leftcol for vline to left of icol, if present ---- */
1.2 albertel 9170: /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
1.1 albertel 9171: /* --- reset justification (if not left-justified) --- */
9172: if ( justify[icol] == 0 ) /* but user wants it centered */
9173: tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
9174: else if ( justify[icol] == 1 ) /* or user wants right-justify */
9175: tokencol = cwidth-twidth; /* so put entire margin at left */
9176: /* --- reset vertical centering (if not baseline-aligned) --- */
9177: if ( rowcenter[irow] ) /* center cells in row vertically */
9178: tokenrow = (rowheight[irow]-theight)/2; /* center row */
9179: /* --- embed token raster at appropriate place in array raster --- */
9180: rastput(arrayrp,tsp->image, /* overlay cell token in array */
9181: toprow+ tokenrow, /*with aligned baseline or centered*/
9182: leftcol+tokencol, 1); /* and justified as requested */
9183: } /* --- end-of-if(tsp!=NULL) --- */
9184: itoken++; /* bump index for next cell */
9185: leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
9186: /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
9187: } /* --- end-of-for(icol) --- */
9188: toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
9189: } /* --- end-of-for(irow) --- */
9190: /* -------------------------------------------------------------------------
9191: draw vlines as necessary
9192: -------------------------------------------------------------------------- */
9193: leftcol = 0; /* start at leftmost column */
9194: for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */
9195: {
9196: if ( vline[icol] != 0 ) /* need vline to left of this col */
9197: { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
9198: if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
9199: rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
9200: leftcol += vlinespace(icol); /* space for vline to left of col */
9201: if ( icol < maxcols ) /* don't address past end of array */
9202: leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
9203: } /* --- end-of-for(icol) --- */
9204: /* -------------------------------------------------------------------------
9205: free workspace and return final result to caller
9206: -------------------------------------------------------------------------- */
9207: end_of_job:
9208: /* --- free workspace --- */
9209: if ( ntokens > 0 ) /* if we have workspace to free */
9210: while ( --ntokens >= 0 ) /* free each token subraster */
9211: if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */
9212: delete_subraster(toksp[ntokens]); /* then free it */
9213: /* --- return final result to caller --- */
9214: return ( arraysp );
9215: } /* --- end-of-function rastarray() --- */
9216:
9217:
9218: /* ==========================================================================
9219: * Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
9220: * Purpose: \picture handler, returns subraster corresponding to picture
9221: * expression (immediately following \picture) at font size
9222: * --------------------------------------------------------------------------
9223: * Arguments: expression (I/O) char ** to first char of null-terminated
9224: * string immediately following \picture to be
9225: * rasterized, and returning ptr immediately
9226: * following last character processed.
9227: * size (I) int containing 0-4 default font size
9228: * basesp (I) subraster * to character (or subexpression)
9229: * immediately preceding \picture
9230: * (unused, but passed for consistency)
9231: * arg1 (I) int unused
9232: * arg2 (I) int unused
9233: * arg3 (I) int unused
9234: * --------------------------------------------------------------------------
9235: * Returns: ( subraster * ) ptr to subraster corresponding to picture
9236: * expression, or NULL for any parsing error
9237: * --------------------------------------------------------------------------
9238: * Notes: o Summary of syntax...
9239: * \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
9240: * o
9241: * ======================================================================= */
9242: /* --- entry point --- */
9243: subraster *rastpicture ( char **expression, int size, subraster *basesp,
9244: int arg1, int arg2, int arg3 )
9245: {
9246: /* -------------------------------------------------------------------------
9247: Allocations and Declarations
9248: -------------------------------------------------------------------------- */
9249: char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
9250: putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
9251: pream[64], *preptr, /* optional put preamble */
9252: picelem[1025]; /* picture element following put */
9253: subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
9254: *new_subraster(), *picturesp=NULL, /* subraster for entire picture */
9255: *oldworkingbox = workingbox; /* save working box on entry */
9256: raster *picturerp=NULL; /* raster for entire picture */
9257: int delete_subraster(); /* free picelemsp[] workspace */
9258: int pixsz = 1; /* pixels are one bit each */
9259: double strtod(), /* convert ascii params to doubles */
9260: x=0.0,y=0.0, /* x,y-coords for put,multiput*/
9261: xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
9262: int width=0, height=0, /* #pixels width,height of picture */
9263: ewidth=0, eheight=0, /* pic element width,height */
9264: ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
9265: num=1, inum; /* number reps, index of element */
9266: int iscenter=0; /* center or lowerleft put position*/
9267: int *oldworkingparam = workingparam, /* save working param on entry */
9268: origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
9269: int rastput(); /* embed elements in picture */
9270: int type_raster(); /* display debugging output */
9271: /* -------------------------------------------------------------------------
9272: First obtain (width,height) arguments immediately following \picture command
9273: -------------------------------------------------------------------------- */
9274: /* --- parse for (width,height) arguments, and bump expression past it --- */
9275: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
9276: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
9277: /* --- now interpret width,height returned in putexpr --- */
9278: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
9279: *putptr = '\000'; /* found it, so replace ',' by '\0'*/
9280: width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
9281: if ( putptr != NULL ) /* 2nd arg, if present, is height */
9282: height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
9283: /* -------------------------------------------------------------------------
9284: Then obtain entire picture {...} subexpression following (width,height)
9285: -------------------------------------------------------------------------- */
9286: /* --- parse for picture subexpression, and bump expression past it --- */
9287: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
9288: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
9289: /* -------------------------------------------------------------------------
9290: allocate subraster and raster for complete picture
9291: -------------------------------------------------------------------------- */
9292: /* --- sanity check on width,height args --- */
9293: if ( width < 2 || width > 600
9294: || height < 2 || height > 600 ) goto end_of_job;
9295: /* --- allocate and initialize subraster for constructed picture --- */
9296: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
9297: == NULL ) goto end_of_job; /* quit if failed */
9298: workingbox = picturesp; /* set workingbox to our picture */
9299: /* --- initialize picture subraster parameters --- */
9300: picturesp->type = IMAGERASTER; /* image */
9301: picturesp->symdef = NULL; /* not applicable for image */
9302: picturesp->baseline = height/2 + 2; /* is a little above center good? */
9303: picturesp->size = size; /* size (probably unneeded) */
9304: picturerp = picturesp->image; /* raster embedded in subraster */
9305: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9306: fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
9307: /* -------------------------------------------------------------------------
9308: parse out each picture element, rasterize it, and place it in picture
9309: -------------------------------------------------------------------------- */
9310: while ( *picptr != '\000' ) /* until we run out of pic_elems */
9311: {
9312: /* -----------------------------------------------------------------------
9313: first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
9314: ------------------------------------------------------------------------ */
9315: /* --- init default values in case not explicitly supplied in args --- */
9316: x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */
9317: iscenter = origin = 0; /* center, origin */
9318: /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
9319: while ( *picptr != '\000' ) /* skip invalid chars preceding ( */
9320: if ( *picptr == '(' ) break; /* found opening ( */
9321: else picptr++; /* else skip invalid char */
9322: picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
9323: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
9324: /* --- first look for $-terminated or for any non-digit preamble --- */
9325: *pream = '\000'; /* init preamble as empty string */
9326: if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
9327: { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
9328: strcpy(pream,putexpr); } /* copy leading preamble from put */
9329: else /* look for any non-digit preamble */
9330: { for ( preptr=pream,putptr=putexpr; ; putptr++ )
9331: if ( *putptr == '\000' /* end-of-putdata signalled */
9332: || !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
9333: else *preptr++ = *putptr; /* copy alpha char to preamble */
9334: *preptr = '\000'; } /* null-terminate preamble */
9335: /* --- interpret preamble --- */
9336: for ( preptr=pream; ; preptr++ ) /* examine each preamble char */
9337: if ( *preptr == '\000' ) break; /* end-of-preamble signalled */
9338: else switch ( tolower(*preptr) ) /* check lowercase preamble char */
9339: {
9340: default: break; /* unrecognized flag */
9341: case 'c': iscenter=1; break; /* center pic_elem at x,y coords */
9342: } /* --- end-of-switch --- */
9343: /* --- interpret x,y;xinc,yinc;num following preamble --- */
9344: if ( *putptr != '\000' ) /*check for put data after preamble*/
9345: {
9346: /* --- first squeeze preamble out of put expression --- */
9347: if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
9348: /* --- interpret x,y --- */
9349: if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
9350: *multptr = '\000'; /* replace semicolon by '\0' */
9351: if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
9352: *putptr = '\000'; /* replace comma by '\0' */
9353: if ( *putexpr != '\000' ) /* leading , may be placeholder */
9354: x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
9355: if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
9356: y = unitlength*strtod(putptr+1,NULL); /* in pixels */
9357: /* --- interpret xinc,yinc,num if we have a multiput --- */
9358: if ( multptr != NULL ) /* found ';' signalling multiput */
9359: {
9360: if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
9361: *preptr = '\000'; /* replace ';' by '\0' */
9362: if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
9363: *putptr = '\000'; /* replace ',' by '\0' */
9364: if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
9365: xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
9366: if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
9367: yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
9368: num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
9369: } /* --- end-of-if(multptr!=NULL) --- */
9370: } /* --- end-of-if(*preptr!='\000') --- */
9371: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9372: fprintf(msgfp,
9373: "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
9374: pream,x,y,xinc,yinc,num);
9375: /* -----------------------------------------------------------------------
9376: now obtain {...} picture element following [multi]put, and rasterize it
9377: ------------------------------------------------------------------------ */
9378: /* --- parse for {...} picture element and bump picptr past it --- */
9379: picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
9380: if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
9381: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9382: fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
9383: /* --- rasterize picture element --- */
9384: origin = 0; /* init origin as working param */
9385: workingparam = &origin; /* and point working param to it */
9386: picelemsp = rasterize(picelem,size); /* rasterize picture element */
9387: if ( picelemsp == NULL ) continue; /* failed to rasterize, skip elem */
9388: ewidth = (picelemsp->image)->width; /* width of element, in pixels */
9389: eheight = (picelemsp->image)->height; /* height of element, in pixels */
9390: if ( origin == 55 ) iscenter = 1; /* origin set to (.5,.5) for center*/
9391: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9392: { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
9393: ewidth,eheight,origin,num);
9394: if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
9395: /* -----------------------------------------------------------------------
9396: embed element in picture (once, or multiple times if requested)
9397: ------------------------------------------------------------------------ */
9398: for ( inum=0; inum<num; inum++ ) /* once, or for num repetitions */
9399: {
9400: /* --- set x,y-coords for this iteration --- */
9401: ix = iround(x); iy = iround(y); /* round x,y to nearest integer */
9402: if ( iscenter ) /* place center of element at x,y */
9403: { xpos = ix - ewidth/2; /* x picture coord to center elem */
9404: ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
9405: else /* default places lower-left at x,y*/
9406: { xpos = ix; /* set x pixel coord for left */
9407: if ( origin==10 || origin==11 ) /* x,yinc's are -+ or -- */
9408: xpos = ix - ewidth; /* so set for right instead */
9409: ypos = height - iy - eheight; /* set y pixel coord for lower */
9410: if ( origin==1 || origin==11 ) /* x,yinc's are +- or -- */
9411: ypos = height - iy; } /* so set for upper instead */
9412: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9413: fprintf(msgfp,
9414: "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
9415: inum,x,y,ix,iy,xpos,ypos);
9416: /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
9417: if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
9418: /* --- apply increment --- */
9419: if ( xinc==0. && yinc==0. ) break; /* quit if both increments zero */
9420: x += xinc; y += yinc; /* increment coords for next iter */
9421: } /* --- end-of-for(inum) --- */
9422: /* --- free picture element subraster after embedding it in picture --- */
9423: delete_subraster(picelemsp); /* done with subraster, so free it */
9424: } /* --- end-of-while(*picptr!=0) --- */
9425: /* -------------------------------------------------------------------------
9426: return picture constructed from pic_elements to caller
9427: -------------------------------------------------------------------------- */
9428: end_of_job:
9429: workingbox = oldworkingbox; /* restore original working box */
9430: workingparam = oldworkingparam; /* restore original working param */
9431: return ( picturesp ); /* return our picture to caller */
9432: } /* --- end-of-function rastpicture() --- */
9433:
9434:
9435: /* ==========================================================================
9436: * Function: rastline ( expression, size, basesp, arg1, arg2, arg3 )
9437: * Purpose: \line handler, returns subraster corresponding to line
9438: * parameters (xinc,yinc){xlen}
9439: * --------------------------------------------------------------------------
9440: * Arguments: expression (I/O) char ** to first char of null-terminated
9441: * string immediately following \line to be
9442: * rasterized, and returning ptr immediately
9443: * following last character processed.
1.3 ! albertel 9444: * size (I) int containing 0-7 default font size
1.1 albertel 9445: * basesp (I) subraster * to character (or subexpression)
9446: * immediately preceding \line
9447: * (unused, but passed for consistency)
9448: * arg1 (I) int unused
9449: * arg2 (I) int unused
9450: * arg3 (I) int unused
9451: * --------------------------------------------------------------------------
9452: * Returns: ( subraster * ) ptr to subraster corresponding to line
9453: * requested, or NULL for any parsing error
9454: * --------------------------------------------------------------------------
9455: * Notes: o Summary of syntax...
9456: * \line(xinc,yinc){xlen}
9457: * o if {xlen} not given, then it's assumed xlen = |xinc|
9458: * ======================================================================= */
9459: /* --- entry point --- */
9460: subraster *rastline ( char **expression, int size, subraster *basesp,
9461: int arg1, int arg2, int arg3 )
9462: {
9463: /* -------------------------------------------------------------------------
9464: Allocations and Declarations
9465: -------------------------------------------------------------------------- */
9466: char *texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
9467: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
9468: /*char *origexpression = *expression;*/ /*original expression after \line*/
9469: int pixsz = 1; /* pixels are one bit each */
1.2 albertel 9470: int thickness = 1; /* line thickness */
1.1 albertel 9471: double strtod(), /* convert ascii params to doubles */
9472: xinc=0.0, yinc=0.0, /* x,y-increments for line, */
9473: xlen=0.0, ylen=0.0; /* x,y lengths for line */
1.2 albertel 9474: int width=0, height=0, /* #pixels width,height of line */
9475: rwidth=0, rheight=0; /*alloc width,height plus thickness*/
1.1 albertel 9476: int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
9477: origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
9478: int line_raster(); /* draw line in linesp->image */
9479: /* -------------------------------------------------------------------------
9480: obtain (xinc,yinc) arguments immediately following \line command
9481: -------------------------------------------------------------------------- */
9482: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
9483: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
9484: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
1.2 albertel 9485: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
9486: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
9487: { *xptr = '\000'; /* terminate linexpr at ; */
9488: thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */
1.1 albertel 9489: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
9490: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
9491: if ( *linexpr != '\000' ) /* check against missing 1st arg */
9492: xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */
9493: if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
9494: yinc = ylen = strtod(xptr+1,NULL); /* in user units */
9495: /* -------------------------------------------------------------------------
9496: obtain optional {xlen} following (xinc,yinc), and calculate ylen
9497: -------------------------------------------------------------------------- */
9498: /* --- check if {xlen} given --- */
9499: if ( *(*expression) == '{' ) /*have {xlen} if leading char is { */
9500: {
9501: /* --- parse {xlen} and bump expression past it, interpret as double --- */
9502: *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
9503: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
9504: xlen = strtod(linexpr,NULL); /* xlen in user units */
9505: /* --- set other values accordingly --- */
9506: if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
9507: if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
9508: else xlen = 0.0; /* can't have xlen if xinc=0 */
9509: } /* --- end-of-if(*(*expression)=='{') --- */
9510: /* -------------------------------------------------------------------------
9511: calculate width,height, etc, based on xlen,ylen, etc
9512: -------------------------------------------------------------------------- */
9513: /* --- force lengths positive --- */
9514: xlen = absval(xlen); /* force xlen positive */
9515: ylen = absval(ylen); /* force ylen positive */
9516: /* --- calculate corresponding lengths in pixels --- */
1.2 albertel 9517: width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
9518: height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
9519: rwidth = width + (ylen<0.001?0:max2(0,thickness-1));
9520: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
1.1 albertel 9521: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
9522: if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
9523: if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
9524: origin = isright*10 + istop; /* interpret 0=(0,0), 11=(1,1), etc*/
9525: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9526: fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
9527: width,height,origin,xinc,yinc);
9528: /* -------------------------------------------------------------------------
1.3 ! albertel 9529: allocate subraster and raster for line
1.1 albertel 9530: -------------------------------------------------------------------------- */
1.2 albertel 9531: /* --- sanity check on width,height,thickness args --- */
1.1 albertel 9532: if ( width < 1 || width > 600
1.2 albertel 9533: || height < 1 || height > 600
9534: || thickness<1||thickness>25 ) goto end_of_job;
1.1 albertel 9535: /* --- allocate and initialize subraster for constructed line --- */
1.2 albertel 9536: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
1.1 albertel 9537: == NULL ) goto end_of_job; /* quit if failed */
9538: /* --- initialize line subraster parameters --- */
9539: linesp->type = IMAGERASTER; /* image */
9540: linesp->symdef = NULL; /* not applicable for image */
1.2 albertel 9541: linesp->baseline = height/2 + 2 /* is a little above center good? */
9542: + (rheight-height)/2; /* account for line thickness too */
1.1 albertel 9543: linesp->size = size; /* size (probably unneeded) */
9544: /* -------------------------------------------------------------------------
9545: draw the line
9546: -------------------------------------------------------------------------- */
9547: line_raster ( linesp->image, /* embedded raster image */
9548: (istop? 0 : height-1), /* row0, from bottom or top */
9549: (isright? width-1 : 0), /* col0, from left or right */
9550: (istop? height-1 : 0), /* row1, to top or bottom */
9551: (isright? 0 : width-1), /* col1, to right or left */
1.2 albertel 9552: thickness ); /* line thickness (usually 1 pixel)*/
1.1 albertel 9553: /* -------------------------------------------------------------------------
9554: return constructed line to caller
9555: -------------------------------------------------------------------------- */
9556: end_of_job:
9557: if ( workingparam != NULL ) /* caller wants origin */
9558: *workingparam = origin; /* return origin corner to caller */
9559: return ( linesp ); /* return line to caller */
9560: } /* --- end-of-function rastline() --- */
9561:
9562:
9563: /* ==========================================================================
1.3 ! albertel 9564: * Function: rastrule ( expression, size, basesp, arg1, arg2, arg3 )
! 9565: * Purpose: \rule handler, returns subraster corresponding to rule
! 9566: * parameters [lift]{width}{height}
! 9567: * --------------------------------------------------------------------------
! 9568: * Arguments: expression (I/O) char ** to first char of null-terminated
! 9569: * string immediately following \rule to be
! 9570: * rasterized, and returning ptr immediately
! 9571: * following last character processed.
! 9572: * size (I) int containing 0-7 default font size
! 9573: * basesp (I) subraster * to character (or subexpression)
! 9574: * immediately preceding \rule
! 9575: * (unused, but passed for consistency)
! 9576: * arg1 (I) int unused
! 9577: * arg2 (I) int unused
! 9578: * arg3 (I) int unused
! 9579: * --------------------------------------------------------------------------
! 9580: * Returns: ( subraster * ) ptr to subraster corresponding to rule
! 9581: * requested, or NULL for any parsing error
! 9582: * --------------------------------------------------------------------------
! 9583: * Notes: o Summary of syntax...
! 9584: * \rule[lift]{width}{height}
! 9585: * o if [lift] not given, then bottom of rule on baseline
! 9586: * o if width=0 then you get an invisible strut 1 (one) pixel wide
! 9587: * ======================================================================= */
! 9588: /* --- entry point --- */
! 9589: subraster *rastrule ( char **expression, int size, subraster *basesp,
! 9590: int arg1, int arg2, int arg3 )
! 9591: {
! 9592: /* -------------------------------------------------------------------------
! 9593: Allocations and Declarations
! 9594: -------------------------------------------------------------------------- */
! 9595: char *texsubexpr(), rulexpr[257]; /* rule[lift]{wdth}{hgt} */
! 9596: subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
! 9597: int pixsz = 1; /* pixels are one bit each */
! 9598: int lift=0, width=0, height=0; /* default rule parameters */
! 9599: double strtod(), dval; /* convert ascii params to doubles */
! 9600: int rwidth=0, rheight=0; /* alloc width, height plus lift */
! 9601: int rule_raster(); /* draw rule in rulesp->image */
! 9602: /* -------------------------------------------------------------------------
! 9603: Obtain lift,width,height
! 9604: -------------------------------------------------------------------------- */
! 9605: /* --- check for optional lift arg --- */
! 9606: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
! 9607: { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
! 9608: dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert [lift] to int */
! 9609: if ( dval <= 99 && dval >= (-99) ) /* sanity check */
! 9610: lift = iround(unitlength*dval); } /* scale by unitlength and round */
! 9611: /* --- parse for width --- */
! 9612: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
! 9613: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
! 9614: dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {width} to int */
! 9615: if ( dval <= 500 && dval >= 0 ) /* sanity check */
! 9616: width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
! 9617: /* --- parse for height --- */
! 9618: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
! 9619: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
! 9620: dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {height} to int */
! 9621: if ( dval <= 500 && dval > 0 ) /* sanity check */
! 9622: height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
! 9623: /* --- raster width,height in pixels --- */
! 9624: rwidth = max2(1,width); /* raster must be at least 1 pixel*/
! 9625: rheight = height + (lift>=0?lift: /* raster height plus lift */
! 9626: (-lift<height?0:-lift-height+1)); /* may need empty space above rule */
! 9627: /* -------------------------------------------------------------------------
! 9628: allocate subraster and raster for rule
! 9629: -------------------------------------------------------------------------- */
! 9630: /* --- sanity check on width,height,thickness args --- */
! 9631: if ( rwidth < 1 || rwidth > 600
! 9632: || rheight < 1 || rheight > 600 ) goto end_of_job;
! 9633: /* --- allocate and initialize subraster for constructed rule --- */
! 9634: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
! 9635: == NULL ) goto end_of_job; /* quit if failed */
! 9636: /* --- initialize line subraster parameters --- */
! 9637: rulesp->type = IMAGERASTER; /* image */
! 9638: rulesp->symdef = NULL; /* not applicable for image */
! 9639: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
! 9640: rulesp->size = size; /* size (probably unneeded) */
! 9641: /* -------------------------------------------------------------------------
! 9642: draw the rule
! 9643: -------------------------------------------------------------------------- */
! 9644: rule_raster ( rulesp->image, /* embedded raster image */
! 9645: (-lift<height?0:rheight-height), /* topmost row for top-left corner*/
! 9646: 0, /* leftmost col for top-left corner*/
! 9647: width, /* rule width */
! 9648: height, /* rule height */
! 9649: ( width>0? 0:4 ) ); /* rule type */
! 9650: /* -------------------------------------------------------------------------
! 9651: return constructed rule to caller
! 9652: -------------------------------------------------------------------------- */
! 9653: end_of_job:
! 9654: return ( rulesp ); /* return rule to caller */
! 9655: } /* --- end-of-function rastrule() --- */
! 9656:
! 9657:
! 9658: /* ==========================================================================
1.1 albertel 9659: * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
9660: * Purpose: \circle handler, returns subraster corresponding to ellipse
9661: * parameters (xdiam[,ydiam])
9662: * --------------------------------------------------------------------------
9663: * Arguments: expression (I/O) char ** to first char of null-terminated
9664: * string immediately following \circle to be
9665: * rasterized, and returning ptr immediately
9666: * following last character processed.
9667: * size (I) int containing 0-4 default font size
9668: * basesp (I) subraster * to character (or subexpression)
9669: * immediately preceding \circle
9670: * (unused, but passed for consistency)
9671: * arg1 (I) int unused
9672: * arg2 (I) int unused
9673: * arg3 (I) int unused
9674: * --------------------------------------------------------------------------
9675: * Returns: ( subraster * ) ptr to subraster corresponding to ellipse
9676: * requested, or NULL for any parsing error
9677: * --------------------------------------------------------------------------
9678: * Notes: o Summary of syntax...
9679: * \circle(xdiam[,ydiam])
9680: * o
9681: * ======================================================================= */
9682: /* --- entry point --- */
9683: subraster *rastcircle ( char **expression, int size, subraster *basesp,
9684: int arg1, int arg2, int arg3 )
9685: {
9686: /* -------------------------------------------------------------------------
9687: Allocations and Declarations
9688: -------------------------------------------------------------------------- */
9689: char *texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
9690: char *qptr=NULL, quads[256]="1234"; /* default to draw all quadrants */
9691: double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/
9692: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
9693: int pixsz = 1; /* pixels are one bit each */
9694: double strtod(), /* convert ascii params to doubles */
9695: xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
9696: int width=0, height=0; /* #pixels width,height of ellipse */
9697: int thickness = 1; /* drawn lines are one pixel thick */
9698: int origin = 55; /* force origin centered */
9699: int circle_raster(), /* draw ellipse in circsp->image */
9700: circle_recurse(); /* for theta0,theta1 args */
9701: /* -------------------------------------------------------------------------
9702: obtain (xdiam[,ydiam]) arguments immediately following \circle command
9703: -------------------------------------------------------------------------- */
9704: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
9705: *expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);
9706: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
9707: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
9708: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
9709: { *qptr = '\000'; /* replace semicolon by '\0' */
9710: strcpy(quads,qptr+1); /* save user-requested quads */
9711: if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
9712: { *qptr = '\000'; /* replace , with null */
9713: theta0 = strtod(quads,NULL); /* theta0 precedes , */
9714: theta1 = strtod(qptr+1,NULL); /* theta1 follows , */
9715: qptr = NULL; } /* signal thetas instead of quads */
9716: else
9717: qptr = quads; } /* set qptr arg for circle_raster()*/
9718: else /* no ;quads at all */
9719: qptr = quads; /* default to all 4 quadrants */
9720: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
9721: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
9722: xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */
9723: if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
9724: ydiam = strtod(xptr+1,NULL); /* in user units */
9725: /* -------------------------------------------------------------------------
9726: calculate width,height, etc
9727: -------------------------------------------------------------------------- */
9728: /* --- calculate width,height in pixels --- */
9729: width = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
9730: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
9731: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9732: fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
9733: width,height,(qptr==NULL?"default":qptr));
9734: /* -------------------------------------------------------------------------
9735: allocate subraster and raster for complete picture
9736: -------------------------------------------------------------------------- */
9737: /* --- sanity check on width,height args --- */
9738: if ( width < 1 || width > 600
9739: || height < 1 || height > 600 ) goto end_of_job;
9740: /* --- allocate and initialize subraster for constructed ellipse --- */
9741: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
9742: == NULL ) goto end_of_job; /* quit if failed */
9743: /* --- initialize ellipse subraster parameters --- */
9744: circsp->type = IMAGERASTER; /* image */
9745: circsp->symdef = NULL; /* not applicable for image */
9746: circsp->baseline = height/2 + 2; /* is a little above center good? */
9747: circsp->size = size; /* size (probably unneeded) */
9748: /* -------------------------------------------------------------------------
9749: draw the ellipse
9750: -------------------------------------------------------------------------- */
9751: if ( qptr != NULL ) /* have quads */
9752: circle_raster ( circsp->image, /* embedded raster image */
9753: 0, 0, /* row0,col0 are upper-left corner */
9754: height-1, width-1, /* row1,col1 are lower-right */
9755: thickness, /* line thickness is 1 pixel */
9756: qptr ); /* "1234" quadrants to be drawn */
9757: else /* have theta0,theta1 */
9758: circle_recurse ( circsp->image, /* embedded raster image */
9759: 0, 0, /* row0,col0 are upper-left corner */
9760: height-1, width-1, /* row1,col1 are lower-right */
9761: thickness, /* line thickness is 1 pixel */
9762: theta0,theta1 ); /* theta0,theta1 arc to be drawn */
9763: /* -------------------------------------------------------------------------
9764: return constructed ellipse to caller
9765: -------------------------------------------------------------------------- */
9766: end_of_job:
9767: if ( workingparam != NULL ) /* caller wants origin */
9768: *workingparam = origin; /* return center origin to caller */
9769: return ( circsp ); /* return ellipse to caller */
9770: } /* --- end-of-function rastcircle() --- */
9771:
9772:
9773: /* ==========================================================================
9774: * Function: rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
9775: * Purpose: \bezier handler, returns subraster corresponding to bezier
9776: * parameters (col0,row0)(col1,row1)(colt,rowt)
9777: * --------------------------------------------------------------------------
9778: * Arguments: expression (I/O) char ** to first char of null-terminated
9779: * string immediately following \bezier to be
9780: * rasterized, and returning ptr immediately
9781: * following last character processed.
9782: * size (I) int containing 0-5 default font size
9783: * basesp (I) subraster * to character (or subexpression)
9784: * immediately preceding \bezier
9785: * (unused, but passed for consistency)
9786: * arg1 (I) int unused
9787: * arg2 (I) int unused
9788: * arg3 (I) int unused
9789: * --------------------------------------------------------------------------
9790: * Returns: ( subraster * ) ptr to subraster corresponding to bezier
9791: * requested, or NULL for any parsing error
9792: * --------------------------------------------------------------------------
9793: * Notes: o Summary of syntax...
9794: * \bezier(col1,row1)(colt,rowt)
9795: * o col0=0,row0=0 assumed, i.e., given by
9796: * \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
9797: * ======================================================================= */
9798: /* --- entry point --- */
9799: subraster *rastbezier ( char **expression, int size, subraster *basesp,
9800: int arg1, int arg2, int arg3 )
9801: {
9802: /* -------------------------------------------------------------------------
9803: Allocations and Declarations
9804: -------------------------------------------------------------------------- */
9805: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
9806: char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
9807: double strtod(); /* convert ascii params to doubles */
9808: double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
9809: rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
9810: rmin=0.0, cmin=0.0, /* minimum r,c */
9811: rmax=0.0, cmax=0.0, /* maximum r,c */
9812: rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
9813: r=0.0, c=0.0; /* some point */
9814: int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
9815: int width=0, height=0; /* dimensions of bezier raster */
9816: int pixsz = 1; /* pixels are one bit each */
9817: /*int thickness = 1;*/ /* drawn lines are one pixel thick */
9818: int origin = 0; /*c's,r's reset to lower-left origin*/
9819: int bezier_raster(); /* draw bezier in bezsp->image */
9820: /* -------------------------------------------------------------------------
9821: obtain (c1,r1)(ct,rt) args immediately following \bezier command
9822: -------------------------------------------------------------------------- */
9823: for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0,r0 1=c1,r1 2=ct,rt */
9824: {
9825: /* --- parse for (r,c) args, and bump expression past them all --- */
9826: *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
9827: if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
9828: /* --- now interpret (r,c) returned in bezexpr --- */
9829: c = r = 0.0; /* init x-coord=col, y-coord=row */
9830: if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
9831: { *xptr = '\000'; /* found it, so replace ',' by '\0'*/
9832: r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
9833: c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */
9834: /* --- store r,c --- */
9835: switch ( iarg )
9836: { case 0: r0=r; c0=c; break;
9837: case 1: r1=r; c1=c; break;
9838: case 2: rt=r; ct=c; break; }
9839: } /* --- end-of-for(iarg) --- */
9840: /* --- determine midpoint and maximum,minimum points --- */
9841: rmid = 0.5*(rt + 0.5*(r0+r1)); /* y-coord at middle of bezier */
9842: cmid = 0.5*(ct + 0.5*(c0+c1)); /* x-coord at middle of bezier */
9843: rmin = min3(r0,r1,rmid); /* lowest row */
9844: cmin = min3(c0,c1,cmid); /* leftmost col */
9845: rmax = max3(r0,r1,rmid); /* highest row */
9846: cmax = max3(c0,c1,cmid); /* rightmost col */
9847: rdelta = rmax-rmin; /* height */
9848: cdelta = cmax-cmin; /* width */
9849: /* --- rescale coords so we start at 0,0 --- */
9850: r0 -= rmin; c0 -= cmin; /* rescale r0,c0 */
9851: r1 -= rmin; c1 -= cmin; /* rescale r1,c1 */
9852: rt -= rmin; ct -= cmin; /* rescale rt,ct */
9853: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
9854: r0 = rdelta - r0 + 1; /* map 0-->height-1, height-1-->0 */
9855: r1 = rdelta - r1 + 1;
9856: rt = rdelta - rt + 1;
9857: /* --- determine width,height of raster needed for bezier --- */
9858: width = (int)(cdelta + 0.9999) + 1; /* round width up */
9859: height = (int)(rdelta + 0.9999) + 1; /* round height up */
9860: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
9861: fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
9862: "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
9863: width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
9864: /* -------------------------------------------------------------------------
9865: allocate raster
9866: -------------------------------------------------------------------------- */
9867: /* --- sanity check on width,height args --- */
9868: if ( width < 1 || width > 600
9869: || height < 1 || height > 600 ) goto end_of_job;
9870: /* --- allocate and initialize subraster for constructed bezier --- */
9871: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
9872: == NULL ) goto end_of_job; /* quit if failed */
9873: /* --- initialize bezier subraster parameters --- */
9874: bezsp->type = IMAGERASTER; /* image */
9875: bezsp->symdef = NULL; /* not applicable for image */
9876: bezsp->baseline = height/2 + 2; /* is a little above center good? */
9877: bezsp->size = size; /* size (probably unneeded) */
9878: /* -------------------------------------------------------------------------
9879: draw the bezier
9880: -------------------------------------------------------------------------- */
9881: bezier_raster ( bezsp->image, /* embedded raster image */
9882: r0, c0, /* row0,col0 are lower-left corner */
9883: r1, c1, /* row1,col1 are upper-right */
9884: rt, ct ); /* bezier tangent point */
9885: /* -------------------------------------------------------------------------
9886: return constructed bezier to caller
9887: -------------------------------------------------------------------------- */
9888: end_of_job:
9889: if ( workingparam != NULL ) /* caller wants origin */
9890: *workingparam = origin; /* return center origin to caller */
9891: return ( bezsp ); /* return bezier to caller */
9892: } /* --- end-of-function rastbezier() --- */
9893:
9894:
9895: /* ==========================================================================
9896: * Function: rastraise ( expression, size, basesp, arg1, arg2, arg3 )
9897: * Purpose: \raisebox{lift}{subexpression} handler, returns subraster
9898: * containing subexpression with its baseline "lifted" by lift
9899: * pixels, scaled by \unitlength, or "lowered" if lift arg
9900: * negative
9901: * --------------------------------------------------------------------------
9902: * Arguments: expression (I/O) char ** to first char of null-terminated
9903: * string immediately following \raisebox to be
9904: * rasterized, and returning ptr immediately
9905: * following last character processed.
1.3 ! albertel 9906: * size (I) int containing 0-7 default font size
1.1 albertel 9907: * basesp (I) subraster * to character (or subexpression)
1.3 ! albertel 9908: * immediately preceding \raisebox
1.1 albertel 9909: * (unused, but passed for consistency)
9910: * arg1 (I) int unused
9911: * arg2 (I) int unused
9912: * arg3 (I) int unused
9913: * --------------------------------------------------------------------------
9914: * Returns: ( subraster * ) ptr to subraster corresponding to \raisebox
9915: * requested, or NULL for any parsing error
9916: * --------------------------------------------------------------------------
9917: * Notes: o Summary of syntax...
9918: * \raisebox{lift}{subexpression}
9919: * o
9920: * ======================================================================= */
9921: /* --- entry point --- */
9922: subraster *rastraise ( char **expression, int size, subraster *basesp,
9923: int arg1, int arg2, int arg3 )
9924: {
9925: /* -------------------------------------------------------------------------
9926: Allocations and Declarations
9927: -------------------------------------------------------------------------- */
1.3 ! albertel 9928: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
1.1 albertel 9929: subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
9930: int lift=0; /* amount to raise/lower baseline */
9931: /* -------------------------------------------------------------------------
9932: obtain {lift} argument immediately following \raisebox command
9933: -------------------------------------------------------------------------- */
9934: /* --- parse for {lift} arg, and bump expression past it --- */
9935: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
9936: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
9937: lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/
9938: if ( abs(lift) > 200 ) lift=0; /* sanity check */
9939: /* -------------------------------------------------------------------------
9940: obtain {subexpr} argument after {lift}, and rasterize it
9941: -------------------------------------------------------------------------- */
9942: /* --- parse for {subexpr} arg, and bump expression past it --- */
9943: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9944: /* --- rasterize subexpression to be raised/lowered --- */
9945: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
9946: == NULL ) goto end_of_job; /* and quit if failed */
9947: /* -------------------------------------------------------------------------
9948: raise/lower baseline and return it to caller
9949: -------------------------------------------------------------------------- */
9950: /* --- raise/lower baseline --- */
9951: raisesp->baseline += lift; /* new baseline (no height checks) */
9952: /* --- return raised subexpr to caller --- */
9953: end_of_job:
9954: return ( raisesp ); /* return raised subexpr to caller */
9955: } /* --- end-of-function rastraise() --- */
9956:
9957:
9958: /* ==========================================================================
9959: * Function: rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
9960: * Purpose: \rotatebox{degrees}{subexpression} handler, returns subraster
9961: * containing subexpression rotated by degrees (counterclockwise
9962: * if degrees positive)
9963: * --------------------------------------------------------------------------
9964: * Arguments: expression (I/O) char ** to first char of null-terminated
9965: * string immediately following \rotatebox to be
9966: * rasterized, and returning ptr immediately
9967: * following last character processed.
1.3 ! albertel 9968: * size (I) int containing 0-7 default font size
1.1 albertel 9969: * basesp (I) subraster * to character (or subexpression)
9970: * immediately preceding \rotatebox
9971: * (unused, but passed for consistency)
9972: * arg1 (I) int unused
9973: * arg2 (I) int unused
9974: * arg3 (I) int unused
9975: * --------------------------------------------------------------------------
9976: * Returns: ( subraster * ) ptr to subraster corresponding to \rotatebox
9977: * requested, or NULL for any parsing error
9978: * --------------------------------------------------------------------------
9979: * Notes: o Summary of syntax...
9980: * \rotatebox{degrees}{subexpression}
9981: * o
9982: * ======================================================================= */
9983: /* --- entry point --- */
9984: subraster *rastrotate ( char **expression, int size, subraster *basesp,
9985: int arg1, int arg2, int arg3 )
9986: {
9987: /* -------------------------------------------------------------------------
9988: Allocations and Declarations
9989: -------------------------------------------------------------------------- */
1.3 ! albertel 9990: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
1.1 albertel 9991: subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */
9992: raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
9993: int delete_raster(); /* delete intermediate rasters */
9994: int baseline=0; /* baseline of rasterized image */
9995: double strtod(), /* convert ascii params to doubles */
9996: degrees=0.0, ipart,fpart; /* degrees to be rotated */
9997: int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
9998: int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
9999: /* -------------------------------------------------------------------------
10000: obtain {degrees} argument immediately following \rotatebox command
10001: -------------------------------------------------------------------------- */
10002: /* --- parse for {degrees} arg, and bump expression past it --- */
10003: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
10004: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
10005: degrees = strtod(degexpr,NULL); /* degrees to be rotated */
10006: if ( degrees < 0.0 ) /* clockwise rotation desired */
10007: { degrees = -degrees; /* flip sign so degrees positive */
10008: isneg = 1; } /* and set flag to indicate flip */
10009: fpart = modf(degrees,&ipart); /* integer and fractional parts */
10010: ipart = (double)(((int)degrees)%360); /* degrees mod 360 */
10011: degrees = ipart + fpart; /* restore fractional part */
10012: if ( isneg ) /* if clockwise rotation requested */
10013: degrees = 360.0 - degrees; /* do equivalent counterclockwise */
10014: idegrees = (int)(degrees+0.5); /* integer degrees */
10015: n90 = idegrees/90; /* degrees is n90 multiples of 90 */
10016: isn90 = (90*n90==idegrees); /*true if degrees is multiple of 90*/
10017: isn90 = 1; /* forced true for time being */
10018: /* -------------------------------------------------------------------------
10019: obtain {subexpr} argument after {degrees}, and rasterize it
10020: -------------------------------------------------------------------------- */
10021: /* --- parse for {subexpr} arg, and bump expression past it --- */
10022: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10023: /* --- rasterize subexpression to be rotated --- */
10024: if ( (rotsp = rasterize(subexpr,size)) /* rasterize subexpression */
10025: == NULL ) goto end_of_job; /* and quit if failed */
10026: /* --- return unmodified image if no rotation requested --- */
10027: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
10028: /* --- extract params for image to be rotated --- */
10029: rotrp = rotsp->image; /* unrotated rasterized image */
10030: baseline = rotsp->baseline; /* and baseline of that image */
10031: /* -------------------------------------------------------------------------
10032: rotate by multiples of 90 degrees
10033: -------------------------------------------------------------------------- */
10034: if ( isn90 ) /* rotation by multiples of 90 */
10035: if ( n90 > 0 ) /* do nothing for 0 degrees */
10036: {
10037: n90 = 4-n90; /* rasrot() rotates clockwise */
10038: while ( n90 > 0 ) /* still have remaining rotations */
10039: { raster *nextrp = rastrot(rotrp); /* rotate raster image */
10040: if ( nextrp == NULL ) break; /* something's terribly wrong */
10041: delete_raster(rotrp); /* free previous raster image */
10042: rotrp = nextrp; /* and replace it with rotated one */
10043: n90--; } /* decrement remaining count */
10044: } /* --- end-of-if(isn90) --- */
10045: /* -------------------------------------------------------------------------
10046: requested rotation not multiple of 90 degrees
10047: -------------------------------------------------------------------------- */
10048: if ( !isn90 ) /* explicitly construct rotation */
10049: { ; } /* not yet implemented */
10050: /* -------------------------------------------------------------------------
10051: re-populate subraster envelope with rotated image
10052: -------------------------------------------------------------------------- */
10053: /* --- re-init various subraster parameters, embedding raster in it --- */
10054: if ( rotrp != NULL ) /* rotated raster constructed okay */
10055: { rotsp->type = IMAGERASTER; /* signal constructed image */
10056: rotsp->image = rotrp; /* raster we just constructed */
10057: /* --- now try to guess pleasing baseline --- */
1.3 ! albertel 10058: if ( idegrees > 2 ) { /* leave unchanged if unrotated */
1.1 albertel 10059: if ( strlen(subexpr) < 3 /* we rotated a short expression */
10060: || abs(idegrees-180) < 3 ) /* or just turned it upside-down */
10061: baseline = rotrp->height - 1; /* so set with nothing descending */
10062: else /* rotated a long expression */
1.3 ! albertel 10063: baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
1.1 albertel 10064: rotsp->baseline = baseline; } /* set baseline as calculated above*/
10065: /* --- return rotated subexpr to caller --- */
10066: end_of_job:
10067: return ( rotsp ); /*return rotated subexpr to caller*/
10068: } /* --- end-of-function rastrotate() --- */
10069:
10070:
10071: /* ==========================================================================
1.3 ! albertel 10072: * Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
! 10073: * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster
! 10074: * containing subexpression reflected horizontally (i.e., around
! 10075: * vertical axis, |_ becomes _|) if [axis] not given or axis=1,
! 10076: * or reflected vertically if axis=2 given.
! 10077: * --------------------------------------------------------------------------
! 10078: * Arguments: expression (I/O) char ** to first char of null-terminated
! 10079: * string immediately following \reflectbox to
! 10080: * be rasterized, and returning ptr immediately
! 10081: * following last character processed.
! 10082: * size (I) int containing 0-7 default font size
! 10083: * basesp (I) subraster * to character (or subexpression)
! 10084: * immediately preceding \reflectbox
! 10085: * (unused, but passed for consistency)
! 10086: * arg1 (I) int unused
! 10087: * arg2 (I) int unused
! 10088: * arg3 (I) int unused
! 10089: * --------------------------------------------------------------------------
! 10090: * Returns: ( subraster * ) ptr to subraster corresponding to \reflectbox
! 10091: * requested, or NULL for any parsing error
! 10092: * --------------------------------------------------------------------------
! 10093: * Notes: o Summary of syntax...
! 10094: * \reflectbox[axis]{subexpression}
! 10095: * o
! 10096: * ======================================================================= */
! 10097: /* --- entry point --- */
! 10098: subraster *rastreflect ( char **expression, int size, subraster *basesp,
! 10099: int arg1, int arg2, int arg3 )
! 10100: {
! 10101: /* -------------------------------------------------------------------------
! 10102: Allocations and Declarations
! 10103: -------------------------------------------------------------------------- */
! 10104: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
! 10105: subraster *rasterize(), *refsp=NULL; /* subraster for reflected subexpr */
! 10106: raster *rastref(), *refrp=NULL; /* reflect subraster->image */
! 10107: int axis = 1; /* default horizontal reflection */
! 10108: int delete_raster(); /* delete intermediate raster */
! 10109: int baseline=0; /* baseline of rasterized image */
! 10110: /* -------------------------------------------------------------------------
! 10111: obtain [axis] argument immediately following \reflectbox command, if given
! 10112: -------------------------------------------------------------------------- */
! 10113: /* --- check for optional [axis] arg --- */
! 10114: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
! 10115: { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
! 10116: axis = atoi(axisexpr); /* convert [axis] to int */
! 10117: if ( axis<1 || axis>2 ) /* check axis input */
! 10118: axis = 1; } /* back to default if illegal */
! 10119: /* -------------------------------------------------------------------------
! 10120: obtain {subexpr} argument after optional [axis], and rasterize it
! 10121: -------------------------------------------------------------------------- */
! 10122: /* --- parse for {subexpr} arg, and bump expression past it --- */
! 10123: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
! 10124: /* --- rasterize subexpression to be reflected --- */
! 10125: if ( (refsp = rasterize(subexpr,size)) /* rasterize subexpression */
! 10126: == NULL ) goto end_of_job; /* and quit if failed */
! 10127: /* --- return unmodified image if no reflection requested --- */
! 10128: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
! 10129: /* --- extract params for image to be reflected --- */
! 10130: refrp = refsp->image; /* unreflected rasterized image */
! 10131: baseline = refsp->baseline; /* and baseline of that image */
! 10132: /* -------------------------------------------------------------------------
! 10133: reflect image and adjust its parameters
! 10134: -------------------------------------------------------------------------- */
! 10135: /* --- reflect image --- */
! 10136: refrp = rastref(refsp->image,axis); /* reflect raster image */
! 10137: if ( refrp == NULL ) goto end_of_job; /* failed to reflect image */
! 10138: delete_raster(refsp->image); /* free original raster image */
! 10139: refsp->image = refrp; /*and replace it with reflected one*/
! 10140: /* --- adjust parameters --- */
! 10141: if ( axis == 2 ) /* for vertical reflection */
! 10142: baseline = refrp->height - 1; /* set with nothing descending */
! 10143: refsp->baseline = baseline; /* reset baseline of reflected image*/
! 10144: /* --- return reflected subexpr to caller --- */
! 10145: end_of_job:
! 10146: return ( refsp ); /*back to caller with reflected expr*/
! 10147: } /* --- end-of-function rastreflect() --- */
! 10148:
! 10149:
! 10150: /* ==========================================================================
1.1 albertel 10151: * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
10152: * Purpose: \fbox{subexpression} handler, returns subraster
10153: * containing subexpression with frame box drawn around it
10154: * --------------------------------------------------------------------------
10155: * Arguments: expression (I/O) char ** to first char of null-terminated
10156: * string immediately following \fbox to be
10157: * rasterized, and returning ptr immediately
10158: * following last character processed.
10159: * size (I) int containing 0-5 default font size
10160: * basesp (I) subraster * to character (or subexpression)
10161: * immediately preceding \fbox
10162: * (unused, but passed for consistency)
10163: * arg1 (I) int unused
10164: * arg2 (I) int unused
10165: * arg3 (I) int unused
10166: * --------------------------------------------------------------------------
10167: * Returns: ( subraster * ) ptr to subraster corresponding to \fbox
10168: * requested, or NULL for any parsing error
10169: * --------------------------------------------------------------------------
10170: * Notes: o Summary of syntax...
10171: * \fbox[width][height]{subexpression}
10172: * o
10173: * ======================================================================= */
10174: /* --- entry point --- */
10175: subraster *rastfbox ( char **expression, int size, subraster *basesp,
10176: int arg1, int arg2, int arg3 )
10177: {
10178: /* -------------------------------------------------------------------------
10179: Allocations and Declarations
10180: -------------------------------------------------------------------------- */
1.3 ! albertel 10181: char *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
1.1 albertel 10182: subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
10183: raster *border_raster(), *bp=NULL; /* framed image raster */
10184: double strtod(); /* interpret [width][height] */
10185: int fwidth=6, fthick=1; /*extra frame width, line thickness*/
10186: int width=(-1), height=(-1), /* optional [width][height] args */
10187: iscompose = 0; /* set true if optional args given */
10188: /* -------------------------------------------------------------------------
10189: obtain optional [width][height] arguments immediately following \fbox
10190: -------------------------------------------------------------------------- */
10191: /* --- first check for optional \fbox[width] --- */
10192: if ( *(*expression) == '[' ) /* check for []-enclosed width arg */
10193: { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
10194: if ( *widtharg != '\000' ) /* got widtharg */
10195: { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
10196: height = 1; fwidth = 2; iscompose = 1; }
10197: } /* --- end-of-if(**expression=='[') --- */
10198: if ( width > 0 ) /* found leading [width], so... */
10199: if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
10200: { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
10201: if ( *widtharg != '\000' ) /* got widtharg */
10202: { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
10203: fwidth = 0; } /* no extra border */
10204: } /* --- end-of-if(**expression=='[') --- */
10205: /* -------------------------------------------------------------------------
10206: obtain {subexpr} argument
10207: -------------------------------------------------------------------------- */
10208: /* --- parse for {subexpr} arg, and bump expression past it --- */
10209: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10210: /* --- rasterize subexpression to be framed --- */
10211: if ( width<0 || height<0 ) /* no explicit dimensions given */
10212: { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
10213: == NULL ) goto end_of_job; } /* and quit if failed */
10214: else
10215: { char composexpr[8192]; /* compose subexpr with empty box */
10216: sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
10217: width,height,subexpr);
10218: if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
10219: == NULL ) goto end_of_job; } /* and quit if failed */
10220: /* -------------------------------------------------------------------------
10221: draw frame, reset params, and return it to caller
10222: -------------------------------------------------------------------------- */
10223: /* --- draw border --- */
10224: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
10225: == NULL ) goto end_of_job; /* draw border and quit if failed */
10226: /* --- replace original image and raise baseline to accommodate frame --- */
10227: framesp->image = bp; /* replace image with framed one */
10228: if ( !iscompose ) /* simple border around subexpr */
10229: framesp->baseline += fwidth; /* so just raise baseline */
10230: else
10231: framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
10232: /* --- return framed subexpr to caller --- */
10233: end_of_job:
10234: return ( framesp ); /* return framed subexpr to caller */
10235: } /* --- end-of-function rastfbox() --- */
10236:
10237:
10238: /* ==========================================================================
10239: * Function: rastinput ( expression, size, basesp, arg1, arg2, arg3 )
10240: * Purpose: \input{filename} handler, reads filename and returns
10241: * subraster containing image of expression read from filename
10242: * --------------------------------------------------------------------------
10243: * Arguments: expression (I/O) char ** to first char of null-terminated
10244: * string immediately following \input to be
10245: * rasterized, and returning ptr immediately
10246: * following last character processed.
10247: * size (I) int containing 0-5 default font size
10248: * basesp (I) subraster * to character (or subexpression)
10249: * immediately preceding \input
10250: * (unused, but passed for consistency)
10251: * arg1 (I) int unused
10252: * arg2 (I) int unused
10253: * arg3 (I) int unused
10254: * --------------------------------------------------------------------------
10255: * Returns: ( subraster * ) ptr to subraster corresponding to expression
10256: * in filename, or NULL for any parsing error
10257: * --------------------------------------------------------------------------
10258: * Notes: o Summary of syntax...
10259: * \input{filename}
10260: * o
10261: * ======================================================================= */
10262: /* --- entry point --- */
10263: subraster *rastinput ( char **expression, int size, subraster *basesp,
10264: int arg1, int arg2, int arg3 )
10265: {
10266: /* -------------------------------------------------------------------------
10267: Allocations and Declarations
10268: -------------------------------------------------------------------------- */
1.3 ! albertel 10269: char *texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
1.1 albertel 10270: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
10271: int status, rastreadfile(); /* read input file */
10272: int format=0, npts=0; /* don't reformat (numerical) input */
1.3 ! albertel 10273: char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
1.1 albertel 10274: *mimeprep(), /* preprocess inputted data */
1.2 albertel 10275: *dbltoa(), *reformat=NULL; /* reformat numerical input */
1.1 albertel 10276: /* -------------------------------------------------------------------------
10277: obtain [tag]{filename} argument
10278: -------------------------------------------------------------------------- */
10279: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
10280: if ( *(*expression) == '[' ) /* check for []-enclosed value */
1.3 ! albertel 10281: { char argfld[MAXTOKNSZ+1]; /* optional argument field */
! 10282: *expression = texsubexpr(*expression,argfld,MAXTOKNSZ,"[","]",0,0);
1.2 albertel 10283: if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
10284: { format = 1; /* signal dtoa()/dbltoa() format */
1.1 albertel 10285: if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
10286: npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
10287: if ( format == 0 ) /* reformat not requested */
10288: strcpy(tag,argfld); } /* so interpret arg as tag */
10289: /* --- parse for {filename} arg, and bump expression past it --- */
10290: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
10291: /* --- check for alternate filename:tag --- */
10292: if ( *filename != '\000' /* got filename */
10293: /*&& *tag == '\000'*/ ) /* but no [tag] */
10294: { char *delim = strchr(filename,':'); /* look for : in filename:tag */
10295: if ( delim != (char *)NULL ) /* found it */
10296: { *delim = '\000'; /* null-terminate filename at : */
10297: strcpy(tag,delim+1); } } /* and stuff after : is tag */
10298: /* --------------------------------------------------------------------------
10299: Read file and rasterize constructed subexpression
10300: -------------------------------------------------------------------------- */
1.2 albertel 10301: status = rastreadfile(filename,0,tag,subexpr); /* read file */
1.1 albertel 10302: if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
10303: /* --- rasterize input subexpression --- */
10304: mimeprep(subexpr); /* preprocess subexpression */
1.2 albertel 10305: if ( format == 1 ) /* dtoa()/dbltoa() */
1.1 albertel 10306: { double d = strtod(subexpr,NULL); /* interpret subexpr as double */
10307: if ( d != 0.0 ) /* conversion to double successful */
1.2 albertel 10308: if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
1.1 albertel 10309: strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
10310: inputsp = rasterize(subexpr,size); /* rasterize subexpression */
10311: /* --- return input image to caller --- */
10312: end_of_job:
10313: return ( inputsp ); /* return input image to caller */
10314: } /* --- end-of-function rastinput() --- */
10315:
10316:
10317: /* ==========================================================================
10318: * Function: rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
10319: * Purpose: \counter[value]{filename} handler, returns subraster
10320: * containing image of counter value read from filename
10321: * (or optional [value]), and increments counter
10322: * --------------------------------------------------------------------------
10323: * Arguments: expression (I/O) char ** to first char of null-terminated
10324: * string immediately following \counter to be
10325: * rasterized, and returning ptr immediately
10326: * following last character processed.
10327: * size (I) int containing 0-5 default font size
10328: * basesp (I) subraster * to character (or subexpression)
10329: * immediately preceding \counter
10330: * (unused, but passed for consistency)
10331: * arg1 (I) int unused
10332: * arg2 (I) int unused
10333: * arg3 (I) int unused
10334: * --------------------------------------------------------------------------
10335: * Returns: ( subraster * ) ptr to subraster corresponding to \counter
10336: * requested, or NULL for any parsing error
10337: * --------------------------------------------------------------------------
10338: * Notes: o Summary of syntax...
1.2 albertel 10339: * \counter[value][logfile]{filename:tag}
10340: * o :tag is optional
1.1 albertel 10341: * ======================================================================= */
10342: /* --- entry point --- */
10343: subraster *rastcounter ( char **expression, int size, subraster *basesp,
10344: int arg1, int arg2, int arg3 )
10345: {
10346: /* -------------------------------------------------------------------------
10347: Allocations and Declarations
10348: -------------------------------------------------------------------------- */
10349: char *texsubexpr(), filename[1024]="\000", /* counter file */
1.3 ! albertel 10350: logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
1.1 albertel 10351: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
10352: FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
1.2 albertel 10353: int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
10354: isstrict = 1; /* true to only write to existing files */
1.3 ! albertel 10355: char text[MAXFILESZ] = "1_", /* only line in counter file without tags */
1.1 albertel 10356: *delim = NULL, /* delimiter in text */
1.2 albertel 10357: utext[128] = "1_", /* default delimiter */
1.1 albertel 10358: *udelim = utext+1; /* underscore delimiter */
1.2 albertel 10359: char *rasteditfilename(), /* edit log file name */
10360: *timestamp(), /* timestamp for logging */
10361: *dbltoa(); /* double to comma-separated ascii */
1.1 albertel 10362: int counter = 1, /* atoi(text) (after _ removed, if present) */
1.2 albertel 10363: value = 1, /* optional [value] argument */
10364: gotvalue = 0, /* set true if [value] supplied */
10365: isdelta = 0, /* set true if [+value] or [-value] is delta*/
1.1 albertel 10366: ordindex = (-1); /* ordinal[] index to append ordinal suffix */
10367: /*--- ordinal suffixes based on units digit of counter ---*/
10368: static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
10369: static char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
10370: static int commentvar = 1; /* logvars[commentvar] replaced by comment */
10371: /* -------------------------------------------------------------------------
10372: first obtain optional [value][logfile] args immediately following \counter
10373: -------------------------------------------------------------------------- */
10374: /* --- first check for optional \counter[value] --- */
10375: if ( *(*expression) == '[' ) /* check for []-enclosed value */
1.2 albertel 10376: { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
10377: if ( *text != '\000' ) /* got counter value (or logfile) */
1.3 ! albertel 10378: if ( strlen(text) >= 1 ) { /* and it's not an empty string */
1.2 albertel 10379: if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
10380: gotvalue = 1; /* signal we got optional value */
10381: else /* not +-digit, so must be logfile */
1.3 ! albertel 10382: strcpy(logfile,text); } /* so just copy it */
1.1 albertel 10383: } /* --- end-of-if(**expression=='[') --- */
10384: /* --- next check for optional \counter[][logfile] --- */
10385: if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
10386: { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
1.2 albertel 10387: if ( *filename != '\000' ) /* got logfile (or counter value) */
1.3 ! albertel 10388: if ( strlen(filename) >= 1 ) { /* and it's not an empty string */
1.2 albertel 10389: if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
10390: || gotvalue ) /* or we already got counter value */
10391: strcpy(logfile,filename); /* so just copy it */
10392: else /* leading +-digit must be value */
10393: { strcpy(text,filename); /* copy value to text line */
1.3 ! albertel 10394: gotvalue = 1; } } /* and signal we got optional value*/
1.1 albertel 10395: } /* --- end-of-if(**expression=='[') --- */
1.2 albertel 10396: /* --- evaluate [value] if present --- */
10397: if ( gotvalue ) { /*leading +-digit should be in text*/
10398: if ( *text == '+' ) isdelta = (+1); /* signal adding */
10399: if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
10400: value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
10401: if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
10402: counter = value; /* re-init counter */
10403: } /* --- end-of-if(gotvalue) --- */
1.1 albertel 10404: /* -------------------------------------------------------------------------
10405: obtain counter {filename} argument
10406: -------------------------------------------------------------------------- */
10407: /* --- parse for {filename} arg, and bump expression past it --- */
10408: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
10409: /* --- check for counter filename:tag --- */
10410: if ( *filename != '\000' ) /* got filename */
10411: if ( (delim=strchr(filename,':')) /* look for : in filename:tag */
10412: != (char *)NULL ) /* found it */
10413: { *delim = '\000'; /* null-terminate filename at : */
10414: strcpy(tag,delim+1); } /* and stuff after : is tag */
10415: /* --------------------------------------------------------------------------
10416: Read and parse file, increment and rewrite counter (with optional underscore)
10417: -------------------------------------------------------------------------- */
10418: if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
10419: {
10420: /* --- read and interpret first (and only) line from counter file --- */
1.2 albertel 10421: if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
10422: if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
10423: { char *vdelim = NULL; /* underscore delim from file */
10424: double fileval = strtod(text,&vdelim); /* value and delim from file */
10425: counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
10426: counter += value; /* bump count by 1 or add/sub delta*/
10427: if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
1.1 albertel 10428: /* --- check for ordinal suffix --- */
10429: if ( udelim != (char *)NULL ) /* have some delim after value */
10430: if ( *udelim == '_' ) /* underscore signals ordinal */
1.2 albertel 10431: { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
10432: ordindex = abscount%10; /* least significant digit */
10433: if ( abscount >= 10 ) /* counter is 10 or greater */
10434: if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
1.1 albertel 10435: ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
10436: /* --- rewrite counter file --- */
1.2 albertel 10437: if ( status >= 0 ) /* file was read okay */
10438: { sprintf(text,"%d",counter); /*build image of incremented counter*/
10439: if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
10440: if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
10441: status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
1.1 albertel 10442: } /* --- end-of-if(strlen(filename)>1) --- */
10443: /* --------------------------------------------------------------------------
10444: log counter request
10445: -------------------------------------------------------------------------- */
10446: if ( strlen(logfile) > 1 ) /* optional [logfile] given */
10447: {
10448: char comment[1024] = "\000", /* embedded comment, logfile:comment*/
10449: *commptr = strchr(logfile,':'); /* check for : signalling comment */
1.2 albertel 10450: int islogokay = 1; /* logfile must exist if isstrict */
1.1 albertel 10451: if ( commptr != NULL ) /* have embedded comment */
10452: { strcpy(comment,commptr+1); /* comment follows : */
10453: *commptr = '\000'; } /* null-terminate actual logfile */
1.2 albertel 10454: strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
10455: if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
10456: else if ( isstrict ) { /*okay, but only write if it exists*/
10457: if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
10458: islogokay = 0; /* so don't write log file */
10459: else fclose(logfp); } /* close file opened for test read */
10460: if ( islogokay ) /* okay to write logfile */
10461: if ( (logfp = fopen(logfile,"a")) /* open logfile */
10462: != (FILE *)NULL ) { /* opened successfully for append */
10463: int ilog=0; /* logvars[] index */
10464: fprintf(logfp,"%s ",timestamp(TZDELTA,0)); /* first emit timestamp */
10465: if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
10466: else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
10467: fprintf(logfp,"=%d",counter); /* emit counter value */
10468: if ( status < 1 ) /* read or re-write failed */
10469: fprintf(logfp,"(%s %d)","error status",status); /* emit error */
10470: for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
10471: if ( ilog == commentvar /* replace with comment... */
10472: && commptr != NULL ) /* ...if available */
10473: fprintf(logfp," %.256s",comment); /* log embedded comment */
10474: else
10475: { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
10476: fprintf(logfp," %.64s", /* log variable */
1.1 albertel 10477: (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
1.2 albertel 10478: fprintf(logfp,"\n"); /* terminating newline */
10479: fclose(logfp); /* close logfile */
10480: } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
1.1 albertel 10481: } /* --- end-of-if(strlen(logfile)>1) --- */
10482: /* --------------------------------------------------------------------------
10483: construct counter expression and rasterize it
10484: -------------------------------------------------------------------------- */
10485: /* --- construct expression --- */
10486: /*sprintf(text,"%d",counter);*/ /* start with counter */
1.2 albertel 10487: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
1.1 albertel 10488: if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
10489: { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
10490: strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
10491: strcat(text,"}}"); } /* finish with }} */
10492: /* --- rasterize it --- */
10493: countersp = rasterize(text,size); /* rasterize counter subexpression */
10494: /* --- return counter image to caller --- */
10495: /*end_of_job:*/
10496: return ( countersp ); /* return counter image to caller */
10497: } /* --- end-of-function rastcounter() --- */
10498:
10499:
10500: /* ==========================================================================
1.2 albertel 10501: * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
10502: * Purpose: handle \today
10503: * --------------------------------------------------------------------------
10504: * Arguments: expression (I/O) char ** to first char of null-terminated
10505: * string immediately following \today,
10506: * and returning ptr immediately
10507: * following last character processed.
10508: * size (I) int containing 0-5 default font size
10509: * basesp (I) subraster * to character (or subexpression)
10510: * immediately preceding \today
10511: * (unused, but passed for consistency)
10512: * arg1 (I) int unused
10513: * arg2 (I) int unused
10514: * arg3 (I) int unused
10515: * --------------------------------------------------------------------------
10516: * Returns: ( subraster * ) subraster ptr to date stamp
10517: * --------------------------------------------------------------------------
10518: * Notes: o
10519: * ======================================================================= */
10520: /* --- entry point --- */
10521: subraster *rasttoday ( char **expression, int size, subraster *basesp,
10522: int arg1, int arg2, int arg3 )
10523: {
10524: /* -------------------------------------------------------------------------
10525: Allocations and Declarations
10526: -------------------------------------------------------------------------- */
10527: char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
10528: char *timestamp(), *today=optarg; /* timestamp to be rasterized */
10529: subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
10530: int ifmt=1, tzdelta=0; /* default timestamp() args */
10531: /* -------------------------------------------------------------------------
10532: Get optional args \today[+/-tzdelta,ifmt]
10533: -------------------------------------------------------------------------- */
10534: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
10535: if ( *(*expression) == '[' ) /* check for []-enclosed value */
10536: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
10537: if ( *optarg != '\000' ) /* got optional arg */
10538: { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
10539: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
10540: if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
10541: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
10542: { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
10543: if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
10544: tzdelta = atoi(arg); /* so interpret arg as tzdelta */
10545: else ifmt = atoi(arg); } /* else interpret args as ifmt */
10546: } /* --- end-of-if(*optarg!='\0') --- */
10547: } /* --- end-of-if(**expression=='[') --- */
10548: /* -------------------------------------------------------------------------
10549: Get timestamp and rasterize it
10550: -------------------------------------------------------------------------- */
10551: strcpy(today,"\\text{"); /* rasterize timestamp as text */
10552: strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
10553: strcat(today,"}"); /* terminate \text{} braces */
10554: todaysp = rasterize(today,size); /* rasterize timestamp */
10555: /* --- return timestamp raster to caller --- */
10556: /*end_of_job:*/
10557: return ( todaysp ); /* return timestamp to caller */
10558: } /* --- end-of-function rasttoday() --- */
10559:
10560:
10561: /* ==========================================================================
10562: * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
10563: * Purpose: handle \calendar
10564: * --------------------------------------------------------------------------
10565: * Arguments: expression (I/O) char ** to first char of null-terminated
10566: * string immediately following \calendar
10567: * and returning ptr immediately
10568: * following last character processed.
10569: * size (I) int containing 0-5 default font size
10570: * basesp (I) subraster * to character (or subexpression)
10571: * immediately preceding \calendar
10572: * (unused, but passed for consistency)
10573: * arg1 (I) int unused
10574: * arg2 (I) int unused
10575: * arg3 (I) int unused
10576: * --------------------------------------------------------------------------
10577: * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
10578: * --------------------------------------------------------------------------
10579: * Notes: o
10580: * ======================================================================= */
10581: /* --- entry point --- */
10582: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
10583: int arg1, int arg2, int arg3 )
10584: {
10585: /* -------------------------------------------------------------------------
10586: Allocations and Declarations
10587: -------------------------------------------------------------------------- */
10588: char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
10589: char *calendar(), *calstr=NULL; /* calendar to be rasterized */
10590: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
10591: int year=0,month=0,day=0, argval=0; /* default calendar() args */
10592: /* -------------------------------------------------------------------------
10593: Get optional args \today[+/-tzdelta,ifmt]
10594: -------------------------------------------------------------------------- */
10595: /* --- check for optional \calendar[year,month] --- */
10596: if ( *(*expression) == '[' ) /* check for []-enclosed value */
10597: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
10598: if ( *optarg != '\000' ) /* got optional arg */
10599: { char *comma = strchr(optarg,','), /* comma between year,month */
10600: *comma2 = NULL; /* second comma before day */
10601: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
10602: if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
10603: if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
10604: { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
10605: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
10606: { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
10607: argval = atoi(arg); /* interpret arg as integer */
10608: if ( iarg < 3 ) /* first two args are month,year */
10609: {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
10610: else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
10611: else /* only 3rd arg can be day */
10612: if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
10613: } /* --- end-of-if(*optarg!='\0') --- */
10614: } /* --- end-of-if(**expression=='[') --- */
10615: /* -------------------------------------------------------------------------
10616: Get calendar string and rasterize it
10617: -------------------------------------------------------------------------- */
10618: if ( msgfp!= NULL && msglevel>=9 )
10619: fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
10620: year,month,day);
10621: calstr = calendar(year,month,day); /* get calendar string */
10622: calendarsp = rasterize(calstr,size); /* rasterize calendar string */
10623: /* --- return calendar raster to caller --- */
10624: /*end_of_job:*/
10625: return ( calendarsp ); /* return calendar to caller */
10626: } /* --- end-of-function rastcalendar() --- */
10627:
10628:
10629: /* ==========================================================================
1.1 albertel 10630: * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
10631: * Purpose: no op -- flush \escape without error
10632: * --------------------------------------------------------------------------
10633: * Arguments: expression (I/O) char ** to first char of null-terminated
10634: * string immediately following \escape to be
10635: * flushed, and returning ptr immediately
10636: * following last character processed.
10637: * size (I) int containing 0-5 default font size
10638: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 10639: * immediately preceding \escape
1.1 albertel 10640: * (unused, but passed for consistency)
10641: * nargs (I) int containing number of {}-args after
10642: * \escape to be flushed along with it
10643: * arg2 (I) int unused
10644: * arg3 (I) int unused
10645: * --------------------------------------------------------------------------
10646: * Returns: ( subraster * ) NULL subraster ptr
10647: * --------------------------------------------------------------------------
10648: * Notes: o
10649: * ======================================================================= */
10650: /* --- entry point --- */
10651: subraster *rastnoop ( char **expression, int size, subraster *basesp,
10652: int nargs, int arg2, int arg3 )
10653: {
10654: /* -------------------------------------------------------------------------
10655: Allocations and Declarations
10656: -------------------------------------------------------------------------- */
1.3 ! albertel 10657: char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
1.1 albertel 10658: subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */
10659: /* --- flush accompanying args if necessary --- */
10660: if ( nargs != NOVALUE /* not unspecified */
10661: && nargs > 0 ) /* and args to be flushed */
10662: while ( --nargs >= 0 ) /* count down */
10663: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
10664: /* --- return null ptr to caller --- */
10665: /*end_of_job:*/
10666: return ( noopsp ); /* return NULL ptr to caller */
10667: } /* --- end-of-function rastnoop() --- */
10668:
10669:
10670: /* ==========================================================================
10671: * Function: rastopenfile ( filename, mode )
10672: * Purpose: Opens filename[.tex] in mode, returning FILE *
10673: * --------------------------------------------------------------------------
10674: * Arguments: filename (I/O) char * to null-terminated string containing
10675: * name of file to open (preceded by path
10676: * relative to mimetex executable)
10677: * If fopen() fails, .tex appeneded,
10678: * and returned if that fopen() succeeds
10679: * mode (I) char * to null-terminated string containing
10680: * fopen() mode
10681: * --------------------------------------------------------------------------
10682: * Returns: ( FILE * ) pointer to opened file, or NULL if error
10683: * --------------------------------------------------------------------------
10684: * Notes: o
10685: * ======================================================================= */
10686: /* --- entry point --- */
10687: FILE *rastopenfile ( char *filename, char *mode )
10688: {
10689: /* -------------------------------------------------------------------------
10690: Allocations and Declarations
10691: -------------------------------------------------------------------------- */
10692: FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
1.3 ! albertel 10693: char texfile[2050] = "\000", /* local, edited copy of filename */
1.2 albertel 10694: *rasteditfilename(), /* prepend pathprefix if necessary */
1.3 ! albertel 10695: amode[512] = "r"; /* test open mode if arg mode=NULL */
1.2 albertel 10696: int ismode = 0; /* true of mode!=NULL */
1.1 albertel 10697: /* --------------------------------------------------------------------------
10698: Check mode and open file
10699: -------------------------------------------------------------------------- */
1.2 albertel 10700: /* --- edit filename --- */
1.3 ! albertel 10701: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
! 10702: texfile[2047] = '\000'; /* make sure it's null terminated */
1.1 albertel 10703: /* --- check mode --- */
10704: if ( mode != (char *)NULL ) /* caller passed mode arg */
10705: if ( *mode != '\000' ) /* and it's not an empty string */
10706: { ismode = 1; /* so flip mode flag true */
1.3 ! albertel 10707: strncpy(amode,mode,254); /* and replace "r" with caller's */
! 10708: amode[254] = '\000'; /* make sure it's null terminated */
1.1 albertel 10709: compress(amode,' '); } /* remove embedded blanks */
10710: /* --- open filename or filename.tex --- */
10711: if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/
10712: if ( (fp = fopen(texfile,amode)) /* try opening given filename */
10713: == NULL ) /* failed to open given filename */
10714: { strcpy(filename,texfile); /* signal possible filename error */
10715: strcat(texfile,".tex"); /* but first try adding .tex */
10716: if ( (fp = fopen(texfile,amode)) /* now try opening filename.tex */
10717: != NULL ) /* filename.tex succeeded */
10718: strcpy(filename,texfile); } /* replace caller's filename */
10719: /* --- close file if only opened to check name --- */
10720: if ( !ismode && fp!=NULL ) /* no mode, so just checking */
10721: fclose(fp); /* close file, fp signals success */
10722: /* --- return fp or NULL to caller --- */
10723: /*end_of_job:*/
10724: if ( msglevel>=9 && msgfp!=NULL ) /* debuging */
10725: { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
10726: filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
10727: return ( fp ); /* return fp or NULL to caller */
10728: } /* --- end-of-function rastopenfile() --- */
10729:
10730:
10731: /* ==========================================================================
1.2 albertel 10732: * Function: rasteditfilename ( filename )
10733: * Purpose: edits filename to remove security problems,
10734: * e.g., removes all ../'s and ..\'s.
10735: * --------------------------------------------------------------------------
10736: * Arguments: filename (I) char * to null-terminated string containing
10737: * name of file to be edited
10738: * --------------------------------------------------------------------------
10739: * Returns: ( char * ) pointer to edited filename,
10740: * or empty string "\000" if any problem
10741: * --------------------------------------------------------------------------
10742: * Notes: o
10743: * ======================================================================= */
10744: /* --- entry point --- */
10745: char *rasteditfilename ( char *filename )
10746: {
10747: /* -------------------------------------------------------------------------
10748: Allocations and Declarations
10749: -------------------------------------------------------------------------- */
1.3 ! albertel 10750: static char editname[2050]; /*edited filename returned to caller*/
1.2 albertel 10751: char *strchange(); /* prepend pathprefix if necessary */
10752: int strreplace(), /* remove ../'s and ..\'s */
10753: isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
10754: /* --------------------------------------------------------------------------
10755: edit filename
10756: -------------------------------------------------------------------------- */
10757: /* --- first check filename arg --- */
10758: *editname = '\000'; /* init edited name as empty string*/
10759: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
10760: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
10761: /* --- init edited filename --- */
10762: strcpy(editname,filename); /* init edited name as input name */
10763: compress(editname,' '); /* remove embedded blanks */
10764: /* --- remove leading or embedded ....'s --- */
10765: while ( strreplace(editname,"....",NULL,0) > 0 ) ; /* squeeze out ....'s */
10766: /* --- remove leading / and \ and dots (and blanks) --- */
10767: if ( *editname != '\000' ) /* still have chars in filename */
10768: while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
10769: strcpy(editname,editname+1); /* so flush leading / or \ (or ' ')*/
10770: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
10771: /* --- remove leading or embedded ../'s and ..\'s --- */
10772: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */
10773: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
10774: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* and ../'s again */
10775: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
10776: if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
10777: strchange(0,editname,pathprefix); /* so prepend prefix */
10778: end_of_job:
10779: return ( editname ); /* back with edited filename */
10780: } /* --- end-of-function rasteditfilename() --- */
10781:
10782:
10783: /* ==========================================================================
10784: * Function: rastreadfile ( filename, islock, tag, value )
1.1 albertel 10785: * Purpose: Read filename, returning value as string
10786: * between <tag>...</tag> or entire file if tag=NULL passed.
10787: * --------------------------------------------------------------------------
10788: * Arguments: filename (I) char * to null-terminated string containing
10789: * name of file to read (preceded by path
10790: * relative to mimetex executable)
1.2 albertel 10791: * islock (I) int containing 1 to lock file while reading
10792: * (hopefully done by opening in "r+" mode)
1.1 albertel 10793: * tag (I) char * to null-terminated string containing
10794: * html-like tagname. File contents between
10795: * <tag> and </tag> will be returned, or
10796: * entire file if tag=NULL passed.
10797: * value (O) char * returning value between <tag>...</tag>
10798: * or entire file if tag=NULL.
10799: * --------------------------------------------------------------------------
10800: * Returns: ( int ) 1=okay, 0=some error
10801: * --------------------------------------------------------------------------
10802: * Notes: o
10803: * ======================================================================= */
10804: /* --- entry point --- */
1.2 albertel 10805: int rastreadfile ( char *filename, int islock, char *tag, char *value )
1.1 albertel 10806: {
10807: /* -------------------------------------------------------------------------
10808: Allocations and Declarations
10809: -------------------------------------------------------------------------- */
10810: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3 ! albertel 10811: char texfile[1024] = "\000", /* local copy of input filename */
! 10812: text[MAXLINESZ+1]; /* line from input file */
! 10813: char *tagp, tag1[1024], tag2[1024]; /* left <tag> and right <tag/> */
! 10814: int vallen=0, maxvallen=MAXFILESZ; /* #chars in value, max allowed */
1.2 albertel 10815: int status = (-1); /* status returned, 1=okay */
1.1 albertel 10816: int tagnum = 0; /* tag we're looking for */
1.2 albertel 10817: /*int islock = 1;*/ /* true to lock file */
1.1 albertel 10818: /* --------------------------------------------------------------------------
10819: Open file
10820: -------------------------------------------------------------------------- */
10821: /* --- first check output arg --- */
10822: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
10823: *value = '\000'; /* init buffer with empty string */
10824: /* --- open filename or filename.tex --- */
10825: if ( filename != (char *)NULL ) /* make sure we got filename arg */
1.3 ! albertel 10826: { strncpy(texfile,filename,1023); /* local copy of filename */
! 10827: texfile[1023] = '\000'; /* make sure it's null terminated */
1.2 albertel 10828: fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
1.1 albertel 10829: /* --- check that file opened --- */
10830: if ( fp == (FILE *)NULL ) /* failed to open file */
10831: { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
10832: goto end_of_job; } /* return error message to caller */
1.2 albertel 10833: status = 0; /* file opened successfully */
10834: if ( islock ) rewind(fp); /* start at beginning of file */
1.1 albertel 10835: /* --------------------------------------------------------------------------
10836: construct <tag>'s
10837: -------------------------------------------------------------------------- */
10838: if ( tag != (char *)NULL ) /* caller passed tag arg */
10839: if ( *tag != '\000' ) /* and it's not an empty string */
10840: { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
10841: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
10842: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
10843: compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
10844: tagnum = 1; } /* signal that we have tag */
10845: /* --------------------------------------------------------------------------
10846: Read file, concatnate lines
10847: -------------------------------------------------------------------------- */
1.3 ! albertel 10848: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
1.1 albertel 10849: switch ( tagnum ) { /* look for left- or right-tag */
1.2 albertel 10850: case 0: status = 1; break; /* no tag to look for */
1.1 albertel 10851: case 1: /* looking for opening left <tag> */
10852: if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
10853: strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */
10854: tagnum = 2; /*now looking for closing right tag*/
10855: case 2: /* looking for closing right </tag> */
10856: if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
10857: *tagp = '\000'; /* terminate line at tag */
10858: tagnum = 3; /* done after this line */
1.2 albertel 10859: status = 1; /* successfully read tag */
1.1 albertel 10860: break;
10861: } /* ---end-of-switch(tagnum) --- */
10862: if ( tagnum != 1 ) { /* no tag or left tag already found*/
10863: int textlen = strlen(text); /* #chars in current line */
10864: if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
10865: strcat(value,text); /* concat line to end of value */
10866: vallen += textlen; /* bump length */
10867: if ( tagnum > 2 ) break; } /* found right tag, so we're done */
10868: } /* --- end-of-while(fgets()!=NULL) --- */
10869: if ( tagnum<1 || tagnum>2 ) status=1; /* okay if no tag or we found tag */
10870: fclose ( fp ); /* close input file after reading */
10871: /* --- return value and status to caller --- */
10872: end_of_job:
10873: return ( status ); /* return status to caller */
10874: } /* --- end-of-function rastreadfile() --- */
10875:
10876:
10877: /* ==========================================================================
10878: * Function: rastwritefile ( filename, tag, value, isstrict )
10879: * Purpose: Re/writes filename, replacing string between <tag>...</tag>
10880: * with value, or writing entire file as value if tag=NULL.
10881: * --------------------------------------------------------------------------
10882: * Arguments: filename (I) char * to null-terminated string containing
10883: * name of file to write (preceded by path
10884: * relative to mimetex executable)
10885: * tag (I) char * to null-terminated string containing
10886: * html-like tagname. File contents between
10887: * <tag> and </tag> will be replaced, or
10888: * entire file written if tag=NULL passed.
10889: * value (I) char * containing string replacing value
10890: * between <tag>...</tag> or replacing entire
10891: * file if tag=NULL.
10892: * isstrict (I) int containing 1 to only rewrite existing
10893: * files, or 0 to create new file if necessary.
10894: * --------------------------------------------------------------------------
10895: * Returns: ( int ) 1=okay, 0=some error
10896: * --------------------------------------------------------------------------
10897: * Notes: o
10898: * ======================================================================= */
10899: /* --- entry point --- */
10900: int rastwritefile( char *filename, char *tag, char *value, int isstrict )
10901: {
10902: /* -------------------------------------------------------------------------
10903: Allocations and Declarations
10904: -------------------------------------------------------------------------- */
10905: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3 ! albertel 10906: char texfile[1024] = "\000", /* local copy of input filename */
! 10907: filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */
! 10908: tag1[1024], tag2[1024], /* left <tag> and right <tag/> */
1.1 albertel 10909: *strchange(), /* put value between <tag>...</tag>*/
10910: *timestamp(); /* log modification time */
10911: int istag=0, rastreadfile(), /* read file if tag!=NULL */
10912: /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
10913: isnewfile = 0, /* true if writing new file */
10914: status = 0; /* status returned, 1=okay */
1.2 albertel 10915: int istimestamp = 0; /* true to update <timestamp> tag */
1.1 albertel 10916: /* --------------------------------------------------------------------------
10917: check args
10918: -------------------------------------------------------------------------- */
10919: /* --- check filename and value --- */
10920: if ( filename == (char *)NULL /* quit if no filename arg supplied*/
10921: || value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
10922: if ( strlen(filename) < 2 /* quit if unreasonable filename */
10923: || *value == '\000' ) goto end_of_job; /* or empty value string supplied */
10924: /* --- establish filename[.tex] --- */
1.3 ! albertel 10925: strncpy(texfile,filename,1023); /* local copy of input filename */
! 10926: texfile[1023] = '\000'; /* make sure it's null terminated */
1.1 albertel 10927: if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */
10928: == (FILE *)NULL ) /* can't open, so write new file */
10929: { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */
10930: isnewfile = 1; } /* signal we're writing new file */
10931: /* --- check whether tag supplied by caller --- */
10932: if ( tag != (char *)NULL ) /* caller passed tag argument */
10933: if ( *tag != '\000' ) /* and it's not an empty string */
10934: { istag = 1; /* so flip tag flag true */
10935: strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin tags with < and </ */
10936: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
10937: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
10938: compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
10939: /* --------------------------------------------------------------------------
10940: read existing file if just rewriting a single tag
10941: -------------------------------------------------------------------------- */
10942: /* --- read original file if only replacing a tag within it --- */
10943: *filebuff = '\000'; /* init as empty file */
10944: if ( !isnewfile ) /* if file already exists */
10945: if ( istag ) /* and just rewriting one tag */
1.2 albertel 10946: if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
10947: <= 0 ) goto end_of_job; /* signal error if failed to read */
1.1 albertel 10948: /* --------------------------------------------------------------------------
10949: construct new file data if needed (entire file replaced by value if no tag)
10950: -------------------------------------------------------------------------- */
10951: if ( istag ) /* only replacing tag in file */
10952: {
10953: /* --- find <tag> and </tag> in file --- */
10954: int tlen1=strlen(tag1), tlen2=strlen(tag2), flen; /*tag,buff lengths*/
10955: char *tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
10956: *tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
10957: /* --- if adding new <tag> just concatanate at end of file --- */
10958: if ( tagp1 == (char *)NULL ) /* add new tag to file */
10959: {
10960: /* --- preprocess filebuff --- */
10961: if ( tagp2 != (char *)NULL ) /* apparently have ...</tag> */
10962: strcpy(filebuff,tagp2+tlen2); /* so get rid of leading ...</tag> */
10963: if ( (flen = strlen(filebuff)) /* #chars currently in buffer */
10964: > 0 ) /* we have non-empty buffer */
10965: if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
10966: if(0)strcat(filebuff,"\n"); /* so add one before new tag */
10967: /* --- add new tag --- */
10968: strcat(filebuff,tag1); /* add opening <tag> */
10969: strcat(filebuff,value); /* then value */
10970: strcat(filebuff,tag2); /* finally closing </tag> */
10971: strcat(filebuff,"\n"); /* newline at end of file */
10972: } /* --- end-of-if(tagp1==NULL) --- */
10973: else /* found existing opening <tag> */
10974: {
10975: if ( tagp2 == NULL ) /* apparently have <tag>... */
10976: { *(tagp1+tlen1) = '\000'; /* so get rid of trailing ... */
10977: strcat(filebuff,value); /* then concatanate value */
10978: strcat(filebuff,tag2); } /* and finally closing </tag> */
10979: else /* else have <tag>...<tag/> */
10980: if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
10981: >= 0 ) /* usually <tag> precedes </tag> */
10982: strchange(flen,tagp1+tlen1,value); /* change ...'s to value */
10983: else /* weirdly, </tag> precedes <tag> */
1.3 ! albertel 10984: { char fbuff[4096]; /* field buff for <tag>value</tag> */
1.1 albertel 10985: if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
10986: <= 0 ) goto end_of_job; /* must be internal error */
10987: strcpy(fbuff,tag1); /* set opening <tag> */
10988: strcat(fbuff,value); /* then value */
10989: strcat(fbuff,tag2); /* finally closing </tag> */
10990: strchange(flen,tagp2,fbuff); } /* replace original </tag>...<tag> */
10991: } /* --- end-of-if/else(tagp1==NULL) --- */
10992: } /* --- end-of-if(istag) --- */
10993: /* --------------------------------------------------------------------------
10994: rewrite file and return to caller
10995: -------------------------------------------------------------------------- */
10996: /* --- first open file for write --- */
10997: if ( (fp=rastopenfile(texfile,"w")) /* open for write */
10998: == (FILE *)NULL ) goto end_of_job; /* signal error if can't open */
10999: /* --- rewrite and close file --- */
11000: if ( fputs((istag?filebuff:value),fp) /* write filebuff or value */
11001: != EOF ) status = 1; /* signal success if succeeded */
11002: fclose ( fp ); /* close output file after writing */
11003: /* --- modify timestamp --- */
1.2 albertel 11004: if ( status > 0 ) /*forget timestamp if write failed*/
11005: if ( istimestamp ) /* if we're updating timestamp */
11006: if ( istag ) /* only log time in tagged file */
11007: if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
11008: { char fbuff[2048]; /* field buff <timestamp> value */
11009: strcpy(fbuff,tag); /* tag modified */
11010: strcat(fbuff," modified at "); /* spacer */
11011: strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
11012: status = rastwritefile(filename,"timestamp",fbuff,1); }
1.1 albertel 11013: /* --- return status to caller --- */
11014: end_of_job:
11015: return ( status ); /* return status to caller */
11016: } /* --- end-of-function rastwritefile() --- */
11017:
11018:
11019: /* ==========================================================================
1.2 albertel 11020: * Function: calendar ( year, month, day )
11021: * Purpose: returns null-terminated character string containing
11022: * \begin{array}...\end{array} for the one-month calendar
11023: * specified by year=1973...2099 and month=1...12.
11024: * If either arg out-of-range, today's value is used.
11025: * --------------------------------------------------------------------------
11026: * Arguments: year (I) int containing 1973...2099 or 0 for current
11027: * year
11028: * month (I) int containing 1...12 or 0 for current month
11029: * day (I) int containing day to emphasize or 0
11030: * --------------------------------------------------------------------------
11031: * Returns: ( char * ) char ptr to null-terminated buffer
11032: * containing \begin{array}...\end{array}
11033: * string that will render calendar for
11034: * requested month, or NULL for any error.
11035: * --------------------------------------------------------------------------
11036: * Notes: o
11037: * ======================================================================= */
11038: /* --- entry point --- */
11039: char *calendar( int year, int month, int day )
11040: {
11041: /* -------------------------------------------------------------------------
11042: Allocations and Declarations
11043: -------------------------------------------------------------------------- */
11044: static char calbuff[4096]; /* calendar returned to caller */
11045: time_t time_val = (time_t)(0); /* binary value returned by time() */
11046: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
11047: int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
11048: int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
11049: char aval[64]; /* ascii day or 4-digit year */
11050: /* --- calendar data --- */
11051: static char *monthnames[] = { "?", "January", "February", "March", "April",
11052: "May", "June", "July", "August", "September", "October",
11053: "November", "December", "?" } ;
11054: static int modays[] =
11055: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
11056: /* -------------------------------------------------------------------------
11057: initialization
11058: -------------------------------------------------------------------------- */
11059: /* --- get current date/time --- */
11060: time((time_t *)(&time_val)); /* get date and time */
11061: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
11062: yy = 1900 + (int)(tmstruct->tm_year); /* current four-digit year */
11063: mm = 1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
11064: dd = (int)(tmstruct->tm_mday); /* current day, 1-31 */
11065: /* --- check args --- */
11066: if ( year<1973 || year>2099 ) year = yy; /* current year if out-of-bounds */
11067: if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
11068: if ( month==mm && year==yy && day==0 ) /* current month and default day */
11069: day = dd; /* emphasize current day */
11070: modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
11071: /* --- initialize calendar string --- */
11072: strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
11073: strcat(calbuff,"\\small\\text{"); /* month set in roman */
11074: strcat(calbuff,monthnames[month]); /* insert month name */
11075: strcat(calbuff," }"); /* add a space */
11076: sprintf(aval,"%d",year); /* convert year to ascii */
11077: strcat(calbuff,aval); /* add year */
11078: strcat(calbuff,"\\\\"); /* end top row */
11079: strcat(calbuff, /* now begin calendar arrayr */
11080: "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
11081: "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
11082: "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
11083: "\\tiny\\text{Sat} \\\\ \\hline " );
11084: /* -------------------------------------------------------------------------
11085: generate calendar
11086: -------------------------------------------------------------------------- */
11087: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
11088: {
11089: /* --- get day-of-week for this day --- */
11090: iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
11091: if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
11092: /* --- may need empty cells at beginning of month --- */
11093: if ( idd == 1 ) /* first day of month */
11094: if ( iday > 0 ) /* need to skip cells */
11095: { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
11096: aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
11097: strcat(calbuff,aval); } /* add skip string to buffer */
11098: /* --- add idd to current cell --- */
11099: sprintf(aval,"%d",idd); /* convert idd to ascii */
11100: if ( idd == day /* emphasize today's date */
11101: /*&& month==mm && year==yy*/ ) /* only if this month's calendar */
11102: { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
11103: strcat(calbuff,aval); /* put in idd */
11104: strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
11105: else /* not today's date */
11106: strcat(calbuff,aval); /* so just put in idd */
11107: /* --- terminate cell --- */
1.3 ! albertel 11108: if ( idd < modays[month] ) { /* not yet end-of-month */
1.2 albertel 11109: if ( iday < 6 ) /* still have days left in week */
11110: strcat(calbuff,"&"); /* new cell in same week */
11111: else /* reached end-of-week */
1.3 ! albertel 11112: strcat(calbuff,"\\\\ \\hline"); } /* so start new week */
1.2 albertel 11113: } /* --- end-of-for(idd) --- */
11114: strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
11115: /* --- return calendar to caller --- */
11116: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
11117: return ( calbuff ); /* back to caller with calendar */
11118: } /* --- end-of-function calendar() --- */
11119:
11120:
11121: /* ==========================================================================
11122: * Function: timestamp ( tzdelta, ifmt )
1.1 albertel 11123: * Purpose: returns null-terminated character string containing
11124: * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
11125: * --------------------------------------------------------------------------
1.2 albertel 11126: * Arguments: tzdelta (I) integer, positive or negative, containing
11127: * containing number of hours to be added or
11128: * subtracted from system time (to accommodate
11129: * your desired time zone).
11130: * ifmt (I) integer containing 0 for default format
1.1 albertel 11131: * --------------------------------------------------------------------------
11132: * Returns: ( char * ) ptr to null-terminated buffer
11133: * containing current date:time stamp
11134: * --------------------------------------------------------------------------
11135: * Notes: o
11136: * ======================================================================= */
11137: /* --- entry point --- */
1.2 albertel 11138: char *timestamp( int tzdelta, int ifmt )
1.1 albertel 11139: {
11140: /* -------------------------------------------------------------------------
11141: Allocations and Declarations
11142: -------------------------------------------------------------------------- */
1.2 albertel 11143: static char timebuff[256]; /* date:time buffer back to caller */
1.1 albertel 11144: /*long time_val = 0L;*/ /* binary value returned by time() */
11145: time_t time_val = (time_t)(0); /* binary value returned by time() */
11146: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
1.2 albertel 11147: int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
11148: month=0, day=0; /* adjust day and month for delta */
11149: int tzadjust(); /* time zone adjustment function */
11150: int daynumber(); /* #days since Jan 1, 1973 */
11151: static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
11152: "Thursday", "Friday", "Saturday", "Sunday" } ;
11153: static char *monthnames[] = { "?", "January", "February", "March", "April",
11154: "May", "June", "July", "August", "September", "October",
11155: "November", "December", "?" } ;
1.1 albertel 11156: /* -------------------------------------------------------------------------
11157: get current date:time, adjust values, and and format stamp
11158: -------------------------------------------------------------------------- */
1.2 albertel 11159: /* --- first init returned timebuff in case of any error --- */
11160: *timebuff = '\000';
1.1 albertel 11161: /* --- get current date:time --- */
11162: time((time_t *)(&time_val)); /* get date and time */
11163: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
1.2 albertel 11164: /* --- extract fields --- */
11165: year = (int)(tmstruct->tm_year); /* local copy of year, 0=1900 */
11166: month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
11167: day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */
11168: hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */
11169: /* --- adjust year --- */
1.1 albertel 11170: year += 1900; /* set century in year */
1.2 albertel 11171: /* --- adjust for timezone --- */
11172: tzadjust(tzdelta,&year,&month,&day,&hour);
11173: /* --- check params --- */
11174: if ( hour<0 || hour>23
11175: || day<1 || day>31
11176: || month<1 || month>12
11177: || year<1973 ) goto end_of_job;
11178: /* --- adjust hour for am/pm --- */
11179: switch ( ifmt )
11180: {
11181: default:
11182: case 0:
11183: if ( hour < 12 ) /* am check */
11184: { ispm=0; /* reset pm flag */
11185: if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
11186: if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
11187: break;
11188: } /* --- end-of-switch(ifmt) --- */
1.1 albertel 11189: /* --- format date:time stamp --- */
1.2 albertel 11190: switch ( ifmt )
11191: {
11192: default:
11193: case 0: /* --- 2005-03-05:11:49:59am --- */
11194: sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
11195: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
11196: break;
11197: case 1: /* --- Saturday, March 5, 2005 --- */
11198: sprintf(timebuff,"%s, %s %d, %d",
11199: daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
11200: break;
11201: case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
11202: sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
11203: daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
11204: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
11205: break;
11206: case 3: /* --- 11:49:59am --- */
11207: sprintf(timebuff,"%d:%02d:%02d%s",
11208: hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
11209: break;
11210: } /* --- end-of-switch(ifmt) --- */
11211: end_of_job:
11212: return ( timebuff ); /* return stamp to caller */
1.1 albertel 11213: } /* --- end-of-function timestamp() --- */
11214:
11215:
11216: /* ==========================================================================
1.2 albertel 11217: * Function: tzadjust ( tzdelta, year, month, day, hour )
11218: * Purpose: Adjusts hour, and day,month,year if necessary,
11219: * by delta increment to accommodate your time zone.
11220: * --------------------------------------------------------------------------
11221: * Arguments: tzdelta (I) integer, positive or negative, containing
11222: * containing number of hours to be added or
11223: * subtracted from given time (to accommodate
11224: * your desired time zone).
11225: * year (I) addr of int containing 4-digit year
11226: * month (I) addr of int containing month 1=Jan - 12=Dec.
11227: * day (I) addr of int containing day 1-31 for Jan.
11228: * hour (I) addr of int containing hour 0-23
11229: * Returns: ( int ) 1 for success, or 0 for error
11230: * --------------------------------------------------------------------------
11231: * Notes: o
11232: * ======================================================================= */
11233: /* --- entry point --- */
11234: int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
11235: {
11236: /* --------------------------------------------------------------------------
11237: Allocations and Declarations
11238: -------------------------------------------------------------------------- */
11239: int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
11240: /* --- calendar data --- */
11241: static int modays[] =
11242: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
11243: /* --------------------------------------------------------------------------
11244: check args
11245: -------------------------------------------------------------------------- */
11246: if ( mm<1 || mm>12 ) return(-1); /* bad month */
11247: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
11248: if ( hh<0 || hh>23 ) return(-1); /* bad hour */
11249: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
11250: /* --------------------------------------------------------------------------
11251: make adjustments
11252: -------------------------------------------------------------------------- */
11253: /* --- adjust hour --- */
11254: hh += tzdelta; /* apply caller's delta */
11255: /* --- adjust for feb 29 --- */
11256: modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
11257: /* --- adjust day --- */
11258: if ( hh < 0 ) /* went to preceding day */
11259: { dd--; hh += 24; }
11260: if ( hh > 23 ) /* went to next day */
11261: { dd++; hh -= 24; }
11262: /* --- adjust month --- */
11263: if ( dd < 1 ) /* went to preceding month */
11264: { mm--; dd = modays[mm]; }
11265: if ( dd > modays[mm] ) /* went to next month */
11266: { mm++; dd = 1; }
11267: /* --- adjust year --- */
11268: if ( mm < 1 ) /* went to preceding year */
11269: { yy--; mm = 12; dd = modays[mm]; }
11270: if ( mm > 12 ) /* went to next year */
11271: { yy++; mm = 1; dd = 1; }
11272: /* --- back to caller --- */
11273: *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
11274: return ( 1 );
11275: } /* --- end-of-function tzadjust() --- */
11276:
11277:
11278: /* ==========================================================================
11279: * Function: daynumber ( year, month, day )
11280: * Purpose: Returns number of actual calendar days from Jan 1, 1973
11281: * to the given date (e.g., bvdaynumber(1974,1,1)=365).
11282: * --------------------------------------------------------------------------
11283: * Arguments: year (I) int containing year -- may be either 1995 or
11284: * 95, or may be either 2010 or 110 for those
11285: * years.
11286: * month (I) int containing month, 1=Jan thru 12=Dec.
11287: * day (I) int containing day of month, 1-31 for Jan, etc.
11288: * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
11289: * or -1 for error (e.g., year<1973).
11290: * --------------------------------------------------------------------------
11291: * Notes: o
11292: * ======================================================================= */
11293: /* --- entry point --- */
11294: int daynumber ( int year, int month, int day )
11295: {
11296: /* --------------------------------------------------------------------------
11297: Allocations and Declarations
11298: -------------------------------------------------------------------------- */
11299: /* --- returned value (note: returned as a default "int") --- */
11300: int ndays; /* #days since jan 1, year0 */
11301: /* --- initial conditions --- */
11302: static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
11303: days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
11304: days1yr = 365;
11305: /* --- table of accumulated days per month (last index not used) --- */
11306: static int modays[] =
11307: { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
11308: /* --- variables for #days since day0 --- */
11309: int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
11310: /* --------------------------------------------------------------------------
11311: Check input
11312: -------------------------------------------------------------------------- */
11313: if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
11314: return ( -1 ); /* otherwise, forget it */
11315: if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
11316: /* --------------------------------------------------------------------------
11317: Find #days since jan 1, 1973
11318: -------------------------------------------------------------------------- */
11319: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
11320: nyears = year - year0; /* #years since year0 */
11321: if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
11322: nfouryrs = nyears/4; /* #complete four-year periods */
11323: nyears -= (4*nfouryrs); /* remainder excluding current year*/
11324: /* --- #days from jan 1, year0 till jan 1, this year --- */
11325: ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
11326: + (days1yr*nyears); /* +remaining days */
11327: /*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
11328: /* --- add #days within current year --- */
11329: ndays += (modays[month-1] + (day-1));
11330: /* --- may need an extra day if current year is a leap year --- */
11331: if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
11332: { if ( month > 2 ) /* past feb so need an extra day */
11333: /*if ( year != 100 )*/ /* unless it's 2000AD */
11334: ndays++; } /* so add it in */
11335: return ( (int)(ndays) ); /* #days back to caller */
11336: } /* --- end-of-function daynumber() --- */
11337:
11338:
11339: /* ==========================================================================
11340: * Function: dbltoa ( dblval, npts )
1.1 albertel 11341: * Purpose: Converts double to ascii, in financial format
11342: * (e.g., comma-separated and negatives enclosed in ()'s).
11343: * -------------------------------------------------------------------------
11344: * Arguments: dblval (I) double containing value to be converted.
11345: * npts (I) int containing #places after decimal point
11346: * to be displayed in returned string.
11347: * Returns: ( char * ) null-terminated string containing
11348: * double converted to financial format.
11349: * -------------------------------------------------------------------------
11350: * Notes: o
11351: * ======================================================================= */
11352: /* --- entry point --- */
1.2 albertel 11353: char *dbltoa ( double dblval, int npts )
11354: /* double dblval;
11355: int npts; */
1.1 albertel 11356: {
11357: /* -------------------------------------------------------------------------
11358: Allocations and Declarations
11359: ------------------------------------------------------------------------- */
1.3 ! albertel 11360: static char finval[256]; /* buffer returned to caller */
1.1 albertel 11361: static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
11362: char *finptr = finval; /* ptr to next char being converted*/
11363: double floor(); /* integer which is glb(double) */
11364: double dbldigit; /* to shift out digits from dblval */
11365: int digit; /* one digit from dblval */
11366: int isneg = 0; /* reset true if dblval negative */
11367: int ifrac = 0; /* npts fractional digits of dblval*/
11368: char digits[64]; int ndigits=0; /* all the digits [0]=least signif */
11369: /* -------------------------------------------------------------------------
11370: Check sign
11371: ------------------------------------------------------------------------- */
11372: if ( dblval < 0.0 ) /* got a negative value to convert */
11373: { isneg=1; dblval=(-dblval); } /* set flag and make it positive */
11374: /* -------------------------------------------------------------------------
11375: Get fractional part of dblval if required
11376: ------------------------------------------------------------------------- */
11377: if ( npts > 0 )
11378: { int ipts = npts; /* loop index */
11379: dbldigit = dblval-floor(dblval); /* fractional part as double */
11380: digit = 1; /* check if rounded frac > 1 */
11381: while ( --ipts >= 0 ) /* count down */
11382: { dbldigit *= 10.0; /* shift left one digit at a time */
11383: digit *= 10; } /* and keep max up-to-date */
11384: ifrac = (int)(dbldigit + 0.5); /* store fractional part as integer*/
11385: if ( ifrac >= digit ) /* round to next whole number */
11386: { dblval++; ifrac=0; } /* bump val, reset frac to zero */
11387: } /* --- end-of-if(npts>0) --- */
11388: else dblval += 0.5; /* no frac, round to nearest whole */
11389: /* -------------------------------------------------------------------------
11390: Get whole digits
11391: ------------------------------------------------------------------------- */
11392: dblval = floor(dblval); /* get rid of fractional part */
11393: while ( dblval > 0.0 ) /* still have data digits remaining*/
11394: { dbldigit = floor(dblval/10.0); /* shift out next digit */
11395: digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
11396: if ( digit<0 || digit>9 ) digit=10; /* index check */
11397: digits[ndigits++] = digittbl[digit]; /* store ascii digit */
11398: dblval = dbldigit; } /* ready for next digit */
11399: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
11400: /* -------------------------------------------------------------------------
11401: Format whole part from digits[] array
11402: ------------------------------------------------------------------------- */
11403: if ( isneg ) *finptr++ = '('; /* leading paren for negative value*/
11404: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
11405: { *finptr++ = digits[digit]; /* store digit */
11406: if ( digit>0 && digit%3==0 ) /* need a comma */
11407: *finptr++ = ','; } /* put in separating comma */
11408: /* -------------------------------------------------------------------------
11409: Format fractional part using ifrac
11410: ------------------------------------------------------------------------- */
11411: if ( npts > 0 )
11412: { *finptr++ = '.'; /* start with decimal point */
11413: sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
11414: finptr += npts; } /* bump ptr past fractional digits */
11415: /* -------------------------------------------------------------------------
11416: End-of-Job
11417: ------------------------------------------------------------------------- */
11418: if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
11419: *finptr = '\000'; /* null-terminate converted double */
11420: return ( finval ); /* converted double back to caller */
1.2 albertel 11421: } /* --- end-of-function dbltoa() --- */
1.1 albertel 11422:
11423:
11424: /* ==========================================================================
11425: * Function: aalowpass ( rp, bytemap, grayscale )
11426: * Purpose: calculates a lowpass anti-aliased bytemap
11427: * for rp->bitmap, with each byte 0...grayscale-1
11428: * --------------------------------------------------------------------------
11429: * Arguments: rp (I) raster * to raster whose bitmap
11430: * is to be anti-aliased
11431: * bytemap (O) intbyte * to bytemap, calculated
11432: * by applying lowpass filter to rp->bitmap,
11433: * and returned (as you'd expect) in 1-to-1
11434: * addressing correspondence with rp->bitmap
11435: * grayscale (I) int containing number of grayscales
11436: * to be calculated, 0...grayscale-1
11437: * (should typically be given as 256)
11438: * --------------------------------------------------------------------------
11439: * Returns: ( int ) 1=success, 0=any error
11440: * --------------------------------------------------------------------------
11441: * Notes: o If the center point of the box being averaged is black,
11442: * then the entire "average" is forced black (grayscale-1)
11443: * regardless of the surrounding points. This is my attempt
11444: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
11445: * lines, which would otherwise turn from black to a gray shade.
11446: * o Also, while the weights for neighbor points are fixed,
11447: * you may adjust the center point weight on the compile line.
11448: * A higher weight sharpens the resulting anti-aliased image;
11449: * lower weights blur it out more (but keep the "center" black
11450: * as per the preceding note).
11451: * ======================================================================= */
11452: /* --- entry point --- */
11453: int aalowpass (raster *rp, intbyte *bytemap, int grayscale)
11454: {
11455: /* -------------------------------------------------------------------------
11456: Allocations and Declarations
11457: -------------------------------------------------------------------------- */
11458: int status = 1; /* 1=success, 0=failure to caller */
11459: pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
11460: int irow=0, icol=0; /* rp->height, rp->width indexes */
11461: int weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
11462: int adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
11463: int totwts = 0; /* sum of all weights in matrix */
11464: int isforceavg = 1, /*force avg black if center black?*/
11465: isminmaxwts = 1, /*use wts or #pts for min/max test */
11466: blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
11467: /* -------------------------------------------------------------------------
11468: Initialization
11469: -------------------------------------------------------------------------- */
11470: /* --- calculate total weights --- */
11471: weights[4]= centerwt; /* weight for center point */
11472: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
11473: totwts = centerwt + 4*(1+adjacentwt); /* tot is center plus neighbors */
11474: /* -------------------------------------------------------------------------
11475: Calculate bytemap as 9-point weighted average over bitmap
11476: -------------------------------------------------------------------------- */
11477: for ( irow=0; irow<rp->height; irow++ )
11478: for ( icol=0; icol<rp->width; icol++ )
11479: {
11480: int ipixel = icol + irow*(rp->width); /* center pixel index */
11481: int jrow=0, jcol=0, /* box around ipixel */
11482: bitval = 0, /* value of bit/pixel at jrow,jcol */
11483: iscenter = 0, /* set true if center pixel black */
11484: nadjacent=0, wadjacent=0, /* #adjacent black pixels, their wts*/
11485: ngaps = 0, /* #gaps in 8 pixels around center */
11486: iwt=(-1), sumwts=0; /* weights index, sum in-bound wts */
11487: char adjmatrix[8]; /* adjacency "matrix" */
11488: memset(adjmatrix,0,8); /* zero out adjacency matrix */
11489: bytemap[ipixel] = 0; /* init pixel white */
11490: /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
11491: for ( jrow=irow-1; jrow<=irow+1; jrow++ ) /* jrow = irow-1...irow+1 */
11492: for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
11493: {
11494: int jpixel = jcol + jrow*(rp->width); /* averaging index */
11495: iwt++; /*always bump weight index*/
11496: if ( jrow<0 || jrow>=rp->height /* if row out pf bounds */
11497: || jcol<0 || jcol>=rp->width ) /* or col out of bounds */
11498: continue; /* ignore this point */
11499: bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
11500: if ( bitval ) /* this is a black pixel */
11501: { if ( jrow==irow && jcol==icol ) /* and this is center point */
11502: iscenter = 1; /* set flag for center point black */
11503: else /* adjacent point black */
11504: { nadjacent++; /* bump adjacent black count */
11505: adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
11506: wadjacent += weights[iwt]; } /* sum weights for black pixels */
11507: sumwts += weights[iwt]; /* and sum weights for all pixels */
11508: } /* --- end-of-for(jrow,jcol) --- */
11509: /* --- count gaps --- */
11510: ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
11511: for ( iwt=0; iwt<7; iwt++ ) /* clockwise around adjacency */
11512: if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
11513: ngaps /= 2; /*each gap has 2 black/white flips*/
11514: /* --- anti-alias pixel, but leave it black if it was already black --- */
11515: if ( isforceavg && iscenter ) /* force avg if center point black */
11516: bytemap[ipixel] = grayscale-1; /* so force grayscale-1=black */
11517: else /* center point not black */
11518: if ( ngaps <= 2 ) /*don't darken checkerboarded pixel*/
11519: { bytemap[ipixel] = /* 0=white ... grayscale-1=black */
11520: ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
11521: if ( blackscale > 0 /* blackscale kludge turned on */
11522: && bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
11523: bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
11524: /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
1.3 ! albertel 11525: if ( !iscenter ) { /* apply min/maxadjacent test */
1.1 albertel 11526: if ( isminmaxwts ) /* min/max refer to adjacent weights*/
11527: { if ( wadjacent < minadjacent /* wts of adjacent points too low */
11528: || wadjacent > maxadjacent ) /* or too high */
11529: bytemap[ipixel] = 0; } /* so leave point white */
11530: else /* min/max refer to #adjacent points*/
11531: { if ( nadjacent < minadjacent /* too few adjacent points black */
11532: || nadjacent > maxadjacent ) /* or too many */
1.3 ! albertel 11533: bytemap[ipixel] = 0; } } /* so leave point white */
1.1 albertel 11534: } /* --- end-of-for(irow,icol) --- */
11535: /* -------------------------------------------------------------------------
11536: Back to caller with gray-scale anti-aliased bytemap
11537: -------------------------------------------------------------------------- */
11538: /*end_of_job:*/
11539: return ( status );
11540: } /* --- end-of-function aalowpass() --- */
11541:
11542:
11543: /* ==========================================================================
11544: * Function: aapnm ( rp, bytemap, grayscale )
11545: * Purpose: calculates a lowpass anti-aliased bytemap
11546: * for rp->bitmap, with each byte 0...grayscale-1,
11547: * based on the pnmalias.c algorithm
11548: * --------------------------------------------------------------------------
11549: * Arguments: rp (I) raster * to raster whose bitmap
11550: * is to be anti-aliased
11551: * bytemap (O) intbyte * to bytemap, calculated
11552: * by applying pnm-based filter to rp->bitmap,
11553: * and returned (as you'd expect) in 1-to-1
11554: * addressing correspondence with rp->bitmap
11555: * grayscale (I) int containing number of grayscales
11556: * to be calculated, 0...grayscale-1
11557: * (should typically be given as 256)
11558: * --------------------------------------------------------------------------
11559: * Returns: ( int ) 1=success, 0=any error
11560: * --------------------------------------------------------------------------
11561: * Notes: o Based on the pnmalias.c algorithm in the netpbm package
11562: * on sourceforge.
11563: * ======================================================================= */
11564: /* --- entry point --- */
11565: int aapnm (raster *rp, intbyte *bytemap, int grayscale)
11566: {
11567: /* -------------------------------------------------------------------------
11568: Allocations and Declarations
11569: -------------------------------------------------------------------------- */
11570: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
11571: int width=rp->width, height=rp->height, /* width, height of raster */
11572: icol = 0, irow = 0, /* width, height indexes */
11573: imap = (-1); /* pixel index = icol + irow*width */
11574: int bgbitval=0, fgbitval=1; /* background, foreground bitval */
1.3 ! albertel 11575: int isfirstaa = 1; /*debugging switch signals 1st pixel*/
1.1 albertel 11576: #if 0
11577: int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
11578: int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
11579: #endif
11580: int totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
11581: int isresetparams = 1, /* true to set antialiasing params */
11582: isfgalias = 1, /* true to antialias fg bits */
11583: isfgonly = 0, /* true to only antialias fg bits */
11584: isbgalias = 0, /* true to antialias bg bits */
11585: isbgonly = 0; /* true to only antialias bg bits */
11586: /* -------------------------------------------------------------------------
11587: Initialization
11588: -------------------------------------------------------------------------- */
11589: /* --- check for bold light --- */
11590: if ( 0 )
11591: { if ( weightnum > 2 ) { isbgalias=1; } /* simulate bold */
11592: if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
11593: /* --- reset wts[], etc, and calculate total weights --- */
11594: if ( isresetparams ) /* wts[], etc taken from params */
11595: { int iwt=0; /* wts[iwt] index */
11596: wts[4]= centerwt; /* weight for center point */
11597: wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
11598: wts[0]=wts[2]=wts[6]=wts[8] = cornerwt; /* and corner points */
11599: for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
11600: isfgalias = fgalias; /* set isfgalias */
11601: isfgonly = fgonly; /* set isfgonly */
11602: isbgalias = bgalias; /* set isbgalias */
11603: isbgonly = bgonly; } /* set isbgonly */
11604: /* -------------------------------------------------------------------------
11605: Calculate bytemap as 9-point weighted average over bitmap
11606: -------------------------------------------------------------------------- */
11607: for ( irow=0; irow<height; irow++ )
11608: for ( icol=0; icol<width; icol++ )
11609: {
11610: /* --- local allocations and declarations --- */
11611: int bitval=0, /* value of rp bit at irow,icol */
11612: nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
11613: ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0; /*compass pt names*/
11614: int isbgedge=0, isfgedge=0; /*does pixel border a bg or fg edge*/
11615: int aabyteval=0; /* antialiased (or unchanged) value*/
11616: /* --- bump imap index and get center bit value --- */
11617: imap++; /* imap = icol + irow*width */
11618: bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
11619: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
11620: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
11621: /* --- check if we're antialiasing this pixel --- */
11622: if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
11623: || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
11624: continue; /* leave default and do next bit */
11625: /* --- get surrounding bits --- */
11626: if ( irow > 0 ) /* nn (north) bit available */
11627: nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
11628: if ( irow < height-1 ) /* ss (south) bit available */
11629: ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
11630: if ( icol > 0 ) /* ww (west) bit available */
11631: { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
11632: if ( irow > 0 ) /* nw bit available */
11633: nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
11634: if ( irow < height-1 ) /* sw bit available */
11635: swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
11636: if ( icol < width-1 ) /* ee (east) bit available */
11637: { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
11638: if ( irow > 0 ) /* ne bit available */
11639: nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
11640: if ( irow < height-1 ) /* se bit available */
11641: sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
11642: /* --- check for edges --- */
11643: isbgedge = /* current pixel borders a bg edge */
11644: (nnbitval==bgbitval && eebitval==bgbitval) || /*upper-right edge*/
11645: (eebitval==bgbitval && ssbitval==bgbitval) || /*lower-right edge*/
11646: (ssbitval==bgbitval && wwbitval==bgbitval) || /*lower-left edge*/
11647: (wwbitval==bgbitval && nnbitval==bgbitval) ; /*upper-left edge*/
11648: isfgedge = /* current pixel borders an fg edge*/
11649: (nnbitval==fgbitval && eebitval==fgbitval) || /*upper-right edge*/
11650: (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/
11651: (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left edge*/
11652: (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left edge*/
1.2 albertel 11653: /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
11654: if ( 1 ) { /* true to perform test */
11655: int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
11656: isbghorz = /* top or bottom edge is all bg */
11657: (nwbitval+nnbitval+nebitval == 3*bgbitval) || /* top edge bg */
11658: (swbitval+ssbitval+sebitval == 3*bgbitval) ; /* bottom edge bg */
11659: isfghorz = /* top or bottom edge is all fg */
11660: (nwbitval+nnbitval+nebitval == 3*fgbitval) || /* top edge fg */
11661: (swbitval+ssbitval+sebitval == 3*fgbitval) ; /* bottom edge fg */
11662: isbgvert = /* left or right edge is all bg */
11663: (nwbitval+wwbitval+swbitval == 3*bgbitval) || /* left edge bg */
11664: (nebitval+eebitval+sebitval == 3*bgbitval) ; /* right edge bg */
11665: isfgvert = /* left or right edge is all bg */
11666: (nwbitval+wwbitval+swbitval == 3*fgbitval) || /* left edge fg */
11667: (nebitval+eebitval+sebitval == 3*fgbitval) ; /* right edge fg */
11668: if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
11669: || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
11670: continue; /* don't antialias */
11671: } /* --- end-of-if(1) --- */
11672: /* --- check #gaps for checkerboard (added by j.forkosh) --- */
11673: if ( 0 ) { /* true to perform test */
11674: int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
11675: if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
11676: if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
11677: if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
11678: if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
11679: if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
11680: if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
11681: if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
11682: if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
11683: if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
11684: if ( ngaps<mingaps || ngaps>maxgaps ) continue;
11685: } /* --- end-of-if(1) --- */
1.1 albertel 11686: /* --- antialias if necessary --- */
11687: if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
11688: || (isfgalias && isfgedge) /* alias pixel surrounding fg */
11689: || (isbgedge && isfgedge) ) /* neighboring fg and bg pixel */
11690: {
11691: int aasumval = /* sum wts[]*bitmap[] */
11692: wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
11693: wts[3]*wwbitval + wts[4]*bitval + wts[5]*eebitval +
11694: wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
11695: double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
11696: aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
11697: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
1.3 ! albertel 11698: if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
! 11699: "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
! 11700: (isfirstaa?"aapnm algorithm":"aapnm"),
1.1 albertel 11701: irow,icol,imap, aawtval,aabyteval);
1.3 ! albertel 11702: isfirstaa = 0; }
1.1 albertel 11703: } /* --- end-of-if(isedge) --- */
11704: } /* --- end-of-for(irow,icol) --- */
11705: /* -------------------------------------------------------------------------
11706: Back to caller with gray-scale anti-aliased bytemap
11707: -------------------------------------------------------------------------- */
11708: /*end_of_job:*/
11709: return ( 1 );
11710: } /* --- end-of-function aapnm() --- */
11711:
11712:
11713: /* ==========================================================================
1.3 ! albertel 11714: * Function: aapnmlookup ( rp, bytemap, grayscale )
! 11715: * Purpose: calculates a lowpass anti-aliased bytemap
! 11716: * for rp->bitmap, with each byte 0...grayscale-1,
! 11717: * based on the pnmalias.c algorithm.
! 11718: * This version uses aagridnum() and aapatternnum() lookups
! 11719: * to interpret 3x3 lowpass pixel grids.
! 11720: * --------------------------------------------------------------------------
! 11721: * Arguments: rp (I) raster * to raster whose bitmap
! 11722: * is to be anti-aliased
! 11723: * bytemap (O) intbyte * to bytemap, calculated
! 11724: * by applying pnm-based filter to rp->bitmap,
! 11725: * and returned (as you'd expect) in 1-to-1
! 11726: * addressing correspondence with rp->bitmap
! 11727: * grayscale (I) int containing number of grayscales
! 11728: * to be calculated, 0...grayscale-1
! 11729: * (should typically be given as 256)
! 11730: * --------------------------------------------------------------------------
! 11731: * Returns: ( int ) 1=success, 0=any error
! 11732: * --------------------------------------------------------------------------
! 11733: * Notes: o Based on the pnmalias.c algorithm in the netpbm package
! 11734: * on sourceforge.
! 11735: * o This version uses aagridnum() and aapatternnum() lookups
! 11736: * to interpret 3x3 lowpass pixel grids.
! 11737: * ======================================================================= */
! 11738: /* --- entry point --- */
! 11739: int aapnmlookup (raster *rp, intbyte *bytemap, int grayscale)
! 11740: {
! 11741: /* -------------------------------------------------------------------------
! 11742: Allocations and Declarations
! 11743: -------------------------------------------------------------------------- */
! 11744: int width=rp->width, height=rp->height, /* width, height of raster */
! 11745: icol = 0, irow = 0, /* width, height indexes */
! 11746: imap = (-1); /* pixel index = icol + irow*width */
! 11747: int bgbitval=0, fgbitval=1; /* background, foreground bitval */
! 11748: int isfirstaa = 1; /*debugging switch signals 1st pixel*/
! 11749: int aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt,
! 11750: totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/
! 11751: int isfgalias = fgalias, /*(1) true to antialias fg bits */
! 11752: isfgonly = fgonly, /*(0) true to only antialias fg bits*/
! 11753: isbgalias = bgalias, /*(0) true to antialias bg bits */
! 11754: isbgonly = bgonly; /*(0) true to only antialias bg bits*/
! 11755: int gridnum=(-1), aagridnum(), /* grid# for 3x3 grid at irow,icol */
! 11756: patternum=(-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
! 11757: int aapatterns(); /* to antialias special patterns */
! 11758: /* ---
! 11759: * pattern number data
! 11760: * ------------------- */
! 11761: /* --- number of adjacent fg pixels set in pattern --- */
! 11762: static int nadjacents[] = { -1, /* #adjacent fg pixels for pattern */
! 11763: 0, 4, 0, 1, 4, 3, 1, 0, 1, 0, /* 1-10 */
! 11764: 2, 2, 3, 4, 3, 4, 2, 2, 1, 2, /* 11-20 */
! 11765: 1, 2, 1, 2, 0, 1, 3, 2, 3, 2, /* 21-30 */
! 11766: 3, 2, 3, 2, 4, 3, 1, 2, 2, 2, /* 31-40 */
! 11767: 2, 1, 2, 2, 3, 0, 3, 2, 2, 1, 4, /* 41-51 */
! 11768: -1 } ; /* --- end-of-nadjacents[] --- */
! 11769: /* --- number of corner fg pixels set in pattern --- */
! 11770: static int ncorners[] = { -1, /* #corner fg pixels for pattern */
! 11771: 0, 4, 1, 0, 3, 4, 1, 2, 1, 2, /* 1-10 */
! 11772: 0, 0, 3, 2, 3, 2, 4, 4, 2, 1, /* 11-20 */
! 11773: 2, 1, 2, 1, 3, 2, 0, 1, 2, 3, /* 21-30 */
! 11774: 2, 3, 2, 3, 1, 2, 4, 3, 2, 2, /* 31-40 */
! 11775: 2, 3, 2, 2, 1, 4, 1, 2, 2, 3, 0, /* 41-51 */
! 11776: -1 } ; /* --- end-of-ncorners[] --- */
! 11777: /* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */
! 11778: static int horzedges[] = { -1, /* 0,1,2 = horz bg,fg,both edge */
! 11779: 0, 1, 0, 0, 1, 1, 0, 0, 0, -1, /* 1-10 */
! 11780: 0, -1, 1, 1, 1, -1, 1, -1, 2, 0, /* 11-20 */
! 11781: -1, -1, -1, 0, -1, -1, -1, -1, 2, 1, /* 21-30 */
! 11782: -1, -1, -1, 1, -1, -1, -1, -1, 2, -1, /* 31-40 */
! 11783: -1, 1, 1, -1, -1, -1, 0, 0, -1, -1, -1, /* 41-51 */
! 11784: -1 } ; /* --- end-of-horzedges[] --- */
! 11785: /* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */
! 11786: static int vertedges[] = { -1, /* 0,1,2 = vert bg,fg,both edge */
! 11787: 0, 1, 0, 0, 1, 1, 0, -1, -1, -1, /* 1-10 */
! 11788: 0, 0, 1, -1, -1, -1, 1, 1, -1, -1, /* 11-20 */
! 11789: -1, 0, 0, 0, -1, -1, 0, -1, -1, -1, /* 21-30 */
! 11790: -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, /* 31-40 */
! 11791: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 41-51 */
! 11792: -1 } ; /* --- end-of-vertedges[] --- */
! 11793: /* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */
! 11794: static int diagedges[] = { -1, /* 0,1,2 = diag bg,fg,both edge */
! 11795: 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, /* 1-10 */
! 11796: 2, -1, 1, 1, 1, 1, 1, -1, 0, 2, /* 11-20 */
! 11797: 0, -1, 0, 2, 0, -1, 1, 1, 1, 1, /* 21-30 */
! 11798: 1, -1, 1, 2, 1, 1, -1, 1, 2, -1, /* 31-40 */
! 11799: 1, 0, -1, 2, 1, 0, 1, -1, 1, -1, 1, /* 41-51 */
! 11800: -1 } ; /* --- end-of-diagedges[] --- */
! 11801: /* -------------------------------------------------------------------------
! 11802: Calculate bytemap as 9-point weighted average over bitmap
! 11803: -------------------------------------------------------------------------- */
! 11804: for ( irow=0; irow<height; irow++ )
! 11805: for ( icol=0; icol<width; icol++ )
! 11806: {
! 11807: /* --- local allocations and declarations --- */
! 11808: int bitval=0, /* value of rp bit at irow,icol */
! 11809: isbgdiag=0, isfgdiag=0, /*does pixel border a bg or fg edge*/
! 11810: aabyteval=0; /* antialiased (or unchanged) value*/
! 11811: /* --- get gridnum and center bit value, init aabyteval --- */
! 11812: imap++; /* first set imap=icol + irow*width*/
! 11813: gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
! 11814: bitval = (gridnum&1); /* center bit set if gridnum odd */
! 11815: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
! 11816: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
! 11817: if ( gridnum<0 || gridnum>511 ) continue; /* gridnum out of bounds*/
! 11818: /* --- check if we're antialiasing this pixel --- */
! 11819: if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
! 11820: || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
! 11821: continue; /* leave default and do next bit */
! 11822: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
! 11823: patternum = aapatternnum(gridnum); /* look up pattern number */
! 11824: if ( patternum<1 || patternum>51 ) continue; /* some internal error */
! 11825: /* --- special pattern number processing --- */
! 11826: if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale))
! 11827: >= 0 ) { /* special processing for pattern */
! 11828: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
! 11829: continue; } /* and continue with next pixel */
! 11830: /* --- check for diagonal edges --- */
! 11831: isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/
! 11832: diagedges[patternum]==0 );
! 11833: isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/
! 11834: diagedges[patternum]==1 );
! 11835: /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
! 11836: if ( 1 ) { /* true to perform test */
! 11837: int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */
! 11838: horzedge=horzedges[patternum], vertedge=vertedges[patternum];
! 11839: isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */
! 11840: isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */
! 11841: isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */
! 11842: isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */
! 11843: if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
! 11844: || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
! 11845: continue; /* don't antialias */
! 11846: } /* --- end-of-if(1) --- */
! 11847: #if 0
! 11848: /* --- check #gaps for checkerboard (added by j.forkosh) --- */
! 11849: if ( 0 ) { /* true to perform test */
! 11850: int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
! 11851: if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
! 11852: if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
! 11853: if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
! 11854: if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
! 11855: if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
! 11856: if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
! 11857: if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
! 11858: if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
! 11859: if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
! 11860: if ( ngaps<mingaps || ngaps>maxgaps ) continue;
! 11861: } /* --- end-of-if(1) --- */
! 11862: #endif
! 11863: /* --- antialias if necessary --- */
! 11864: if ( (isbgalias && isbgdiag) /* alias pixel surrounding bg */
! 11865: || (isfgalias && isfgdiag) /* alias pixel surrounding fg */
! 11866: || (isbgdiag && isfgdiag) ) /* neighboring fg and bg pixel */
! 11867: {
! 11868: int aasumval = /* sum wts[]*bitmap[] */
! 11869: aacenterwt*bitval + /* apply centerwt to center pixel */
! 11870: aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */
! 11871: aacornerwt*ncorners[patternum]; /* and corners */
! 11872: double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
! 11873: aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
! 11874: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
! 11875: if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
! 11876: "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d",
! 11877: (isfirstaa?"aapnmlookup algorithm":"aapnm"),
! 11878: irow,icol,imap, aawtval,aabyteval);
! 11879: if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */
! 11880: else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum);
! 11881: isfirstaa = 0; }
! 11882: } /* --- end-of-if(isedge) --- */
! 11883: } /* --- end-of-for(irow,icol) --- */
! 11884: /* -------------------------------------------------------------------------
! 11885: Back to caller with gray-scale anti-aliased bytemap
! 11886: -------------------------------------------------------------------------- */
! 11887: /*end_of_job:*/
! 11888: return ( 1 );
! 11889: } /* --- end-of-function aapnmlookup() --- */
! 11890:
! 11891:
! 11892: /* ==========================================================================
! 11893: * Function: aapatterns ( rp, irow, icol, gridnum, patternum, grayscale )
! 11894: * Purpose: For patterns requireing special processing,
! 11895: * calculates anti-aliased value for pixel at irow,icol,
! 11896: * whose surrounding 3x3 pixel grid is coded by gridnum
! 11897: * (which must correspond to a pattern requiring special
! 11898: * processing).
! 11899: * --------------------------------------------------------------------------
! 11900: * Arguments: rp (I) raster * to raster whose bitmap
! 11901: * is to be anti-aliased
! 11902: * irow (I) int containing row, 0...height-1,
! 11903: * of pixel to be antialiased
! 11904: * icol (I) int containing col, 0...width-1,
! 11905: * of pixel to be antialiased
! 11906: * gridnum (I) int containing 0...511 corresponding to
! 11907: * 3x3 pixel grid surrounding irow,icol
! 11908: * patternum (I) int containing 1...51 pattern# of
! 11909: * the 3x3 grid surrounding irow,icol
! 11910: * grayscale (I) int containing number of grayscales
! 11911: * to be calculated, 0...grayscale-1
! 11912: * (should typically be given as 256)
! 11913: * --------------------------------------------------------------------------
! 11914: * Returns: ( int ) 0...grayscale-1 for success,
! 11915: * -1 = error, or no special processing required
! 11916: * --------------------------------------------------------------------------
! 11917: * Notes: o
! 11918: * ======================================================================= */
! 11919: /* --- entry point --- */
! 11920: int aapatterns (raster *rp, int irow, int icol,
! 11921: int gridnum, int patternum, int grayscale)
! 11922: {
! 11923: /* -------------------------------------------------------------------------
! 11924: Allocations and Declarations
! 11925: -------------------------------------------------------------------------- */
! 11926: int aaval = (-1); /* antialiased value returned */
! 11927: int iscenter = (gridnum&1); /* true if center pixel set/black */
! 11928: int aapatternnum(), /* if patternum not supplied */
! 11929: aapattern1124(), /* routine for patterns #11,24 */
! 11930: aapattern19(), /* special routine for pattern #19 */
! 11931: aapattern20(), /* special routine for pattern #20 */
! 11932: aapattern39(); /* special routine for pattern #39 */
! 11933: /* -------------------------------------------------------------------------
! 11934: special pattern number processing
! 11935: -------------------------------------------------------------------------- */
! 11936: if ( 1 ) {
! 11937: if ( patternum < 1 ) /* pattern# not supplied by caller */
! 11938: patternum = aapatternnum(gridnum); /* so look it up ourselves */
! 11939: switch ( patternum ) {
! 11940: default: break; /* no special processing */
! 11941: case 11:
! 11942: case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break;
! 11943: case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break;
! 11944: case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break;
! 11945: case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break;
! 11946: /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */
! 11947: case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */
! 11948: } /* --- end-of-switch(patternum) --- */
! 11949: } /* --- end-of-if() --- */
! 11950: return ( aaval ); /* return antialiased val to caller*/
! 11951: } /* --- end-of-function aapatterns() --- */
! 11952:
! 11953:
! 11954: /* ==========================================================================
! 11955: * Function: aapattern1124 ( rp, irow, icol, gridnum, grayscale )
! 11956: * Purpose: calculates anti-aliased value for pixel at irow,icol,
! 11957: * whose surrounding 3x3 pixel grid is coded by gridnum
! 11958: * (which must correspond to pattern #11 or #24).
! 11959: * --------------------------------------------------------------------------
! 11960: * Arguments: rp (I) raster * to raster whose bitmap
! 11961: * is to be anti-aliased
! 11962: * irow (I) int containing row, 0...height-1,
! 11963: * of pixel to be antialiased
! 11964: * icol (I) int containing col, 0...width-1,
! 11965: * of pixel to be antialiased
! 11966: * gridnum (I) int containing 0...511 corresponding to
! 11967: * 3x3 pixel grid surrounding irow,icol
! 11968: * grayscale (I) int containing number of grayscales
! 11969: * to be calculated, 0...grayscale-1
! 11970: * (should typically be given as 256)
! 11971: * --------------------------------------------------------------------------
! 11972: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
! 11973: * --------------------------------------------------------------------------
! 11974: * Notes: o Handles the eight gridnum's
! 11975: * (gridnum/2 shown to eliminate irrelevant low-order bit)
! 11976: * --- --- -*- -*-
! 11977: * --* = 10 *-- = 18 --* = 72 *-- = 80 (pattern$11)
! 11978: * -*- -*- --- ---
! 11979: *
! 11980: * --- --- -** **-
! 11981: * --* = 11 *-- = 22 --* = 104 *-- = 208 (pattern$24)
! 11982: * -** **- --- ---
! 11983: * o For black * center pixel, using grid#10 as an example,
! 11984: * pixel stays --- antialiased ---*
! 11985: * black if -*** if part of -**
! 11986: * part of a -*- a diagonal -*-
! 11987: * corner, eg, * line, eg, *
! 11988: * ======================================================================= */
! 11989: /* --- entry point --- */
! 11990: int aapattern1124 (raster *rp, int irow, int icol,
! 11991: int gridnum, int grayscale)
! 11992: {
! 11993: /* -------------------------------------------------------------------------
! 11994: Allocations and Declarations
! 11995: -------------------------------------------------------------------------- */
! 11996: int aaval = (-1); /* antialiased value returned */
! 11997: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
! 11998: int patternum = 24; /* init for pattern#24 default */
! 11999: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
! 12000: int width=rp->width, height=rp->height; /* width, height of raster */
! 12001: int jrow=irow, jcol=icol; /* corner or diagonal row,col */
! 12002: int vertcornval=0, horzcornval=0, /* vertical, horizontal corner bits*/
! 12003: topdiagval=0, botdiagval=0, /* upper,lower diagonal pixel bits */
! 12004: cornval=0, diagval=0; /* vert+horzcorn, top+botdiag */
! 12005: int hdirection=99, vdirection=99, /* horz,vert corner direction */
! 12006: hturn=99,vturn=99, aafollowline(); /* follow corner till turns */
! 12007: /* -------------------------------------------------------------------------
! 12008: Check corner and diagonal pixels
! 12009: -------------------------------------------------------------------------- */
! 12010: if ( 0 ) goto end_of_job; /* true to turn off pattern1124 */
! 12011: switch ( gridnum/2 ) { /* check pattern#11,24 corner, diag*/
! 12012: default: goto end_of_job; /* not a pattern#11,24 gridnum */
! 12013: case 10: patternum=11; case 11:
! 12014: hdirection = 2; vdirection = -1; /* directions to follow corner */
! 12015: if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
! 12016: vertcornval = getlongbit(bitmap,(icol+jrow*width));
! 12017: if ( (icol-1) >= 0 ) /* lower diag left of center */
! 12018: botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
! 12019: if ( (jcol=icol+2) < width ) { /* horz corner right of center */
! 12020: horzcornval = getlongbit(bitmap,(jcol+irow*width));
! 12021: if ( (irow-1) >= 0 ) /* upper diag above center */
! 12022: topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
! 12023: break;
! 12024: case 18: patternum=11; case 22:
! 12025: hdirection = -2; vdirection = -1; /* directions to follow corner */
! 12026: if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
! 12027: vertcornval = getlongbit(bitmap,(icol+jrow*width));
! 12028: if ( (icol+1) < width ) /* lower diag right of center */
! 12029: botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
! 12030: if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
! 12031: horzcornval = getlongbit(bitmap,(jcol+irow*width));
! 12032: if ( (irow-1) >= 0 ) /* upper diag above center */
! 12033: topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
! 12034: break;
! 12035: case 72: patternum=11; case 104:
! 12036: hdirection = 2; vdirection = 1; /* directions to follow corner */
! 12037: if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
! 12038: vertcornval = getlongbit(bitmap,(icol+jrow*width));
! 12039: if ( (icol-1) >= 0 ) /* upper diag left of center */
! 12040: topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
! 12041: if ( (jcol=icol+2) < width ) { /* horz corner right of center */
! 12042: horzcornval = getlongbit(bitmap,(jcol+irow*width));
! 12043: if ( (irow+1) < height ) /* lower diag below center */
! 12044: botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
! 12045: break;
! 12046: case 80: patternum=11; case 208:
! 12047: hdirection = -2; vdirection = 1; /* directions to follow corner */
! 12048: if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
! 12049: vertcornval = getlongbit(bitmap,(icol+jrow*width));
! 12050: if ( (icol+1) < width ) /* upper diag right of center */
! 12051: topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
! 12052: if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
! 12053: horzcornval = getlongbit(bitmap,(jcol+irow*width));
! 12054: if ( (irow+1) < height ) /* lower diag below center */
! 12055: botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
! 12056: break;
! 12057: } /* --- end-of-switch(gridnum/2) --- */
! 12058: cornval = vertcornval+horzcornval; /* 0=no corner bits, 1, 2=both */
! 12059: diagval = topdiagval+botdiagval; /* 0=no diag bits, 1, 2=both */
! 12060: /* -------------------------------------------------------------------------
! 12061: Handle white center
! 12062: -------------------------------------------------------------------------- */
! 12063: if ( 1 && !iscenter ) { aaval = (patternum==11?51:64); goto end_of_job; }
! 12064: /* -------------------------------------------------------------------------
! 12065: Handle black center
! 12066: -------------------------------------------------------------------------- */
! 12067: if ( diagval > 1 ) aaval = ( patternum==24? 255:191 );
! 12068: else {
! 12069: hturn = aafollowline(rp,irow,icol,hdirection);
! 12070: vturn = aafollowline(rp,irow,icol,vdirection);
! 12071: if ( vturn*hdirection < 0 && hturn*vdirection < 0 )
! 12072: aaval = ( patternum==24? 255:191 );
! 12073: else aaval = grayscale-1; } /* actual corner */
! 12074: /* -------------------------------------------------------------------------
! 12075: Back to caller with grayscale antialiased value for pixel at irow,icol
! 12076: -------------------------------------------------------------------------- */
! 12077: end_of_job:
! 12078: if ( aaval >= 0 ) /* have antialiasing result */
! 12079: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
! 12080: "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, "
! 12081: "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n",
! 12082: irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval,
! 12083: vdirection,hdirection, vturn,hturn, aaval);
! 12084: return ( aaval ); /* back with antialiased value */
! 12085: } /* --- end-of-function aapattern1124() --- */
! 12086:
! 12087:
! 12088: /* ==========================================================================
! 12089: * Function: aapattern19 ( rp, irow, icol, gridnum, grayscale )
! 12090: * Purpose: calculates anti-aliased value for pixel at irow,icol,
! 12091: * whose surrounding 3x3 pixel grid is coded by gridnum
! 12092: * (which must correspond to pattern #19).
! 12093: * --------------------------------------------------------------------------
! 12094: * Arguments: rp (I) raster * to raster whose bitmap
! 12095: * is to be anti-aliased
! 12096: * irow (I) int containing row, 0...height-1,
! 12097: * of pixel to be antialiased
! 12098: * icol (I) int containing col, 0...width-1,
! 12099: * of pixel to be antialiased
! 12100: * gridnum (I) int containing 0...511 corresponding to
! 12101: * 3x3 pixel grid surrounding irow,icol
! 12102: * grayscale (I) int containing number of grayscales
! 12103: * to be calculated, 0...grayscale-1
! 12104: * (should typically be given as 256)
! 12105: * --------------------------------------------------------------------------
! 12106: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
! 12107: * --------------------------------------------------------------------------
! 12108: * Notes: o Handles the four gridnum's
! 12109: * (gridnum/2 shown to eliminate irrelevant low-order bit)
! 12110: * --- --* *-- ***
! 12111: * --- = 7 --* = 41 *-- = 148 --- = 224
! 12112: * *** --* *-- ---
! 12113: * ======================================================================= */
! 12114: /* --- entry point --- */
! 12115: int aapattern19 (raster *rp, int irow, int icol,
! 12116: int gridnum, int grayscale)
! 12117: {
! 12118: /* -------------------------------------------------------------------------
! 12119: Allocations and Declarations
! 12120: -------------------------------------------------------------------------- */
! 12121: int aaval = (-1); /* antialiased value returned */
! 12122: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
! 12123: int orientation = 1, /* 1=vertical, 2=horizontal */
! 12124: jrow=irow, jcol=icol; /* middle pixel of *** line */
! 12125: int turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */
! 12126: /* -------------------------------------------------------------------------
! 12127: Initialization and calculation of antialiased value
! 12128: -------------------------------------------------------------------------- */
! 12129: /* --- check input -- */
! 12130: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
! 12131: /* --- set params --- */
! 12132: switch ( gridnum/2 ) { /* check pattern#19 orientation */
! 12133: default: goto end_of_job; /* not a pattern#19 gridnum */
! 12134: case 7: orientation=2; jrow++; break;
! 12135: case 41: orientation=1; jcol++; break;
! 12136: case 148: orientation=1; jcol--; break;
! 12137: case 224: orientation=2; jrow--; break;
! 12138: } /* --- end-of-switch(gridnum/2) --- */
! 12139: /* --- get turns in both directions --- */
! 12140: if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job;
! 12141: if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job;
! 12142: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
! 12143: /* --- weight pixel --- */
! 12144: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
! 12145: /* -------------------------------------------------------------------------
! 12146: Back to caller with grayscale antialiased value for pixel at irow,icol
! 12147: -------------------------------------------------------------------------- */
! 12148: end_of_job:
! 12149: if ( aaval >= 0 ) /* have antialiasing result */
! 12150: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
! 12151: "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n",
! 12152: irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval);
! 12153: return ( aaval ); /* back with antialiased value */
! 12154: } /* --- end-of-function aapattern19() --- */
! 12155:
! 12156:
! 12157: /* ==========================================================================
! 12158: * Function: aapattern20 ( rp, irow, icol, gridnum, grayscale )
! 12159: * Purpose: calculates anti-aliased value for pixel at irow,icol,
! 12160: * whose surrounding 3x3 pixel grid is coded by gridnum
! 12161: * (which must correspond to pattern #20).
! 12162: * --------------------------------------------------------------------------
! 12163: * Arguments: rp (I) raster * to raster whose bitmap
! 12164: * is to be anti-aliased
! 12165: * irow (I) int containing row, 0...height-1,
! 12166: * of pixel to be antialiased
! 12167: * icol (I) int containing col, 0...width-1,
! 12168: * of pixel to be antialiased
! 12169: * gridnum (I) int containing 0...511 corresponding to
! 12170: * 3x3 pixel grid surrounding irow,icol
! 12171: * grayscale (I) int containing number of grayscales
! 12172: * to be calculated, 0...grayscale-1
! 12173: * (should typically be given as 256)
! 12174: * --------------------------------------------------------------------------
! 12175: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
! 12176: * --------------------------------------------------------------------------
! 12177: * Notes: o Handles the eight gridnum's
! 12178: * (gridnum/2 shown to eliminate irrelevant low-order bit)
! 12179: * --- --- --* -*-
! 12180: * --* = 14 *-- = 19 --* = 42 --* = 73
! 12181: * **- -** -*- --*
! 12182: *
! 12183: * -*- -** *-- **-
! 12184: * *-- = 84 *-- = 112 *-- = 146 --* = 200
! 12185: * *-- --- -*- ---
! 12186: * ======================================================================= */
! 12187: /* --- entry point --- */
! 12188: int aapattern20 (raster *rp, int irow, int icol,
! 12189: int gridnum, int grayscale)
! 12190: {
! 12191: /* -------------------------------------------------------------------------
! 12192: Allocations and Declarations
! 12193: -------------------------------------------------------------------------- */
! 12194: int aaval = (-1); /* antialiased value returned */
! 12195: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
! 12196: int direction = 1, /* direction to follow ** line */
! 12197: jrow1=irow, jcol1=icol, /* coords of * */
! 12198: jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
! 12199: int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
! 12200: /* -------------------------------------------------------------------------
! 12201: Initialization and calculation of antialiased value
! 12202: -------------------------------------------------------------------------- */
! 12203: /* --- check input -- */
! 12204: if ( 1 ) goto end_of_job; /* don't want this one */
! 12205: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
! 12206: /* --- set params --- */
! 12207: switch ( gridnum/2 ) { /* check pattern#20 orientation */
! 12208: default: goto end_of_job; /* not a pattern#20 gridnum */
! 12209: case 14: direction=-2; jcol1++; jrow2++; break;
! 12210: case 19: direction=2; jcol1--; jrow2++; break;
! 12211: case 42: direction=1; jrow1++; jcol2++; break;
! 12212: case 73: direction=-1; jrow1--; jcol2++; break;
! 12213: case 84: direction=-1; jrow1--; jcol2--; break;
! 12214: case 112: direction=2; jcol1--; jrow2--; break;
! 12215: case 146: direction=1; jrow1++; jcol2--; break;
! 12216: case 200: direction=-2; jcol1++; jrow2--; break;
! 12217: } /* --- end-of-switch(gridnum/2) --- */
! 12218: /* --- get turns in both directions --- */
! 12219: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job;
! 12220: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job;
! 12221: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
! 12222: /* --- weight pixel --- */
! 12223: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
! 12224: /* -------------------------------------------------------------------------
! 12225: Back to caller with grayscale antialiased value for pixel at irow,icol
! 12226: -------------------------------------------------------------------------- */
! 12227: end_of_job:
! 12228: if ( aaval >= 0 ) /* have antialiasing result */
! 12229: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
! 12230: "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
! 12231: irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
! 12232: return ( aaval ); /* back with antialiased value */
! 12233: } /* --- end-of-function aapattern20() --- */
! 12234:
! 12235:
! 12236: /* ==========================================================================
! 12237: * Function: aapattern39 ( rp, irow, icol, gridnum, grayscale )
! 12238: * Purpose: calculates anti-aliased value for pixel at irow,icol,
! 12239: * whose surrounding 3x3 pixel grid is coded by gridnum
! 12240: * (which must correspond to pattern #39).
! 12241: * --------------------------------------------------------------------------
! 12242: * Arguments: rp (I) raster * to raster whose bitmap
! 12243: * is to be anti-aliased
! 12244: * irow (I) int containing row, 0...height-1,
! 12245: * of pixel to be antialiased
! 12246: * icol (I) int containing col, 0...width-1,
! 12247: * of pixel to be antialiased
! 12248: * gridnum (I) int containing 0...511 corresponding to
! 12249: * 3x3 pixel grid surrounding irow,icol
! 12250: * grayscale (I) int containing number of grayscales
! 12251: * to be calculated, 0...grayscale-1
! 12252: * (should typically be given as 256)
! 12253: * --------------------------------------------------------------------------
! 12254: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
! 12255: * --------------------------------------------------------------------------
! 12256: * Notes: o Handles the eight gridnum's
! 12257: * (gridnum/2 shown to eliminate irrelevant low-order bit)
! 12258: * --- --- --* -**
! 12259: * --* = 15 *-- = 23 --* = 43 --* = 105
! 12260: * *** *** -** --*
! 12261: *
! 12262: * **- *** *-- ***
! 12263: * *-- = 212 *-- = 240 *-- = 150 --* = 232
! 12264: * *-- --- **- ---
! 12265: * ======================================================================= */
! 12266: /* --- entry point --- */
! 12267: int aapattern39 (raster *rp, int irow, int icol,
! 12268: int gridnum, int grayscale)
! 12269: {
! 12270: /* -------------------------------------------------------------------------
! 12271: Allocations and Declarations
! 12272: -------------------------------------------------------------------------- */
! 12273: int aaval = (-1); /* antialiased value returned */
! 12274: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
! 12275: int direction = 1, /* direction to follow ** line */
! 12276: jrow1=irow, jcol1=icol, /* coords of * */
! 12277: jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
! 12278: int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
! 12279: /* -------------------------------------------------------------------------
! 12280: Initialization and calculation of antialiased value
! 12281: -------------------------------------------------------------------------- */
! 12282: /* --- check input -- */
! 12283: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
! 12284: /* --- set params --- */
! 12285: switch ( gridnum/2 ) { /* check pattern#39 orientation */
! 12286: default: goto end_of_job; /* not a pattern#39 gridnum */
! 12287: case 15: direction=-2; jcol1++; jrow2++; break;
! 12288: case 23: direction=2; jcol1--; jrow2++; break;
! 12289: case 43: direction=1; jrow1++; jcol2++; break;
! 12290: case 105: direction=-1; jrow1--; jcol2++; break;
! 12291: case 212: direction=-1; jrow1--; jcol2--; break;
! 12292: case 240: direction=2; jcol1--; jrow2--; break;
! 12293: case 150: direction=1; jrow1++; jcol2--; break;
! 12294: case 232: direction=-2; jcol1++; jrow2--; break;
! 12295: } /* --- end-of-switch(gridnum/2) --- */
! 12296: /* --- get turns directions (tunr1==1 signals inside corner) --- */
! 12297: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 )
! 12298: { aaval=0; goto end_of_job; }
! 12299: if ( 1 ) goto end_of_job; /* stop here for now */
! 12300: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job;
! 12301: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
! 12302: /* --- weight pixel --- */
! 12303: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
! 12304: /* -------------------------------------------------------------------------
! 12305: Back to caller with grayscale antialiased value for pixel at irow,icol
! 12306: -------------------------------------------------------------------------- */
! 12307: end_of_job:
! 12308: if ( aaval >= 0 ) /* have antialiasing result */
! 12309: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
! 12310: "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
! 12311: irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
! 12312: return ( aaval ); /* back with antialiased value */
! 12313: } /* --- end-of-function aapattern39() --- */
! 12314:
! 12315:
! 12316: /* ==========================================================================
! 12317: * Function: aafollowline ( rp, irow, icol, direction )
! 12318: * Purpose: starting with pixel at irow,icol, moves in
! 12319: * specified direction looking for a "turn"
! 12320: * --------------------------------------------------------------------------
! 12321: * Arguments: rp (I) raster * to raster containing pixel image
! 12322: * irow (I) int containing row, 0...height-1,
! 12323: * of first pixel
! 12324: * icol (I) int containing col, 0...width-1,
! 12325: * of first pixel
! 12326: * direction (I) int containing +1 to follow line up/north
! 12327: * (decreasing irow), -1 to follow line
! 12328: * down/south (increasing irow), +2 to follow
! 12329: * line right/east (increasing icol),
! 12330: * -2 to follow line left/west (decreasing icol)
! 12331: * --------------------------------------------------------------------------
! 12332: * Returns: ( int ) #rows or #cols traversed prior to turn,
! 12333: * or 0 if no turn detected (or for any error).
! 12334: * Sign is + if turn direction is right/east or
! 12335: * up/north, or is - if left/west or down/south.
! 12336: * --------------------------------------------------------------------------
! 12337: * Notes: o Here are some examples illustrating turn detection in
! 12338: * +2 (right/east) direction. Turns in other directions
! 12339: * are detected similarly/symmetrically. * denotes black
! 12340: * bits (usually fg), - denotes white bits (usually bg),
! 12341: * and ? denotes "don't care" bit (won't affect outcome).
! 12342: * Arrow --> points to start pixel denoted by irow,icol.
! 12343: *
! 12344: * *??? -??? turn=0 (no turn) is returned
! 12345: * -->*??? or -->-??? because the start pixel isn't
! 12346: * *??? -??? on an edge to begin with
! 12347: *
! 12348: * ---- **-- turn=0 returned because the
! 12349: * -->***- or -->***- line ends abruptly without
! 12350: * ---- ---- turning (even the second case)
! 12351: *
! 12352: * ---* ---* turn=0 returned because the
! 12353: * -->***- or -->**** line forms a Y or T rather
! 12354: * ---* ---* than turning
! 12355: *
! 12356: * ***- **** turn=+3 returned
! 12357: * -->***- or -->***- (outside corner)
! 12358: * ---- ----
! 12359: *
! 12360: * ***** ****- turn=-4 returned
! 12361: * -->***** or -->****- (inside corner)
! 12362: * ----* ----*
! 12363: *
! 12364: * ----* ----* turn=+4 returned
! 12365: * -->****- or -->***** (outside or inside corner)
! 12366: * ----- -----
! 12367: * ======================================================================= */
! 12368: /* --- entry point --- */
! 12369: int aafollowline (raster *rp, int irow, int icol, int direction)
! 12370: {
! 12371: /* -------------------------------------------------------------------------
! 12372: Allocations and Declarations
! 12373: -------------------------------------------------------------------------- */
! 12374: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
! 12375: int width=rp->width, height=rp->height; /* width, height of raster */
! 12376: int drow=0, dcol=0, /* delta row,col to follow line */
! 12377: jrow=irow, jcol=icol; /* current row,col following line */
! 12378: int bitval=1, /* value of rp bit at irow,icol */
! 12379: fgval=1, bgval=0, /* "fg" is whatever bitval is */
! 12380: bitminus=0, bitplus=0; /* value of left/down, right/up bit*/
! 12381: int isline=1, isedge=0; /*isline signals one-pixel wide line*/
! 12382: int turn = 0, /* detected turn back to caller */
! 12383: maxturn = maxfollow; /* don't follow more than max pixels*/
! 12384: /* -------------------------------------------------------------------------
! 12385: Initialization
! 12386: -------------------------------------------------------------------------- */
! 12387: /* --- check input --- */
! 12388: if ( irow<0 || irow>=height /* irow out-of-bounds */
! 12389: || icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
! 12390: /* --- starting bit -- see if we're following a fg (usual), or bg line --- */
! 12391: bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
! 12392: fgval = bitval; bgval = (1-bitval); /* define "fg" as whatever bitval is*/
! 12393: /* --- set drow,dcol corresponding to desired direction --- */
! 12394: switch ( direction ) { /* determine drow,dcol for direction*/
! 12395: default: goto end_of_job; /* unrecognized direction arg */
! 12396: case 1: drow = (-1); break; /* follow line up/north */
! 12397: case -1: drow = 1; break; /* down/south */
! 12398: case 2: dcol = 1; break; /* right/east */
! 12399: case -2: dcol = (-1); break; } /* left/west */
! 12400: /* --- set bitminus and bitplus --- */
! 12401: if ( drow == 0 ) { /* we're following line right/left */
! 12402: if ( irow < height ) /* there's a pixel below current */
! 12403: bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */
! 12404: if ( irow > 0 ) /* there's a pixel above current */
! 12405: bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */
! 12406: if ( dcol == 0 ) { /* we're following line up/down */
! 12407: if ( icol < width ) /* there's a pixel to the right */
! 12408: bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */
! 12409: if ( icol > 0 ) /* there's a pixel to the left */
! 12410: bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */
! 12411: /* --- check for lack of line to follow --- */
! 12412: if ( bitval == bitplus /* starting pixel same as above */
! 12413: && bitval == bitminus ) /* and below (or right and left) */
! 12414: goto end_of_job; /* so there's no line to follow */
! 12415: /* --- set isline and isedge (already initted for isline) --- */
! 12416: if ( bitval == bitplus ) /* starting pixel same as above */
! 12417: { isedge = (-1); isline = 0; } /* so we're at an edge below */
! 12418: if ( bitval == bitminus ) /* starting pixel same as below */
! 12419: { isedge = 1; isline = 0; } /* so we're at an edge above */
! 12420: /* -------------------------------------------------------------------------
! 12421: follow line
! 12422: -------------------------------------------------------------------------- */
! 12423: while ( 1 ) { /* until turn found (or max) */
! 12424: /* --- local allocations and declarations --- */
! 12425: int dbitval=0, /* value of bit at jrow,jcol */
! 12426: dbitminus=0, dbitplus=0; /* value of left/down, right/up bit*/
! 12427: /* --- bump pixel count and indexes; check for max or end-of-raster --- */
! 12428: turn++; /* bump #pixels followed */
! 12429: jrow += drow; jcol += dcol; /* indexes of next pixel to check */
! 12430: if ( turn > maxturn /* already followed max #pixels */
! 12431: || jrow<0 || jrow>=height /* or jrow past end-of-raster */
! 12432: || jcol<0 || jcol>=width ) /* or jcol past end-of-raster */
! 12433: { turn = 0; goto end_of_job; } /* so quit without finding a turn */
! 12434: /* --- set current bit (dbitval) --- */
! 12435: dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/
! 12436: /* --- set dbitminus and dbitplus --- */
! 12437: if ( drow == 0 ) { /* we're following line right/left */
! 12438: if ( irow < height ) /* there's a pixel below current */
! 12439: dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */
! 12440: if ( irow > 0 ) /* there's a pixel above current */
! 12441: dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */
! 12442: if ( dcol == 0 ) { /* we're following line up/down */
! 12443: if ( icol < width ) /* there's a pixel to the right */
! 12444: dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */
! 12445: if ( icol > 0 ) /* there's a pixel to the left */
! 12446: dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */
! 12447: /* --- first check for abrupt end-of-line, or for T or Y --- */
! 12448: if ( isline != 0 ) /* abrupt end or T,Y must be a line*/
! 12449: if ( ( bgval == dbitval /* end-of-line if pixel flips to bg*/
! 12450: && bgval == dbitplus /* and bg same as above pixel */
! 12451: && bgval == dbitminus ) /* and below (or right and left) */
! 12452: || ( fgval == dbitplus /* T or Y if fg same as above pixel*/
! 12453: && fgval == dbitminus ) ) /* and below (or right and left) */
! 12454: { turn = 0; goto end_of_job; } /* so we're at a T or Y */
! 12455: /* --- check for turning line --- */
! 12456: if ( isline != 0 ) { /* turning line must be a line */
! 12457: if ( fgval == dbitminus ) /* turning down */
! 12458: { turn = -turn; goto end_of_job; } /* so return negative turn */
! 12459: else if ( fgval == dbitplus ) /* turning up */
! 12460: goto end_of_job; } /* so return positive turn */
! 12461: /* --- check for inside corner at edge --- */
! 12462: if ( isedge != 0 ) { /* inside corner must be a edge */
! 12463: if ( isedge < 0 && fgval == bitminus ) /* corner below */
! 12464: { turn = -turn; goto end_of_job; } /* so return negative turn */
! 12465: if ( isedge > 0 && fgval == bitplus ) /* corner above */
! 12466: goto end_of_job; } /* so return positive turn */
! 12467: /* --- check for abrupt end at edge --- */
! 12468: if ( isedge != 0 /* abrupt edge end must be an edge */
! 12469: && fgval == dbitval ) /* and line must not end */
! 12470: if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */
! 12471: || (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */
! 12472: { turn = 0; goto end_of_job; } /* so edge ended abruptly */
! 12473: /* --- check for outside corner at edge --- */
! 12474: if ( isedge != 0 /* outside corner must be a edge */
! 12475: && bgval == dbitval ) { /* and line must end */
! 12476: if ( isedge > 0 ) turn = -turn; /* outside turn down from edge above*/
! 12477: goto end_of_job; }
! 12478: } /* --- end-of-while(1) --- */
! 12479: /* -------------------------------------------------------------------------
! 12480: Back to caller with #rows or #cols traversed, and direction of detected turn
! 12481: -------------------------------------------------------------------------- */
! 12482: end_of_job:
! 12483: if ( msglevel>=99 && msgfp!=NULL ) /* debugging/diagnostic output */
! 12484: fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n",
! 12485: irow,icol,direction,turn);
! 12486: return ( turn );
! 12487: } /* --- end-of-function aafollowline() --- */
! 12488:
! 12489:
! 12490: /* ==========================================================================
! 12491: * Function: aagridnum ( rp, irow, icol )
! 12492: * Purpose: calculates gridnum, 0-511 (see Notes below),
! 12493: * for 3x3 grid centered at irow,icol
! 12494: * --------------------------------------------------------------------------
! 12495: * Arguments: rp (I) raster * to raster containing
! 12496: * bitmap image (to be anti-aliased)
! 12497: * irow (I) int containing row, 0...height-1,
! 12498: * at center of 3x3 grid
! 12499: * icol (I) int containing col, 0...width-1,
! 12500: * at center of 3x3 grid
! 12501: * --------------------------------------------------------------------------
! 12502: * Returns: ( int ) 0-511 grid number, or -1=any error
! 12503: * --------------------------------------------------------------------------
! 12504: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
! 12505: * whose bit positions (and corresponding values) in gridnum are
! 12506: * 876 256 128 64
! 12507: * 504 = 32 1 16
! 12508: * 321 8 4 2
! 12509: * Thus, for example (*=pixel set/black, -=pixel not set/white),
! 12510: * *-- *-- -** (note that 209 is the
! 12511: * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset,
! 12512: * --* *** --- of 302)
! 12513: * o A set pixel is considered black, an unset pixel considered
! 12514: * white.
! 12515: * ======================================================================= */
! 12516: /* --- entry point --- */
! 12517: int aagridnum (raster *rp, int irow, int icol)
! 12518: {
! 12519: /* -------------------------------------------------------------------------
! 12520: Allocations and Declarations
! 12521: -------------------------------------------------------------------------- */
! 12522: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
! 12523: int width=rp->width, height=rp->height, /* width, height of raster */
! 12524: imap = icol + irow*width; /* pixel index = icol + irow*width */
! 12525: int bitval=0, /* value of rp bit at irow,icol */
! 12526: nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
! 12527: ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0, /*compass pt names*/
! 12528: gridnum = (-1); /* grid# 0-511 for above 9 bits */
! 12529: /* -------------------------------------------------------------------------
! 12530: check input
! 12531: -------------------------------------------------------------------------- */
! 12532: if ( irow<0 || irow>=height /* irow out-of-bounds */
! 12533: || icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
! 12534: /* -------------------------------------------------------------------------
! 12535: get the 9 bits comprising the 3x3 grid centered at irow,icol
! 12536: -------------------------------------------------------------------------- */
! 12537: /* --- get center bit --- */
! 12538: bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
! 12539: /* --- get 8 surrounding bits --- */
! 12540: if ( irow > 0 ) /* nn (north) bit available */
! 12541: nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
! 12542: if ( irow < height-1 ) /* ss (south) bit available */
! 12543: ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
! 12544: if ( icol > 0 ) /* ww (west) bit available */
! 12545: { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
! 12546: if ( irow > 0 ) /* nw bit available */
! 12547: nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
! 12548: if ( irow < height-1 ) /* sw bit available */
! 12549: swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
! 12550: if ( icol < width-1 ) /* ee (east) bit available */
! 12551: { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
! 12552: if ( irow > 0 ) /* ne bit available */
! 12553: nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
! 12554: if ( irow < height-1 ) /* se bit available */
! 12555: sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
! 12556: /* --- set gridnum --- */
! 12557: gridnum = 0; /* clear all bits */
! 12558: if ( bitval ) gridnum = 1; /* set1bit(gridnum,0); */
! 12559: if ( nwbitval ) gridnum += 256; /* set1bit(gridnum,8); */
! 12560: if ( nnbitval ) gridnum += 128; /* set1bit(gridnum,7); */
! 12561: if ( nebitval ) gridnum += 64; /* set1bit(gridnum,6); */
! 12562: if ( wwbitval ) gridnum += 32; /* set1bit(gridnum,5); */
! 12563: if ( eebitval ) gridnum += 16; /* set1bit(gridnum,4); */
! 12564: if ( swbitval ) gridnum += 8; /* set1bit(gridnum,3); */
! 12565: if ( ssbitval ) gridnum += 4; /* set1bit(gridnum,2); */
! 12566: if ( sebitval ) gridnum += 2; /* set1bit(gridnum,1); */
! 12567: /* -------------------------------------------------------------------------
! 12568: Back to caller with gridnum coding 3x3 grid centered at irow,icol
! 12569: -------------------------------------------------------------------------- */
! 12570: end_of_job:
! 12571: return ( gridnum );
! 12572: } /* --- end-of-function aagridnum() --- */
! 12573:
! 12574:
! 12575: /* ==========================================================================
! 12576: * Function: aapatternnum ( gridnum )
! 12577: * Purpose: Looks up the pattern number 1...51
! 12578: * corresponding to the 3x3 pixel grid coded by gridnum 0=no
! 12579: * pixels set (white) to 511=all pixels set (black).
! 12580: * --------------------------------------------------------------------------
! 12581: * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
! 12582: * (see Notes below)
! 12583: * --------------------------------------------------------------------------
! 12584: * Returns: ( int ) 1 to 51, or -1=error
! 12585: * --------------------------------------------------------------------------
! 12586: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
! 12587: * whose bit positions (and corresponding values) in gridnum are
! 12588: * 876 256 128 64
! 12589: * 504 = 32 1 16
! 12590: * 321 8 4 2
! 12591: * Thus, for example (*=pixel set/black, -=pixel not set/white),
! 12592: * *-- *-- -** (note that 209 is the
! 12593: * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset,
! 12594: * --* *** --- of 302)
! 12595: * o A set pixel is considered black, an unset pixel considered
! 12596: * white.
! 12597: * o Ignoring whether the center pixel is set or unset, and
! 12598: * taking rotation, reflection and inversion (set<-->unset)
! 12599: * symmetries into account, there are 32 unique pixel patterns.
! 12600: * If inversions are listed separately, there are 51 patterns.
! 12601: * o Here are the 51 unique patterns, with ? always denoting the
! 12602: * undetermined center pixel. At the upper-left corner of each
! 12603: * pattern is the "pattern index number" assigned to it in this
! 12604: * function. At the upper-right is the pattern's multiplicity,
! 12605: * i.e., the number of different patterns obtained by rotations
! 12606: * and reflection of the illustrated one. Inverse patters are
! 12607: * illustrated immediately beneath the original (the first three
! 12608: * four-pixel patterns have identical inverses).
! 12609: * -------------------------------------------------------------
! 12610: * No pixels set:
! 12611: * #1 1 (in this case, 1 signifies that rotation
! 12612: * --- and reflection give no different grids)
! 12613: * -?-
! 12614: * ---
! 12615: * Inverse, all eight pixels set
! 12616: * #2 1 (the inverse multiplicity is always the same)
! 12617: * ***
! 12618: * *?*
! 12619: * ***
! 12620: * -------------------------------------------------------------
! 12621: * One pixel set:
! 12622: * #3 4 #4 4
! 12623: * *-- -*-
! 12624: * -?- -?-
! 12625: * --- ---
! 12626: * Inverse, seven pixels set:
! 12627: * #5 4 #6 4
! 12628: * -** *-*
! 12629: * *?* *?*
! 12630: * *** ***
! 12631: * -------------------------------------------------------------
! 12632: * Two pixels set:
! 12633: * #7 8 #8 4 #9 8 10 2 11 4 12 2
! 12634: * **- *-* *-- *-- -*- -*-
! 12635: * -?- -?- -?* -?- -?* -?-
! 12636: * --- --- --- --* --- -*-
! 12637: * Inverse, six pixels set:
! 12638: * #13 8 14 4 15 8 16 2 17 4 18 2
! 12639: * --* -*- -** -** *-* *-*
! 12640: * *?* *?* *?- *?* *?- *?*
! 12641: * *** *** *** **- *** *-*
! 12642: * -------------------------------------------------------------
! 12643: * Three pixels set:
! 12644: * #19 4 20 8 21 8 22 8 23 8 24 4 25 4 26 4 27 4 28 4
! 12645: * *** **- **- **- **- **- *-* *-* -*- -*-
! 12646: * -?- -?* -?- -?- -?- *?- -?- -?- -?* -?*
! 12647: * --- --- --* -*- *-- --- --* -*- -*- *--
! 12648: * Inverse, five pixels set:
! 12649: * #29 4 30 8 31 8 32 8 33 8 34 4 35 4 36 4 37 4 38 4
! 12650: * --- --* --* --* --* --* -*- -*- *-* *-*
! 12651: * *?* *?- *?* *?* *?* -?* *?* *?* *?- *?-
! 12652: * *** *** **- *-* -** *** **- *-* *-* -**
! 12653: * -------------------------------------------------------------
! 12654: * Four pixels set (including inverses):
! 12655: * #39 8 40 4 41 8 42 8 43 4 44 4 45 8 46 1
! 12656: * *** **- **- *** *** **- **- *-*
! 12657: * -?* -?- -?* -?- -?- -?* -?* -?-
! 12658: * --- -** *-- --* -*- --* -*- *-*
! 12659: *
! 12660: * #47 8 48 4 49 4 50 8 51 1
! 12661: * --- --- --* --* -*-
! 12662: * *?* *?* *?- *?- *?*
! 12663: * **- *-* **- *-* -*-
! 12664: * ======================================================================= */
! 12665: /* --- entry point --- */
! 12666: int aapatternnum ( int gridnum )
! 12667: {
! 12668: /* -------------------------------------------------------------------------
! 12669: Allocations and Declarations
! 12670: -------------------------------------------------------------------------- */
! 12671: int pattern = (-1); /*pattern#, 1-51, for input gridnum*/
! 12672: /* ---
! 12673: * pattern number corresponding to input gridnum/2 code
! 12674: * ( gridnum/2 strips off gridnum's low bit because it's
! 12675: * the same pattern whether or not center pixel is set )
! 12676: * --- */
! 12677: static int patternnum[] = {
! 12678: 1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39, /* 0- 15 */
! 12679: 4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29, /* 16- 31 */
! 12680: 3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34, /* 32- 47 */
! 12681: 9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13, /* 48- 63 */
! 12682: 4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33, /* 64- 79 */
! 12683: 11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14, /* 80- 95 */
! 12684: 7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13, /* 96-111 */
! 12685: 20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5, /* 112-127 */
! 12686: 3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30, /* 128-143 */
! 12687: 7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13, /* 144-159 */
! 12688: 8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17, /* 160-175 */
! 12689: 23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6, /* 176-191 */
! 12690: 7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15, /* 192-207 */
! 12691: 24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5, /* 208-223 */
! 12692: 19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6, /* 224-239 */
! 12693: 39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2, /* 240-255 */
! 12694: -1 } ; /* --- end-of-patternnum[] --- */
! 12695: /* -------------------------------------------------------------------------
! 12696: look up pattern number for gridnum
! 12697: -------------------------------------------------------------------------- */
! 12698: /* --- first check input --- */
! 12699: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
! 12700: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
! 12701: pattern = patternnum[gridnum/2]; /* /2 strips off gridnum's low bit */
! 12702: if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */
! 12703: end_of_job:
! 12704: return ( pattern ); /* back to caller with pattern# */
! 12705: } /* --- end-of-function aapatternnum() --- */
! 12706:
! 12707:
! 12708: /* ==========================================================================
! 12709: * Function: aalookup ( gridnum )
! 12710: * Purpose: Looks up the grayscale value 0=white to 255=black
! 12711: * corresponding to the 3x3 pixel grid coded by gridnum 0=no
! 12712: * pixels set (white) to 511=all pixels set (black).
! 12713: * --------------------------------------------------------------------------
! 12714: * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
! 12715: * --------------------------------------------------------------------------
! 12716: * Returns: ( int ) 0=white to 255=black, or -1=error
! 12717: * --------------------------------------------------------------------------
! 12718: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
! 12719: * o A set pixel is considered black, an unset pixel considered
! 12720: * white. Likewise, the returned grayscale is 255 for black,
! 12721: * 0 for white. You'd more typically want to use 255-grayscale
! 12722: * so that 255 is white and 0 is black.
! 12723: * o The returned number is the (lowpass) antialiased grayscale
! 12724: * for the center pixel (gridnum bit 0) of the grid.
! 12725: * ======================================================================= */
! 12726: /* --- entry point --- */
! 12727: int aalookup ( int gridnum )
! 12728: {
! 12729: /* -------------------------------------------------------------------------
! 12730: Allocations and Declarations
! 12731: -------------------------------------------------------------------------- */
! 12732: int grayscale = (-1); /*returned grayscale, init for error*/
! 12733: int pattern = (-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
! 12734: int iscenter = gridnum&1; /*low-order bit set for center pixel*/
! 12735: /* --- gray scales --- */
! 12736: #define WHT 0
! 12737: #define LGT 64
! 12738: #define GRY 128
! 12739: #define DRK 192
! 12740: #define BLK 255
! 12741: #if 1
! 12742: /* ---
! 12743: * modified aapnm() grayscales (second try)
! 12744: * --- */
! 12745: /* --- grayscale for each pattern when center pixel set/black --- */
! 12746: static int grayscale1[] = { -1, /* [0] index not used */
! 12747: BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160, /* 1-10 */
! 12748: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */
! 12749: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
! 12750: BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
! 12751: 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
! 12752: 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
! 12753: -1 } ; /* --- end-of-grayscale1[] --- */
! 12754: /* --- grayscale for each pattern when center pixel not set/white --- */
! 12755: static int grayscale0[] = { -1, /* [0] index not used */
! 12756: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
! 12757: 64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
! 12758: /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
! 12759: WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
! 12760: 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
! 12761: 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
! 12762: -1 } ; /* --- end-of-grayscale0[] --- */
! 12763: #endif
! 12764: #if 0
! 12765: /* ---
! 12766: * modified aapnm() grayscales (first try)
! 12767: * --- */
! 12768: /* --- grayscale for each pattern when center pixel set/black --- */
! 12769: static int grayscale1[] = { -1, /* [0] index not used */
! 12770: BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK, /* 1-10 */
! 12771: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */
! 12772: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
! 12773: BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191, /* 21-30 */
! 12774: /* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */ /* 21-30 */
! 12775: 204,BLK,204,BLK,217,204,BLK,191,GRY,BLK, /* 31-40 */
! 12776: /* 204,BLK,204,191,217,204,BLK,191,178,BLK, */ /* 31-40 */
! 12777: 178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204, /* 41-51 */
! 12778: /* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */
! 12779: -1 } ; /* --- end-of-grayscale1[] --- */
! 12780: /* --- grayscale for each pattern when center pixel not set/white --- */
! 12781: static int grayscale0[] = { -1, /* [0] index not used */
! 12782: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
! 12783: GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY, /* 11-20 */
! 12784: /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
! 12785: WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89, /* 21-30 */
! 12786: /* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */ /* 21-30 */
! 12787: 102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT, /* 31-40 */
! 12788: /* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */ /* 31-40 */
! 12789: 76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
! 12790: /* 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */
! 12791: -1 } ; /* --- end-of-grayscale0[] --- */
! 12792: #endif
! 12793: #if 0
! 12794: /* ---
! 12795: * these grayscales _exactly_ correspond to the aapnm() algorithm
! 12796: * --- */
! 12797: /* --- grayscale for each pattern when center pixel set/black --- */
! 12798: static int grayscale1[] = { -1, /* [0] index not used */
! 12799: BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, /* 1-10 */
! 12800: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
! 12801: BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
! 12802: 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
! 12803: 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
! 12804: -1 } ; /* --- end-of-grayscale1[] --- */
! 12805: /* --- grayscale for each pattern when center pixel not set/white --- */
! 12806: static int grayscale0[] = { -1, /* [0] index not used */
! 12807: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
! 12808: 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
! 12809: WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
! 12810: 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
! 12811: 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
! 12812: -1 } ; /* --- end-of-grayscale0[] --- */
! 12813: #endif
! 12814: /* -------------------------------------------------------------------------
! 12815: look up grayscale for gridnum
! 12816: -------------------------------------------------------------------------- */
! 12817: /* --- first check input --- */
! 12818: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
! 12819: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
! 12820: pattern = aapatternnum(gridnum); /* look up pattern number */
! 12821: if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */
! 12822: if ( ispatternnumcount ) { /* counts being accumulated */
! 12823: if (iscenter) patternnumcount1[pattern] += 1; /* bump diagnostic count */
! 12824: else patternnumcount0[pattern] += 1; }
! 12825: /* --- look up grayscale for this pattern --- */
! 12826: grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] );
! 12827: end_of_job:
! 12828: return ( grayscale ); /* back to caller with grayscale */
! 12829: } /* --- end-of-function aalookup() --- */
! 12830:
! 12831:
! 12832: /* ==========================================================================
! 12833: * Function: aalowpasslookup ( rp, bytemap, grayscale )
! 12834: * Purpose: calls aalookup() for each pixel in rp->bitmap
! 12835: * to create anti-aliased bytemap
! 12836: * --------------------------------------------------------------------------
! 12837: * Arguments: rp (I) raster * to raster whose bitmap
! 12838: * is to be anti-aliased
! 12839: * bytemap (O) intbyte * to bytemap, calculated
! 12840: * by calling aalookup() for each pixel
! 12841: * in rp->bitmap
! 12842: * grayscale (I) int containing number of grayscales
! 12843: * to be calculated, 0...grayscale-1
! 12844: * (should typically be given as 256)
! 12845: * --------------------------------------------------------------------------
! 12846: * Returns: ( int ) 1=success, 0=any error
! 12847: * --------------------------------------------------------------------------
! 12848: * Notes: o
! 12849: * ======================================================================= */
! 12850: /* --- entry point --- */
! 12851: int aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale)
! 12852: {
! 12853: /* -------------------------------------------------------------------------
! 12854: Allocations and Declarations
! 12855: -------------------------------------------------------------------------- */
! 12856: int width=rp->width, height=rp->height, /* width, height of raster */
! 12857: icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */
! 12858: int bgbitval=0 /*, fgbitval=1*/; /* background, foreground bitval */
! 12859: int bitval=0, /* value of rp bit at irow,icol */
! 12860: aabyteval=0; /* antialiased (or unchanged) value*/
! 12861: int gridnum=0, aagridnum(), /* grid# for 3x3 grid at irow,icol */
! 12862: aalookup(); /* table look up antialiased value*/
! 12863: /* -------------------------------------------------------------------------
! 12864: generate bytemap by table lookup for each pixel of bitmap
! 12865: -------------------------------------------------------------------------- */
! 12866: for ( irow=0; irow<height; irow++ )
! 12867: for ( icol=0; icol<width; icol++ )
! 12868: {
! 12869: /* --- get gridnum and center bit value, init aabyteval --- */
! 12870: gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
! 12871: bitval = (gridnum&1); /* center bit set if gridnum odd */
! 12872: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
! 12873: imap++; /* first bump bitmap[] index */
! 12874: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
! 12875: /* --- look up antialiased value for this grid --- */
! 12876: aabyteval = aalookup(gridnum); /* look up on grid# */
! 12877: if ( aabyteval>=0 && aabyteval<=255 ) /* check for success */
! 12878: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
! 12879: } /* --- end-of-for(irow,icol) --- */
! 12880: ispatternnumcount = 0; /* accumulate counts only once */
! 12881: /* -------------------------------------------------------------------------
! 12882: Back to caller with gray-scale anti-aliased bytemap
! 12883: -------------------------------------------------------------------------- */
! 12884: /*end_of_job:*/
! 12885: return ( 1 );
! 12886: } /* --- end-of-function aalowpasslookup() --- */
! 12887:
! 12888:
! 12889: /* ==========================================================================
1.1 albertel 12890: * Function: aasupsamp ( rp, aa, sf, grayscale )
12891: * Purpose: calculates a supersampled anti-aliased bytemap
12892: * for rp->bitmap, with each byte 0...grayscale-1
12893: * --------------------------------------------------------------------------
12894: * Arguments: rp (I) raster * to raster whose bitmap
12895: * is to be anti-aliased
12896: * aa (O) address of raster * to supersampled bytemap,
12897: * calculated by supersampling rp->bitmap
12898: * sf (I) int containing supersampling shrinkfactor
12899: * grayscale (I) int containing number of grayscales
12900: * to be calculated, 0...grayscale-1
12901: * (should typically be given as 256)
12902: * --------------------------------------------------------------------------
12903: * Returns: ( int ) 1=success, 0=any error
12904: * --------------------------------------------------------------------------
12905: * Notes: o If the center point of the box being averaged is black,
12906: * then the entire "average" is forced black (grayscale-1)
12907: * regardless of the surrounding points. This is my attempt
12908: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
12909: * lines, which would otherwise turn from black to a gray shade.
12910: * ======================================================================= */
12911: /* --- entry point --- */
12912: int aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
12913: {
12914: /* -------------------------------------------------------------------------
12915: Allocations and Declarations
12916: -------------------------------------------------------------------------- */
12917: int status = 0; /* 1=success, 0=failure to caller */
12918: int rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
12919: heightrem=0, widthrem=0, /* rp+rem is a multiple of shrinkf */
12920: aaheight=0, aawidth=0, /* supersampled dimensions */
12921: aapixsz=8; /* output pixels are 8-bit bytes */
12922: int maxaaval=(-9999), /* max grayscale val set in matrix */
12923: isrescalemax=1; /* 1=rescale maxaaval to grayscale */
12924: int irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
12925: raster *aap=NULL, *new_raster(); /* raster for supersampled image */
12926: raster *aaweights(); /* get weight matrix applied to rp */
12927: static raster *aawts = NULL; /* aaweights() resultant matrix */
12928: static int prevshrink = NOVALUE, /* shrinkfactor from previous call */
12929: sumwts = 0; /* sum of weights */
12930: static int blackfrac = 40, /* force black if this many pts are */
12931: /*grayfrac = 20,*/
12932: maxwt = 10, /* max weight in weight matrix */
12933: minwtfrac=10, maxwtfrac=70; /* force light pts white, dark black*/
12934: int type_raster(), type_bytemap(); /* debugging display routines */
12935: int delete_raster(); /* delete old rasters */
12936: /* -------------------------------------------------------------------------
12937: Initialization
12938: -------------------------------------------------------------------------- */
12939: /* --- check args --- */
12940: if ( aa == NULL ) goto end_of_job; /* no ptr for return output arg */
12941: *aa = NULL; /* init null ptr for error return */
12942: if ( rp == NULL /* no ptr to input arg */
12943: || sf < 1 /* invalid shrink factor */
12944: || grayscale < 2 ) goto end_of_job; /* invalid grayscale */
12945: /* --- get weight matrix (or use current one) --- */
12946: if ( sf != prevshrink ) /* have new shrink factor */
12947: { if ( aawts != NULL ) /* have unneeded weight matrix */
12948: delete_raster(aawts); /* so free it */
12949: sumwts = 0; /* reinitialize sum of weights */
12950: aawts = aaweights(sf,sf); /* get new weight matrix */
12951: if ( aawts != NULL ) /* got weight matrix okay*/
12952: for ( jwt=0; jwt<sf; jwt++ ) /* for each row */
12953: for ( iwt=0; iwt<sf; iwt++ ) /* and each column */
12954: { int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
12955: if ( wt > maxwt ) /* don't overweight center pts */
12956: { wt = maxwt; /* scale it back */
12957: setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
12958: sumwts += wt; } /* add weight to sum */
12959: prevshrink = sf; } /* save new shrink factor */
12960: if ( msgfp!=NULL && msglevel>=999 )
12961: { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
12962: type_bytemap((intbyte *)aawts->pixmap,grayscale,
12963: aawts->width,aawts->height,msgfp); }
12964: /* --- calculate supersampled height,width and allocate output raster */
12965: heightrem = rpheight%sf; /* remainder after division... */
12966: widthrem = rpwidth%sf; /* ...by shrinkfactor */
12967: aaheight = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
12968: aawidth = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
12969: if ( msgfp!=NULL && msglevel>=999 )
12970: { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
12971: rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
12972: fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
12973: type_raster(rp,msgfp); } /* ascii image of rp raster */
12974: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
12975: == NULL ) goto end_of_job; /* quit if alloc fails */
12976: /* -------------------------------------------------------------------------
12977: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
12978: -------------------------------------------------------------------------- */
12979: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
12980: {
12981: for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
12982: {
12983: int aaval=0; /* weighted rpvals */
12984: int nrp=0, mrp=0; /* #rp bits set, #within matrix */
12985: for ( jwt=0; jwt<sf; jwt++ )
12986: for ( iwt=0; iwt<sf; iwt++ )
12987: {
12988: int i=irp+iwt, j=jrp+jwt; /* rp->pixmap point */
12989: int rpval = 0; /* rp->pixmap value at i,j */
12990: if ( i>=0 && i<rpwidth /* i within actual pixmap */
12991: && j>=0 && j<rpheight ) /* ditto j */
12992: { mrp++; /* count another bit within matrix */
12993: rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
12994: if ( rpval != 0 )
12995: { nrp++; /* count another bit set */
12996: aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
12997: } /* --- end-of-for(iwt,jwt) --- */
12998: if ( aaval > 0 ) /*normalize and rescale non-zero val*/
12999: { int aafrac = (100*aaval)/sumwts; /* weighted percent of black points */
13000: /*if((100*nrp)/mrp >=blackfrac)*/ /* many black interior pts */
13001: if( aafrac >= maxwtfrac ) /* high weight of sampledblack pts */
13002: aaval = grayscale-1; /* so set supersampled pt black */
13003: else if( aafrac <= minwtfrac ) /* low weight of sampledblack pts */
13004: aaval = 0; /* so set supersampled pt white */
13005: else /* rescale calculated weight */
13006: aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
13007: maxaaval = max2(maxaaval,aaval); /* largest aaval so far */
13008: if ( msgfp!=NULL && msglevel>=999 )
13009: fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
13010: " mrp,nrp=%d,%d aaval=%d\n",
13011: jrp,irp, jaa,iaa, mrp,nrp, aaval);
13012: if ( jaa<aaheight && iaa<aawidth ) /* bounds check */
13013: setpixel(aap,jaa,iaa,aaval); /*weighted val in supersamp raster*/
13014: else if( msgfp!=NULL && msglevel>=9 ) /* emit error if out-of-bounds */
13015: fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
13016: aaheight,aawidth, jaa,iaa);
13017: iaa++; /* bump aa col index */
13018: } /* --- end-of-for(irp) --- */
13019: jaa++; /* bump aa row index */
13020: } /* --- end-of-for(jrp) --- */
13021: /* --- rescale supersampled image so darkest points become black --- */
13022: if ( isrescalemax ) /* flag set to rescale maxaaval */
13023: {
13024: double scalef = ((double)(grayscale-1))/((double)maxaaval);
13025: for ( jaa=0; jaa<aaheight; jaa++ ) /* height */
13026: for ( iaa=0; iaa<aawidth; iaa++ ) /* width */
13027: { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
13028: aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
13029: aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
13030: if( aafrac >= blackfrac ) /* high weight of sampledblack pts */
13031: aaval = grayscale-1; /* so set supersampled pt black */
13032: else if( 0&&aafrac <= minwtfrac ) /* low weight of sampledblack pts */
13033: aaval = 0; /* so set supersampled pt white */
13034: setpixel(aap,jaa,iaa,aaval); } /* replace rescaled val in raster */
13035: } /* --- end-of-if(isrescalemax) --- */
13036: *aa = aap; /* return supersampled image*/
13037: status = 1; /* set successful status */
13038: if ( msgfp!=NULL && msglevel>=999 )
13039: { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
13040: type_bytemap((intbyte *)aap->pixmap,grayscale,
13041: aap->width,aap->height,msgfp); fflush(msgfp); }
13042: /* -------------------------------------------------------------------------
13043: Back to caller with gray-scale anti-aliased bytemap
13044: -------------------------------------------------------------------------- */
13045: end_of_job:
13046: return ( status );
13047: } /* --- end-of-function aasupsamp() --- */
13048:
13049:
13050: /* ==========================================================================
13051: * Function: aacolormap ( bytemap, nbytes, colors, colormap )
13052: * Purpose: searches bytemap, returning a list of its discrete values
13053: * in ascending order in colors[], and returning an "image"
13054: * of bytemap (where vales are replaced by colors[]
13055: * indexes) in colormap[].
13056: * --------------------------------------------------------------------------
13057: * Arguments: bytemap (I) intbyte * to bytemap containing
13058: * grayscale values (usually 0=white
13059: * through 255=black) for which colors[]
13060: * and colormap[] will be constructed.
13061: * nbytes (I) int containing #bytes in bytemap
13062: * (usually just #rows * #cols)
13063: * colors (O) intbyte * (to be interpreted as ints)
13064: * returning a list of the discrete/different
1.3 ! albertel 13065: * values in bytemap, in ascending value order,
! 13066: * and with gamma correction applied
1.1 albertel 13067: * colormap (O) intbyte * returning a bytemap "image",
13068: * i.e., in one-to-one pixel correspondence
13069: * with bytemap, but where the values have been
13070: * replaced with corresponding colors[] indexes.
13071: * --------------------------------------------------------------------------
13072: * Returns: ( int ) #colors in colors[], or 0 for any error
13073: * --------------------------------------------------------------------------
13074: * Notes: o
13075: * ======================================================================= */
13076: /* --- entry point --- */
13077: int aacolormap ( intbyte *bytemap, int nbytes,
13078: intbyte *colors, intbyte *colormap )
13079: {
13080: /* -------------------------------------------------------------------------
13081: Allocations and Declarations
13082: -------------------------------------------------------------------------- */
13083: int ncolors = 0, /* #different values in bytemap */
13084: igray, grayscale = 256; /* bytemap contains intbyte's */
13085: intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/
13086: int ibyte; /* bytemap/colormap index */
1.3 ! albertel 13087: int isscale = 0, /* true to scale largest val to 255*/
! 13088: isgamma = 1; /* true to apply gamma correction */
1.1 albertel 13089: int maxcolors = 0; /* maximum ncolors */
13090: /* -------------------------------------------------------------------------
13091: Accumulate colors[] from values occurring in bytemap
13092: -------------------------------------------------------------------------- */
13093: /* --- initialization --- */
13094: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
13095: == NULL ) goto end_of_job; /* signal error if malloc() failed */
13096: memset(bytevalues,0,grayscale); /* zero out bytevalues */
13097: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
13098: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
13099: bytevalues[(int)bytemap[ibyte]] = 1; /*use its value to index bytevalues*/
13100: /* --- collect the 1's indexes in colors[] --- */
13101: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
13102: if ( (int)bytevalues[igray] ) /*bytemap contains igray somewheres*/
13103: { colors[ncolors] = (intbyte)igray; /* so store igray in colors */
13104: bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
13105: if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
13106: bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
13107: ncolors++; } /* and bump #colors */
13108: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
13109: if ( isscale ) /* only rescale if requested */
13110: if ( ncolors > 1 ) /* and if not a "blank" raster */
13111: if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
13112: {
13113: /* --- multiply each colors[] by factor that scales largest to 255 --- */
13114: double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
13115: for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
13116: { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
13117: if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
13118: } /* --- end-of-if(isscale) --- */
1.3 ! albertel 13119: /* --- apply gamma correction --- */
! 13120: if ( isgamma /* only gamma correct if requested */
! 13121: && gammacorrection > 0.0001 ) /* and if we have gamma correction */
! 13122: if ( ncolors > 1 ) /* and if not a "blank" raster */
! 13123: if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
! 13124: {
! 13125: for ( igray=1; igray<ncolors; igray++ ) { /*gamma correct each colors[]*/
! 13126: int grayval = colors[igray], /* original 0=white to 255=black */
! 13127: gmax = grayscale-1; /* should be 255 */
! 13128: double dgray=((double)(gmax-grayval))/((double)gmax); /*0=black 1=white*/
! 13129: dgray = pow(dgray,(1.0/gammacorrection)); /* apply gamma correction */
! 13130: grayval = (int)( gmax*(1.0-dgray) + 0.5 ); /* convert back to grayval */
! 13131: colors[igray] = grayval; } /* store back in colors[] */
! 13132: } /* --- end-of-if(isgamma) --- */
1.1 albertel 13133: /* -------------------------------------------------------------------------
13134: Construct colormap
13135: -------------------------------------------------------------------------- */
13136: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
13137: colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
13138: /* -------------------------------------------------------------------------
13139: back to caller with #colors, or 0 for any error
13140: -------------------------------------------------------------------------- */
13141: end_of_job:
13142: if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
13143: if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
13144: ncolors = maxcolors; /* return maximum to caller */
13145: return ( ncolors ); /* back with #colors, or 0=error */
13146: } /* --- end-of-function aacolormap() --- */
13147:
13148:
13149: /* ==========================================================================
13150: * Function: aaweights ( width, height )
13151: * Builds "canonical" weight matrix, width x height, in a raster
13152: * (see Notes below for discussion).
13153: * --------------------------------------------------------------------------
13154: * Arguments: width (I) int containing width (#cols) of returned
13155: * raster/matrix of weights
13156: * height (I) int containing height (#rows) of returned
13157: * raster/matrix of weights
13158: * --------------------------------------------------------------------------
13159: * Returns: ( raster * ) ptr to raster containing width x height
13160: * weight matrix, or NULL for any error
13161: * --------------------------------------------------------------------------
13162: * Notes: o For example, given width=7, height=5, builds the matrix
13163: * 1 2 3 4 3 2 1
13164: * 2 4 6 8 6 4 2
13165: * 3 6 9 12 9 6 3
13166: * 2 4 6 8 6 4 2
13167: * 1 2 3 4 3 2 1
13168: * If an even dimension given, the two center numbers stay
13169: * the same, e.g., 123321 for the top row if width=6.
13170: * o For an odd square n x n matrix, the sum of all n^2
13171: * weights will be ((n+1)/2)^4.
13172: * o The largest weight (in the allocated pixsz=8 raster) is 255,
13173: * so the largest square matrix is 31 x 31. Any weight that
13174: * tries to grow beyond 255 is held constant at 255.
13175: * o A new_raster(), pixsz=8, is allocated for the caller.
13176: * To avoid memory leaks, be sure to delete_raster() when done.
13177: * ======================================================================= */
13178: /* --- entry point --- */
13179: raster *aaweights ( int width, int height )
13180: {
13181: /* -------------------------------------------------------------------------
13182: Allocations and Declarations
13183: -------------------------------------------------------------------------- */
13184: raster *new_raster(), *weights=NULL; /* raster of weights returned */
13185: int irow=0, icol=0, /* height, width indexes */
13186: weight = 0; /*running weight, as per Notes above*/
13187: /* -------------------------------------------------------------------------
13188: Initialization
13189: -------------------------------------------------------------------------- */
13190: /* --- allocate raster for weights --- */
13191: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
13192: == NULL ) goto end_of_job; /* return NULL error if failed */
13193: /* -------------------------------------------------------------------------
13194: Fill weight matrix, as per Notes above
13195: -------------------------------------------------------------------------- */
13196: for ( irow=0; irow<height; irow++ ) /* outer loop over rows */
13197: for ( icol=0; icol<width; icol++ ) /* inner loop over cols */
13198: {
13199: int jrow = height-irow-1, /* backwards irow, height-1,...,0 */
13200: jcol = width-icol-1; /* backwards icol, width-1,...,0 */
13201: weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
13202: if ( aaalgorithm == 1 ) weight=1; /* force equal weights */
13203: setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
13204: } /* --- end-of-for(irow,icol) --- */
13205: end_of_job:
13206: return ( weights ); /* back with weights or NULL=error */
13207: } /* --- end-of-function aaweights() --- */
13208:
13209:
13210: /* ==========================================================================
13211: * Function: aawtpixel ( image, ipixel, weights, rotate )
13212: * Purpose: Applies matrix of weights to the pixels
13213: * surrounding ipixel in image, rotated clockwise
13214: * by rotate degrees (typically 0 or 30).
13215: * --------------------------------------------------------------------------
13216: * Arguments: image (I) raster * to bitmap (though it can be bytemap)
13217: * containing image with pixels to be averaged.
13218: * ipixel (I) int containing index (irow*width+icol) of
13219: * center pixel of image for weighted average.
13220: * weights (I) raster * to bytemap of relative weights
13221: * (0-255), whose dimensions (usually odd width
13222: * and odd height) determine the "subgrid" of
13223: * image surrounding ipixel to be averaged.
13224: * rotate (I) int containing degrees clockwise rotation
13225: * (typically 0 or 30), i.e., imagine weights
13226: * rotated clockwise and then averaging the
13227: * image pixels "underneath" it now.
13228: * --------------------------------------------------------------------------
13229: * Returns: ( int ) 0-255 weighted average, or -1 for any error
13230: * --------------------------------------------------------------------------
13231: * Notes: o The rotation matrix used (when requested) is
13232: * / x' \ / cos(theta) sin(theta)/a \ / x \
13233: * | | = | | | |
13234: * \ y' / \ -a*sin(theta) cos(theta) / \ y /
13235: * where a=1 (current default) is the pixel (not screen)
13236: * aspect ratio width:height, and theta is rotate (converted
13237: * from degrees to radians). Then x=col,y=row are the integer
13238: * pixel coords relative to the input center ipixel, and
13239: * x',y' are rotated coords which aren't necessarily integer.
13240: * The actual pixel used is the one nearest x',y'.
13241: * ======================================================================= */
13242: /* --- entry point --- */
13243: int aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
13244: {
13245: /* -------------------------------------------------------------------------
13246: Allocations and Declarations
13247: -------------------------------------------------------------------------- */
13248: int aaimgval = 0, /* weighted avg returned to caller */
13249: totwts=0, sumwts=0; /* total of all wts, sum wts used */
13250: int pixsz = image->pixsz, /* #bits per image pixel */
13251: black1=1, black8=255, /* black for 1-bit, 8-bit pixels */
13252: black = (pixsz==1? black1:black8), /* black value for our image */
13253: scalefactor = (black1+black8-black), /* only scale 1-bit images */
13254: iscenter = 0; /* set true if center pixel black */
13255: /* --- grid dimensions and indexes --- */
13256: int wtheight = weights->height, /* #rows in weight matrix */
13257: wtwidth = weights->width, /* #cols in weight matrix */
13258: imgheight = image->height, /* #rows in image */
13259: imgwidth = image->width; /* #cols in image */
13260: int wtrow, wtrow0 = wtheight/2, /* center row index for weights */
13261: wtcol, wtcol0 = wtwidth/2, /* center col index for weights */
13262: imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
13263: imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
13264: /* --- rotated grid variables --- */
13265: static int prevrotate = 0; /* rotate from previous call */
13266: static double costheta = 1.0, /* cosine for previous rotate */
13267: sintheta = 0.0; /* and sine for previous rotate */
13268: double a = 1.0; /* default aspect ratio */
13269: /* -------------------------------------------------------------------------
13270: Initialization
13271: -------------------------------------------------------------------------- */
13272: /* --- refresh trig functions for rotate when it changes --- */
13273: if ( rotate != prevrotate ) /* need new sine/cosine */
13274: { costheta = cos(((double)rotate)/57.29578); /*cos of rotate in radians*/
13275: sintheta = sin(((double)rotate)/57.29578); /*sin of rotate in radians*/
13276: prevrotate = rotate; } /* save current rotate as prev */
13277: /* -------------------------------------------------------------------------
13278: Calculate aapixel as weighted average over image points around ipixel
13279: -------------------------------------------------------------------------- */
13280: for ( wtrow=0; wtrow<wtheight; wtrow++ )
13281: for ( wtcol=0; wtcol<wtwidth; wtcol++ )
13282: {
13283: /* --- allocations and declarations --- */
13284: int wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
13285: int drow = wtrow - wtrow0, /* delta row offset from center */
13286: dcol = wtcol - wtcol0; /* delta col offset from center */
13287: int iscenter = 0; /* set true if center point black */
13288: /* --- initialization --- */
13289: totwts += wt; /* sum all weights */
13290: /* --- rotate (if requested) --- */
13291: if ( rotate != 0 ) /* non-zero rotation */
13292: {
13293: /* --- apply rotation matrix to (x=dcol,y=drow) --- */
13294: double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
13295: dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
13296: dy = -a*dx*sintheta + dy*costheta; /* dy becomes new dy' */
13297: dx = dtemp; /* just for notational convenience */
13298: /* --- replace original (drow,dcol) with nearest rotated point --- */
13299: drow = (int)(dy+0.5); /* round dy for nearest row */
13300: dcol = (int)(dx+0.5); /* round dx for nearest col */
13301: } /* --- end-of-if(rotate!=0) --- */
13302: /* --- select image pixel to be weighted --- */
13303: imgrow = imgrow0 + drow; /*apply displacement to center row*/
13304: imgcol = imgcol0 + dcol; /*apply displacement to center col*/
13305: /* --- if pixel in bounds, accumulate weighted average --- */
13306: if ( imgrow>=0 && imgrow<imgheight ) /* row is in bounds */
13307: if ( imgcol>=0 && imgcol<imgwidth ) /* and col is in bounds */
13308: {
13309: /* --- accumulate weighted average --- */
13310: int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
13311: aaimgval += wt*imgval; /* weighted sum of image values */
13312: sumwts += wt; /* and also sum weights used */
13313: /* --- check if center image pixel black --- */
13314: if ( drow==0 && dcol==0 ) /* this is center ipixel */
13315: if ( imgval==black ) /* and it's black */
13316: iscenter = 1; /* so set black center flag true */
13317: } /* --- end-of-if(bounds checks ok) --- */
13318: } /* --- end-of-for(irow,icol) --- */
13319: if ( 0 && iscenter ) /* center point is black */
13320: aaimgval = black8; /* so force average black */
13321: else /* center point not black */
13322: aaimgval = /* 0=white ... black */
13323: ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
13324: /*end_of_job:*/
13325: return ( aaimgval );
13326: } /* --- end-of-function aawtpixel() --- */
1.3 ! albertel 13327:
! 13328:
! 13329: /* ==========================================================================
! 13330: * Function: mimetexsetmsg ( newmsglevel, newmsgfp )
! 13331: * Purpose: Sets msglevel and msgfp, usually called from
! 13332: * an external driver (i.e., DRIVER not defined
! 13333: * in this module).
! 13334: * --------------------------------------------------------------------------
! 13335: * Arguments: newmsglevel (I) int containing new msglevel
! 13336: * (unchanged if newmsglevel<0)
! 13337: * newmsgfp (I) FILE * containing new msgfp
! 13338: * (unchanged if newmsgfp=NULL)
! 13339: * --------------------------------------------------------------------------
! 13340: * Returns: ( int ) always 1
! 13341: * --------------------------------------------------------------------------
! 13342: * Notes: o
! 13343: * ======================================================================= */
! 13344: /* --- entry point --- */
! 13345: int mimetexsetmsg ( int newmsglevel, FILE *newmsgfp )
! 13346: {
! 13347: /* -------------------------------------------------------------------------
! 13348: set msglevel and msgfp
! 13349: -------------------------------------------------------------------------- */
! 13350: if ( newmsglevel >= 0 ) msglevel = newmsglevel;
! 13351: if ( newmsgfp != NULL ) msgfp = newmsgfp;
! 13352: return ( 1 );
! 13353: } /* --- end-of-function mimetexsetmsg() --- */
1.1 albertel 13354: #endif /* PART3 */
13355:
13356: /* ---
13357: * PART1
13358: * ------ */
13359: #if !defined(PARTS) || defined(PART1)
13360: #ifdef DRIVER
13361: /* ==========================================================================
13362: * Function: main() driver for mimetex.c
13363: * Purpose: emits a mime xbitmap or gif image of a LaTeX math expression
13364: * entered either as
13365: * (1) html query string from a browser (most typical), or
13366: * (2) a query string from an html <form method="get">
13367: * whose <input name="formdata"> (mostly for demo), or
13368: * (3) command-line arguments (mostly to test).
13369: * If no input supplied, expression defaults to "f(x)=x^2",
13370: * treated as test (input method 3).
13371: * If args entered on command-line (or if no input supplied),
13372: * output is (usually) human-viewable ascii raster images on
13373: * stdout rather than the usual mime xbitmaps or gif images.
13374: * --------------------------------------------------------------------------
13375: * Command-Line Arguments:
13376: * When running mimeTeX from the command-line, rather than
13377: * from a browser, syntax is
13378: * ./mimetex [-d ] dump gif to stdout
13379: * [expression expression, e.g., x^2+y^2,
13380: * |-f input_file] or read expression from file
13381: * [-m msglevel] verbosity of debugging output
13382: * [-s fontsize] default fontsize, 0-5
13383: * -d Rather than ascii debugging output, mimeTeX dumps the
13384: * actual gif (or xbitmap) to stdout, e.g.,
13385: * ./mimetex -d x^2+y^2 > expression.gif
13386: * creates a gif file containing an image of x^2+y^2
13387: * -f Reads expression from input_file, and automatically
13388: * assumes -d switch. The input_file may contain the
13389: * expression on one line or spread out over many lines.
13390: * MimeTeX will concatanate all lines from input_file
13391: * to construct one long expression. Blanks, tabs, and
13392: * newlines will just be ignored.
13393: * -m 0-99, controls verbosity level for debugging output
13394: * (usually used only while testing code).
13395: * -s Font size, 0-5. As usual, the font size can
13396: * also be specified in the expression by a leading
13397: * preamble terminated by $, e.g., 3$f(x)=x^2 displays
13398: * f(x)=x^2 at font size 3. Default font size is 2.
13399: * --------------------------------------------------------------------------
13400: * Exits: 0=success, 1=some error
13401: * --------------------------------------------------------------------------
13402: * Notes: o For an executable that emits mime xbitmaps, compile as
13403: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
13404: * or, alternatively, for an executable that emits gif images
13405: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
13406: * or for gif images with anti-aliasing
13407: * cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
13408: * See Notes at top of file for other compile-line -D options.
13409: * o Move executable to your cgi-bin directory and either
13410: * point your browser to it directly in the form
13411: * http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
13412: * or put a tag in your html document of the form
13413: * <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
13414: * border=0 align=absmiddle>
13415: * where f(x)=x^2 (or any other expression) will be displayed
13416: * either as a mime xbitmap or gif image (as per -D flag).
13417: * ======================================================================= */
13418:
13419: /* -------------------------------------------------------------------------
13420: header files and other data
13421: -------------------------------------------------------------------------- */
13422: /* --- (additional) standard headers --- */
13423: /* --- other data --- */
13424: #ifdef DUMPENVIRON
13425: extern char **environ; /* environment information */
13426: #endif
13427:
13428: /* -------------------------------------------------------------------------
13429: globals for gif and png callback functions
13430: -------------------------------------------------------------------------- */
13431: GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */
13432: GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */
13433: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
13434: #ifdef AA /* if anti-aliasing requested */
13435: #define ISAAVALUE 1 /* turn flag on */
13436: #else
13437: #define ISAAVALUE 0 /* else turn flag off */
13438: #endif
13439: GLOBAL(int,isaa,ISAAVALUE); /* set anti-aliasing flag */
13440:
13441: /* -------------------------------------------------------------------------
13442: logging data structure, and default data to be logged
13443: -------------------------------------------------------------------------- */
13444: /* --- logging data structure --- */
13445: #define logdata struct logdata_struct /* "typedef" for logdata_struct*/
13446: logdata
13447: {
13448: /* -----------------------------------------------------------------------
13449: environment variable name, max #chars to display, min msglevel to display
13450: ------------------------------------------------------------------------ */
13451: char *name; /* environment variable name */
13452: int maxlen; /* max #chars to display */
13453: int msglevel; /* min msglevel to display data */
13454: } ; /* --- end-of-logdata_struct --- */
13455: /* --- data logged by mimeTeX --- */
13456: STATIC logdata mimelog[]
13457: #ifdef INITVALS
13458: =
13459: {
13460: /* ------ variable ------ maxlen msglevel ----- */
13461: { "QUERY_STRING", 999, 4 },
13462: { "REMOTE_ADDR", 999, 3 },
13463: { "HTTP_REFERER", 999, 3 },
13464: { "REQUEST_URI", 999, 5 },
13465: { "HTTP_USER_AGENT", 999, 3 },
13466: { "HTTP_X_FORWARDED_FOR", 999, 3 },
13467: { NULL, -1, -1 } /* trailer record */
13468: } /* --- end-of-mimelog[] --- */
13469: #endif
13470: ;
13471:
13472: /* -------------------------------------------------------------------------
13473: messages
13474: -------------------------------------------------------------------------- */
13475: static char *copyright = /* copyright, gnu/gpl notice */
13476: "+-----------------------------------------------------------------------+\n"
1.3 ! albertel 13477: "|mimeTeX vers 1.64, Copyright(c) 2002-2006, John Forkosh Associates, Inc|\n"
1.1 albertel 13478: "+-----------------------------------------------------------------------+\n"
13479: "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
13480: "| and comes with absolutely no warranty whatsoever. |\n"
13481: "+-----------------------------------------------------------------------+";
13482: static int maxmsgnum = 2; /* maximum msgtable[] index */
13483: static char *msgtable[] = { /* messages referenced by [index] */
13484: "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
13485: "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
13486: "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
13487: "\\red\\small\\rm\\fbox{\\array{" /* [1] */
13488: "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
13489: "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
13490: "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
13491: "\\red\\small\\rm\\fbox{\\array{" /* [2] */
13492: "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
13493: "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
13494: "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
13495: NULL } ; /* trailer */
13496:
13497:
13498: /* --- entry point --- */
13499: int main ( int argc, char *argv[]
13500: #ifdef DUMPENVP
13501: , char *envp[]
13502: #endif
13503: )
13504: {
13505: /* -------------------------------------------------------------------------
13506: Allocations and Declarations
13507: -------------------------------------------------------------------------- */
13508: /* --- expression to be emitted --- */
1.3 ! albertel 13509: static char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */
1.1 albertel 13510: char *expression = exprbuffer; /* ptr to expression */
13511: int size = NORMALSIZE; /* default font size */
13512: char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
13513: char *mimeprep(); /* preprocess expression */
13514: int unescape_url(); /* convert %xx's to ascii chars */
13515: int emitcache(); /* emit cached image if it exists */
13516: int isquery = 0, /* true if input from QUERY_STRING */
13517: isqempty = 0, /* true if query string empty */
1.2 albertel 13518: isqforce = 0, /* true to force query emulation */
1.1 albertel 13519: isqlogging = 0, /* true if logging in query mode */
13520: isformdata = 0, /* true if input from html form */
1.2 albertel 13521: isinmemory = 1, /* true to generate image in memory*/
13522: isdumpimage = 0, /* true to dump image on stdout */
13523: isdumpbuffer = 0; /* true to dump to memory buffer */
1.1 albertel 13524: /* --- rasterization --- */
1.2 albertel 13525: subraster *rasterize(), *sp=NULL; /* rasterize expression */
13526: raster *border_raster(), *bp=NULL; /* put a border around raster */
13527: int delete_subraster(); /* for clean-up at end-of-job */
1.1 albertel 13528: int type_raster(), type_bytemap(), /* screen dump function prototypes */
13529: xbitmap_raster(); /* mime xbitmap output function */
13530: /* --- http_referer --- */
13531: char *referer = REFERER; /* http_referer must contain this */
13532: struct { char *referer; int msgnum; } /* http_referer can't contain this */
13533: denyreferer[] = { /* referer table to deny access to */
13534: #ifdef DENYREFERER
13535: #include DENYREFERER /* e.g., {"",1}, for no referer */
13536: #endif
13537: { NULL, -999 } }; /* trailer */
13538: char *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
13539: int ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));
13540: int isstrstr(); /* search http_referer for referer */
13541: int isinvalidreferer = 0; /* true for inavlid referer */
13542: int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/
13543: /* --- gif --- */
13544: #if defined(GIF)
13545: int GetPixel(); /* feed pixels to gifsave library */
13546: int GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
13547: void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
13548: #endif
13549: char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
1.3 ! albertel 13550: gif_buffer[MAXGIFSZ] = "\000", /* or gif written in memory buffer */
1.1 albertel 13551: cachefile[256] = "\000", /* full path and name to cache file*/
13552: *md5str(); /* md5 has of expression */
13553: int maxage = 7200; /* max-age is two hours */
1.2 albertel 13554: /* --- pbm/pgm (-g switch) --- */
13555: int ispbmpgm = 0; /* true to write pbm/pgm file */
13556: int type_pbmpgm(), ptype=0; /* entry point, graphic format */
13557: char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
1.1 albertel 13558: /* --- anti-aliasing --- */
13559: intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
13560: colors[256]; /* grayscale vals in bytemap */
13561: int aalowpass(), aapnm(), /*lowpass filters for anti-aliasing*/
13562: grayscale = 256; /* 0-255 grayscales in 8-bit bytes */
13563: int ncolors=2, /* #colors (2=b&w) */
13564: aacolormap(); /* build colormap from bytemap */
1.3 ! albertel 13565: int ipattern; /*patternnumcount[] index diagnostic*/
1.1 albertel 13566: /* --- messages --- */
13567: char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/
13568: cachelog[256] = CACHELOG; /* cached image log in cachepath/ */
13569: char *timestamp(); /* time stamp for logged messages */
13570: int logger(); /* logs environ variables */
13571: int ismonth(); /* check argv[0] for current month */
13572: char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */
13573: char *dashes = /* separates logfile entries */
13574: "--------------------------------------------------------------------------";
13575: char *invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */
13576: /* -------------------------------------------------------------------------
13577: initialization
13578: -------------------------------------------------------------------------- */
13579: /* --- run optional system command string --- */
13580: #ifdef SYSTEM
13581: system(SYSTEM);
13582: #endif
13583: /* --- set global variables --- */
13584: msgfp = stdout; /* for comamnd-line mode output */
13585: isss = issupersampling; /* set supersampling flag */
1.2 albertel 13586: gifSize = 0; /* signal that image not in memory */
1.1 albertel 13587: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
1.3 ! albertel 13588: for ( ipattern=1; ipattern<=51; ipattern++ )
! 13589: patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
1.1 albertel 13590: /* ---
13591: * check QUERY_STRING query for expression overriding command-line arg
13592: * ------------------------------------------------------------------- */
13593: if ( query != NULL ) /* check query string from environ */
13594: if ( strlen(query) >= 1 ) /* caller gave us a query string */
1.3 ! albertel 13595: { strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
! 13596: expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
1.1 albertel 13597: isquery = 1; } /* and set isquery flag */
13598: if ( !isquery ) /* empty query string */
13599: { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
13600: *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
13601: if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
13602: { isquery = 1; /* set flag to signal query */
1.2 albertel 13603: strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
1.1 albertel 13604: isqempty = 1; /* signal empty query string */
13605: } /* --- end-of-if(!isquery) --- */
13606: /* ---
13607: * process command-line input args (if not a query)
13608: * ------------------------------------------------ */
13609: if ( !isquery /* don't have an html query string */
13610: || ( /*isqempty &&*/ argc>1) ) /* or have command-line args */
13611: {
13612: char *argsignal = ARGSIGNAL, /* signals start of mimeTeX args */
13613: stopsignal[32] = "--"; /* default Unix end-of-args signal */
13614: int iarg=0, argnum=0, /*argv[] index for command-line args*/
13615: exprarg = 0, /* argv[] index for expression */
13616: infilearg = 0, /* argv[] index for infile */
13617: nswitches = 0, /* number of -switches */
13618: isstopsignal = 0, /* true after stopsignal found */
13619: isstrict = 1/*iswindows*/, /* true for strict arg checking */
13620: /*nb, windows has apache "x -3" bug*/
13621: nargs=0, nbadargs=0, /* number of arguments, bad ones */
13622: maxbadargs = (isstrict?0:1), /*assume query if too many bad args*/
13623: isgoodargs = 0; /* true to accept command-line args*/
13624: if ( argsignal != NULL ) /* if compiled with -DARGSIGNAL */
13625: while ( argc > ++iarg ) /* check each argv[] for argsignal */
13626: if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
13627: { argnum = iarg; /* got it, start parsing next arg */
13628: break; } /* stop looking for argsignal */
13629: while ( argc > ++argnum ) /* check for switches and values, */
13630: {
13631: nargs++; /* count another command-line arg */
13632: if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
13633: { isstopsignal = 1; /* so set stopsignal flag */
13634: continue; } /* and get expression after it */
13635: if ( !isstopsignal /* haven't seen stopsignal switch */
13636: && *argv[argnum] == '-' ) /* and have some '-' switch */
13637: {
1.2 albertel 13638: char *field = argv[argnum] + 1; /* ptr to char(s) following - */
13639: char flag = tolower(*field); /* single char following '-' */
13640: int arglen = strlen(field); /* #chars following - */
1.1 albertel 13641: argnum++; /* arg following flag/switch is usually its value */
13642: nswitches++; /* another switch on command line */
1.2 albertel 13643: if ( isstrict && /* if strict checking then... */
13644: !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
1.1 albertel 13645: { nbadargs++; argnum--; } /* so ignore longer -xxx switch */
13646: else /* process single-char -x switch */
13647: switch ( flag ) { /* see what user wants to tell us */
13648: /* --- ignore uninterpreted flag --- */
13649: default: nbadargs++; argnum--; break;
13650: /* --- adjustable program parameters (not checking input) --- */
1.2 albertel 13651: case 'b': isdumpimage++; isdumpbuffer++; argnum--; break;
1.1 albertel 13652: case 'd': isdumpimage++; argnum--; break;
13653: case 'e': isdumpimage++; gif_outfile=argv[argnum]; break;
13654: case 'f': isdumpimage++; infilearg=argnum; break;
1.2 albertel 13655: case 'g': ispbmpgm++;
13656: if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
13657: if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
13658: else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
1.1 albertel 13659: case 'm': msglevel = atoi(argv[argnum]); break;
1.3 ! albertel 13660: case 'o': istransparent = (istransparent?0:1); argnum--; break;
1.2 albertel 13661: case 'q': isqforce = 1; argnum--; break;
1.1 albertel 13662: case 's': size = atoi(argv[argnum]); break;
13663: } /* --- end-of-switch(flag) --- */
13664: } /* --- end-of-if(*argv[argnum]=='-') --- */
13665: else /* expression if arg not a -flag */
13666: if ( infilearg == 0 ) /* no infile arg yet */
13667: { if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
13668: exprarg = argnum; /* but take last expression */
13669: /*infilearg = (-1);*/ } /* and set infilearg */
13670: else nbadargs++; /* infile and expression invalid */
13671: } /* --- end-of-while(argc>++argnum) --- */
13672: if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
1.2 albertel 13673: { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
13674: argc,progname,nargs,nbadargs);
13675: fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
13676: cachepath,pathprefix); }
1.1 albertel 13677: /* ---
13678: * decide whether command-line input overrides query_string
13679: * -------------------------------------------------------- */
13680: if ( isdumpimage > 2 ) nbadargs++; /* duplicate/conflicting -switch */
13681: isgoodargs = ( !isstrict /*good if not doing strict checking*/
13682: || !isquery /* or if no query, must use args */
13683: || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
13684: /* ---
13685: * take expression from command-line args
13686: * -------------------------------------- */
13687: if ( isgoodargs && exprarg > 0 /* good expression on command line */
13688: && infilearg <= 0 ) /* and not given in input file */
13689: if ( !isquery /* no conflict if no query_string */
13690: || nswitches > 0 ) /* explicit -switch(es) also given */
1.3 ! albertel 13691: { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/
! 13692: expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
1.1 albertel 13693: isquery = 0; } /* and not from a query_string */
13694: /* ---
13695: * or read expression from input file
13696: * ---------------------------------- */
13697: if ( isgoodargs && infilearg > 0 ) /* have a good -f arg */
13698: {
13699: FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
13700: if ( infile != (FILE *)NULL ) /* opened input file successfully */
1.3 ! albertel 13701: { char instring[MAXLINESZ+1]; /* line from file */
! 13702: int exprsz = 0; /* total #bytes read from file */
1.1 albertel 13703: isquery = 0; /* file input, not a query_string */
13704: *expression = '\000'; /* start expresion as empty string */
1.3 ! albertel 13705: while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/
! 13706: if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */
! 13707: strcat(expression,instring); /* concat line to end of expression*/
! 13708: exprsz += strlen(instring); } /* update expression buffer length */
1.1 albertel 13709: fclose ( infile ); } /*close input file after reading expression*/
13710: } /* --- end-of-if(infilearg>0) --- */
1.2 albertel 13711: /* ---
1.3 ! albertel 13712: * xlate +++'s to blanks only if query
! 13713: * ----------------------------------- */
! 13714: if ( !isquery ) isplusblank = 0; /* don't xlate +++'s to blanks */
! 13715: /* ---
1.2 albertel 13716: * check if emulating query (for testing)
13717: * -------------------------------------- */
13718: if ( isqforce ) isquery = 1; /* emulate query string processing */
13719: /* ---
13720: * check if emitting pbm/pgm graphic
13721: * --------------------------------- */
13722: if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
13723: if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
13724: if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
13725: { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
13726: gif_outfile = (char *)NULL; /* reset gif output file */
13727: /*isdumpimage--;*/ } /* and decrement -e count */
1.1 albertel 13728: } /* --- end-of-if(!isquery) --- */
13729: /* ---
13730: * check for <form> input
13731: * ---------------------- */
1.3 ! albertel 13732: if ( isquery ) { /* must be <form method="get"> */
1.1 albertel 13733: if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
13734: { char *delim=strchr(expression,'='); /* find equal following formdata */
13735: if ( delim != (char *)NULL ) /* found unescaped equal sign */
13736: strcpy(expression,delim+1); /* so shift name= out of expression*/
13737: while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
13738: *delim = ' '; /* is "shorthand" for blank space */
13739: /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */
13740: unescape_url(expression,0); /* convert all %xx's to chars */
13741: unescape_url(expression,0); /* repeat */
1.3 ! albertel 13742: if(0) msglevel = FORMLEVEL; /* msglevel for forms */
1.1 albertel 13743: isformdata = 1; } /* set flag to signal form data */
13744: else /* --- query, but not <form> input --- */
1.3 ! albertel 13745: unescape_url(expression,0); } /* convert _all_ %xx's to chars */
1.1 albertel 13746: /* ---
13747: * check queries for embedded prefixes signalling special processing
13748: * ----------------------------------------------------------------- */
13749: if ( isquery ) /* only check queries */
13750: {
13751: /* --- check for msglevel=###$ prefix --- */
13752: if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
13753: { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
13754: if ( delim != (char *)NULL ) /* check that we found delim */
13755: { *delim = '\000'; /* replace delim with null */
13756: if ( seclevel <= 9 ) /* permit msglevel specification */
13757: msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
13758: strcpy(expression,delim+1); } } /* shift out prefix and delim */
13759: /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
13760: if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
13761: { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
13762: if ( delim != (char *)NULL ) /* check that we found delim */
13763: { *delim = '\000'; /* replace delim with null */
13764: if ( seclevel <= 3 ) /* permit logfile specification */
13765: strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */
13766: strcpy(expression,delim+1); } } /* shift out prefix and delim */
13767: } /* --- end-of-if(isquery) --- */
13768: /* ---
13769: * log query (e.g., for debugging)
13770: * ------------------------------- */
13771: if ( isquery ) /* only log query_string's */
13772: if ( msglevel >= LOGLEVEL /* check if logging */
13773: && seclevel <= 5 ) /* and if logging permitted */
13774: if ( logfile != NULL ) /* if a logfile is given */
1.3 ! albertel 13775: if ( *logfile != '\000' ) { /*and if it's not an empty string*/
1.1 albertel 13776: if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */
13777: != NULL ) /* ignore logging if can't open */
13778: {
13779: /* --- default logging --- */
13780: logger(msgfp,msglevel,expression,mimelog); /* log query */
13781: /* --- additional debug logging (argv and environment) --- */
13782: if ( msglevel >= 9 ) /* log environment */
13783: { int i; /*char name[999],*value;*/
13784: fprintf(msgfp,"Command-line arguments...\n");
13785: if ( argc < 1 ) /* no command-line args */
13786: fprintf(msgfp," ...argc=%d, no argv[] variables\n",argc);
13787: else
13788: for ( i=0; i<argc; i++ ) /* display all argv[]'s */
13789: fprintf(msgfp," argv[%d] = \"%s\"\n",i,argv[i]);
13790: #ifdef DUMPENVP /* char *envp[] available for dump */
13791: fprintf(msgfp,"Environment variables (using envp[])...\n");
13792: if ( envp == (char **)NULL ) /* envp not provided */
13793: fprintf(msgfp," ...envp[] environment variables not available\n");
13794: else
13795: for ( i=0; ; i++ ) /* display all envp[]'s */
13796: if ( envp[i] == (char *)NULL ) break;
13797: else fprintf(msgfp," envp[%d] = \"%s\"\n",i,envp[i]);
13798: #endif /* --- DUMPENVP ---*/
13799: #ifdef DUMPENVIRON /* skip what should be redundant output */
13800: fprintf(msgfp,"Environment variables (using environ)...\n");
13801: if ( environ == (char **)NULL ) /* environ not provided */
13802: fprintf(msgfp," ...extern environ variables not available\n");
13803: else
13804: for ( i=0; ; i++ ) /*display environ[] and getenv()'s*/
13805: if ( environ[i] == (char *)NULL ) break;
13806: else {
13807: strcpy(name,environ[i]); /* set up name for getenv() arg */
13808: if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
13809: { *value = '\000'; /* got it, so null-terminate name */
13810: value = getenv(name); } /* and look up name using getenv() */
13811: else strcpy(name,"NULL"); /* missing = delim in environ[i] */
13812: fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
13813: i,environ[i],name,(value==NULL?"NULL":value));
13814: } /* --- end-of-if/else --- */
13815: #endif /* --- DUMPENVIRON ---*/
13816: } /* --- end-of-if(msglevel>=9) --- */
13817: /* --- close log file if no longer needed --- */
13818: if ( msglevel < DBGLEVEL ) /* logging, but not debugging */
13819: { fprintf(msgfp,"%s\n",dashes); /* so log separator line, */
13820: fclose(msgfp); /* close logfile immediately, */
13821: msgfp = NULL; } /* and reset msgfp pointer */
13822: else
13823: isqlogging = 1; /* set query logging flag */
13824: } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
13825: else /* couldn't open logfile */
1.3 ! albertel 13826: msglevel = 0; } /* can't emit messages */
1.1 albertel 13827: /* ---
13828: * prepend prefix to submitted expression
13829: * -------------------------------------- */
13830: if ( 1 || isquery ) /* queries or command-line */
13831: if ( *exprprefix != '\000' ) /* we have a prefix string */
13832: { int npref = strlen(exprprefix); /* #chars in prefix */
13833: memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/
13834: memcpy(expression,exprprefix,npref); /* copy prefix into expression */
13835: expression[npref] = '{'; /* followed by { */
13836: strcat(expression,"}"); } /* and terminating } to balance { */
13837: /* ---
13838: * check if http_referer is allowed to use this image
13839: * -------------------------------------------------- */
13840: if ( isquery ) /* not relevant if "interactive" */
13841: if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */
13842: if ( strcmp(referer,"month") != 0 ) /* nor if it's *only* "month" */
13843: if ( http_referer != NULL ) /* nor if called "standalone" */
13844: if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */
13845: { expression = invalid_referer_msg; /* so give user error message */
13846: isinvalidreferer = 1; } /* and signal invalid referer */
13847: /* ---
13848: * check if referer contains "month" signal
13849: * ---------------------------------------- */
13850: if ( isquery ) /* not relevant if "interactive" */
13851: if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */
13852: if ( !isinvalidreferer ) /* nor if already invalid referer */
13853: if ( strstr(referer,"month") != NULL ) /* month check requested */
13854: if ( !ismonth(progname) ) /* not executed as mimetexJan-Dec */
13855: { expression = invalid_referer_msg; /* so give user error message */
13856: isinvalidreferer = 1; } /* and signal invalid referer */
13857: /* ---
13858: * check if http_referer is to be denied access
13859: * -------------------------------------------- */
13860: if ( isquery ) /* not relevant if "interactive" */
13861: if ( !isinvalidreferer ) /* nor if already invalid referer */
13862: { int iref=0, msgnum=(-999); /* denyreferer index, message# */
13863: for ( iref=0; msgnum<0; iref++ ) { /* run through denyreferer[] table */
13864: char *deny = denyreferer[iref].referer; /* referer to be denied */
13865: if ( deny == NULL ) break; /* null signals end-of-table */
13866: if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
13867: {fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
13868: iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
13869: if ( *deny == '\000' ) /* signal to check for no referer */
13870: { if ( http_referer == NULL ) /* http_referer not supplied */
13871: msgnum = denyreferer[iref].msgnum; } /* so set message# */
13872: else /* have referer to check for */
13873: if ( http_referer != NULL ) /* and have referer to be checked */
13874: if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
13875: msgnum = denyreferer[iref].msgnum; /* so set message# */
13876: } /* --- end-of-for(iref) --- */
13877: if ( msgnum >= 0 ) /* deny access to this referer */
13878: { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
13879: expression = msgtable[msgnum]; /* set user error message */
13880: isinvalidreferer = 1; } /* and signal invalid referer */
13881: } /* --- end-of-if(!isinvalidreferer) --- */
13882: /* --- also check maximum query_string length if no http_referer given --- */
13883: if ( isquery ) /* not relevant if "interactive" */
13884: if ( !isinvalidreferer ) /* nor if already invalid referer */
13885: if ( !ishttpreferer ) /* no http_referer supplied */
13886: if ( strlen(expression) > norefmaxlen ) /* query_string too long */
13887: { expression = invalid_referer_msg; /* set invalid http_referer message*/
13888: isinvalidreferer = 1; } /* and signal invalid referer */
13889: /* ---
13890: * check for image caching
13891: * ----------------------- */
1.2 albertel 13892: if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */
13893: || strstr(expression,"\\input") != NULL /* can't cache \input{} */
13894: || strstr(expression,"\\today") != NULL /* can't cache \today */
13895: || strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
13896: || strstr(expression,"\\nocach") != NULL /* no caching requested */
13897: || isformdata /* don't cache user form input */
1.1 albertel 13898: ) { iscaching = 0; /* so turn caching off */
1.2 albertel 13899: maxage = 5; } /* and set max-age to 5 seconds */
1.1 albertel 13900: if ( isquery ) /* don't cache command-line images */
13901: if ( iscaching ) /* image caching enabled */
13902: {
13903: /* --- set up path to cached image file --- */
13904: char *md5hash = md5str(expression); /* md5 hash of expression */
13905: if ( md5hash == NULL ) /* failed for some reason */
13906: iscaching = 0; /* so turn off caching */
13907: else
13908: {
13909: strcpy(cachefile,cachepath); /* start with (relative) path */
13910: strcat(cachefile,md5hash); /* add md5 hash of expression */
13911: strcat(cachefile,".gif"); /* finish with .gif extension */
13912: gif_outfile = cachefile; /* signal GIF_Create() to cache */
1.2 albertel 13913: /* --- emit mime content-type line --- */
1.3 ! albertel 13914: if ( 0 && isemitcontenttype ) /* now done in emitcache() */
1.2 albertel 13915: { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
13916: fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.1 albertel 13917: /* --- emit cached image if it already exists --- */
1.2 albertel 13918: if ( emitcache(cachefile,maxage,0) > 0 ) /* cached image emitted */
1.1 albertel 13919: goto end_of_job; /* so nothing else to do */
13920: /* --- log caching request --- */
13921: if ( msglevel >= 1 /* check if logging */
13922: /*&& seclevel <= 5*/ ) /* and if logging permitted */
13923: if ( cachelog != NULL ) /* if a logfile is given */
13924: if ( *cachelog != '\000' ) /*and if it's not an empty string*/
13925: { char filename[256]; /* construct cachepath/cachelog */
13926: FILE *filefp=NULL; /* fopen(filename) */
13927: strcpy(filename,cachepath); /* start with (relative) path */
13928: strcat(filename,cachelog); /* add cache log filename */
13929: if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
13930: != NULL ) /* ignore logging if can't open */
13931: { int isreflogged = 0; /* set true if http_referer logged */
13932: fprintf(filefp,"%s %s\n", /* timestamp, md5 file */
1.2 albertel 13933: timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
1.1 albertel 13934: fprintf(filefp,"%s\n",expression); /* expression in filename */
13935: if ( http_referer != NULL ) /* show referer if we have one */
13936: if ( *http_referer != '\000' ) /* and if not an empty string*/
13937: { int loglen = strlen(dashes); /* #chars on line in log file*/
13938: char *refp = http_referer; /* line to be printed */
13939: isreflogged = 1; /* signal http_referer logged*/
13940: while ( 1 ) { /* printed in parts if needed*/
13941: fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
13942: if ( strlen(refp) <= loglen ) break; /* no more parts */
13943: refp += loglen; } } /* bump ptr to next part */
13944: if ( !isreflogged ) /* http_referer not logged */
13945: fprintf(filefp,"http://none\n"); /* so log dummy referer line */
13946: fprintf(filefp,"%s\n",dashes); /* separator line */
13947: fclose(filefp); } /* close logfile immediately */
13948: } /* --- end-of-if(cachelog!=NULL) --- */
13949: } /* --- end-of-if/else(md5hash==NULL) --- */
13950: } /* --- end-of-if(iscaching) --- */
13951: /* ---
13952: * emit copyright, gnu/gpl notice (if "interactive")
13953: * ------------------------------------------------- */
13954: if ( !isdumpimage ) /* don't mix ascii with image dump */
13955: if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */
13956: fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */
13957: /* -------------------------------------------------------------------------
13958: rasterize expression and put a border around it
13959: -------------------------------------------------------------------------- */
13960: /* --- preprocess expression, converting LaTeX constructs for mimeTeX --- */
13961: expression = mimeprep(expression); /* preprocess expression */
13962: /* --- double-check that we actually have an expression to rasterize --- */
13963: if ( expression == NULL ) /* nothing to rasterize */
13964: { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
13965: fprintf(msgfp,"No expression to rasterize\n");
13966: goto end_of_job; } /* and then quit */
13967: /* --- rasterize expression --- */
13968: if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */
13969: { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
13970: fprintf(msgfp,"Failed to rasterize %s\n",expression);
13971: if ( isquery ) sp = rasterize( /* or emit error raster if query */
13972: "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1);
13973: if ( sp == NULL ) goto end_of_job; } /* re-check for failure */
13974: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
13975: if ( issupersampling ) /* no border needed for gifs */
13976: bp = sp->image; /* so just extract pixel map */
13977: else /* for mime xbitmaps must have... */
13978: bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
13979: sp->image = bitmap_raster = bp; /* global copy for gif,png output */
1.2 albertel 13980: if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
13981: type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
1.1 albertel 13982: /* -------------------------------------------------------------------------
13983: generate anti-aliased bytemap from (bordered) bitmap
13984: -------------------------------------------------------------------------- */
13985: if ( isaa ) /* we want anti-aliased bitmap */
13986: {
13987: /* ---
13988: * allocate bytemap and colormap as per width*height of bitmap
13989: * ----------------------------------------------------------- */
13990: int nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/
13991: if ( isss ) /* anti-aliasing by supersampling */
13992: bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
13993: else /* need to allocate bytemap */
13994: if ( aaalgorithm == 0 ) /* anti-aliasing not wanted */
13995: isaa = 0; /* so signal no anti-aliasing */
13996: else /* anti-aliasing wanted */
13997: if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
13998: == NULL ) isaa = 0; /* reset flag if malloc failed */
13999: if ( isaa ) /* have bytemap, so... */
14000: if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */
14001: == NULL ) isaa = 0; /* reset flag if malloc failed */
14002: /* ---
14003: * now generate anti-aliased bytemap and colormap from bitmap
14004: * ---------------------------------------------------------- */
14005: if ( isaa ) /*re-check that we're anti-aliasing*/
14006: {
14007: /* ---
14008: * select anti-aliasing algorithm
14009: * ------------------------------ */
14010: if ( !isss ) /* generate bytemap for lowpass */
1.3 ! albertel 14011: switch ( aaalgorithm ) { /* choose antialiasing algorithm */
! 14012: default: isaa = 0; break; /* unrecognized algorithm */
! 14013: case 1: /* 1 for aalowpass() */
! 14014: if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/
! 14015: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
! 14016: break;
! 14017: case 2: /*2 for netpbm pnmalias.c algorithm*/
! 14018: if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
! 14019: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
! 14020: break;
! 14021: case 3: /*3 for aapnm() based on aagridnum()*/
! 14022: if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
! 14023: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
! 14024: break;
! 14025: case 4: /* 4 for aalookup() table lookup */
! 14026: if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */
! 14027: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
! 14028: break;
! 14029: } /* --- end-of-switch(aaalgorithm) --- */
! 14030: /* ---
! 14031: * emit aalookup() pattern# counts/percents diagnostics
! 14032: * ---------------------------------------------------- */
! 14033: if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/
! 14034: int pcount0=0, pcount1=0; /* init total w,b center counts */
! 14035: for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
! 14036: if ( ipattern > 1 ) /* ignore all-white squares */
! 14037: pcount0 += patternnumcount0[ipattern]; /* bump total white centers */
! 14038: pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */
! 14039: if ( pcount0+pcount1 > 0 ) /* have pcounts (using aalookup) */
! 14040: fprintf(msgfp, " aalookup() patterns excluding#1 white"
! 14041: " (%%'s are in tenths of a percent)...\n");
! 14042: for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
! 14043: int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern];
! 14044: if ( tot > 0 ) /* this pattern occurs in image */
! 14045: fprintf(msgfp,
! 14046: " pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n",
! 14047: ipattern, patternnumcount0[ipattern], (ipattern<=1? 999.99:
! 14048: 1000.*((double)patternnumcount0[ipattern])/((double)pcount0)),
! 14049: patternnumcount1[ipattern],
! 14050: 1000.*((double)patternnumcount1[ipattern])/((double)pcount1),
! 14051: tot, (ipattern<=1? 999.99:
! 14052: 1000.*((double)tot)/((double)(pcount0+pcount1))) ); }
! 14053: if ( pcount0+pcount1 > 0 ) /* true when using aalookup() */
! 14054: fprintf(msgfp,
! 14055: "all patterns: %7d +%7d =%7d total pixels\n",
! 14056: pcount0,pcount1,pcount0+pcount1); }
1.1 albertel 14057: /* ---
14058: * finally, generate colors and colormap
14059: * ------------------------------------- */
14060: if ( isaa ) { /* we have bytemap_raster */
14061: ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
14062: if ( ncolors < 2 ) /* failed */
14063: { isaa = 0; /* so turn off anti-aliasing */
14064: ncolors = 2; } /* and reset for black&white */
14065: } /* --- end-of-if(isaa) --- */
1.2 albertel 14066: if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch */
14067: raster pbm_raster; /*construct arg for write_pbmpgm()*/
14068: pbm_raster.width = bp->width; pbm_raster.height = bp->height;
14069: pbm_raster.pixsz = 8; pbm_raster.pixmap = (pixbyte *)bytemap_raster;
14070: type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
1.1 albertel 14071: } /* --- end-of-if(isaa) --- */
14072: } /* --- end-of-if(isaa) --- */
14073: /* -------------------------------------------------------------------------
14074: display results on msgfp if called from command line (usually for testing)
14075: -------------------------------------------------------------------------- */
14076: if ( (!isquery||isqlogging) || msglevel >= 99 ) /*command line or debuging*/
14077: if ( !isdumpimage ) /* don't mix ascii with image dump */
14078: {
14079: /* ---
14080: * display ascii image of rasterize()'s rasterized bitmap
14081: * ------------------------------------------------------ */
14082: if ( !isss ) /* no bitmap for supersampling */
14083: { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
14084: type_raster(bp,msgfp); } /* emit ascii image of raster */
14085: /* ---
14086: * display anti-aliasing results applied to rasterized bitmap
14087: * ---------------------------------------------------------- */
14088: if ( isaa ) /* if anti-aliasing applied */
14089: {
14090: int igray; /* colors[] index */
14091: /* --- anti-aliased bytemap image --- */
14092: if ( msgfp!=NULL && msglevel>=9 ) /* don't usually emit raw bytemap */
14093: { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
14094: "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
14095: type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); }
14096: /* --- colormap image --- */
14097: fprintf(msgfp,"\nHex dump of colormap indexes, " /* emit colormap */
14098: "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
14099: type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp);
14100: /* --- rgb values corresponding to colormap indexes */
14101: fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
14102: for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
14103: fprintf(msgfp,"%s%2x-->%3d", (igray%5?" ":"\n"),
14104: igray,(int)(colors[ncolors-1]-colors[igray]));
14105: fprintf(msgfp,"\n"); /* always needs a final newline */
14106: } /* --- end-of-if(isaa) --- */
14107: } /* --- end-of-if(!isquery||msglevel>=9) --- */
14108: /* -------------------------------------------------------------------------
14109: emit xbitmap or gif image, and exit
14110: -------------------------------------------------------------------------- */
14111: if ( isquery /* called from browser (usual) */
1.2 albertel 14112: || (isdumpimage && !ispbmpgm) /* or to emit gif dump of image */
1.1 albertel 14113: || msglevel >= 99 ) /* or for debugging */
14114: {
14115: int igray = 0; /* grayscale index */
14116: #if defined(GIF) /* compiled to emit gif */
14117: /* ------------------------------------------------------------------------
14118: emit GIF image
14119: ------------------------------------------------------------------------- */
1.2 albertel 14120: /* --- don't use memory buffer if outout file given --- */
14121: if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
1.1 albertel 14122: /* --- emit mime content-type line --- */
1.3 ! albertel 14123: if ( isemitcontenttype /* content-type lines wanted */
! 14124: && !isdumpimage /* don't mix ascii with image dump */
1.2 albertel 14125: && !isinmemory /* done below if in memory */
14126: && !iscaching ) /* done by emitcache() if caching */
1.1 albertel 14127: { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
14128: /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
14129: /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/
14130: fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.2 albertel 14131: /* --- write output to memory buffer, possibly for testing --- */
14132: if ( isinmemory /* want gif written to memory */
14133: || isdumpbuffer ) /*or dump memory buffer for testing*/
14134: if ( gif_outfile == NULL ) /* and don't already have a file */
14135: { *gif_buffer = '\000'; /* init buffer as empty string */
1.3 ! albertel 14136: memset(gif_buffer,0,MAXGIFSZ); /* zero out buffer */
1.2 albertel 14137: gif_outfile = gif_buffer; /* and point outfile to buffer */
14138: if ( isdumpbuffer ) /* buffer dump test requested */
14139: isdumpbuffer = 999; } /* so signal dumping to buffer */
1.1 albertel 14140: /* --- initialize gifsave library and colors --- */
14141: if ( msgfp!=NULL && msglevel>=999 )
1.2 albertel 14142: { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
14143: bp->width,bp->height,ncolors); fflush(msgfp); }
1.1 albertel 14144: while ( 1 ) /* init gifsave lib, and retry if caching fails */
14145: { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);
14146: if ( status == 0 ) break; /* continue if succeeded */
14147: if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
14148: iscaching = 0; /* retry without cache file */
1.2 albertel 14149: isdumpbuffer = 0; /* reset isdumpbuffer signal */
14150: if ( isquery ) isinmemory = 1; /* force in-memory image generation*/
14151: if ( isinmemory ) { /* using memory buffer */
14152: gif_outfile = gif_buffer; /* emit images to memory buffer */
14153: *gif_outfile = '\000'; } /* empty string signals buffer */
14154: else { /* or */
14155: gif_outfile = (char *)NULL; /* emit images to stdout */
1.3 ! albertel 14156: if ( isemitcontenttype ) { /* content-type lines wanted */
! 14157: fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
! 14158: fprintf( stdout, "Content-type: image/gif\n\n" ); } }
1.2 albertel 14159: } /* --- end-of-while(1) --- */
1.1 albertel 14160: GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */
14161: if ( !isaa ) /* just b&w if not anti-aliased */
14162: { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
14163: colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
14164: else /* set grayscales for anti-aliasing */
14165: /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
14166: for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
14167: {
14168: /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
14169: double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
14170: /* --- r,g,b components go from background to foreground color --- */
14171: int red = iround(((double)bgred) +gfrac*((double)(fgred-bgred))),
14172: green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
14173: blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
14174: /* --- set color index number igray to rgb values gray,gray,gray --- */
14175: GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
14176: } /* --- end-of-for(igray) --- */
14177: /* --- set gif color#0 (background) transparent --- */
14178: if ( istransparent ) /* transparent background wanted */
14179: GIF_SetTransparent(0); /* set transparent background */
14180: if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
14181: /* --- emit compressed gif image (to stdout or cache file) --- */
14182: GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
14183: GIF_Close(); /* close file */
1.2 albertel 14184: if ( msgfp!=NULL && msglevel>=9 )
14185: { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
14186: fflush(msgfp); }
14187: /* --- may need to emit image from cached file or from memory --- */
14188: if ( isquery /* have an actual query string */
14189: || isdumpimage /* or dumping image */
14190: || msglevel >= 99 ) { /* or debugging */
14191: int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
14192: if ( iscaching ) /* caching enabled */
14193: emitcache(cachefile,maxage2,0); /* cached image (hopefully) emitted*/
14194: else if ( isinmemory ) /* or emit image from memory buffer*/
14195: emitcache(gif_buffer,maxage2,1); } /* emitted from memory buffer */
14196: /* --- for testing, may need to write image buffer to file --- */
14197: if ( isdumpbuffer > 99 ) /* gif image in memory buffer */
14198: if ( gifSize > 0 ) /* and it's not an empty buffer */
14199: { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
14200: if ( dumpfp != NULL ) /* file opened successfully */
14201: { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
14202: fclose(dumpfp); } /* and close file */
14203: } /* --- end-of-if(isdumpbuffer>99) --- */
1.1 albertel 14204: #else
14205: /* ------------------------------------------------------------------------
14206: emit mime XBITMAP image
14207: ------------------------------------------------------------------------- */
14208: xbitmap_raster(bp,stdout); /* default emits mime xbitmap */
14209: #endif
14210: } /* --- end-of-if(isquery) --- */
14211: /* --- exit --- */
14212: end_of_job:
1.2 albertel 14213: if ( !isss ) /*bytemap raster in sp for supersamp*/
14214: if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
1.1 albertel 14215: if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
1.2 albertel 14216: if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
14217: if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */
1.1 albertel 14218: if ( msgfp != NULL /* have message/log file open */
14219: && msgfp != stdout ) /* and it's not stdout */
1.2 albertel 14220: { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
14221: timestamp(TZDELTA,0));
1.1 albertel 14222: fprintf(msgfp,"%s\n",dashes); /* so log separator line */
14223: fclose(msgfp); } /* and close logfile */
1.2 albertel 14224: /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
14225: #if defined(_CRTDBG_MAP_ALLOC)
14226: _CrtDumpMemoryLeaks();
14227: #endif
14228: /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
14229: #if !defined(_USRDLL)
14230: exit ( 0 );
14231: #endif
1.1 albertel 14232: } /* --- end-of-function main() --- */
14233:
14234: /* ==========================================================================
1.2 albertel 14235: * Function: CreateGifFromEq ( expression, gifFileName )
14236: * Purpose: shortcut method to create GIF file for expression,
14237: * with antialising and all other capabilities
14238: * --------------------------------------------------------------------------
14239: * Arguments: expression (I) char *ptr to null-terminated string
14240: * containing LaTeX expression to be rendred
14241: * gifFileName (I) char *ptr to null-terminated string
14242: * containing name of output gif file
14243: * --------------------------------------------------------------------------
14244: * Returns: ( int ) exit value from main (0 if successful)
14245: * --------------------------------------------------------------------------
14246: * Notes: o This function is the entry point when mimeTeX is built
14247: * as a Win32 DLL rather then a standalone app or CGI
14248: * o Contributed to mimeTeX by Shital Shah. See his homepage
14249: * http://www.shitalshah.com
14250: * o Shital discusses the mimeTeX Win32 DLL project at
14251: * http://www.codeproject.com/dotnet/Eq2Img.asp
14252: * and you can download his latest code from
14253: * http://www.shitalshah.com/dev/eq2img_all.zip
14254: * ======================================================================= */
14255: /* --- include function to expose Win32 DLL to outside world --- */
14256: #if defined(_USRDLL)
14257: extern _declspec(dllexport)int _cdecl
14258: CreateGifFromEq ( char *expression, char *gifFileName );
14259: #endif
14260: /* --- entry point --- */
14261: int CreateGifFromEq ( char *expression, char *gifFileName )
14262: {
14263: /* -------------------------------------------------------------------------
14264: Allocations and Declarations
14265: -------------------------------------------------------------------------- */
14266: int main(); /* main() akways returns an int */
14267: /* --- set constants --- */
14268: int argc = 4; /* count of args supplied to main() */
14269: char *argv[5] = /* command line args to run with -e option */
14270: { "MimeTeXWin32DLL", "-e", /* constant args */
14271: /*gifFileName, expression,*/ NULL, NULL, NULL };
14272: /* --- set argv[]'s not computable at load time --- */
14273: argv[2] = gifFileName; /* args are -e gifFileName */
14274: argv[3] = expression; /* and now -e gifFileName expression */
14275: /* -------------------------------------------------------------------------
14276: Run mimeTeX in command-line mode with -e (export) option, and then return
14277: -------------------------------------------------------------------------- */
14278: return main ( argc, argv
14279: #ifdef DUMPENVP
14280: , NULL
14281: #endif
14282: ) ;
14283: } /* --- end-of-function CreateGifFromEq() --- */
14284:
14285: /* ==========================================================================
1.1 albertel 14286: * Function: isstrstr ( char *string, char *snippets, int iscase )
14287: * Purpose: determine whether any substring of 'string'
14288: * matches any of the comma-separated list of 'snippets',
14289: * ignoring case if iscase=0.
14290: * --------------------------------------------------------------------------
14291: * Arguments: string (I) char * containing null-terminated
14292: * string that will be searched for
14293: * any one of the specified snippets
14294: * snippets (I) char * containing null-terminated,
14295: * comma-separated list of snippets
14296: * to be searched for in string
14297: * iscase (I) int containing 0 for case-insensitive
14298: * comparisons, or 1 for case-sensitive
14299: * --------------------------------------------------------------------------
14300: * Returns: ( int ) 1 if any snippet is a substring of
14301: * string, 0 if not
14302: * --------------------------------------------------------------------------
14303: * Notes: o
14304: * ======================================================================= */
14305: /* --- entry point --- */
14306: int isstrstr ( char *string, char *snippets, int iscase )
14307: {
14308: /* -------------------------------------------------------------------------
14309: Allocations and Declarations
14310: -------------------------------------------------------------------------- */
14311: int status = 0; /*1 if any snippet found in string*/
14312: char snip[99], *snipptr = snippets, /* munge through each snippet */
14313: delim = ',', *delimptr = NULL; /* separated by delim's */
14314: char stringcp[999], *cp = stringcp; /*maybe lowercased copy of string*/
14315: /* -------------------------------------------------------------------------
14316: initialization
14317: -------------------------------------------------------------------------- */
14318: /* --- arg check --- */
14319: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
14320: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
14321: /* --- copy string and lowercase it if case-insensitive --- */
14322: strcpy(stringcp,string); /* local copy of string */
14323: if ( !iscase ) /* want case-insensitive compares */
14324: for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
14325: if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
14326: /* -------------------------------------------------------------------------
14327: extract each snippet and see if it's a substring of string
14328: -------------------------------------------------------------------------- */
14329: while ( snipptr != NULL ) /* while we still have snippets */
14330: {
14331: /* --- extract next snippet --- */
14332: if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
14333: == NULL ) /*not found following last snippet*/
14334: { strcpy(snip,snipptr); /* local copy of last snippet */
14335: snipptr = NULL; } /* signal end-of-string */
14336: else /* snippet ends just before delim */
14337: { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */
14338: memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */
14339: snip[sniplen] = '\000'; /* null-terminated snippet */
14340: snipptr = delimptr + 1; } /* next snippet starts after delim */
14341: /* --- lowercase snippet if case-insensitive --- */
14342: if ( !iscase ) /* want case-insensitive compares */
14343: for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
14344: if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
14345: /* --- check if snippet in string --- */
14346: if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */
14347: { status = 1; /* so reset return status */
14348: break; } /* no need to check any further */
14349: } /* --- end-of-while(*snipptr!=0) --- */
14350: end_of_job: return ( status ); /*1 if snippet found in list, else 0*/
14351: } /* --- end-of-function isstrstr() --- */
14352:
14353: /* ==========================================================================
14354: * Function: ismonth ( char *month )
14355: * Purpose: returns 1 if month contains current month "jan"..."dec".
14356: * --------------------------------------------------------------------------
14357: * Arguments: month (I) char * containing null-terminated string
14358: * in which "jan"..."dec" is (putatively)
14359: * contained as a substring.
14360: * --------------------------------------------------------------------------
14361: * Returns: ( int ) 1 if month contains current month,
14362: * 0 otherwise
14363: * --------------------------------------------------------------------------
14364: * Notes: o There's a three day "grace period", e.g., Dec 3 mtaches Nov.
14365: * ======================================================================= */
14366: /* --- entry point --- */
14367: int ismonth ( char *month )
14368: {
14369: /* -------------------------------------------------------------------------
14370: Allocations and Declarations
14371: -------------------------------------------------------------------------- */
14372: int isokay = 0; /*1 if month contains current month*/
14373: /*long time_val = 0L;*/ /* binary value returned by time() */
14374: time_t time_val = (time_t)(0); /* binary value returned by time() */
14375: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
14376: int imonth, mday; /* current month 1-12 and day 1-31 */
14377: int ngrace = 3; /* grace period */
14378: char lcmonth[128]="\000"; int i=0; /* lowercase month */
14379: static char *months[] = /* month must contain current one */
14380: {"dec","jan","feb","mar","apr","may","jun",
14381: "jul","aug","sep","oct","nov","dec","jan"};
14382: /* -------------------------------------------------------------------------
14383: get current date:time info, and check month
14384: -------------------------------------------------------------------------- */
14385: /* --- lowercase input month --- */
14386: if ( month != NULL ) /* check that we got input */
14387: for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
14388: lcmonth[i] = tolower(*month); /* lowerase each char in month */
14389: if ( i < 2 ) goto end_of_job; /* must be invalid input */
14390: lcmonth[i] = '\000'; /* null-terminate lcmonth[] */
14391: /* --- get current date:time --- */
14392: time((time_t *)(&time_val)); /* get date and time */
14393: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
14394: /* --- month and day --- */
14395: imonth = 1 + (int)(tmstruct->tm_mon); /* 1=jan ... 12=dec */
14396: mday = (int)(tmstruct->tm_mday); /* 1-31 */
14397: if ( imonth<1 || imonth>12 /* quit if month out-of-range */
14398: || mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
14399: /* --- check input month against current date --- */
14400: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
14401: if ( mday <= ngrace ) /* 1-3 within grace period */
14402: if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
14403: if ( mday >= 31-ngrace ) /* 28-31 within grace period */
14404: if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
14405: end_of_job:
14406: return ( isokay ); /*1 if month contains current month*/
14407: } /* --- end-of-function ismonth() --- */
14408:
14409: /* ==========================================================================
14410: * Functions: int unescape_url ( char *url, int isescape )
14411: * char x2c ( char *what )
14412: * Purpose: unescape_url replaces 3-character sequences %xx in url
14413: * with the single character represented by hex xx.
14414: * x2c returns the single character represented by hex xx
14415: * passed as a 2-character sequence in what.
14416: * --------------------------------------------------------------------------
14417: * Arguments: url (I) char * containing null-terminated
14418: * string with embedded %xx sequences
14419: * to be converted.
14420: * isescape (I) int containing 1 to _not_ unescape
14421: * \% sequences (0 would be NCSA default)
14422: * what (I) char * whose first 2 characters are
14423: * interpreted as ascii representations
14424: * of hex digits.
14425: * --------------------------------------------------------------------------
14426: * Returns: ( int ) unescape_url always returns 0.
14427: * ( char ) x2c returns the single char
14428: * corresponding to hex xx passed in what.
14429: * --------------------------------------------------------------------------
14430: * Notes: o These two functions were taken verbatim from util.c in
14431: * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
14432: * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
14433: * so unescape_url() can be safely applied to input which may or
1.3 ! albertel 14434: * may not have been url-encoded. (Note: currently, all calls
! 14435: * to unescape_url() pass iescape=0, so it's not used.)
! 14436: * o Added +++'s to blank xlation on 24-Sep-06
! 14437: * o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
1.1 albertel 14438: * ======================================================================= */
14439: /* --- entry point --- */
14440: int unescape_url(char *url, int isescape) {
14441: int x=0,y=0,prevescape=0,gotescape=0;
1.3 ! albertel 14442: int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
! 14443: int strreplace(); /* replace + with blank, if needed */
1.1 albertel 14444: char x2c();
14445: static char *hex="0123456789ABCDEFabcdef";
1.3 ! albertel 14446: /* ---
! 14447: * xlate ctrl chars to blanks
! 14448: * -------------------------- */
! 14449: if ( 1 ) { /* xlate ctrl chars to blanks */
! 14450: char *ctrlchars = "\n\t\v\b\r\f\a\015";
! 14451: int seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
! 14452: int urllen = strlen(url); /* total length of url string */
! 14453: /* --- first, entirely remove ctrlchars from beginning and end --- */
! 14454: if ( seglen > 0 ) { /*have ctrlchars at start of string*/
! 14455: strcpy(url,url+seglen); /* squeeze out initial ctrlchars */
! 14456: urllen -= seglen; } /* string is now shorter */
! 14457: while ( --urllen >= 0 ) /* now remove ctrlchars from end */
! 14458: if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
! 14459: url[urllen] = '\000'; /* re-terminate string before it */
! 14460: else break; /* or we're done */
! 14461: urllen++; /* length of url string */
! 14462: /* --- now, replace interior ctrlchars with ~ blanks --- */
! 14463: while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
! 14464: url[seglen] = '~'; /* replace ctrlchar with ~ */
! 14465: } /* --- end-of-if(1) --- */
! 14466: /* ---
! 14467: * xlate +'s to blanks if requested or if deemed necessary
! 14468: * ------------------------------------------------------- */
! 14469: if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/
! 14470: char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
! 14471: "+=+","+-+", NULL };
! 14472: int isearch = 0, /* searchfor[] index */
! 14473: nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
! 14474: /* --- locate occurrences of searchfor[] strings in url --- */
! 14475: for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
! 14476: char *psearch = url; /* start search at beginning */
! 14477: nfound[isearch] = 0; /* init #occurrences count */
! 14478: while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
! 14479: nfound[isearch] += 1; /* count another occurrence */
! 14480: psearch += strlen(searchfor[isearch]); } /*resume search after it*/
! 14481: } /* --- end-of-for(isearch) --- */
! 14482: /* --- apply some common-sense logic --- */
! 14483: if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */
! 14484: isplusblank = xlateplus = 0; /* so +++'s aren't blanks */
! 14485: if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
! 14486: if ( isplusblank != 0 ) /* and haven't disabled xlation */
! 14487: isplusblank = xlateplus = 1; /* so +++'s are blanks */
! 14488: else /* we have _both_ "%20" and "%2b" */
! 14489: xlateplus = 0; } /* tough call */
! 14490: if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */
! 14491: || nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */
! 14492: if ( isplusblank != 0 ) /* and haven't disabled xlation */
! 14493: xlateplus = 1; /* so xlate +++'s to blanks */
! 14494: } /* --- end-of-if(isplusblank==-1) --- */
! 14495: if ( xlateplus > 0 ) { /* want +'s xlated to blanks */
! 14496: char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
! 14497: while ( xlateplus > 0 ) { /* still have +++'s to xlate */
! 14498: char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
! 14499: plusses[xlateplus] = '\000'; /* null-terminate +++'s */
! 14500: strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
! 14501: xlateplus--; /* next shorter +++ string */
! 14502: } /* --- end-of-while(xlateplus>0) --- */
! 14503: } /* --- end-of-if(xlateplus) --- */
! 14504: isplusblank = 0; /* don't iterate this xlation */
! 14505: /* ---
! 14506: * xlate %nn to corresponding char
! 14507: * ------------------------------- */
1.1 albertel 14508: for(;url[y];++x,++y) {
14509: gotescape = prevescape;
14510: prevescape = (url[x]=='\\');
14511: if((url[x] = url[y]) == '%')
14512: if(!isescape || !gotescape)
14513: if(isthischar(url[y+1],hex)
14514: && isthischar(url[y+2],hex))
14515: { url[x] = x2c(&url[y+1]);
14516: y+=2; }
14517: }
14518: url[x] = '\0';
14519: return 0;
14520: } /* --- end-of-function unescape_url() --- */
14521: /* --- entry point --- */
14522: char x2c(char *what) {
14523: char digit;
14524: digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
14525: digit *= 16;
14526: digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
14527: return(digit);
14528: } /* --- end-of-function x2c() --- */
14529:
14530: /* ==========================================================================
14531: * Function: logger ( fp, msglevel, message, logvars )
14532: * Purpose: Logs the environment variables specified in logvars
14533: * to fp if their msglevel is >= the passed msglevel.
14534: * --------------------------------------------------------------------------
14535: * Arguments: fp (I) FILE * to file containing log
14536: * msglevel (I) int containing logging message level
14537: * message (I) char * to optional message, or NULL
14538: * logvars (I) logdata * to array of environment variables
14539: * to be logged
14540: * --------------------------------------------------------------------------
14541: * Returns: ( int ) number of variables from logvars
14542: * that were actually logged
14543: * --------------------------------------------------------------------------
14544: * Notes: o
14545: * ======================================================================= */
14546: /* --- entry point --- */
14547: int logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
14548: {
14549: /* -------------------------------------------------------------------------
14550: Allocations and Declarations
14551: -------------------------------------------------------------------------- */
14552: int ilog=0, nlogged=0; /* logvars[] index, #vars logged */
14553: char *timestamp(); /* timestamp logged */
14554: char *value = NULL; /* getenv(name) to be logged */
14555: /* -------------------------------------------------------------------------
14556: Log each variable
14557: -------------------------------------------------------------------------- */
1.2 albertel 14558: fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
1.1 albertel 14559: if ( message != NULL ) /* optional message supplied */
14560: fprintf(fp," MESSAGE = %s\n",message); /* emit caller-supplied message */
14561: if ( logvars != (logdata *)NULL ) /* have logvars */
14562: for ( ilog=0; logvars[ilog].name != NULL; ilog++ ) /* till end-of-table */
14563: if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
14564: if ( (value=getenv(logvars[ilog].name)) /* getenv(name) to be logged */
14565: != NULL ) /* check that name exists */
14566: {
14567: fprintf(fp," %s = %.*s\n", /* emit variable name = value */
14568: logvars[ilog].name,logvars[ilog].maxlen,value);
14569: nlogged++; /* bump #vars logged */
14570: } /* --- end-of-for(ilog) --- */
14571: return ( nlogged ); /* back to caller */
14572: } /* --- end-of-function logger() --- */
14573:
14574: /* ==========================================================================
1.2 albertel 14575: * Function: emitcache ( cachefile, maxage, isbuffer )
1.1 albertel 14576: * Purpose: dumps bytes from cachefile to stdout
14577: * --------------------------------------------------------------------------
14578: * Arguments: cachefile (I) pointer to null-terminated char string
1.2 albertel 14579: * containing full path to file to be dumped,
14580: * or contains buffer of bytes to be dumped
14581: * maxage (I) int containing maxage. in seconds, for
14582: * http header, or -1 to not emit headers
14583: * isbuffer (I) 1 if cachefile is buffer of bytes to be
14584: * dumped
1.1 albertel 14585: * --------------------------------------------------------------------------
14586: * Returns: ( int ) #bytes dumped (0 signals error)
14587: * --------------------------------------------------------------------------
14588: * Notes: o
14589: * ======================================================================= */
14590: /* --- entry point --- */
1.2 albertel 14591: int emitcache ( char *cachefile, int maxage, int isbuffer )
1.1 albertel 14592: {
14593: /* -------------------------------------------------------------------------
14594: Allocations and Declarations
14595: -------------------------------------------------------------------------- */
1.2 albertel 14596: int nbytes=gifSize, readcachefile(); /* read cache file */
14597: FILE *emitptr = stdout; /* emit cachefile to stdout */
1.3 ! albertel 14598: unsigned char buffer[MAXGIFSZ+1]; /* bytes from cachefile */
1.2 albertel 14599: unsigned char *buffptr = buffer; /* ptr to buffer */
1.1 albertel 14600: /* -------------------------------------------------------------------------
14601: initialization
14602: -------------------------------------------------------------------------- */
14603: /* --- check that files opened okay --- */
1.2 albertel 14604: if ( emitptr == (FILE *)NULL ) /* failed to open emit file */
1.1 albertel 14605: goto end_of_job; /* so return 0 bytes to caller */
1.2 albertel 14606: /* --- read the file if necessary --- */
14607: if ( isbuffer ) /* cachefile is buffer */
14608: buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */
14609: else /* cachefile is file name */
14610: if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
14611: < 1 ) goto end_of_job; /* quit if file not read */
14612: /* --- first emit http headers if requested --- */
1.3 ! albertel 14613: if ( isemitcontenttype /* content-type lines enabled */
! 14614: && maxage >= 0 ) /* caller wants http headers */
1.2 albertel 14615: { /* --- emit mime content-type line --- */
14616: fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
14617: fprintf( emitptr, "Content-Length: %d\n",nbytes );
14618: fprintf( emitptr, "Content-type: image/gif\n\n" ); }
14619: /* -------------------------------------------------------------------------
14620: set stdout to binary mode (for Windows)
14621: -------------------------------------------------------------------------- */
1.1 albertel 14622: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */ /* doesn't work portably, */
14623: #ifdef WINDOWS /* so instead... */
14624: #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */
14625: if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
14626: == -1 ) ; /* handle error */ /* sets stdout to binary mode */
14627: #else /* setmode() not available */
14628: #if 1
14629: freopen ("CON", "wb", stdout); /* freopen() stdout binary */
14630: #else
14631: stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
14632: #endif
14633: #endif
14634: #endif
14635: /* -------------------------------------------------------------------------
14636: emit bytes from cachefile
14637: -------------------------------------------------------------------------- */
1.2 albertel 14638: /* --- write bytes to stdout --- */
14639: if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
14640: < nbytes ) /* failed to write all bytes */
14641: nbytes = 0; /* reset total count to 0 */
14642: end_of_job:
14643: return ( nbytes ); /* back with #bytes emitted */
14644: } /* --- end-of-function emitcache() --- */
14645:
14646: /* ==========================================================================
14647: * Function: readcachefile ( cachefile, buffer )
14648: * Purpose: read cachefile into buffer
14649: * --------------------------------------------------------------------------
14650: * Arguments: cachefile (I) pointer to null-terminated char string
14651: * containing full path to file to be read
14652: * buffer (O) pointer to unsigned char string
14653: * returning contents of cachefile
14654: * (max 64000 bytes)
14655: * --------------------------------------------------------------------------
14656: * Returns: ( int ) #bytes read (0 signals error)
14657: * --------------------------------------------------------------------------
14658: * Notes: o
14659: * ======================================================================= */
14660: /* --- entry point --- */
14661: int readcachefile ( char *cachefile, unsigned char *buffer )
14662: {
14663: /* -------------------------------------------------------------------------
14664: Allocations and Declarations
14665: -------------------------------------------------------------------------- */
14666: FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
14667: unsigned char cachebuff[64]; /* bytes from cachefile */
14668: int buflen = 32, /* #bytes we try to read from file */
14669: nread = 0, /* #bytes actually read from file */
1.3 ! albertel 14670: maxbytes = MAXGIFSZ, /* max #bytes returned in buffer */
1.2 albertel 14671: nbytes = 0; /* total #bytes read */
14672: /* -------------------------------------------------------------------------
14673: initialization
14674: -------------------------------------------------------------------------- */
14675: /* --- check that files opened okay --- */
14676: if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
14677: /* --- check that output buffer provided --- */
14678: if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
14679: /* -------------------------------------------------------------------------
14680: read bytes from cachefile
14681: -------------------------------------------------------------------------- */
1.1 albertel 14682: while ( 1 )
14683: {
14684: /* --- read bytes from cachefile --- */
1.2 albertel 14685: nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
14686: if ( nbytes + nread > maxbytes ) /* block too big for buffer */
14687: nread = maxbytes - nbytes; /* so truncate it */
1.1 albertel 14688: if ( nread < 1 ) break; /* no bytes left in cachefile */
1.2 albertel 14689: /* --- store bytes in buffer --- */
14690: memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
14691: /* --- ready to read next block --- */
1.1 albertel 14692: nbytes += nread; /* bump total #bytes emitted */
14693: if ( nread < buflen ) break; /* no bytes left in cachefile */
1.2 albertel 14694: if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */
1.1 albertel 14695: } /* --- end-of-while(1) --- */
14696: end_of_job:
14697: if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
14698: return ( nbytes ); /* back with #bytes emitted */
1.2 albertel 14699: } /* --- end-of-function readcachefile() --- */
1.1 albertel 14700:
14701: /* ==========================================================================
14702: * Function: md5str ( instr )
14703: * Purpose: returns null-terminated character string containing
14704: * md5 hash of instr (input string)
14705: * --------------------------------------------------------------------------
14706: * Arguments: instr (I) pointer to null-terminated char string
14707: * containing input string whose md5 hash
14708: * is desired
14709: * --------------------------------------------------------------------------
14710: * Returns: ( char * ) ptr to null-terminated 32-character
14711: * md5 hash of instr
14712: * --------------------------------------------------------------------------
14713: * Notes: o Other md5 library functions are included below.
14714: * They're all taken from Christophe Devine's code,
14715: * which (as of 04-Aug-2004) is available from
14716: * http://www.cr0.net:8040/code/crypto/md5/
14717: * o The P,F,S macros in the original code are replaced
14718: * by four functions P1()...P4() to accommodate a problem
14719: * with Compaq's vax/vms C compiler.
14720: * ======================================================================= */
14721: /* --- #include "md5.h" --- */
14722: #ifndef uint8
14723: #define uint8 unsigned char
14724: #endif
14725: #ifndef uint32
14726: #define uint32 unsigned long int
14727: #endif
14728: typedef struct
14729: { uint32 total[2];
14730: uint32 state[4];
14731: uint8 buffer[64];
14732: } md5_context;
14733: void md5_starts( md5_context *ctx );
14734: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
14735: void md5_finish( md5_context *ctx, uint8 digest[16] );
14736: /* --- md5.h --- */
14737: #define GET_UINT32(n,b,i) \
14738: { (n) = ( (uint32) (b)[(i) ] ) \
14739: | ( (uint32) (b)[(i) + 1] << 8 ) \
14740: | ( (uint32) (b)[(i) + 2] << 16 ) \
14741: | ( (uint32) (b)[(i) + 3] << 24 ); }
14742: #define PUT_UINT32(n,b,i) \
14743: { (b)[(i) ] = (uint8) ( (n) ); \
14744: (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
14745: (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
14746: (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
14747: /* --- P,S,F macros defined as functions --- */
14748: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
14749: { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
14750: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
14751: return; }
14752: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
14753: { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
14754: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
14755: return; }
14756: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
14757: { *a += (uint32)(b ^ c ^ d) + X[k] + t;
14758: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
14759: return; }
14760: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
14761: { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
14762: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
14763: return; }
14764:
14765: /* --- entry point (this one little stub written by me)--- */
14766: char *md5str( char *instr )
14767: { static char outstr[64];
14768: unsigned char md5sum[16];
14769: md5_context ctx;
14770: int j;
14771: md5_starts( &ctx );
14772: md5_update( &ctx, (uint8 *)instr, strlen(instr) );
14773: md5_finish( &ctx, md5sum );
14774: for( j=0; j<16; j++ )
14775: sprintf( outstr + j*2, "%02x", md5sum[j] );
14776: outstr[32] = '\000';
14777: return ( outstr ); }
14778:
14779: /* --- entry point (all md5 functions below by Christophe Devine) --- */
14780: void md5_starts( md5_context *ctx )
14781: { ctx->total[0] = 0;
14782: ctx->total[1] = 0;
14783: ctx->state[0] = 0x67452301;
14784: ctx->state[1] = 0xEFCDAB89;
14785: ctx->state[2] = 0x98BADCFE;
14786: ctx->state[3] = 0x10325476; }
14787:
14788: void md5_process( md5_context *ctx, uint8 data[64] )
14789: { uint32 X[16], A, B, C, D;
14790: GET_UINT32( X[0], data, 0 );
14791: GET_UINT32( X[1], data, 4 );
14792: GET_UINT32( X[2], data, 8 );
14793: GET_UINT32( X[3], data, 12 );
14794: GET_UINT32( X[4], data, 16 );
14795: GET_UINT32( X[5], data, 20 );
14796: GET_UINT32( X[6], data, 24 );
14797: GET_UINT32( X[7], data, 28 );
14798: GET_UINT32( X[8], data, 32 );
14799: GET_UINT32( X[9], data, 36 );
14800: GET_UINT32( X[10], data, 40 );
14801: GET_UINT32( X[11], data, 44 );
14802: GET_UINT32( X[12], data, 48 );
14803: GET_UINT32( X[13], data, 52 );
14804: GET_UINT32( X[14], data, 56 );
14805: GET_UINT32( X[15], data, 60 );
14806: A = ctx->state[0];
14807: B = ctx->state[1];
14808: C = ctx->state[2];
14809: D = ctx->state[3];
14810: P1( X, &A, B, C, D, 0, 7, 0xD76AA478 );
14811: P1( X, &D, A, B, C, 1, 12, 0xE8C7B756 );
14812: P1( X, &C, D, A, B, 2, 17, 0x242070DB );
14813: P1( X, &B, C, D, A, 3, 22, 0xC1BDCEEE );
14814: P1( X, &A, B, C, D, 4, 7, 0xF57C0FAF );
14815: P1( X, &D, A, B, C, 5, 12, 0x4787C62A );
14816: P1( X, &C, D, A, B, 6, 17, 0xA8304613 );
14817: P1( X, &B, C, D, A, 7, 22, 0xFD469501 );
14818: P1( X, &A, B, C, D, 8, 7, 0x698098D8 );
14819: P1( X, &D, A, B, C, 9, 12, 0x8B44F7AF );
14820: P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
14821: P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
14822: P1( X, &A, B, C, D, 12, 7, 0x6B901122 );
14823: P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
14824: P1( X, &C, D, A, B, 14, 17, 0xA679438E );
14825: P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
14826: P2( X, &A, B, C, D, 1, 5, 0xF61E2562 );
14827: P2( X, &D, A, B, C, 6, 9, 0xC040B340 );
14828: P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
14829: P2( X, &B, C, D, A, 0, 20, 0xE9B6C7AA );
14830: P2( X, &A, B, C, D, 5, 5, 0xD62F105D );
14831: P2( X, &D, A, B, C, 10, 9, 0x02441453 );
14832: P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
14833: P2( X, &B, C, D, A, 4, 20, 0xE7D3FBC8 );
14834: P2( X, &A, B, C, D, 9, 5, 0x21E1CDE6 );
14835: P2( X, &D, A, B, C, 14, 9, 0xC33707D6 );
14836: P2( X, &C, D, A, B, 3, 14, 0xF4D50D87 );
14837: P2( X, &B, C, D, A, 8, 20, 0x455A14ED );
14838: P2( X, &A, B, C, D, 13, 5, 0xA9E3E905 );
14839: P2( X, &D, A, B, C, 2, 9, 0xFCEFA3F8 );
14840: P2( X, &C, D, A, B, 7, 14, 0x676F02D9 );
14841: P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
14842: P3( X, &A, B, C, D, 5, 4, 0xFFFA3942 );
14843: P3( X, &D, A, B, C, 8, 11, 0x8771F681 );
14844: P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
14845: P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
14846: P3( X, &A, B, C, D, 1, 4, 0xA4BEEA44 );
14847: P3( X, &D, A, B, C, 4, 11, 0x4BDECFA9 );
14848: P3( X, &C, D, A, B, 7, 16, 0xF6BB4B60 );
14849: P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
14850: P3( X, &A, B, C, D, 13, 4, 0x289B7EC6 );
14851: P3( X, &D, A, B, C, 0, 11, 0xEAA127FA );
14852: P3( X, &C, D, A, B, 3, 16, 0xD4EF3085 );
14853: P3( X, &B, C, D, A, 6, 23, 0x04881D05 );
14854: P3( X, &A, B, C, D, 9, 4, 0xD9D4D039 );
14855: P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
14856: P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
14857: P3( X, &B, C, D, A, 2, 23, 0xC4AC5665 );
14858: P4( X, &A, B, C, D, 0, 6, 0xF4292244 );
14859: P4( X, &D, A, B, C, 7, 10, 0x432AFF97 );
14860: P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
14861: P4( X, &B, C, D, A, 5, 21, 0xFC93A039 );
14862: P4( X, &A, B, C, D, 12, 6, 0x655B59C3 );
14863: P4( X, &D, A, B, C, 3, 10, 0x8F0CCC92 );
14864: P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
14865: P4( X, &B, C, D, A, 1, 21, 0x85845DD1 );
14866: P4( X, &A, B, C, D, 8, 6, 0x6FA87E4F );
14867: P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
14868: P4( X, &C, D, A, B, 6, 15, 0xA3014314 );
14869: P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
14870: P4( X, &A, B, C, D, 4, 6, 0xF7537E82 );
14871: P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
14872: P4( X, &C, D, A, B, 2, 15, 0x2AD7D2BB );
14873: P4( X, &B, C, D, A, 9, 21, 0xEB86D391 );
14874: ctx->state[0] += A;
14875: ctx->state[1] += B;
14876: ctx->state[2] += C;
14877: ctx->state[3] += D; }
14878:
14879: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
14880: { uint32 left, fill;
14881: if( length < 1 ) return;
14882: left = ctx->total[0] & 0x3F;
14883: fill = 64 - left;
14884: ctx->total[0] += length;
14885: ctx->total[0] &= 0xFFFFFFFF;
14886: if( ctx->total[0] < length )
14887: ctx->total[1]++;
14888: if( left && length >= fill )
14889: { memcpy( (void *) (ctx->buffer + left),
14890: (void *) input, fill );
14891: md5_process( ctx, ctx->buffer );
14892: length -= fill;
14893: input += fill;
14894: left = 0; }
14895: while( length >= 64 )
14896: { md5_process( ctx, input );
14897: length -= 64;
14898: input += 64; }
14899: if( length >= 1 )
14900: memcpy( (void *) (ctx->buffer + left),
14901: (void *) input, length ); }
14902:
14903: void md5_finish( md5_context *ctx, uint8 digest[16] )
14904: { static uint8 md5_padding[64] =
14905: { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14906: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14907: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14908: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
14909: uint32 last, padn;
14910: uint32 high, low;
14911: uint8 msglen[8];
14912: high = ( ctx->total[0] >> 29 )
14913: | ( ctx->total[1] << 3 );
14914: low = ( ctx->total[0] << 3 );
14915: PUT_UINT32( low, msglen, 0 );
14916: PUT_UINT32( high, msglen, 4 );
14917: last = ctx->total[0] & 0x3F;
14918: padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
14919: md5_update( ctx, md5_padding, padn );
14920: md5_update( ctx, msglen, 8 );
14921: PUT_UINT32( ctx->state[0], digest, 0 );
14922: PUT_UINT32( ctx->state[1], digest, 4 );
14923: PUT_UINT32( ctx->state[2], digest, 8 );
14924: PUT_UINT32( ctx->state[3], digest, 12 ); }
14925: /* --- end-of-function md5str() and "friends" --- */
14926:
14927: #if defined(GIF)
14928: /* ==========================================================================
14929: * Function: GetPixel ( int x, int y )
14930: * Purpose: callback for GIF_CompressImage() returning the
14931: * pixel at column x, row y
14932: * --------------------------------------------------------------------------
14933: * Arguments: x (I) int containing column=0...width-1
14934: * of desired pixel
14935: * y (I) int containing row=0...height-1
14936: * of desired pixel
14937: * --------------------------------------------------------------------------
14938: * Returns: ( int ) 0 or 1, if pixel at x,y is off or on
14939: * --------------------------------------------------------------------------
14940: * Notes: o
14941: * ======================================================================= */
14942: /* --- entry point --- */
14943: int GetPixel ( int x, int y )
14944: {
14945: int ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/
14946: int pixval =0; /* value of pixel */
14947: if ( !isaa ) /* use bitmap if not anti-aliased */
14948: pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
14949: else /* else use anti-aliased grayscale*/
14950: pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
14951: if ( msgfp!=NULL && msglevel>=9999 ) /* dump pixel */
14952: { fprintf(msgfp,"GetPixel> x=%d, y=%d pixel=%d\n",x,y,pixval);
14953: fflush(msgfp); }
14954: return pixval;
14955: } /* --- end-of-function GetPixel() --- */
14956: #endif /* gif */
14957: #endif /* driver */
14958: #endif /* PART1 */
14959: /* ======================= END-OF-FILE MIMETEX.C ========================= */
14960:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>