Annotation of loncom/cgi/mimeTeX/mimetex.c, revision 1.5
1.1 albertel 1: /****************************************************************************
2: *
1.5 ! raeburn 3: * Copyright(c) 2002-2012, John Forkosh Associates, Inc. All rights reserved.
1.4 riegler 4: * http://www.forkosh.com mailto: john@forkosh.com
1.1 albertel 5: * --------------------------------------------------------------------------
6: * This file is part of mimeTeX, which is free software. You may redistribute
7: * and/or modify it under the terms of the GNU General Public License,
1.4 riegler 8: * version 3 or later, as published by the Free Software Foundation.
1.1 albertel 9: * MimeTeX is distributed in the hope that it will be useful, but
10: * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
11: * See the GNU General Public License for specific details.
12: * By using mimeTeX, you warrant that you have read, understood and
13: * agreed to these terms and conditions, and that you possess the legal
14: * right and ability to enter into this agreement and to use mimeTeX
15: * in accordance with it.
1.4 riegler 16: * Your mimetex.zip distribution file should contain the file COPYING,
17: * an ascii text copy of the GNU General Public License, version 3.
18: * If not, point your browser to http://www.gnu.org/licenses/
19: * or write to the Free Software Foundation, Inc.,
20: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
1.1 albertel 21: * --------------------------------------------------------------------------
22: *
23: * Purpose: o MimeTeX, licensed under the gpl, lets you easily embed
24: * LaTeX math in your html pages. It parses a LaTeX math
25: * expression and immediately emits the corresponding gif
26: * image, rather than the usual TeX dvi. And mimeTeX is an
27: * entirely separate little program that doesn't use TeX or
28: * its fonts in any way. It's just one cgi that you put in
29: * your site's cgi-bin/ directory, with no other dependencies.
1.4 riegler 30: * So mimeTeX is very easy to install. And it's equally
31: * easy to use. Just place an html <img> tag in your document
1.1 albertel 32: * wherever you want to see the corresponding LaTeX expression.
33: * For example,
34: * <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"
35: * alt="" border=0 align=middle>
36: * immediately generates the corresponding gif image on-the-fly,
37: * displaying the rendered expression wherever you put that
1.4 riegler 38: * <img> tag.
39: * MimeTeX doesn't need intermediate dvi-to-gif conversion,
40: * and it doesn't clutter up your filesystem with separate
41: * little gif files for each converted expression.
1.3 albertel 42: * But image caching is available by using mimeTeX's
43: * -DCACHEPATH=\"path/\" compile option (see below).
1.1 albertel 44: * There's also no inherent need to repeatedly write the
45: * cumbersome <img> tag illustrated above. You can write
46: * your own custom tags, or write a wrapper script around
1.3 albertel 47: * mimeTeX to simplify the notation.
1.4 riegler 48: * Further discussion about mimeTeX's features and
49: * usage is available on its homepage,
50: * http://www.forkosh.com/mimetex.html
51: * and similarly in mimetex.html included with your mimetex.zip
1.5 ! raeburn 52: * distribution file. (Note: http://www.forkosh.com/mimetex.html
! 53: * is a "quickstart" version of the the full mimetex.html manual
! 54: * included in your mimetex.zip distribution file.)
1.1 albertel 55: *
1.5 ! raeburn 56: * Functions: The following "table of contents" lists each function
! 57: * comprising mimeTeX in the order it appears in this file.
! 58: * See individual function entry points for specific comments
! 59: * about its purpose, calling sequence, side effects, etc.
! 60: * (All these functions eventually belong in several
! 61: * different modules, possibly along the lines suggested
! 62: * by the divisions below. But until the best decomposition
! 63: * becomes clear, it seems better to keep mimetex.c
! 64: * neatly together, avoiding a bad decomposition that
! 65: * becomes permanent by default.)
! 66: * ===================== Raster Functions ======================
1.1 albertel 67: * PART2 --- raster constructor functions ---
68: * new_raster(width,height,pixsz) allocation (and constructor)
69: * new_subraster(width,height,pixsz)allocation (and constructor)
70: * new_chardef() allocate chardef struct
71: * delete_raster(rp) deallocate raster (rp = raster ptr)
72: * delete_subraster(sp) deallocate subraster (sp=subraster ptr)
73: * delete_chardef(cp) deallocate chardef (cp = chardef ptr)
74: * --- primitive (sub)raster functions ---
75: * rastcpy(rp) allocate new copy of rp
76: * subrastcpy(sp) allocate new copy of sp
77: * rastrot(rp) new raster rotated right 90 degrees to rp
1.5 ! raeburn 78: * rastmag(rp,magstep) new raster magnified by "magstep" to rp
! 79: * bytemapmag(bytemap,width,height,magstep) magnify bytemap
1.3 albertel 80: * rastref(rp,axis) new raster reflected (axis 1=horz,2=vert)
1.1 albertel 81: * rastput(target,source,top,left,isopaque) overlay src on trgt
82: * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
83: * rastcat(sp1,sp2,isfree) concatanate sp1||sp2
84: * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
85: * rastile(tiles,ntiles) create composite raster from tiles
1.2 albertel 86: * rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2
1.3 albertel 87: * rastsmashcheck(term) check if term is "safe" to smash
1.1 albertel 88: * --- raster "drawing" functions ---
1.5 ! raeburn 89: * accent_subraster(accent,width,height,direction,pixsz)\hat\vec
1.1 albertel 90: * arrow_subraster(width,height,drctn,isBig) left/right arrow
91: * uparrow_subraster(width,height,drctn,isBig) up/down arrow
92: * rule_raster(rp,top,left,width,height,type) draw rule in rp
93: * line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
94: * line_recurse(rp,row0,col0,row1,col1,thickness) recurse line
95: * circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
96: * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
97: * bezier_raster(rp,r0,c0,r1,c1,rt,ct) draw bezier recursively
98: * border_raster(rp,ntop,nbot,isline,isfree)put border around rp
1.3 albertel 99: * backspace_raster(rp,nback,pback,minspace,isfree) neg space
1.1 albertel 100: * --- raster (and chardef) output functions ---
101: * type_raster(rp,fp) emit ascii dump of rp on file ptr fp
102: * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
103: * xbitmap_raster(rp,fp) emit mime xbitmap of rp on fp
1.2 albertel 104: * type_pbmpgm(rp,ptype,file) pbm or pgm image of rp to file
1.1 albertel 105: * cstruct_chardef(cp,fp,col1) emit C struct of cp on fp
106: * cstruct_raster(rp,fp,col1) emit C struct of rp on fp
107: * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
108: * --- ancillary output functions ---
109: * emit_string(fp,col1,string,comment) emit string and C comment
1.2 albertel 110: * gftobitmap(rp) convert .gf-like pixmap to bitmap image
1.1 albertel 111: * ====================== Font Functions =======================
112: * --- font lookup functions ---
1.3 albertel 113: * get_symdef(symbol) return mathchardef for symbol
114: * get_ligature(expr,family) return symtable index for ligature
115: * get_chardef(symdef,size) return chardef for symdef,size
1.1 albertel 116: * get_charsubraster(symdef,size) wrap subraster around chardef
1.2 albertel 117: * get_symsubraster(symbol,size) returns subraster for symbol
1.1 albertel 118: * --- ancillary font functions ---
119: * get_baseline(gfdata) determine baseline (in our coords)
120: * get_delim(symbol,height,family) delim just larger than height
121: * make_delim(symbol,height) construct delim exactly height size
122: * ================= Tokenize/Parse Functions ==================
123: * texchar(expression,chartoken) retruns next char or \sequence
124: * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
1.2 albertel 125: * texleft(expr,subexpr,maxsubsz,ldelim,rdelim) \left...\right
1.1 albertel 126: * texscripts(expression,subscript,superscript,which)get scripts
127: * --- ancillary parse functions ---
128: * isbrace(expression,braces,isescape) check for leading brace
129: * preamble(expression,size,subexpr) parse preamble
130: * mimeprep(expression) preprocessor converts \left( to \(, etc.
131: * strchange(nfirst,from,to) change nfirst chars of from to to
132: * strreplace(string,from,to,nreplace) change from to to in str
1.3 albertel 133: * strwstr(string,substr,white,sublen) find substr in string
1.5 ! raeburn 134: * strdetex(s,mode) replace math chars like \^_{} for display
1.1 albertel 135: * strtexchr(string,texchr) find texchr in string
136: * findbraces(expression,command) find opening { or closing }
1.5 ! raeburn 137: * strpspn(s,reject,segment) non-() chars of s not in reject
! 138: * isstrstr(string,snippets,iscase) are any snippets in string?
! 139: * isnumeric(s) determine if s is an integer
! 140: * evalterm(store,term) evaluate numeric value of expression
! 141: * getstore(store,identifier)return value corresponding to ident
! 142: * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
1.1 albertel 143: * PART3 =========== Rasterize an Expression (recursively) ===========
144: * --- here's the primary entry point for all of mimeTeX ---
145: * rasterize(expression,size) parse and rasterize expression
146: * --- explicitly called handlers that rasterize... ---
147: * rastparen(subexpr,size,basesp) parenthesized subexpr
148: * rastlimits(expression,size,basesp) dispatch super/sub call
149: * rastscripts(expression,size,basesp) super/subscripted exprssn
150: * rastdispmath(expression,size,sp) scripts for displaymath
151: * --- table-driven handlers that rasterize... ---
152: * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
1.2 albertel 153: * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
154: * rastmiddle(expression,size,basesp,arg1,arg2,arg3) \middle
1.1 albertel 155: * rastflags(expression,size,basesp,flag,value,arg3) set flag
156: * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
157: * rastnewline(expression,size,basesp,arg1,arg2,arg3) \\
158: * rastarrow(expression,size,basesp,width,height,drctn) \longarr
159: * rastuparrow(expression,size,basesp,width,height,drctn)up/down
160: * rastoverlay(expression,size,basesp,overlay,arg2,arg3) \not
161: * rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
162: * rastackrel(expression,size,basesp,base,arg2,arg3) \stackrel
163: * rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
164: * rastsqrt(expression,size,basesp,arg1,arg2,arg3) \sqrt
165: * rastaccent(expression,size,basesp,accent,isabove,isscript)
166: * rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
167: * rastbegin(expression,size,basesp,arg1,arg2,arg3) \begin{}
168: * rastarray(expression,size,basesp,arg1,arg2,arg3) \array
169: * rastpicture(expression,size,basesp,arg1,arg2,arg3) \picture
170: * rastline(expression,size,basesp,arg1,arg2,arg3) \line
1.3 albertel 171: * rastrule(expression,size,basesp,arg1,arg2,arg3) \rule
1.1 albertel 172: * rastcircle(expression,size,basesp,arg1,arg2,arg3) \circle
173: * rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier
174: * rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox
175: * rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox
1.5 ! raeburn 176: * rastmagnify(expression,size,basesp,arg1,arg2,arg3) \magnify
1.3 albertel 177: * rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
1.1 albertel 178: * rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
179: * rastinput(expression,size,basesp,arg1,arg2,arg3) \input
180: * rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter
1.5 ! raeburn 181: * rasteval(expression,size,basesp,arg1,arg2,arg3) \eval
1.2 albertel 182: * rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
183: * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
1.5 ! raeburn 184: * rastenviron(expression,size,basesp,arg1,arg2,arg3) \environ
! 185: * rastmessage(expression,size,basesp,arg1,arg2,arg3) \message
1.1 albertel 186: * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
187: * --- helper functions for handlers ---
188: * rastopenfile(filename,mode) opens filename[.tex] in mode
1.2 albertel 189: * rasteditfilename(filename) edit filename (for security)
190: * rastreadfile(filename,islock,tag,value) read <tag>...</tag>
1.1 albertel 191: * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
1.2 albertel 192: * calendar(year,month,day) formats one-month calendar string
193: * timestamp(tzdelta,ifmt) formats timestamp string
194: * tzadjust(tzdelta,year,month,day,hour) adjust date/time
195: * daynumber(year,month,day) #days since Monday, Jan 1, 1973
1.5 ! raeburn 196: * strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
! 197: * strnlower(s,n) lowercase the first n chars of string s
! 198: * urlprune(url,n) http://abc.def.ghi.com/etc-->abc.def.ghi.com
! 199: * urlncmp(url1,url2,n) compares topmost n levels of two url's
1.2 albertel 200: * dbltoa(d,npts) double to comma-separated ascii
1.1 albertel 201: * === Anti-alias completed raster (lowpass) or symbols (ss) ===
202: * aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap
203: * aapnm(rp,bytemap,grayscale) lowpass based on pnmalias.c
1.3 albertel 204: * aapnmlookup(rp,bytemap,grayscale) aapnm based on aagridnum()
205: * aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
206: * aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
207: * aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
208: * aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
209: * aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
210: * aafollowline(rp,irow,icol,direction) looks for a "turn"
211: * aagridnum(rp,irow,icol) calculates gridnum, 0-511
212: * aapatternnum(gridnum) looks up pattern#, 1-51, for gridnum
213: * aalookup(gridnum) table lookup for all possible 3x3 grids
214: * aalowpasslookup(rp,bytemap,grayscale) driver for aalookup()
1.1 albertel 215: * aasupsamp(rp,aa,sf,grayscale) or by supersampling
216: * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
217: * aaweights(width,height) builds "canonical" weight matrix
218: * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
1.3 albertel 219: * === miscellaneous ===
220: * mimetexsetmsg(newmsglevel,newmsgfp) set msglevel and msgfp
1.1 albertel 221: * PART1 ========================== Driver ===========================
222: * main(argc,argv) parses math expression and emits mime xbitmap
1.2 albertel 223: * CreateGifFromEq(expression,gifFileName) entry pt for win dll
1.1 albertel 224: * ismonth(month) is month current month ("jan"-"dec")?
225: * logger(fp,msglevel,logvars) logs environment variables
1.4 riegler 226: * emitcache(cachefile,maxage,valign,isbuffer) emit cachefile
1.2 albertel 227: * readcachefile(cachefile,buffer) read cachefile into buffer
1.5 ! raeburn 228: * advertisement(expression,mode) wrap expression in ad message
! 229: * crc16(s) 16-bit crc of string s
1.1 albertel 230: * md5str(instr) md5 hash library functions
231: * GetPixel(x,y) callback function for gifsave library
232: *
233: * Source: mimetex.c (needs mimetex.h and texfonts.h to compile,
1.4 riegler 234: * and also needs gifsave.c when compiled with -DAA or -DGIF)
1.1 albertel 235: *
236: * --------------------------------------------------------------------------
1.5 ! raeburn 237: * Notes o See individual function entry points for specific comments
! 238: * about the purpose, calling sequence, side effects, etc
! 239: * of each mimeTeX function listed above.
! 240: * o See bottom of file for main() driver (and "friends"),
1.1 albertel 241: * and compile as
242: * cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
243: * to produce an executable that emits gif images with
244: * anti-aliasing (see Notes below). You may also compile
245: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
246: * to produce an executable that emits gif images without
247: * anti-aliasing. Alternatively, compile mimeTeX as
248: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
249: * to produce an executable that just emits mime xbitmaps.
250: * In either case you'll need mimetex.h and texfonts.h,
251: * and with -DAA or -DGIF you'll also need gifsave.c
1.5 ! raeburn 252: * o The font information in texfonts.h was produced by multiple
! 253: * runs of gfuntype, one run per struct (i.e., one run per font
! 254: * family at a particular size). Compile gfuntype as
! 255: * cc gfuntype.c mimetex.c -lm -o gfuntype
! 256: * See gfuntype.c, and also mimetex.html#fonts, for details.
1.1 albertel 257: * o For gif images, the gifsave.c library by Sverre H. Huseby
258: * <http://shh.thathost.com> slightly modified by me to allow
1.4 riegler 259: * (a)sending output to stdout or returning it in memory,
260: * and (b)specifying a transparent background color index,
261: * is included with mimeTeX, and it's documented in
1.5 ! raeburn 262: * mimetex.html#gifsave
! 263: * o MimeTeX's principal reusable function is rasterize(),
! 264: * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
! 265: * and returns a (sub)raster representing it as a bit or bytemap.
! 266: * Your application can do anything it likes with this pixel map.
! 267: * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
! 268: * See mimetex.html#makeraster for further discussion
! 269: * and examples.
! 270: * o File mimetex.c also contains library functions implementing
! 271: * a raster datatype, functions to manipulate rasterized .mf
! 272: * fonts (see gfuntype.c which rasterizes .mf fonts), functions
! 273: * to parse LaTeX expressions, etc. As already mentioned,
! 274: * a complete list of mimetex.c functions is above. See their
! 275: * individual entry points below for further comments.
! 276: * As also mentioned, these functions eventually belong in
! 277: * several different modules, possibly along the lines suggested
! 278: * by the divisions above. But until the best decomposition
! 279: * becomes clear, it seems better to keep mimetex.c
! 280: * neatly together, avoiding a bad decomposition that
! 281: * becomes permanent by default.
1.1 albertel 282: * o Optional compile-line -D defined symbols are documented
1.3 albertel 283: * in mimetex.html#options . They include (additional -D
1.5 ! raeburn 284: * switches are discussed at mimetex.html#options)...
1.1 albertel 285: * -DAA
286: * Turns on gif anti-aliasing with default values
287: * (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
288: * for the following anti-aliasing parameters...
289: * -DCENTERWT=n
290: * -DADJACENTWT=j
291: * -DCORNERWT=k
1.5 ! raeburn 292: * *** Note: Ignore these three switches because
! 293: * *** mimeTeX's current anti-aliasing algorithm
! 294: * *** no longer uses them (as of version 1.60).
1.1 albertel 295: * MimeTeX currently provides a lowpass filtering
296: * algorithm for anti-aliasing, which is applied to the
297: * existing set of bitmap fonts. This lowpass filter
298: * applies default weights
1.3 albertel 299: * 1 2 1
300: * 2 8 2
301: * 1 2 1
1.1 albertel 302: * to neighboring pixels. The defaults weights are
1.3 albertel 303: * CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
1.1 albertel 304: * which you can adjust to control anti-aliasing.
305: * Lower CENTERWT values will blur/spread out lines
306: * while higher values will tend to sharpen lines.
307: * Experimentation is recommended to determine
308: * what value works best for you.
309: * -DCACHEPATH=\"path/\"
310: * This option saves each rendered image to a file
311: * in directory path/ which mimeTeX reads rather than
312: * re-rendering the same image every time it's given
313: * the same LaTeX expression. Sometimes mimeTeX disables
314: * caching, e.g., expressions containing \input{ } are
315: * re-rendered since the contents of the inputted file
316: * may have changed. If compiled without -DCACHEPATH
317: * mimeTeX always re-renders expressions. This usually
318: * isn't too cpu intensive, but if you have unusually
319: * high hit rates then image caching may be helpful.
320: * The path/ is relative to mimetex.cgi, and must
321: * be writable by it. Files created under path/ are
322: * named filename.gif, where filename is the 32-character
323: * MD5 hash of the LaTeX expression.
1.5 ! raeburn 324: * -DDEFAULTSIZE=n
! 325: * MimeTeX currently has eight font sizes numbered 0-7,
! 326: * and always starts in DEFAULTSIZE whose default value
! 327: * is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
! 328: * on the compile line if you prefer mimeTeX to start in
! 329: * larger default size 4 (corresponding to \Large), etc.
1.1 albertel 330: * -DDISPLAYSIZE=n
331: * By default, operator limits like \int_a^b are rendered
332: * \textstyle at font sizes \normalsize and smaller,
333: * and rendered \displaystyle at font sizes \large and
334: * larger. This default corresponds to -DDISPLAYSIZE=3,
335: * which you can adjust; e.g., -DDISPLAYSIZE=0 always
336: * defaults to \displaystyle, and 99 (or any large number)
337: * always defaults to \textstyle. Note that explicit
338: * \textstyle, \displaystyle, \limits or \nolimits
339: * directives in an expression always override
340: * the DISPLAYSIZE default.
1.5 ! raeburn 341: * -DERRORSTATUS=n
! 342: * The default, 0, means mimeTeX always exits with status 0,
! 343: * regardless of whether or not it detects error(s) while
! 344: * trying to render your expression. Specify any non-zero
! 345: * value (typically -1) if you write a script/plugin for
! 346: * mimeTeX that traps non-zero exit statuses. MimeTeX then
! 347: * exits with its own non-zero status when it detects an
! 348: * error it can identify, or with your ERRORSTATUS value
! 349: * for errors it can't specifically identify.
1.1 albertel 350: * -DREFERER=\"domain\" -or-
351: * -DREFERER=\"domain1,domain2,etc\"
352: * Blocks mimeTeX requests from unauthorized domains that
353: * may be using your server's mimetex.cgi without permission.
354: * If REFERER is defined, mimeTeX checks for the environment
355: * variable HTTP_REFERER and, if it exists, performs a
356: * case-insensitive test to make sure it contains 'domain'
357: * as a substring. If given several 'domain's (second form)
358: * then HTTP_REFERER must contain either 'domain1' or
359: * 'domain2', etc, as a (case-insensitive) substring.
360: * If HTTP_REFERER fails to contain a substring matching
361: * any of these domain(s), mimeTeX emits an error message
362: * image corresponding to the expression specified by
363: * the invalid_referer_msg string defined in main().
364: * Note: if HTTP_REFERER is not an environment variable,
365: * mimeTeX correctly generates the requested expression
366: * (i.e., no referer error).
367: * -DWARNINGS=n -or-
368: * -DNOWARNINGS
369: * If an expression submitted to mimeTeX contains an
370: * unrecognzied escape sequence, e.g., "y=x+\abc+1", then
371: * mimeTeX generates a gif image containing an embedded
372: * warning in the form "y=x+[\abc?]+1". If you want these
373: * warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
374: * mimeTeX to ignore unrecognized symbols, and the rendered
375: * image is "y=x++1" instead.
376: * -DWHITE
377: * MimeTeX usually renders black symbols on a white
378: * background. This option renders white symbols on
379: * a black background instead.
380: * --------------------------------------------------------------------------
381: * Revision History:
382: * 09/18/02 J.Forkosh Installation.
383: * 12/11/02 J.Forkosh Version 1.00 released.
384: * 07/04/03 J.Forkosh Version 1.01 released.
385: * 10/17/03 J.Forkosh Version 1.20 released.
386: * 12/21/03 J.Forkosh Version 1.30 released.
387: * 02/01/04 J.Forkosh Version 1.40 released.
388: * 10/02/04 J.Forkosh Version 1.50 released.
389: * 11/30/04 J.Forkosh Version 1.60 released.
1.3 albertel 390: * 10/11/05 J.Forkosh Version 1.64 released.
1.4 riegler 391: * 11/30/06 J.Forkosh Version 1.65 released.
392: * 09/06/08 J.Forkosh Version 1.70 released.
1.5 ! raeburn 393: * 03/23/09 J.Forkosh Version 1.71 released.
! 394: * 11/18/09 J.Forkosh Version 1.72 released.
! 395: * 11/15/11 J.Forkosh Version 1.73 released.
! 396: * 02/15/12 J.Forkosh Version 1.74 released.
! 397: * 03/31/12 J.Forkosh Most recent revision (also see REVISIONDATE)
! 398: * See http://www.forkosh.com/mimetexchangelog.html for further details.
1.1 albertel 399: *
400: ****************************************************************************/
401:
402: /* -------------------------------------------------------------------------
1.5 ! raeburn 403: Program id
! 404: -------------------------------------------------------------------------- */
! 405: #define VERSION "1.74" /* mimeTeX version number */
! 406: #define REVISIONDATE "31 March 2012" /* date of most recent revision */
! 407: #define COPYRIGHTTEXT "Copyright(c) 2002-2012, John Forkosh Associates, Inc"
! 408:
! 409: /* -------------------------------------------------------------------------
1.1 albertel 410: header files and macros
411: -------------------------------------------------------------------------- */
412: /* --- standard headers --- */
413: #include <stdio.h>
414: #include <stdlib.h>
415: /*#include <unistd.h>*/
416: #include <string.h>
417: #include <ctype.h>
418: #include <math.h>
419: #include <time.h>
1.5 ! raeburn 420: extern char **environ; /* for \environment directive */
! 421:
! 422: /* -------------------------------------------------------------------------
! 423: messages (used mostly by main() and also by rastmessage())
! 424: -------------------------------------------------------------------------- */
! 425: static char *copyright1 = /* copyright, gnu/gpl notice */
! 426: "+-----------------------------------------------------------------------+\n"
! 427: "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT "|\n"
! 428: "+-----------------------------------------------------------------------+\n"
! 429: "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
! 430: "| and comes with absolutely no warranty whatsoever. |",
! 431: *copyright2 =
! 432: "| See http://www.forkosh.com/mimetex.html for details. |\n"
! 433: "+-----------------------------------------------------------------------+";
! 434: static int maxmsgnum = 3, /* maximum msgtable[] index */
! 435: /* --- keep these message numbers updated if table changes --- */
! 436: invmsgnum = 0, /* general invalid message */
! 437: refmsgnum = 3; /* urlncmp() failed to validate */
! 438: static char *msgtable[] = { /* messages referenced by [index] */
! 439: "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
! 440: "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
! 441: "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
! 442: "\\red\\small\\rm\\fbox{\\array{" /* [1] */
! 443: "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
! 444: "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
! 445: "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
! 446: "\\red\\small\\rm\\fbox{\\array{" /* [2] */
! 447: "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
! 448: "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
! 449: "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
! 450: "\\red\\small\\rm\\fbox{\\array{" /* [3] */
! 451: "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
! 452: "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
! 453: "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
! 454: NULL } ; /* trailer */
1.1 albertel 455:
1.5 ! raeburn 456: /* -------------------------------------------------------------------------
! 457: additional symbols
! 458: -------------------------------------------------------------------------- */
! 459: /* ---
! 460: * windows-specific header info
! 461: * ---------------------------- */
1.1 albertel 462: #ifndef WINDOWS /* -DWINDOWS not supplied by user */
1.2 albertel 463: #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
464: || defined(DJGPP) /* try to recognize windows compilers */ \
465: || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
1.1 albertel 466: #define WINDOWS /* signal windows */
467: #endif
468: #endif
469: #ifdef WINDOWS /* Windows opens stdout in char mode, and */
470: #include <fcntl.h> /* precedes every 0x0A with spurious 0x0D.*/
471: #include <io.h> /* So emitcache() issues a Win _setmode() */
472: /* call to put stdout in binary mode. */
473: #if defined(_O_BINARY) && !defined(O_BINARY) /* only have _O_BINARY */
474: #define O_BINARY _O_BINARY /* make O_BINARY available, etc... */
475: #define setmode _setmode
476: #define fileno _fileno
477: #endif
478: #if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */
479: #define HAVE_SETMODE /* so we'll use setmode() */
480: #endif
1.2 albertel 481: #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
482: /* to show source file and line numbers where memory leaks occur... */
483: #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
484: #include <crtdbg.h> /* and this debug library */
485: #endif
1.1 albertel 486: #define ISWINDOWS 1
487: #else
488: #define ISWINDOWS 0
489: #endif
490:
1.5 ! raeburn 491: /* ---
! 492: * check for supersampling or low-pass anti-aliasing
! 493: * ------------------------------------------------- */
1.1 albertel 494: #ifdef SS
495: #define ISSUPERSAMPLING 1
496: #ifndef AAALGORITHM
497: #define AAALGORITHM 1 /* default supersampling algorithm */
498: #endif
499: #ifndef AA /* anti-aliasing not explicitly set */
500: #define AA /* so define it ourselves */
501: #endif
502: #ifndef SSFONTS /* need supersampling fonts */
503: #define SSFONTS
504: #endif
505: #else
506: #define ISSUPERSAMPLING 0
507: #ifndef AAALGORITHM
1.3 albertel 508: #define AAALGORITHM 3 /*2*/ /* default lowpass algorithm */
1.1 albertel 509: #endif
510: #endif
1.3 albertel 511: #ifndef MAXFOLLOW
512: #define MAXFOLLOW 8 /* aafollowline() maxturn default */
513: #endif
1.1 albertel 514:
1.5 ! raeburn 515: /* ---
! 516: * set aa (and default gif) if any anti-aliasing options specified
! 517: * --------------------------------------------------------------- */
1.1 albertel 518: #if defined(AA) || defined(GIF) || defined(PNG) \
519: || defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
520: || defined(MINADJACENT) || defined(MAXADJACENT)
521: #if !defined(GIF) && !defined(AA) /* aa not explicitly specified */
522: #define AA /* so define it ourselves */
523: #endif
524: #if !defined(GIF) && !defined(PNG) /* neither gif nor png specified */
525: #define GIF /* so default to gif */
526: #endif
527: #endif
528: /* --- resolve output option inconsistencies --- */
529: #if defined(XBITMAP) /* xbitmap supercedes gif and png */
530: #ifdef AA
531: #undef AA
532: #endif
533: #ifdef GIF
534: #undef GIF
535: #endif
536: #ifdef PNG
537: #undef PNG
538: #endif
539: #endif
540:
1.5 ! raeburn 541: /* ---
! 542: * decide whether or not to compile main()
! 543: * --------------------------------------- */
1.1 albertel 544: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
1.5 ! raeburn 545: /* --- yes, compile main() --- */
! 546: #define DRIVER /* main() driver will be compiled */
! 547: #else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */
1.3 albertel 548: #ifndef TEXFONTS
549: #define NOTEXFONTS /* texfonts not required */
550: #endif
1.1 albertel 551: #endif
552:
1.5 ! raeburn 553: /* ---
! 554: * application headers
! 555: * ------------------- */
1.1 albertel 556: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
557: #define TEXFONTS /* to include texfonts.h */
558: #endif
559: #include "mimetex.h"
1.5 ! raeburn 560:
! 561: /* ---
! 562: * info needed when gif image returned in memory buffer
! 563: * ---------------------------------------------------- */
1.2 albertel 564: #ifdef GIF /* compiling along with gifsave.c */
565: extern int gifSize;
566: extern int maxgifSize;
567: #else /* or just set dummy values */
568: static int gifSize=0, maxgifSize=0;
569: #endif
1.3 albertel 570: /* --- gamma correction --- */
571: #ifndef GAMMA
572: #define GAMMA 1.25 /*1.75*/ /*2.2*/
573: #endif
574: #ifndef REVERSEGAMMA
575: #define REVERSEGAMMA 0.5 /* for \reverse white-on-black */
576: #endif
577: /* --- opaque background (default to transparent) --- */
578: #ifndef OPAQUE
579: #define ISTRANSPARENT 1
580: #else
581: #define ISTRANSPARENT 0
582: #endif
1.5 ! raeburn 583:
! 584: /* ---
! 585: * internal buffer sizes
! 586: * --------------------- */
1.3 albertel 587: #if !defined(MAXEXPRSZ)
588: #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/
589: #endif
590: #if !defined(MAXSUBXSZ)
591: #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
592: #endif
593: #if !defined(MAXTOKNSZ)
594: #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
595: #endif
596: #if !defined(MAXFILESZ)
597: #define MAXFILESZ (65536-1) /*max #bytes in input (output) file*/
598: #endif
599: #if !defined(MAXLINESZ)
600: #define MAXLINESZ (4096-1) /* max #chars in line from file */
601: #endif
602: #if !defined(MAXGIFSZ)
603: #define MAXGIFSZ 131072 /* max #bytes in output GIF image */
604: #endif
1.1 albertel 605:
606: /* -------------------------------------------------------------------------
607: adjustable default values
608: -------------------------------------------------------------------------- */
1.5 ! raeburn 609: /* ---
! 610: * anti-aliasing parameters
! 611: * ------------------------ */
1.1 albertel 612: #ifndef CENTERWT
1.2 albertel 613: /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
1.1 albertel 614: /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
1.2 albertel 615: #define CENTERWT 8 /* anti-aliasing centerwt default */
1.1 albertel 616: #endif
617: #ifndef ADJACENTWT
618: /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
1.2 albertel 619: #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
1.1 albertel 620: #endif
621: #ifndef CORNERWT
622: #define CORNERWT 1 /* anti-aliasing cornerwt default*/
623: #endif
624: #ifndef MINADJACENT
625: #define MINADJACENT 6 /*anti-aliasing minadjacent default*/
626: #endif
627: #ifndef MAXADJACENT
628: #define MAXADJACENT 8 /*anti-aliasing maxadjacent default*/
629: #endif
630: /* --- variables for anti-aliasing parameters --- */
631: GLOBAL(int,centerwt,CENTERWT); /*lowpass matrix center pixel wt */
632: GLOBAL(int,adjacentwt,ADJACENTWT); /*lowpass matrix adjacent pixel wt*/
633: GLOBAL(int,cornerwt,CORNERWT); /*lowpass matrix corner pixel wt */
634: GLOBAL(int,minadjacent,MINADJACENT); /* darken if>=adjacent pts black*/
635: GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */
636: GLOBAL(int,weightnum,1); /* font wt, */
637: GLOBAL(int,maxaaparams,4); /* #entries in table */
1.3 albertel 638: /* --- anti-aliasing parameter values by font weight --- */
1.1 albertel 639: #define aaparameters struct aaparameters_struct /* typedef */
640: aaparameters
641: { int centerwt; /* lowpass matrix center pixel wt*/
642: int adjacentwt; /* lowpass matrix adjacent pixel wt*/
643: int cornerwt; /* lowpass matrix corner pixel wt*/
644: int minadjacent; /* darken if >= adjacent pts black */
645: int maxadjacent; /* darken if <= adjacent pts black */
646: int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
647: STATIC aaparameters aaparams[] /* set params by weight */
648: #ifdef INITVALS
649: =
650: { /* ----------------------------------------------------
651: centerwt adj corner minadj max fgalias,only,bgalias,only
652: ------------------------------------------------------- */
653: { 64, 1, 1, 6, 8, 1,0,0,0 }, /* 0 = light */
654: { CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
655: { 8, 1, 1, 5, 8, 1,0,0,0 }, /* 2 = semibold */
656: { 8, 2, 1, 4, 9, 1,0,0,0 } /* 3 = bold */
657: } /* --- end-of-aaparams[] --- */
658: #endif
659: ;
1.3 albertel 660: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
661: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
662: ispatternnumcount = 1; /* true to accumulate counts */
1.1 albertel 663:
664: /* -------------------------------------------------------------------------
665: other variables
666: -------------------------------------------------------------------------- */
667: /* --- black on white background (default), or white on black --- */
668: #ifdef WHITE
669: #define ISBLACKONWHITE 0 /* white on black background */
670: #else
671: #define ISBLACKONWHITE 1 /* black on white background */
672: #endif
673: /* --- colors --- */
674: #define BGRED (ISBLACKONWHITE?255:0)
675: #define BGGREEN (ISBLACKONWHITE?255:0)
676: #define BGBLUE (ISBLACKONWHITE?255:0)
677: #ifndef FGRED
678: #define FGRED (ISBLACKONWHITE?0:255)
679: #endif
680: #ifndef FGGREEN
681: #define FGGREEN (ISBLACKONWHITE?0:255)
682: #endif
683: #ifndef FGBLUE
684: #define FGBLUE (ISBLACKONWHITE?0:255)
685: #endif
1.5 ! raeburn 686: /* --- advertisement
! 687: one image in every ADFREQUENCY is wrapped in "advertisement" --- */
! 688: #if !defined(ADFREQUENCY)
! 689: #define ADFREQUENCY 0 /* never show advertisement if 0 */
! 690: #endif
! 691: #ifndef HOST_SHOWAD
! 692: #define HOST_SHOWAD "\000" /* show ads on all hosts */
! 693: #endif
1.2 albertel 694: /* --- "smash" margin (0 means no smashing) --- */
695: #ifndef SMASHMARGIN
696: #ifdef NOSMASH
697: #define SMASHMARGIN 0
1.1 albertel 698: #else
1.2 albertel 699: #define SMASHMARGIN 3
1.1 albertel 700: #endif
701: #endif
1.3 albertel 702: #ifndef SMASHCHECK
703: #define SMASHCHECK 0
704: #endif
1.1 albertel 705: /* --- textwidth --- */
706: #ifndef TEXTWIDTH
707: #define TEXTWIDTH (400)
708: #endif
709: /* --- font "combinations" --- */
1.2 albertel 710: #define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
1.1 albertel 711: /* --- prefix prepended to all expressions --- */
712: #ifndef PREFIX
713: #define PREFIX "\000" /* default no prepended prefix */
714: #endif
715: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
716: #ifdef NOARGSIGNAL
717: #define ARGSIGNAL NULL
718: #endif
719: #ifndef ARGSIGNAL
720: #define ARGSIGNAL "++"
721: #endif
722: /* --- security and logging (inhibit message logging, etc) --- */
723: #ifndef SECURITY
724: #define SECURITY 999 /* default highest security level */
725: #endif
726: #ifndef LOGFILE
727: #define LOGFILE "mimetex.log" /* default log file */
728: #endif
729: #ifndef CACHELOG
730: #define CACHELOG "mimetex.log" /* default caching log file */
731: #endif
732: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
733: #define DUMPENVP /* assume char *envp[] available */
734: #endif
1.5 ! raeburn 735: /* --- max query_string length if no http_referer supplied --- */
! 736: #ifndef NOREFMAXLEN
! 737: #define NOREFMAXLEN 9999 /* default to any length query */
! 738: #endif
! 739: #ifndef NOREFSAFELEN
! 740: #define NOREFSAFELEN 24 /* too small for hack exploit */
! 741: #endif
! 742: /* --- check whether or not to perform http_referer check --- */
! 743: #ifdef REFERER /* only specified referers allowed */
! 744: #undef NOREFMAXLEN
! 745: #define NOREFMAXLEN NOREFSAFELEN
! 746: #else /* all http_referer's allowed */
! 747: #define REFERER NULL
! 748: #endif
! 749: /* --- check top levels of http_referer against server_name --- */
! 750: #ifdef REFLEVELS /* #topmost levels to check */
! 751: #undef NOREFMAXLEN
! 752: #define NOREFMAXLEN NOREFSAFELEN
! 753: #else
! 754: #ifdef NOREFCHECK
! 755: #define REFLEVELS 0 /* don't match host and referer */
! 756: #else
! 757: #define REFLEVELS 3 /* default matches abc.def.com */
! 758: #endif
! 759: #endif
! 760: /* --- check whether or not \input, \counter, \environment permitted --- */
! 761: #ifdef DEFAULTSECURITY /* default security specified */
! 762: #define EXPLICITDEFSECURITY /* don't override explicit default */
! 763: #else /* defualt security not specified */
! 764: #define DEFAULTSECURITY (8) /* so set default security level */
! 765: #endif
! 766: #ifdef INPUTREFERER /*http_referer's permitted to \input*/
! 767: #ifndef INPUTSECURITY /* so we need to permit \input{} */
! 768: #define INPUTSECURITY (99999) /* make sure SECURITY<INPUTSECURITY */
! 769: #endif
! 770: #else /* no INPUTREFERER list supplied */
! 771: #define INPUTREFERER NULL /* so init it as NULL pointer */
! 772: #endif
! 773: #ifndef INPUTPATH /* \input{} paths permitted for... */
! 774: #define INPUTPATH NULL /* ...any referer */
! 775: #endif
! 776: #ifndef INPUTSECURITY /* \input{} security not specified */
! 777: #ifdef INPUTOK /* but INPUTOK flag specified */
! 778: #define INPUTSECURITY (99999) /* so enable \input{} */
! 779: #ifndef EXPLICITDEFSECURITY /* don't override explicit default */
! 780: #undef DEFAULTSECURITY /* but we'll override our default */
! 781: #define DEFAULTSECURITY (99999) /*let -DINPUTOK enable \counter,etc*/
! 782: #endif
! 783: #else /* else no \input{} specified */
! 784: #define INPUTSECURITY DEFAULTSECURITY /* set default \input security */
! 785: #endif
! 786: #endif
! 787: #ifndef COUNTERSECURITY /*\counter{} security not specified*/
! 788: #ifdef COUNTEROK /* but COUNTEROK flag specified */
! 789: #define COUNTERSECURITY (99999) /* so enable \counter{} */
! 790: #else /* else no \counter{} specified */
! 791: #define COUNTERSECURITY DEFAULTSECURITY /*set default \counter security*/
! 792: #endif
! 793: #endif
! 794: #ifndef ENVIRONSECURITY /* \environ security not specified */
! 795: #ifdef ENVIRONOK /* but ENVIRONOK flag specified */
! 796: #define ENVIRONSECURITY (99999) /* so enable \environ */
! 797: #else /* else no \environ specified */
! 798: #define ENVIRONSECURITY DEFAULTSECURITY /*set default \environ security*/
! 799: #endif
! 800: #endif
1.1 albertel 801: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
802: #ifndef CACHEPATH
803: #define ISCACHING 0 /* no caching */
804: #define CACHEPATH "\000" /* same directory as mimetex.cgi */
805: #else
806: #define ISCACHING 1 /* caching if -DCACHEPATH="path" */
807: #endif
808: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
809: #ifndef PATHPREFIX
810: #define PATHPREFIX "\000" /* paths relative mimetex.cgi */
811: #endif
1.2 albertel 812: /* --- time zone delta t (in hours) --- */
813: #ifndef TZDELTA
814: #define TZDELTA 0
815: #endif
1.3 albertel 816: /* --- treat +'s in query string as blanks? --- */
817: #ifdef PLUSBLANK /* + always interpreted as blank */
818: #define ISPLUSBLANK 1
819: #else
820: #ifdef PLUSNOTBLANK /* + never interpreted as blank */
821: #define ISPLUSBLANK 0
822: #else /* program tries to determine */
823: #define ISPLUSBLANK (-1)
824: #endif
825: #endif
1.1 albertel 826:
827: /* -------------------------------------------------------------------------
828: debugging and logging / error reporting
829: -------------------------------------------------------------------------- */
830: /* --- debugging and error reporting --- */
831: #ifndef MSGLEVEL
832: #define MSGLEVEL 1
833: #endif
834: #define DBGLEVEL 9 /* debugging if msglevel>=DBGLEVEL */
835: #define LOGLEVEL 3 /* logging if msglevel>=LOGLEVEL */
836: #ifndef FORMLEVEL
837: #define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/
838: #endif
1.5 ! raeburn 839: #ifndef ERRORSTATUS /* exit(ERRORSTATUS) for any error */
! 840: #define ERRORSTATUS 0 /* default doesn't signal errors */
! 841: #endif
1.1 albertel 842: GLOBAL(int,seclevel,SECURITY); /* security level */
1.5 ! raeburn 843: GLOBAL(int,inputseclevel,INPUTSECURITY); /* \input{} security level */
! 844: GLOBAL(int,counterseclevel,COUNTERSECURITY); /* \counter{} security level */
! 845: GLOBAL(int,environseclevel,ENVIRONSECURITY); /* \environ{} security level */
1.1 albertel 846: GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */
1.5 ! raeburn 847: GLOBAL(int,errorstatus,ERRORSTATUS); /* exit status if error encountered*/
! 848: GLOBAL(int,exitstatus,0); /* exit status (0=success) */
1.1 albertel 849: STATIC FILE *msgfp; /* output in command-line mode */
850: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
851: #ifdef WARNINGS
852: #define WARNINGLEVEL WARNINGS
853: #else
854: #ifdef NOWARNINGS
855: #define WARNINGLEVEL 0
856: #else
857: #define WARNINGLEVEL 1
858: #endif
859: #endif
860: GLOBAL(int,warninglevel,WARNINGLEVEL); /* warning level */
861:
862: /* -------------------------------------------------------------------------
863: control flags and values
864: -------------------------------------------------------------------------- */
1.5 ! raeburn 865: GLOBAL(int,daemonlevel,0); /* incremented in main() */
1.1 albertel 866: GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
867: GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
1.2 albertel 868: GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
1.3 albertel 869: GLOBAL(int,isligature,0); /* true if ligature found */
870: GLOBAL(char,*subexprptr,(char *)NULL); /* ptr within expression to subexpr*/
1.2 albertel 871: /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
1.1 albertel 872: GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
873: GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
1.5 ! raeburn 874: GLOBAL(int,ninputcmds,0); /* # of \input commands processed */
1.2 albertel 875: GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
1.1 albertel 876: GLOBAL(int,fontsize,NORMALSIZE); /* current size */
1.5 ! raeburn 877: GLOBAL(int,magstep,1); /* magstep (1=no change) */
1.1 albertel 878: GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
879: GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
1.5 ! raeburn 880: GLOBAL(int,rastlift,0); /* rastraise() lift parameter */
! 881: GLOBAL(int,rastlift1,0); /* rastraise() lift for base exprssn*/
1.1 albertel 882: GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
1.5 ! raeburn 883: GLOBAL(int,iunitlength,1); /* #pixels per unit as int for store*/
1.1 albertel 884: /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
1.5 ! raeburn 885: GLOBAL(int,adfrequency,ADFREQUENCY); /* advertisement frequency */
1.3 albertel 886: GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/
1.2 albertel 887: GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
1.3 albertel 888: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
1.2 albertel 889: GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
1.3 albertel 890: GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */
891: GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */
1.5 ! raeburn 892: GLOBAL(int,isnomath,0); /* true to inhibit math mode */
1.3 albertel 893: GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/
894: GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */
895: GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/
896: #define BLANKSIGNAL (-991234) /*rastsmash signal right-hand blank*/
897: GLOBAL(int,blanksignal,BLANKSIGNAL); /*rastsmash signal right-hand blank*/
898: GLOBAL(int,blanksymspace,0); /* extra (or too much) space wanted*/
899: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
1.1 albertel 900: GLOBAL(int,fgred,FGRED);
901: GLOBAL(int,fggreen,FGGREEN);
902: GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */
903: GLOBAL(int,bgred,BGRED);
904: GLOBAL(int,bggreen,BGGREEN);
905: GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */
1.3 albertel 906: GLOBAL(double,gammacorrection,GAMMA); /* gamma correction */
907: GLOBAL(int,isplusblank,ISPLUSBLANK); /*interpret +'s in query as blanks?*/
1.1 albertel 908: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
909: GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */
910: GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */
1.3 albertel 911: GLOBAL(int,maxfollow,MAXFOLLOW); /* aafollowline() maxturn parameter*/
1.1 albertel 912: GLOBAL(int,fgalias,1);
913: GLOBAL(int,fgonly,0);
914: GLOBAL(int,bgalias,0);
915: GLOBAL(int,bgonly,0); /* aapnm() params */
916: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
917: GLOBAL(int,isss,ISSUPERSAMPLING); /* supersampling flag for main() */
918: GLOBAL(int,*workingparam,(int *)NULL); /* working parameter */
919: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
920: GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */
921: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
922: GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/
1.3 albertel 923: GLOBAL(int,fraccenterline,NOVALUE); /* baseline for punct. after \frac */
924: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
1.1 albertel 925: GLOBAL(int,iscaching,ISCACHING); /* true if caching images */
926: GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */
1.4 riegler 927: GLOBAL(int,isemitcontenttype,1); /* true to emit mime content-type */
928: int iscachecontenttype = 0; /* true to cache mime content-type */
929: char contenttype[2048] = "\000"; /* content-type:, etc buffer */
1.1 albertel 930: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
931: /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */
932:
933: /* -------------------------------------------------------------------------
1.5 ! raeburn 934: store for evalterm() [n.b., these are stripped-down funcs from nutshell]
! 935: -------------------------------------------------------------------------- */
! 936: #define STORE struct store_struct /* "typedef" for store struct */
! 937: #define MAXSTORE 100 /* max 100 identifiers */
! 938: STORE {
! 939: char *identifier; /* identifier */
! 940: int *value; /* address of corresponding value */
! 941: } ; /* --- end-of-store_struct --- */
! 942: static STORE mimestore[MAXSTORE] = {
! 943: { "fontsize", &fontsize }, { "fs", &fontsize }, /* font size */
! 944: { "fontnum", &fontnum }, { "fn", &fontnum }, /* font number */
! 945: { "unitlength", &iunitlength }, /* unitlength */
! 946: /*{ "mytestvar", &mytestvar },*/
! 947: { NULL, NULL } /* end-of-store */
! 948: } ; /* --- end-of-mimestore[] --- */
! 949:
! 950: /* -------------------------------------------------------------------------
1.1 albertel 951: miscellaneous macros
952: -------------------------------------------------------------------------- */
1.5 ! raeburn 953: #if 0 /* --- these are now #define'd in mimetex.h --- */
1.1 albertel 954: #define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */
955: #define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
956: #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
957: #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
958: #define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
959: #define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
960: #define dmod(x,y) ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
1.5 ! raeburn 961: #endif
1.1 albertel 962: #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \
1.5 ! raeburn 963: { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
1.1 albertel 964: #define slower(s) if ((s)!=NULL) /* lowercase all chars in s */ \
965: { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
1.3 albertel 966: /*subraster *subrastcpy();*/ /* need global module declaration */
1.5 ! raeburn 967: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
! 968: /* sp->type=blanksignal */
! 969: /* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
! 970: #define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
! 971: /* --- check if a string is empty --- */
! 972: #define isempty(s) ((s)==NULL?1:(*(s)=='\000'?1:0))
! 973: /* --- last char of a string --- */
! 974: #define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
! 975: /* --- lowercase a string --- */
! 976: #define strlower(s) strnlower((s),0) /* lowercase an entire string */
! 977: /* --- strip leading and trailing whitespace (including ~) --- */
! 978: #define trimwhite(thisstr) if ( (thisstr) != NULL ) { \
! 979: int thislen = strlen(thisstr); \
! 980: while ( --thislen >= 0 ) \
! 981: if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
! 982: (thisstr)[thislen] = '\000'; \
! 983: else break; \
! 984: if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
! 985: {strsqueeze((thisstr),thislen);} } else
! 986: /* --- strncpy() n bytes and make sure it's null-terminated --- */
! 987: #define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
! 988: char *thissource = (source); \
! 989: (target)[0] = '\000'; \
! 990: if ( (n)>0 && thissource!=NULL ) { \
! 991: strncpy((target),thissource,(n)); \
! 992: (target)[(n)] = '\000'; } }
! 993: /* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
! 994: #define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
! 995: int thislen3=strlen(s); \
! 996: if ((n) >= thislen3) *(s) = '\000'; \
! 997: else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
! 998: /* --- strsqueeze(s,t) with two pointers --- */
! 999: #define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
! 1000: int sqlen=strlen((s))-strlen((t)); \
! 1001: if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
1.1 albertel 1002:
1003: /* ---
1004: * PART2
1005: * ------ */
1006: #if !defined(PARTS) || defined(PART2)
1007: /* ==========================================================================
1008: * Function: new_raster ( width, height, pixsz )
1009: * Purpose: Allocation and constructor for raster.
1010: * mallocs and initializes memory for width*height pixels,
1011: * and returns raster struct ptr to caller.
1012: * --------------------------------------------------------------------------
1013: * Arguments: width (I) int containing width, in bits,
1014: * of raster pixmap to be allocated
1015: * height (I) int containing height, in bits/scans,
1016: * of raster pixmap to be allocated
1017: * pixsz (I) int containing #bits per pixel, 1 or 8
1018: * --------------------------------------------------------------------------
1019: * Returns: ( raster * ) ptr to allocated and initialized
1020: * raster struct, or NULL for any error.
1021: * --------------------------------------------------------------------------
1022: * Notes:
1023: * ======================================================================= */
1024: /* --- entry point --- */
1025: raster *new_raster ( int width, int height, int pixsz )
1026: {
1027: /* -------------------------------------------------------------------------
1028: Allocations and Declarations
1029: -------------------------------------------------------------------------- */
1030: raster *rp = (raster *)NULL; /* raster ptr returned to caller */
1031: pixbyte *pixmap = NULL; /* raster pixel map to be malloced */
1032: int nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
1033: int filler = (isstring?' ':0); /* pixmap filler */
1034: int delete_raster(); /* in case pixmap malloc() fails */
1035: int npadding = (0&&issupersampling?8+256:0); /* padding bytes */
1036: /* -------------------------------------------------------------------------
1037: allocate and initialize raster struct and embedded bitmap
1038: -------------------------------------------------------------------------- */
1039: if ( msgfp!=NULL && msglevel>=9999 )
1040: { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
1041: width,height,pixsz); fflush(msgfp); }
1042: /* --- allocate and initialize raster struct --- */
1043: rp = (raster *)malloc(sizeof(raster)); /* malloc raster struct */
1044: if ( msgfp!=NULL && msglevel>=9999 )
1045: { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
1046: sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1047: if ( rp == (raster *)NULL ) /* malloc failed */
1048: goto end_of_job; /* return error to caller */
1049: rp->width = width; /* store width in raster struct */
1050: rp->height = height; /* and store height */
1.2 albertel 1051: rp->format = 1; /* initialize as bitmap format */
1.1 albertel 1052: rp->pixsz = pixsz; /* store #bits per pixel */
1053: rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
1054: /* --- allocate and initialize bitmap array --- */
1055: if ( msgfp!=NULL && msglevel>=9999 )
1056: { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
1057: nbytes); fflush(msgfp); }
1058: if ( nbytes>0 && nbytes<=pixsz*maxraster ) /* fail if width*height too big*/
1059: pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
1060: if ( msgfp!=NULL && msglevel>=9999 )
1061: { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
1062: nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
1063: if ( pixmap == (pixbyte *)NULL ) /* malloc failed */
1064: { delete_raster(rp); /* so free everything */
1065: rp = (raster *)NULL; /* reset pointer */
1066: goto end_of_job; } /* and return error to caller */
1067: memset((void *)pixmap,filler,nbytes); /* init bytes to binary 0's or ' 's*/
1068: *pixmap = (pixbyte)0; /* and first byte alwasy 0 */
1069: rp->pixmap = pixmap; /* store ptr to malloced memory */
1070: /* -------------------------------------------------------------------------
1071: Back to caller with address of raster struct, or NULL ptr for any error.
1072: -------------------------------------------------------------------------- */
1073: end_of_job:
1074: if ( msgfp!=NULL && msglevel>=9999 )
1075: { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
1076: width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1077: return ( rp ); /* back to caller with raster */
1078: } /* --- end-of-function new_raster() --- */
1079:
1080:
1081: /* ==========================================================================
1082: * Function: new_subraster ( width, height, pixsz )
1083: * Purpose: Allocate a new subraster along with
1084: * an embedded raster of width x height.
1085: * --------------------------------------------------------------------------
1086: * Arguments: width (I) int containing width of embedded raster
1087: * height (I) int containing height of embedded raster
1088: * pixsz (I) int containing #bits per pixel, 1 or 8
1089: * --------------------------------------------------------------------------
1090: * Returns: ( subraster * ) ptr to newly-allocated subraster,
1091: * or NULL for any error.
1092: * --------------------------------------------------------------------------
1093: * Notes: o if width or height <=0, embedded raster not allocated
1094: * ======================================================================= */
1095: /* --- entry point --- */
1096: subraster *new_subraster ( int width, int height, int pixsz )
1097: {
1098: /* -------------------------------------------------------------------------
1099: Allocations and Declarations
1100: -------------------------------------------------------------------------- */
1101: subraster *sp=NULL; /* subraster returned to caller */
1102: raster *new_raster(), *rp=NULL; /* image raster embedded in sp */
1103: int delete_subraster(); /* in case new_raster() fails */
1104: int size = NORMALSIZE, /* default size */
1105: baseline = height-1; /* and baseline */
1106: /* -------------------------------------------------------------------------
1107: allocate and initialize subraster struct
1108: -------------------------------------------------------------------------- */
1109: if ( msgfp!=NULL && msglevel>=9999 )
1110: { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
1111: width,height,pixsz); fflush(msgfp); }
1112: /* --- allocate subraster struct --- */
1113: sp = (subraster *)malloc(sizeof(subraster)); /* malloc subraster struct */
1114: if ( sp == (subraster *)NULL ) /* malloc failed */
1115: goto end_of_job; /* return error to caller */
1116: /* --- initialize subraster struct --- */
1117: sp->type = NOVALUE; /* character or image raster */
1118: sp->symdef = (mathchardef *)NULL; /* mathchardef identifying image */
1119: sp->baseline = baseline; /*0 if image is entirely descending*/
1120: sp->size = size; /* font size 0-4 */
1121: sp->toprow = sp->leftcol = (-1); /* upper-left corner of subraster */
1122: sp->image = (raster *)NULL; /*ptr to bitmap image of subraster*/
1123: /* -------------------------------------------------------------------------
1124: allocate raster and embed it in subraster, and return to caller
1125: -------------------------------------------------------------------------- */
1126: /* --- allocate raster struct if desired --- */
1127: if ( width>0 && height>0 && pixsz>0 ) /* caller wants raster */
1128: { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
1129: != NULL ) /* if allocate succeeded */
1130: sp->image = rp; /* embed raster in subraster */
1131: else /* or if allocate failed */
1132: { delete_subraster(sp); /* free non-unneeded subraster */
1133: sp = NULL; } } /* signal error */
1134: /* --- back to caller with new subraster or NULL --- */
1135: end_of_job:
1136: if ( msgfp!=NULL && msglevel>=9999 )
1137: { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
1138: width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
1139: return ( sp );
1140: } /* --- end-of-function new_subraster() --- */
1141:
1142:
1143: /* ==========================================================================
1144: * Function: new_chardef ( )
1145: * Purpose: Allocates and initializes a chardef struct,
1146: * but _not_ the embedded raster struct.
1147: * --------------------------------------------------------------------------
1148: * Arguments: none
1149: * --------------------------------------------------------------------------
1150: * Returns: ( chardef * ) ptr to allocated and initialized
1151: * chardef struct, or NULL for any error.
1152: * --------------------------------------------------------------------------
1153: * Notes:
1154: * ======================================================================= */
1155: /* --- entry point --- */
1156: chardef *new_chardef ( )
1157: {
1158: /* -------------------------------------------------------------------------
1159: Allocations and Declarations
1160: -------------------------------------------------------------------------- */
1161: chardef *cp = (chardef *)NULL; /* chardef ptr returned to caller */
1162: /* -------------------------------------------------------------------------
1163: allocate and initialize chardef struct
1164: -------------------------------------------------------------------------- */
1165: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
1166: if ( cp == (chardef *)NULL ) /* malloc failed */
1167: goto end_of_job; /* return error to caller */
1168: cp->charnum = cp->location = 0; /* init character description */
1169: cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
1170: cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
1171: cp->image.width = cp->image.height = 0; /* init raster dimensions */
1.2 albertel 1172: cp->image.format = 0; /* init raster format */
1.1 albertel 1173: cp->image.pixsz = 0; /* and #bits per pixel */
1174: cp->image.pixmap = NULL; /* init raster pixmap as null */
1175: /* -------------------------------------------------------------------------
1176: Back to caller with address of chardef struct, or NULL ptr for any error.
1177: -------------------------------------------------------------------------- */
1178: end_of_job:
1179: return ( cp );
1180: } /* --- end-of-function new_chardef() --- */
1181:
1182:
1183: /* ==========================================================================
1184: * Function: delete_raster ( rp )
1185: * Purpose: Destructor for raster.
1186: * Frees memory for raster bitmap and struct.
1187: * --------------------------------------------------------------------------
1188: * Arguments: rp (I) ptr to raster struct to be deleted.
1189: * --------------------------------------------------------------------------
1190: * Returns: ( int ) 1 if completed successfully,
1191: * or 0 otherwise (for any error).
1192: * --------------------------------------------------------------------------
1193: * Notes:
1194: * ======================================================================= */
1195: /* --- entry point --- */
1196: int delete_raster ( raster *rp )
1197: {
1198: /* -------------------------------------------------------------------------
1199: free raster bitmap and struct
1200: -------------------------------------------------------------------------- */
1201: if ( rp != (raster *)NULL ) /* can't free null ptr */
1202: {
1203: if ( rp->pixmap != (pixbyte *)NULL ) /* can't free null ptr */
1204: free((void *)rp->pixmap); /* free pixmap within raster */
1205: free((void *)rp); /* lastly, free raster struct */
1206: } /* --- end-of-if(rp!=NULL) --- */
1207: return ( 1 ); /* back to caller, 1=okay 0=failed */
1208: } /* --- end-of-function delete_raster() --- */
1209:
1210:
1211: /* ==========================================================================
1212: * Function: delete_subraster ( sp )
1213: * Purpose: Deallocates a subraster (and embedded raster)
1214: * --------------------------------------------------------------------------
1215: * Arguments: sp (I) ptr to subraster struct to be deleted.
1216: * --------------------------------------------------------------------------
1217: * Returns: ( int ) 1 if completed successfully,
1218: * or 0 otherwise (for any error).
1219: * --------------------------------------------------------------------------
1220: * Notes:
1221: * ======================================================================= */
1222: /* --- entry point --- */
1223: int delete_subraster ( subraster *sp )
1224: {
1225: /* -------------------------------------------------------------------------
1226: free subraster struct
1227: -------------------------------------------------------------------------- */
1228: int delete_raster(); /* to delete embedded raster */
1229: if ( sp != (subraster *)NULL ) /* can't free null ptr */
1230: {
1231: if ( sp->type != CHARASTER ) /* not static character data */
1232: if ( sp->image != NULL ) /*raster allocated within subraster*/
1233: delete_raster(sp->image); /* so free embedded raster */
1234: free((void *)sp); /* and free subraster struct itself*/
1235: } /* --- end-of-if(sp!=NULL) --- */
1236: return ( 1 ); /* back to caller, 1=okay 0=failed */
1237: } /* --- end-of-function delete_subraster() --- */
1238:
1239:
1240: /* ==========================================================================
1241: * Function: delete_chardef ( cp )
1242: * Purpose: Deallocates a chardef (and bitmap of embedded raster)
1243: * --------------------------------------------------------------------------
1244: * Arguments: cp (I) ptr to chardef struct to be deleted.
1245: * --------------------------------------------------------------------------
1246: * Returns: ( int ) 1 if completed successfully,
1247: * or 0 otherwise (for any error).
1248: * --------------------------------------------------------------------------
1249: * Notes:
1250: * ======================================================================= */
1251: /* --- entry point --- */
1252: int delete_chardef ( chardef *cp )
1253: {
1254: /* -------------------------------------------------------------------------
1255: free chardef struct
1256: -------------------------------------------------------------------------- */
1257: if ( cp != (chardef *)NULL ) /* can't free null ptr */
1258: {
1259: if ( cp->image.pixmap != NULL ) /* pixmap allocated within raster */
1260: free((void *)cp->image.pixmap); /* so free embedded pixmap */
1261: free((void *)cp); /* and free chardef struct itself */
1262: } /* --- end-of-if(cp!=NULL) --- */
1263: /* -------------------------------------------------------------------------
1264: Back to caller with 1=okay, 0=failed.
1265: -------------------------------------------------------------------------- */
1266: return ( 1 );
1267: } /* --- end-of-function delete_chardef() --- */
1268:
1269:
1270: /* ==========================================================================
1271: * Function: rastcpy ( rp )
1272: * Purpose: makes duplicate copy of rp
1273: * --------------------------------------------------------------------------
1274: * Arguments: rp (I) ptr to raster struct to be copied
1275: * --------------------------------------------------------------------------
1276: * Returns: ( raster * ) ptr to new copy rp,
1277: * or NULL for any error.
1278: * --------------------------------------------------------------------------
1279: * Notes: o
1280: * ======================================================================= */
1281: /* --- entry point --- */
1282: raster *rastcpy ( raster *rp )
1283: {
1284: /* -------------------------------------------------------------------------
1285: Allocations and Declarations
1286: -------------------------------------------------------------------------- */
1287: raster *new_raster(), *newrp=NULL; /*copied raster returned to caller*/
1288: int height= (rp==NULL?0:rp->height), /* original and copied height */
1289: width = (rp==NULL?0:rp->width), /* original and copied width */
1290: pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
1291: nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
1292: /* -------------------------------------------------------------------------
1293: allocate rotated raster and fill it
1294: -------------------------------------------------------------------------- */
1295: /* --- allocate copied raster with same width,height, and copy bitmap --- */
1296: if ( rp != NULL ) /* nothing to copy if ptr null */
1297: if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
1298: != NULL ) /* check that allocate succeeded */
1299: memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
1300: return ( newrp ); /* return copied raster to caller */
1301: } /* --- end-of-function rastcpy() --- */
1302:
1303:
1304: /* ==========================================================================
1305: * Function: subrastcpy ( sp )
1306: * Purpose: makes duplicate copy of sp
1307: * --------------------------------------------------------------------------
1308: * Arguments: sp (I) ptr to subraster struct to be copied
1309: * --------------------------------------------------------------------------
1310: * Returns: ( subraster * ) ptr to new copy sp,
1311: * or NULL for any error.
1312: * --------------------------------------------------------------------------
1313: * Notes: o
1314: * ======================================================================= */
1315: /* --- entry point --- */
1316: subraster *subrastcpy ( subraster *sp )
1317: {
1318: /* -------------------------------------------------------------------------
1319: Allocations and Declarations
1320: -------------------------------------------------------------------------- */
1321: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
1322: raster *rastcpy(), *newrp=NULL; /* and new raster image within it */
1323: int delete_subraster(); /* dealloc newsp if rastcpy() fails*/
1324: /* -------------------------------------------------------------------------
1325: make copy, and return it to caller
1326: -------------------------------------------------------------------------- */
1327: if ( sp == NULL ) goto end_of_job; /* nothing to copy */
1328: /* --- allocate new subraster "envelope" for copy --- */
1329: if ( (newsp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
1330: == NULL ) goto end_of_job; /* and quit if we fail to allocate */
1331: /* --- transparently copy original envelope to new one --- */
1332: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
1333: /* --- make a copy of the rasterized image itself, if there is one --- */
1334: if ( sp->image != NULL ) /* there's an image embedded in sp */
1335: if ( (newrp = rastcpy(sp->image)) /* so copy rasterized image in sp */
1336: == NULL ) /* failed to copy successfully */
1337: { delete_subraster(newsp); /* won't need newsp any more */
1338: newsp = NULL; /* because we're returning error */
1339: goto end_of_job; } /* back to caller with error signal*/
1340: /* --- set new params in new envelope --- */
1341: newsp->image = newrp; /* new raster image we just copied */
1342: switch ( sp->type ) /* set new raster image type */
1343: { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
1344: case ASCIISTRING: newsp->type = ASCIISTRING; break;
1.3 albertel 1345: case FRACRASTER: newsp->type = FRACRASTER; break;
1346: case BLANKSIGNAL: newsp->type = blanksignal; break;
1.1 albertel 1347: case IMAGERASTER: default: newsp->type = IMAGERASTER; break; }
1348: /* --- return copy of sp to caller --- */
1349: end_of_job:
1350: return ( newsp ); /* copy back to caller */
1351: } /* --- end-of-function subrastcpy() --- */
1352:
1353:
1354: /* ==========================================================================
1355: * Function: rastrot ( rp )
1356: * Purpose: rotates rp image 90 degrees right/clockwise
1357: * --------------------------------------------------------------------------
1358: * Arguments: rp (I) ptr to raster struct to be rotated
1359: * --------------------------------------------------------------------------
1.3 albertel 1360: * Returns: ( raster * ) ptr to new raster rotated relative to rp,
1.1 albertel 1361: * or NULL for any error.
1362: * --------------------------------------------------------------------------
1363: * Notes: o An underbrace is } rotated 90 degrees clockwise,
1364: * a hat is <, etc.
1365: * ======================================================================= */
1366: /* --- entry point --- */
1367: raster *rastrot ( raster *rp )
1368: {
1369: /* -------------------------------------------------------------------------
1370: Allocations and Declarations
1371: -------------------------------------------------------------------------- */
1372: raster *new_raster(), *rotated=NULL; /*rotated raster returned to caller*/
1373: int height = rp->height, irow, /* original height, row index */
1374: width = rp->width, icol, /* original width, column index */
1375: pixsz = rp->pixsz; /* #bits per pixel */
1376: /* -------------------------------------------------------------------------
1377: allocate rotated raster and fill it
1378: -------------------------------------------------------------------------- */
1379: /* --- allocate rotated raster with flipped width<-->height --- */
1380: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
1381: != NULL ) /* check that allocation succeeded */
1382: /* --- fill rotated raster --- */
1383: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
1384: for ( icol=0; icol<width; icol++ ) /* and each column of rp */
1385: { int value = getpixel(rp,irow,icol);
1386: /* setpixel(rotated,icol,irow,value); } */
1387: setpixel(rotated,icol,(height-1-irow),value); }
1388: return ( rotated ); /* return rotated raster to caller */
1389: } /* --- end-of-function rastrot() --- */
1390:
1391:
1392: /* ==========================================================================
1.5 ! raeburn 1393: * Function: rastmag ( rp, magstep )
! 1394: * Purpose: magnifies rp by integer magstep,
! 1395: * e.g., double-height and double-width if magstep=2
! 1396: * --------------------------------------------------------------------------
! 1397: * Arguments: rp (I) ptr to raster struct to be "magnified"
! 1398: * magstep (I) int containing magnification scale,
! 1399: * e.g., 2 to double the width and height of rp
! 1400: * --------------------------------------------------------------------------
! 1401: * Returns: ( raster * ) ptr to new raster magnified relative to rp,
! 1402: * or NULL for any error.
! 1403: * --------------------------------------------------------------------------
! 1404: * Notes: o
! 1405: * ======================================================================= */
! 1406: /* --- entry point --- */
! 1407: raster *rastmag ( raster *rp, int magstep )
! 1408: {
! 1409: /* -------------------------------------------------------------------------
! 1410: Allocations and Declarations
! 1411: -------------------------------------------------------------------------- */
! 1412: raster *new_raster(), *magnified=NULL; /* magnified raster back to caller */
! 1413: int height = rp->height, irow, /* height, row index */
! 1414: width = rp->width, icol, /* width, column index */
! 1415: mrow = 0, mcol = 0, /* dup pixels magstep*magstep times*/
! 1416: pixsz = rp->pixsz; /* #bits per pixel */
! 1417: /* -------------------------------------------------------------------------
! 1418: check args
! 1419: -------------------------------------------------------------------------- */
! 1420: if ( rp == NULL ) goto end_of_job; /* no input raster supplied */
! 1421: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
! 1422: /* -------------------------------------------------------------------------
! 1423: allocate magnified raster and fill it
! 1424: -------------------------------------------------------------------------- */
! 1425: /* --- allocate magnified raster with magstep*width, magstep*height --- */
! 1426: if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
! 1427: != NULL ) /* check that allocation succeeded */
! 1428: /* --- fill reflected raster --- */
! 1429: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
! 1430: for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
! 1431: for ( icol=0; icol<width; icol++ ) /* and for each column of rp */
! 1432: for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
! 1433: int value = getpixel(rp,irow,icol);
! 1434: int row1 = irow*magstep, col1 = icol*magstep;
! 1435: setpixel(magnified,(row1+mrow),(col1+mcol),value); }
! 1436: end_of_job:
! 1437: return ( magnified ); /*return magnified raster to caller*/
! 1438: } /* --- end-of-function rastmag() --- */
! 1439:
! 1440:
! 1441: /* ==========================================================================
! 1442: * Function: bytemapmag ( bytemap, width, height, magstep )
! 1443: * Purpose: magnifies a bytemap by integer magstep,
! 1444: * e.g., double-height and double-width if magstep=2
! 1445: * --------------------------------------------------------------------------
! 1446: * Arguments: bytemap (I) intbyte * ptr to byte map to be "magnified"
! 1447: * width (I) int containing #cols in original bytemap
! 1448: * height (I) int containing #rows in original bytemap
! 1449: * magstep (I) int containing magnification scale,
! 1450: * e.g., 2 to double the width and height of rp
! 1451: * --------------------------------------------------------------------------
! 1452: * Returns: ( intbyte * ) ptr to new bytemap magnified relative to
! 1453: * original bytemap, or NULL for any error.
! 1454: * --------------------------------------------------------------------------
! 1455: * Notes: o Apply EPX/Scale2x/AdvMAME2x for magstep 2,
! 1456: * and Scale3x/AdvMAME3x for magstep 3,
! 1457: * as described by http://en.wikipedia.org/wiki/2xSaI
! 1458: * ======================================================================= */
! 1459: /* --- entry point --- */
! 1460: intbyte *bytemapmag ( intbyte *bytemap, int width, int height, int magstep )
! 1461: {
! 1462: /* -------------------------------------------------------------------------
! 1463: Allocations and Declarations
! 1464: -------------------------------------------------------------------------- */
! 1465: intbyte *magnified=NULL; /* magnified bytemap back to caller*/
! 1466: int irow, icol, /* original height, width indexes */
! 1467: mrow=0, mcol=0; /* dup bytes magstep*magstep times */
! 1468: int imap = (-1), /* original bytemap[] index */
! 1469: byteval = 0; /* byteval=bytemap[imap] */
! 1470: int isAdvMAME = 1; /* true to apply AdvMAME2x and 3x */
! 1471: int icell[10], /* bytemap[] nearest neighbors */
! 1472: bmmdiff = 64; /* nearest neighbor diff allowed */
! 1473: #define bmmeq(i,j) ((absval((icell[i]-icell[j]))<=bmmdiff)) /*approx equal*/
! 1474: /* -------------------------------------------------------------------------
! 1475: check args
! 1476: -------------------------------------------------------------------------- */
! 1477: if ( bytemap == NULL ) goto end_of_job; /* no input bytemap supplied */
! 1478: if ( width<1 || height<1 ) goto end_of_job; /* invalid bytemap dimensions */
! 1479: if ( width*height>100000 ) goto end_of_job; /* sanity check */
! 1480: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
! 1481: /* -------------------------------------------------------------------------
! 1482: allocate magnified bytemap and fill it
! 1483: -------------------------------------------------------------------------- */
! 1484: /* --- allocate bytemap for magstep*width, magstep*height --- */
! 1485: if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
! 1486: != NULL ) /* check that allocation succeeded */
! 1487: /* --- fill reflected raster --- */
! 1488: for ( irow=0; irow<height; irow++ ) /* for each row of bytemap */
! 1489: for ( icol=0; icol<width; icol++ ) { /* and for each column of bytemap */
! 1490: int imag1 = (icol + irow*(width*magstep))*magstep; /*upper-left corner*/
! 1491: imap++; /* bump bytemap[] index */
! 1492: byteval = (int)(bytemap[imap]); /* grayscale value at this pixel */
! 1493: for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
! 1494: for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
! 1495: int idup = mcol + mrow*(width*magstep); /* offset from imag1 */
! 1496: int imag = imag1+idup; /* adjust magnified[imag] */
! 1497: magnified[imag] = (intbyte)(byteval);
! 1498: /* --- apply AdvMAME2x and 3x (if desired) --- */
! 1499: if ( isAdvMAME ) { /* AdvMAME2x and 3x wanted */
! 1500: int mcell = 1 + mcol + magstep*mrow; /*1,2,3,4 or 1,2,3,4,5,6,7,8,9*/
! 1501: icell[5]= byteval, /* center cell of 3x3 bytemap[] */
! 1502: icell[4]= (icol>0?(int)(bytemap[imap-1]):byteval), /*left of center*/
! 1503: icell[6]= (icol<width?(int)(bytemap[imap+1]):byteval), /*right*/
! 1504: icell[2]= (irow>0?(int)(bytemap[imap-width]):byteval),/*above center*/
! 1505: icell[8]= (irow<height?(int)(bytemap[imap+width]):byteval), /*below*/
! 1506: icell[1]= (irow>0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
! 1507: icell[3]= (irow>0&&icol<width?(int)(bytemap[imap-width+1]):byteval),
! 1508: icell[7]= (irow<height&&icol>0?(int)(bytemap[imap+width-1]):byteval),
! 1509: icell[9]=(irow<height&&icol<width?(int)(bytemap[imap+width+1]):byteval);
! 1510: switch ( magstep ) { /* 2x magstep=2, 3x magstep=3 */
! 1511: default: break; /* no AdvMAME at other magsteps */
! 1512: case 2: /* AdvMAME2x */
! 1513: if ( mcell == 1 )
! 1514: if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
! 1515: magnified[imag] = icell[2];
! 1516: if ( mcell == 2 )
! 1517: if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
! 1518: magnified[imag] = icell[6];
! 1519: if ( mcell == 4 )
! 1520: if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
! 1521: magnified[imag] = icell[8];
! 1522: if ( mcell == 3 )
! 1523: if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
! 1524: magnified[imag] = icell[4];
! 1525: break;
! 1526: case 3: /* AdvMAME3x */
! 1527: if ( mcell == 1 )
! 1528: if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
! 1529: magnified[imag] = icell[4];
! 1530: if ( mcell == 2 )
! 1531: if ( (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,3))
! 1532: || (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,1)) )
! 1533: magnified[imag] = icell[2];
! 1534: if ( mcell == 3 )
! 1535: if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
! 1536: magnified[imag] = icell[6];
! 1537: if ( mcell == 4 )
! 1538: if ( (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,1))
! 1539: || (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,7)) )
! 1540: magnified[imag] = icell[4];
! 1541: if ( mcell == 6 )
! 1542: if ( (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,9))
! 1543: || (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,3)) )
! 1544: magnified[imag] = icell[6];
! 1545: if ( mcell == 7 )
! 1546: if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
! 1547: magnified[imag] = icell[4];
! 1548: if ( mcell == 8 )
! 1549: if ( (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,7))
! 1550: || (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,9)) )
! 1551: magnified[imag] = icell[8];
! 1552: if ( mcell == 9 )
! 1553: if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
! 1554: magnified[imag] = icell[6];
! 1555: break;
! 1556: } } /* --- end-of-switch(magstep) --- */
! 1557: } /* --- end-of-for(mrow,mcol) --- */
! 1558: } /* --- end-of-for(irow,icol) --- */
! 1559: end_of_job:
! 1560: return ( magnified ); /*return magnified raster to caller*/
! 1561: } /* --- end-of-function bytemapmag() --- */
! 1562:
! 1563:
! 1564: /* ==========================================================================
1.3 albertel 1565: * Function: rastref ( rp, axis )
1566: * Purpose: reflects rp, horizontally about y-axis |_ becomes _| if axis=1
1567: * or vertically about x-axis M becomes W if axis=2.
1568: * --------------------------------------------------------------------------
1569: * Arguments: rp (I) ptr to raster struct to be reflected
1570: * axis (I) int containing 1 for horizontal reflection,
1571: * or 2 for vertical
1572: * --------------------------------------------------------------------------
1573: * Returns: ( raster * ) ptr to new raster reflected relative to rp,
1574: * or NULL for any error.
1575: * --------------------------------------------------------------------------
1576: * Notes: o
1577: * ======================================================================= */
1578: /* --- entry point --- */
1579: raster *rastref ( raster *rp, int axis )
1580: {
1581: /* -------------------------------------------------------------------------
1582: Allocations and Declarations
1583: -------------------------------------------------------------------------- */
1584: raster *new_raster(), *reflected=NULL; /* reflected raster back to caller */
1585: int height = rp->height, irow, /* height, row index */
1586: width = rp->width, icol, /* width, column index */
1587: pixsz = rp->pixsz; /* #bits per pixel */
1588: /* -------------------------------------------------------------------------
1589: allocate reflected raster and fill it
1590: -------------------------------------------------------------------------- */
1591: /* --- allocate reflected raster with same width, height --- */
1592: if ( axis==1 || axis==2 ) /* first validate axis arg */
1593: if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
1594: != NULL ) /* check that allocation succeeded */
1595: /* --- fill reflected raster --- */
1596: for ( irow=0; irow<height; irow++ ) /* for each row of rp */
1597: for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
1598: int value = getpixel(rp,irow,icol);
1599: if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
1600: if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
1601: return ( reflected ); /*return reflected raster to caller*/
1602: } /* --- end-of-function rastref() --- */
1603:
1604:
1605: /* ==========================================================================
1.1 albertel 1606: * Function: rastput ( target, source, top, left, isopaque )
1607: * Purpose: Overlays source onto target,
1608: * with the 0,0-bit of source onto the top,left-bit of target.
1609: * --------------------------------------------------------------------------
1610: * Arguments: target (I) ptr to target raster struct
1611: * source (I) ptr to source raster struct
1612: * top (I) int containing 0 ... target->height - 1
1613: * left (I) int containing 0 ... target->width - 1
1614: * isopaque (I) int containing false (zero) to allow
1615: * original 1-bits of target to "show through"
1616: * 0-bits of source.
1617: * --------------------------------------------------------------------------
1618: * Returns: ( int ) 1 if completed successfully,
1619: * or 0 otherwise (for any error).
1620: * --------------------------------------------------------------------------
1621: * Notes:
1622: * ======================================================================= */
1623: /* --- entry point --- */
1624: int rastput ( raster *target, raster *source,
1625: int top, int left, int isopaque )
1626: {
1627: /* -------------------------------------------------------------------------
1628: Allocations and Declarations
1629: -------------------------------------------------------------------------- */
1630: int irow, icol, /* indexes over source raster */
1631: twidth=target->width, theight=target->height, /*target width,height*/
1632: tpix, ntpix = twidth*theight; /* #pixels in target */
1633: int isfatal = 0, /* true to abend on out-of-bounds error */
1634: isstrict = 0/*1*/, /* true for strict bounds check - no "wrap"*/
1635: isokay = 1; /* true if no pixels out-of-bounds */
1636: /* -------------------------------------------------------------------------
1637: superimpose source onto target, one bit at a time
1638: -------------------------------------------------------------------------- */
1639: if ( isstrict && (top<0 || left<0) ) /* args fail strict test */
1640: isokay = 0; /* so just return error */
1641: else
1642: for ( irow=0; irow<source->height; irow++ ) /* for each scan line */
1643: {
1644: tpix = (top+irow)*target->width + left - 1; /*first target pixel (-1)*/
1645: for ( icol=0; icol<source->width; icol++ ) /* each pixel in scan line */
1646: {
1647: int svalue = getpixel(source,irow,icol); /* source pixel value */
1648: ++tpix; /* bump target pixel */
1649: if ( msgfp!=NULL && msglevel>=9999 ) /* debugging output */
1650: { fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
1651: "left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
1652: left,icol,twidth); fflush(msgfp); }
1653: if ( tpix >= ntpix /* bounds check failed */
1654: || (isstrict && (irow+top>=theight || icol+left>=twidth)) )
1655: { isokay = 0; /* reset okay flag */
1656: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
1657: else break; } /*or just go on to next row*/
1658: if ( tpix >= 0 ) /* bounds check okay */
1.3 albertel 1659: if ( svalue!=0 || isopaque ) { /*got dark or opaque source*/
1660: setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
1.1 albertel 1661: } /* --- end-of-for(icol) --- */
1662: } /* --- end-of-for(irow) --- */
1663: /* -------------------------------------------------------------------------
1664: Back to caller with 1=okay, 0=failed.
1665: -------------------------------------------------------------------------- */
1666: end_of_job:
1667: return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
1668: } /* --- end-of-function rastput() --- */
1669:
1670:
1671: /* ==========================================================================
1672: * Function: rastcompose ( sp1, sp2, offset2, isalign, isfree )
1673: * Purpose: Overlays sp2 on top of sp1, leaving both unchanged
1674: * and returning a newly-allocated composite subraster.
1675: * Frees/deletes input sp1 and/or sp2 depending on value
1676: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1677: * --------------------------------------------------------------------------
1678: * Arguments: sp1 (I) subraster * to "underneath" subraster,
1679: * whose baseline is preserved
1680: * sp2 (I) subraster * to "overlaid" subraster
1681: * offset2 (I) int containing 0 or number of pixels
1682: * to horizontally shift sp2 relative to sp1,
1683: * either positive (right) or negative
1684: * isalign (I) int containing 1 to align baselines,
1.5 ! raeburn 1685: * or 0 to vertically center sp2 over sp1.
! 1686: * For isalign=2, images are vertically
! 1687: * centered, but then adjusted by \raisebox
! 1688: * lifts, using global variables rastlift1
! 1689: * for sp1 and rastlift for sp2.
1.1 albertel 1690: * isfree (I) int containing 1=free sp1 before return,
1691: * 2=free sp2, 3=free both, 0=free none.
1692: * --------------------------------------------------------------------------
1693: * Returns: ( subraster * ) pointer to constructed subraster
1694: * or NULL for any error
1695: * --------------------------------------------------------------------------
1.5 ! raeburn 1696: * Notes: o The top-left corner of each raster box has coords (0,0),
! 1697: * down to (h-1,w-1) for a box of height h and width w.
! 1698: * o A raster's baseline, b, is typically 0 <= b < h.
! 1699: * But b can actually go out-of-bounds, b>=h or b<0, for
! 1700: * an image additionally lifted (b>=h) or lowered (b<0)
! 1701: * with respect to the surrounding expression.
! 1702: * o Note that b=h-1 means no descenders and the bottom
! 1703: * of the symbol rests exactly on the baseline,
! 1704: * whereas b=0 means the top pixel of the symbol rests
! 1705: * on the baseline, and all other pixels are descenders.
! 1706: * o The composite raster is constructed as follows...
! 1707: * The base image is labelled height h1 and baseline b1,
! 1708: * the overlay h2 and b2, and the composite H and B.
! 1709: * base overlay
! 1710: * --- +------------------------+ --- For the overlay to be
! 1711: * ^ | ^ +----------+| ^ vertically centered with
! 1712: * | | | | || | respect to the base,
! 1713: * | | |B-b1 | || | B - b1 = H-B -(h1-b1), so
! 1714: * | | v | || | 2*B = H-h1 + 2*b1
! 1715: * | |+----------+| || | B = b1 + (H-h1)/2
! 1716: * B || ^ ^ || || | And when the base image is
! 1717: * | || | | || || | bigger, H=h1 and B=b1 is
! 1718: * | || b1 | || || | the obvious correct answer.
! 1719: * | || | h1 || || H=h2
! 1720: * v || v | || || |
! 1721: * ----------||-------|--|| ||--|--------
! 1722: * baseline || h1-b1 v || overlay || |
! 1723: * for base |+----------+| baseline || |
! 1724: * and com- | ^ | ignored || |
! 1725: * posite | |H-B- |----------|| |
! 1726: * | | (h1-b1)| || |
! 1727: * | v +----------+| v
! 1728: * +------------------------+ ---
1.1 albertel 1729: * ======================================================================= */
1730: /* --- entry point --- */
1731: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
1732: int isalign, int isfree )
1733: {
1734: /* -------------------------------------------------------------------------
1735: Allocations and Declarations
1736: -------------------------------------------------------------------------- */
1737: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1738: raster *rp=(raster *)NULL; /* new composite raster in sp */
1739: int delete_subraster(); /* in case isfree non-zero */
1740: int rastput(); /*place sp1,sp2 in composite raster*/
1741: int base1 = sp1->baseline, /*baseline for underlying subraster*/
1742: height1 = (sp1->image)->height, /* height for underlying subraster */
1743: width1 = (sp1->image)->width, /* width for underlying subraster */
1744: pixsz1 = (sp1->image)->pixsz, /* pixsz for underlying subraster */
1745: base2 = sp2->baseline, /*baseline for overlaid subraster */
1746: height2 = (sp2->image)->height, /* height for overlaid subraster */
1747: width2 = (sp2->image)->width, /* width for overlaid subraster */
1748: pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
1.5 ! raeburn 1749: int height = max2(height1,height2), /*composite height if sp2 centered*/
! 1750: base = base1 + (height-height1)/2, /* and composite baseline */
! 1751: tlc2 = (height-height2)/2, /* top-left corner for overlay */
! 1752: width=0, pixsz=0; /* other params for composite */
! 1753: int lift1 = rastlift1, /* vertical \raisebox lift for sp1 */
! 1754: lift2 = rastlift; /* vertical \raisebox lift for sp2 */
1.1 albertel 1755: /* -------------------------------------------------------------------------
1756: Initialization
1757: -------------------------------------------------------------------------- */
1758: /* --- determine height, width and baseline of composite raster --- */
1.5 ! raeburn 1759: switch ( isalign ) {
! 1760: default:
! 1761: case 0: /* centered, baselines not aligned */
! 1762: height = max2(height1,height2); /* max height */
! 1763: base = base1 + (height-height1)/2; /* baseline for sp1 */
! 1764: break;
! 1765: case 1: /* baselines of sp1,sp2 aligned */
! 1766: height = max2(base1+1,base2+1) /* max height above baseline */
! 1767: + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
! 1768: base = max2(base1,base2); /* max space above baseline */
! 1769: break;
! 1770: case 2: /* centered +/- \raisebox lifts */
! 1771: base1 -= lift1; base2 -= lift2; /* reset to unlifted images */
! 1772: /* --- start with default for centered, unlifted images --- */
! 1773: height2 += 2*absval(lift2); /* "virtual" height of overlay */
! 1774: height = max2(height1,height2); /* max height */
! 1775: base = base1 + (height-height1)/2; /* baseline for sp1 */
! 1776: tlc2 = (height-height2)/2 /* top-left corner for overlay */
! 1777: + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
! 1778: break;
! 1779: } /* --- end-of-switch(isalign) --- */
! 1780: width = max2(width1,width2+abs(offset2)); /* max width */
! 1781: pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
1.1 albertel 1782: /* -------------------------------------------------------------------------
1783: allocate concatted composite subraster
1784: -------------------------------------------------------------------------- */
1785: /* --- allocate returned subraster (and then initialize it) --- */
1786: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1787: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1788: /* --- initialize subraster parameters --- */
1789: sp->type = IMAGERASTER; /* image */
1790: sp->baseline = base; /* composite baseline */
1791: sp->size = sp1->size; /* underlying char is sp1 */
1.5 ! raeburn 1792: if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
1.1 albertel 1793: /* --- extract raster from subraster --- */
1794: rp = sp->image; /* raster allocated in subraster */
1795: /* -------------------------------------------------------------------------
1796: overlay sp1 and sp2 in new composite raster
1797: -------------------------------------------------------------------------- */
1.5 ! raeburn 1798: switch ( isalign ) {
! 1799: default:
! 1800: case 0: /* centered, baselines not aligned */
! 1801: rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
! 1802: rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
! 1803: (width-width2)/2+offset2, 0);
! 1804: break;
! 1805: case 1: /* baselines of sp1,sp2 aligned */
! 1806: rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
1.1 albertel 1807: rastput (rp, sp2->image, base-base2, /*overlaid*/
1.5 ! raeburn 1808: (width-width2)/2+offset2, 0);
! 1809: break;
! 1810: case 2: if(1){ /* centered +/- \raisebox lifts */
! 1811: rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
! 1812: rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
! 1813: break;
! 1814: } /* --- end-of-switch(isalign) --- */
1.1 albertel 1815: /* -------------------------------------------------------------------------
1816: free input if requested
1817: -------------------------------------------------------------------------- */
1818: if ( isfree > 0 ) /* caller wants input freed */
1819: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1820: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
1821: /* -------------------------------------------------------------------------
1822: Back to caller with pointer to concatted subraster or with null for error
1823: -------------------------------------------------------------------------- */
1824: end_of_job:
1825: return ( sp ); /* back with subraster or null ptr */
1826: } /* --- end-of-function rastcompose() --- */
1827:
1828:
1829: /* ==========================================================================
1830: * Function: rastcat ( sp1, sp2, isfree )
1831: * Purpose: "Concatanates" subrasters sp1||sp2, leaving both unchanged
1832: * and returning a newly-allocated subraster.
1833: * Frees/deletes input sp1 and/or sp2 depending on value
1834: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1835: * --------------------------------------------------------------------------
1836: * Arguments: sp1 (I) subraster * to left-hand subraster
1837: * sp2 (I) subraster * to right-hand subraster
1838: * isfree (I) int containing 1=free sp1 before return,
1839: * 2=free sp2, 3=free both, 0=free none.
1840: * --------------------------------------------------------------------------
1841: * Returns: ( subraster * ) pointer to constructed subraster sp1||sp2
1842: * or NULL for any error
1843: * --------------------------------------------------------------------------
1844: * Notes:
1845: * ======================================================================= */
1846: /* --- entry point --- */
1847: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
1848: {
1849: /* -------------------------------------------------------------------------
1850: Allocations and Declarations
1851: -------------------------------------------------------------------------- */
1852: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1853: raster *rp=(raster *)NULL; /* new concatted raster */
1854: int delete_subraster(); /* in case isfree non-zero */
1855: int rastput(); /*place sp1,sp2 in concatted raster*/
1856: int type_raster(); /* debugging display */
1857: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
1858: height1 = (sp1->image)->height, /* height for left-hand subraster */
1859: width1 = (sp1->image)->width, /* width for left-hand subraster */
1860: pixsz1 = (sp1->image)->pixsz, /* pixsz for left-hand subraster */
1861: type1 = sp1->type, /* image type for left-hand */
1862: base2 = sp2->baseline, /*baseline for right-hand subraster*/
1863: height2 = (sp2->image)->height, /* height for right-hand subraster */
1864: width2 = (sp2->image)->width, /* width for right-hand subraster */
1865: pixsz2 = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
1866: type2 = sp2->type; /* image type for right-hand */
1867: int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
1.2 albertel 1868: int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
1869: isopaque = (issmash?0:1), /* not oppaque if smashing */
1870: rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
1.3 albertel 1871: oldsmashmargin = smashmargin, /* save original smashmargin */
1872: oldblanksymspace = blanksymspace, /* save original blanksymspace */
1873: oldnocatspace = isnocatspace; /* save original isnocatspace */
1.1 albertel 1874: mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
1875: *symdef2 = sp2->symdef; /* mathchardef of right-hand char */
1876: int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
1877: class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
1878: smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
1879: class1==OPENING||class1==CLOSING||class1==PUNCTION),
1880: smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
1881: class2==OPENING||class2==CLOSING||class2==PUNCTION),
1882: space = fontsize/2+1; /* #cols between sp1 and sp2 */
1.3 albertel 1883: int isfrac = (type1 == FRACRASTER /* sp1 is a \frac */
1884: && class2 == PUNCTION); /* and sp2 is punctuation */
1.1 albertel 1885: /* -------------------------------------------------------------------------
1886: Initialization
1887: -------------------------------------------------------------------------- */
1888: /* --- determine inter-character space from character class --- */
1889: if ( !isstring )
1890: space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
1891: else space = 1; /* space for ascii string */
1.3 albertel 1892: if ( isnocatspace > 0 ) { /* spacing explicitly turned off */
1893: space = 0; /* reset space */
1894: isnocatspace--; } /* and decrement isnocatspace flag */
1895: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
1896: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
1897: space = 0; /* no extra space between spaces */
1898: if ( sp2->type != BLANKSIGNAL ) /* not a blank space signal */
1899: if ( blanksymspace != 0 ) { /* and we have a space adjustment */
1900: space = max2(0,space+blanksymspace); /* adjust as much as possible */
1901: blanksymspace = 0; } /* and reset adjustment */
1902: if ( msgfp!=NULL && msglevel>=999 ) /* display space results */
1903: { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
1904: space,oldblanksymspace,oldnocatspace); fflush(msgfp); }
1.2 albertel 1905: /* --- determine smash --- */
1.3 albertel 1906: if ( !isstring && !isfrac ) /* don't smash strings or \frac's */
1.2 albertel 1907: if ( issmash ) { /* raster smash wanted */
1908: int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
1909: margin = smashmargin; /* init margin without delta */
1.1 albertel 1910: if ( (1 && smash1 && smash2) /* concatanating two chars */
1.3 albertel 1911: || (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
1912: && type1!=FRACRASTER && type2!=FRACRASTER ) )
1.2 albertel 1913: /*maxsmash = 0;*/ /* turn off smash */
1914: margin = max2(space-1,0); /* force small smashmargin */
1.1 albertel 1915: else /* adjust for delta if images */
1.2 albertel 1916: if ( issmashdelta ) /* smashmargin is a delta value */
1.1 albertel 1917: margin += fontsize; /* add displaystyle base to margin */
1.2 albertel 1918: if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
1.1 albertel 1919: isblank = 1; /* set blank flag signal */
1920: else /* see how much extra space we have*/
1.2 albertel 1921: if ( maxsmash > margin ) /* enough space for adjustment */
1922: nsmash = maxsmash-margin; /* make adjustment */
1923: if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
1924: { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
1925: maxsmash,margin,nsmash);
1.1 albertel 1926: fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
1927: (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
1928: fflush(msgfp); }
1.2 albertel 1929: } /* --- end-of-if(issmash) --- */
1.1 albertel 1930: /* --- determine height, width and baseline of composite raster --- */
1931: if ( !isstring )
1932: { height = max2(base1+1,base2+1) /* max height above baseline */
1933: + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1.2 albertel 1934: width = width1+width2 + space-nsmash; /*add widths and space-smash*/
1935: width = max3(width,width1,width2); } /* don't "over-smash" composite */
1.1 albertel 1936: else /* ascii string */
1937: { height = 1; /* default */
1938: width = width1 + width2 + space - 1; } /* no need for two nulls */
1939: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
1940: base = max2(base1,base2); /* max space above baseline */
1941: if ( msgfp!=NULL && msglevel>=9999 ) /* display components */
1942: { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1943: height1,width1,pixsz1,base1);
1944: type_raster(sp1->image,msgfp); /* display left-hand raster */
1945: fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
1946: height2,width2,pixsz2,base2);
1947: type_raster(sp2->image,msgfp); /* display right-hand raster */
1948: fprintf(msgfp,
1.2 albertel 1949: "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
1950: height,width,nsmash,pixsz,base);
1.1 albertel 1951: fflush(msgfp); } /* flush msgfp buffer */
1952: /* -------------------------------------------------------------------------
1953: allocate concatted composite subraster
1954: -------------------------------------------------------------------------- */
1955: /* --- allocate returned subraster (and then initialize it) --- */
1956: if ( msgfp!=NULL && msglevel>=9999 )
1957: { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
1958: width,height,pixsz); fflush(msgfp); }
1959: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1960: == (subraster *)NULL ) /* failed */
1961: { if ( msgfp!=NULL && msglevel>=1 ) /* report failure */
1962: { fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
1963: width,height,pixsz); fflush(msgfp); }
1964: goto end_of_job; } /* failed, so quit */
1965: /* --- initialize subraster parameters --- */
1966: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */ /*concatted string*/
1967: if ( !isstring )
1.2 albertel 1968: sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
1.3 albertel 1969: (type2!=CHARASTER? type2 :
1970: (type1!=CHARASTER&&type1!=BLANKSIGNAL
1971: &&type1!=FRACRASTER?type1:IMAGERASTER));
1.1 albertel 1972: else
1973: sp->type = ASCIISTRING; /* concatted ascii string */
1974: sp->symdef = symdef2; /* rightmost char is sp2 */
1975: sp->baseline = base; /* composite baseline */
1976: sp->size = sp2->size; /* rightmost char is sp2 */
1977: if ( isblank ) /* need to propagate blanksignal */
1978: sp->type = blanksignal; /* may not be completely safe??? */
1979: /* --- extract raster from subraster --- */
1980: rp = sp->image; /* raster allocated in subraster */
1981: /* -------------------------------------------------------------------------
1982: overlay sp1 and sp2 in new composite raster
1983: -------------------------------------------------------------------------- */
1984: if ( msgfp!=NULL && msglevel>=9999 )
1985: { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
1986: fflush(msgfp); } /* flush msgfp buffer */
1987: if ( !isstring )
1988: rastput (rp, sp1->image, base-base1, /* overlay left-hand */
1.2 albertel 1989: max2(0,nsmash-width1), 1); /* plus any residual smash space */
1.1 albertel 1990: else
1991: memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1); /*init left string*/
1992: if ( msgfp!=NULL && msglevel>=9999 )
1993: { type_raster(sp->image,msgfp); /* display composite raster */
1994: fflush(msgfp); } /* flush msgfp buffer */
1995: if ( !isstring )
1.3 albertel 1996: { int fracbase = ( isfrac? /* baseline for punc after \frac */
1997: max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
1998: rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
1999: max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
2000: if ( 1 && type1 == FRACRASTER /* we're done with \frac image */
2001: && type2 != FRACRASTER ) /* unless we have \frac\frac */
2002: fraccenterline = NOVALUE; /* so reset centerline signal */
2003: if ( fraccenterline != NOVALUE ) /* sp2 is a fraction */
2004: fraccenterline += (base-base2); } /* so adjust its centerline */
1.1 albertel 2005: else
2006: { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
2007: ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
2008: if ( msgfp!=NULL && msglevel>=9999 )
2009: { type_raster(sp->image,msgfp); /* display composite raster */
2010: fflush(msgfp); } /* flush msgfp buffer */
2011: /* -------------------------------------------------------------------------
2012: free input if requested
2013: -------------------------------------------------------------------------- */
2014: if ( isfree > 0 ) /* caller wants input freed */
2015: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2016: if ( isfree >= 2 ) delete_subraster(sp2); } /* and/or sp2 */
2017: /* -------------------------------------------------------------------------
2018: Back to caller with pointer to concatted subraster or with null for error
2019: -------------------------------------------------------------------------- */
2020: end_of_job:
1.2 albertel 2021: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.1 albertel 2022: return ( sp ); /* back with subraster or null ptr */
2023: } /* --- end-of-function rastcat() --- */
2024:
2025:
2026: /* ==========================================================================
2027: * Function: rastack ( sp1, sp2, base, space, iscenter, isfree )
2028: * Purpose: Stack subrasters sp2 atop sp1, leaving both unchanged
2029: * and returning a newly-allocated subraster,
2030: * whose baseline is sp1's if base=1, or sp2's if base=2.
2031: * Frees/deletes input sp1 and/or sp2 depending on value
2032: * of isfree (0=none, 1=sp1, 2=sp2, 3=both).
2033: * --------------------------------------------------------------------------
2034: * Arguments: sp1 (I) subraster * to lower subraster
2035: * sp2 (I) subraster * to upper subraster
2036: * base (I) int containing 1 if sp1 is baseline,
2037: * or 2 if sp2 is baseline.
2038: * space (I) int containing #rows blank space inserted
2039: * between sp1's image and sp2's image.
2040: * iscenter (I) int containing 1 to center both sp1 and sp2
2041: * in stacked array, 0 to left-justify both
2042: * isfree (I) int containing 1=free sp1 before return,
2043: * 2=free sp2, 3=free both, 0=free none.
2044: * --------------------------------------------------------------------------
2045: * Returns: ( subraster * ) pointer to constructed subraster sp2 atop sp1
2046: * or NULL for any error
2047: * --------------------------------------------------------------------------
2048: * Notes:
2049: * ======================================================================= */
2050: /* --- entry point --- */
2051: subraster *rastack ( subraster *sp1, subraster *sp2,
2052: int base, int space, int iscenter, int isfree )
2053: {
2054: /* -------------------------------------------------------------------------
2055: Allocations and Declarations
2056: -------------------------------------------------------------------------- */
2057: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
2058: raster *rp=(raster *)NULL; /* new stacked raster in sp */
2059: int delete_subraster(); /* in case isfree non-zero */
2060: int rastput(); /* place sp1,sp2 in stacked raster */
2061: int base1 = sp1->baseline, /* baseline for lower subraster */
2062: height1 = (sp1->image)->height, /* height for lower subraster */
2063: width1 = (sp1->image)->width, /* width for lower subraster */
2064: pixsz1 = (sp1->image)->pixsz, /* pixsz for lower subraster */
2065: base2 = sp2->baseline, /* baseline for upper subraster */
2066: height2 = (sp2->image)->height, /* height for upper subraster */
2067: width2 = (sp2->image)->width, /* width for upper subraster */
2068: pixsz2 = (sp2->image)->pixsz; /* pixsz for upper subraster */
2069: int height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
2070: mathchardef *symdef1 = sp1->symdef, /* mathchardef of right lower char */
2071: *symdef2 = sp2->symdef; /* mathchardef of right upper char */
2072: /* -------------------------------------------------------------------------
2073: Initialization
2074: -------------------------------------------------------------------------- */
2075: /* --- determine height, width and baseline of composite raster --- */
2076: height = height1 + space + height2; /* sum of heights plus space */
2077: width = max2(width1,width2); /* max width is overall width */
2078: pixsz = max2(pixsz1,pixsz2); /* bitmap||bytemap becomes bytemap */
2079: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
2080: /* -------------------------------------------------------------------------
2081: allocate stacked composite subraster (with embedded raster)
2082: -------------------------------------------------------------------------- */
2083: /* --- allocate returned subraster (and then initialize it) --- */
2084: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
2085: == (subraster *)NULL ) goto end_of_job; /* failed, so quit */
2086: /* --- initialize subraster parameters --- */
2087: sp->type = IMAGERASTER; /* stacked rasters */
2088: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
2089: sp->baseline = baseline; /* composite baseline */
2090: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
2091: /* --- extract raster from subraster --- */
2092: rp = sp->image; /* raster embedded in subraster */
2093: /* -------------------------------------------------------------------------
2094: overlay sp1 and sp2 in new composite raster
2095: -------------------------------------------------------------------------- */
2096: if ( iscenter == 1 ) /* center both sp1 and sp2 */
2097: { rastput (rp, sp2->image, 0, (width-width2)/2, 1); /* overlay upper */
2098: rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
2099: else /* left-justify both sp1 and sp2 */
2100: { rastput (rp, sp2->image, 0, 0, 1); /* overlay upper */
2101: rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
2102: /* -------------------------------------------------------------------------
2103: free input if requested
2104: -------------------------------------------------------------------------- */
2105: if ( isfree > 0 ) /* caller wants input freed */
2106: { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2107: if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
2108: /* -------------------------------------------------------------------------
2109: Back to caller with pointer to stacked subraster or with null for error
2110: -------------------------------------------------------------------------- */
2111: end_of_job:
2112: return ( sp ); /* back with subraster or null ptr */
2113: } /* --- end-of-function rastack() --- */
2114:
2115:
2116: /* ==========================================================================
2117: * Function: rastile ( tiles, ntiles )
2118: * Purpose: Allocate and build up a composite raster
2119: * from the ntiles components/characters supplied in tiles.
2120: * --------------------------------------------------------------------------
2121: * Arguments: tiles (I) subraster * to array of subraster structs
2122: * describing the components and their locations
2123: * ntiles (I) int containing number of subrasters in tiles[]
2124: * --------------------------------------------------------------------------
2125: * Returns: ( raster * ) ptr to composite raster,
2126: * or NULL for any error.
2127: * --------------------------------------------------------------------------
2128: * Notes: o The top,left corner of a raster is row=0,col=0
2129: * with row# increasing as you move down,
2130: * and col# increasing as you move right.
2131: * Metafont numbers rows with the baseline=0,
2132: * so the top row is a positive number that
2133: * decreases as you move down.
2134: * o rastile() is no longer used.
2135: * It was used by an earlier rasterize() algorithm,
2136: * and I've left it in place should it be needed again.
2137: * But recent changes haven't been tested/exercised.
2138: * ======================================================================= */
2139: /* --- entry point --- */
2140: raster *rastile ( subraster *tiles, int ntiles )
2141: {
2142: /* -------------------------------------------------------------------------
2143: Allocations and Declarations
2144: -------------------------------------------------------------------------- */
2145: raster *new_raster(), *composite=(raster *)NULL; /*raster back to caller*/
2146: int width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
2147: toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
2148: botrow=-999, leftcol=9999; /* extreme lower-left corner of tiles */
2149: int itile; /* tiles[] index */
2150: int rastput(); /* overlay each tile in composite raster */
2151: /* -------------------------------------------------------------------------
2152: run through tiles[] to determine dimensions for composite raster
2153: -------------------------------------------------------------------------- */
2154: /* --- determine row and column bounds of composite raster --- */
2155: for ( itile=0; itile<ntiles; itile++ )
2156: {
2157: subraster *tile = &(tiles[itile]); /* ptr to current tile */
2158: /* --- upper-left corner of composite --- */
2159: toprow = min2(toprow, tile->toprow);
2160: leftcol = min2(leftcol, tile->leftcol);
2161: /* --- lower-right corner of composite --- */
2162: botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
2163: rightcol = max2(rightcol, tile->leftcol + (tile->image)->width - 1);
2164: /* --- pixsz of composite --- */
2165: pixsz = max2(pixsz,(tile->image)->pixsz);
2166: } /* --- end-of-for(itile) --- */
2167: /* --- calculate width and height from bounds --- */
2168: width = rightcol - leftcol + 1;
2169: height = botrow - toprow + 1;
2170: /* --- sanity check (quit if bad dimensions) --- */
2171: if ( width<1 || height<1 ) goto end_of_job;
2172: /* -------------------------------------------------------------------------
2173: allocate composite raster, and embed tiles[] within it
2174: -------------------------------------------------------------------------- */
2175: /* --- allocate composite raster --- */
2176: if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
2177: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
2178: /* --- embed tiles[] in composite --- */
2179: for ( itile=0; itile<ntiles; itile++ )
2180: { subraster *tile = &(tiles[itile]); /* ptr to current tile */
2181: rastput (composite, tile->image, /* overlay tile image at...*/
2182: tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
2183: /* -------------------------------------------------------------------------
2184: Back to caller with composite raster (or null for any error)
2185: -------------------------------------------------------------------------- */
2186: end_of_job:
2187: return ( composite ); /* back with composite or null ptr */
2188: } /* --- end-of-function rastile() --- */
2189:
2190:
2191: /* ==========================================================================
1.2 albertel 2192: * Function: rastsmash ( sp1, sp2 )
1.1 albertel 2193: * Purpose: When concatanating sp1||sp2, calculate #pixels
1.2 albertel 2194: * we can "smash sp2 left"
1.1 albertel 2195: * --------------------------------------------------------------------------
2196: * Arguments: sp1 (I) subraster * to left-hand raster
2197: * sp2 (I) subraster * to right-hand raster
2198: * --------------------------------------------------------------------------
1.2 albertel 2199: * Returns: ( int ) max #pixels we can smash sp1||sp2,
1.1 albertel 2200: * or "blanksignal" if sp2 intentionally blank,
2201: * or 0 for any error.
2202: * --------------------------------------------------------------------------
2203: * Notes: o
2204: * ======================================================================= */
2205: /* --- entry point --- */
1.2 albertel 2206: int rastsmash ( subraster *sp1, subraster *sp2 )
1.1 albertel 2207: {
2208: /* -------------------------------------------------------------------------
2209: Allocations and Declarations
2210: -------------------------------------------------------------------------- */
1.2 albertel 2211: int nsmash = 0; /* #pixels to smash sp1||sp2 */
1.1 albertel 2212: int base1 = sp1->baseline, /*baseline for left-hand subraster*/
2213: height1 = (sp1->image)->height, /* height for left-hand subraster */
2214: width1 = (sp1->image)->width, /* width for left-hand subraster */
2215: base2 = sp2->baseline, /*baseline for right-hand subraster*/
2216: height2 = (sp2->image)->height, /* height for right-hand subraster */
2217: width2 = (sp2->image)->width; /* width for right-hand subraster */
2218: int base = max2(base1,base2), /* max ascenders - 1 above baseline*/
2219: top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
2220: bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
2221: height = max2(bot1,bot2)+1; /* total height */
2222: int irow1=0,irow2=0, icol=0; /* row,col indexes */
2223: int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
2224: firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
2225: int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
2226: int type_raster(); /* display debugging output */
2227: /* -------------------------------------------------------------------------
2228: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
2229: -------------------------------------------------------------------------- */
2230: /* --- check args --- */
2231: if ( isstring ) goto end_of_job; /* ignore string rasters */
1.2 albertel 2232: if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
2233: if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
1.1 albertel 2234: if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 albertel 2235: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 2236: /* --- init firstcol1[], firstcol2[] --- */
2237: for ( irow1=0; irow1<height; irow1++ ) /* for each row */
2238: firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
2239: /* --- set firstcol2[] indicating left edge of sp2 --- */
2240: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
2241: for ( icol=0; icol<width2; icol++ ) /* find first non-empty col in row */
2242: if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
2243: { firstcol2[irow2] = icol; /* icol is #cols from left edge */
2244: nfirst2++; /* bump #rows containing set pixels*/
2245: break; } /* and go on to next row */
2246: if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
1.2 albertel 2247: { nsmash = blanksignal; /* signal intentional blanks */
2248: goto end_of_job; } /* don't smash intentional blanks */
1.1 albertel 2249: /* --- now check if preceding image in sp1 was an intentional blank --- */
2250: if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
1.2 albertel 2251: goto end_of_job; /* don't smash intentional blank */
1.1 albertel 2252: /* --- set firstcol1[] indicating right edge of sp1 --- */
2253: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
2254: for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
2255: if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
2256: { firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
2257: nfirst1++; /* bump #rows containing set pixels*/
2258: break; } /* and go on to next row */
2259: if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
1.2 albertel 2260: goto end_of_job; /* don't smash intentional blanks */
1.1 albertel 2261: /* -------------------------------------------------------------------------
2262: find minimum separation
2263: -------------------------------------------------------------------------- */
2264: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
2265: int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
1.3 albertel 2266: if ( margin2 != blanksignal ) { /* irow2 not an empty/blank row */
1.1 albertel 2267: for ( irow1=max2(irow2-smin,top1); ; irow1++ )
2268: if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
2269: else
2270: if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
2271: int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
2272: if ( ds >= smin ) continue; /* min unchanged */
1.2 albertel 2273: if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
1.1 albertel 2274: smin=ds; xmin=dx; ymin=dy; /* set new min */
2275: } /* --- end-of-if(margin1!=blanksignal) --- */
1.3 albertel 2276: } /* --- end-of-if(margin2!=blanksignal) --- */
1.2 albertel 2277: if ( smin<2 ) goto end_of_job; /* can't smash */
1.1 albertel 2278: } /* --- end-of-for(irow2) --- */
1.2 albertel 2279: /*nsmash = min2(xmin,width2);*/ /* permissible smash */
2280: nsmash = xmin; /* permissible smash */
1.1 albertel 2281: /* -------------------------------------------------------------------------
1.2 albertel 2282: Back to caller with #pixels to smash sp1||sp2
1.1 albertel 2283: -------------------------------------------------------------------------- */
2284: end_of_job:
2285: /* --- debugging output --- */
2286: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
1.2 albertel 2287: { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
2288: nsmash,smashmargin);
1.1 albertel 2289: if ( msglevel >= 999 ) /* also display rasters */
1.2 albertel 2290: { fprintf(msgfp,"rastsmash>left-hand image...\n");
1.1 albertel 2291: if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
1.2 albertel 2292: fprintf(msgfp,"rastsmash>right-hand image...\n");
1.1 albertel 2293: if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
2294: fflush(msgfp); }
1.2 albertel 2295: return ( nsmash ); /* back with #smash pixels */
2296: } /* --- end-of-function rastsmash() --- */
1.1 albertel 2297:
2298:
2299: /* ==========================================================================
1.3 albertel 2300: * Function: rastsmashcheck ( term )
2301: * Purpose: Check an exponent term to see if its leading symbol
2302: * would make smashing dangerous
2303: * --------------------------------------------------------------------------
2304: * Arguments: term (I) char * to null-terminated string
2305: * containing right-hand exponent term about to
2306: * be smashed against existing left-hand.
2307: * --------------------------------------------------------------------------
2308: * Returns: ( int ) 1 if it's okay to smash term, or
2309: * 0 if smash is dangerous.
2310: * --------------------------------------------------------------------------
2311: * Notes: o
2312: * ======================================================================= */
2313: /* --- entry point --- */
2314: int rastsmashcheck ( char *term )
2315: {
2316: /* -------------------------------------------------------------------------
2317: Allocations and Declarations
2318: -------------------------------------------------------------------------- */
2319: int isokay = 0; /* 1 to signal okay to caller */
2320: static char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
2321: static char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
2322: static char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
2323: "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
2324: char *expression = term; /* local ptr to beginning of expression */
2325: char *token = NULL; int i; /* token = nosmashstrs[i] or grayspace[i] */
2326: /* -------------------------------------------------------------------------
2327: see if smash check enabled
2328: -------------------------------------------------------------------------- */
2329: if ( smashcheck < 1 ) { /* no smash checking wanted */
2330: if ( smashcheck >= 0 ) /* -1 means check should always fail */
2331: isokay = 1; /* otherwise (if 0), signal okay to smash */
2332: goto end_of_job; } /* return to caller */
2333: /* -------------------------------------------------------------------------
2334: skip leading white and gray space
2335: -------------------------------------------------------------------------- */
2336: /* --- first check input --- */
1.5 ! raeburn 2337: if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
1.3 albertel 2338: /* --- skip leading white space --- */
1.5 ! raeburn 2339: skipwhite(term); /* skip leading white space */
1.3 albertel 2340: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
2341: /* --- skip leading gray space --- */
2342: skipgray:
2343: for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
2344: if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
2345: term += strlen(token); /* skip past this grayspace token */
1.5 ! raeburn 2346: skipwhite(term); /* and skip any subsequent white space */
1.3 albertel 2347: if ( *term == '\000' ) { /* nothing left so quit */
2348: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
2349: fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
2350: goto end_of_job; }
2351: goto skipgray; } /* restart grayspace check from beginning */
2352: /* -------------------------------------------------------------------------
2353: check for leading no-smash single char
2354: -------------------------------------------------------------------------- */
2355: /* --- don't smash if term begins with a "nosmash" char --- */
2356: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
2357: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
2358: fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
2359: goto end_of_job; }
2360: /* -------------------------------------------------------------------------
2361: check for leading no-smash token
2362: -------------------------------------------------------------------------- */
2363: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
2364: if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
2365: if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
2366: fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
2367: goto end_of_job; } /* so don't smash term */
2368: /* -------------------------------------------------------------------------
2369: back to caller
2370: -------------------------------------------------------------------------- */
2371: isokay = 1; /* no problem, so signal okay to smash */
2372: end_of_job:
2373: if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */
2374: fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
2375: isokay,(expression==NULL?"<no input>":expression));
2376: return ( isokay ); /* back to caller with 1 if okay to smash */
2377: } /* --- end-of-function rastsmashcheck() --- */
2378:
2379:
2380: /* ==========================================================================
1.5 ! raeburn 2381: * Function: accent_subraster ( accent, width, height, direction, pixsz )
1.1 albertel 2382: * Purpose: Allocate a new subraster of width x height
2383: * (or maybe different dimensions, depending on accent),
2384: * and draw an accent (\hat or \vec or \etc) that fills it
2385: * --------------------------------------------------------------------------
2386: * Arguments: accent (I) int containing either HATACCENT or VECACCENT,
2387: * etc, indicating the type of accent desired
2388: * width (I) int containing desired width of accent (#cols)
2389: * height (I) int containing desired height of accent(#rows)
1.5 ! raeburn 2390: * direction (I) int containing desired direction of accent,
! 2391: * +1=right, -1=left, 0=left/right
1.1 albertel 2392: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
2393: * --------------------------------------------------------------------------
2394: * Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
2395: * or NULL for any error.
2396: * --------------------------------------------------------------------------
2397: * Notes: o Some accents have internally-determined dimensions,
2398: * and caller should check dimensions in returned subraster
2399: * ======================================================================= */
2400: /* --- entry point --- */
1.5 ! raeburn 2401: subraster *accent_subraster ( int accent, int width, int height,
! 2402: int direction, int pixsz )
1.1 albertel 2403: {
2404: /* -------------------------------------------------------------------------
2405: Allocations and Declarations
2406: -------------------------------------------------------------------------- */
2407: /* --- general info --- */
2408: raster *new_raster(), *rp=NULL; /*raster containing desired accent*/
2409: subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
2410: int delete_raster(), delete_subraster(); /*free allocated raster on err*/
2411: int line_raster(), /* draws lines */
1.2 albertel 2412: rule_raster(), /* draw solid boxes */
1.1 albertel 2413: thickness = 1; /* line thickness */
2414: /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
2415: /* --- other working info --- */
2416: int col0, col1, /* cols for line */
2417: row0, row1; /* rows for line */
2418: subraster *get_delim(), *accsp=NULL; /*find suitable cmex10 symbol/accent*/
2419: /* --- info for under/overbraces, tildes, etc --- */
2420: char brace[16]; /*"{" for over, "}" for under, etc*/
2421: raster *rastrot(), /* rotate { for overbrace, etc */
2422: *rastcpy(); /* may need copy of original */
2423: subraster *arrow_subraster(); /* rightarrow for vec */
2424: subraster *rastack(); /* stack accent atop extra space */
1.5 ! raeburn 2425: int iswidthneg = 0; /* set true if width<0 arg passed */
! 2426: int serifwidth=0; /* serif for surd */
! 2427: int isBig=0; /* true for ==>arrow, false for -->*/
! 2428: /* -------------------------------------------------------------------------
! 2429: initialization
! 2430: -------------------------------------------------------------------------- */
! 2431: if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
1.1 albertel 2432: /* -------------------------------------------------------------------------
2433: outer switch() traps accents that may change caller's height,width
2434: -------------------------------------------------------------------------- */
2435: switch ( accent )
2436: {
2437: default:
2438: /* -----------------------------------------------------------------------
2439: inner switch() first allocates fixed-size raster for accents that don't
2440: ------------------------------------------------------------------------ */
2441: if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
2442: != NULL ) /* and if we succeeded... */
2443: switch ( accent ) /* ...draw requested accent in it */
2444: {
2445: /* --- unrecognized request --- */
2446: default: delete_raster(rp); /* unrecognized accent requested */
2447: rp = NULL; break; /* so free raster and signal error */
2448: /* --- bar request --- */
2449: case UNDERBARACCENT:
2450: case BARACCENT:
1.2 albertel 2451: thickness = 1; /*height-1;*/ /* adjust thickness */
1.1 albertel 2452: if ( accent == BARACCENT ) /* bar is above expression */
1.2 albertel 2453: { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
2454: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
1.1 albertel 2455: else /* underbar is below expression */
1.2 albertel 2456: { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
2457: line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
1.1 albertel 2458: break;
2459: /* --- dot request --- */
2460: case DOTACCENT:
2461: thickness = height-1; /* adjust thickness */
1.2 albertel 2462: /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
2463: rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
1.1 albertel 2464: break;
2465: /* --- ddot request --- */
2466: case DDOTACCENT:
2467: thickness = height-1; /* adjust thickness */
1.2 albertel 2468: col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
2469: col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
2470: if ( col0+thickness >= col1 ) /* dots overlap */
2471: { col0 = max2(col0-1,0); /* try moving left dot more left */
2472: col1 = min2(col1+1,width-thickness); } /* and right dot right */
2473: if ( col0+thickness >= col1 ) /* dots _still_ overlap */
2474: thickness = max2(thickness-1,1); /* so try reducing thickness */
2475: /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
2476: /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
2477: rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
2478: rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
1.1 albertel 2479: break;
2480: /* --- hat request --- */
2481: case HATACCENT:
1.2 albertel 2482: thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
1.1 albertel 2483: line_raster(rp,height-1,0,0,width/2,thickness); /* / part of hat*/
2484: line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
2485: break;
2486: /* --- sqrt request --- */
2487: case SQRTACCENT:
1.5 ! raeburn 2488: serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
! 2489: col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
! 2490: /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
! 2491: col0 = (col1-serifwidth+1)/2; /* midpoint col of sqrt */
! 2492: row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
1.1 albertel 2493: row1 = height-1; /* bottom row of sqrt */
1.5 ! raeburn 2494: /*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
! 2495: line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
! 2496: line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
1.1 albertel 2497: line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
2498: line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
2499: break;
2500: } /* --- end-of-inner-switch(accent) --- */
2501: break; /* break from outer accent switch */
2502: /* --- underbrace, overbrace request --- */
2503: case UNDERBRACE:
2504: case OVERBRACE:
2505: if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
2506: if ( accent == OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
2507: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2508: != NULL ) /* found desired brace */
2509: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
2510: delete_subraster(accsp); } /* and free subraster "envelope" */
2511: break;
2512: /* --- hat request --- */
2513: case HATACCENT:
2514: if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
2515: if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2516: != NULL ) /* found desired brace */
2517: { rp = rastrot(accsp->image); /* rotate 90 degrees clockwise */
2518: delete_subraster(accsp); } /* and free subraster "envelope" */
2519: break;
2520: /* --- vec request --- */
2521: case VECACCENT:
2522: height = 2*(height/2) + 1; /* force height odd */
1.5 ! raeburn 2523: if ( absval(direction) >= 9 ) { /* want ==> arrow rather than --> */
! 2524: isBig = 1; /* signal "Big" arrow */
! 2525: direction -= 10; } /* reset direction = +1, -1, or 0 */
! 2526: if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
1.1 albertel 2527: != NULL ) /* succeeded */
2528: { rp = accsp->image; /* "extract" raster with bitmap */
2529: free((void *)accsp); } /* and free subraster "envelope" */
2530: break;
2531: /* --- tilde request --- */
2532: case TILDEACCENT:
2533: accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
2534: get_delim("~",-width,CMEX10)); /*width search for tilde*/
2535: if ( accsp != NULL ) /* found desired tilde */
2536: if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
2537: != NULL ) /* have tilde with space below it */
2538: { rp = sp->image; /* "extract" raster with bitmap */
1.2 albertel 2539: free((void *)sp); /* and free subraster "envelope" */
2540: leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
1.1 albertel 2541: break;
2542: } /* --- end-of-outer-switch(accent) --- */
2543: /* -------------------------------------------------------------------------
2544: if we constructed accent raster okay, embed it in a subraster and return it
2545: -------------------------------------------------------------------------- */
2546: /* --- if all okay, allocate subraster to contain constructed raster --- */
1.3 albertel 2547: if ( rp != NULL ) { /* accent raster constructed okay */
1.1 albertel 2548: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
2549: == NULL ) /* and if we fail to allocate */
2550: delete_raster(rp); /* free now-unneeded raster */
2551: else /* subraster allocated okay */
2552: { /* --- init subraster parameters, embedding raster in it --- */
2553: sp->type = IMAGERASTER; /* constructed image */
2554: sp->image = rp; /* raster we just constructed */
2555: sp->size = (-1); /* can't set font size here */
2556: sp->baseline = 0; } /* can't set baseline here */
1.3 albertel 2557: } /* --- end-of-if(rp!=NULL) --- */
1.1 albertel 2558: /* --- return subraster containing desired accent to caller --- */
2559: return ( sp ); /* return accent or NULL to caller */
2560: } /* --- end-of-function accent_subraster() --- */
2561:
2562:
2563: /* ==========================================================================
2564: * Function: arrow_subraster ( width, height, pixsz, drctn, isBig )
2565: * Purpose: Allocate a raster/subraster and draw left/right arrow in it
2566: * --------------------------------------------------------------------------
2567: * Arguments: width (I) int containing number of cols for arrow
2568: * height (I) int containing number of rows for arrow
2569: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
2570: * drctn (I) int containing +1 for right arrow,
2571: * or -1 for left, 0 for leftright
2572: * isBig (I) int containing 1/true for \Long arrows,
2573: * or false for \long arrows, i.e.,
2574: * true for ===> or false for --->.
2575: * --------------------------------------------------------------------------
2576: * Returns: ( subraster * ) ptr to constructed left/right arrow
2577: * or NULL for any error.
2578: * --------------------------------------------------------------------------
2579: * Notes: o
2580: * ======================================================================= */
2581: /* --- entry point --- */
2582: subraster *arrow_subraster ( int width, int height, int pixsz,
2583: int drctn, int isBig )
2584: {
2585: /* -------------------------------------------------------------------------
2586: Allocations and Declarations
2587: -------------------------------------------------------------------------- */
2588: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2589: int rule_raster(); /* draw arrow line */
2590: int irow, midrow=height/2; /* index, midrow is arrowhead apex */
1.2 albertel 2591: int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
1.1 albertel 2592: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2593: int ipix, /* raster pixmap[] index */
2594: npix = width*height; /* #pixels malloced in pixmap[] */
2595: /* -------------------------------------------------------------------------
2596: allocate raster/subraster and draw arrow line
2597: -------------------------------------------------------------------------- */
2598: if ( height < 3 ) { height=3; midrow=1; } /* set minimum height */
2599: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2600: == NULL ) goto end_of_job; /* and quit if failed */
2601: if ( !isBig ) /* single line */
2602: rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
2603: else
2604: { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
2605: rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
2606: rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
2607: /* -------------------------------------------------------------------------
2608: construct arrowhead(s)
2609: -------------------------------------------------------------------------- */
2610: for ( irow=0; irow<height; irow++ ) /* for each row of arrow */
2611: {
2612: int delta = abs(irow-midrow); /*arrowhead offset for irow*/
2613: /* --- right arrowhead --- */
2614: if ( drctn >= 0 ) /* right arrowhead wanted */
2615: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
2616: { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
1.3 albertel 2617: if ( ipix >= 0 ) { /* bounds check */
1.1 albertel 2618: if ( pixsz == 1 ) /* have a bitmap */
2619: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2620: else /* should have a bytemap */
2621: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 albertel 2622: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2623: /* --- left arrowhead (same as right except for ipix calculation) --- */
2624: if ( drctn <= 0 ) /* left arrowhead wanted */
2625: for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
2626: { ipix = irow*width + delta + icol; /* leftmost bit+delta+icol */
1.3 albertel 2627: if ( ipix < npix ) { /* bounds check */
1.1 albertel 2628: if ( pixsz == 1 ) /* have a bitmap */
2629: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2630: else /* should have a bytemap */
2631: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 albertel 2632: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2633: } /* --- end-of-for(irow) --- */
2634: end_of_job:
2635: return ( arrowsp ); /*back to caller with arrow or NULL*/
2636: } /* --- end-of-function arrow_subraster() --- */
2637:
2638:
2639: /* ==========================================================================
2640: * Function: uparrow_subraster ( width, height, pixsz, drctn, isBig )
2641: * Purpose: Allocate a raster/subraster and draw up/down arrow in it
2642: * --------------------------------------------------------------------------
2643: * Arguments: width (I) int containing number of cols for arrow
2644: * height (I) int containing number of rows for arrow
2645: * pixsz (I) int containing 1 for bitmap, 8 for bytemap
2646: * drctn (I) int containing +1 for up arrow,
2647: * or -1 for down, or 0 for updown
2648: * isBig (I) int containing 1/true for \Long arrows,
2649: * or false for \long arrows, i.e.,
2650: * true for ===> or false for --->.
2651: * --------------------------------------------------------------------------
2652: * Returns: ( subraster * ) ptr to constructed up/down arrow
2653: * or NULL for any error.
2654: * --------------------------------------------------------------------------
2655: * Notes: o
2656: * ======================================================================= */
2657: /* --- entry point --- */
2658: subraster *uparrow_subraster ( int width, int height, int pixsz,
2659: int drctn, int isBig )
2660: {
2661: /* -------------------------------------------------------------------------
2662: Allocations and Declarations
2663: -------------------------------------------------------------------------- */
2664: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2665: int rule_raster(); /* draw arrow line */
2666: int icol, midcol=width/2; /* index, midcol is arrowhead apex */
1.2 albertel 2667: int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
1.1 albertel 2668: int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2669: int ipix, /* raster pixmap[] index */
2670: npix = width*height; /* #pixels malloced in pixmap[] */
2671: /* -------------------------------------------------------------------------
2672: allocate raster/subraster and draw arrow line
2673: -------------------------------------------------------------------------- */
2674: if ( width < 3 ) { width=3; midcol=1; } /* set minimum width */
2675: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2676: == NULL ) goto end_of_job; /* and quit if failed */
2677: if ( !isBig ) /* single line */
2678: rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
2679: else
2680: { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
2681: rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
2682: rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
2683: /* -------------------------------------------------------------------------
2684: construct arrowhead(s)
2685: -------------------------------------------------------------------------- */
2686: for ( icol=0; icol<width; icol++ ) /* for each col of arrow */
2687: {
2688: int delta = abs(icol-midcol); /*arrowhead offset for icol*/
2689: /* --- up arrowhead --- */
2690: if ( drctn >= 0 ) /* up arrowhead wanted */
2691: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
2692: { ipix = (irow+delta)*width + icol; /* leftmost+icol */
1.3 albertel 2693: if ( ipix < npix ) { /* bounds check */
1.1 albertel 2694: if ( pixsz == 1 ) /* have a bitmap */
2695: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2696: else /* should have a bytemap */
2697: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 albertel 2698: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1 albertel 2699: /* --- down arrowhead (same as up except for ipix calculation) --- */
2700: if ( drctn <= 0 ) /* down arrowhead wanted */
2701: for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
2702: { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
1.3 albertel 2703: if ( ipix > 0 ) { /* bounds check */
1.1 albertel 2704: if ( pixsz == 1 ) /* have a bitmap */
2705: setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2706: else /* should have a bytemap */
2707: if ( pixsz == 8 ) /* check pixsz for bytemap */
1.3 albertel 2708: ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.2 albertel 2709: } /* --- end-of-for(icol) --- */
1.1 albertel 2710: end_of_job:
2711: return ( arrowsp ); /*back to caller with arrow or NULL*/
2712: } /* --- end-of-function uparrow_subraster() --- */
2713:
2714:
2715: /* ==========================================================================
2716: * Function: rule_raster ( rp, top, left, width, height, type )
2717: * Purpose: Draw a solid or dashed line (or box) in existing raster rp,
2718: * starting at top,left with dimensions width,height.
2719: * --------------------------------------------------------------------------
2720: * Arguments: rp (I) raster * to raster in which rule
2721: * will be drawn
2722: * top (I) int containing row at which top-left corner
2723: * of rule starts (0 is topmost)
2724: * left (I) int containing col at which top-left corner
2725: * of rule starts (0 is leftmost)
2726: * width (I) int containing number of cols for rule
2727: * height (I) int containing number of rows for rule
2728: * type (I) int containing 0 for solid rule,
2729: * 1 for horizontal dashes, 2 for vertical
1.3 albertel 2730: * 3 for solid rule with corners removed (bevel)
2731: * 4 for strut (nothing drawn)
1.1 albertel 2732: * --------------------------------------------------------------------------
2733: * Returns: ( int ) 1 if rule drawn okay,
2734: * or 0 for any error.
2735: * --------------------------------------------------------------------------
2736: * Notes: o Rule line is implicitly "horizontal" or "vertical" depending
2737: * on relative width,height dimensions. It's a box if they're
2738: * more or less comparable.
2739: * ======================================================================= */
2740: /* --- entry point --- */
2741: int rule_raster ( raster *rp, int top, int left,
2742: int width, int height, int type )
2743: {
2744: /* -------------------------------------------------------------------------
2745: Allocations and Declarations
2746: -------------------------------------------------------------------------- */
1.2 albertel 2747: int irow=0, icol=0; /* indexes over rp raster */
2748: int ipix = 0, /* raster pixmap[] index */
1.1 albertel 2749: npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
2750: int isfatal = 0; /* true to abend on out-of-bounds error */
1.3 albertel 2751: int hdash=1, vdash=2, /* type for horizontal, vertical dashes */
2752: bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */
1.1 albertel 2753: int dashlen=3, spacelen=2, /* #pixels for dash followed by space */
2754: isdraw=1; /* true when drawing dash (init for solid) */
2755: /* -------------------------------------------------------------------------
2756: Check args
2757: -------------------------------------------------------------------------- */
1.3 albertel 2758: if ( rp == (raster *)NULL ) { /* no raster arg supplied */
1.1 albertel 2759: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
2760: rp = workingbox->image; /* use workingbox if possible */
1.3 albertel 2761: else return ( 0 ); } /* otherwise signal error to caller */
2762: if ( type == bevel ) /* remove corners of solid box */
1.2 albertel 2763: if ( width<3 || height<3 ) type=0; /* too small to remove corners */
1.1 albertel 2764: /* -------------------------------------------------------------------------
2765: Fill line/box
2766: -------------------------------------------------------------------------- */
1.3 albertel 2767: if ( width > 0 ) /* zero width implies strut*/
2768: for ( irow=top; irow<top+height; irow++ ) /* for each scan line */
1.1 albertel 2769: {
1.3 albertel 2770: if ( type == strut ) isdraw = 0; /* draw nothing for strut */
1.1 albertel 2771: if ( type == vdash ) /*set isdraw for vert dash*/
2772: isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
2773: ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/
2774: for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */
2775: {
1.3 albertel 2776: if ( type == bevel ) { /* remove corners of box */
1.2 albertel 2777: if ( (irow==top && icol==left) /* top-left corner */
2778: || (irow==top && icol>=left+width-1) /* top-right corner */
2779: || (irow>=top+height-1 && icol==left) /* bottom-left corner */
2780: || (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
1.3 albertel 2781: isdraw = 0; else isdraw = 1; } /*set isdraw to skip corner*/
1.1 albertel 2782: if ( type == hdash ) /*set isdraw for horiz dash*/
2783: isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
2784: if ( ++ipix >= npix ) /* bounds check failed */
2785: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2786: else break; /*or just go on to next row*/
2787: else /*ibit is within rp bounds*/
1.3 albertel 2788: if ( isdraw ) { /*and we're drawing this bit*/
1.1 albertel 2789: if ( rp->pixsz == 1 ) /* have a bitmap */
2790: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2791: else /* should have a bytemap */
2792: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
1.3 albertel 2793: ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
1.1 albertel 2794: } /* --- end-of-for(icol) --- */
2795: } /* --- end-of-for(irow) --- */
2796: end_of_job:
2797: return ( isfatal? (ipix<npix? 1:0) : 1 );
2798: } /* --- end-of-function rule_raster() --- */
2799:
2800:
2801: /* ==========================================================================
2802: * Function: line_raster ( rp, row0, col0, row1, col1, thickness )
2803: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2804: * in existing raster rp.
2805: * --------------------------------------------------------------------------
2806: * Arguments: rp (I) raster * to raster in which a line
2807: * will be drawn
2808: * row0 (I) int containing row at which
2809: * line will start (0 is topmost)
2810: * col0 (I) int containing col at which
2811: * line will start (0 is leftmost)
2812: * row1 (I) int containing row at which
2813: * line will end (rp->height-1 is bottom-most)
2814: * col1 (I) int containing col at which
2815: * line will end (rp->width-1 is rightmost)
2816: * thickness (I) int containing number of pixels/bits
2817: * thick the line will be
2818: * --------------------------------------------------------------------------
2819: * Returns: ( int ) 1 if line drawn okay,
2820: * or 0 for any error.
2821: * --------------------------------------------------------------------------
2822: * Notes: o if row0==row1, a horizontal line is drawn
2823: * between col0 and col1, with row0(==row1) the top row
2824: * and row0+(thickness-1) the bottom row
2825: * o if col0==col1, a vertical bar is drawn
2826: * between row0 and row1, with col0(==col1) the left col
2827: * and col0+(thickness-1) the right col
2828: * o if both the above, you get a square thickness x thickness
2829: * whose top-left corner is row0,col0.
2830: * ======================================================================= */
2831: /* --- entry point --- */
2832: int line_raster ( raster *rp, int row0, int col0,
2833: int row1, int col1, int thickness )
2834: {
2835: /* -------------------------------------------------------------------------
2836: Allocations and Declarations
2837: -------------------------------------------------------------------------- */
1.2 albertel 2838: int irow=0, icol=0, /* indexes over rp raster */
1.1 albertel 2839: locol=col0, hicol=col1, /* col limits at irow */
2840: lorow=row0, hirow=row1; /* row limits at icol */
1.2 albertel 2841: int width=rp->width, height=rp->height; /* dimensions of input raster */
2842: int ipix = 0, /* raster pixmap[] index */
2843: npix = width*height; /* #pixels malloced in rp->pixmap[] */
1.1 albertel 2844: int isfatal = 0; /* true to abend on out-of-bounds error */
2845: int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
2846: double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
2847: dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
2848: a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
1.2 albertel 2849: xcol=0, xrow=0; /* calculated col at irow, or row at icol */
1.1 albertel 2850: double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
2851: xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
2852: ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
2853: xheight = 1.0;
2854: int line_recurse(), isrecurse=1; /* true to draw line recursively */
2855: /* -------------------------------------------------------------------------
2856: Check args
2857: -------------------------------------------------------------------------- */
1.3 albertel 2858: if ( rp == (raster *)NULL ) { /* no raster arg supplied */
1.1 albertel 2859: if ( workingbox != (subraster *)NULL ) /* see if we have a workingbox */
2860: rp = workingbox->image; /* use workingbox if possible */
1.3 albertel 2861: else return ( 0 ); } /* otherwise signal error to caller */
1.1 albertel 2862: /* -------------------------------------------------------------------------
2863: Initialization
2864: -------------------------------------------------------------------------- */
1.3 albertel 2865: if ( msgfp!=NULL && msglevel>=29 ) { /* debugging */
1.1 albertel 2866: fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
2867: "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
1.3 albertel 2868: row0,col0, row1,col1, thickness, dy,dx, a, xwidth); fflush(msgfp); }
1.1 albertel 2869: /* --- check for recursive line drawing --- */
1.2 albertel 2870: if ( isrecurse ) { /* drawing lines recursively */
2871: for ( irow=0; irow<thickness; irow++ ) /* each line 1 pixel thick */
2872: { double xrow0=(double)row0, xcol0=(double)col0,
2873: xrow1=(double)row1, xcol1=(double)col1;
2874: if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
2875: else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
2876: if( xrow0>(-0.001) && xcol0>(-0.001) /*check line inside raster*/
2877: && xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
2878: line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
2879: return ( 1 ); }
1.1 albertel 2880: /* --- set params for horizontal line or vertical bar --- */
2881: if ( isline ) /*interpret row as top row*/
2882: row1 = row0 + (thickness-1); /* set bottom row for line */
2883: if ( 0&&isbar ) /*interpret col as left col*/
2884: hicol = col0 + (thickness-1); /* set right col for bar */
2885: /* -------------------------------------------------------------------------
2886: draw line one row at a time
2887: -------------------------------------------------------------------------- */
2888: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
2889: {
2890: if ( !isbar && !isline ) /* neither vert nor horiz */
2891: { xcol = col0 + ((double)(irow-row0))/a; /* "middle" col in irow */
2892: locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
2893: hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
2894: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2895: fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
2896: irow,xcol,locol,hicol);
2897: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2898: for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
2899: if ( ++ipix >= npix ) /* bounds check failed */
2900: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2901: else break; /*or just go on to next row*/
2902: else /* turn on pixel in line */
2903: if ( rp->pixsz == 1 ) /* have a pixel bitmap */
2904: setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
2905: else /* should have a bytemap */
2906: if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
2907: ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
2908: } /* --- end-of-for(irow) --- */
2909: /* -------------------------------------------------------------------------
2910: now _redraw_ line one col at a time to avoid "gaps"
2911: -------------------------------------------------------------------------- */
2912: if ( 1 )
2913: for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
2914: {
2915: if ( !isbar && !isline ) /* neither vert nor horiz */
2916: { xrow = row0 + ((double)(icol-col0))*a; /* "middle" row in icol */
2917: lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
2918: hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
2919: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
2920: fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
2921: icol,xrow,lorow,hirow);
2922: ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
2923: for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
2924: if ( irow<0 || irow>=rp->height
2925: || icol<0 || icol>=rp->width ) /* bounds check */
2926: if ( isfatal ) goto end_of_job; /* abort if error is fatal */
2927: else continue; /*or just go on to next row*/
2928: else
2929: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
2930: } /* --- end-of-for(irow) --- */
2931: /* -------------------------------------------------------------------------
2932: Back to caller with 1=okay, 0=failed.
2933: -------------------------------------------------------------------------- */
2934: end_of_job:
2935: return ( isfatal? (ipix<npix? 1:0) : 1 );
2936: } /* --- end-of-function line_raster() --- */
2937:
2938:
2939: /* ==========================================================================
2940: * Function: line_recurse ( rp, row0, col0, row1, col1, thickness )
2941: * Purpose: Draw a line from row0,col0 to row1,col1 of thickness
2942: * in existing raster rp.
2943: * --------------------------------------------------------------------------
2944: * Arguments: rp (I) raster * to raster in which a line
2945: * will be drawn
2946: * row0 (I) double containing row at which
2947: * line will start (0 is topmost)
2948: * col0 (I) double containing col at which
2949: * line will start (0 is leftmost)
2950: * row1 (I) double containing row at which
2951: * line will end (rp->height-1 is bottom-most)
2952: * col1 (I) double containing col at which
2953: * line will end (rp->width-1 is rightmost)
2954: * thickness (I) int containing number of pixels/bits
2955: * thick the line will be
2956: * --------------------------------------------------------------------------
2957: * Returns: ( int ) 1 if line drawn okay,
2958: * or 0 for any error.
2959: * --------------------------------------------------------------------------
2960: * Notes: o Recurses, drawing left- and right-halves of line
2961: * until a horizontal or vertical segment is found
2962: * ======================================================================= */
2963: /* --- entry point --- */
2964: int line_recurse ( raster *rp, double row0, double col0,
2965: double row1, double col1, int thickness )
2966: {
2967: /* -------------------------------------------------------------------------
2968: Allocations and Declarations
2969: -------------------------------------------------------------------------- */
2970: double delrow = fabs(row1-row0), /* 0 if line horizontal */
2971: delcol = fabs(col1-col0), /* 0 if line vertical */
2972: tolerance = 0.5; /* draw line when it goes to point */
2973: double midrow = 0.5*(row0+row1), /* midpoint row */
2974: midcol = 0.5*(col0+col1); /* midpoint col */
2975: /* -------------------------------------------------------------------------
2976: recurse if either delta > tolerance
2977: -------------------------------------------------------------------------- */
2978: if ( delrow > tolerance /* row hasn't converged */
2979: || delcol > tolerance ) /* col hasn't converged */
2980: { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
2981: line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
2982: return ( 1 ); }
2983: /* -------------------------------------------------------------------------
2984: draw converged point
2985: -------------------------------------------------------------------------- */
2986: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
2987: return ( 1 );
2988: } /* --- end-of-function line_recurse() --- */
2989:
2990:
2991: /* ==========================================================================
2992: * Function: circle_raster ( rp, row0, col0, row1, col1,
2993: * thickness, quads )
2994: * Purpose: Draw quad(rant)s of an ellipse in box determined by
2995: * diagonally opposite corner points (row0,col0) and
2996: * (row1,col1), of thickness pixels in existing raster rp.
2997: * --------------------------------------------------------------------------
2998: * Arguments: rp (I) raster * to raster in which an ellipse
2999: * will be drawn
3000: * row0 (I) int containing 1st corner row bounding ellipse
3001: * (0 is topmost)
3002: * col0 (I) int containing 1st corner col bounding ellipse
3003: * (0 is leftmost)
3004: * row1 (I) int containing 2nd corner row bounding ellipse
3005: * (rp->height-1 is bottom-most)
3006: * col1 (I) int containing 2nd corner col bounding ellipse
3007: * (rp->width-1 is rightmost)
3008: * thickness (I) int containing number of pixels/bits
3009: * thick the ellipse arc line will be
3010: * quads (I) char * to null-terminated string containing
3011: * any subset/combination of "1234" specifying
3012: * which quadrant(s) of ellipse to draw.
3013: * NULL ptr draws all four quadrants;
3014: * otherwise 1=upper-right quadrant,
3015: * 2=uper-left, 3=lower-left, 4=lower-right,
3016: * i.e., counterclockwise from 1=positive quad.
3017: * --------------------------------------------------------------------------
3018: * Returns: ( int ) 1 if ellipse drawn okay,
3019: * or 0 for any error.
3020: * --------------------------------------------------------------------------
3021: * Notes: o row0==row1 or col0==col1 are errors
3022: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3023: * ======================================================================= */
3024: /* --- entry point --- */
3025: int circle_raster ( raster *rp, int row0, int col0,
3026: int row1, int col1, int thickness, char *quads )
3027: {
3028: /* -------------------------------------------------------------------------
3029: Allocations and Declarations
3030: -------------------------------------------------------------------------- */
3031: /* --- lower-left and upper-right bounding points (in our coords) --- */
3032: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
3033: locol = min2(col0,col1), /* lower bounding col (left of box)*/
3034: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
3035: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
3036: /* --- a and b ellipse params --- */
3037: int width = hicol-locol+1, /* width of bounding box */
3038: height= hirow-lorow+1, /* height of bounding box */
3039: islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
3040: double a = ((double)width)/2.0, /* x=a when y=0 */
3041: b = ((double)height)/2.0, /* y=b when x=0 */
3042: abmajor = (islandscape? a : b), /* max2(a,b) */
3043: abminor = (islandscape? b : a), /* min2(a,b) */
3044: abmajor2 = abmajor*abmajor, /* abmajor^2 */
3045: abminor2 = abminor*abminor; /* abminor^2 */
3046: /* --- other stuff --- */
3047: int imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
3048: iminor=0, nminor=min2(width,height); /* solved index on minor axis */
3049: int irow, icol, /* raster indexes at circumference */
3050: rsign=1, csign=1; /* row,col signs, both +1 in quad 1*/
3051: double midrow= ((double)(row0+row1))/2.0, /* center row */
3052: midcol= ((double)(col0+col1))/2.0; /* center col */
3053: double xy, xy2, /* major axis ellipse coord */
3054: yx2, yx; /* solved minor ellipse coord */
3055: int isokay = 1; /* true if no pixels out-of-bounds */
3056: char *qptr=NULL, *allquads="1234"; /* quadrants if input quads==NULL */
3057: int circle_recurse(), isrecurse=1; /* true to draw ellipse recursively*/
3058: /* -------------------------------------------------------------------------
3059: pixel-by-pixel along positive major axis, quit when it goes negative
3060: -------------------------------------------------------------------------- */
3061: if ( quads == NULL ) quads = allquads; /* draw all quads, or only user's */
3062: if ( msgfp!=NULL && msglevel>=39 ) /* debugging */
3063: fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
3064: width,height,quads);
3065: if ( nmajor < 1 ) isokay = 0; /* problem with input args */
3066: else
3067: {
3068: if ( isrecurse ) /* use recursive algorithm */
3069: {
3070: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3071: {
3072: double theta0=0.0, theta1=0.0; /* set thetas based on quadrant */
3073: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
3074: { default: /* unrecognized, assume quadrant 1 */
3075: case '1': theta0= 0.0; theta1= 90.0; break; /* first quadrant */
3076: case '2': theta0= 90.0; theta1=180.0; break; /* second quadrant */
3077: case '3': theta0=180.0; theta1=270.0; break; /* third quadrant */
3078: case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
3079: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
3080: } /* --- end-of-for(qptr) --- */
3081: return ( 1 );
3082: } /* --- end-of-if(isrecurse) --- */
3083: for ( imajor=(nmajor+1)/2; ; imajor-- )
3084: {
3085: /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
3086: xy = ((double)imajor); /* xy = abmajor ... 0 */
3087: if ( xy < 0.0 ) break; /* negative side symmetrical */
3088: yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
3089: yx = (yx2>0.0? sqrt(yx2) : 0.0); /* take sqrt if possible */
3090: iminor = iround(yx); /* nearest integer */
3091: /* --- set pixels for each requested quadrant --- */
3092: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3093: {
3094: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
3095: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
3096: { default: break; /* unrecognized, assume quadrant 1 */
3097: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
3098: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
3099: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
3100: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3101: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3102: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3103: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3104: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
3105: fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
3106: imajor,iminor,*qptr,irow,icol);
3107: if ( irow<0 || irow>=rp->height /* row outside raster */
3108: || icol<0 || icol>=rp->width ) /* col outside raster */
3109: { isokay = 0; /* signal out-of-bounds pixel */
3110: continue; } /* but still try remaining points */
3111: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
3112: } /* --- end-of-for(qptr) --- */
3113: } /* --- end-of-for(imajor) --- */
3114: /* ------------------------------------------------------------------------
3115: now do it _again_ along minor axis to avoid "gaps"
3116: ------------------------------------------------------------------------- */
3117: if ( 1 && iminor>0 )
3118: for ( iminor=(nminor+1)/2; ; iminor-- )
3119: {
3120: /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
3121: yx = ((double)iminor); /* yx = abminor ... 0 */
3122: if ( yx < 0.0 ) break; /* negative side symmetrical */
3123: xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
3124: xy = (xy2>0.0? sqrt(xy2) : 0.0); /* take sqrt if possible */
3125: imajor = iround(xy); /* nearest integer */
3126: /* --- set pixels for each requested quadrant --- */
3127: for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3128: {
3129: rsign = (-1); csign = 1; /* init row,col in user quadrant 1 */
3130: switch ( *qptr ) /* check for quadrant 1,2,3,4 */
3131: { default: break; /* unrecognized, assume quadrant 1 */
3132: case '4': rsign = 1; break; /* row,col both pos in quadrant 4 */
3133: case '3': rsign = 1; /* row pos, col neg in quadrant 3 */
3134: case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
3135: irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3136: irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3137: icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3138: icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3139: if ( msgfp!=NULL && msglevel>=49 ) /* debugging */
3140: fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
3141: iminor,imajor,*qptr,irow,icol);
3142: if ( irow<0 || irow>=rp->height /* row outside raster */
3143: || icol<0 || icol>=rp->width ) /* col outside raster */
3144: { isokay = 0; /* signal out-of-bounds pixel */
3145: continue; } /* but still try remaining points */
3146: setpixel(rp,irow,icol,255); /* set pixel at irow,icol */
3147: } /* --- end-of-for(qptr) --- */
3148: } /* --- end-of-for(iminor) --- */
3149: } /* --- end-of-if/else(nmajor<1) --- */
3150: return ( isokay );
3151: } /* --- end-of-function circle_raster() --- */
3152:
3153:
3154: /* ==========================================================================
3155: * Function: circle_recurse ( rp, row0, col0, row1, col1,
3156: * thickness, theta0, theta1 )
3157: * Purpose: Recursively draws arc theta0<=theta<=theta1 of the ellipse
3158: * in box determined by diagonally opposite corner points
3159: * (row0,col0) and (row1,col1), of thickness pixels in raster rp.
3160: * --------------------------------------------------------------------------
3161: * Arguments: rp (I) raster * to raster in which an ellipse
3162: * will be drawn
3163: * row0 (I) int containing 1st corner row bounding ellipse
3164: * (0 is topmost)
3165: * col0 (I) int containing 1st corner col bounding ellipse
3166: * (0 is leftmost)
3167: * row1 (I) int containing 2nd corner row bounding ellipse
3168: * (rp->height-1 is bottom-most)
3169: * col1 (I) int containing 2nd corner col bounding ellipse
3170: * (rp->width-1 is rightmost)
3171: * thickness (I) int containing number of pixels/bits
3172: * thick the ellipse arc line will be
3173: * theta0 (I) double containing first angle -360 -> +360
3174: * theta1 (I) double containing second angle -360 -> +360
3175: * 0=x-axis, positive moving counterclockwise
3176: * --------------------------------------------------------------------------
3177: * Returns: ( int ) 1 if ellipse drawn okay,
3178: * or 0 for any error.
3179: * --------------------------------------------------------------------------
3180: * Notes: o row0==row1 or col0==col1 are errors
3181: * o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3182: * Then, with x=r*cos(theta), y=r*sin(theta), ellipse
3183: * equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
3184: * ======================================================================= */
3185: /* --- entry point --- */
3186: int circle_recurse ( raster *rp, int row0, int col0,
3187: int row1, int col1, int thickness, double theta0, double theta1 )
3188: {
3189: /* -------------------------------------------------------------------------
3190: Allocations and Declarations
3191: -------------------------------------------------------------------------- */
3192: /* --- lower-left and upper-right bounding points (in our coords) --- */
3193: int lorow = min2(row0,row1), /* lower bounding row (top of box) */
3194: locol = min2(col0,col1), /* lower bounding col (left of box)*/
3195: hirow = max2(row0,row1), /* upper bounding row (bot of box) */
3196: hicol = max2(col0,col1); /* upper bounding col (right of box)*/
3197: /* --- a and b ellipse params --- */
3198: int width = hicol-locol+1, /* width of bounding box */
3199: height= hirow-lorow+1; /* height of bounding box */
3200: double a = ((double)width)/2.0, /* col x=a when row y=0 */
3201: b = ((double)height)/2.0, /* row y=b when col x=0 */
3202: ab=a*b, a2=a*a, b2=b*b; /* product and squares */
3203: /* --- arc parameters --- */
3204: double rads = 0.017453292, /* radians per degree = 1/57.29578 */
3205: lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
3206: hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
3207: locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
3208: hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
3209: rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
3210: rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
3211: xlo=rlo*locos, ylo=rlo*losin, /*col,row pixel coords for lotheta*/
3212: xhi=rhi*hicos, yhi=rhi*hisin, /*col,row pixel coords for hitheta*/
3213: xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
3214: tolerance = 0.5; /* convergence tolerance */
3215: /* -------------------------------------------------------------------------
3216: recurse if either delta > tolerance
3217: -------------------------------------------------------------------------- */
3218: if ( ydelta > tolerance /* row hasn't converged */
3219: || xdelta > tolerance ) /* col hasn't converged */
3220: { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
3221: circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta); /*lo*/
3222: circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
3223: /* -------------------------------------------------------------------------
3224: draw converged point
3225: -------------------------------------------------------------------------- */
3226: else
3227: { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi), /* relative to center*/
3228: centerrow = 0.5*((double)(lorow+hirow)), /* ellipse y-center */
3229: centercol = 0.5*((double)(locol+hicol)), /* ellipse x-center */
3230: midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
3231: setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
3232: return ( 1 );
3233: } /* --- end-of-function circle_recurse() --- */
3234:
3235:
3236: /* ==========================================================================
3237: * Function: bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
3238: * Purpose: Recursively draw bezier from r0,c0 to r1,c1
3239: * (with tangent point rt,ct) in existing raster rp.
3240: * --------------------------------------------------------------------------
3241: * Arguments: rp (I) raster * to raster in which a line
3242: * will be drawn
3243: * r0 (I) double containing row at which
3244: * bezier will start (0 is topmost)
3245: * c0 (I) double containing col at which
3246: * bezier will start (0 is leftmost)
3247: * r1 (I) double containing row at which
3248: * bezier will end (rp->height-1 is bottom-most)
3249: * c1 (I) double containing col at which
3250: * bezier will end (rp->width-1 is rightmost)
3251: * rt (I) double containing row for tangent point
3252: * ct (I) double containing col for tangent point
3253: * --------------------------------------------------------------------------
3254: * Returns: ( int ) 1 if line drawn okay,
3255: * or 0 for any error.
3256: * --------------------------------------------------------------------------
3257: * Notes: o Recurses, drawing left- and right-halves of bezier curve
3258: * until a point is found
3259: * ======================================================================= */
3260: /* --- entry point --- */
3261: int bezier_raster ( raster *rp, double r0, double c0,
3262: double r1, double c1, double rt, double ct )
3263: {
3264: /* -------------------------------------------------------------------------
3265: Allocations and Declarations
3266: -------------------------------------------------------------------------- */
3267: double delrow = fabs(r1-r0), /* 0 if same row */
3268: delcol = fabs(c1-c0), /* 0 if same col */
3269: tolerance = 0.5; /* draw curve when it goes to point*/
3270: double midrow = 0.5*(r0+r1), /* midpoint row */
3271: midcol = 0.5*(c0+c1); /* midpoint col */
3272: int irow=0, icol=0; /* point to be drawn */
3273: int status = 1; /* return status */
3274: /* -------------------------------------------------------------------------
3275: recurse if either delta > tolerance
3276: -------------------------------------------------------------------------- */
3277: if ( delrow > tolerance /* row hasn't converged */
3278: || delcol > tolerance ) /* col hasn't converged */
3279: { bezier_raster(rp, r0,c0, /* left half */
3280: 0.5*(rt+midrow), 0.5*(ct+midcol),
3281: 0.5*(r0+rt), 0.5*(c0+ct) );
3282: bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
3283: r1,c1,
3284: 0.5*(r1+rt), 0.5*(c1+ct) );
3285: return ( 1 ); }
3286: /* -------------------------------------------------------------------------
3287: draw converged point
3288: -------------------------------------------------------------------------- */
3289: /* --- get integer point --- */
3290: irow = iround(midrow); /* row pixel coord */
3291: icol = iround(midcol); /* col pixel coord */
3292: /* --- bounds check --- */
3293: if ( irow>=0 && irow<rp->height /* row in bounds */
3294: && icol>=0 && icol<rp->width ) /* col in bounds */
3295: setpixel(rp,irow,icol,255); /* so set pixel at irow,icol*/
3296: else status = 0; /* bad status if out-of-bounds */
3297: return ( status );
3298: } /* --- end-of-function bezier_raster() --- */
3299:
3300:
3301: /* ==========================================================================
3302: * Function: border_raster ( rp, ntop, nbot, isline, isfree )
3303: * Purpose: Allocate a new raster containing a copy of input rp,
3304: * along with ntop extra rows at top and nbot at bottom,
3305: * and whose width is either adjusted correspondingly,
3306: * or is automatically enlarged to a multiple of 8
3307: * with original bitmap centered
3308: * --------------------------------------------------------------------------
3309: * Arguments: rp (I) raster * to raster on which a border
3310: * is to be placed
3311: * ntop (I) int containing number extra rows at top.
3312: * if negative, abs(ntop) used, and same
3313: * number of extra cols added at left.
3314: * nbot (I) int containing number extra rows at bottom.
3315: * if negative, abs(nbot) used, and same
3316: * number of extra cols added at right.
3317: * isline (I) int containing 0 to leave border pixels clear
1.5 ! raeburn 3318: * or >0 to draw a line around border of
! 3319: * thickness isline pixels. See Notes below.
1.1 albertel 3320: * isfree (I) int containing true to free rp before return
3321: * --------------------------------------------------------------------------
3322: * Returns: ( raster * ) ptr to bordered raster,
3323: * or NULL for any error.
3324: * --------------------------------------------------------------------------
1.5 ! raeburn 3325: * Notes: o The isline arg also controls which sides border lines
! 3326: * are drawn for. To do this, isline is interpreted as
! 3327: * thickness + 100*sides so that, e.g., passing isline=601
! 3328: * is interpreted as sides=6 and thickness=1. And
! 3329: * sides is further interpreted as 1=left side, 2=top,
! 3330: * 4=right and 8=bottom. For example, sides=6 where 6=2+4
! 3331: * draws the top and right borders only. 15 draws all four
! 3332: * sides. And 0 (no sides value embedded in isline)
! 3333: * draws all four sides, too.
1.1 albertel 3334: * ======================================================================= */
3335: /* --- entry point --- */
3336: raster *border_raster ( raster *rp, int ntop, int nbot,
3337: int isline, int isfree )
3338: {
3339: /* -------------------------------------------------------------------------
3340: Allocations and Declarations
3341: -------------------------------------------------------------------------- */
3342: raster *new_raster(), *bp=(raster *)NULL; /*raster back to caller*/
3343: int rastput(); /* overlay rp in new bordered raster */
1.3 albertel 3344: int width = (rp==NULL?0:rp->width), /* width of raster */
3345: height = (rp==NULL?0:rp->height), /* height of raster */
3346: istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
1.1 albertel 3347: leftmargin = 0; /* adjust width to whole number of bytes */
1.5 ! raeburn 3348: int left=1, top=1, right=1, bot=1; /* frame sides to draw */
1.3 albertel 3349: int delete_raster(); /* free input rp if isfree is true */
1.1 albertel 3350: /* -------------------------------------------------------------------------
3351: Initialization
3352: -------------------------------------------------------------------------- */
3353: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
3354: if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
3355: { bp=rp; goto end_of_job; } /* return ascii string unchanged */
3356: /* --- check for negative args --- */
3357: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
3358: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
3359: /* --- adjust height for ntop and nbot margins --- */
3360: height += (ntop+nbot); /* adjust height for margins */
3361: /* --- adjust width for left and right margins --- */
3362: if ( istopneg || isbotneg ) /*caller wants nleft=ntop and/or nright=nbot*/
3363: { /* --- adjust width (and leftmargin) as requested by caller -- */
3364: if ( istopneg ) { width += ntop; leftmargin = ntop; }
3365: if ( isbotneg ) width += nbot; }
3366: else
3367: { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
3368: leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
3369: width += leftmargin; /* width now multiple of 8 */
3370: leftmargin /= 2; } /* center original raster */
1.5 ! raeburn 3371: /* --- check which sides to draw --- */
! 3372: if ( isline > 100 ) { /* sides arg embedded in isline */
! 3373: int iside=0, sides=isline/100; /* index, sides=1-15 from 101-1599 */
! 3374: isline -= 100*sides; /* and remove sides from isline */
! 3375: for ( iside=1; iside<=4; iside++ ) { /* check left, top, right, bot */
! 3376: int shift = sides/2; /* shift sides left one bit */
! 3377: if ( sides == 2*shift ) /* low-order bit is >>not<< set */
! 3378: switch ( iside ) { /* don't draw corresponding side */
! 3379: default: break; /* internal error */
! 3380: case 1: left = 0; break; /* 1 = left side */
! 3381: case 2: top = 0; break; /* 2 = top side */
! 3382: case 3: right= 0; break; /* 4 = tight side */
! 3383: case 4: bot = 0; break; } /* 8 = bottom side */
! 3384: sides = shift; /* ready for next side */
! 3385: } /* --- end-of-for(iside) --- */
! 3386: } /* --- end-of-if(isline>100) --- */
1.1 albertel 3387: /* -------------------------------------------------------------------------
3388: allocate bordered raster, and embed rp within it
3389: -------------------------------------------------------------------------- */
3390: /* --- allocate bordered raster --- */
3391: if ( (bp=new_raster(width,height,rp->pixsz)) /*allocate bordered raster*/
3392: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
3393: /* --- embed rp in it --- */
3394: rastput(bp,rp,ntop,leftmargin,1); /* rp embedded in bp */
3395: /* -------------------------------------------------------------------------
3396: draw border if requested
3397: -------------------------------------------------------------------------- */
3398: if ( isline )
3399: { int irow, icol, nthick=isline; /*height,width index, line thickness*/
3400: /* --- draw left- and right-borders --- */
3401: for ( irow=0; irow<height; irow++ ) /* for each row of bp */
3402: for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
1.5 ! raeburn 3403: { if(left){setpixel(bp,irow,icol,255);} /* left border */
! 3404: if(right){setpixel(bp,irow,width-1-icol,255);} } /* right border */
1.1 albertel 3405: /* --- draw top- and bottom-borders --- */
3406: for ( icol=0; icol<width; icol++ ) /* for each col of bp */
3407: for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
1.5 ! raeburn 3408: { if(top){setpixel(bp,irow,icol,255);} /* top border */
! 3409: if(bot){setpixel(bp,height-1-irow,icol,255);} } /* bottom border */
1.1 albertel 3410: } /* --- end-of-if(isline) --- */
3411: /* -------------------------------------------------------------------------
3412: free rp if no longer needed
3413: -------------------------------------------------------------------------- */
3414: if ( isfree ) /*caller no longer needs rp*/
3415: delete_raster(rp); /* so free it for him */
3416: /* -------------------------------------------------------------------------
3417: Back to caller with bordered raster (or null for any error)
3418: -------------------------------------------------------------------------- */
3419: end_of_job:
3420: return ( bp ); /* back with bordered or null ptr */
3421: } /* --- end-of-function border_raster() --- */
3422:
3423:
3424: /* ==========================================================================
1.3 albertel 3425: * Function: backspace_raster ( rp, nback, pback, minspace, isfree )
3426: * Purpose: Allocate a new raster containing a copy of input rp,
3427: * but with trailing nback columns removed.
3428: * If minspace>=0 then (at least) that many columns
3429: * of whitespace will be left in place, regardless of nback.
3430: * If minspace<0 then existing black pixels will be deleted
3431: * as required.
3432: * --------------------------------------------------------------------------
3433: * Arguments: rp (I) raster * to raster on which a border
3434: * is to be placed
3435: * nback (I) int containing number of columns to
3436: * backspace (>=0)
3437: * pback (O) ptr to int returning #pixels actually
3438: * backspaced (or NULL to not use)
3439: * minspace (I) int containing number of columns
3440: * of whitespace to be left in place
3441: * isfree (I) int containing true to free rp before return
3442: * --------------------------------------------------------------------------
3443: * Returns: ( raster * ) ptr to backspaced raster,
3444: * or NULL for any error.
3445: * --------------------------------------------------------------------------
3446: * Notes: o For \! negative space, for \hspace{-10}, etc.
3447: * ======================================================================= */
3448: /* --- entry point --- */
3449: raster *backspace_raster ( raster *rp, int nback, int *pback, int minspace,
3450: int isfree )
3451: {
3452: /* -------------------------------------------------------------------------
3453: Allocations and Declarations
3454: -------------------------------------------------------------------------- */
3455: raster *new_raster(), *bp=(raster *)NULL; /* raster returned to caller */
3456: int delete_raster(); /* free input rp if isfree is true */
3457: int width = (rp==NULL?0:rp->width), /* original width of raster */
3458: height = (rp==NULL?0:rp->height), /* height of raster */
3459: mback = nback, /* nback adjusted for minspace */
3460: newwidth = width, /* adjusted width after backspace */
3461: icol=0, irow=0; /* col,row index */
3462: if ( rp == NULL ) goto end_of_job; /* no input given */
3463: /* -------------------------------------------------------------------------
3464: locate rightmost column of rp containing ink, and determine backspaced width
3465: -------------------------------------------------------------------------- */
3466: /* --- locate rightmost column of rp containing ink --- */
3467: if ( minspace >= 0 ) /* only needed if given minspace */
3468: for ( icol=width-1; icol>=0; icol-- ) /* find first non-empty col in row */
3469: for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
3470: if ( getpixel(rp,irow,icol) != 0 ) { /* found a set pixel */
3471: int whitecols = (width-1) - icol; /* #cols containing white space */
3472: mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
3473: goto gotright; } /* no need to look further */
3474: /* --- determine width of new backspaced raster --- */
3475: gotright: /* found col with ink (or rp empty)*/
3476: if ( mback > width ) mback = width; /* can't backspace before beginning*/
3477: newwidth = max2(1,width-mback); /* #cols in backspaced raster */
3478: if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
3479: /* -------------------------------------------------------------------------
3480: allocate new raster and fill it with leftmost cols of rp
3481: -------------------------------------------------------------------------- */
3482: /* --- allocate backspaced raster --- */
3483: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
3484: == (raster *)NULL ) goto end_of_job; /* and quit if failed */
3485: /* --- fill new raster --- */
1.5 ! raeburn 3486: if ( 1 || width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/
1.3 albertel 3487: for ( icol=0; icol<newwidth; icol++ ) /* find first non-empty col in row */
3488: for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
3489: { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
3490: setpixel(bp,irow,icol,value); } /* saved in backspaced raster */
3491: /* -------------------------------------------------------------------------
3492: Back to caller with backspaced raster (or null for any error)
3493: -------------------------------------------------------------------------- */
3494: end_of_job:
3495: if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
3496: "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
3497: nback,minspace,mback,width,newwidth); fflush(msgfp); }
3498: if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
3499: return ( bp ); /* back with backspaced or null ptr*/
3500: } /* --- end-of-function backspace_raster() --- */
3501:
3502:
3503: /* ==========================================================================
1.1 albertel 3504: * Function: type_raster ( rp, fp )
3505: * Purpose: Emit an ascii dump representing rp, on fp.
3506: * --------------------------------------------------------------------------
3507: * Arguments: rp (I) ptr to raster struct for which an
3508: * ascii dump is to be constructed.
3509: * fp (I) File ptr to output device (defaults to
3510: * stdout if passed as NULL).
3511: * --------------------------------------------------------------------------
3512: * Returns: ( int ) 1 if completed successfully,
3513: * or 0 otherwise (for any error).
3514: * --------------------------------------------------------------------------
3515: * Notes:
3516: * ======================================================================= */
3517: /* --- entry point --- */
3518: int type_raster ( raster *rp, FILE *fp )
3519: {
3520: /* -------------------------------------------------------------------------
3521: Allocations and Declarations
3522: -------------------------------------------------------------------------- */
3523: static int display_width = 72; /* max columns for display */
3524: static char display_chars[16] = /* display chars for bytemap */
3525: { ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
3526: char scanline[133]; /* ascii image for one scan line */
3527: int scan_width; /* #chars in scan (<=display_width)*/
3528: int irow, locol,hicol=(-1); /* height index, width indexes */
1.2 albertel 3529: raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
3530: int delete_raster(); /*free bitmap converted for display*/
1.1 albertel 3531: /* --------------------------------------------------------------------------
3532: initialization
3533: -------------------------------------------------------------------------- */
3534: /* --- redirect null fp --- */
3535: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
1.3 albertel 3536: if ( msglevel >= 999 ) { fprintf(fp, /* debugging diagnostics */
3537: "type_raster> width=%d height=%d ...\n",
3538: rp->width,rp->height); fflush(fp); }
1.1 albertel 3539: /* --- check for ascii string --- */
3540: if ( isstring /* pixmap has string, not raster */
1.2 albertel 3541: || (0 && rp->height==1) ) /* infer input rp is a string */
1.1 albertel 3542: {
3543: char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
3544: int width = strlen(string); /* #chars in ascii string */
3545: while ( width > display_width-2 ) /* too big for one line */
3546: { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
3547: string += (display_width-2); /* bump string past displayed chars*/
3548: width -= (display_width-2); } /* decrement remaining width */
3549: fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
3550: return ( 1 );
3551: } /* --- end-of-if(isstring) --- */
3552: /* --------------------------------------------------------------------------
3553: display ascii dump of bitmap image (in segments if display_width < rp->width)
3554: -------------------------------------------------------------------------- */
1.2 albertel 3555: if ( rp->format == 2 /* input is .gf-formatted */
3556: || rp->format == 3 )
3557: bitmaprp = gftobitmap(rp); /* so convert it for display */
3558: if ( bitmaprp != NULL ) /* if we have image for display */
3559: while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
1.1 albertel 3560: {
3561: /* --- set hicol for this pass (locol set above) --- */
3562: hicol += display_width; /* show as much as display allows */
3563: if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
3564: scan_width = hicol-locol+1; /* #chars in this scan */
3565: if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
3566: /* ------------------------------------------------------------------------
3567: display all scan lines for this local...hicol segment range
3568: ------------------------------------------------------------------------ */
3569: for ( irow=0; irow<rp->height; irow++ ) /* all scan lines for col range */
3570: {
3571: /* --- allocations and declarations --- */
3572: int ipix, /* pixmap[] index for this scan */
3573: lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
3574: /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
3575: for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
1.2 albertel 3576: if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
3577: scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
1.1 albertel 3578: else /* should have a bytemap */
1.2 albertel 3579: if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
3580: { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
1.1 albertel 3581: ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
3582: scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
3583: /* --- display completed scan line --- */
3584: fprintf(fp,"%.*s\n",scan_width,scanline);
3585: } /* --- end-of-for(irow) --- */
3586: } /* --- end-of-while(hicol<rp->width) --- */
3587: /* -------------------------------------------------------------------------
3588: Back to caller with 1=okay, 0=failed.
3589: -------------------------------------------------------------------------- */
1.2 albertel 3590: if ( rp->format == 2 /* input was .gf-format */
3591: || rp->format == 3 )
3592: if ( bitmaprp != NULL ) /* and we converted it for display */
3593: delete_raster(bitmaprp); /* no longer needed, so free it */
1.1 albertel 3594: return ( 1 );
3595: } /* --- end-of-function type_raster() --- */
3596:
3597:
3598: /* ==========================================================================
3599: * Function: type_bytemap ( bp, grayscale, width, height, fp )
3600: * Purpose: Emit an ascii dump representing bp, on fp.
3601: * --------------------------------------------------------------------------
3602: * Arguments: bp (I) intbyte * to bytemap for which an
3603: * ascii dump is to be constructed.
3604: * grayscale (I) int containing #gray shades, 256 for 8-bit
3605: * width (I) int containing #cols in bytemap
3606: * height (I) int containing #rows in bytemap
3607: * fp (I) File ptr to output device (defaults to
3608: * stdout if passed as NULL).
3609: * --------------------------------------------------------------------------
3610: * Returns: ( int ) 1 if completed successfully,
3611: * or 0 otherwise (for any error).
3612: * --------------------------------------------------------------------------
3613: * Notes:
3614: * ======================================================================= */
3615: /* --- entry point --- */
3616: int type_bytemap ( intbyte *bp, int grayscale,
3617: int width, int height, FILE *fp )
3618: {
3619: /* -------------------------------------------------------------------------
3620: Allocations and Declarations
3621: -------------------------------------------------------------------------- */
3622: static int display_width = 72; /* max columns for display */
3623: int byte_width = 3, /* cols to display byte (ff+space) */
3624: maxbyte = 0; /* if maxbyte<16, set byte_width=2 */
3625: int white_byte = 0, /* show dots for white_byte's */
3626: black_byte = grayscale-1; /* show stars for black_byte's */
3627: char scanline[133]; /* ascii image for one scan line */
3628: int scan_width, /* #chars in scan (<=display_width)*/
3629: scan_cols; /* #cols in scan (hicol-locol+1) */
3630: int ibyte, /* bp[] index */
3631: irow, locol,hicol=(-1); /* height index, width indexes */
3632: /* --------------------------------------------------------------------------
3633: initialization
3634: -------------------------------------------------------------------------- */
3635: /* --- redirect null fp --- */
3636: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3637: /* --- check for ascii string --- */
3638: if ( isstring ) /* bp has ascii string, not raster */
3639: { width = strlen((char *)bp); /* #chars in ascii string */
3640: height = 1; } /* default */
3641: /* --- see if we can get away with byte_width=1 --- */
3642: for ( ibyte=0; ibyte<width*height; ibyte++ ) /* check all bytes */
3643: { int byteval = (int)bp[ibyte]; /* current byte value */
3644: if ( byteval < black_byte ) /* if it's less than black_byte */
3645: maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
3646: if ( maxbyte < 16 ) /* bytevals will fit in one column */
3647: byte_width = 1; /* so reset display byte_width */
3648: /* --------------------------------------------------------------------------
3649: display ascii dump of bitmap image (in segments if display_width < rp->width)
3650: -------------------------------------------------------------------------- */
3651: while ( (locol=hicol+1) < width ) /*start where prev segment left off*/
3652: {
3653: /* --- set hicol for this pass (locol set above) --- */
3654: hicol += display_width/byte_width; /* show as much as display allows */
3655: if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
3656: scan_cols = hicol-locol+1; /* #cols in this scan */
3657: scan_width = byte_width*scan_cols; /* #chars in this scan */
3658: if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
3659: /* ------------------------------------------------------------------------
3660: display all scan lines for this local...hicol segment range
3661: ------------------------------------------------------------------------ */
3662: for ( irow=0; irow<height; irow++ ) /* all scan lines for col range */
3663: {
3664: /* --- allocations and declarations --- */
3665: int lobyte = irow*width + locol; /* first bp[] byte in this scan */
3666: char scanbyte[32]; /* sprintf() buffer for byte */
3667: /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
3668: memset(scanline,' ',scan_width); /* blank out scanline */
3669: for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
3670: { int byteval = (int)bp[lobyte+ibyte]; /* value of current byte */
3671: memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
3672: if ( byteval == black_byte ) /* but if we have a black byte */
3673: memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
3674: if ( byte_width > 1 ) /* don't blank out single char */
3675: scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
3676: if ( byteval != white_byte /* format bytes that are non-white */
3677: && byteval != black_byte ) /* and that are non-black */
3678: sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
3679: memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
3680: /* --- display completed scan line --- */
3681: fprintf(fp,"%.*s\n",scan_width,scanline);
3682: } /* --- end-of-for(irow) --- */
3683: } /* --- end-of-while(hicol<width) --- */
3684: /* -------------------------------------------------------------------------
3685: Back to caller with 1=okay, 0=failed.
3686: -------------------------------------------------------------------------- */
3687: return ( 1 );
3688: } /* --- end-of-function type_bytemap() --- */
3689:
3690:
3691: /* ==========================================================================
3692: * Function: xbitmap_raster ( rp, fp )
3693: * Purpose: Emit a mime xbitmap representing rp, on fp.
3694: * --------------------------------------------------------------------------
3695: * Arguments: rp (I) ptr to raster struct for which a mime
3696: * xbitmap is to be constructed.
3697: * fp (I) File ptr to output device (defaults to
3698: * stdout if passed as NULL).
3699: * --------------------------------------------------------------------------
3700: * Returns: ( int ) 1 if completed successfully,
3701: * or 0 otherwise (for any error).
3702: * --------------------------------------------------------------------------
3703: * Notes:
3704: * ======================================================================= */
3705: /* --- entry point --- */
3706: int xbitmap_raster ( raster *rp, FILE *fp )
3707: {
3708: /* -------------------------------------------------------------------------
3709: Allocations and Declarations
3710: -------------------------------------------------------------------------- */
3711: char *title = "image"; /* dummy title */
3712: int hex_bitmap(); /* dump bitmap as hex bytes */
3713: /* --------------------------------------------------------------------------
3714: emit text to display mime xbitmap representation of rp->bitmap image
3715: -------------------------------------------------------------------------- */
3716: /* --- first redirect null fp --- */
3717: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3718: /* --- check for ascii string --- */
3719: if ( isstring ) /* pixmap has string, not raster */
3720: return ( 0 ); /* can't handle ascii string */
3721: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
3722: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
3723: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
3724: title,rp->width, title,rp->height );
3725: fprintf( fp, "static char %s_bits[] = {\n", title );
3726: hex_bitmap(rp,fp,0,0); /* emit hex dump of bitmap bytes */
3727: fprintf (fp,"};\n"); /* ending with "};" for C array */
3728: /* -------------------------------------------------------------------------
3729: Back to caller with 1=okay, 0=failed.
3730: -------------------------------------------------------------------------- */
3731: return ( 1 );
3732: } /* --- end-of-function xbitmap_raster() --- */
3733:
3734:
3735: /* ==========================================================================
1.2 albertel 3736: * Function: type_pbmpgm ( rp, ptype, file )
3737: * Purpose: Write pbm or pgm image of rp to file
3738: * --------------------------------------------------------------------------
3739: * Arguments: rp (I) ptr to raster struct for which
3740: * a pbm/pgm file is to be written.
3741: * ptype (I) int containing 1 for pbm, 2 for pgm, or
3742: * 0 to determine ptype from values in rp
3743: * file (I) ptr to null-terminated char string
3744: * containing name of fuke to be written
3745: * (see notes below).
3746: * --------------------------------------------------------------------------
3747: * Returns: ( int ) total #bytes written,
3748: * or 0 for any error.
3749: * --------------------------------------------------------------------------
3750: * Notes: o (a) If file==NULL, output is written to stdout;
3751: * (b) if *file=='\000' then file is taken as the
3752: * address of an output buffer to which output
3753: * is written (and is followed by a terminating
3754: * '\0' which is not counted in #bytes returned);
3755: * (c) otherwise file is the filename (opened and
3756: * closed internally) to which output is written,
3757: * except that any final .ext extension is replaced
3758: * by .pbm or .pgm depending on ptype.
3759: * ======================================================================= */
3760: /* --- entry point --- */
3761: int type_pbmpgm ( raster *rp, int ptype, char *file )
3762: {
3763: /* -------------------------------------------------------------------------
3764: Allocations and Declarations
3765: -------------------------------------------------------------------------- */
3766: int isokay=0, nbytes=0; /* completion flag, total #bytes written */
3767: int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
3768: int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
3769: ngray = 0; /* #gray scale values */
3770: FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
3771: char outline[1024], outfield[256], /* output line, field */
3772: cr[16] = "\n\000"; /* cr at end-of-line */
3773: int maxlinelen = 70; /* maximum allowed line length */
3774: int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
3775: static char
3776: *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
3777: *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
3778: *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
3779: /* -------------------------------------------------------------------------
3780: check input, determine grayscale, and set up output file if necessary
3781: -------------------------------------------------------------------------- */
3782: /* --- check input args --- */
3783: if ( rp == NULL ) goto end_of_job; /* no input raster provided */
3784: if ( ptype != 0 ) /* we'll determine ptype below */
3785: if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
3786: /* --- determine largest (and smallest) value in pixmap --- */
3787: for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
3788: for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
3789: { int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
3790: pixmin = min2(pixmin,pixval); /* new minimum */
3791: pixmax = max2(pixmax,pixval); } /* new maximum */
3792: ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
3793: if ( ptype == 0 ) /* caller wants us to set ptype */
3794: ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
3795: /* --- open output file if necessary --- */
3796: if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
3797: else if ( *file != '\000' ) { /* explicit filename provided, so...*/
3798: char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
3799: strncpy(fname,file,255); /* local copy of file name */
3800: fname[255] = '\000'; /* make sure it's null terminated */
3801: if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
3802: strcat(fname,suffix[ptype]); /* so add extension */
3803: else /* we already have an extension */
3804: strcpy(pdot,suffix[ptype]); /* so replace original extension */
3805: if ( (fp = fopen(fname,mode[ptype])) /* open output file */
3806: == (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
3807: } /* --- ens-of-if(*file!='\0') --- */
3808: /* -------------------------------------------------------------------------
3809: format and write header
3810: -------------------------------------------------------------------------- */
3811: /* --- format header info --- */
3812: *outline = '\000'; /* initialize line buffer */
3813: strcat(outline,magic[ptype]); /* begin file with "magic number" */
3814: strcat(outline,cr); /* followed by cr to end line */
3815: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
3816: strcat(outline,outfield); /* add width and height to header */
3817: strcat(outline,cr); /* followed by cr to end line */
3818: if ( ptype == 2 ) /* need max grayscale value */
3819: { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
3820: strcat(outline,outfield); /* add max value to header */
3821: strcat(outline,cr); } /* followed by cr to end line */
3822: /* --- write header to file or memory buffer --- */
3823: if ( fp == NULL ) /* if we have no open file... */
3824: strcat(file,outline); /* add header to caller's buffer */
3825: else /* or if we have an open file... */
3826: if ( fputs(outline,fp) /* try writing header to open file */
3827: == EOF ) goto end_of_job; /* return with error if failed */
3828: nbytes += strlen(outline); /* bump output byte count */
3829: /* -------------------------------------------------------------------------
3830: format and write pixels
3831: -------------------------------------------------------------------------- */
3832: *outline = '\000'; /* initialize line buffer */
3833: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
3834: for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
3835: /* --- format value at irow,jcol--- */
3836: *outfield = '\000'; /* init empty field */
3837: if ( irow < rp->height ) { /* check row index */
3838: int pixval = getpixel(rp,irow,jcol); /* value of pixel at irow,jcol */
3839: if ( ptype == 1 ) /* pixval must be 1 or 0 */
3840: pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
3841: sprintf(outfield,"%d ",pixval); } /* format pixel value */
3842: /* --- write line if this value won't fit on it (or last line) --- */
3843: if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
3844: || irow >= rp->height ) { /* force writing last line */
3845: strcat(outline,cr); /* add cr to end current line */
3846: if ( fp == NULL ) /* if we have no open file... */
3847: strcat(file,outline); /* add header to caller's buffer */
3848: else /* or if we have an open file... */
3849: if ( fputs(outline,fp) /* try writing header to open file */
3850: == EOF ) goto end_of_job; /* return with error if failed */
3851: nbytes += strlen(outline); /* bump output byte count */
3852: *outline = '\000'; /* re-initialize line buffer */
3853: } /* --- end-of-if(strlen>=maxlinelen) --- */
3854: if ( irow >= rp->height ) break; /* done after writing last line */
3855: /* --- concatanate value to line -- */
3856: strcat(outline,outfield); /* concatanate value to line */
3857: } /* --- end-of-for(jcol,irow) --- */
3858: isokay = 1; /* signal successful completion */
3859: /* -------------------------------------------------------------------------
3860: Back to caller with total #bytes written, or 0=failed.
3861: -------------------------------------------------------------------------- */
3862: end_of_job:
3863: if ( fp != NULL /* output written to an open file */
3864: && fp != stdout ) /* and it's not just stdout */
3865: fclose(fp); /* so close file before returning */
3866: return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
3867: } /* --- end-of-function type_pbmpgm() --- */
3868:
3869:
3870: /* ==========================================================================
1.1 albertel 3871: * Function: cstruct_chardef ( cp, fp, col1 )
3872: * Purpose: Emit a C struct of cp on fp, starting in col1.
3873: * --------------------------------------------------------------------------
3874: * Arguments: cp (I) ptr to chardef struct for which
3875: * a C struct is to be generated.
3876: * fp (I) File ptr to output device (defaults to
3877: * stdout if passed as NULL).
3878: * col1 (I) int containing 0...65; output lines
3879: * are preceded by col1 blanks.
3880: * --------------------------------------------------------------------------
3881: * Returns: ( int ) 1 if completed successfully,
3882: * or 0 otherwise (for any error).
3883: * --------------------------------------------------------------------------
3884: * Notes:
3885: * ======================================================================= */
3886: /* --- entry point --- */
3887: int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
3888: {
3889: /* -------------------------------------------------------------------------
3890: Allocations and Declarations
3891: -------------------------------------------------------------------------- */
3892: char field[64]; /* field within output line */
3893: int cstruct_raster(), /* emit a raster */
3894: emit_string(); /* emit a string and comment */
3895: /* -------------------------------------------------------------------------
3896: emit charnum, location, name / hirow, hicol, lorow, locol
3897: -------------------------------------------------------------------------- */
3898: /* --- charnum, location, name --- */
3899: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location); /*char#,location*/
3900: emit_string ( fp, col1, field, "character number, location");
3901: /* --- toprow, topleftcol, botrow, botleftcol --- */
1.2 albertel 3902: sprintf(field," %3d,%2d, %3d,%2d,\n", /* format... */
1.1 albertel 3903: cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
3904: cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
3905: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
3906: /* -------------------------------------------------------------------------
3907: emit raster and chardef's closing brace, and then return to caller
3908: -------------------------------------------------------------------------- */
3909: cstruct_raster(&cp->image,fp,col1+4); /* emit raster */
3910: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3911: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3912: } /* --- end-of-function cstruct_chardef() --- */
3913:
3914:
3915: /* ==========================================================================
3916: * Function: cstruct_raster ( rp, fp, col1 )
3917: * Purpose: Emit a C struct of rp on fp, starting in col1.
3918: * --------------------------------------------------------------------------
3919: * Arguments: rp (I) ptr to raster struct for which
3920: * a C struct is to be generated.
3921: * fp (I) File ptr to output device (defaults to
3922: * stdout if passed as NULL).
3923: * col1 (I) int containing 0...65; output lines
3924: * are preceded by col1 blanks.
3925: * --------------------------------------------------------------------------
3926: * Returns: ( int ) 1 if completed successfully,
3927: * or 0 otherwise (for any error).
3928: * --------------------------------------------------------------------------
3929: * Notes:
3930: * ======================================================================= */
3931: /* --- entry point --- */
3932: int cstruct_raster ( raster *rp, FILE *fp, int col1 )
3933: {
3934: /* -------------------------------------------------------------------------
3935: Allocations and Declarations
3936: -------------------------------------------------------------------------- */
3937: char field[64]; /* field within output line */
3938: char typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
3939: int hex_bitmap(); /* to emit raster bitmap */
3940: int emit_string(); /* emit a string and comment */
3941: /* -------------------------------------------------------------------------
3942: emit width and height
3943: -------------------------------------------------------------------------- */
1.2 albertel 3944: sprintf(field,"{ %2d, %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
3945: rp->width,rp->height,rp->format,rp->pixsz,typecast);
3946: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
1.1 albertel 3947: /* -------------------------------------------------------------------------
3948: emit bitmap and closing brace, and return to caller
3949: -------------------------------------------------------------------------- */
3950: hex_bitmap(rp,fp,col1+2,1); /* emit bitmap */
3951: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
3952: return ( 1 ); /* back to caller with 1=okay, 0=failed */
3953: } /* --- end-of-function cstruct_raster() --- */
3954:
3955:
3956: /* ==========================================================================
3957: * Function: hex_bitmap ( rp, fp, col1, isstr )
3958: * Purpose: Emit a hex dump of the bitmap of rp on fp, starting in col1.
3959: * If isstr (is string) is true, the dump is of the form
3960: * "\x01\x02\x03\x04\x05..."
3961: * Otherwise, if isstr is false, the dump is of the form
3962: * 0x01,0x02,0x03,0x04,0x05...
3963: * --------------------------------------------------------------------------
3964: * Arguments: rp (I) ptr to raster struct for which
3965: * a hex dump is to be constructed.
3966: * fp (I) File ptr to output device (defaults to
3967: * stdout if passed as NULL).
3968: * col1 (I) int containing 0...65; output lines
3969: * are preceded by col1 blanks.
3970: * isstr (I) int specifying dump format as described above
3971: * --------------------------------------------------------------------------
3972: * Returns: ( int ) 1 if completed successfully,
3973: * or 0 otherwise (for any error).
3974: * --------------------------------------------------------------------------
3975: * Notes:
3976: * ======================================================================= */
3977: /* --- entry point --- */
3978: int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
3979: {
3980: /* -------------------------------------------------------------------------
3981: Allocations and Declarations
3982: -------------------------------------------------------------------------- */
1.2 albertel 3983: int ibyte, /* pixmap[ibyte] index */
3984: nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
1.1 albertel 3985: char stub[64]=" ";/* col1 leading blanks */
3986: int linewidth = 64, /* (roughly) rightmost column */
3987: colwidth = (isstr? 4:5); /* #cols required for each byte */
3988: int ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
3989: /* --------------------------------------------------------------------------
3990: initialization
3991: -------------------------------------------------------------------------- */
3992: /* --- redirect null fp --- */
3993: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
3994: /* --- emit initial stub if wanted --- */
3995: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
3996: /* --------------------------------------------------------------------------
3997: emit hex dump of rp->bitmap image
3998: -------------------------------------------------------------------------- */
3999: if ( isstr ) fprintf(fp,"\""); /* opening " before first line */
4000: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* one byte at a time */
4001: {
4002: /* --- display a byte as hex char or number, depending on isstr --- */
4003: if ( isstr ) /* string format wanted */
4004: fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]); /*print byte as hex char*/
4005: else /* comma-separated format wanted */
4006: fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]); /*print byte as hex number*/
4007: /* --- add a separator and newline, etc, as necessary --- */
4008: if ( ibyte < nbytes-1) /* not the last byte yet */
4009: {
4010: if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
1.3 albertel 4011: if ( (ibyte+1)%ncols==0 ) { /* need new line after every ncols */
1.1 albertel 4012: if ( !isstr ) /* for hex numbers format ... */
4013: fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
4014: else /* for string format... */
1.3 albertel 4015: fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
1.1 albertel 4016: } /* --- end-of-if(ibyte<nbytes-1) --- */
4017: } /* --- end-of-for(ibyte) --- */
4018: if ( isstr ) fprintf(fp,"\""); /* closing " after last line */
4019: return ( 1 ); /* back with 1=okay, 0=failed */
4020: } /* --- end-of-function hex_bitmap() --- */
4021:
4022:
4023: /* ==========================================================================
4024: * Function: emit_string ( fp, col1, string, comment )
4025: * Purpose: Emit string on fp, starting in col1,
4026: * and followed by right-justified comment.
4027: * --------------------------------------------------------------------------
4028: * Arguments: fp (I) File ptr to output device (defaults to
4029: * stdout if passed as NULL).
4030: * col1 (I) int containing 0 or #blanks preceding string
4031: * string (I) char * containing string to be emitted.
4032: * If last char of string is '\n',
4033: * the emitted line ends with a newline,
4034: * otherwise not.
4035: * comment (I) NULL or char * containing right-justified
4036: * comment (we enclose between /star and star/)
4037: * --------------------------------------------------------------------------
4038: * Returns: ( int ) 1 if completed successfully,
4039: * or 0 otherwise (for any error).
4040: * --------------------------------------------------------------------------
4041: * Notes: o
4042: * ======================================================================= */
4043: /* --- entry point --- */
4044: int emit_string ( FILE *fp, int col1, char *string, char *comment )
4045: {
4046: /* -------------------------------------------------------------------------
4047: Allocations and Declarations
4048: -------------------------------------------------------------------------- */
4049: char line[256]; /* construct line with caller's fields */
4050: int fieldlen; /* #chars in one of caller's fields */
4051: int linelen = 72; /*line length (for right-justified comment)*/
4052: int isnewline = 0; /* true to emit \n at end of line */
4053: /* --------------------------------------------------------------------------
4054: construct line containing prolog, string, epilog, and finally comment
4055: -------------------------------------------------------------------------- */
4056: /* --- init line --- */
4057: memset(line,' ',255); /* start line with blanks */
4058: /* --- embed string into line --- */
4059: if ( string != NULL ) /* if caller gave us a string... */
4060: { fieldlen = strlen(string); /* #cols required for string */
4061: if ( string[fieldlen-1] == '\n' ) /* check last char for newline */
4062: { isnewline = 1; /* got it, so set flag */
4063: fieldlen--; } /* but don't print it yet */
4064: memcpy(line+col1,string,fieldlen); /* embid string starting at col1 */
4065: col1 += fieldlen; } /* bump col past epilog */
4066: /* --- embed comment into line --- */
4067: if ( comment != NULL ) /* if caller gave us a comment... */
4068: { fieldlen = 6 + strlen(comment); /* plus /star, star/, 2 spaces */
4069: if ( linelen-fieldlen < col1 ) /* comment won't fit */
4070: fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
4071: if ( fieldlen > 6 ) /* can fit all or part of comment */
4072: sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
4073: '*', fieldlen-6,comment, '*');
4074: col1 = linelen; } /* indicate line filled */
4075: /* --- line completed --- */
4076: line[col1] = '\000'; /* null-terminate completed line */
4077: /* -------------------------------------------------------------------------
4078: emit line, then back to caller with 1=okay, 0=failed.
4079: -------------------------------------------------------------------------- */
4080: /* --- first redirect null fp --- */
4081: if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
4082: /* --- emit line (and optional newline) --- */
4083: fprintf(fp,"%.*s",linelen,line); /* no more than linelen chars */
4084: if ( isnewline ) fprintf(fp,"\n"); /*caller wants terminating newline*/
4085: return ( 1 );
4086: } /* --- end-of-function emit_string() --- */
4087:
4088:
4089: /* ==========================================================================
1.2 albertel 4090: * Function: gftobitmap ( gf )
4091: * Purpose: convert .gf-like pixmap to bitmap image
4092: * --------------------------------------------------------------------------
4093: * Arguments: gf (I) raster * to struct in .gf-format
4094: * --------------------------------------------------------------------------
4095: * Returns: ( raster * ) image-format raster * if successful,
4096: * or NULL for any error.
4097: * --------------------------------------------------------------------------
4098: * Notes: o
4099: * ======================================================================= */
4100: /* --- entry point --- */
4101: raster *gftobitmap ( raster *gf )
4102: {
4103: /* -------------------------------------------------------------------------
4104: Allocations and Declarations
4105: -------------------------------------------------------------------------- */
4106: raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
4107: int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
4108: int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
4109: ibit=0, bitval=0; /* bitmap index, bit value */
4110: int isrepeat = 1, /* true to process repeat counts */
4111: repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
4112: nrepeats=0, irepeat=0, /* scan line repeat count,index */
4113: wbits = 0; /* count bits to width of scan line*/
4114: /* -------------------------------------------------------------------------
4115: initialization
4116: -------------------------------------------------------------------------- */
4117: /* --- check args --- */
4118: if ( gf == NULL ) goto end_of_job; /* input raster not provided */
4119: format = gf->format; /* 2 or 3 */
4120: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
4121: ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
4122: /* --- allocate output raster with proper dimensions for bitmap --- */
4123: width=gf->width; height=gf->height; /* dimensions of raster */
4124: if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
4125: == NULL ) goto end_of_job; /* quit if failed to allocate */
4126: totbits = width*height; /* total #bits in image */
4127: /* -------------------------------------------------------------------------
4128: fill bitmap
4129: -------------------------------------------------------------------------- */
4130: for ( icount=0,bitval=0; icount<ncounts; icount++ )
4131: {
4132: int nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
4133: if ( isrepeat /* we're proxessing repeat counts */
1.3 albertel 4134: && nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
1.2 albertel 4135: if ( nrepeats == 0 ) /* recursive repeat is error */
4136: { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
4137: nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
4138: icount += 2; } /* bump byte/nibble count */
4139: else /* some internal error occurred */
4140: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
1.3 albertel 4141: fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
1.2 albertel 4142: if ( 0 )
4143: fprintf(stdout,
4144: "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
4145: icount,bitval,nbits,ibit,totbits);
4146: for ( ; nbits>0; nbits-- ) /* count down */
4147: { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
4148: for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
4149: if ( bitval == 1 ) /* set pixel */
4150: { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
4151: else /* clear pixel */
4152: { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
4153: if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
4154: ibit++; } /* bump bit index */
4155: bitval = 1-bitval; /* flip bit value */
4156: if ( wbits >= width ) { /* completed repeats */
4157: ibit += nrepeats*width; /*bump bit count past repeated scans*/
4158: if ( wbits > width ) /* out-of alignment error */
4159: if ( msgfp!=NULL && msglevel>=1 ) /* report error */
4160: fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
4161: wbits = nrepeats = 0; } /* reset repeat counts */
4162: } /* --- end-of-for(icount) --- */
4163: end_of_job:
4164: return ( rp ); /* back to caller with image */
4165: } /* --- end-of-function gftobitmap() --- */
4166:
4167:
4168: /* ==========================================================================
1.1 albertel 4169: * Function: get_symdef ( symbol )
4170: * Purpose: returns mathchardef struct for symbol
4171: * --------------------------------------------------------------------------
4172: * Arguments: symbol (I) char * containing symbol
4173: * whose corresponding mathchardef is wanted
4174: * --------------------------------------------------------------------------
4175: * Returns: ( mathchardef * ) pointer to struct defining symbol,
4176: * or NULL for any error
4177: * --------------------------------------------------------------------------
4178: * Notes: o Input symbol need only contain a leading substring to match,
4179: * e.g., \gam passed in symbol will match \gamma in the table.
4180: * If the table contains two or more possible matches,
4181: * the shortest is returned, e.g., input \e will return with
4182: * data for \eta rather than \epsilon. To get \epsilon,
4183: * you must pass a leading substring long enough to eliminate
4184: * shorter table matches, i.e., in this case \ep
4185: * ======================================================================= */
4186: /* --- entry point --- */
4187: mathchardef *get_symdef ( char *symbol )
4188: {
4189: /* -------------------------------------------------------------------------
4190: Allocations and Declarations
4191: -------------------------------------------------------------------------- */
4192: mathchardef *symdefs = symtable; /* table of mathchardefs */
1.3 albertel 4193: int ligdef=0, get_ligature(); /* or we may have a ligature */
1.1 albertel 4194: int idef = 0, /* symdefs[] index */
4195: bestdef = (-9999); /*index of shortest matching symdef*/
4196: int symlen = strlen(symbol), /* length of input symbol */
4197: deflen, minlen=9999; /*length of shortest matching symdef*/
4198: int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
1.5 ! raeburn 4199: alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */
! 4200: slashsym = (*symbol=='\\'); /* or \backslashed symbol */
1.2 albertel 4201: int family = fontinfo[fontnum].family; /* current font family */
1.1 albertel 4202: static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
1.2 albertel 4203: /* --- see table on page 536 in TLC2 --- */
1.1 albertel 4204: {"\\int", "\\Bigint"},
4205: {"\\oint", "\\Bigoint"},
4206: {"\\sum", "\\Bigsum"},
4207: {"\\prod", "\\Bigprod"},
4208: {"\\coprod", "\\Bigcoprod"},
1.2 albertel 4209: /* --- must be 'big' when related to similar binary operators --- */
4210: {"\\bigcup", "\\Bigcup"},
4211: {"\\bigsqcup", "\\Bigsqcup"},
4212: {"\\bigcap", "\\Bigcap"},
4213: /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
4214: {"\\bigodot", "\\Bigodot"},
4215: {"\\bigoplus", "\\Bigoplus"},
4216: {"\\bigominus", "\\ominus"},
4217: {"\\bigotimes", "\\Bigotimes"},
4218: {"\\bigoslash", "\\oslash"},
4219: {"\\biguplus", "\\Biguplus"},
4220: {"\\bigwedge", "\\Bigwedge"},
4221: {"\\bigvee", "\\Bigvee"},
1.1 albertel 4222: {NULL, NULL} };
4223: /* -------------------------------------------------------------------------
1.3 albertel 4224: First check for ligature
4225: -------------------------------------------------------------------------- */
4226: isligature = 0; /* init signal for no ligature */
4227: if ( family == CYR10 ) /*only check for cyrillic ligatures*/
4228: if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
4229: >= 0 ) /* found a ligature */
4230: { bestdef = ligdef; /* set bestdef for ligature */
4231: isligature = 1; /* signal we found a ligature */
4232: goto end_of_job; } /* so just give it to caller */
4233: /* -------------------------------------------------------------------------
1.1 albertel 4234: If in \displaystyle mode, first xlate int to Bigint, etc.
4235: -------------------------------------------------------------------------- */
4236: if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */
4237: for ( idef=0; ; idef++ ) { /* lookup symbol in displaysyms */
4238: char *fromsym = displaysyms[idef][0], /* look for this symbol */
4239: *tosym = displaysyms[idef][1]; /* and xlate it to this symbol */
4240: if ( fromsym == NULL ) break; /* end-of-table */
4241: if ( !strcmp(symbol,fromsym) ) /* found a match */
4242: { if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
4243: { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
4244: isdisplaystyle,symbol,tosym); fflush(msgfp); }
4245: symbol = tosym; /* so look up tosym instead */
4246: symlen = strlen(symbol); /* reset symbol length */
4247: break; } /* no need to search further */
4248: } /* --- end-of-for(idef) --- */
4249: /* -------------------------------------------------------------------------
4250: search symdefs[] in order for first occurrence of symbol
4251: -------------------------------------------------------------------------- */
4252: for ( idef=0; ;idef++ ) /* until trailer record found */
4253: if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
4254: else /* check against caller's symbol */
4255: if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
1.3 albertel 4256: if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */
1.5 ! raeburn 4257: || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
! 4258: || (1 && istextmode && slashsym) /*text mode and \backslashed symbol*/
1.2 albertel 4259: || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
4260: || symdefs[idef].handler!=NULL)) /* or text mode and directive */
4261: || (symdefs[idef].family==family /* have correct family */
4262: && symdefs[idef].handler==NULL) ) /* and not a handler collision */
4263: #if 0
4264: || (fontnum==1 && symdefs[idef].family==CMR10) /*textmode && rm text*/
4265: || (fontnum==2 && symdefs[idef].family==CMMI10) /*textmode && it text*/
4266: || (fontnum==3 && symdefs[idef].family==BBOLD10 /*textmode && bb text*/
4267: && symdefs[idef].handler==NULL)
4268: || (fontnum==4 && symdefs[idef].family==CMMIB10 /*textmode && bf text*/
4269: && symdefs[idef].handler==NULL) )
4270: #endif
1.1 albertel 4271: if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
4272: { bestdef = idef; /* save index of new best match */
4273: if ( (minlen = deflen) /* and save its len for next test */
4274: == symlen ) break; } /*perfect match, so return with it*/
4275: if ( bestdef < 0 ) /* failed to look up symbol */
1.2 albertel 4276: if ( fontnum != 0 ) /* we're in a restricted font mode */
4277: { int oldfontnum = fontnum; /* save current font family */
4278: mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
4279: fontnum = 0; /*try to look up symbol in any font*/
4280: symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
4281: fontnum = oldfontnum; /* reset font family */
4282: return symdef; } /* caller gets fontnum=0 lookup */
1.3 albertel 4283: end_of_job:
4284: if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
4285: { fprintf(msgfp,
4286: "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
4287: symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
1.1 albertel 4288: fflush(msgfp); }
1.3 albertel 4289: return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
1.1 albertel 4290: } /* --- end-of-function get_symdef() --- */
4291:
4292:
4293: /* ==========================================================================
1.3 albertel 4294: * Function: get_ligature ( expression, family )
4295: * Purpose: returns symtable[] index for ligature
4296: * --------------------------------------------------------------------------
4297: * Arguments: expression (I) char * containing ligature
4298: * whose corresponding mathchardef is wanted
4299: * family (I) int containing NOVALUE for any family,
4300: * or, e.g., CYR10 for cyrillic, etc.
4301: * --------------------------------------------------------------------------
4302: * Returns: ( int ) symtable[] index defining ligature,
4303: * or -9999 if no ligature found or for any error
4304: * --------------------------------------------------------------------------
4305: * Notes: o
4306: * ======================================================================= */
4307: /* --- entry point --- */
4308: int get_ligature ( char *expression, int family )
4309: {
4310: /* -------------------------------------------------------------------------
4311: Allocations and Declarations
4312: -------------------------------------------------------------------------- */
4313: mathchardef *symdefs = symtable; /* table of mathchardefs */
4314: char *ligature = expression /*- 1*/, /* expression ptr */
4315: *symbol = NULL; /* symdefs[idef].symbol */
4316: int liglen = strlen(ligature); /* #chars remaining in expression */
4317: int iscyrfam = (family==CYR10); /* true for cyrillic families */
4318: int idef = 0, /* symdefs[] index */
4319: bestdef = (-9999), /*index of longest matching symdef*/
4320: maxlen=(-9999); /*length of longest matching symdef*/
4321: /* -------------------------------------------------------------------------
4322: search symdefs[] in order for first occurrence of symbol
4323: -------------------------------------------------------------------------- */
4324: if ( !isstring ) { /* no ligatures in "string" mode */
4325: for ( idef=0; ;idef++ ) /* until trailer record found */
4326: if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
4327: else { /* check against caller's ligature */
4328: int symlen = strlen(symbol); /* #chars in symbol */
4329: if ( ( symlen>1 || iscyrfam ) /*ligature >1 char long or cyrillic*/
4330: && symlen <= liglen /* and enough remaining chars */
4331: && ( *symbol!='\\' || iscyrfam ) /* not escaped or cyrillic */
4332: && symdefs[idef].handler == NULL ) /* and not a handler */
4333: if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
4334: if ( family < 0 /* no family specifies */
4335: || symdefs[idef].family == family ) /* or have correct family */
4336: if ( symlen > maxlen ) /* new longest ligature */
4337: { bestdef = idef; /* save index of new best match */
4338: maxlen = symlen; } /* and save its len for next test */
4339: } /* --- end-of-if/else(symbol==NULL) --- */
4340: if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
4341: { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
4342: ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
4343: fflush(msgfp); }
4344: } /* --- end-of-if(!isstring) --- */
4345: return ( bestdef ); /* -9999 or index of best symdef[] */
4346: } /* --- end-of-function get_ligature --- */
4347:
4348:
4349: /* ==========================================================================
1.1 albertel 4350: * Function: get_chardef ( symdef, size )
4351: * Purpose: returns chardef ptr containing data for symdef at given size
4352: * --------------------------------------------------------------------------
4353: * Arguments: symdef (I) mathchardef * corresponding to symbol
4354: * whose corresponding chardef is wanted
1.2 albertel 4355: * size (I) int containing 0-5 for desired size
1.1 albertel 4356: * --------------------------------------------------------------------------
4357: * Returns: ( chardef * ) pointer to struct defining symbol at size,
4358: * or NULL for any error
4359: * --------------------------------------------------------------------------
4360: * Notes: o if size unavailable, the next-closer-to-normalsize
4361: * is returned instead.
4362: * ======================================================================= */
4363: /* --- entry point --- */
4364: chardef *get_chardef ( mathchardef *symdef, int size )
4365: {
4366: /* -------------------------------------------------------------------------
4367: Allocations and Declarations
4368: -------------------------------------------------------------------------- */
4369: fontfamily *fonts = fonttable; /* table of font families */
4370: chardef **fontdef, /*tables for desired font, by size*/
4371: *gfdata = (chardef *)NULL; /* chardef for symdef,size */
4372: int ifont; /* fonts[] index */
4373: int family, charnum; /* indexes retrieved from symdef */
4374: int sizeinc = 0, /*+1 or -1 to get closer to normal*/
4375: normalsize = 2; /* this size always present */
4376: int isBig = 0; /*true if symbol's 1st char is upper*/
4377: char *symptr = NULL; /* look for 1st alpha of symbol */
4378: /* -------------------------------------------------------------------------
4379: initialization
4380: -------------------------------------------------------------------------- */
4381: /* --- check symdef --- */
1.3 albertel 4382: if ( symdef == NULL ) goto end_of_job; /* get_symdef() probably failed */
1.1 albertel 4383: /* --- get local copy of indexes from symdef --- */
4384: family = symdef->family; /* font family containing symbol */
4385: charnum = symdef->charnum; /* char# of symbol within font */
4386: /* --- check for supersampling --- */
4387: if ( issupersampling ) /* check for supersampling fonts */
4388: if ( fonts != ssfonttable ) /* uh oh--probably internal error */
4389: { fonts = ssfonttable; } /* force it */
4390: /* --- check requested size, and set size increment --- */
4391: if ( 0 && issupersampling ) /* set size index for supersampling */
4392: size = LARGESTSIZE+1; /* index 1 past largest size */
4393: else /* low pass indexes 0...LARGESTSIZE */
4394: {
4395: if( size<0 ) size = 0; /* size was definitely too small */
4396: if( size>LARGESTSIZE ) size = LARGESTSIZE; /* or definitely too large */
4397: if( size<normalsize ) sizeinc = (+1); /*use next larger if size too small*/
4398: if( size>normalsize ) sizeinc = (-1); /*or next smaller if size too large*/
4399: }
4400: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
4401: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
4402: if ( isalpha(*symptr) ) /* found leading alpha char */
4403: { isBig = isupper(*symptr); /* is 1st char of name uppercase? */
4404: if ( !isBig /* 1st char lowercase */
4405: && strlen(symptr) >= 4 ) /* but followed by at least 3 chars */
4406: isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
4407: || !memcmp(symptr,"bigg",4); /* or with bigg */
4408: break; } /* don't check beyond 1st char */
4409: /* -------------------------------------------------------------------------
4410: find font family in table of fonts[]
4411: -------------------------------------------------------------------------- */
4412: /* --- look up font family --- */
4413: for ( ifont=0; ;ifont++ ) /* until trailer record found */
1.3 albertel 4414: if ( fonts[ifont].family < 0 ) { /* error, no such family */
4415: if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4416: fprintf(msgfp,"get_chardef> failed to find font family %d\n",
4417: family); fflush(msgfp); }
4418: goto end_of_job; } /* quit if can't find font family*/
1.1 albertel 4419: else if ( fonts[ifont].family == family ) break; /* found font family */
4420: /* --- get local copy of table for this family by size --- */
4421: fontdef = fonts[ifont].fontdef; /* font by size */
4422: /* -------------------------------------------------------------------------
4423: get font in desired size, or closest available size, and return symbol
4424: -------------------------------------------------------------------------- */
4425: /* --- get font in desired size --- */
4426: while ( 1 ) /* find size or closest available */
4427: if ( fontdef[size] != NULL ) break; /* found available size */
4428: else /* adjust size closer to normal */
4429: if ( size == NORMALSIZE /* already normal so no more sizes,*/
1.3 albertel 4430: || sizeinc == 0 ) { /* or must be supersampling */
4431: if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4432: fprintf(msgfp,"get_chardef> failed to find font size %d\n",
4433: size); fflush(msgfp); }
4434: goto end_of_job; } /* quit if can't find desired size */
1.1 albertel 4435: else /*bump size 1 closer to NORMALSIZE*/
4436: size += sizeinc; /* see if adjusted size available */
4437: /* --- ptr to chardef struct --- */
4438: gfdata = &((fontdef[size])[charnum]); /*ptr to chardef for symbol in size*/
4439: /* -------------------------------------------------------------------------
4440: kludge to tweak CMEX10 (which appears to have incorrect descenders)
4441: -------------------------------------------------------------------------- */
4442: if ( family == CMEX10 ) /* cmex10 needs tweak */
4443: { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
4444: gfdata->botrow = (isBig? (-height/3) : (-height/4));
4445: gfdata->toprow = gfdata->botrow + gfdata->image.height; }
4446: /* -------------------------------------------------------------------------
4447: return subraster containing chardef data for symbol in requested size
4448: -------------------------------------------------------------------------- */
1.3 albertel 4449: end_of_job:
4450: if ( msgfp!=NULL && msglevel>=999 )
4451: { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
4452: else
4453: fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d %s\n",
4454: symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
4455: fflush(msgfp); }
4456: return ( gfdata ); /*ptr to chardef for symbol in size*/
1.1 albertel 4457: } /* --- end-of-function get_chardef() --- */
4458:
4459:
4460: /* ==========================================================================
4461: * Function: get_charsubraster ( symdef, size )
4462: * Purpose: returns new subraster ptr containing
4463: * data for symdef at given size
4464: * --------------------------------------------------------------------------
1.2 albertel 4465: * Arguments: symdef (I) mathchardef * corresponding to symbol whose
4466: * corresponding chardef subraster is wanted
4467: * size (I) int containing 0-5 for desired size
1.1 albertel 4468: * --------------------------------------------------------------------------
4469: * Returns: ( subraster * ) pointer to struct defining symbol at size,
4470: * or NULL for any error
4471: * --------------------------------------------------------------------------
4472: * Notes: o just wraps a subraster envelope around get_chardef()
4473: * ======================================================================= */
4474: /* --- entry point --- */
4475: subraster *get_charsubraster ( mathchardef *symdef, int size )
4476: {
4477: /* -------------------------------------------------------------------------
4478: Allocations and Declarations
4479: -------------------------------------------------------------------------- */
4480: chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
4481: int get_baseline(); /* baseline of gfdata */
4482: subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
1.2 albertel 4483: raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
4484: int delete_subraster(); /* in case gftobitmap() fails */
1.1 albertel 4485: int aasupsamp(), /*antialias char with supersampling*/
4486: grayscale=256; /* aasupersamp() parameters */
4487: /* -------------------------------------------------------------------------
4488: look up chardef for symdef at size, and embed data (gfdata) in subraster
4489: -------------------------------------------------------------------------- */
4490: if ( (gfdata=get_chardef(symdef,size)) /* look up chardef for symdef,size */
4491: != NULL ) /* and check that we found it */
4492: if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
4493: != NULL ) /* and check that we succeeded */
4494: {
1.2 albertel 4495: raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
4496: int format = image->format; /* 1=bitmap, else .gf */
1.1 albertel 4497: sp->symdef = symdef; /* replace NULL with caller's arg */
4498: sp->size = size; /*replace default with caller's size*/
4499: sp->baseline = get_baseline(gfdata); /* get baseline of character */
1.2 albertel 4500: if ( format == 1 ) /* already a bitmap */
4501: { sp->type = CHARASTER; /* static char raster */
4502: sp->image = image; } /* store ptr to its bitmap */
4503: else /* need to convert .gf-to-bitmap */
4504: if ( (bitmaprp = gftobitmap(image)) /* convert */
4505: != (raster *)NULL ) /* successful */
4506: { sp->type = IMAGERASTER; /* allocated raster will be freed */
4507: sp->image = bitmaprp; } /* store ptr to converted bitmap */
4508: else /* conversion failed */
4509: { delete_subraster(sp); /* free unneeded subraster */
4510: sp = (subraster *)NULL; /* signal error to caller */
4511: goto end_of_job; } /* quit */
1.1 albertel 4512: if ( issupersampling ) /* antialias character right here */
4513: {
4514: raster *aa = NULL; /* antialiased char raster */
4515: int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
4516: if ( status ) /* supersampled successfully */
4517: { int baseline = sp->baseline; /* baseline before supersampling */
4518: int height = gfdata->image.height; /* #rows before supersampling */
4519: sp->image = aa; /* replace chardef with ss image */
4520: if ( baseline >= height-1 ) /* baseline at bottom of char */
4521: sp->baseline = aa->height -1; /* so keep it at bottom */
4522: else /* char has descenders */
4523: sp->baseline /= shrinkfactor; /* rescale baseline */
4524: sp->type = IMAGERASTER; } /* character is an image raster */
4525: } /* --- end-of-if(issupersampling) --- */
4526: } /* --- end-of-if(sp!=NULL) --- */
1.2 albertel 4527: end_of_job:
4528: if ( msgfp!=NULL && msglevel>=999 )
1.3 albertel 4529: { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
4530: " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
4531: (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
4532: fflush(msgfp); }
1.1 albertel 4533: return ( sp ); /* back to caller */
4534: } /* --- end-of-function get_charsubraster() --- */
4535:
4536:
4537: /* ==========================================================================
1.2 albertel 4538: * Function: get_symsubraster ( symbol, size )
4539: * Purpose: returns new subraster ptr containing
4540: * data for symbol at given size
4541: * --------------------------------------------------------------------------
4542: * Arguments: symbol (I) char * corresponding to symbol
4543: * whose corresponding subraster is wanted
4544: * size (I) int containing 0-5 for desired size
4545: * --------------------------------------------------------------------------
4546: * Returns: ( subraster * ) pointer to struct defining symbol at size,
4547: * or NULL for any error
4548: * --------------------------------------------------------------------------
4549: * Notes: o just combines get_symdef() and get_charsubraster()
4550: * ======================================================================= */
4551: /* --- entry point --- */
4552: subraster *get_symsubraster ( char *symbol, int size )
4553: {
4554: /* -------------------------------------------------------------------------
4555: Allocations and Declarations
4556: -------------------------------------------------------------------------- */
4557: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
4558: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
4559: /* -------------------------------------------------------------------------
4560: look up mathchardef for symbol
4561: -------------------------------------------------------------------------- */
4562: if ( symbol != NULL ) /* user supplied input symbol */
4563: symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
4564: /* -------------------------------------------------------------------------
4565: look up chardef for mathchardef and wrap a subraster structure around data
4566: -------------------------------------------------------------------------- */
4567: if ( symdef != NULL ) /* lookup succeeded */
4568: sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
4569: return ( sp ); /* back to caller with sp or NULL */
4570: } /* --- end-of-function get_symsubraster() --- */
4571:
4572:
4573: /* ==========================================================================
1.1 albertel 4574: * Function: get_baseline ( gfdata )
4575: * Purpose: returns baseline for a chardef struct
4576: * --------------------------------------------------------------------------
4577: * Arguments: gfdata (I) chardef * containing chardef for symbol
4578: * whose baseline is wanted
4579: * --------------------------------------------------------------------------
4580: * Returns: ( int ) baseline for symdef,
4581: * or -1 for any error
4582: * --------------------------------------------------------------------------
4583: * Notes: o Unlike TeX, the top-left corners of our rasters are (0,0),
4584: * with (row,col) increasing as you move down and right.
4585: * Baselines are calculated with respect to this scheme,
4586: * so 0 would mean the very top row is on the baseline
4587: * and everything else descends below the baseline.
4588: * ======================================================================= */
4589: /* --- entry point --- */
4590: int get_baseline ( chardef *gfdata )
4591: {
4592: /* -------------------------------------------------------------------------
4593: Allocations and Declarations
4594: -------------------------------------------------------------------------- */
4595: int /*toprow = gfdata->toprow,*/ /*TeX top row from .gf file info*/
4596: botrow = gfdata->botrow, /*TeX bottom row from .gf file info*/
4597: height = gfdata->image.height; /* #rows comprising symbol */
4598: /* -------------------------------------------------------------------------
4599: give caller baseline
4600: -------------------------------------------------------------------------- */
4601: return ( (height-1) + botrow ); /* note: descenders have botrow<0 */
4602: } /* --- end-of-function get_baseline() --- */
4603:
4604:
4605: /* ==========================================================================
4606: * Function: get_delim ( char *symbol, int height, int family )
4607: * Purpose: returns subraster corresponding to the samllest
4608: * character containing symbol, but at least as large as height,
4609: * and in caller's family (if specified).
4610: * If no symbol character as large as height is available,
4611: * then the largest availabale character is returned instead.
4612: * --------------------------------------------------------------------------
4613: * Arguments: symbol (I) char * containing (substring of) desired
4614: * symbol, e.g., if symbol="(", then any
4615: * mathchardef like "(" or "\\(", etc, match.
4616: * height (I) int containing minimum acceptable height
4617: * for returned character
4618: * family (I) int containing -1 to consider all families,
4619: * or, e.g., CMEX10 for only that family
4620: * --------------------------------------------------------------------------
4621: * Returns: ( subraster * ) best matching character available,
4622: * or NULL for any error
4623: * --------------------------------------------------------------------------
4624: * Notes: o If height is passed as negative, its absolute value is used
4625: * but the best-fit width is searched for (rather than height)
4626: * ======================================================================= */
4627: /* --- entry point --- */
4628: subraster *get_delim ( char *symbol, int height, int family )
4629: {
4630: /* -------------------------------------------------------------------------
4631: Allocations and Declarations
4632: -------------------------------------------------------------------------- */
4633: mathchardef *symdefs = symtable; /* table of mathchardefs */
4634: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
4635: subraster *make_delim(); /* construct delim if can't find it*/
4636: chardef *get_chardef(), *gfdata=NULL; /* get chardef struct for a symdef */
4637: char lcsymbol[256], *symptr, /* lowercase symbol for comparison */
4638: *unescsymbol = symbol; /* unescaped symbol */
4639: int symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
4640: deflen = 0; /* length of symdef (aka lcsymbol) */
4641: int idef = 0, /* symdefs[] index */
4642: bestdef = (-9999), /* index of best fit symdef */
4643: bigdef = (-9999); /*index of biggest (in case no best)*/
4644: int size = 0, /* size index 0...LARGESTSIZE */
4645: bestsize = (-9999), /* index of best fit size */
4646: bigsize = (-9999); /*index of biggest (in case no best)*/
4647: int defheight, bestheight=9999, /* height of best fit symdef */
4648: bigheight = (-9999); /*height of biggest(in case no best)*/
4649: int iswidth = 0; /* true if best-fit width desired */
4650: int isunesc = 0, /* true if leading escape removed */
4651: issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */
1.3 albertel 4652: int iscurly = 0; /* true for StMary's curly symbols */
1.1 albertel 4653: char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
4654: /* -------------------------------------------------------------------------
4655: determine if searching height or width, and search symdefs[] for best-fit
4656: -------------------------------------------------------------------------- */
4657: /* --- arg checks --- */
4658: if ( symlen < 1 ) return (sp); /* no input symbol suplied */
4659: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
1.3 albertel 4660: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
1.1 albertel 4661: /* --- ignore leading escapes for CMEX10 --- */
4662: if ( 1 ) /* ignore leading escape */
4663: if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
4664: if ( strstr(symbol,"sq") != NULL ) /* \sq symbol requested */
4665: issq = 1; /* seq \sq signal */
4666: if ( strstr(symbol,"oint") != NULL ) /* \oint symbol requested */
4667: isoint = 1; /* seq \oint signal */
4668: if ( *symbol=='\\' ) /* have leading \ */
4669: { unescsymbol = symbol+1; /* push past leading \ */
4670: if ( --symlen < 1 ) return(sp); /* one less char */
4671: if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
4672: unescsymbol = bigint; /* but big version looks better */
4673: if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
4674: unescsymbol = bigoint; /* but big version looks better */
4675: symlen = strlen(unescsymbol); /* explicitly recalculate length */
4676: isunesc = 1; } /* signal leading escape removed */
4677: } /* --- end-of-if(family) --- */
4678: /* --- determine whether searching for best-fit height or width --- */
4679: if ( height < 0 ) /* negative signals width search */
4680: { height = (-height); /* flip "height" positive */
4681: iswidth = 1; } /* set flag for width search */
4682: /* --- search symdefs[] for best-fit height (or width) --- */
4683: for ( idef=0; ;idef++ ) /* until trailer record found */
4684: {
4685: char *defsym = symdefs[idef].symbol; /* local copies */
4686: int deffam = symdefs[idef].family;
4687: if ( defsym == NULL ) break; /* reached end-of-table */
4688: else /* check against caller's symbol */
4689: if ( family<0 || deffam == family /* if explicitly in caller's family*/
1.2 albertel 4690: || (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
1.1 albertel 4691: {
4692: strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
4693: if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
1.5 ! raeburn 4694: {strsqueeze(lcsymbol,1);} /*so squeeze it out of lcsymbol too*/
1.1 albertel 4695: if ( 0 ) /* don't ignore case */
1.5 ! raeburn 4696: for ( symptr=lcsymbol; *symptr!='\000'; symptr++ )/*for each symbol ch*/
! 4697: if ( isalpha(*symptr) ) *symptr=tolower(*symptr);/*lowercase the char*/
1.1 albertel 4698: deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
4699: if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
4700: if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
4701: && (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
1.3 albertel 4702: if ( ( deffam == CMSY10 ? /* CMSY10 or not CMSY10 */
1.1 albertel 4703: symptr == lcsymbol /* caller's sym is a prefix */
4704: && deflen == symlen: /* and same length */
1.3 albertel 4705: (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
4706: (symptr == lcsymbol /* caller's sym is a prefix */
4707: || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
1.1 albertel 4708: for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
4709: if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
4710: { defheight = gfdata->image.height; /* height of this character */
4711: if ( iswidth ) /* width search wanted instead... */
4712: defheight = gfdata->image.width; /* ...so substitute width */
4713: leftsymdef = &(symdefs[idef]); /* set symbol class, etc */
4714: if ( defheight>=height && defheight<bestheight ) /*new best fit*/
4715: { bestdef=idef; bestsize=size; /* save indexes of best fit */
4716: bestheight = defheight; } /* and save new best height */
4717: if ( defheight >= bigheight ) /* new biggest character */
4718: { bigdef=idef; bigsize=size; /* save indexes of biggest */
4719: bigheight = defheight; } /* and save new big height */
4720: } /* --- end-of-if(gfdata!=NULL) --- */
4721: } /* --- end-of-if(family) --- */
4722: } /* --- end-of-for(idef) --- */
4723: /* -------------------------------------------------------------------------
4724: construct subraster for best fit character, and return it to caller
4725: -------------------------------------------------------------------------- */
4726: if ( bestdef >= 0 ) /* found a best fit for caller */
4727: sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
1.2 albertel 4728: if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
4729: || bigdef < 0 ) /* delim not in font tables */
1.1 albertel 4730: sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
4731: if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
4732: sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
1.2 albertel 4733: if ( msgfp!=NULL && msglevel>=99 )
4734: fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
4735: (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
1.1 albertel 4736: return ( sp );
4737: } /* --- end-of-function get_delim() --- */
4738:
4739:
4740: /* ==========================================================================
4741: * Function: make_delim ( char *symbol, int height )
4742: * Purpose: constructs subraster corresponding to symbol
4743: * exactly as large as height,
4744: * --------------------------------------------------------------------------
4745: * Arguments: symbol (I) char * containing, e.g., if symbol="("
4746: * for desired delimiter
4747: * height (I) int containing height
4748: * for returned character
4749: * --------------------------------------------------------------------------
4750: * Returns: ( subraster * ) constructed delimiter
4751: * or NULL for any error
4752: * --------------------------------------------------------------------------
4753: * Notes: o If height is passed as negative, its absolute value is used
4754: * and interpreted as width (rather than height)
4755: * ======================================================================= */
4756: /* --- entry point --- */
4757: subraster *make_delim ( char *symbol, int height )
4758: {
4759: /* -------------------------------------------------------------------------
4760: Allocations and Declarations
4761: -------------------------------------------------------------------------- */
4762: subraster *sp = (subraster *)NULL, /* subraster returned to caller */
4763: *new_subraster(); /* allocate subraster */
1.2 albertel 4764: subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
4765: *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
4766: *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
4767: *rastack(), *rastcat(); /* stack pieces, concat filler */
4768: int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
1.1 albertel 4769: raster *rasp = (raster *)NULL; /* sp->image */
4770: int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
1.2 albertel 4771: int pixsz = 1, /* pixels are one bit each */
4772: symsize = 0; /* size arg for get_symsubraster() */
1.1 albertel 4773: int thickness = 1; /* drawn lines are one pixel thick */
4774: int aspectratio = 8; /* default height/width for parens */
4775: int iswidth = 0, /*true if width specified by height*/
4776: width = height; /* #pixels width (e.g., of ellipse)*/
1.2 albertel 4777: char *lp=NULL, *rp=NULL, /* check symbol for left or right */
4778: *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
4779: *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
4780: *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
1.1 albertel 4781: int circle_raster(), /* ellipse for ()'s in sp->image */
4782: rule_rsater(), /* horizontal or vertical lines */
4783: line_raster(); /* line between two points */
1.2 albertel 4784: subraster *uparrow_subraster(); /* up/down arrows */
4785: int isprealloc = 1; /*pre-alloc subraster, except arrow*/
4786: int oldsmashmargin = smashmargin, /* save original smashmargin */
1.3 albertel 4787: wasnocatspace = isnocatspace; /* save original isnocatspace */
1.1 albertel 4788: /* -------------------------------------------------------------------------
4789: initialization
4790: -------------------------------------------------------------------------- */
4791: /* --- determine whether constructing height or width --- */
4792: if ( height < 0 ) /* negative "height" signals width */
4793: { width = height = (-height); /* flip height positive */
4794: iswidth = 1; } /* set flag for width */
4795: if ( height < 3 ) goto end_of_job; /* too small, must be error */
4796: /* --- set default width (or height) accordingly --- */
4797: if ( iswidth ) height = (width+(aspectratio+1)/2)/aspectratio;
4798: else width = (height+(aspectratio+1)/2)/aspectratio;
4799: if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
1.2 albertel 4800: || strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
4801: || strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
4802: width = max2(width,6); /* need space between two |'s */
1.1 albertel 4803: if ( width < 2 ) width=2; /* set min width */
4804: if ( strchr(symbol,'(') != NULL /* if left ( */
4805: || strchr(symbol,')') != NULL ) /* or right ) paren wanted */
1.2 albertel 4806: { width = (3*width)/2; /* adjust width */
4807: if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
4808: if ( strchr(symbol,'/') != NULL /* left / */
4809: || strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
4810: || strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
4811: width = max2(height/3,5);
4812: if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
4813: { width = min2(height/3,20); /* adjust width */
4814: isprealloc = 0; } /* don't preallocate subraster */
4815: if ( strchr(symbol,'{') != NULL /* if left { */
4816: || strchr(symbol,'}') != NULL ) /* or right } brace wanted */
4817: { isprealloc = 0; } /* don't preallocate */
1.1 albertel 4818: /* --- allocate and initialize subraster for constructed delimiter --- */
1.2 albertel 4819: if ( isprealloc ) /* pre-allocation wanted */
4820: { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
4821: == NULL ) goto end_of_job; /* quit if failed */
4822: /* --- initialize delimiter subraster parameters --- */
4823: sp->type = IMAGERASTER; /* image */
4824: sp->symdef = NULL; /* not applicable for image */
4825: sp->baseline = height/2 + 2; /* is a little above center good? */
4826: sp->size = NORMALSIZE; /* size (probably unneeded) */
4827: rasp = sp->image; } /* pointer to image in subraster */
1.1 albertel 4828: /* -------------------------------------------------------------------------
4829: ( ) parens
4830: -------------------------------------------------------------------------- */
4831: if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
4832: || (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
4833: {
1.2 albertel 4834: if ( isdrawparen ) { /* draw the paren */
4835: int mywidth = min2(width,20); /* max width for ()'s */
4836: circle_raster ( rasp, /* embedded raster image */
1.1 albertel 4837: 0, 0, /* row0,col0 are upper-left corner */
4838: height-1, mywidth-1, /* row1,col1 are lower-right */
4839: thickness, /* line thickness is 1 pixel */
4840: (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
1.2 albertel 4841: isokay = 1; } /* set flag */
4842: else {
4843: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
4844: char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
4845: *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
4846: *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
4847: int baseht=0, barht=0, /* height of base=top+bot, bar */
4848: ibar=0, nbars=0; /* bar index, #bars between top&bot*/
4849: int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
4850: topfill=(isleft?0:0), botfill=(isleft?0:0),
4851: barfill=(isleft?0:7); /* alignment fillers */
4852: /* --- get pieces at largest size smaller than total height --- */
4853: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
4854: {
4855: /* --- get pieces at current test size --- */
4856: isokay = 1; /* check for all pieces */
4857: if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
4858: if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
4859: if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
4860: /* --- check sum of pieces against total desired height --- */
4861: if ( isokay ) { /* all pieces retrieved */
4862: baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
4863: barht = (symbar->image)->height; /* bar height */
4864: if ( baseht < height+5 ) break; /* largest base that's not too big */
4865: if ( symsize < 1 ) break; /* or smallest available base */
4866: } /* --- end-of-if(isokay) --- */
4867: /* --- free test pieces that were too big --- */
4868: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
4869: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
4870: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
4871: isokay = 0; /* nothing available */
4872: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
4873: } /* --- end-of-for(symsize) --- */
4874: /* --- construct brace from pieces --- */
4875: if ( isokay ) { /* we have the pieces */
4876: /* --- add alignment fillers --- */
1.3 albertel 4877: smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/
1.2 albertel 4878: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
4879: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
4880: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
4881: smashmargin = oldsmashmargin; /* reset smashmargin */
1.3 albertel 4882: isnocatspace = wasnocatspace; /* reset isnocatspace */
1.2 albertel 4883: /* --- #bars needed between top and bot --- */
4884: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
4885: /* --- stack pieces --- */
4886: sp = topsym; /* start with top piece */
4887: if ( nbars > 0 ) /* need nbars between top and bot */
4888: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4889: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
4890: delete_subraster(barsym); /* barsym no longer needed */
4891: } /* --- end-of-if(isokay) --- */
4892: } /* --- end-of-if/else(isdrawparen) --- */
1.1 albertel 4893: } /* --- end-of-if(left- or right-() paren wanted) --- */
4894: /* -------------------------------------------------------------------------
1.2 albertel 4895: { } braces
4896: -------------------------------------------------------------------------- */
4897: else
4898: if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
4899: || (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
4900: {
4901: int isleft = (lp!=NULL?1:0); /* true for left, false for right */
4902: char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
4903: *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
4904: *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
4905: *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
4906: int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
4907: ibar=0, nbars=0; /* bar index, #bars above,below mid*/
4908: int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
4909: topfill=(isleft?4:0), botfill=(isleft?4:0),
4910: midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
4911: /* --- get pieces at largest size smaller than total height --- */
4912: for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
4913: {
4914: /* --- get pieces at current test size --- */
4915: isokay = 1; /* check for all pieces */
4916: if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
4917: if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
4918: if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
4919: if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
4920: /* --- check sum of pieces against total desired height --- */
4921: if ( isokay ) { /* all pieces retrieved */
4922: baseht = (symtop->image)->height + (symbot->image)->height
4923: + (symmid->image)->height; /* top+bot+mid height */
4924: barht = (symbar->image)->height; /* bar height */
4925: if ( baseht < height+5 ) break; /* largest base that's not too big */
4926: if ( symsize < 1 ) break; /* or smallest available base */
4927: } /* --- end-of-if(isokay) --- */
4928: /* --- free test pieces that were too big --- */
4929: if ( symtop != NULL ) delete_subraster(symtop); /* free top */
4930: if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
4931: if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
4932: if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
4933: isokay = 0; /* nothing available */
4934: if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
4935: } /* --- end-of-for(symsize) --- */
4936: /* --- construct brace from pieces --- */
4937: if ( isokay ) { /* we have the pieces */
4938: /* --- add alignment fillers --- */
1.3 albertel 4939: smashmargin=0; isnocatspace=99; /*turn off rastcat smashing,space*/
1.2 albertel 4940: topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
4941: botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
4942: midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
4943: barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
4944: smashmargin = oldsmashmargin; /* reset smashmargin */
1.3 albertel 4945: isnocatspace = wasnocatspace; /* reset isnocatspace */
1.2 albertel 4946: /* --- #bars needed on each side of mid piece --- */
4947: nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
4948: /* --- stack pieces --- */
4949: sp = topsym; /* start with top piece */
4950: if ( nbars > 0 ) /* need nbars above middle */
4951: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4952: sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
4953: if ( nbars > 0 ) /* need nbars below middle */
4954: for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
4955: sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
4956: delete_subraster(barsym); /* barsym no longer needed */
4957: } /* --- end-of-if(isokay) --- */
4958: } /* --- end-of-if(left- or right-{} brace wanted) --- */
4959: /* -------------------------------------------------------------------------
1.1 albertel 4960: [ ] brackets
4961: -------------------------------------------------------------------------- */
4962: else
4963: if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
1.2 albertel 4964: || (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
4965: || (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
4966: || (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
4967: || (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
4968: || (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
4969: || (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
4970: || (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
4971: {
4972: /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
4973: int mywidth = min2(width,12), /* max width for horizontal bars */
4974: wthick = 1; /* thickness of top.bottom bars */
4975: thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
4976: if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
4977: wthick = thickness; /* same thickness for top/bot bar */
4978: if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
4979: rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
4980: if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
4981: rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
4982: if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
1.1 albertel 4983: rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
1.2 albertel 4984: if ( lp4 != NULL ) /* 2nd left vertical bar needed */
4985: rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
4986: if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
1.1 albertel 4987: rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
1.2 albertel 4988: if ( rp4 != NULL ) /* 2nd right vertical bar needed */
4989: rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
1.1 albertel 4990: isokay = 1; /* set flag */
4991: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
4992: /* -------------------------------------------------------------------------
4993: < > brackets
4994: -------------------------------------------------------------------------- */
4995: else
4996: if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
4997: || (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
4998: {
1.2 albertel 4999: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
5000: int mywidth = min2(width,12), /* max width for brackets */
5001: mythick = 1; /* all lines one pixel thick */
5002: thickness = (height<25?1:2); /* set line pixel thickness */
1.1 albertel 5003: if ( lp != NULL ) /* left < bracket wanted */
1.2 albertel 5004: { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
5005: if ( thickness>1 )
5006: line_raster(rasp,height/2,1,0,mywidth-1,mythick);
5007: line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
5008: if ( thickness>1 )
5009: line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
1.1 albertel 5010: if ( rp != NULL ) /* right > bracket wanted */
1.2 albertel 5011: { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
5012: if ( thickness>1 )
5013: line_raster(rasp,height/2,mywidth-2,0,0,mythick);
5014: line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
5015: if ( thickness>1 )
5016: line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
1.1 albertel 5017: isokay = 1; /* set flag */
5018: } /* --- end-of-if(left- or right-<> bracket wanted) --- */
5019: /* -------------------------------------------------------------------------
1.2 albertel 5020: / \ delimiters
5021: -------------------------------------------------------------------------- */
5022: else
5023: if ( (lp=strchr(symbol,'/')) != NULL /* left / wanted */
5024: || (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
5025: || (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
5026: {
5027: /* --- use line_raster( rasp, row0, col0, row1, col1, thickness ) --- */
5028: int mywidth = width; /* max width for / \ */
5029: thickness = 1; /* set line pixel thickness */
5030: if ( lp != NULL ) /* left / wanted */
5031: line_raster(rasp,0,mywidth-1,height-1,0,thickness);
5032: if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
5033: line_raster(rasp,0,0,height-1,mywidth-1,thickness);
5034: isokay = 1; /* set flag */
5035: } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
5036: /* -------------------------------------------------------------------------
5037: arrow delimiters
5038: -------------------------------------------------------------------------- */
5039: else
5040: if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
5041: {
5042: /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
5043: int mywidth = width; /* max width for / \ */
5044: int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
5045: || strstr(symbol,"Down")!=NULL); /* or a Down */
5046: int drctn = +1; /* init for uparrow */
5047: if ( strstr(symbol,"down")!=NULL /* down if we have down */
5048: || strstr(symbol,"Down")!=NULL ) /* or Down */
5049: { drctn = (-1); /* reset direction to down */
5050: if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
5051: || strstr(symbol,"Up")!=NULL ) /* and down or Down */
5052: drctn = 0; } /* reset direction to updown */
5053: sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
5054: if ( sp != NULL )
5055: { sp->type = IMAGERASTER; /* image */
5056: sp->symdef = NULL; /* not applicable for image */
5057: sp->baseline = height/2 + 2; /* is a little above center good? */
5058: sp->size = NORMALSIZE; /* size (probably unneeded) */
5059: isokay = 1; } /* set flag */
5060: } /* --- end-of-if(arrow delimiter wanted) --- */
5061: /* -------------------------------------------------------------------------
1.1 albertel 5062: \- for | | brackets or \= for || || brackets
5063: -------------------------------------------------------------------------- */
5064: else
5065: if ( (lp=strchr(symbol,'-')) != NULL /* left or right | bracket wanted */
5066: || (lp2=strchr(symbol,'|')) != NULL /* synonym for | bracket */
5067: || (rp=strchr(symbol,'=')) != NULL /* left or right || bracket wanted */
5068: || (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
5069: {
5070: /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
5071: int midcol = width/2; /* middle col, left of mid if even */
5072: if ( rp != NULL /* left or right || bracket wanted */
5073: || rp2 != NULL ) /* or || in standard tex notation */
1.2 albertel 5074: { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
1.1 albertel 5075: rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
5076: rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
5077: else /*nb, lp2 spuriously set if rp2 set*/
5078: if ( lp != NULL /* left or right | bracket wanted */
5079: || lp2 != NULL ) /* ditto for synomym */
1.2 albertel 5080: { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
1.1 albertel 5081: rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
5082: isokay = 1; /* set flag */
5083: } /* --- end-of-if(left- or right-[] bracket wanted) --- */
5084: /* -------------------------------------------------------------------------
5085: back to caller
5086: -------------------------------------------------------------------------- */
5087: end_of_job:
1.2 albertel 5088: if ( msgfp!=NULL && msglevel>=99 )
5089: fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
5090: (symbol==NULL?"null":symbol),isokay);
1.1 albertel 5091: if ( !isokay ) /* don't have requested delimiter */
1.2 albertel 5092: { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
1.1 albertel 5093: sp = NULL; } /* and signal error to caller */
5094: return ( sp ); /*back to caller with delim or NULL*/
5095: } /* --- end-of-function make_delim() --- */
5096:
5097:
5098: /* ==========================================================================
5099: * Function: texchar ( expression, chartoken )
5100: * Purpose: scans expression, returning either its first character,
5101: * or the next \sequence if that first char is \,
5102: * and a pointer to the first expression char past that.
5103: * --------------------------------------------------------------------------
5104: * Arguments: expression (I) char * to first char of null-terminated
5105: * string containing valid LaTeX expression
5106: * to be scanned
5107: * chartoken (O) char * to null-terminated string returning
5108: * either the first (non-whitespace) character
5109: * of expression if that char isn't \, or else
5110: * the \ and everything following it up to
5111: * the next non-alphabetic character (but at
5112: * least one char following the \ even if
5113: * it's non-alpha)
5114: * --------------------------------------------------------------------------
5115: * Returns: ( char * ) ptr to the first char of expression
5116: * past returned chartoken,
5117: * or NULL for any parsing error.
5118: * --------------------------------------------------------------------------
5119: * Notes: o Does *not* skip leading whitespace, but simply
5120: * returns any whitespace character as the next character.
5121: * ======================================================================= */
5122: /* --- entry point --- */
5123: char *texchar ( char *expression, char *chartoken )
5124: {
5125: /* -------------------------------------------------------------------------
5126: Allocations and Declarations
5127: -------------------------------------------------------------------------- */
5128: int esclen = 0, /*length of escape sequence*/
5129: maxesclen = 128; /* max len of esc sequence */
5130: char *ptoken = chartoken; /* ptr into chartoken */
5131: int iprefix = 0; /* prefix index */
5132: static char *prefixes[] = /*e.g., \big followed by ( */
5133: { /* "\\left", "\\right", */
5134: "\\big", "\\Big", "\\bigg", "\\Bigg",
5135: "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
5136: "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
1.3 albertel 5137: static char *starred[] = /* may be followed by * */
5138: { "\\hspace", "\\!", NULL };
1.1 albertel 5139: /* -------------------------------------------------------------------------
5140: just return the next char if it's not \
5141: -------------------------------------------------------------------------- */
5142: /* --- error check for end-of-string --- */
5143: *ptoken = '\000'; /* init in case of error */
5144: if ( expression == NULL ) return(NULL); /* nothing to scan */
5145: if ( *expression == '\000' ) return(NULL); /* nothing to scan */
5146: /* --- always returning first character (either \ or some other char) --- */
5147: *ptoken++ = *expression++; /* here's first character */
5148: /* --- if first char isn't \, then just return it to caller --- */
5149: if ( !isthischar(*(expression-1),ESCAPE) ) /* not a \, so return char */
5150: { *ptoken = '\000'; /* add a null terminator */
5151: goto end_of_job; } /* ptr past returned char */
5152: if ( *expression == '\000' ) /* \ is very last char */
5153: { *chartoken = '\000'; /* flush bad trailing \ */
5154: return(NULL); } /* and signal end-of-job */
5155: /* -------------------------------------------------------------------------
5156: we have an escape sequence, so return all alpha chars following \
5157: -------------------------------------------------------------------------- */
5158: /* --- accumulate chars until first non-alpha char found --- */
5159: for ( ; isalpha(*expression); esclen++ ) /* till first non-alpha... */
5160: { if ( esclen < maxesclen-3 ) /* more room in chartoken */
5161: *ptoken++ = *expression; /*copy alpha char, bump ptr*/
5162: expression++; } /* bump expression ptr */
5163: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
5164: *ptoken = '\000'; /* set null for compare */
5165: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
5166: if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
5167: { char nextchar[256]; int nextlen=0; /* texchar after prefix */
5168: skipwhite(expression); /* skip space after prefix*/
5169: expression = texchar(expression,nextchar); /* get nextchar */
5170: if ( (nextlen = strlen(nextchar)) > 0 ) /* #chars in nextchar */
5171: { strcpy(ptoken,nextchar); /* append nextchar */
5172: ptoken += strlen(nextchar); /* point to null terminator*/
5173: esclen += strlen(nextchar); } /* and bump escape length */
5174: break; } /* stop checking prefixes */
5175: /* --- every \ must be followed by at least one char, e.g., \[ --- */
5176: if ( esclen < 1 ) /* \ followed by non-alpha */
5177: *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
1.3 albertel 5178: *ptoken = '\000'; /* null-terminate token */
5179: /* --- check for \hspace* or other starred commands --- */
5180: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
5181: if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */
5182: if ( *expression == '*' ) /* follows by a * */
5183: { *ptoken++ = *expression++; /* copy * and bump ptr */
5184: *ptoken = '\000'; /* null-terminate token */
5185: break; } /* stop checking */
5186: /* --- respect spaces in text mode, except first space after \escape --- */
5187: if ( esclen >= 1 ) { /*only for alpha \sequences*/
1.2 albertel 5188: if ( istextmode ) /* in \rm or \it text mode */
5189: if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
5190: expression++; } /* so flush delim */
1.1 albertel 5191: /* --- back to caller --- */
5192: end_of_job:
5193: if ( msgfp!=NULL && msglevel>=999 )
5194: { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
5195: fflush(msgfp); }
5196: return ( expression ); /*ptr to 1st non-alpha char*/
5197: } /* --- end-of-function texchar() --- */
5198:
5199:
5200: /* ==========================================================================
5201: * Function: texsubexpr (expression,subexpr,maxsubsz,
5202: * left,right,isescape,isdelim)
5203: * Purpose: scans expression, returning everything between a balanced
5204: * left{...right} subexpression if the first non-whitespace
5205: * char of expression is an (escaped or unescaped) left{,
5206: * or just the next texchar() otherwise,
5207: * and a pointer to the first expression char past that.
5208: * --------------------------------------------------------------------------
5209: * Arguments: expression (I) char * to first char of null-terminated
5210: * string containing valid LaTeX expression
5211: * to be scanned
5212: * subexpr (O) char * to null-terminated string returning
5213: * either everything between a balanced {...}
5214: * subexpression if the first char is {,
5215: * or the next texchar() otherwise.
5216: * maxsubsz (I) int containing max #bytes returned
5217: * in subexpr buffer (0 means unlimited)
5218: * left (I) char * specifying allowable left delimiters
5219: * that begin subexpression, e.g., "{[(<"
5220: * right (I) char * specifying matching right delimiters
5221: * in the same order as left, e.g., "}])>"
5222: * isescape (I) int controlling whether escaped and/or
5223: * unescaped left,right are matched;
5224: * see isbrace() comments below for details.
5225: * isdelim (I) int containing true (non-zero) to return
5226: * the leading left and trailing right delims
5227: * (if any were found) along with subexpr,
5228: * or containing false=0 to return subexpr
5229: * without its delimiters
5230: * --------------------------------------------------------------------------
5231: * Returns: ( char * ) ptr to the first char of expression
5232: * past returned subexpr (see Notes),
5233: * or NULL for any parsing error.
5234: * --------------------------------------------------------------------------
5235: * Notes: o If subexpr is of the form left{...right},
5236: * the outer {}'s are returned as part of subexpr
5237: * if isdelim is true; if isdelim is false the {}'s aren't
5238: * returned. In either case the returned pointer is
5239: * *always* bumped past the closing right}, even if
5240: * that closing right} isn't returned in subexpr.
5241: * o If subexpr is not of the form left{...right},
5242: * the returned pointer is on the character immediately
5243: * following the last character returned in subexpr
5244: * o \. acts as LaTeX \right. and matches any \left(
5245: * And it also acts as a LaTeX \left. and matches any \right)
5246: * ======================================================================= */
5247: /* --- entry point --- */
5248: char *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
5249: char *left, char *right, int isescape, int isdelim )
5250: {
5251: /* -------------------------------------------------------------------------
5252: Allocations and Declarations
5253: -------------------------------------------------------------------------- */
5254: char *texchar(); /*next char (or \sequence) from expression*/
1.2 albertel 5255: char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
5256: rightdelim[256] = ")\000"; /* and matching right) */
1.1 albertel 5257: char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
1.2 albertel 5258: char *strtexchr(), *texleft(); /* check for \left, and get it */
1.1 albertel 5259: int gotescape = 0, /* true if leading char of expression is \ */
5260: prevescape = 0; /* while parsing, true if preceding char \ */
5261: int isbrace(); /* check for left,right braces */
5262: int isanyright = 1; /* true matches any right with left, (...] */
5263: int isleftdot = 0; /* true if left brace is a \. */
5264: int nestlevel = 1; /* current # of nested braces */
1.3 albertel 5265: int subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
1.1 albertel 5266: /* -------------------------------------------------------------------------
5267: skip leading whitespace and just return the next char if it's not {
5268: -------------------------------------------------------------------------- */
5269: /* --- skip leading whitespace and error check for end-of-string --- */
5270: *subexpr = '\000'; /* init in case of error */
5271: if ( expression == NULL ) return(NULL); /*can't dereference null ptr*/
5272: skipwhite(expression); /* leading whitespace gone */
5273: if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
5274: /* --- set maxsubsz --- */
1.3 albertel 5275: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2; /* input 0 means unlimited */
1.1 albertel 5276: /* --- check for escape --- */
5277: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
5278: gotescape = 1; /* so set flag accordingly */
1.2 albertel 5279: /* --- check for \left...\right --- */
5280: if ( gotescape ) /* begins with \ */
5281: if ( memcmp(expression+1,"left",4) ) /* and followed by left */
5282: if ( strchr(left,'l') != NULL ) /* caller wants \left's */
5283: if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
5284: { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
5285: (isdelim?NULL:leftdelim),rightdelim);
5286: if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
5287: return ( pright ); /*back to caller past \right*/
5288: } /* --- end-of-if(expression=="\\left") --- */
1.1 albertel 5289: /* --- if first char isn't left{ or script, just return it to caller --- */
1.3 albertel 5290: if ( !isbrace(expression,left,isescape) ) { /* not a left{ */
1.1 albertel 5291: if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
5292: return ( texchar(expression,subexpr) ); /* next char to caller */
5293: else /* --- kludge for super/subscripts to accommodate texscripts() --- */
5294: { *subexpr++ = *expression; /* signal script */
5295: *subexpr = '\000'; /* null-terminate subexpr */
1.3 albertel 5296: return ( expression ); } } /* leave script in stream */
1.1 albertel 5297: /* --- extract left and find matching right delimiter --- */
5298: *leftdelim = *(expression+gotescape); /* the left( in expression */
5299: if ( (gotescape && *leftdelim == '.') /* we have a left \. */
5300: || (gotescape && isanyright) ) /*or are matching any right*/
5301: { isleftdot = 1; /* so just set flag */
5302: *leftdelim = '\000'; } /* and reset leftdelim */
5303: else /* find matching \right */
5304: if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
5305: *rightdelim = right[(int)(leftptr-left)]; /* get the matching right) */
5306: else /* can't happen -- pgm bug */
5307: return ( NULL ); /*just signal eoj to caller*/
5308: /* -------------------------------------------------------------------------
5309: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
5310: -------------------------------------------------------------------------- */
5311: /* --- first initialize by bumping past left{ or \{ --- */
5312: if ( isdelim ) *subexpr++ = *expression++; /*caller wants { in subexpr*/
5313: else expression++; /* always bump past left{ */
1.3 albertel 5314: if ( gotescape ) { /*need to bump another char*/
1.1 albertel 5315: if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
1.3 albertel 5316: else expression++; } /* else just bump past it */
1.1 albertel 5317: /* --- set maximum size for numerical arguments --- */
5318: if ( 0 ) /* check turned on or off? */
5319: if ( !isescape && !isdelim ) /*looking for numerical arg*/
5320: maxsubsz = 96; /* set max arg size */
5321: /* --- search for matching right} --- */
5322: while ( 1 ) /*until balanced right} */
5323: {
5324: /* --- error check for end-of-string --- */
5325: if ( *expression == '\000' ) /* premature end-of-string */
5326: { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/
5327: { expression = origexpression; /* so end-of-string is error*/
5328: subexpr = origsubexpr; } /* so reset all ptrs */
1.3 albertel 5329: if ( isdelim ) { /* generate fake right */
1.1 albertel 5330: if ( gotescape ) /* need escaped right */
5331: { *subexpr++ = '\\'; /* set escape char */
5332: *subexpr++ = '.'; } /* and fake \right. */
5333: else /* escape not wanted */
1.3 albertel 5334: *subexpr++ = *rightdelim; } /* so fake actual right */
1.1 albertel 5335: *subexpr = '\000'; /* null-terminate subexpr */
5336: return ( expression ); } /* back with final token */
5337: /* --- check preceding char for escape --- */
5338: if ( isthischar(*(expression-1),ESCAPE) ) /* previous char was \ */
5339: prevescape = 1-prevescape; /* so flip escape flag */
5340: else prevescape = 0; /* or turn flag off */
5341: /* --- check for { and } (un/escaped as per leading left) --- */
5342: if ( gotescape == prevescape ) /* escaped iff leading is */
5343: { /* --- check for (closing) right delim and see if we're done --- */
5344: if ( isthischar(*expression,rightdelim) /* found a right} */
5345: || (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
5346: || (prevescape && isthischar(*expression,".")) ) /*or found \right. */
5347: if ( --nestlevel < 1 ) /*\right balances 1st \left*/
5348: { if ( isdelim ) /*caller wants } in subexpr*/
5349: *subexpr++ = *expression; /* so end subexpr with } */
5350: else /*check for \ before right}*/
5351: if ( prevescape ) /* have unwanted \ */
5352: *(subexpr-1) = '\000'; /* so replace it with null */
5353: *subexpr = '\000'; /* null-terminate subexpr */
5354: return ( expression+1 ); } /* back with char after } */
5355: /* --- check for (another) left{ --- */
5356: if ( isthischar(*expression,leftdelim) /* found another left{ */
5357: || (isleftdot && isthischar(*expression,left)) ) /* any left{ */
5358: nestlevel++;
5359: } /* --- end-of-if(gotescape==prevescape) --- */
5360: /* --- not done, so copy char to subexpr and continue with next char --- */
5361: if ( ++subsz < maxsubsz-5 ) /* more room in subexpr */
5362: *subexpr++ = *expression; /* so copy char and bump ptr*/
5363: expression++; /* bump expression ptr */
5364: } /* --- end-of-while(1) --- */
5365: } /* --- end-of-function texsubexpr() --- */
5366:
5367:
5368: /* ==========================================================================
1.2 albertel 5369: * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
5370: * Purpose: scans expression, starting after opening \left,
5371: * and returning ptr after matching closing \right.
5372: * Everything between is returned in subexpr, if given.
5373: * Likewise, if given, ldelim returns delimiter after \left
5374: * and rdelim returns delimiter after \right.
5375: * If ldelim is given, the returned subexpr doesn't include it.
5376: * If rdelim is given, the returned pointer is after that delim.
5377: * --------------------------------------------------------------------------
5378: * Arguments: expression (I) char * to first char of null-terminated
5379: * string immediately following opening \left
5380: * subexpr (O) char * to null-terminated string returning
5381: * either everything between balanced
5382: * \left ... \right. If leftdelim given,
5383: * subexpr does _not_ contain that delimiter.
5384: * maxsubsz (I) int containing max #bytes returned
5385: * in subexpr buffer (0 means unlimited)
5386: * ldelim (O) char * returning delimiter following
5387: * opening \left
5388: * rdelim (O) char * returning delimiter following
5389: * closing \right
5390: * --------------------------------------------------------------------------
5391: * Returns: ( char * ) ptr to the first char of expression
5392: * past closing \right, or past closing
5393: * right delimiter if rdelim!=NULL,
5394: * or NULL for any error.
5395: * --------------------------------------------------------------------------
5396: * Notes: o
5397: * ======================================================================= */
5398: /* --- entry point --- */
5399: char *texleft ( char *expression, char *subexpr, int maxsubsz,
5400: char *ldelim, char *rdelim )
5401: {
5402: /* -------------------------------------------------------------------------
5403: Allocations and Declarations
5404: -------------------------------------------------------------------------- */
5405: char *texchar(), /* get delims after \left,\right */
5406: *strtexchr(), *pright=expression; /* locate matching \right */
5407: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
5408: int sublen = 0; /* #chars between \left...\right */
5409: /* -------------------------------------------------------------------------
5410: initialization
5411: -------------------------------------------------------------------------- */
5412: /* --- init output --- */
5413: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
5414: if ( ldelim != NULL ) *ldelim = '\000'; /* init ldelim, if given */
5415: if ( rdelim != NULL ) *rdelim = '\000'; /* init rdelim, if given */
5416: /* --- check args --- */
5417: if ( expression == NULL ) goto end_of_job; /* no input supplied */
5418: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
5419: /* --- determine left delimiter --- */
5420: if ( ldelim != NULL ) /* caller wants left delim */
5421: { skipwhite(expression); /* interpret \left ( as \left( */
5422: expression = texchar(expression,ldelim); } /*delim from expression*/
5423: /* -------------------------------------------------------------------------
5424: locate \right balancing opening \left
5425: -------------------------------------------------------------------------- */
5426: /* --- first \right following \left --- */
5427: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
5428: != NULL ) { /* found it */
5429: /* --- find matching \right by pushing past any nested \left's --- */
5430: char *pleft = expression; /* start after first \left( */
5431: while ( 1 ) { /*break when matching \right found*/
5432: /* -- locate next nested \left if there is one --- */
5433: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
5434: == NULL ) break; /*no more, so matching \right found*/
5435: pleft += strlen(left); /* push ptr past \left token */
5436: if ( pleft >= pright ) break; /* not nested if \left after \right*/
5437: /* --- have nested \left, so push forward to next \right --- */
5438: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
5439: == NULL ) break; /* ran out of \right's */
5440: } /* --- end-of-while(1) --- */
5441: } /* --- end-of-if(pright!=NULL) --- */
5442: /* --- set subexpression length, push pright past \right --- */
5443: if ( pright != (char *)NULL ) /* found matching \right */
5444: { sublen = (int)(pright-expression); /* #chars between \left...\right */
5445: pright += strlen(right); } /* so push pright past \right */
5446: /* -------------------------------------------------------------------------
5447: get rightdelim and subexpr between \left...\right
5448: -------------------------------------------------------------------------- */
5449: /* --- get delimiter following \right --- */
1.3 albertel 5450: if ( rdelim != NULL ) { /* caller wants right delim */
1.2 albertel 5451: if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
5452: { strcpy(rdelim,"."); /* set default \right. */
5453: sublen = strlen(expression); /* use entire remaining expression */
5454: pright = expression + sublen; } /* and push pright to end-of-string*/
5455: else /* have explicit matching \right */
5456: { skipwhite(pright); /* interpret \right ) as \right) */
5457: pright = texchar(pright,rdelim); /* pull delim from expression */
1.3 albertel 5458: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
1.2 albertel 5459: /* --- get subexpression between \left...\right --- */
5460: if ( sublen > 0 ) /* have subexpr */
5461: if ( subexpr != NULL ) { /* and caller wants it */
5462: if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
5463: memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
5464: subexpr[sublen] = '\000'; } /* null-terminate subexpr */
5465: end_of_job:
5466: if ( msglevel>=99 && msgfp!=NULL )
5467: { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
5468: (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
5469: (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
5470: return ( pright );
5471: } /* --- end-of-function texleft --- */
5472:
5473:
5474: /* ==========================================================================
1.1 albertel 5475: * Function: texscripts ( expression, subscript, superscript, which )
5476: * Purpose: scans expression, returning subscript and/or superscript
5477: * if expression is of the form _x^y or ^{x}_{y},
5478: * or any (valid LaTeX) permutation of the above,
5479: * and a pointer to the first expression char past "scripts"
5480: * --------------------------------------------------------------------------
5481: * Arguments: expression (I) char * to first char of null-terminated
5482: * string containing valid LaTeX expression
5483: * to be scanned
5484: * subscript (O) char * to null-terminated string returning
5485: * subscript (without _), if found, or "\000"
5486: * superscript (O) char * to null-terminated string returning
5487: * superscript (without ^), if found, or "\000"
5488: * which (I) int containing 1 for subscript only,
5489: * 2 for superscript only, >=3 for either/both
5490: * --------------------------------------------------------------------------
5491: * Returns: ( char * ) ptr to the first char of expression
5492: * past returned "scripts" (unchanged
5493: * except for skipped whitespace if
5494: * neither subscript nor superscript found),
5495: * or NULL for any parsing error.
5496: * --------------------------------------------------------------------------
5497: * Notes: o an input expression like ^a^b_c will return superscript="b",
5498: * i.e., totally ignoring all but the last "script" encountered
5499: * ======================================================================= */
5500: /* --- entry point --- */
5501: char *texscripts ( char *expression, char *subscript,
5502: char *superscript, int which )
5503: {
5504: /* -------------------------------------------------------------------------
5505: Allocations and Declarations
5506: -------------------------------------------------------------------------- */
5507: char *texsubexpr(); /* next subexpression from expression */
5508: int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
5509: /* -------------------------------------------------------------------------
1.2 albertel 5510: init "scripts"
1.1 albertel 5511: -------------------------------------------------------------------------- */
1.2 albertel 5512: if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
5513: if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
1.1 albertel 5514: /* -------------------------------------------------------------------------
5515: get subscript and/or superscript from expression
5516: -------------------------------------------------------------------------- */
1.2 albertel 5517: while ( expression != NULL ) {
5518: skipwhite(expression); /* leading whitespace gone */
5519: if ( *expression == '\000' ) return(expression); /* nothing left to scan */
1.1 albertel 5520: if ( isthischar(*expression,SUBSCRIPT) /* found _ */
5521: && (which==1 || which>2 ) ) /* and caller wants it */
5522: { if ( gotsub /* found 2nd subscript */
5523: || subscript == NULL ) break; /* or no subscript buffer */
5524: gotsub = 1; /* set subscript flag */
5525: expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
5526: else /* no _, check for ^ */
5527: if ( isthischar(*expression,SUPERSCRIPT) /* found ^ */
5528: && which>=2 ) /* and caller wants it */
5529: { if ( gotsup /* found 2nd superscript */
5530: || superscript == NULL ) break; /* or no superscript buffer*/
5531: gotsup = 1; /* set superscript flag */
5532: expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
5533: else /* neither _ nor ^ */
5534: return ( expression ); /*return ptr past "scripts"*/
1.2 albertel 5535: } /* --- end-of-while(expression!=NULL) --- */
1.1 albertel 5536: return ( expression );
5537: } /* --- end-of-function texscripts() --- */
5538:
5539:
5540: /* ==========================================================================
5541: * Function: isbrace ( expression, braces, isescape )
5542: * Purpose: checks leading char(s) of expression for a brace,
5543: * either escaped or unescaped depending on isescape,
5544: * except that { and } are always matched, if they're
5545: * in braces, regardless of isescape.
5546: * --------------------------------------------------------------------------
5547: * Arguments: expression (I) char * to first char of null-terminated
5548: * string containing a valid LaTeX expression
5549: * whose leading char(s) are checked for braces
5550: * that begin subexpression, e.g., "{[(<"
5551: * braces (I) char * specifying matching brace delimiters
5552: * to be checked for, e.g., "{[(<" or "}])>"
5553: * isescape (I) int containing 0 to match only unescaped
5554: * braces, e.g., (...) or {...}, etc,
5555: * or containing 1 to match only escaped
5556: * braces, e.g., \(...\) or \[...\], etc,
5557: * or containing 2 to match either.
5558: * But note: if {,} are in braces
5559: * then they're *always* matched whether
5560: * escaped or not, regardless of isescape.
5561: * --------------------------------------------------------------------------
5562: * Returns: ( int ) 1 if the leading char(s) of expression
5563: * is a brace, or 0 if not.
5564: * --------------------------------------------------------------------------
5565: * Notes: o
5566: * ======================================================================= */
5567: /* --- entry point --- */
5568: int isbrace ( char *expression, char *braces, int isescape )
5569: {
5570: /* -------------------------------------------------------------------------
5571: Allocations and Declarations
5572: -------------------------------------------------------------------------- */
5573: int gotescape = 0, /* true if leading char is an escape */
5574: gotbrace = 0; /*true if first non-escape char is a brace*/
5575: /* -------------------------------------------------------------------------
5576: check for brace
5577: -------------------------------------------------------------------------- */
1.3 albertel 5578: /* --- first check for end-of-string or \= ligature --- */
5579: if ( *expression == '\000' /* nothing to check */
5580: || isligature ) goto end_of_job; /* have a \= ligature */
1.1 albertel 5581: /* --- check leading char for escape --- */
5582: if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
5583: { gotescape = 1; /* so set flag accordingly */
5584: expression++; } /* and bump past escape */
5585: /* --- check (maybe next char) for brace --- */
5586: if ( isthischar(*expression,braces) ) /* expression is braced */
5587: gotbrace = 1; /* so set flag accordingly */
5588: if ( gotescape && *expression == '.' ) /* \. matches any brace */
5589: gotbrace = 1; /* set flag */
5590: /* --- check for TeX brace { or } --- */
5591: if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
5592: if ( isescape ) isescape = 2; /* reset escape flag */
5593: /* -------------------------------------------------------------------------
5594: back to caller
5595: -------------------------------------------------------------------------- */
1.3 albertel 5596: end_of_job:
5597: if ( msglevel>=999 && msgfp!=NULL )
5598: { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
5599: expression,gotbrace,isligature); fflush(msgfp); }
5600: if ( gotbrace && /* found a brace */
1.1 albertel 5601: ( isescape==2 || /* escape irrelevant */
5602: gotescape==isescape ) /* un/escaped as requested */
5603: ) return ( 1 ); return ( 0 ); /* return 1,0 accordingly */
5604: } /* --- end-of-function isbrace() --- */
5605:
5606:
5607: /* ==========================================================================
5608: * Function: preamble ( expression, size, subexpr )
5609: * Purpose: parses $-terminated preamble, if present, at beginning
5610: * of expression, re-setting size if necessary, and
5611: * returning any other parameters besides size in subexpr.
5612: * --------------------------------------------------------------------------
5613: * Arguments: expression (I) char * to first char of null-terminated
5614: * string containing LaTeX expression possibly
5615: * preceded by $-terminated preamble
5616: * size (I/O) int * containing 0-4 default font size,
5617: * and returning size modified by first
5618: * preamble parameter (or unchanged)
5619: * subexpr(O) char * returning any remaining preamble
5620: * parameters past size
5621: * --------------------------------------------------------------------------
5622: * Returns: ( char * ) ptr to first char past preamble in expression
5623: * or NULL for any parsing error.
5624: * --------------------------------------------------------------------------
5625: * Notes: o size can be any number >=0. If preceded by + or -, it's
5626: * interpreted as an increment to input size; otherwise
5627: * it's interpreted as the size.
5628: * o if subexpr is passed as NULL ptr, then returned expression
5629: * ptr will have "flushed" and preamble parameters after size
5630: * ======================================================================= */
5631: /* --- entry point --- */
5632: char *preamble ( char *expression, int *size, char *subexpr )
5633: {
5634: /* -------------------------------------------------------------------------
5635: Allocations and Declarations
5636: -------------------------------------------------------------------------- */
5637: char pretext[512], *prep=expression, /*pream from expression, ptr into it*/
5638: *dollar, *comma; /* preamble delimiters */
5639: int prelen = 0, /* preamble length */
5640: sizevalue = 0, /* value of size parameter */
5641: isfontsize = 0, /*true if leading fontsize present*/
5642: isdelta = 0; /*true to increment passed size arg*/
5643: /* -------------------------------------------------------------------------
5644: initialization
5645: -------------------------------------------------------------------------- */
5646: if ( subexpr != NULL ) /* caller passed us an address */
5647: *subexpr = '\000'; /* so init assuming no preamble */
5648: if ( expression == NULL ) goto end_of_job; /* no input */
5649: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
5650: /* -------------------------------------------------------------------------
5651: process preamble if present
5652: -------------------------------------------------------------------------- */
5653: /*process_preamble:*/
5654: if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
1.3 albertel 5655: != NULL ) { /* found embedded $ */
1.1 albertel 5656: if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
5657: > 0 ) { /* must have preamble preceding $ */
5658: if ( prelen < 65 ) { /* too long for a prefix */
5659: memcpy(pretext,expression,prelen); /* local copy of preamble */
5660: pretext[prelen] = '\000'; /* null-terminated */
5661: if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
5662: && strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
5663: /* --- skip any leading whitespace --- */
5664: prep = pretext; /* start at beginning of preamble */
5665: skipwhite(prep); /* skip any leading white space */
5666: /* --- check for embedded , or leading +/- (either signalling size) --- */
5667: if ( isthischar(*prep,"+-") ) /* have leading + or - */
5668: isdelta = 1; /* so use size value as increment */
5669: comma = strchr(pretext,','); /* , signals leading size param */
5670: /* --- process leading size parameter if present --- */
5671: if ( comma != NULL /* size param explicitly signalled */
5672: || isdelta || isdigit(*prep) ) { /* or inferred implicitly */
5673: /* --- parse size parameter and reset size accordingly --- */
5674: if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
5675: sizevalue = atoi(prep); /* convert size string to integer */
5676: if ( size != NULL ) /* caller passed address for size */
5677: *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
5678: /* --- finally, set flag and shift size parameter out of preamble --- */
5679: isfontsize = 1; /*set flag showing font size present*/
1.5 ! raeburn 5680: if ( comma != NULL ) /*2/15/12-isn't this superfluous???*/
! 5681: {strsqueezep(pretext,comma+1);} /* squeeze out leading size param */
1.1 albertel 5682: } /* --- end-of-if(comma!=NULL||etc) --- */
5683: /* --- copy any preamble params following size to caller's subexpr --- */
5684: if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
5685: if ( subexpr != NULL ) /* caller passed us an address */
5686: strcpy(subexpr,pretext); /*so return extra params to caller*/
5687: /* --- finally, set prep to shift preamble out of expression --- */
5688: prep = expression + prelen+1; /* set prep past $ in expression */
5689: } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
5690: } /* --- end-of-if(prelen<65) --- */
5691: } /* --- end-of-if(prelen>0) --- */
5692: else { /* $ is first char of expression */
5693: int ndollars = 0; /* number of $...$ pairs removed */
5694: prep = expression; /* start at beginning of expression*/
5695: while ( *prep == '$' ) { /* remove all matching $...$'s */
5696: int explen = strlen(prep)-1; /* index of last char in expression*/
5697: if ( explen < 2 ) break; /* no $...$'s left to remove */
5698: if ( prep[explen] != '$' ) break; /* unmatched $ */
5699: prep[explen] = '\000'; /* remove trailing $ */
5700: prep++; /* and remove matching leading $ */
5701: ndollars++; /* count another pair removed */
5702: } /* --- end-of-while(*prep=='$') --- */
5703: ispreambledollars = ndollars; /* set flag to fix \displaystyle */
5704: if ( ndollars == 1 ) /* user submitted $...$ expression */
5705: isdisplaystyle = 0; /* so set \textstyle */
5706: if ( ndollars > 1 ) /* user submitted $$...$$ */
5707: isdisplaystyle = 2; /* so set \displaystyle */
5708: /*goto process_preamble;*/ /*check for preamble after leading $*/
5709: } /* --- end-of-if/else(prelen>0) --- */
1.3 albertel 5710: } /* --- end-of-if(dollar!=NULL) --- */
1.1 albertel 5711: /* -------------------------------------------------------------------------
5712: back to caller
5713: -------------------------------------------------------------------------- */
5714: end_of_job:
5715: return ( prep ); /*expression, or ptr past preamble*/
5716: } /* --- end-of-function preamble() --- */
5717:
5718:
5719: /* ==========================================================================
5720: * Function: mimeprep ( expression )
5721: * Purpose: preprocessor for mimeTeX input, e.g.,
5722: * (a) removes comments,
5723: * (b) converts \left( to \( and \right) to \),
5724: * (c) xlates &html; special chars to equivalent latex
5725: * Should only be called once (after unescape_url())
5726: * --------------------------------------------------------------------------
5727: * Arguments: expression (I/O) char * to first char of null-terminated
5728: * string containing mimeTeX/LaTeX expression,
5729: * and returning preprocessed string
5730: * --------------------------------------------------------------------------
5731: * Returns: ( char * ) ptr to input expression,
5732: * or NULL for any parsing error.
5733: * --------------------------------------------------------------------------
5734: * Notes: o
5735: * ======================================================================= */
5736: /* --- entry point --- */
5737: char *mimeprep ( char *expression )
5738: {
5739: /* -------------------------------------------------------------------------
5740: Allocations and Declarations
5741: -------------------------------------------------------------------------- */
5742: char *expptr=expression, /* ptr within expression */
5743: *tokptr=NULL, /*ptr to token found in expression*/
5744: *texsubexpr(), argval[8192]; /*parse for macro args after token*/
5745: char *strchange(); /* change leading chars of string */
1.5 ! raeburn 5746: int strreplace(); /* replace nnn with actual num, etc*/
1.3 albertel 5747: char *strwstr(); /*use strwstr() instead of strstr()*/
1.1 albertel 5748: char *findbraces(); /*find left { and right } for \atop*/
5749: int idelim=0, /* left- or right-index */
5750: isymbol=0; /*symbols[],rightcomment[],etc index*/
5751: int xlateleft = 0; /* true to xlate \left and \right */
5752: /* ---
5753: * comments
5754: * -------- */
5755: char *leftptr=NULL; /* find leftcomment in expression */
5756: static char *leftcomment = "%%", /* open comment */
5757: *rightcomment[] = {"\n", "%%", NULL}; /* close comments */
5758: /* ---
5759: * special long (more than 1-char) \left and \right delimiters
5760: * ----------------------------------------------------------- */
5761: static char *leftfrom[] = /* xlate any \left suffix... */
5762: { "\\|", /* \left\| */
5763: "\\{", /* \left\{ */
5764: "\\langle", /* \left\langle */
5765: NULL } ; /* --- end-of-leftfrom[] --- */
5766: static char *leftto[] = /* ...to this instead */
5767: { "=", /* = */
5768: "{", /* { */
5769: "<", /* < */
5770: NULL } ; /* --- end-of-leftto[] --- */
5771: static char *rightfrom[] = /* xlate any \right suffix... */
5772: { "\\|", /* \right\| */
5773: "\\}", /* \right\} */
5774: "\\rangle", /* \right\rangle */
5775: NULL } ; /* --- end-of-rightfrom[] --- */
5776: static char *rightto[] = /* ...to this instead */
5777: { "=", /* = */
5778: "}", /* } */
5779: ">", /* > */
5780: NULL } ; /* --- end-of-rightto[] --- */
5781: /* ---
5782: * { \atop }-like commands
5783: * ----------------------- */
5784: char *atopsym=NULL; /* atopcommands[isymbol] */
5785: static char *atopcommands[] = /* list of {a+b\command c+d}'s */
5786: { "\\over", /* plain tex for \frac */
5787: "\\choose", /* binomial coefficient */
5788: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
5789: "\\atop",
5790: #endif
5791: NULL } ; /* --- end-of-atopcommands[] --- */
5792: static char *atopdelims[] = /* delims for atopcommands[] */
5793: { NULL, NULL, /* \\over has no delims */
5794: "\\left(", "\\right)", /* \\choose has ( ) delims*/
5795: #ifndef NOATOP /*noatop preserves old mimeTeX rule*/
5796: NULL, NULL, /* \\atop has no delims */
5797: #endif
5798: NULL, NULL } ; /* --- end-of-atopdelims[] --- */
5799: /* ---
5800: * html special/escape chars converted to latex equivalents
5801: * -------------------------------------------------------- */
5802: char *htmlsym=NULL; /* symbols[isymbol].html */
5803: static struct { char *html; char *args; char *latex; } symbols[] =
1.5 ! raeburn 5804: { /* --------------------------------------------
1.1 albertel 5805: user-supplied newcommands
1.5 ! raeburn 5806: -------------------------------------------- */
! 5807: #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */
! 5808: #include NEWCOMMANDS
! 5809: #endif
! 5810: /* --------------------------------------------
! 5811: Specials termchar value...
! 5812: -------------------------------------------- */
! 5813: { "\\version", NULL, "{\\small\\red\\text \\fbox{\\begin{gather}"
! 5814: "mime\\TeX version \\versionnumber \\\\"
! 5815: "last revised \\revisiondate \\\\ \\copyrighttext \\\\"
! 5816: "see \\homepagetext for details \\end{gather}}}" },
! 5817: { "\\copyright", NULL,
! 5818: "{\\small\\red\\text \\fbox{\\begin{gather}"
! 5819: "mimeTeX \\copyrighttext \\\\"
! 5820: "see \\homepagetext for details \\end{gather}}}" },
! 5821: { "\\versionnumber", NULL, "{\\text " VERSION "}" },
! 5822: { "\\revisiondate", NULL, "{\\text " REVISIONDATE "}" },
! 5823: { "\\copyrighttext", NULL, "{\\text " COPYRIGHTTEXT ".}" },
! 5824: { "\\homepagetext", NULL,
! 5825: "{\\text http://www.forkosh.com/mimetex.html}" },
! 5826: /* --------------------------------------------
! 5827: Cyrillic termchar mimeTeX equivalent...
! 5828: -------------------------------------------- */
1.3 albertel 5829: { "\\\'G", "embed\\","{\\acute{G}}" },
5830: { "\\\'g", "embed\\","{\\acute{g}}" },
5831: { "\\\'K", "embed\\","{\\acute{K}}" },
5832: { "\\\'k", "embed\\","{\\acute{k}}" },
5833: { "\\u U", "embed\\","{\\breve{U}}" },
5834: { "\\u u", "embed\\","{\\breve{u}}" },
5835: /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/
5836: /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
5837: { "\\\"I", "embed\\","{\\ddot{\\=I}}" },
5838: { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
1.5 ! raeburn 5839: /* --------------------------------------------
! 5840: LaTeX Macro #args,default template...
! 5841: -------------------------------------------- */
1.1 albertel 5842: { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
1.2 albertel 5843: { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
5844: { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
5845: { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
5846: { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
1.3 albertel 5847: { "\\buildrel","3", "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
1.1 albertel 5848: { "\\overset", NULL, "\\stackrel" }, /* just an alias */
5849: { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
1.5 ! raeburn 5850: { "\\dfrac", "2", "{\\frac{#1}{#2}}" },
! 5851: { "\\binom", "2", "{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" },
! 5852: { "\\aangle","26", "{\\boxaccent{#1}{#2}}" },
! 5853: { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/
! 5854: { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" },
! 5855: /* --------------------------------------------
! 5856: html char termchar LaTeX equivalent...
! 5857: -------------------------------------------- */
1.1 albertel 5858: { """, ";", "\"" }, /* " is first, " */
5859: { "&", ";", "&" },
5860: { "<", ";", "<" },
5861: { ">", ";", ">" },
1.5 ! raeburn 5862: /*{ "\", ";", "\\" },*/ /* backslash */
1.3 albertel 5863: { "&backslash",";", "\\" },
1.1 albertel 5864: { " ", ";", "~" },
5865: { "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
5866: { "¦", ";", "|" },
5867: { "±", ";", "\\pm" },
5868: { "²", ";", "{{}^2}" },
5869: { "³", ";", "{{}^3}" },
5870: { "µ", ";", "\\mu" },
5871: { "¹", ";", "{{}^1}" },
5872: { "¼", ";", "{\\frac14}" },
5873: { "½", ";", "{\\frac12}" },
5874: { "¾", ";", "{\\frac34}" },
5875: { "¿", ";", "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
5876: { "Â", ";", "{\\rm~\\hat~A}" },
5877: { "Ã", ";", "{\\rm~\\tilde~A}" },
5878: { "Ä", ";", "{\\rm~\\ddot~A}" },
5879: { "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
5880: { "ã", ";", "{\\rm~\\tilde~a}" },
1.5 ! raeburn 5881: { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
! 5882: { "&#", ";", "{[\\&\\#nnn?]}" }, /* all other explicit &#nnn's */
! 5883: /* --------------------------------------------
! 5884: html tag termchar LaTeX equivalent...
! 5885: -------------------------------------------- */
! 5886: { "< br >", "embed\\i", "\\\\" },
! 5887: { "< br / >", "embed\\i", "\\\\" },
! 5888: { "< dd >", "embed\\i", " \000" },
! 5889: { "< / dd >", "embed\\i", " \000" },
! 5890: { "< dl >", "embed\\i", " \000" },
! 5891: { "< / dl >", "embed\\i", " \000" },
! 5892: { "< p >", "embed\\i", " \000" },
! 5893: { "< / p >", "embed\\i", " \000" },
! 5894: /* --------------------------------------------
! 5895: garbage termchar LaTeX equivalent...
! 5896: -------------------------------------------- */
! 5897: { "< tex >", "embed\\i", " \000" },
! 5898: { "< / tex >", "embed\\i", " \000" },
! 5899: /* --------------------------------------------
1.1 albertel 5900: LaTeX termchar mimeTeX equivalent...
1.5 ! raeburn 5901: -------------------------------------------- */
1.1 albertel 5902: { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
5903: { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
5904: { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
5905: { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
1.2 albertel 5906: { "\\dots", NULL, "{\\cdots}" },
1.1 albertel 5907: { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
5908: { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
1.5 ! raeburn 5909: { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4."
! 5910: "\\hspace1\\raisebox0.}"},
1.1 albertel 5911: { "\\notin", NULL, "{\\not\\in}" },
5912: { "\\neq", NULL, "{\\not=}" },
1.2 albertel 5913: { "\\ne", NULL, "{\\not=}" },
1.5 ! raeburn 5914: { "\\mapsto", NULL, "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
1.1 albertel 5915: { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
5916: { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
1.2 albertel 5917: { "\\textcelsius", NULL, "{\\textdegree C}"},
5918: { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
1.1 albertel 5919: { "\\cr", NULL, "\\\\" },
1.5 ! raeburn 5920: /*{ "\\colon", NULL, "{:}" },*/
1.2 albertel 5921: { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
1.1 albertel 5922: { "\\iint", NULL, "{\\int\\int}\\limits" },
5923: { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
1.2 albertel 5924: { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
1.5 ! raeburn 5925: { "\\_", "embed","{\\underline{\\ }}" }, /* displayed underscore */
1.1 albertel 5926: { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
5927: { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
1.2 albertel 5928: { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
5929: { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
5930: { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
5931: { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
5932: { "\'", "embed","^{\\fs{-1}\\prime}" },
1.1 albertel 5933: { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
1.2 albertel 5934: { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
1.1 albertel 5935: { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
5936: { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
5937: { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
5938: { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
5939: { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
1.2 albertel 5940: { "\\cancel",NULL, "\\Not" },
1.1 albertel 5941: { "\\hhline",NULL, "\\Hline" },
5942: { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
1.5 ! raeburn 5943: /* -----------------------------------------------------------------------
! 5944: As per emails with Zbigniew Fiedorowicz <fiedorow@math.ohio-state.edu>
1.1 albertel 5945: "Algebra Syntax" termchar mimeTeX/LaTeX equivalent...
1.5 ! raeburn 5946: ----------------------------------------------------------------------- */
1.1 albertel 5947: { "sqrt", "1", "{\\sqrt{#1}}" },
5948: { "sin", "1", "{\\sin{#1}}" },
5949: { "cos", "1", "{\\cos{#1}}" },
5950: { "asin", "1", "{\\sin^{-1}{#1}}" },
5951: { "acos", "1", "{\\cos^{-1}{#1}}" },
1.2 albertel 5952: { "exp", "1", "{{\\rm~e}^{#1}}" },
1.1 albertel 5953: { "det", "1", "{\\left|{#1}\\right|}" },
1.5 ! raeburn 5954: /* --------------------------------------------
! 5955: LaTeX Constant termchar value...
! 5956: -------------------------------------------- */
! 5957: { "\\thinspace", NULL, "\\," },
! 5958: { "\\thinmathspace", NULL, "\\," },
1.1 albertel 5959: { "\\textwidth", NULL, "400" },
1.5 ! raeburn 5960: /* --- end-of-table indicator --- */
1.1 albertel 5961: { NULL, NULL, NULL }
5962: } ; /* --- end-of-symbols[] --- */
1.5 ! raeburn 5963: /* ---
! 5964: * html &#nn chars converted to latex equivalents
! 5965: * ---------------------------------------------- */
! 5966: int htmlnum=0; /* numbers[inum].html */
! 5967: static struct { int html; char *latex; } numbers[] =
! 5968: { /* ---------------------------------------
! 5969: html num LaTeX equivalent...
! 5970: --------------------------------------- */
! 5971: { 9, " " }, /* horizontal tab */
! 5972: { 10, " " }, /* line feed */
! 5973: { 13, " " }, /* carriage return */
! 5974: { 32, " " }, /* space */
! 5975: { 33, "!" }, /* exclamation point */
! 5976: { 34, "\"" }, /* " */
! 5977: { 35, "#" }, /* hash mark */
! 5978: { 36, "$" }, /* dollar */
! 5979: { 37, "%" }, /* percent */
! 5980: { 38, "&" }, /* & */
! 5981: { 39, "\'" }, /* apostrophe (single quote) */
! 5982: { 40, ")" }, /* left parenthesis */
! 5983: { 41, ")" }, /* right parenthesis */
! 5984: { 42, "*" }, /* asterisk */
! 5985: { 43, "+" }, /* plus */
! 5986: { 44, "," }, /* comma */
! 5987: { 45, "-" }, /* hyphen (minus) */
! 5988: { 46, "." }, /* period */
! 5989: { 47, "/" }, /* slash */
! 5990: { 58, ":" }, /* colon */
! 5991: { 59, ";" }, /* semicolon */
! 5992: { 60, "<" }, /* < */
! 5993: { 61, "=" }, /* = */
! 5994: { 62, ">" }, /* > */
! 5995: { 63, "\?" }, /* question mark */
! 5996: { 64, "@" }, /* commercial at sign */
! 5997: { 91, "[" }, /* left square bracket */
! 5998: { 92, "\\" }, /* backslash */
! 5999: { 93, "]" }, /* right square bracket */
! 6000: { 94, "^" }, /* caret */
! 6001: { 95, "_" }, /* underscore */
! 6002: { 96, "`" }, /* grave accent */
! 6003: { 123, "{" }, /* left curly brace */
! 6004: { 124, "|" }, /* vertical bar */
! 6005: { 125, "}" }, /* right curly brace */
! 6006: { 126, "~" }, /* tilde */
! 6007: { 160, "~" }, /* (use tilde for latex) */
! 6008: { 166, "|" }, /* ¦ (broken vertical bar) */
! 6009: { 173, "-" }, /* ­ (soft hyphen) */
! 6010: { 177, "{\\pm}" }, /* ± (plus or minus) */
! 6011: { 215, "{\\times}" }, /* × (plus or minus) */
! 6012: { -999, NULL }
! 6013: } ; /* --- end-of-numbers[] --- */
1.1 albertel 6014: /* -------------------------------------------------------------------------
6015: first remove comments
6016: -------------------------------------------------------------------------- */
6017: expptr = expression; /* start search at beginning */
6018: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
6019: {
6020: char *rightsym=NULL; /* rightcomment[isymbol] */
6021: expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
6022: /* --- check for any closing rightcomment, in given precedent order --- */
6023: if ( *expptr != '\000' ) /*have chars after this leftcomment*/
6024: for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
6025: if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
6026: { tokptr += strlen(rightsym); /* first char after rightcomment */
6027: if ( *tokptr == '\000' ) /*nothing after this rightcomment*/
6028: { *leftptr = '\000'; /*so terminate expr at leftcomment*/
6029: break; } /* and stop looking for comments */
6030: *leftptr = '~'; /* replace entire comment by ~ */
1.5 ! raeburn 6031: strsqueezep(leftptr+1,tokptr); /* squeeze out comment */
1.1 albertel 6032: goto next_comment; } /* stop looking for rightcomment */
6033: /* --- no rightcomment after opening leftcomment --- */
6034: *leftptr = '\000'; /* so terminate expression */
6035: /* --- resume search past squeezed-out comment --- */
6036: next_comment:
6037: if ( *leftptr == '\000' ) break; /* reached end of expression */
6038: expptr = leftptr+1; /*resume search after this comment*/
6039: } /* --- end-of-while(leftptr!=NULL) --- */
6040: /* -------------------------------------------------------------------------
6041: run thru table, converting all occurrences of each macro to its expansion
6042: -------------------------------------------------------------------------- */
6043: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
6044: {
6045: int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
6046: int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
1.3 albertel 6047: int isembedded = 0, /* true to xlate even if embedded */
1.5 ! raeburn 6048: istag=0, isamp=0, /* true for <tag>, &char; symbols */
1.3 albertel 6049: isstrwstr = 0, /* true to use strwstr() */
6050: wstrlen = 0; /* length of strwstr() match */
1.1 albertel 6051: char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
1.3 albertel 6052: char embedkeywd[99] = "embed", /* keyword to signal embedded token*/
6053: embedterm = '\000'; /* char immediately after embed */
6054: int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */
1.1 albertel 6055: char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
6056: *htmlterm = args, /*if *args nonumeric, then html term*/
1.5 ! raeburn 6057: *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
! 6058: errorsym[256]; /*or latexsym may point to error msg*/
1.1 albertel 6059: char abuff[8192]; int iarg,nargs=0; /* macro expansion params */
1.3 albertel 6060: char wstrwhite[99]; /* whitespace chars for strwstr() */
1.5 ! raeburn 6061: skipwhite(htmlsym); /*skip any bogus leading whitespace*/
! 6062: htmllen = strlen(htmlsym); /* reset length of html token */
! 6063: istag = (isthischar(*htmlsym,"<")?1:0); /* html <tag> starts with < */
! 6064: isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
1.1 albertel 6065: if ( args != NULL ) /*we have args (or htmlterm) param*/
1.3 albertel 6066: if ( *args != '\000' ) { /* and it's not an empty string */
1.1 albertel 6067: if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
6068: { htmlterm = NULL; /* if so, then we have no htmlterm */
6069: *abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */
6070: nargs = atoi(abuff); } /* interpret #args to numeric */
1.3 albertel 6071: else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
1.5 ! raeburn 6072: { int arglen = strlen(args); /* length of "embed..." string */
! 6073: htmlterm = NULL; /* if so, then we have no htmlterm */
1.3 albertel 6074: isembedded = 1 ; /* turn on embedded flag */
1.5 ! raeburn 6075: if ( arglen > embedlen ) /* have embed "allow escape" flag */
! 6076: embedterm = args[embedlen]; /* char immediately after "embed" */
! 6077: if (arglen > embedlen+1) { /* have embed,flag,white for strwstr*/
! 6078: isstrwstr = 1; /* turn on strwtsr flag */
! 6079: strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
1.3 albertel 6080: } /* --- end-of-if(*args!='\000') --- */
1.1 albertel 6081: expptr = expression; /* re-start search at beginning */
1.3 albertel 6082: while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
6083: strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
1.5 ! raeburn 6084: != NULL ) { /* found another sym */
! 6085: int toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
1.3 albertel 6086: char termchar = *(tokptr+toklen), /* char terminating html sequence */
1.5 ! raeburn 6087: prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
! 6088: int isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
1.3 albertel 6089: int escapelen = toklen; /* total length of escape sequence */
1.5 ! raeburn 6090: int isflush = 0; /* true to flush (don't xlate) */
! 6091: /* --- check odd/even backslashes preceding tokens --- */
! 6092: if ( isescaped ) { /* have one preceding backslash */
! 6093: char *p = tokptr-1; /* ptr to that preceding backslash */
! 6094: while ( p != expptr ) { /* and we may have more preceding */
! 6095: p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
! 6096: isescaped = 1-isescaped; } } /* or flip isescaped flag if we do */
! 6097: /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
1.1 albertel 6098: *abuff = '\000'; /* default to empty string */
6099: if ( latexsym != NULL ) /* table has .latex xlation */
6100: if ( *latexsym != '\000' ) /* and it's not an empty string */
6101: strcpy(abuff,latexsym); /* so get local copy */
1.5 ! raeburn 6102: if ( !isembedded ) /*embedded sequences not terminated*/
! 6103: if ( htmlterm != NULL ) /* sequence may have terminator */
1.1 albertel 6104: escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
1.5 ! raeburn 6105: /* --- don't xlate if we just found prefix of longer symbol, etc --- */
! 6106: if ( !isembedded ) { /* not embedded */
! 6107: if ( isescaped ) /* escaped */
! 6108: isflush = 1; /* set flag to flush escaped token */
! 6109: if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
! 6110: isflush = 1; /* so just a prefix of longer symbol*/
! 6111: if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */
! 6112: if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
! 6113: isflush = 1; } /* set flag to flush token */
! 6114: if ( isembedded ) /* for embedded token */
! 6115: if ( isescaped ) /* and embedded \token escaped */
! 6116: if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
! 6117: isflush = 1; /* set flag to flush token */
! 6118: if ( isflush ) /* don't xlate this token */
! 6119: { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */
1.1 albertel 6120: continue; } /* but don't replace it */
1.5 ! raeburn 6121: /* --- check for &# prefix signalling &#nnn; --- */
! 6122: if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
! 6123: /* --- accumulate chars comprising number following &# --- */
! 6124: char anum[32]; /* chars comprising number after &# */
! 6125: int inum = 0; /* no chars accumulated yet */
! 6126: while ( termchar != '\000' ) { /* don't go past end-of-string */
! 6127: if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
! 6128: if ( inum > 10 ) break; /* some syntax error in expression */
! 6129: anum[inum] = termchar; /* accumulate this digit */
! 6130: inum++; toklen++; /* bump field length, token length */
! 6131: termchar = *(tokptr+toklen); } /* char terminating html sequence */
! 6132: anum[inum] = '\000'; /* null-terminate anum */
! 6133: escapelen = toklen; /* length of &#nnn; sequence */
! 6134: if ( htmlterm != NULL ) /* sequence may have terminator */
! 6135: escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
! 6136: /* --- look up &#nnn in number[] table --- */
! 6137: htmlnum = atoi(anum); /* convert anum[] to an integer */
! 6138: strninit(errorsym,latexsym,128); /* init error message */
! 6139: latexsym = errorsym; /* init latexsym as error message */
! 6140: strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/
! 6141: for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
! 6142: if ( htmlnum == numbers[inum].html ) { /* till we find a match */
! 6143: latexsym = numbers[inum].latex; /* latex replacement */
! 6144: break; } /* no need to look any further */
! 6145: if ( latexsym != NULL ) /* table has .latex xlation */
! 6146: if ( *latexsym != '\000' ) /* and it's not an empty string */
! 6147: strcpy(abuff,latexsym); /* so get local copy */
! 6148: } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */
! 6149: /* --- substitute macro arguments --- */
1.1 albertel 6150: if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/
6151: {
6152: char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
6153: char *optarg = args+1; /* ptr 1 char past #args digit 0-9 */
6154: expptr = arg1ptr; /* ptr to beginning of next arg */
6155: for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
6156: {
6157: char argsignal[32] = "#1", /* #1...#9 signals arg replacement */
6158: *argsigptr = NULL; /* ptr to argsignal in abuff[] */
6159: /* --- get argument value --- */
6160: *argval = '\000'; /* init arg as empty string */
6161: skipwhite(expptr); /* and skip leading white space */
6162: if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
6163: && !isalgebra ) /* but not in "algebra syntax" */
6164: { strcpy(argval,optarg); /* init with default value */
6165: if ( *expptr == '[' ) /* but user gave us [argval] */
1.5 ! raeburn 6166: expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
1.1 albertel 6167: else /* not optional, so get {argval} */
1.3 albertel 6168: if ( *expptr != '\000' ) { /* check that some argval provided */
1.5 ! raeburn 6169: if ( !isalgebra ) /* only { } delims for latex macro */
! 6170: expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
! 6171: else { /*any delim for algebra syntax macro*/
! 6172: expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
1.1 albertel 6173: if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
1.5 ! raeburn 6174: if ( *argval != '{' ) { /* and it's not { }-enclosed */
! 6175: strchange(0,argval,"\\left"); /* insert opening \left, */
! 6176: strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
! 6177: } /* --- end-of-if(*expptr!='\000') --- */
! 6178: /* --- (recursively) call mimeprep() to prep the argument --- */
! 6179: if ( !isempty(argval) ) /* have an argument */
! 6180: mimeprep(argval); /* so (recursively) prep it */
1.1 albertel 6181: /* --- replace #`iarg` in macro with argval --- */
6182: sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */
6183: while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
1.5 ! raeburn 6184: {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
1.1 albertel 6185: while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
6186: strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
6187: } /* --- end-of-for(iarg) --- */
6188: escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
6189: } /* --- end-of-if(nargs>0) --- */
6190: strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
6191: expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
1.5 ! raeburn 6192: } /* --- end-of-while(tokptr!=NULL) --- */
1.1 albertel 6193: } /* --- end-of-for(isymbol) --- */
6194: /* -------------------------------------------------------------------------
1.3 albertel 6195: convert \left( to \( and \right) to \), etc.
6196: -------------------------------------------------------------------------- */
6197: if ( xlateleft ) /* \left...\right xlation wanted */
6198: for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left and 1 for \right */
6199: {
6200: char *lrstr = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
6201: int lrlen = (idelim==0?5:6); /* strlen() of \left or \right */
6202: char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
6203: **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
6204: **lrto = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
6205: *lrsym = NULL; /* lrfrom[isymbol] */
6206: expptr = expression; /* start search at beginning */
6207: while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
6208: {
6209: if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
1.5 ! raeburn 6210: { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
1.3 albertel 6211: expptr = tokptr+2; } /* and resume search past brace */
6212: else /* may be a "long" brace like \| */
6213: {
6214: expptr = tokptr+lrlen; /*init to resume search past\left\rt*/
6215: for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
6216: { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
6217: if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
1.5 ! raeburn 6218: { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
1.3 albertel 6219: *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
6220: expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
6221: break; } /* no need to check more lrsym's */
6222: } /* --- end-of-for(isymbol) --- */
6223: } /* --- end-of-if/else(isthischar()) --- */
6224: } /* --- end-of-while(tokptr!=NULL) --- */
6225: } /* --- end-of-for(idelim) --- */
6226: /* -------------------------------------------------------------------------
1.1 albertel 6227: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
6228: -------------------------------------------------------------------------- */
6229: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
6230: {
6231: int atoplen = strlen(atopsym); /* #chars in \atop */
6232: expptr = expression; /* re-start search at beginning */
6233: while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
6234: { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
6235: char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
6236: if ( msgfp!=NULL && msglevel>=999 )
6237: { fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
6238: (int)(tokptr-expression),tokptr);
6239: fflush(msgfp); }
6240: if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
6241: { expptr = tokptr+atoplen; /* just resume search after prefix */
6242: continue; } /* but don't process it */
6243: leftbrace = findbraces(expression,tokptr); /* find left { */
6244: rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
6245: if ( leftbrace==NULL || rightbrace==NULL )
6246: { expptr += atoplen; continue; } /* skip command if didn't find */
6247: else /* we have bracketed { \atop } */
6248: {
6249: int leftlen = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
6250: rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
6251: totlen = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
6252: char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
6253: char arg[8192], command[8192]; /* left/right args, new \atop{}{} */
6254: *command = '\000'; /* start with null string */
6255: if (open!=NULL) strcat(command,open); /* add open delim if needed */
6256: strcat(command,atopsym); /* add command with \atop */
6257: arg[0] = '{'; /* arg starts with { */
6258: memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
6259: arg[leftlen+1] = '\000'; /* and null terminate it */
6260: strcat(command,arg); /* concatanate {left-arg to \atop */
6261: strcat(command,"}{"); /* close left-arg, open right-arg */
6262: memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
6263: arg[rightlen] = '}'; /* add closing } */
6264: arg[rightlen+1] = '\000'; /* and null terminate it */
6265: if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
1.5 ! raeburn 6266: {strsqueeze(arg,1);} /* so squeeze it out */
1.1 albertel 6267: strcat(command,arg); /* concatanate right-arg} */
6268: if (close!=NULL) strcat(command,close); /* add close delim if needed*/
6269: strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
6270: expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
6271: }
6272: } /* --- end-of-while(tokptr!=NULL) --- */
6273: } /* --- end-of-for(isymbol) --- */
6274: /* -------------------------------------------------------------------------
6275: back to caller with preprocessed expression
6276: -------------------------------------------------------------------------- */
6277: if ( msgfp!=NULL && msglevel>=99 ) /* display preprocessed expression */
6278: { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
6279: fflush(msgfp); }
6280: return ( expression );
6281: } /* --- end-of-function mimeprep() --- */
6282:
6283:
6284: /* ==========================================================================
6285: * Function: strchange ( int nfirst, char *from, char *to )
6286: * Purpose: Changes the nfirst leading chars of `from` to `to`.
6287: * For example, to change char x[99]="12345678" to "123ABC5678"
6288: * call strchange(1,x+3,"ABC")
6289: * --------------------------------------------------------------------------
6290: * Arguments: nfirst (I) int containing #leading chars of `from`
6291: * that will be replace by `to`
6292: * from (I/O) char * to null-terminated string whose nfirst
6293: * leading chars will be replaced by `to`
6294: * to (I) char * to null-terminated string that will
6295: * replace the nfirst leading chars of `from`
6296: * --------------------------------------------------------------------------
6297: * Returns: ( char * ) ptr to first char of input `from`
6298: * or NULL for any error.
6299: * --------------------------------------------------------------------------
6300: * Notes: o If strlen(to)>nfirst, from must have memory past its null
6301: * (i.e., we don't do a realloc)
6302: * ======================================================================= */
6303: /* --- entry point --- */
6304: char *strchange ( int nfirst, char *from, char *to )
6305: {
6306: /* -------------------------------------------------------------------------
6307: Allocations and Declarations
6308: -------------------------------------------------------------------------- */
6309: int tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
6310: nshift = abs(tolen-nfirst); /*need to shift from left or right*/
6311: /* -------------------------------------------------------------------------
6312: shift from left or right to accommodate replacement of its nfirst chars by to
6313: -------------------------------------------------------------------------- */
6314: if ( tolen < nfirst ) /* shift left is easy */
1.5 ! raeburn 6315: {strsqueeze(from,nshift);} /* memmove avoids overlap memory */
1.1 albertel 6316: if ( tolen > nfirst ) /* need more room at start of from */
6317: { char *pfrom = from+strlen(from); /* ptr to null terminating from */
6318: for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
6319: *(pfrom+nshift) = *pfrom; } /* shift chars nshift places right */
6320: /* -------------------------------------------------------------------------
6321: from has exactly the right number of free leading chars, so just put to there
6322: -------------------------------------------------------------------------- */
6323: if ( tolen != 0 ) /* make sure to not empty or null */
6324: memcpy(from,to,tolen); /* chars moved into place */
6325: return ( from ); /* changed string back to caller */
6326: } /* --- end-of-function strchange() --- */
6327:
6328:
6329: /* ==========================================================================
6330: * Function: strreplace (char *string, char *from, char *to, int nreplace)
6331: * Purpose: Changes the first nreplace occurrences of 'from' to 'to'
6332: * in string, or all occurrences if nreplace=0.
6333: * --------------------------------------------------------------------------
6334: * Arguments: string (I/0) char * to null-terminated string in which
6335: * occurrence of 'from' will be replaced by 'to'
6336: * from (I) char * to null-terminated string
6337: * to be replaced by 'to'
6338: * to (I) char * to null-terminated string that will
6339: * replace 'from'
6340: * nreplace (I) int containing (maximum) number of
6341: * replacements, or 0 to replace all.
6342: * --------------------------------------------------------------------------
6343: * Returns: ( int ) number of replacements performed,
6344: * or 0 for no replacements or -1 for any error.
6345: * --------------------------------------------------------------------------
6346: * Notes: o
6347: * ======================================================================= */
6348: /* --- entry point --- */
6349: int strreplace ( char *string, char *from, char *to, int nreplace )
6350: {
6351: /* -------------------------------------------------------------------------
6352: Allocations and Declarations
6353: -------------------------------------------------------------------------- */
6354: int fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
6355: tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
6356: char *pfrom = (char *)NULL, /*ptr to 1st char of from in string*/
6357: *pstring = string, /*ptr past previously replaced from*/
6358: *strchange(); /* change 'from' to 'to' */
6359: int nreps = 0; /* #replacements returned to caller*/
6360: /* -------------------------------------------------------------------------
6361: repace occurrences of 'from' in string to 'to'
6362: -------------------------------------------------------------------------- */
6363: if ( string == (char *)NULL /* no input string */
6364: || (fromlen<1 && nreplace<=0) ) /* replacing empty string forever */
6365: nreps = (-1); /* so signal error */
6366: else /* args okay */
6367: while (nreplace<1 || nreps<nreplace) /* up to #replacements requested */
6368: {
6369: if ( fromlen > 0 ) /* have 'from' string */
6370: pfrom = strstr(pstring,from); /*ptr to 1st char of from in string*/
6371: else pfrom = pstring; /*or empty from at start of string*/
6372: if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
6373: if ( strchange(fromlen,pfrom,to) /* leading 'from' changed to 'to' */
6374: == (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
6375: nreps++; /* count another replacement */
6376: pstring = pfrom+tolen; /* pick up search after 'to' */
6377: if ( *pstring == '\000' ) break; /* but quit at end of string */
6378: } /* --- end-of-while() --- */
6379: return ( nreps ); /* #replacements back to caller */
6380: } /* --- end-of-function strreplace() --- */
6381:
6382:
6383: /* ==========================================================================
1.3 albertel 6384: * Function: strwstr (char *string, char *substr, char *white, int *sublen)
6385: * Purpose: Find first substr in string, but wherever substr contains
6386: * a whitespace char (in white), string may contain any number
6387: * (including 0) of whitespace chars. If white contains I or i,
6388: * then match is case-insensitive (and I,i _not_ whitespace).
6389: * --------------------------------------------------------------------------
6390: * Arguments: string (I) char * to null-terminated string in which
6391: * first occurrence of substr will be found
6392: * substr (I) char * to null-terminated string containing
6393: * "template" that will be searched for
6394: * white (I) char * to null-terminated string containing
6395: * whitespace chars. If NULL or empty, then
6396: * "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
6397: * If white contains I or i, then match is
6398: * case-insensitive (and I,i _not_ considered
6399: * whitespace).
6400: * sublen (O) address of int returning "length" of substr
6401: * found in string (which may be longer or
6402: * shorter than substr itself).
6403: * --------------------------------------------------------------------------
6404: * Returns: ( char * ) ptr to first char of substr in string
6405: * or NULL if not found or for any error.
6406: * --------------------------------------------------------------------------
6407: * Notes: o Wherever a single whitespace char appears in substr,
6408: * the corresponding position in string may contain any
6409: * number (including 0) of whitespace chars, e.g.,
6410: * string="abc def" and string="abcdef" both match
6411: * substr="c d" at offset 2 of string.
6412: * o If substr="c d" (two spaces between c and d),
6413: * then string must have at least one space, so now "abcdef"
6414: * doesn't match. In general, the minimum number of spaces
6415: * in string is the number of spaces in substr minus 1
6416: * (so 1 space in substr permits 0 spaces in string).
6417: * o Embedded spaces are counted in sublen, e.g.,
6418: * string="c d" (three spaces) matches substr="c d"
6419: * with sublen=5 returned. But string="ab c d" will
6420: * also match substr=" c d" returning sublen=5 and
6421: * a ptr to the "c". That is, the mandatory preceding
6422: * space is _not_ counted as part of the match.
6423: * But all the embedded space is counted.
6424: * (An inconsistent bug/feature is that mandatory
6425: * terminating space is counted.)
6426: * o Moreover, string="c d" matches substr=" c d", i.e.,
6427: * the very beginning of a string is assumed to be preceded
6428: * by "virtual blanks".
6429: * ======================================================================= */
6430: /* --- entry point --- */
6431: char *strwstr ( char *string, char *substr, char *white, int *sublen )
6432: {
6433: /* -------------------------------------------------------------------------
6434: Allocations and Declarations
6435: -------------------------------------------------------------------------- */
6436: char *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
6437: *pfound = (char *)NULL; /*ptr to found substr back to caller*/
6438: char *pwhite=NULL, whitespace[256]; /* callers white whithout i,I */
6439: int iscase = (white==NULL?1: /* case-sensitive if i,I in white */
6440: strchr(white,'i')==NULL && strchr(white,'I')==NULL);
6441: int foundlen = 0; /* length of substr found in string*/
6442: int nstrwhite=0, nsubwhite=0, /* #leading white chars in str,sub */
6443: nminwhite=0; /* #mandatory leading white in str */
6444: int nstrchars=0, nsubchars=0, /* #non-white chars to be matched */
6445: isncmp=0; /*strncmp() or strncasecmp() result*/
6446: /* -------------------------------------------------------------------------
6447: Initialization
6448: -------------------------------------------------------------------------- */
6449: /* --- set up whitespace --- */
6450: strcpy(whitespace,WHITEMATH); /*default if no user input for white*/
6451: if ( white != NULL ) /*user provided ptr to white string*/
6452: if ( *white != '\000' ) { /*and it's not just an empty string*/
6453: strcpy(whitespace,white); /* so use caller's white spaces */
6454: while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
1.5 ! raeburn 6455: {strsqueeze(pwhite,1);} /* so squeeze it out */
1.3 albertel 6456: while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
1.5 ! raeburn 6457: {strsqueeze(pwhite,1);} /* so squeeze it out */
1.3 albertel 6458: if ( *whitespace == '\000' ) /* caller's white just had i,I */
6459: strcpy(whitespace,WHITEMATH); } /* so revert back to default */
6460: /* -------------------------------------------------------------------------
6461: Find first occurrence of substr in string
6462: -------------------------------------------------------------------------- */
6463: if ( string != NULL ) /* caller passed us a string ptr */
6464: while ( *pstring != '\000' ) { /* break when string exhausted */
6465: char *pstrptr = pstring; /* (re)start at next char in string*/
6466: int leadingwhite = 0; /* leading whitespace */
6467: psubstr = substr; /* start at beginning of substr */
6468: foundlen = 0; /* reset length of found substr */
6469: if ( substr != NULL ) /* caller passed us a substr ptr */
6470: while ( *psubstr != '\000' ) { /*see if pstring begins with substr*/
6471: /* --- check for end-of-string before finding match --- */
6472: if ( *pstrptr == '\000' ) /* end-of-string without a match */
6473: goto nextstrchar; /* keep trying with next char */
6474: /* --- actual amount of whitespace in string and substr --- */
6475: nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
6476: nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
6477: nminwhite = max2(0,nsubwhite-1); /* #mandatory leading white in str */
6478: /* --- check for mandatory leading whitespace in string --- */
6479: if ( pstrptr != string ) /*not mandatory at start of string*/
6480: if ( nstrwhite < nminwhite ) /* too little leading white space */
6481: goto nextstrchar; /* keep trying with next char */
6482: /* ---hold on to #whitespace chars in string preceding substr match--- */
6483: if ( pstrptr == pstring ) /* whitespace at start of substr */
6484: leadingwhite = nstrwhite; /* save it as leadingwhite */
6485: /* --- check for optional whitespace --- */
6486: if ( psubstr != substr ) /* always okay at start of substr */
6487: if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */
6488: goto nextstrchar; /* keep trying with next char */
6489: /* --- skip any leading whitespace in substr and string --- */
6490: psubstr += nsubwhite; /* push past leading sub whitespace*/
6491: pstrptr += nstrwhite; /* push past leading str whitespace*/
6492: /* --- now get non-whitespace chars that we have to match --- */
6493: nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
6494: nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
6495: if ( nstrchars < nsubchars ) /* too few chars for match */
6496: goto nextstrchar; /* keep trying with next char */
6497: /* --- see if next nsubchars are a match --- */
6498: isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
6499: strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
6500: if ( isncmp != 0 ) /* no match */
6501: goto nextstrchar; /* keep trying with next char */
6502: /* --- push past matched chars --- */
6503: psubstr += nsubchars; pstrptr += nsubchars; /*nsubchars were matched*/
6504: } /* --- end-of-while(*psubstr!='\000') --- */
6505: pfound = pstring + leadingwhite; /* found match starting at pstring */
6506: foundlen = (int)(pstrptr-pfound); /* consisting of this many chars */
6507: goto end_of_job; /* back to caller */
6508: /* ---failed to find substr, continue trying with next char in string--- */
6509: nextstrchar: /* continue outer loop */
6510: pstring++; /* bump to next char in string */
6511: } /* --- end-of-while(*pstring!='\000') --- */
6512: /* -------------------------------------------------------------------------
6513: Back to caller with ptr to first occurrence of substr in string
6514: -------------------------------------------------------------------------- */
6515: end_of_job:
6516: if ( msglevel>=999 && msgfp!=NULL) { /* debugging/diagnostic output */
6517: fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
6518: string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
6519: if ( sublen != NULL ) /*caller wants length of found substr*/
6520: *sublen = foundlen; /* give it to him along with ptr */
6521: return ( pfound ); /*ptr to first found substr, or NULL*/
6522: } /* --- end-of-function strwstr() --- */
6523:
6524:
6525: /* ==========================================================================
1.5 ! raeburn 6526: * Function: strdetex ( s, mode )
! 6527: * Purpose: Removes/replaces any LaTeX math chars in s
! 6528: * so that s can be displayed "verbatim",
! 6529: * e.g., for error messages.
! 6530: * --------------------------------------------------------------------------
! 6531: * Arguments: s (I) char * to null-terminated string
! 6532: * whose math chars are to be removed/replaced
! 6533: * mode (I) int containing 0 to _not_ use macros (i.e.,
! 6534: * mimeprep won't be called afterwards),
! 6535: * or containing 1 to use macros that will
! 6536: * be expanded by a subsequent call to mimeprep.
! 6537: * --------------------------------------------------------------------------
! 6538: * Returns: ( char * ) ptr to "cleaned" copy of s
! 6539: * or "" (empty string) for any error.
! 6540: * --------------------------------------------------------------------------
! 6541: * Notes: o The returned pointer addresses a static buffer,
! 6542: * so don't call strdetex() again until you're finished
! 6543: * with output from the preceding call.
! 6544: * ======================================================================= */
! 6545: /* --- entry point --- */
! 6546: char *strdetex ( char *s, int mode )
! 6547: {
! 6548: /* -------------------------------------------------------------------------
! 6549: Allocations and Declarations
! 6550: -------------------------------------------------------------------------- */
! 6551: static char sbuff[4096]; /* copy of s with no math chars */
! 6552: int strreplace(); /* replace _ with -, etc */
! 6553: /* -------------------------------------------------------------------------
! 6554: Make a clean copy of s
! 6555: -------------------------------------------------------------------------- */
! 6556: /* --- check input --- */
! 6557: *sbuff = '\000'; /* initialize in case of error */
! 6558: if ( isempty(s) ) goto end_of_job; /* no input */
! 6559: /* --- start with copy of s --- */
! 6560: strninit(sbuff,s,2048); /* leave room for replacements */
! 6561: /* --- make some replacements -- we *must* replace \ { } first --- */
! 6562: strreplace(sbuff,"\\","\\backslash~\\!\\!",0); /*change all \'s to text*/
! 6563: strreplace(sbuff,"{", "\\lbrace~\\!\\!",0); /*change all {'s to \lbrace*/
! 6564: strreplace(sbuff,"}", "\\rbrace~\\!\\!",0); /*change all }'s to \rbrace*/
! 6565: /* --- now our further replacements may contain \directives{args} --- */
! 6566: if( mode >= 1 ) strreplace(sbuff,"_","\\_",0); /* change all _'s to \_ */
! 6567: else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
! 6568: if(0)strreplace(sbuff,"<","\\textlangle ",0); /* change all <'s to text */
! 6569: if(0)strreplace(sbuff,">","\\textrangle ",0); /* change all >'s to text */
! 6570: if(0)strreplace(sbuff,"$","\\textdollar ",0); /* change all $'s to text */
! 6571: strreplace(sbuff,"$","\\$",0); /* change all $'s to \$ */
! 6572: strreplace(sbuff,"&","\\&",0); /* change all &'s to \& */
! 6573: strreplace(sbuff,"%","\\%",0); /* change all %'s to \% */
! 6574: strreplace(sbuff,"#","\\#",0); /* change all #'s to \# */
! 6575: /*strreplace(sbuff,"~","\\~",0);*/ /* change all ~'s to \~ */
! 6576: strreplace(sbuff,"^","{\\fs{+2}\\^}",0); /* change all ^'s to \^ */
! 6577: end_of_job:
! 6578: return ( sbuff ); /* back with clean copy of s */
! 6579: } /* --- end-of-function strdetex() --- */
! 6580:
! 6581:
! 6582: /* ==========================================================================
1.3 albertel 6583: * Function: strtexchr ( char *string, char *texchr )
1.1 albertel 6584: * Purpose: Find first texchr in string, but texchr must be followed
6585: * by non-alpha
6586: * --------------------------------------------------------------------------
6587: * Arguments: string (I) char * to null-terminated string in which
1.3 albertel 6588: * first occurrence of delim will be found
1.1 albertel 6589: * texchr (I) char * to null-terminated string that
6590: * will be searched for
6591: * --------------------------------------------------------------------------
6592: * Returns: ( char * ) ptr to first char of texchr in string
6593: * or NULL if not found or for any error.
6594: * --------------------------------------------------------------------------
6595: * Notes: o texchr should contain its leading \, e.g., "\\left"
6596: * ======================================================================= */
6597: /* --- entry point --- */
6598: char *strtexchr ( char *string, char *texchr )
6599: {
6600: /* -------------------------------------------------------------------------
6601: Allocations and Declarations
6602: -------------------------------------------------------------------------- */
1.2 albertel 6603: char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
1.1 albertel 6604: char *pstring = string; /* start or continue up search here*/
6605: int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
6606: /* -------------------------------------------------------------------------
6607: locate texchr in string
6608: -------------------------------------------------------------------------- */
6609: if ( string != (char *)NULL /* check that we got input string */
1.3 albertel 6610: && texchrlen > 0 ) { /* and a texchr to search for */
1.1 albertel 6611: while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
6612: != (char *)NULL ) /* found it */
6613: if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */
6614: == '\000' ) break; /* texchr at very end of string */
6615: else /* if there are chars after texchr */
6616: if ( isalpha(delim) /*texchr is prefix of longer symbol*/
6617: || 0 ) /* other tests to be determined */
6618: pstring = ptexchr + texchrlen; /* continue search after texchr */
6619: else /* passed all tests */
1.3 albertel 6620: break; } /*so return ptr to texchr to caller*/
1.1 albertel 6621: return ( ptexchr ); /* ptr to texchar back to caller */
6622: } /* --- end-of-function strtexchr() --- */
6623:
6624:
6625: /* ==========================================================================
6626: * Function: findbraces ( char *expression, char *command )
6627: * Purpose: If expression!=NULL, finds opening left { preceding command;
6628: * if expression==NULL, finds closing right } after command.
6629: * For example, to parse out {a+b\over c+d} call findbraces()
6630: * twice.
6631: * --------------------------------------------------------------------------
6632: * Arguments: expression (I) NULL to find closing right } after command,
6633: * or char * to null-terminated string to find
6634: * left opening { preceding command.
6635: * command (I) char * to null-terminated string whose
6636: * first character is usually the \ of \command
6637: * --------------------------------------------------------------------------
6638: * Returns: ( char * ) ptr to either opening { or closing },
6639: * or NULL for any error.
6640: * --------------------------------------------------------------------------
6641: * Notes: o
6642: * ======================================================================= */
6643: /* --- entry point --- */
6644: char *findbraces ( char *expression, char *command )
6645: {
6646: /* -------------------------------------------------------------------------
6647: Allocations and Declarations
6648: -------------------------------------------------------------------------- */
6649: int isopen = (expression==NULL?0:1); /* true to find left opening { */
6650: char *left="{", *right="}", /* delims bracketing {x\command y} */
6651: *delim = (isopen?left:right), /* delim we want, { if isopen */
6652: *match = (isopen?right:left), /* matching delim, } if isopen */
6653: *brace = NULL; /* ptr to delim returned to caller */
6654: int inc = (isopen?-1:+1); /* pointer increment */
6655: int level = 1; /* nesting level, for {{}\command} */
6656: char *ptr = command; /* start search here */
6657: int setbrace = 1; /* true to set {}'s if none found */
6658: /* -------------------------------------------------------------------------
6659: search for left opening { before command, or right closing } after command
6660: -------------------------------------------------------------------------- */
6661: while ( 1 ) /* search for brace, or until end */
6662: {
6663: /* --- next char to check for delim --- */
6664: ptr += inc; /* bump ptr left or right */
6665: /* --- check for beginning or end of expression --- */
6666: if ( isopen ) /* going left, check for beginning */
6667: { if ( ptr < expression ) break; } /* went before start of string */
6668: else { if ( *ptr == '\000' ) break; } /* went past end of string */
6669: /* --- don't check this char if it's escaped --- */
6670: if ( !isopen || ptr>expression ) /* very first char can't be escaped*/
6671: if ( isthischar(*(ptr-1),ESCAPE) ) /* escape char precedes current */
6672: continue; /* so don't check this char */
6673: /* --- check for delim --- */
6674: if ( isthischar(*ptr,delim) ) /* found delim */
6675: if ( --level == 0 ) /* and it's not "internally" nested*/
6676: { brace = ptr; /* set ptr to brace */
6677: goto end_of_job; } /* and return it to caller */
6678: /* --- check for matching delim --- */
6679: if ( isthischar(*ptr,match) ) /* found matching delim */
6680: level++; /* so bump nesting level */
6681: } /* --- end-of-while(1) --- */
6682: end_of_job:
6683: if ( brace == (char *)NULL ) /* open{ or close} not found */
6684: if ( setbrace ) /* want to force one at start/end? */
6685: brace = ptr; /* { before expressn, } after cmmnd*/
6686: return ( brace ); /*back to caller with delim or NULL*/
6687: } /* --- end-of-function findbraces() --- */
6688:
1.5 ! raeburn 6689:
! 6690: /* ==========================================================================
! 6691: * Function: strpspn ( char *s, char *reject, char *segment )
! 6692: * Purpose: finds the initial segment of s containing no chars
! 6693: * in reject that are outside (), [] and {} parens, e.g.,
! 6694: * strpspn("abc(---)def+++","+-",segment) returns
! 6695: * segment="abc(---)def" and a pointer to the first '+' in s
! 6696: * because the -'s are enclosed in () parens.
! 6697: * --------------------------------------------------------------------------
! 6698: * Arguments: s (I) (char *)pointer to null-terminated string
! 6699: * whose initial segment is desired
! 6700: * reject (I) (char *)pointer to null-terminated string
! 6701: * containing the "reject chars"
! 6702: * segment (O) (char *)pointer returning null-terminated
! 6703: * string comprising the initial segment of s
! 6704: * that contains non-rejected chars outside
! 6705: * (),[],{} parens, i.e., all the chars up to
! 6706: * but not including the returned pointer.
! 6707: * (That's the entire string if no non-rejected
! 6708: * chars are found.)
! 6709: * --------------------------------------------------------------------------
! 6710: * Returns: ( char * ) pointer to first reject-char found in s
! 6711: * outside parens, or a pointer to the
! 6712: * terminating '\000' of s if there are
! 6713: * no reject chars in s outside all () parens.
! 6714: * --------------------------------------------------------------------------
! 6715: * Notes: o the return value is _not_ like strcspn()'s
! 6716: * o improperly nested (...[...)...] are not detected,
! 6717: * but are considered "balanced" after the ]
! 6718: * o if reject not found, segment returns the entire string s
! 6719: * o leading/trailing whitespace is trimmed from returned segment
! 6720: * ======================================================================= */
! 6721: /* --- entry point --- */
! 6722: char *strpspn ( char *s, char *reject, char *segment )
! 6723: {
! 6724: /* -------------------------------------------------------------------------
! 6725: Allocations and Declarations
! 6726: -------------------------------------------------------------------------- */
! 6727: char *ps = s; /* current pointer into s */
! 6728: int depth = 0; /* () paren nesting level */
! 6729: int seglen=0, maxseg=2047; /* segment length, max allowed */
! 6730: /* -------------------------------------------------------------------------
! 6731: initialization
! 6732: -------------------------------------------------------------------------- */
! 6733: /* --- check arguments --- */
! 6734: if ( isempty(s) ) /* no input string supplied */
! 6735: goto end_of_job; /* no reject chars supplied */
! 6736: /* -------------------------------------------------------------------------
! 6737: find first char from s outside () parens (and outside ""'s) and in reject
! 6738: -------------------------------------------------------------------------- */
! 6739: while ( *ps != '\000' ) { /* search till end of input string */
! 6740: if ( isthischar(*ps,"([{") ) depth++; /* push another paren */
! 6741: if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */
! 6742: if ( depth < 1 ) { /* we're outside all parens */
! 6743: if ( isempty(reject) ) break; /* no reject so break immediately */
! 6744: if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
! 6745: if ( segment != NULL ) /* caller gave us segment */
! 6746: if ( seglen < maxseg ) /* don't overflow segment buffer */
! 6747: memcpy(segment+seglen,ps,1); /* so copy non-reject char */
! 6748: seglen += 1; ps += 1; /* bump to next char */
! 6749: } /* --- end-of-while(*ps!=0) --- */
! 6750: end_of_job:
! 6751: if ( segment != NULL ) { /* caller gave us segment */
! 6752: if ( isempty(reject) ) { /* no reject char */
! 6753: segment[min2(seglen,maxseg)] = *ps; seglen++; } /*closing )]} to seg*/
! 6754: segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
! 6755: trimwhite(segment); } /* trim leading/trailing whitespace*/
! 6756: return ( ps ); /* back to caller */
! 6757: } /* --- end-of-function strpspn() --- */
! 6758:
! 6759:
1.1 albertel 6760: /* ==========================================================================
1.5 ! raeburn 6761: * Function: isstrstr ( char *string, char *snippets, int iscase )
! 6762: * Purpose: determine whether any substring of 'string'
! 6763: * matches any of the comma-separated list of 'snippets',
! 6764: * ignoring case if iscase=0.
1.1 albertel 6765: * --------------------------------------------------------------------------
1.5 ! raeburn 6766: * Arguments: string (I) char * containing null-terminated
! 6767: * string that will be searched for
! 6768: * any one of the specified snippets
! 6769: * snippets (I) char * containing null-terminated,
! 6770: * comma-separated list of snippets
! 6771: * to be searched for in string
! 6772: * iscase (I) int containing 0 for case-insensitive
! 6773: * comparisons, or 1 for case-sensitive
1.1 albertel 6774: * --------------------------------------------------------------------------
1.5 ! raeburn 6775: * Returns: ( int ) 1 if any snippet is a substring of
! 6776: * string, 0 if not
1.1 albertel 6777: * --------------------------------------------------------------------------
1.5 ! raeburn 6778: * Notes: o
1.1 albertel 6779: * ======================================================================= */
6780: /* --- entry point --- */
1.5 ! raeburn 6781: int isstrstr ( char *string, char *snippets, int iscase )
! 6782: {
! 6783: /* -------------------------------------------------------------------------
! 6784: Allocations and Declarations
! 6785: -------------------------------------------------------------------------- */
! 6786: int status = 0; /*1 if any snippet found in string*/
! 6787: char snip[256], *snipptr = snippets, /* munge through each snippet */
! 6788: delim = ',', *delimptr = NULL; /* separated by delim's */
! 6789: char stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/
! 6790: /* -------------------------------------------------------------------------
! 6791: initialization
! 6792: -------------------------------------------------------------------------- */
! 6793: /* --- arg check --- */
! 6794: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
! 6795: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
! 6796: /* --- copy string and lowercase it if case-insensitive --- */
! 6797: strninit(stringcp,string,4064); /* local copy of string */
! 6798: if ( !iscase ) /* want case-insensitive compares */
! 6799: for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
! 6800: if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
! 6801: /* -------------------------------------------------------------------------
! 6802: extract each snippet and see if it's a substring of string
! 6803: -------------------------------------------------------------------------- */
! 6804: while ( snipptr != NULL ) /* while we still have snippets */
! 6805: {
! 6806: /* --- extract next snippet --- */
! 6807: if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
! 6808: == NULL ) /*not found following last snippet*/
! 6809: { strninit(snip,snipptr,255); /* local copy of last snippet */
! 6810: snipptr = NULL; } /* signal end-of-string */
! 6811: else /* snippet ends just before delim */
! 6812: { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */
! 6813: memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */
! 6814: snip[sniplen] = '\000'; /* null-terminated snippet */
! 6815: snipptr = delimptr + 1; } /* next snippet starts after delim */
! 6816: /* --- lowercase snippet if case-insensitive --- */
! 6817: if ( !iscase ) /* want case-insensitive compares */
! 6818: for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
! 6819: if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
! 6820: /* --- check if snippet in string --- */
! 6821: if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */
! 6822: { status = 1; /* so reset return status */
! 6823: break; } /* no need to check any further */
! 6824: } /* --- end-of-while(*snipptr!=0) --- */
! 6825: end_of_job: return ( status ); /*1 if snippet found in list, else 0*/
! 6826: } /* --- end-of-function isstrstr() --- */
! 6827:
! 6828:
! 6829: /* ==========================================================================
! 6830: * Function: isnumeric ( s )
! 6831: * Purpose: determine if s is an integer
! 6832: * --------------------------------------------------------------------------
! 6833: * Arguments: s (I) (char *)pointer to null-terminated string
! 6834: * that's checked for a leading + or -
! 6835: * followed by digits
! 6836: * --------------------------------------------------------------------------
! 6837: * Returns: ( int ) 1 if s is numeric, 0 if it is not
! 6838: * --------------------------------------------------------------------------
! 6839: * Notes: o
! 6840: * ======================================================================= */
! 6841: /* --- entry point --- */
! 6842: int isnumeric ( char *s )
! 6843: {
! 6844: /* -------------------------------------------------------------------------
! 6845: determine whether s is an integer
! 6846: -------------------------------------------------------------------------- */
! 6847: int status = 0; /* return 0 if not numeric, 1 if is*/
! 6848: char *p = s; /* pointer into s */
! 6849: if ( isempty(s) ) goto end_of_job; /* missing arg or empty string */
! 6850: skipwhite(p); /*check for leading +or- after space*/
! 6851: if ( *p=='+' || *p=='-' ) p++; /* skip leading + or - */
! 6852: for ( ; *p != '\000'; p++ ) { /* check rest of s for digits */
! 6853: if ( isdigit(*p) ) continue; /* still got uninterrupted digits */
! 6854: if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
! 6855: skipwhite(p); /* skip all subsequent whitespace */
! 6856: if ( *p == '\000' ) break; /* trailing whitespace okay */
! 6857: goto end_of_job; /* embedded whitespace non-numeric */
! 6858: } /* --- end-of-for(*p) --- */
! 6859: status = 1; /* numeric after checks succeeded */
! 6860: end_of_job:
! 6861: return ( status ); /*back to caller with 1=string, 0=no*/
! 6862: } /* --- end-of-function isnumeric() --- */
! 6863:
! 6864:
! 6865: /* ==========================================================================
! 6866: * Function: evalterm ( STORE *store, char *term )
! 6867: * Purpose: evaluates a term
! 6868: * --------------------------------------------------------------------------
! 6869: * Arguments: store (I/O) STORE * containing environment
! 6870: * in which term is to be evaluated
! 6871: * term (I) char * containing null-terminated string
! 6872: * with a term like "3" or "a" or "a+3"
! 6873: * whose value is to be determined
! 6874: * --------------------------------------------------------------------------
! 6875: * Returns: ( int ) value of term,
! 6876: * or NOVALUE for any error
! 6877: * --------------------------------------------------------------------------
! 6878: * Notes: o Also evaluates index?a:b:c:etc, returning a if index<=0,
! 6879: * b if index=1, etc, and the last value if index is too large.
! 6880: * Each a:b:c:etc can be another expression, including another
! 6881: * (index?a:b:c:etc) which must be enclosed in parentheses.
! 6882: * ======================================================================= */
! 6883: /* --- entry point --- */
! 6884: int evalterm ( STORE *store, char *term )
! 6885: {
! 6886: /* -------------------------------------------------------------------------
! 6887: Allocations and Declarations
! 6888: -------------------------------------------------------------------------- */
! 6889: int termval = 0; /* term value returned to caller */
! 6890: char token[2048] = "\000", /* copy term */
! 6891: *delim = NULL; /* delim '(' or '?' in token */
! 6892: /*int evalwff(),*/ /* recurse to evaluate terms */
! 6893: /* evalfunc();*/ /* evaluate function(arg1,arg2,...)*/
! 6894: char *strpspn(); /* span delims */
! 6895: int getstore(); /* lookup variables */
! 6896: int isnumeric(); /* numeric=constant, else variable */
! 6897: static int evaltermdepth = 0; /* recursion depth */
! 6898: int novalue = (-89123456); /* dummy (for now) error signal */
! 6899: /* -------------------------------------------------------------------------
! 6900: Initialization
! 6901: -------------------------------------------------------------------------- */
! 6902: if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
! 6903: if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
! 6904: skipwhite(term); /* skip any leading whitespace */
! 6905: /* -------------------------------------------------------------------------
! 6906: First look for conditional of the form term?term:term:...
! 6907: -------------------------------------------------------------------------- */
! 6908: /* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
! 6909: delim = strpspn(term,"?",token); /* chars preceding ? outside () */
! 6910: if ( *delim != '\000' ) { /* found conditional expression */
! 6911: int ncolons = 0; /* #colons we've found so far */
! 6912: if ( *token != '\000' ) /* evaluate "index" value on left */
! 6913: if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
! 6914: == novalue ) goto end_of_job; /* return error if failed */
! 6915: while ( *delim != '\000' ) { /* still have chars in term */
! 6916: delim++; *token='\000'; /* initialize for next "value:" */
! 6917: if ( *delim == '\000' ) break; /* no more values */
! 6918: delim = strpspn(delim,":",token); /* chars preceding : outside () */
! 6919: if ( ncolons++ >= termval ) break; /* have corresponding term */
! 6920: } /* --- end-of-while(*delim!='\000')) --- */
! 6921: if ( *token != '\000' ) /* have x:x:value:x:x on right */
! 6922: termval=evalterm(store,token); /* so evaluate it */
! 6923: goto end_of_job; /* return result to caller */
! 6924: } /* --- end-of-if(*delim!='\000')) --- */
! 6925: /* -------------------------------------------------------------------------
! 6926: evaluate a+b recursively
! 6927: -------------------------------------------------------------------------- */
! 6928: /* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
! 6929: term = strpspn(term,"/+-*%",token); /* chars preceding /+-*% outside ()*/
! 6930: /* --- evaluate a+b, a-b, etc --- */
! 6931: if ( *term != '\000' ) { /* found arithmetic operation */
! 6932: int leftval=0, rightval=0; /* init leftval for unary +a or -a */
! 6933: if ( *token != '\000' ) /* or eval for binary a+b or a-b */
! 6934: if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
! 6935: == novalue ) goto end_of_job; /* return error if failed */
! 6936: if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
! 6937: == novalue ) goto end_of_job; /* return error if failed */
! 6938: switch ( *term ) { /* perform requested arithmetic */
! 6939: default: break; /* internal error */
! 6940: case '+': termval = leftval+rightval; break; /* addition */
! 6941: case '-': termval = leftval-rightval; break; /* subtraction */
! 6942: case '*': termval = leftval*rightval; break; /* multiplication */
! 6943: case '/': if ( rightval != 0 ) /* guard against divide by zero */
! 6944: termval = leftval/rightval; break; /* integer division */
! 6945: case '%': if ( rightval != 0 ) /* guard against divide by zero */
! 6946: termval = leftval%rightval; break; /*left modulo right */
! 6947: } /* --- end-of-switch(*relation) --- */
! 6948: goto end_of_job; /* return result to caller */
! 6949: } /* --- end-of-if(*term!='\000')) --- */
! 6950: /* -------------------------------------------------------------------------
! 6951: check for parenthesized expression or term of the form function(arg1,arg2,...)
! 6952: -------------------------------------------------------------------------- */
! 6953: if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
! 6954: /* --- strip trailing paren (if there hopefully is one) --- */
! 6955: int toklen = strlen(token); /* total #chars in token */
! 6956: if ( token[toklen-1] == ')' ) /* found matching ) at end of token*/
! 6957: token[--toklen] = '\000'; /* remove trailing ) */
! 6958: /* --- handle parenthesized subexpression --- */
! 6959: if ( *token == '(' ) { /* have parenthesized expression */
! 6960: strsqueeze(token,1); /* so squeeze out leading ( */
! 6961: /* --- evaluate edited term --- */
! 6962: trimwhite(token); /* trim leading/trailing whitespace*/
! 6963: termval = evalterm(store,token); } /* evaluate token recursively */
! 6964: /* --- handle function(arg1,arg2,...) --- */
! 6965: else { /* have function(arg1,arg2,...) */
! 6966: *delim = '\000'; /* separate function name and args */
! 6967: /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
! 6968: goto end_of_job; } /* return result to caller */
! 6969: /* -------------------------------------------------------------------------
! 6970: evaluate constants directly, or recursively evaluate variables, etc
! 6971: -------------------------------------------------------------------------- */
! 6972: if ( *token != '\000' ) { /* empty string */
! 6973: if ( isnumeric(token) ) /* have a constant */
! 6974: termval = atoi(token); /* convert ascii-to-int */
! 6975: else { /* variable or "stored proposition"*/
! 6976: termval = getstore(store,token); } /* look up token */
! 6977: } /* --- end-of-if(*token!=0) --- */
! 6978: /* -------------------------------------------------------------------------
! 6979: back to caller with truth value of proposition
! 6980: -------------------------------------------------------------------------- */
! 6981: end_of_job:
! 6982: /* --- back to caller --- */
! 6983: if ( evaltermdepth > 0 ) evaltermdepth--; /* pop recursion depth */
! 6984: return ( termval ); /* back to caller with value */
! 6985: } /* --- end-of-function evalterm() --- */
! 6986:
! 6987:
! 6988: /* ==========================================================================
! 6989: * Function: getstore ( store, identifier )
! 6990: * Purpose: finds identifier in store and returns corresponding value
! 6991: * --------------------------------------------------------------------------
! 6992: * Arguments: store (I) (STORE *)pointer to store containing
! 6993: * the desired identifier
! 6994: * identifier (I) (char *)pointer to null-terminated string
! 6995: * containing the identifier whose value
! 6996: * is to be returned
! 6997: * --------------------------------------------------------------------------
! 6998: * Returns: ( int ) identifier's corresponding value,
! 6999: * or 0 if identifier not found (or any error)
! 7000: * --------------------------------------------------------------------------
! 7001: * Notes: o
! 7002: * ======================================================================= */
! 7003: /* --- entry point --- */
! 7004: int getstore ( STORE *store, char *identifier )
! 7005: {
! 7006: /* -------------------------------------------------------------------------
! 7007: Allocations and Declarations
! 7008: -------------------------------------------------------------------------- */
! 7009: int value = 0; /* store[istore].value for identifier */
! 7010: int istore=0; /* store[] index containing identifier */
! 7011: char seek[512], hide[512]; /* identifier arg, identifier in store */
! 7012: /* --- first check args --- */
! 7013: if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
! 7014: strninit(seek,identifier,500); /* local copy of caller's identifier */
! 7015: trimwhite(seek); /* remove leading/trailing whitespace */
! 7016: /* --- loop over store --- */
! 7017: for ( istore=0; istore<MAXSTORE; istore++ ) { /* until end-of-table */
! 7018: char *idstore = store[istore].identifier; /* ptr to identifier in store */
! 7019: if ( isempty(idstore) ) /* empty id signals eot */
! 7020: break; /* de-reference any default/error value */
! 7021: strninit(hide,idstore,500); /* local copy of store[] identifier */
! 7022: trimwhite(hide); /* remove leading/trailing whitespace */
! 7023: if ( !strcmp(hide,seek) ) /* found match */
! 7024: break; /* de-reference corresponding value */
! 7025: } /* --- end-of-for(istore) --- */
! 7026: if ( store[istore].value != NULL ) /* address of int supplied */
! 7027: value = *(store[istore].value); /* return de-referenced int */
! 7028: end_of_job:
! 7029: return ( value ); /* store->values[istore] or NULL */
! 7030: } /* --- end-of-function getstore() --- */
! 7031:
! 7032:
! 7033: /* ==========================================================================
! 7034: * Functions: int unescape_url ( char *url, int isescape )
! 7035: * char x2c ( char *what )
! 7036: * Purpose: unescape_url replaces 3-character sequences %xx in url
! 7037: * with the single character represented by hex xx.
! 7038: * x2c returns the single character represented by hex xx
! 7039: * passed as a 2-character sequence in what.
! 7040: * --------------------------------------------------------------------------
! 7041: * Arguments: url (I) char * containing null-terminated
! 7042: * string with embedded %xx sequences
! 7043: * to be converted.
! 7044: * isescape (I) int containing 1 to _not_ unescape
! 7045: * \% sequences (0 would be NCSA default)
! 7046: * what (I) char * whose first 2 characters are
! 7047: * interpreted as ascii representations
! 7048: * of hex digits.
! 7049: * --------------------------------------------------------------------------
! 7050: * Returns: ( int ) unescape_url always returns 0.
! 7051: * ( char ) x2c returns the single char
! 7052: * corresponding to hex xx passed in what.
! 7053: * --------------------------------------------------------------------------
! 7054: * Notes: o These two functions were taken verbatim from util.c in
! 7055: * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
! 7056: * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
! 7057: * so unescape_url() can be safely applied to input which may or
! 7058: * may not have been url-encoded. (Note: currently, all calls
! 7059: * to unescape_url() pass iescape=0, so it's not used.)
! 7060: * o Added +++'s to blank xlation on 24-Sep-06
! 7061: * o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
! 7062: * ======================================================================= */
! 7063: /* --- entry point --- */
! 7064: int unescape_url(char *url, int isescape) {
! 7065: int x=0,y=0,prevescape=0,gotescape=0;
! 7066: int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
! 7067: int strreplace(); /* replace + with blank, if needed */
! 7068: char x2c();
! 7069: static char *hex="0123456789ABCDEFabcdef";
! 7070: /* ---
! 7071: * xlate ctrl chars to blanks
! 7072: * -------------------------- */
! 7073: if ( 1 ) { /* xlate ctrl chars to blanks */
! 7074: char *ctrlchars = "\n\t\v\b\r\f\a\015";
! 7075: int seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
! 7076: int urllen = strlen(url); /* total length of url string */
! 7077: /* --- first, entirely remove ctrlchars from beginning and end --- */
! 7078: if ( seglen > 0 ) { /*have ctrlchars at start of string*/
! 7079: strsqueeze(url,seglen); /* squeeze out initial ctrlchars */
! 7080: urllen -= seglen; } /* string is now shorter */
! 7081: while ( --urllen >= 0 ) /* now remove ctrlchars from end */
! 7082: if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
! 7083: url[urllen] = '\000'; /* re-terminate string before it */
! 7084: else break; /* or we're done */
! 7085: urllen++; /* length of url string */
! 7086: /* --- now, replace interior ctrlchars with ~ blanks --- */
! 7087: while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
! 7088: url[seglen] = '~'; /* replace ctrlchar with ~ */
! 7089: } /* --- end-of-if(1) --- */
! 7090: /* ---
! 7091: * xlate +'s to blanks if requested or if deemed necessary
! 7092: * ------------------------------------------------------- */
! 7093: if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/
! 7094: char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
! 7095: "+=+","+-+", NULL };
! 7096: int isearch = 0, /* searchfor[] index */
! 7097: nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
! 7098: /* --- locate occurrences of searchfor[] strings in url --- */
! 7099: for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
! 7100: char *psearch = url; /* start search at beginning */
! 7101: nfound[isearch] = 0; /* init #occurrences count */
! 7102: while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
! 7103: nfound[isearch] += 1; /* count another occurrence */
! 7104: psearch += strlen(searchfor[isearch]); } /*resume search after it*/
! 7105: } /* --- end-of-for(isearch) --- */
! 7106: /* --- apply some common-sense logic --- */
! 7107: if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */
! 7108: isplusblank = xlateplus = 0; /* so +++'s aren't blanks */
! 7109: if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
! 7110: if ( isplusblank != 0 ) /* and haven't disabled xlation */
! 7111: isplusblank = xlateplus = 1; /* so +++'s are blanks */
! 7112: else /* we have _both_ "%20" and "%2b" */
! 7113: xlateplus = 0; } /* tough call */
! 7114: if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */
! 7115: || nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */
! 7116: if ( isplusblank != 0 ) /* and haven't disabled xlation */
! 7117: xlateplus = 1; /* so xlate +++'s to blanks */
! 7118: } /* --- end-of-if(isplusblank==-1) --- */
! 7119: if ( xlateplus > 0 ) { /* want +'s xlated to blanks */
! 7120: char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
! 7121: while ( xlateplus > 0 ) { /* still have +++'s to xlate */
! 7122: char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
! 7123: plusses[xlateplus] = '\000'; /* null-terminate +++'s */
! 7124: strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
! 7125: xlateplus--; /* next shorter +++ string */
! 7126: } /* --- end-of-while(xlateplus>0) --- */
! 7127: } /* --- end-of-if(xlateplus) --- */
! 7128: isplusblank = 0; /* don't iterate this xlation */
! 7129: /* ---
! 7130: * xlate %nn to corresponding char
! 7131: * ------------------------------- */
! 7132: for(;url[y];++x,++y) {
! 7133: gotescape = prevescape;
! 7134: prevescape = (url[x]=='\\');
! 7135: if((url[x] = url[y]) == '%')
! 7136: if(!isescape || !gotescape)
! 7137: if(isthischar(url[y+1],hex)
! 7138: && isthischar(url[y+2],hex))
! 7139: { url[x] = x2c(&url[y+1]);
! 7140: y+=2; }
! 7141: }
! 7142: url[x] = '\0';
! 7143: return 0;
! 7144: } /* --- end-of-function unescape_url() --- */
! 7145: /* --- entry point --- */
! 7146: char x2c(char *what) {
! 7147: char digit;
! 7148: digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
! 7149: digit *= 16;
! 7150: digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
! 7151: return(digit);
! 7152: } /* --- end-of-function x2c() --- */
! 7153: #endif /* PART2 */
! 7154:
! 7155: /* ---
! 7156: * PART3
! 7157: * ------ */
! 7158: #if !defined(PARTS) || defined(PART3)
! 7159: /* ==========================================================================
! 7160: * Function: rasterize ( expression, size )
! 7161: * Purpose: returns subraster corresponding to (a valid LaTeX) expression
! 7162: * at font size
! 7163: * --------------------------------------------------------------------------
! 7164: * Arguments: expression (I) char * to first char of null-terminated
! 7165: * string containing valid LaTeX expression
! 7166: * to be rasterized
! 7167: * size (I) int containing 0-4 default font size
! 7168: * --------------------------------------------------------------------------
! 7169: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
! 7170: * or NULL for any parsing error.
! 7171: * --------------------------------------------------------------------------
! 7172: * Notes: o This is mimeTeX's "main" reusable entry point. Easy to use:
! 7173: * just call it with a LaTeX expression, and get back a bitmap
! 7174: * of that expression. Then do what you want with the bitmap.
! 7175: * ======================================================================= */
! 7176: /* --- entry point --- */
! 7177: subraster *rasterize ( char *expression, int size )
1.1 albertel 7178: {
7179: /* -------------------------------------------------------------------------
7180: Allocations and Declarations
7181: -------------------------------------------------------------------------- */
1.3 albertel 7182: char *preamble(), pretext[512]; /* process preamble, if present */
7183: char chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
1.1 albertel 7184: *subexpr = chartoken; /* token may be parenthesized expr */
7185: int isbrace(); /* check subexpr for braces */
7186: mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/
1.3 albertel 7187: int ligdef, get_ligature(); /*get symtable[] index for ligature*/
1.1 albertel 7188: int natoms=0; /* #atoms/tokens processed so far */
7189: int type_raster(); /* display debugging output */
7190: subraster *rasterize(), /* recurse */
7191: *rastparen(), /* handle parenthesized subexpr's */
7192: *rastlimits(); /* handle sub/superscripted expr's */
7193: subraster *rastcat(), /* concatanate atom subrasters */
7194: *subrastcpy(), /* copy final result if a charaster*/
7195: *new_subraster(); /* new subraster for isstring mode */
7196: subraster *get_charsubraster(), /* character subraster */
7197: *sp=NULL, *prevsp=NULL, /* raster for current, prev char */
7198: *expraster = (subraster *)NULL; /* raster returned to caller */
7199: int delete_subraster(); /* free everything before returning*/
1.3 albertel 7200: int family = fontinfo[fontnum].family; /* current font family */
7201: int isleftscript = 0, /* true if left-hand term scripted */
7202: wasscripted = 0, /* true if preceding token scripted*/
7203: wasdelimscript = 0; /* true if preceding delim scripted*/
1.1 albertel 7204: /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
1.5 ! raeburn 7205: char *strdetex(); /* detex token for error message */
1.1 albertel 7206: /* --- global values saved/restored at each recursive iteration --- */
1.2 albertel 7207: int wasstring = isstring, /* initial isstring mode flag */
1.1 albertel 7208: wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
1.2 albertel 7209: oldfontnum = fontnum, /* initial font family */
1.1 albertel 7210: oldfontsize = fontsize, /* initial fontsize */
7211: olddisplaysize = displaysize, /* initial \displaystyle size */
7212: oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
1.2 albertel 7213: oldsmashmargin = smashmargin, /* initial smashmargin */
7214: oldissmashdelta = issmashdelta, /* initial issmashdelta */
1.3 albertel 7215: oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
7216: oldisscripted = isscripted, /* initial isscripted */
1.1 albertel 7217: *oldworkingparam = workingparam; /* initial working parameter */
7218: subraster *oldworkingbox = workingbox, /* initial working box */
7219: *oldleftexpression = leftexpression; /*left half rasterized so far*/
7220: double oldunitlength = unitlength; /* initial unitlength */
7221: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
7222: /* -------------------------------------------------------------------------
7223: initialization
7224: -------------------------------------------------------------------------- */
7225: recurlevel++; /* wind up one more recursion level*/
7226: leftexpression = NULL; /* no leading left half yet */
7227: isreplaceleft = 0; /* reset replaceleft flag */
1.3 albertel 7228: if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */
1.1 albertel 7229: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
7230: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
1.5 ! raeburn 7231: rastlift = 0; /* reset global rastraise() lift */
1.3 albertel 7232: if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/
7233: fprintf(msgfp,
7234: "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
7235: recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
1.1 albertel 7236: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
7237: /* -------------------------------------------------------------------------
7238: preocess optional $-terminated preamble preceding expression
7239: -------------------------------------------------------------------------- */
7240: expression = preamble(expression,&size,pretext); /* size may be modified */
7241: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
7242: fontsize = size; /* start at requested size */
7243: if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/
7244: if ( !ispreambledollars ) /* style fixed by $$...$$'s */
7245: isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
7246: /* -------------------------------------------------------------------------
7247: build up raster one character (or subexpression) at a time
7248: -------------------------------------------------------------------------- */
7249: while ( 1 )
7250: {
1.3 albertel 7251: /* --- kludge for \= cyrillic ligature --- */
7252: isligature = 0; /* no ligature found yet */
7253: family = fontinfo[fontnum].family; /* current font family */
7254: if ( family == CYR10 ) /* may have cyrillic \= ligature */
7255: if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
7256: >= 0 ) /* got some ligature */
7257: if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
7258: isligature = 1; /* signal \= ligature */
1.1 albertel 7259: /* --- get next character/token or subexpression --- */
1.3 albertel 7260: subexprptr = expression; /* ptr within expression to subexpr*/
1.1 albertel 7261: expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
7262: subexpr = chartoken; /* "local" copy of chartoken ptr */
7263: leftsymdef = NULL; /* no character identified yet */
7264: sp = NULL; /* no subraster yet */
7265: size = fontsize; /* in case reset by \tiny, etc */
1.3 albertel 7266: /*isleftscript = isdelimscript;*/ /*was preceding term scripted delim*/
7267: wasscripted = isscripted; /* true if preceding token scripted*/
7268: wasdelimscript = isdelimscript; /* preceding \right delim scripted */
7269: if(1)isscripted = 0; /* no subscripted expression yet */
7270: isdelimscript = 0; /* reset \right delim scripted flag*/
1.1 albertel 7271: /* --- debugging output --- */
1.3 albertel 7272: if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */
7273: fprintf(msgfp,
7274: "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
7275: recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
1.1 albertel 7276: if ( expression == NULL /* no more tokens */
7277: && *subexpr == '\000' ) break; /* and this token empty */
7278: if ( *subexpr == '\000' ) break; /* enough if just this token empty */
7279: /* --- check for parenthesized subexpression --- */
7280: if ( isbrace(subexpr,LEFTBRACES,1) ) /* got parenthesized subexpression */
7281: { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
7282: == NULL ) continue; } /* flush it if failed to rasterize */
7283: else /* --- single-character atomic token --- */
7284: if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
7285: {
1.3 albertel 7286: /* --- first check for opening $ in \text{ if $n-m$ even} --- */
7287: if ( istextmode /* we're in \text mode */
7288: && *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
7289: char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
7290: int exprlen = 0; /* length of $expression$ */
7291: int textfontnum = fontnum; /* current text font number */
7292: /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
7293: if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
7294: exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
7295: else { /* no closing $ found */
7296: exprlen = strlen(expression); /* just assume entire expression */
7297: endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
7298: exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */
7299: if ( exprlen > 0 ) { /* have something between $$ */
7300: memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
7301: mathexpr[exprlen] = '\000'; /* null-terminate it */
7302: fontnum = 0; /* set math mode */
7303: sp = rasterize(mathexpr,size); /* and rasterize $expression$ */
7304: fontnum = textfontnum; } /* set back to text mode */
7305: expression = endptr+1; /* push expression past closing $ */
7306: } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
7307: else
7308: /* --- otherwise, look up mathchardef for atomic token in table --- */
7309: if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
7310: == NULL ) /* lookup failed */
7311: { char literal[512] = "[?]"; /*display for unrecognized literal*/
7312: int oldfontnum = fontnum; /* error display in default mode */
7313: if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
1.1 albertel 7314: { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
7315: chartoken); fflush(msgfp); }
1.3 albertel 7316: sp = (subraster *)NULL; /* init to signal failure */
7317: if ( warninglevel < 1 ) continue; /* warnings not wanted */
7318: fontnum = 0; /* reset from \mathbb, etc */
7319: if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
7320: { /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
1.5 ! raeburn 7321: strcpy(literal,"{\\rm~["); /* init error message token */
! 7322: strcat(literal,strdetex(chartoken,0)); /* detex the token */
! 7323: strcat(literal,"?]}"); } /* add closing ? and brace */
1.3 albertel 7324: sp = rasterize(literal,size-1); /* rasterize literal token */
7325: fontnum = oldfontnum; /* reset font family */
7326: if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
7327: else /* --- check if we have special handler to process this token --- */
7328: if ( symdef->handler != NULL ) /* have a handler for this token */
7329: { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
7330: if ( (sp = (subraster *) /* returned void* is subraster* */
7331: (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
7332: continue; } /* flush token if handler failed */
7333: else /* --- no handler, so just get subraster for this character --- */
7334: if ( !isstring ) /* rasterizing */
7335: { if ( isligature ) /* found a ligature */
7336: expression = subexprptr + strlen(symdef->symbol); /*push past it*/
7337: if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
1.1 albertel 7338: == NULL ) continue; } /* flush token if failed */
1.3 albertel 7339: else /* constructing ascii string */
1.1 albertel 7340: { char *symbol = symdef->symbol; /* symbol for ascii string */
7341: int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
7342: if ( symlen < 1 ) continue; /* no symbol for ascii string */
7343: if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
7344: == NULL ) continue; /* flush token if malloc failed */
7345: sp->type = ASCIISTRING; /* set subraster type */
7346: sp->symdef = symdef; /* and set symbol definition */
7347: sp->baseline = 1; /* default (should be unused) */
7348: strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
7349: /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
1.3 albertel 7350: } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
1.1 albertel 7351: /* --- handle any super/subscripts following symbol or subexpression --- */
7352: sp = rastlimits(&expression,size,sp);
1.3 albertel 7353: isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
1.1 albertel 7354: /* --- debugging output --- */
1.3 albertel 7355: if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */
7356: fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
7357: recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
7358: if ( msglevel >= 9 ) fprintf(msgfp,
7359: " isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
7360: isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
7361: if ( msglevel >= 99 )
7362: if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
7363: fflush(msgfp); } /* flush msgfp buffer */
1.1 albertel 7364: /* --- accumulate atom or parenthesized subexpression --- */
7365: if ( natoms < 1 /* nothing previous to concat */
7366: || expraster == NULL /* or previous was complete error */
7367: || isreplaceleft ) /* or we're replacing previous */
1.2 albertel 7368: { if ( 1 && expraster!=NULL ) /* probably replacing left */
7369: delete_subraster(expraster); /* so first free original left */
7370: expraster = subrastcpy(sp); /* copy static CHARASTER or left */
1.1 albertel 7371: isreplaceleft = 0; } /* reset replacement flag */
7372: else /*we've already built up atoms so...*/
1.3 albertel 7373: if ( sp != NULL ) { /* ...if we have a new term */
7374: int prevsmashmargin = smashmargin; /* save current smash margin */
7375: if ( isleftscript ) { /* don't smash against scripts */
7376: isdelimscript = 0; /* reset \right delim scripted flag*/
7377: if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
7378: expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
7379: smashmargin = prevsmashmargin; } /* restore current smash margin */
1.1 albertel 7380: delete_subraster(prevsp); /* free prev (if not a CHARASTER) */
7381: prevsp = sp; /* current becomes previous */
7382: leftexpression = expraster; /* left half rasterized so far */
7383: /* --- bump count --- */
7384: natoms++; /* bump #atoms count */
7385: } /* --- end-of-while(expression!=NULL) --- */
7386: /* -------------------------------------------------------------------------
7387: back to caller with rasterized expression
7388: -------------------------------------------------------------------------- */
7389: end_of_job:
7390: delete_subraster(prevsp); /* free last (if not a CHARASTER) */
7391: /* --- debugging output --- */
7392: if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
7393: { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
7394: recurlevel,natoms);
7395: if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
7396: type_raster(expraster->image,msgfp); /* display completed raster */
7397: fflush(msgfp); } /* flush msgfp buffer */
1.2 albertel 7398: /* --- set final raster buffer --- */
7399: if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
1.3 albertel 7400: { int type = expraster->type; /* type of constructed image */
7401: if ( type != FRACRASTER ) /* leave \frac alone */
7402: expraster->type = IMAGERASTER; /* set type to constructed image */
1.2 albertel 7403: if ( istextmode ) /* but in text mode */
7404: expraster->type = blanksignal; /* set type to avoid smash */
7405: expraster->size = fontsize; } /* set original input font size */
1.1 albertel 7406: /* --- restore flags/values to original saved values --- */
7407: isstring = wasstring; /* string mode reset */
7408: isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
1.2 albertel 7409: fontnum = oldfontnum; /* font family reset */
1.1 albertel 7410: fontsize = oldfontsize; /* fontsize reset */
7411: displaysize = olddisplaysize; /* \displaystyle size reset */
7412: shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
1.2 albertel 7413: smashmargin = oldsmashmargin; /* smashmargin reset */
7414: issmashdelta = oldissmashdelta; /* issmashdelta reset */
1.3 albertel 7415: isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */
7416: isscripted = oldisscripted; /* isscripted reset */
1.1 albertel 7417: workingparam = oldworkingparam; /* working parameter reset */
7418: workingbox = oldworkingbox; /* working box reset */
7419: leftexpression = oldleftexpression; /* leftexpression reset */
7420: leftsymdef = oldleftsymdef; /* leftsymdef reset */
7421: unitlength = oldunitlength; /* unitlength reset */
1.5 ! raeburn 7422: iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
1.1 albertel 7423: recurlevel--; /* unwind one recursion level */
7424: /* --- return final subraster to caller --- */
7425: return ( expraster );
7426: } /* --- end-of-function rasterize() --- */
7427:
7428:
7429: /* ==========================================================================
7430: * Function: rastparen ( subexpr, size, basesp )
7431: * Purpose: parentheses handler, returns a subraster corresponding to
7432: * parenthesized subexpression at font size
7433: * --------------------------------------------------------------------------
7434: * Arguments: subexpr (I) char ** to first char of null-terminated
7435: * string beginning with a LEFTBRACES
7436: * to be rasterized
1.5 ! raeburn 7437: * size (I) int containing 0-7 default font size
1.1 albertel 7438: * basesp (I) subraster * to character (or subexpression)
7439: * immediately preceding leading left{
7440: * (unused, but passed for consistency)
7441: * --------------------------------------------------------------------------
7442: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
7443: * or NULL for any parsing error
7444: * --------------------------------------------------------------------------
7445: * Notes: o This "handler" isn't in the mathchardef symbol table,
7446: * but is called directly from rasterize(), as necessary.
7447: * o Though subexpr is returned unchanged, it's passed as char **
7448: * for consistency with other handlers. Ditto, basesp is unused
7449: * but passed for consistency
7450: * ======================================================================= */
7451: /* --- entry point --- */
7452: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
7453: {
7454: /* -------------------------------------------------------------------------
7455: Allocations and Declarations
7456: -------------------------------------------------------------------------- */
7457: char *expression = *subexpr; /* dereference subexpr to get char* */
7458: int explen = strlen(expression); /* total #chars, including parens */
7459: int isescape = 0, /* true if parens \escaped */
7460: isrightdot = 0, /* true if right paren is \right. */
7461: isleftdot = 0; /* true if left paren is \left. */
1.3 albertel 7462: char left[32], right[32]; /* parens enclosing expresion */
7463: char noparens[MAXSUBXSZ+1]; /* get subexpr without parens */
1.1 albertel 7464: subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */
7465: int isheight = 1; /*true=full height, false=baseline*/
7466: int height, /* height of rasterized noparens[] */
7467: baseline; /* and its baseline */
1.2 albertel 7468: int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
1.1 albertel 7469: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
7470: subraster *rastcat(); /* concatanate subrasters */
7471: int delete_subraster(); /*in case of error after allocation*/
7472: /* -------------------------------------------------------------------------
7473: rasterize "interior" of expression, i.e., without enclosing parens
7474: -------------------------------------------------------------------------- */
7475: /* --- first see if enclosing parens are \escaped --- */
7476: if ( isthischar(*expression,ESCAPE) ) /* expression begins with \escape */
7477: isescape = 1; /* so set flag accordingly */
7478: /* --- get expression *without* enclosing parens --- */
7479: strcpy(noparens,expression); /* get local copy of expression */
7480: noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
1.5 ! raeburn 7481: strsqueeze(noparens,(1+isescape)); /* and then squeeze out left{ */
1.1 albertel 7482: /* --- rasterize it --- */
7483: if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
7484: == NULL ) goto end_of_job; /* quit if failed */
7485: /* --- no need to add parentheses for unescaped { --- */
7486: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
7487: goto end_of_job; /* just return sp to caller */
7488: /* -------------------------------------------------------------------------
7489: obtain paren characters to enclose noparens[] raster with
7490: -------------------------------------------------------------------------- */
7491: /* --- first get left and right parens from expression --- */
7492: memset(left,0,16); memset(right,0,16); /* init parens with nulls */
7493: left[0] = *(expression+isescape); /* left{ is 1st or 2nd char */
7494: right[0] = *(expression+explen-1); /* right} is always last char */
7495: isleftdot = (isescape && isthischar(*left,".")); /* true if \left. */
7496: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
7497: /* --- need height of noparens[] raster as minimum parens height --- */
7498: height = (sp->image)->height; /* height of noparens[] raster */
7499: baseline = sp->baseline; /* baseline of noparens[] raster */
7500: if ( !isheight ) height = baseline+1; /* parens only enclose baseline up */
7501: /* --- get best-fit parentheses characters --- */
7502: if ( !isleftdot ) /* if not \left. */
7503: lp = get_delim(left,height+1,family); /* get left paren char */
7504: if ( !isrightdot ) /* and if not \right. */
7505: rp = get_delim(right,height+1,family); /* get right paren char */
7506: if ( (lp==NULL && !isleftdot) /* check that we got left( */
7507: || (rp==NULL && !isrightdot) ) /* and right) if needed */
7508: { delete_subraster(sp); /* if failed, free subraster */
7509: if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
7510: if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
7511: sp = (subraster *)NULL; /* signal error to caller */
7512: goto end_of_job; } /* and quit */
7513: /* -------------------------------------------------------------------------
7514: set paren baselines to center on noparens[] raster, and concat components
7515: -------------------------------------------------------------------------- */
7516: /* --- set baselines to center paren chars on raster --- */
7517: if ( lp != NULL ) /* ignore for \left. */
7518: lp->baseline = baseline + ((lp->image)->height - height)/2;
7519: if ( rp != NULL ) /* ignore for \right. */
7520: rp->baseline = baseline + ((rp->image)->height - height)/2;
7521: /* --- concat lp||sp||rp to obtain final result --- */
7522: if ( lp != NULL ) /* ignore \left. */
7523: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
7524: if ( sp != NULL ) /* succeeded or ignored \left. */
7525: if ( rp != NULL ) /* ignore \right. */
7526: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
7527: /* --- back to caller --- */
7528: end_of_job:
7529: return ( sp );
7530: } /* --- end-of-function rastparen() --- */
7531:
7532:
7533: /* ==========================================================================
7534: * Function: rastlimits ( expression, size, basesp )
7535: * Purpose: \limits, \nolimts, _ and ^ handler,
7536: * dispatches call to rastscripts() or to rastdispmath()
7537: * as necessary, to handle sub/superscripts following symbol
7538: * --------------------------------------------------------------------------
7539: * Arguments: expression (I) char ** to first char of null-terminated
7540: * LaTeX expression (unused/unchanged)
7541: * size (I) int containing base font size (not used,
7542: * just stored in subraster)
7543: * basesp (I) subraster * to current character (or
7544: * subexpression) immediately preceding script
7545: * indicator
7546: * --------------------------------------------------------------------------
7547: * Returns: ( subraster * ) ptr to subraster returned by rastscripts()
7548: * or rastdispmath(), or NULL for any error
7549: * --------------------------------------------------------------------------
7550: * Notes: o
7551: * ======================================================================= */
7552: /* --- entry point --- */
7553: subraster *rastlimits ( char **expression, int size, subraster *basesp )
7554: {
7555: /* -------------------------------------------------------------------------
7556: Allocations and Declarations
7557: -------------------------------------------------------------------------- */
7558: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
7559: *rastcat(), /* may need to concat scripts */
1.3 albertel 7560: *rasterize(), /* may need to construct dummy base*/
7561: *scriptsp = basesp, /* and this will become the result */
7562: *dummybase = basesp; /* for {}_i construct a dummy base */
1.1 albertel 7563: int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
1.2 albertel 7564: int oldsmashmargin = smashmargin; /* save original smashmargin */
1.1 albertel 7565: int type_raster(); /* display debugging output */
1.3 albertel 7566: int delete_subraster(); /* free dummybase, if necessary */
7567: int rastsmashcheck(); /* check if okay to smash scripts */
1.1 albertel 7568: /* --- to check for \limits or \nolimits preceding scripts --- */
7569: char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
7570: int toklen=0; /* strlen(limtoken) */
7571: mathchardef *tokdef, *get_symdef(); /* mathchardef struct for limtoken */
7572: int class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
7573: /* -------------------------------------------------------------------------
7574: determine whether or not to use displaymath
7575: -------------------------------------------------------------------------- */
7576: scriptlevel++; /* first, increment subscript level*/
7577: *limtoken = '\000'; /* no token yet */
1.3 albertel 7578: isscripted = 0; /* signal term not (text) scripted */
1.1 albertel 7579: if ( msgfp!=NULL && msglevel>=999 )
7580: { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
7581: scriptlevel,(exprptr==NULL?"null":exprptr)); fflush(msgfp); }
7582: if ( isstring ) goto end_of_job; /* no scripts for ascii string */
7583: /* --- check for \limits or \nolimits --- */
7584: skipwhite(exprptr); /* skip white space before \limits */
1.5 ! raeburn 7585: if ( !isempty(exprptr) ) /* non-empty expression supplied */
1.1 albertel 7586: exprptr = texchar(exprptr,limtoken); /* retrieve next token */
7587: if ( *limtoken != '\000' ) /* have token */
7588: if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
7589: if ( memcmp("\\limits",limtoken,toklen) == 0 /* may be \limits */
7590: || memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
7591: if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */
1.3 albertel 7592: != NULL ) { /* found token in table */
1.1 albertel 7593: if ( strcmp("\\limits",tokdef->symbol) == 0 ) /* found \limits */
7594: isdisplay = 1; /* so explicitly set displaymath */
7595: else /* wasn't \limits */
7596: if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
1.3 albertel 7597: isdisplay = 0; } /* so explicitly reset displaymath */
1.1 albertel 7598: /* --- see if we found \[no]limits --- */
7599: if ( isdisplay != (-1) ) /* explicit directive found */
7600: *expression = exprptr; /* so bump expression past it */
7601: else /* noexplicit directive */
7602: { isdisplay = 0; /* init displaymath flag off */
1.3 albertel 7603: if ( isdisplaystyle ) { /* we're in displaystyle math mode */
1.1 albertel 7604: if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */
7605: { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
7606: isdisplay = 1; } /* set flag if mode forced true */
7607: else
7608: if ( isdisplaystyle >= 2 ) /*or mode forced conditionally true*/
7609: { if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
7610: && class!=OPENING && class!=CLOSING /*don't force ('s and )'s*/
7611: && class!=BINARYOP /* don't force binary operators */
7612: && class!=NOVALUE ) /* finally, don't force "images" */
7613: isdisplay = 1; } /* set flag if mode forced true */
7614: else /* determine mode from base symbol */
7615: if ( class == DISPOPER ) /* it's a displaystyle operator */
1.3 albertel 7616: isdisplay = 1; } } /* so set flag */
1.1 albertel 7617: /* -------------------------------------------------------------------------
7618: dispatch call to create sub/superscripts
7619: -------------------------------------------------------------------------- */
7620: if ( isdisplay ) /* scripts above/below base symbol */
7621: scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
1.3 albertel 7622: else { /* scripts alongside base symbol */
7623: if ( dummybase == NULL ) /* no base symbol preceding scripts*/
7624: dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
7625: issmashokay = 1; /*haven't found a no-smash char yet*/
7626: if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
1.1 albertel 7627: scriptsp = basesp; /* so just return unscripted symbol*/
1.3 albertel 7628: else { /* symbols followed by scripts */
7629: isscripted = 1; /*signal current term text-scripted*/
1.1 albertel 7630: if ( basesp != NULL ) /* have base symbol */
1.3 albertel 7631: { /*if(0)smashmargin = 0;*/ /*don't smash script (doesn't work)*/
1.2 albertel 7632: /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
1.3 albertel 7633: /* --- smash (or just concat) script raster against base symbol --- */
7634: if ( !issmashokay ) /* don't smash leading - */
7635: if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
1.2 albertel 7636: scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
1.3 albertel 7637: if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
7638: /* --- smash (or just concat) scripted term to stuff to its left --- */
7639: issmashokay = 1; /* okay to smash base expression */
7640: if ( 0 && smashcheck > 1 ) /* smashcheck=2 to check base */
7641: /* note -- we _don't_ have base expression available to check */
7642: issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
7643: if ( !issmashokay ) /* don't smash leading - */
7644: if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
7645: scriptsp->size = size; } } } /* and set font size */
1.1 albertel 7646: end_of_job:
1.2 albertel 7647: smashmargin = oldsmashmargin; /* reset original smashmargin */
1.3 albertel 7648: if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
1.1 albertel 7649: if ( msgfp!=NULL && msglevel>=99 )
7650: { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
7651: scriptlevel,(scriptsp==NULL?"null":"..."));
7652: if ( scriptsp != NULL ) /* have a constructed raster */
7653: type_raster(scriptsp->image,msgfp); /*display constructed raster*/
7654: fflush(msgfp); }
7655: scriptlevel--; /*lastly, decrement subscript level*/
7656: return ( scriptsp );
7657: } /* --- end-of-function rastlimits() --- */
7658:
7659:
7660: /* ==========================================================================
7661: * Function: rastscripts ( expression, size, basesp )
7662: * Purpose: super/subscript handler, returns subraster for the leading
7663: * scripts in expression, whose base symbol is at font size
7664: * --------------------------------------------------------------------------
7665: * Arguments: expression (I/O) char ** to first char of null-terminated
7666: * string beginning with a super/subscript,
7667: * and returning ptr immediately following
7668: * last script character processed.
1.5 ! raeburn 7669: * size (I) int containing 0-7 default font size
1.1 albertel 7670: * basesp (I) subraster * to character (or subexpression)
7671: * immediately preceding leading script
7672: * (scripts will be placed relative to base)
7673: * --------------------------------------------------------------------------
7674: * Returns: ( subraster * ) ptr to subraster corresponding to scripts,
7675: * or NULL for any parsing error
7676: * --------------------------------------------------------------------------
7677: * Notes: o This "handler" isn't in the mathchardef symbol table,
7678: * but is called directly from rasterize(), as necessary.
7679: * ======================================================================= */
7680: /* --- entry point --- */
7681: subraster *rastscripts ( char **expression, int size, subraster *basesp )
7682: {
7683: /* -------------------------------------------------------------------------
7684: Allocations and Declarations
7685: -------------------------------------------------------------------------- */
7686: char *texscripts(), /* parse expression for scripts */
7687: subscript[512], supscript[512]; /* scripts parsed from expression */
7688: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
7689: subraster *new_subraster(), *sp=NULL, /* super- over subscript subraster */
7690: *rastack(); /*sets scripts in displaymath mode*/
7691: raster *rp=NULL; /* image raster embedded in sp */
7692: int height=0, width=0, baseline=0, /* height,width,baseline of sp */
7693: subht=0, subwidth=0, subln=0, /* height,width,baseline of sub */
7694: supht=0, supwidth=0, supln=0, /* height,width,baseline of sup */
7695: baseht=0, baseln=0; /* height,baseline of base */
7696: int bdescend=0, sdescend=0; /* descender of base, subscript */
7697: int issub=0, issup=0, isboth=0, /* true if we have sub,sup,both */
7698: isbase=0; /* true if we have base symbol */
7699: int szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
7700: vbetween = 2, /* vertical space between scripts */
7701: vabove = szval+1, /*sup's top/bot above base's top/bot*/
7702: vbelow = szval+1, /*sub's top/bot below base's top/bot*/
7703: vbottom = szval+1; /*sup's bot above (sub's below) bsln*/
7704: /*int istweak = 1;*/ /* true to tweak script positioning */
7705: int rastput(); /*put scripts in constructed raster*/
7706: int delete_subraster(); /* free work areas */
1.3 albertel 7707: int rastsmashcheck(); /* check if okay to smash scripts */
1.1 albertel 7708: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
7709: /* -------------------------------------------------------------------------
7710: Obtain subscript and/or superscript expressions, and rasterize them/it
7711: -------------------------------------------------------------------------- */
7712: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
7713: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
7714: if ( *expression == NULL ) goto end_of_job; /* no expression given */
7715: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
7716: *expression = texscripts(*expression,subscript,supscript,3);
7717: /* --- rasterize scripts --- */
7718: if ( *subscript != '\000' ) /* have a subscript */
7719: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
7720: if ( *supscript != '\000' ) /* have a superscript */
7721: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
7722: /* --- set flags for convenience --- */
7723: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
7724: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
7725: isboth = (issub && issup); /* true if we have both */
7726: if (!issub && !issup) goto end_of_job; /* quit if we have neither */
1.3 albertel 7727: /* --- check for leading no-smash chars (if enabled) --- */
7728: issmashokay = 0; /* default, don't smash scripts */
7729: if ( smashcheck > 0 ) { /* smash checking wanted */
7730: issmashokay = 1; /*haven't found a no-smash char yet*/
7731: if ( issub ) /* got a subscript */
7732: issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
7733: if ( issmashokay ) /* clean sub, so check sup */
7734: if ( issup ) /* got a superscript */
7735: issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
7736: } /* --- end-of-if(smashcheck>0) --- */
1.1 albertel 7737: /* -------------------------------------------------------------------------
7738: get height, width, baseline of scripts, and height, baseline of base symbol
7739: -------------------------------------------------------------------------- */
7740: /* --- get height and width of components --- */
7741: if ( issub ) /* we have a subscript */
7742: { subht = (subsp->image)->height; /* so get its height */
7743: subwidth = (subsp->image)->width; /* and width */
7744: subln = subsp->baseline; } /* and baseline */
7745: if ( issup ) /* we have a superscript */
7746: { supht = (supsp->image)->height; /* so get its height */
7747: supwidth = (supsp->image)->width; /* and width */
7748: supln = supsp->baseline; } /* and baseline */
7749: /* --- get height and baseline of base, and descender of base and sub --- */
7750: if ( basesp == (subraster *)NULL ) /* no base symbol for scripts */
7751: basesp = leftexpression; /* try using left side thus far */
7752: if ( basesp != (subraster *)NULL ) /* we have base symbol for scripts */
7753: { baseht = (basesp->image)->height; /* height of base symbol */
7754: baseln = basesp->baseline; /* and its baseline */
7755: bdescend = baseht-(baseln+1); /* and base symbol descender */
7756: sdescend = bdescend + vbelow; /*sub must descend by at least this*/
7757: if ( baseht > 0 ) isbase = 1; } /* set flag */
7758: /* -------------------------------------------------------------------------
7759: determine width of constructed raster
7760: -------------------------------------------------------------------------- */
7761: width = max2(subwidth,supwidth); /*widest component is overall width*/
7762: /* -------------------------------------------------------------------------
7763: determine height and baseline of constructed raster
7764: -------------------------------------------------------------------------- */
7765: /* --- both super/subscript --- */
7766: if ( isboth ) /*we have subscript and superscript*/
7767: { height = max2(subht+vbetween+supht, /* script heights + space bewteen */
7768: vbelow+baseht+vabove); /*sub below base bot, sup above top*/
7769: baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
7770: /* --- superscript only --- */
7771: if ( !issub ) /* we only have a superscript */
7772: { height = max3(baseln+1+vabove, /* sup's top above base symbol top */
7773: supht+vbottom, /* sup's bot above baseln */
7774: supht+vabove-bdescend); /* sup's bot above base symbol bot */
7775: baseline = height-1; } /*sup's baseline at bottom of raster*/
7776: /* --- subscript only --- */
1.3 albertel 7777: if ( !issup ) { /* we only have a subscript */
1.1 albertel 7778: if ( subht > sdescend ) /*sub can descend below base bot...*/
7779: { height = subht; /* ...without extra space on top */
7780: baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
7781: baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
7782: else /* sub's top will be below baseln */
7783: { height = sdescend+1; /* sub's bot below base symbol bot */
1.3 albertel 7784: baseline = 0; } } /* sub's baseline at top of raster */
1.1 albertel 7785: /* -------------------------------------------------------------------------
7786: construct raster with superscript over subscript
7787: -------------------------------------------------------------------------- */
7788: /* --- allocate subraster containing constructed raster --- */
7789: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
7790: == NULL ) /* and if we fail to allocate */
7791: goto end_of_job; /* quit */
7792: /* --- initialize subraster parameters --- */
7793: sp->type = IMAGERASTER; /* set type as constructed image */
7794: sp->size = size; /* set given size */
7795: sp->baseline = baseline; /* composite scripts baseline */
7796: rp = sp->image; /* raster embedded in subraster */
7797: /* --- place super/subscripts in new raster --- */
7798: if ( issup ) /* we have a superscript */
7799: rastput(rp,supsp->image,0,0,1); /* it goes in upper-left corner */
7800: if ( issub ) /* we have a subscript */
7801: rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
7802: /* -------------------------------------------------------------------------
7803: free unneeded component subrasters and return final result to caller
7804: -------------------------------------------------------------------------- */
7805: end_of_job:
7806: if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
7807: if ( issup ) delete_subraster(supsp); /* and superscript */
7808: return ( sp );
7809: } /* --- end-of-function rastscripts() --- */
7810:
7811:
7812: /* ==========================================================================
7813: * Function: rastdispmath ( expression, size, sp )
7814: * Purpose: displaymath handler, returns sp along with
7815: * its immediately following super/subscripts
7816: * --------------------------------------------------------------------------
7817: * Arguments: expression (I/O) char ** to first char of null-terminated
7818: * string immediately following sp to be
7819: * rasterized along with its super/subscripts,
7820: * and returning ptr immediately following last
7821: * character processed.
1.5 ! raeburn 7822: * size (I) int containing 0-7 default font size
1.1 albertel 7823: * sp (I) subraster * to display math operator
7824: * to which super/subscripts will be added
7825: * --------------------------------------------------------------------------
7826: * Returns: ( subraster * ) ptr to subraster corresponding to sp
7827: * plus its scripts, or NULL for any error
7828: * --------------------------------------------------------------------------
7829: * Notes: o sp returned unchanged if no super/subscript(s) follow it.
7830: * ======================================================================= */
7831: /* --- entry point --- */
7832: subraster *rastdispmath ( char **expression, int size, subraster *sp )
7833: {
7834: /* -------------------------------------------------------------------------
7835: Allocations and Declarations
7836: -------------------------------------------------------------------------- */
7837: char *texscripts(), /* parse expression for scripts */
7838: subscript[512], supscript[512]; /* scripts parsed from expression */
7839: int issub=0, issup=0; /* true if we have sub,sup */
7840: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
7841: *rastack(), /* stack operator with scripts */
7842: *new_subraster(); /* for dummy base sp, if needed */
7843: int vspace = 1; /* vertical space between scripts */
7844: /* -------------------------------------------------------------------------
7845: Obtain subscript and/or superscript expressions, and rasterize them/it
7846: -------------------------------------------------------------------------- */
7847: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
7848: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
7849: if ( *expression == NULL ) goto end_of_job; /* no expression given */
7850: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
7851: *expression = texscripts(*expression,subscript,supscript,3);
7852: /* --- rasterize scripts --- */
7853: if ( *subscript != '\000' ) /* have a subscript */
7854: subsp = rasterize(subscript,size-1); /* so rasterize it at size-1 */
7855: if ( *supscript != '\000' ) /* have a superscript */
7856: supsp = rasterize(supscript,size-1); /* so rasterize it at size-1 */
7857: /* --- set flags for convenience --- */
7858: issub = (subsp != (subraster *)NULL); /* true if we have subscript */
7859: issup = (supsp != (subraster *)NULL); /* true if we have superscript */
7860: if (!issub && !issup) goto end_of_job; /*return operator alone if neither*/
7861: /* -------------------------------------------------------------------------
7862: stack operator and its script(s)
7863: -------------------------------------------------------------------------- */
7864: /* --- stack superscript atop operator --- */
1.3 albertel 7865: if ( issup ) { /* we have a superscript */
1.1 albertel 7866: if ( sp == NULL ) /* but no base expression */
7867: sp = supsp; /* so just use superscript */
7868: else /* have base and superscript */
7869: if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
1.3 albertel 7870: == NULL ) goto end_of_job; } /* and quit if failed */
1.1 albertel 7871: /* --- stack operator+superscript atop subscript --- */
1.3 albertel 7872: if ( issub ) { /* we have a subscript */
1.1 albertel 7873: if ( sp == NULL ) /* but no base expression */
7874: sp = subsp; /* so just use subscript */
7875: else /* have base and subscript */
7876: if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
1.3 albertel 7877: == NULL ) goto end_of_job; } /* and quit if failed */
1.1 albertel 7878: sp->type = IMAGERASTER; /* flip type of composite object */
7879: sp->size = size; /* and set font size */
7880: /* -------------------------------------------------------------------------
7881: free unneeded component subrasters and return final result to caller
7882: -------------------------------------------------------------------------- */
7883: end_of_job:
7884: return ( sp );
7885: } /* --- end-of-function rastdispmath() --- */
7886:
7887:
7888: /* ==========================================================================
7889: * Function: rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
7890: * Purpose: \left...\right handler, returns a subraster corresponding to
7891: * delimited subexpression at font size
7892: * --------------------------------------------------------------------------
7893: * Arguments: expression (I) char ** to first char of null-terminated
7894: * string beginning with a \left
7895: * to be rasterized
1.5 ! raeburn 7896: * size (I) int containing 0-7 default font size
1.1 albertel 7897: * basesp (I) subraster * to character (or subexpression)
7898: * immediately preceding leading left{
7899: * (unused, but passed for consistency)
1.2 albertel 7900: * ildelim (I) int containing ldelims[] index of
1.1 albertel 7901: * left delimiter
7902: * arg2 (I) int unused
7903: * arg3 (I) int unused
7904: * --------------------------------------------------------------------------
7905: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
7906: * or NULL for any parsing error
7907: * --------------------------------------------------------------------------
7908: * Notes: o
7909: * ======================================================================= */
7910: /* --- entry point --- */
7911: subraster *rastleft ( char **expression, int size, subraster *basesp,
7912: int ildelim, int arg2, int arg3 )
7913: {
7914: /* -------------------------------------------------------------------------
7915: Allocations and Declarations
7916: -------------------------------------------------------------------------- */
7917: subraster *rasterize(), *sp=NULL; /*rasterize between \left...\right*/
7918: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
7919: subraster *rastlimits(); /*handle sub/super scripts on lp,rp*/
7920: subraster *rastcat(); /* concat lp||sp||rp subrasters */
7921: int family=CMSYEX, /* get_delim() family */
7922: height=0, rheight=0, /* subexpr, right delim height */
7923: margin=(size+1), /* delim height margin over subexpr*/
7924: opmargin=(5); /* extra margin for \int,\sum,\etc */
1.3 albertel 7925: char /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
1.1 albertel 7926: char *texchar(), /* get delims after \left,\right */
7927: ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
7928: char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
7929: int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
1.3 albertel 7930: int isleftscript=0, isrightscript=0; /* true if delims are scripted */
1.1 albertel 7931: int sublen=0; /* strlen(subexpr) */
7932: int idelim=0; /* 1=left,2=right */
1.2 albertel 7933: /* int gotldelim = 0; */ /* true if ildelim given by caller */
1.1 albertel 7934: int delete_subraster(); /* free subraster if rastleft fails*/
7935: int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
1.2 albertel 7936: int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
1.1 albertel 7937: /* --- recognized delimiters --- */
7938: static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
7939: static char *ldelims[] = {
7940: "unused", ".", /* 1 for \left., \right. */
7941: "(", ")", /* 2,3 for \left(, \right) */
7942: "\\{","\\}", /* 4,5 for \left\{, \right\} */
7943: "[", "]", /* 6,7 for \left[, \right] */
7944: "<", ">", /* 8,9 for \left<, \right> */
7945: "|", "\\|", /* 10,11 for \left,\right |,\|*/
7946: NULL };
7947: /* --- recognized operator delimiters --- */
7948: static char *opdelims[] = { /* operator delims from cmex10 */
7949: "int", "sum", "prod",
7950: "cup", "cap", "dot",
7951: "plus", "times", "wedge",
7952: "vee",
7953: NULL }; /* --- end-of-opdelims[] --- */
7954: /* --- delimiter xlation --- */
7955: static char *xfrom[] = /* xlate any delim suffix... */
7956: { "\\|", /* \| */
7957: "\\{", /* \{ */
7958: "\\}", /* \} */
7959: "\\lbrace", /* \lbrace */
7960: "\\rbrace", /* \rbrace */
7961: "\\langle", /* \langle */
7962: "\\rangle", /* \rangle */
7963: NULL } ; /* --- end-of-xfrom[] --- */
7964: static char *xto[] = /* ...to this instead */
7965: { "=", /* \| to = */
7966: "{", /* \{ to { */
7967: "}", /* \} to } */
7968: "{", /* \lbrace to { */
7969: "}", /* \rbrace to } */
7970: "<", /* \langle to < */
7971: ">", /* \rangle to > */
7972: NULL } ; /* --- end-of-xto[] --- */
1.2 albertel 7973: /* --- non-displaystyle delimiters --- */
7974: static char *textdelims[] = /* these delims _aren't_ display */
7975: { "|", "=",
7976: "(", ")",
7977: "[", "]",
7978: "<", ">",
7979: "{", "}",
7980: "dbl", /* \lbrackdbl and \rbrackdbl */
7981: NULL } ; /* --- end-of-textdelims[] --- */
1.1 albertel 7982: /* -------------------------------------------------------------------------
7983: initialization
7984: -------------------------------------------------------------------------- */
7985: /* --- check args --- */
7986: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
7987: /* --- determine left delimiter, and set default \right. delimiter --- */
7988: if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
1.2 albertel 7989: { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
7990: /* gotldelim = 1; */ } /* and set flag that we got it */
1.1 albertel 7991: else /* trapped \left without delim */
7992: { skipwhite(*expression); /* interpret \left ( as \left( */
1.2 albertel 7993: if ( *(*expression) == '\000' ) /* end-of-string after \left */
7994: goto end_of_job; /* so return NULL */
7995: *expression = texchar(*expression,ldelim); /*pull delim from expression*/
7996: if ( *expression == NULL /* probably invalid end-of-string */
7997: || *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
1.1 albertel 7998: strcpy(rdelim,"."); /* init default \right. delim */
7999: /* -------------------------------------------------------------------------
8000: locate \right balancing our opening \left
8001: -------------------------------------------------------------------------- */
8002: /* --- first \right following \left --- */
8003: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
8004: != NULL ) { /* found it */
8005: /* --- find matching \right by pushing past any nested \left's --- */
8006: pleft = *expression; /* start after first \left( */
8007: while ( 1 ) { /*break when matching \right found*/
8008: /* -- locate next nested \left if there is one --- */
8009: if ( (pleft=strtexchr(pleft,left)) /* find next \left */
8010: == NULL ) break; /*no more, so matching \right found*/
8011: pleft += strlen(left); /* push ptr past \left token */
8012: if ( pleft >= pright ) break; /* not nested if \left after \right*/
8013: /* --- have nested \left, so push forward to next \right --- */
8014: if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
8015: == NULL ) break; /* ran out of \right's */
8016: } /* --- end-of-while(1) --- */
8017: } /* --- end-of-if(pright!=NULL) --- */
8018: /* -------------------------------------------------------------------------
8019: push past \left(_a^b sub/superscripts, if present
8020: -------------------------------------------------------------------------- */
8021: pleft = *expression; /*reset pleft after opening \left( */
1.2 albertel 8022: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
8023: != NULL ) /* found actual _a^b scripts, too */
8024: { delete_subraster(lp); /* but we don't need them */
8025: lp = NULL; } /* reset pointer, too */
1.1 albertel 8026: /* -------------------------------------------------------------------------
8027: get \right delimiter and subexpression between \left...\right, xlate delims
8028: -------------------------------------------------------------------------- */
8029: /* --- get delimiter following \right --- */
8030: if ( pright == (char *)NULL ) { /* assume \right. at end of exprssn*/
8031: strcpy(rdelim,"."); /* set default \right. */
8032: sublen = strlen(*expression); /* use entire remaining expression */
8033: memcpy(subexpr,*expression,sublen); /* copy all remaining chars */
8034: *expression += sublen; } /* and push expression to its null */
8035: else { /* have explicit matching \right */
8036: sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
8037: memcpy(subexpr,*expression,sublen); /* copy chars preceding \right */
8038: *expression = pright+strlen(right); /* push expression past \right */
8039: skipwhite(*expression); /* interpret \right ) as \right) */
8040: *expression = texchar(*expression,rdelim); /*pull delim from expression*/
8041: if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
8042: /* --- get subexpression between \left...\right --- */
8043: if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
8044: subexpr[sublen] = '\000'; /* and null-terminate it */
1.2 albertel 8045: /* --- adjust margin for expressions containing \middle's --- */
8046: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
8047: margin = 1; /* so don't "overwhelm" them */
1.1 albertel 8048: /* --- check for operator delimiter --- */
8049: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
8050: if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
8051: { margin += opmargin; /* extra height for operator */
8052: if ( *ldelim == '\\' ) /* have leading escape */
1.5 ! raeburn 8053: {strsqueeze(ldelim,1);} /* squeeze it out */
1.1 albertel 8054: break; } /* no need to check rest of table */
1.2 albertel 8055: /* --- xlate delimiters and check for textstyle --- */
1.1 albertel 8056: for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
8057: char *lrdelim = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
8058: int ix; char *xdelim; /* xfrom[] and xto[] index, delim */
8059: for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
8060: if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
8061: { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
8062: break; } /* no need to check further */
1.2 albertel 8063: for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
8064: if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
8065: { if ( idelim == 1 ) /* if it's the \left one */
8066: istextleft = 1; /* set left textstyle flag */
8067: else istextright = 1; /* else set right textstyle flag */
8068: break; } /* no need to check further */
1.1 albertel 8069: } /* --- end-of-for(idelim) --- */
8070: /* --- debugging --- */
8071: if ( msgfp!=NULL && msglevel>=99 )
8072: fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
8073: ldelim,rdelim,subexpr);
8074: /* -------------------------------------------------------------------------
8075: rasterize subexpression
8076: -------------------------------------------------------------------------- */
8077: /* --- rasterize subexpression --- */
8078: if ( (sp = rasterize(subexpr,size)) /* rasterize chars between delims */
8079: == NULL ) goto end_of_job; /* quit if failed */
8080: height = (sp->image)->height; /* height of subexpr raster */
8081: rheight = height+margin; /*default rheight as subexpr height*/
8082: /* -------------------------------------------------------------------------
8083: rasterize delimiters, reset baselines, and add sub/superscripts if present
8084: -------------------------------------------------------------------------- */
8085: /* --- check for dot delimiter --- */
8086: isleftdot = (strchr(ldelim,'.')!=NULL); /* true if \left. */
8087: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
8088: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
1.2 albertel 8089: isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
1.1 albertel 8090: if ( !isleftdot ) /* if not \left. */
8091: { /* --- first get requested \left delimiter --- */
8092: lp = get_delim(ldelim,rheight,family); /* get \left delim char */
8093: /* --- reset lp delim baseline to center delim on subexpr raster --- */
8094: if ( lp != NULL ) /* if get_delim() succeeded */
8095: { int lheight = (lp->image)->height; /* actual height of left delim */
8096: lp->baseline = sp->baseline + (lheight - height)/2;
8097: if ( lheight > rheight ) /* got bigger delim than requested */
8098: rheight = lheight-1; } /* make sure right delim matches */
8099: /* --- then add on any sub/superscripts attached to \left( --- */
1.3 albertel 8100: lp = rastlimits(&pleft,size,lp); /*\left(_a^b and push pleft past b*/
8101: isleftscript = isscripted; } /* check if left delim scripted */
1.2 albertel 8102: isdisplaystyle = (istextright?0:9); /* force \displaystyle */
1.1 albertel 8103: if ( !isrightdot ) /* and if not \right. */
8104: { /* --- first get requested \right delimiter --- */
8105: rp = get_delim(rdelim,rheight,family); /* get \right delim char */
8106: /* --- reset rp delim baseline to center delim on subexpr raster --- */
8107: if ( rp != NULL ) /* if get_delim() succeeded */
8108: rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
8109: /* --- then add on any sub/superscripts attached to \right) --- */
1.3 albertel 8110: rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/
8111: isrightscript = isscripted; } /* check if right delim scripted */
1.1 albertel 8112: isdisplaystyle = wasdisplaystyle; /* original \displystyle default */
8113: /* --- check that we got delimiters --- */
8114: if ( 0 )
8115: if ( (lp==NULL && !isleftdot) /* check that we got left( */
8116: || (rp==NULL && !isrightdot) ) /* and right) if needed */
8117: { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
8118: if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
8119: if (0) { delete_subraster(sp); /* if failed, free subraster */
8120: sp = (subraster *)NULL; } /* signal error to caller */
8121: goto end_of_job; } /* and quit */
8122: /* -------------------------------------------------------------------------
8123: concat lp || sp || rp components
8124: -------------------------------------------------------------------------- */
8125: /* --- concat lp||sp||rp to obtain final result --- */
8126: if ( lp != NULL ) /* ignore \left. */
8127: sp = rastcat(lp,sp,3); /* concat lp||sp and free sp,lp */
8128: if ( sp != NULL ) /* succeeded or ignored \left. */
8129: if ( rp != NULL ) /* ignore \right. */
8130: sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
8131: /* --- back to caller --- */
8132: end_of_job:
1.3 albertel 8133: isdelimscript = isrightscript; /* signal if right delim scripted */
1.1 albertel 8134: return ( sp );
8135: } /* --- end-of-function rastleft() --- */
8136:
8137:
8138: /* ==========================================================================
1.2 albertel 8139: * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
8140: * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
8141: * --------------------------------------------------------------------------
8142: * Arguments: expression (I) char ** to first char of null-terminated
8143: * string beginning with a \right
8144: * to be rasterized
1.5 ! raeburn 8145: * size (I) int containing 0-7 default font size
1.2 albertel 8146: * basesp (I) subraster * to character (or subexpression)
8147: * immediately preceding leading left{
8148: * (unused, but passed for consistency)
8149: * ildelim (I) int containing rdelims[] index of
8150: * right delimiter
8151: * arg2 (I) int unused
8152: * arg3 (I) int unused
8153: * --------------------------------------------------------------------------
8154: * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
8155: * or NULL for any parsing error
8156: * --------------------------------------------------------------------------
8157: * Notes: o
8158: * ======================================================================= */
8159: /* --- entry point --- */
8160: subraster *rastright ( char **expression, int size, subraster *basesp,
8161: int ildelim, int arg2, int arg3 )
8162: {
8163: /* -------------------------------------------------------------------------
8164: Allocations and Declarations
8165: -------------------------------------------------------------------------- */
8166: subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
8167: if ( sp != NULL ) /* returning entire expression */
8168: {
8169: isreplaceleft = 1; /* set flag to replace left half*/
8170: }
8171: return ( sp );
8172: } /* --- end-of-function rastright() --- */
8173:
8174:
8175: /* ==========================================================================
8176: * Function: rastmiddle ( expression, size, basesp, arg1, arg2, arg3 )
8177: * Purpose: \middle handler, returns subraster corresponding to
8178: * entire expression with \middle delimiter(s) sized to fit.
8179: * --------------------------------------------------------------------------
8180: * Arguments: expression (I/O) char ** to first char of null-terminated
8181: * string immediately following \middle to be
8182: * rasterized, and returning ptr immediately
8183: * to terminating null.
1.5 ! raeburn 8184: * size (I) int containing 0-7 default font size
1.2 albertel 8185: * basesp (I) subraster * to character (or subexpression)
8186: * immediately preceding \middle
8187: * (unused, but passed for consistency)
8188: * arg1 (I) int unused
8189: * arg2 (I) int unused
8190: * arg3 (I) int unused
8191: * --------------------------------------------------------------------------
8192: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
8193: * or NULL for any parsing error
8194: * (expression ptr unchanged if error occurs)
8195: * --------------------------------------------------------------------------
8196: * Notes: o
8197: * ======================================================================= */
8198: /* --- entry point --- */
8199: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
8200: int arg1, int arg2, int arg3 )
8201: {
8202: /* -------------------------------------------------------------------------
8203: Allocations and Declarations
8204: -------------------------------------------------------------------------- */
8205: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
8206: char *exprptr = *expression, /* local copy of ptr to expression */
8207: *texchar(), delim[32][132], /* delimiters following \middle's */
8208: *strtexchr(), /* locate \middle's */
1.3 albertel 8209: subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
1.2 albertel 8210: int height=0, habove=0, hbelow=0; /* height, above & below baseline */
8211: int idelim, ndelims=0, /* \middle count (max 32) */
8212: family = CMSYEX; /* delims from CMSY10 or CMEX10 */
8213: subraster *subrastcpy(), /* copy subraster */
8214: *rastcat(), /* concatanate subraster */
8215: *get_delim(); /* get rasterized delimiter */
8216: int delete_subraster(); /* free work area subsp[]'s at eoj */
8217: /* -------------------------------------------------------------------------
8218: initialization
8219: -------------------------------------------------------------------------- */
8220: subsp[0] = leftexpression; /* expressn preceding 1st \middle */
8221: subsp[1] = NULL; /* set first null */
8222: /* -------------------------------------------------------------------------
8223: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
8224: -------------------------------------------------------------------------- */
8225: while ( ndelims < 30 ) /* max of 31 \middle's */
8226: {
8227: /* --- maintain max height above,below baseline --- */
8228: if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
8229: { int baseline = (subsp[ndelims])->baseline; /* #rows above baseline */
8230: height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
8231: habove = max2(habove,baseline); /* max #rows above baseline */
8232: hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
8233: /* --- get delimter after \middle --- */
8234: skipwhite(exprptr); /*skip space betwn \middle & \delim*/
8235: exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
8236: if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
8237: break; /* ignore it and consider job done */
8238: ndelims++; /* count another \middle\delim */
8239: /* --- get subexpression between \delim and next \middle --- */
8240: subsp[ndelims] = NULL; /* no subexpresion yet */
8241: if ( *exprptr == '\000' ) /* end-of-expression after \delim */
8242: break; /* so we have all subexpressions */
8243: if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
8244: == NULL ) /* no more \middle's */
1.3 albertel 8245: { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
8246: subexpr[MAXSUBXSZ] = '\000'; /* make sure it's null-terminated */
1.2 albertel 8247: exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
8248: else /* have another \middle */
8249: { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
1.3 albertel 8250: memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
8251: subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
1.2 albertel 8252: exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
8253: /* --- rasterize subexpression --- */
8254: subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
8255: } /* --- end-of-while(1) --- */
8256: /* -------------------------------------------------------------------------
8257: construct \middle\delim's and concatanate them between subexpressions
8258: -------------------------------------------------------------------------- */
8259: if ( ndelims < 1 /* no delims */
8260: || (height=habove+hbelow) < 1 ) /* or no subexpressions? */
8261: goto end_of_job; /* just flush \middle directive */
8262: for ( idelim=0; idelim<=ndelims; idelim++ )
8263: {
8264: /* --- first add on subexpression preceding delim --- */
1.3 albertel 8265: if ( subsp[idelim] != NULL ) { /* have subexpr preceding delim */
1.2 albertel 8266: if ( sp == NULL ) /* this is first piece */
8267: { sp = subsp[idelim]; /* so just use it */
8268: if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
1.3 albertel 8269: else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
1.2 albertel 8270: /* --- now construct delimiter --- */
8271: if ( *(delim[idelim]) != '\000' ) /* have delimter */
8272: { subraster *delimsp = get_delim(delim[idelim],height,family);
8273: if ( delimsp != NULL ) /* rasterized delim */
8274: { delimsp->baseline = habove; /* set baseline */
8275: if ( sp == NULL ) /* this is first piece */
8276: sp = delimsp; /* so just use it */
8277: else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
8278: } /* --- end-of-for(idelim) --- */
8279: /* --- back to caller --- */
8280: end_of_job:
8281: if ( 0 ) /* now handled above */
8282: for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
8283: if ( subsp[idelim] != NULL ) /* have allocated subraster */
8284: delete_subraster(subsp[idelim]); /* so free it */
8285: if ( sp != NULL ) /* returning entire expression */
8286: { int newht = (sp->image)->height; /* height of returned subraster */
8287: sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
8288: isreplaceleft = 1; /* set flag to replace left half*/
8289: *expression += strlen(*expression); } /* and push to terminating null*/
8290: return ( sp );
8291: } /* --- end-of-function rastmiddle() --- */
8292:
8293:
8294: /* ==========================================================================
1.1 albertel 8295: * Function: rastflags ( expression, size, basesp, flag, value, arg3 )
8296: * Purpose: sets an internal flag, e.g., for \rm, or sets an internal
8297: * value, e.g., for \unitlength=<value>, and returns NULL
8298: * so nothing is displayed
8299: * --------------------------------------------------------------------------
8300: * Arguments: expression (I) char ** to first char of null-terminated
8301: * LaTeX expression (unused/unchanged)
8302: * size (I) int containing base font size (not used,
8303: * just stored in subraster)
8304: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 8305: * immediately preceding "flags" directive
8306: * (unused but passed for consistency)
1.1 albertel 8307: * flag (I) int containing #define'd symbol specifying
8308: * internal flag to be set
8309: * value (I) int containing new value of flag
8310: * arg3 (I) int unused
8311: * --------------------------------------------------------------------------
8312: * Returns: ( subraster * ) NULL so nothing is displayed
8313: * --------------------------------------------------------------------------
8314: * Notes: o
8315: * ======================================================================= */
8316: /* --- entry point --- */
8317: subraster *rastflags ( char **expression, int size, subraster *basesp,
8318: int flag, int value, int arg3 )
8319: {
8320: /* -------------------------------------------------------------------------
8321: Allocations and Declarations
8322: -------------------------------------------------------------------------- */
8323: char *texsubexpr(), /* parse expression for... */
8324: valuearg[1024]="NOVALUE"; /* value from expression, if needed */
8325: int argvalue=NOVALUE, /* atoi(valuearg) */
8326: isdelta=0, /* true if + or - precedes valuearg */
8327: valuelen=0; /* strlen(valuearg) */
1.3 albertel 8328: double dblvalue=(-99.), strtod(); /*convert ascii {valuearg} to double*/
1.1 albertel 8329: static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */
8330: /* -------------------------------------------------------------------------
8331: set flag or value
8332: -------------------------------------------------------------------------- */
8333: switch ( flag )
8334: {
8335: default: break; /* unrecognized flag */
1.2 albertel 8336: case ISFONTFAM:
1.1 albertel 8337: if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
8338: (*expression)++; /* skip leading ~ after \rm */
1.2 albertel 8339: fontnum = value; /* set font family */
1.1 albertel 8340: break;
8341: case ISSTRING: isstring=value; break; /* set string/image mode */
8342: case ISDISPLAYSTYLE: /* set \displaystyle mode */
8343: displaystylelevel = recurlevel; /* \displaystyle set at recurlevel */
8344: isdisplaystyle=value; break;
8345: case ISOPAQUE: istransparent=value; break; /* set transparent/opaque */
8346: case ISREVERSE: /* reverse video */
8347: if ( value==1 || value==NOVALUE )
8348: { fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
8349: if ( value==2 || value==NOVALUE )
8350: { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
8351: if ( value==2 || value==NOVALUE )
8352: isblackonwhite = !isblackonwhite;
1.3 albertel 8353: if ( gammacorrection > 0.0001 ) /* have gamma correction */
8354: gammacorrection = REVERSEGAMMA; /* use reverse video gamma instead */
1.1 albertel 8355: break;
8356: case ISSUPER: /* set supersampling/lowpass flag */
8357: #ifndef SSFONTS /* don't have ss fonts loaded */
8358: value = 0; /* so force lowpass */
8359: #endif
8360: isss = issupersampling = value;
8361: fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
8362: break;
8363: case ISFONTSIZE: /* set fontsize */
1.5 ! raeburn 8364: case ISMAGSTEP: /* set magstep */
1.1 albertel 8365: case ISDISPLAYSIZE: /* set displaysize */
1.3 albertel 8366: case ISCONTENTTYPE: /*enable/disable content-type lines*/
1.5 ! raeburn 8367: case ISCONTENTCACHED: /* write content-type to cache file*/
1.1 albertel 8368: case ISSHRINK: /* set shrinkfactor */
8369: case ISAAALGORITHM: /* set anti-aliasing algorithm */
8370: case ISWEIGHT: /* set font weight */
8371: case ISCENTERWT: /* set lowpass center pixel weight */
8372: case ISADJACENTWT: /* set lowpass adjacent weight */
8373: case ISCORNERWT: /* set lowpass corner weight */
8374: case ISCOLOR: /* set red(1),green(2),blue(3) */
1.2 albertel 8375: case ISSMASH: /* set (minimum) "smash" margin */
1.3 albertel 8376: case ISGAMMA: /* set gamma correction */
1.1 albertel 8377: if ( value != NOVALUE ) /* passed a fixed value to be set */
1.3 albertel 8378: { argvalue = value; /* set given fixed int value */
8379: dblvalue = (double)value; } /* or maybe interpreted as double */
1.1 albertel 8380: else /* get value from expression */
8381: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
8382: if ( *valuearg != '\000' ) /* guard against empty string */
8383: if ( !isalpha(*valuearg) ) /* and against alpha string args */
8384: if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
8385: { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
8386: if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
1.5 ! raeburn 8387: { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
1.3 albertel 8388: switch ( flag ) { /* convert to double or int */
8389: default: argvalue = atoi(valuearg); break; /* convert to int */
8390: case ISGAMMA:
8391: dblvalue = strtod(valuearg,NULL); break; } /* or to double */
8392: } /* --- end-of-if(*valuearg!='?') --- */
8393: } /* --- end-of-if(value==NOVALUE) --- */
1.1 albertel 8394: switch ( flag )
8395: {
8396: default: break;
8397: case ISCOLOR: /* set color */
8398: slower(valuearg); /* convert arg to lower case */
8399: if ( argvalue==1 || strstr(valuearg,"red") )
8400: { fggreen = fgblue = (isblackonwhite?0:255);
8401: fgred = (isblackonwhite?255:0); }
8402: if ( argvalue==2 || strstr(valuearg,"green") )
8403: { fgred = fgblue = (isblackonwhite?0:255);
8404: fggreen = (isblackonwhite?255:0); }
8405: if ( argvalue==3 || strstr(valuearg,"blue") )
8406: { fgred = fggreen = (isblackonwhite?0:255);
8407: fgblue = (isblackonwhite?255:0); }
8408: if ( argvalue==0 || strstr(valuearg,"black") )
8409: fgred = fggreen = fgblue = (isblackonwhite?0:255);
8410: if ( argvalue==7 || strstr(valuearg,"white") )
8411: fgred = fggreen = fgblue = (isblackonwhite?255:0);
8412: break;
8413: case ISFONTSIZE: /* set fontsize */
8414: if ( argvalue != NOVALUE ) /* got a value */
8415: { int largestsize = (issupersampling?16:LARGESTSIZE);
8416: fontsize = (isdelta? fontsize+argvalue : argvalue);
8417: fontsize = max2(0,min2(fontsize,largestsize));
8418: shrinkfactor = shrinkfactors[fontsize];
1.2 albertel 8419: if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
8420: || (1 && isdisplaystyle==2) /* displaystyle enabled and set */
8421: || (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
1.1 albertel 8422: if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
1.3 albertel 8423: if ( !ispreambledollars ) { /* respect $$...$$'s */
1.2 albertel 8424: if ( fontsize >= displaysize )
8425: isdisplaystyle = 2; /* forced */
1.3 albertel 8426: else isdisplaystyle = 1; }
1.1 albertel 8427: /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
8428: else /* embed font size in expression */
8429: { sprintf(valuearg,"%d",fontsize); /* convert size */
8430: valuelen = strlen(valuearg); /* ought to be 1 */
8431: if ( *expression != '\000' ) /* ill-formed expression */
8432: { *expression = (char *)(*expression-valuelen); /*back up buff*/
8433: memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
8434: break;
1.5 ! raeburn 8435: case ISMAGSTEP: /* set magstep */
! 8436: if ( argvalue != NOVALUE ) { /* got a value */
! 8437: int largestmag = 10;
! 8438: magstep = (isdelta? magstep+argvalue : argvalue);
! 8439: magstep = max2(1,min2(magstep,largestmag)); }
! 8440: break;
1.1 albertel 8441: case ISDISPLAYSIZE: /* set displaysize */
8442: if ( argvalue != NOVALUE ) /* got a value */
8443: displaysize = (isdelta? displaysize+argvalue : argvalue);
8444: break;
1.3 albertel 8445: case ISCONTENTTYPE: /*enable/disable content-type lines*/
8446: if ( argvalue != NOVALUE ) /* got a value */
8447: isemitcontenttype = (argvalue>0?1:0);
8448: break;
1.5 ! raeburn 8449: case ISCONTENTCACHED: /* write content-type to cache file*/
! 8450: if ( argvalue != NOVALUE ) /* got a value */
! 8451: iscachecontenttype = (argvalue>0?1:0);
! 8452: break;
1.2 albertel 8453: case ISSMASH: /* set (minimum) "smash" margin */
1.1 albertel 8454: if ( argvalue != NOVALUE ) /* got a value */
1.2 albertel 8455: { smashmargin = argvalue; /* set value */
1.1 albertel 8456: if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
1.2 albertel 8457: issmashdelta = (isdelta?1:0); } /* and set delta flag */
8458: smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
1.3 albertel 8459: isexplicitsmash = 1; /* signal explicit \smash directive*/
1.1 albertel 8460: break;
8461: case ISSHRINK: /* set shrinkfactor */
8462: if ( argvalue != NOVALUE ) /* got a value */
8463: shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
8464: shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
8465: break;
8466: case ISAAALGORITHM: /* set anti-aliasing algorithm */
1.3 albertel 8467: if ( argvalue != NOVALUE ) { /* got a value */
8468: if ( argvalue >= 0 ) { /* non-negative to set algorithm */
8469: aaalgorithm = argvalue; /* set algorithm number */
8470: aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
8471: else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
1.1 albertel 8472: break;
8473: case ISWEIGHT: /* set font weight number */
8474: value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
8475: (isdelta? weightnum+argvalue : argvalue));
8476: if ( value>=0 && value<maxaaparams ) /* in range */
8477: { weightnum = value; /* reset weightnum index */
8478: minadjacent = aaparams[weightnum].minadjacent;
8479: maxadjacent = aaparams[weightnum].maxadjacent;
8480: cornerwt = aaparams[weightnum].cornerwt;
8481: adjacentwt = aaparams[weightnum].adjacentwt;
8482: centerwt = aaparams[weightnum].centerwt;
8483: fgalias = aaparams[weightnum].fgalias;
8484: fgonly = aaparams[weightnum].fgonly;
8485: bgalias = aaparams[weightnum].bgalias;
8486: bgonly = aaparams[weightnum].bgonly; }
8487: break;
8488: case ISCENTERWT: /* set lowpass center pixel weight */
8489: if ( argvalue != NOVALUE ) /* got a value */
8490: centerwt = argvalue; /* set lowpass center weight */
8491: break;
8492: case ISADJACENTWT: /* set lowpass adjacent weight */
8493: if ( argvalue != NOVALUE ) /* got a value */
8494: adjacentwt = argvalue; /* set lowpass adjacent weight */
8495: break;
8496: case ISCORNERWT: /* set lowpass corner weight */
8497: if ( argvalue != NOVALUE ) /* got a value */
8498: cornerwt = argvalue; /* set lowpass corner weight */
8499: break;
1.3 albertel 8500: case ISGAMMA: /* set gamma correction */
8501: if ( dblvalue >= 0.0 ) /* got a value */
8502: gammacorrection = dblvalue; /* set gamma correction */
8503: break;
1.1 albertel 8504: } /* --- end-of-switch() --- */
8505: break;
8506: case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/
8507: *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
8508: valuelen = strlen(valuearg); /* ought to be 1-4 */
8509: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
8510: if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
8511: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
8512: if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
8513: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
8514: if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
8515: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
8516: if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
8517: break;
8518: case UNITLENGTH:
8519: if ( value != NOVALUE ) /* passed a fixed value to be set */
8520: unitlength = (double)(value); /* set given fixed value */
8521: else /* get value from expression */
8522: { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
8523: if ( *valuearg != '\000' ) /* guard against empty string */
8524: unitlength = strtod(valuearg,NULL); } /* convert to double */
1.5 ! raeburn 8525: iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
1.1 albertel 8526: break;
8527: } /* --- end-of-switch(flag) --- */
8528: return ( NULL ); /*just set value, nothing to display*/
8529: } /* --- end-of-function rastflags() --- */
8530:
8531:
8532: /* ==========================================================================
8533: * Function: rastspace(expression, size, basesp, width, isfill, isheight)
8534: * Purpose: returns a blank/space subraster width wide,
8535: * with baseline and height corresponding to basep
8536: * --------------------------------------------------------------------------
8537: * Arguments: expression (I) char ** to first char of null-terminated
8538: * LaTeX expression (unused/unchanged)
8539: * size (I) int containing base font size (not used,
8540: * just stored in subraster)
8541: * basesp (I) subraster * to character (or subexpression)
8542: * immediately preceding space, whose baseline
8543: * and height params are transferred to space
8544: * width (I) int containing #bits/pixels for space width
8545: * isfill (I) int containing true to \hfill complete
8546: * expression out to width
1.3 albertel 8547: * (Kludge: isfill=99 signals \hspace*
8548: * for negative space)
1.1 albertel 8549: * isheight (I) int containing true (but not NOVALUE)
8550: * to treat width arg as height
8551: * --------------------------------------------------------------------------
8552: * Returns: ( subraster * ) ptr to empty/blank subraster
8553: * or NULL for any error
8554: * --------------------------------------------------------------------------
8555: * Notes: o
8556: * ======================================================================= */
8557: /* --- entry point --- */
8558: subraster *rastspace ( char **expression, int size, subraster *basesp,
8559: int width, int isfill, int isheight )
8560: {
8561: /* -------------------------------------------------------------------------
8562: Allocations and Declarations
8563: -------------------------------------------------------------------------- */
8564: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
1.3 albertel 8565: raster *bp=NULL, *backspace_raster(); /* for negative space */
8566: int delete_subraster(); /* if fail, free unneeded subraster*/
1.1 albertel 8567: int baseht=1, baseln=0; /* height,baseline of base symbol */
8568: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
1.3 albertel 8569: int isstar=0, minspace=0; /* defaults for negative hspace */
1.1 albertel 8570: char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
1.5 ! raeburn 8571: int evalterm(), evalue=0; /* evaluate [args], {args} */
1.1 albertel 8572: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
8573: subraster *rastcat(); /* cat rightsp after \hfill */
8574: /* -------------------------------------------------------------------------
8575: initialization
8576: -------------------------------------------------------------------------- */
1.3 albertel 8577: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
1.1 albertel 8578: if ( isfill == NOVALUE ) isfill=0; /* novalue means false */
8579: if ( isheight == NOVALUE ) isheight=0; /* novalue means false */
1.3 albertel 8580: minspace = (isstar?(-1):0); /* reset default minspace */
1.1 albertel 8581: /* -------------------------------------------------------------------------
8582: determine width if not given (e.g., \hspace{width}, \hfill{width})
8583: -------------------------------------------------------------------------- */
1.3 albertel 8584: if ( width == 0 ) { /* width specified in expression */
8585: double dwidth; int widthval; /* test {width} before using it */
8586: int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
8587: /* --- check if optional [minspace] given for negative \hspace --- */
8588: if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */
1.5 ! raeburn 8589: /* ---parse [minspace], bump expression past it, evaluate expression--- */
1.3 albertel 8590: *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
1.5 ! raeburn 8591: if ( !isempty(widtharg) ) { /* got [minspace] */
! 8592: evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
! 8593: minspace = iround(unitlength*((double)evalue)); } /* in pixels */
1.3 albertel 8594: } /* --- end-of-if(*(*expression)=='[') --- */
8595: width = 1; /* set default width */
8596: *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
1.5 ! raeburn 8597: dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
1.3 albertel 8598: widthval = /* convert {width} to integer */
8599: (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
8600: if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
8601: width = widthval; /* replace deafault width */
8602: } /* --- end-of-if(width==0) --- */
8603: /* -------------------------------------------------------------------------
8604: first check for negative space
8605: -------------------------------------------------------------------------- */
8606: if ( width < 0 ) { /* have negative hspace */
8607: if ( leftexpression != (subraster *)NULL ) /* can't backspace */
8608: if ( (spacesp=new_subraster(0,0,0)) /* get new subraster for backspace */
8609: != NULL ) { /* and if we succeed... */
8610: int nback=(-width), pback; /*#pixels wanted,actually backspaced*/
8611: if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
8612: != NULL ) { /* and if backspace succeeds... */
8613: spacesp->image = bp; /* save backspaced image */
8614: /*spacesp->type = leftexpression->type;*/ /* copy original type */
8615: spacesp->type = blanksignal; /* need to propagate blanks */
8616: spacesp->size = leftexpression->size; /* copy original font size */
8617: spacesp->baseline = leftexpression->baseline; /* and baseline */
8618: blanksymspace += -(nback-pback); /* wanted more than we got */
8619: isreplaceleft = 1; } /*signal to replace entire expressn*/
8620: else { /* backspace failed */
8621: delete_subraster(spacesp); /* free unneeded envelope */
8622: spacesp = (subraster *)NULL; } } /* and signal failure */
8623: goto end_of_job;
8624: } /* --- end-of-if(width<0) --- */
1.1 albertel 8625: /* -------------------------------------------------------------------------
8626: see if width is "absolute" or fill width
8627: -------------------------------------------------------------------------- */
8628: if ( isfill /* called as \hfill{} */
8629: && !isheight ) /* parameter conflict */
8630: { if ( leftexpression != NULL ) /* if we have left half */
8631: width -= (leftexpression->image)->width; /*reduce left width from total*/
8632: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
8633: != NULL ) /* succeeded */
8634: width -= (rightsp->image)->width; } /* reduce right width from total */
8635: /* -------------------------------------------------------------------------
8636: construct blank subraster, and return it to caller
8637: -------------------------------------------------------------------------- */
8638: /* --- get parameters from base symbol --- */
8639: if ( basesp != (subraster *)NULL ) /* we have base symbol for space */
8640: { baseht = (basesp->image)->height; /* height of base symbol */
8641: baseln = basesp->baseline; } /* and its baseline */
8642: /* --- flip params for height --- */
8643: if ( isheight ) /* width is actually height */
8644: { baseht = width; /* use given width as height */
8645: width = 1; } /* and set default width */
8646: /* --- generate and init space subraster --- */
8647: if ( width > 0 ) /*make sure we have positive width*/
8648: if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
8649: != NULL ) /* and if we succeed... */
8650: { /* --- ...re-init subraster parameters --- */
8651: spacesp->size = size; /*propagate base font size forward*/
1.3 albertel 8652: if(1)spacesp->type = blanksignal; /* need to propagate blanks (???) */
1.1 albertel 8653: spacesp->baseline = baseln; } /* ditto baseline */
8654: /* -------------------------------------------------------------------------
8655: concat right half if \hfill-ing
8656: -------------------------------------------------------------------------- */
8657: if ( rightsp != NULL ) /* we have a right half after fill */
8658: { spacesp = (spacesp==NULL? rightsp: /* no space, so just use right half*/
8659: rastcat(spacesp,rightsp,3)); /* or cat right half after space */
8660: spacesp->type = blanksignal; /* need to propagate blanks */
8661: *expression += strlen((*expression)); } /* push expression to its null */
1.3 albertel 8662: end_of_job:
8663: return ( spacesp );
1.1 albertel 8664: } /* --- end-of-function rastspace() --- */
8665:
8666:
8667: /* ==========================================================================
8668: * Function: rastnewline ( expression, size, basesp, arg1, arg2, arg3 )
8669: * Purpose: \\ handler, returns subraster corresponding to
8670: * left-hand expression preceding \\ above right-hand expression
8671: * --------------------------------------------------------------------------
8672: * Arguments: expression (I/O) char ** to first char of null-terminated
8673: * string immediately following \\ to be
8674: * rasterized, and returning ptr immediately
8675: * to terminating null.
1.5 ! raeburn 8676: * size (I) int containing 0-7 default font size
1.1 albertel 8677: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 8678: * immediately preceding \\
1.1 albertel 8679: * (unused, but passed for consistency)
8680: * arg1 (I) int unused
8681: * arg2 (I) int unused
8682: * arg3 (I) int unused
8683: * --------------------------------------------------------------------------
8684: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
8685: * or NULL for any parsing error
8686: * (expression ptr unchanged if error occurs)
8687: * --------------------------------------------------------------------------
8688: * Notes: o
8689: * ======================================================================= */
8690: /* --- entry point --- */
8691: subraster *rastnewline ( char **expression, int size, subraster *basesp,
8692: int arg1, int arg2, int arg3 )
8693: {
8694: /* -------------------------------------------------------------------------
8695: Allocations and Declarations
8696: -------------------------------------------------------------------------- */
8697: subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
8698: subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
8699: char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
1.5 ! raeburn 8700: int evalterm(), evalue=0; /* evaluate [arg], {arg} */
1.1 albertel 8701: int vspace = size+2; /* #pixels between lines */
8702: /* -------------------------------------------------------------------------
8703: obtain optional [vspace] argument immediately following \\ command
8704: -------------------------------------------------------------------------- */
8705: /* --- check if [vspace] given --- */
8706: if ( *(*expression) == '[' ) /*have [vspace] if leading char is [*/
8707: {
8708: /* ---parse [vspace] and bump expression past it, interpret as double--- */
8709: *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
8710: if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
1.5 ! raeburn 8711: evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
! 8712: vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
1.1 albertel 8713: } /* --- end-of-if(*(*expression)=='[') --- */
8714: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
8715: /* -------------------------------------------------------------------------
8716: rasterize right half of expression and stack left half above it
8717: -------------------------------------------------------------------------- */
8718: /* --- rasterize right half --- */
8719: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
8720: == NULL ) goto end_of_job; /* quit if failed */
8721: /* --- stack left half above it --- */
1.2 albertel 8722: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
8723: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
1.1 albertel 8724: /* --- back to caller --- */
8725: end_of_job:
8726: if ( newlsp != NULL ) /* returning entire expression */
8727: { int newht = (newlsp->image)->height; /* height of returned subraster */
8728: newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
8729: isreplaceleft = 1; /* so set flag to replace left half*/
8730: *expression += strlen(*expression); } /* and push to terminating null*/
8731: return ( newlsp ); /* 1st line over 2nd, or null=error*/
8732: } /* --- end-of-function rastnewline() --- */
8733:
8734:
8735: /* ==========================================================================
8736: * Function: rastarrow ( expression, size, basesp, drctn, isBig, arg3 )
8737: * Purpose: returns left/right arrow subraster (e.g., for \longrightarrow)
8738: * --------------------------------------------------------------------------
8739: * Arguments: expression (I) char ** to first char of null-terminated
8740: * LaTeX expression (unused/unchanged)
8741: * size (I) int containing base font size (not used,
8742: * just stored in subraster)
8743: * basesp (I) subraster * to character (or subexpression)
8744: * immediately preceding space, whose baseline
8745: * and height params are transferred to space
8746: * drctn (I) int containing +1 for right, -1 for left,
8747: * or 0 for leftright
8748: * isBig (I) int containing 0 for ---> or 1 for ===>
8749: * arg3 (I) int unused
8750: * --------------------------------------------------------------------------
8751: * Returns: ( subraster * ) ptr to left/right arrow subraster
8752: * or NULL for any error
8753: * --------------------------------------------------------------------------
8754: * Notes: o An optional argument [width] may *immediately* follow
8755: * the \longxxx to explicitly set the arrow's width in pixels.
8756: * For example, \longrightarrow calculates a default width
8757: * (as usual in LaTeX), whereas \longrightarrow[50] explicitly
8758: * draws a 50-pixel long arrow. This can be used, e.g.,
8759: * to draw commutative diagrams in conjunction with
8760: * \array (and maybe with \stackrel and/or \relstack, too).
8761: * o In case you really want to render, say, [f]---->[g], just
8762: * use an intervening space, i.e., [f]\longrightarrow~[g].
8763: * In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
8764: * ======================================================================= */
8765: /* --- entry point --- */
8766: subraster *rastarrow ( char **expression, int size, subraster *basesp,
8767: int drctn, int isBig, int arg3 )
8768: {
8769: /* -------------------------------------------------------------------------
8770: Allocations and Declarations
8771: -------------------------------------------------------------------------- */
8772: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
8773: char *texsubexpr(), widtharg[256]; /* parse for optional [width] */
8774: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
8775: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
8776: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
8777: int delete_subraster(); /*free work areas in case of error*/
1.5 ! raeburn 8778: int evalterm(); /* evaluate [arg], {arg} */
1.1 albertel 8779: int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
8780: int islimits = 1; /*true to handle limits internally*/
8781: int limsize = size-1; /* font size for limits */
8782: int vspace = 1; /* #empty rows below arrow */
8783: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
8784: /* -------------------------------------------------------------------------
8785: construct longleft/rightarrow subraster, with limits, and return it to caller
8786: -------------------------------------------------------------------------- */
8787: /* --- check for optional width arg and replace default width --- */
8788: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
8789: { int widthval; /* test [width] before using it */
8790: *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
8791: widthval = /* convert [width] to integer */
1.5 ! raeburn 8792: (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
1.1 albertel 8793: if ( widthval>=2 && widthval<=600 ) /* sanity check */
8794: width = widthval; } /* replace deafault width */
8795: /* --- now parse for limits, and bump expression past it(them) --- */
8796: if ( islimits ) /* handling limits internally */
8797: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
8798: if ( *sub != '\000' ) /*have a subscript following arrow*/
8799: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
8800: if ( *super != '\000' ) /*have superscript following arrow*/
8801: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
8802: /* --- set height based on width --- */
8803: height = min2(17,max2(9,(width+2)/6)); /* height based on width */
8804: height = 1 + (height/2)*2; /* always force odd height */
8805: /* --- generate arrow subraster --- */
8806: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
8807: == NULL ) goto end_of_job; /* and quit if we failed */
8808: /* --- add space below arrow --- */
8809: if ( vspace > 0 ) /* if we have space below arrow */
8810: if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
8811: != NULL ) /* and if we succeeded */
8812: if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
8813: == NULL ) goto end_of_job; /* and quit if we failed */
8814: /* --- init arrow subraster parameters --- */
8815: arrowsp->size = size; /*propagate base font size forward*/
8816: arrowsp->baseline = height+vspace-1; /* set baseline at bottom of arrow */
8817: /* --- add limits above/below arrow, as necessary --- */
8818: if ( subsp != NULL ) /* stack subscript below arrow */
8819: if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
8820: == NULL ) goto end_of_job; /* quit if failed */
8821: if ( supsp != NULL ) /* stack superscript above arrow */
8822: if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
8823: == NULL ) goto end_of_job; /* quit if failed */
8824: /* --- return arrow (or NULL) to caller --- */
8825: end_of_job:
8826: return ( arrowsp );
8827: } /* --- end-of-function rastarrow() --- */
8828:
8829:
8830: /* ==========================================================================
8831: * Function: rastuparrow ( expression, size, basesp, drctn, isBig, arg3 )
8832: * Purpose: returns an up/down arrow subraster (e.g., for \longuparrow)
8833: * --------------------------------------------------------------------------
8834: * Arguments: expression (I) char ** to first char of null-terminated
8835: * LaTeX expression (unused/unchanged)
8836: * size (I) int containing base font size (not used,
8837: * just stored in subraster)
8838: * basesp (I) subraster * to character (or subexpression)
8839: * immediately preceding space, whose baseline
8840: * and height params are transferred to space
8841: * drctn (I) int containing +1 for up, -1 for down,
8842: * or 0 for updown
8843: * isBig (I) int containing 0 for ---> or 1 for ===>
8844: * arg3 (I) int unused
8845: * --------------------------------------------------------------------------
8846: * Returns: ( subraster * ) ptr to up/down arrow subraster
8847: * or NULL for any error
8848: * --------------------------------------------------------------------------
8849: * Notes: o An optional argument [height] may *immediately* follow
8850: * the \longxxx to explicitly set the arrow's height in pixels.
8851: * For example, \longuparrow calculates a default height
8852: * (as usual in LaTeX), whereas \longuparrow[25] explicitly
8853: * draws a 25-pixel high arrow. This can be used, e.g.,
8854: * to draw commutative diagrams in conjunction with
8855: * \array (and maybe with \stackrel and/or \relstack, too).
8856: * o In case you really want to render, say, [f]---->[g], just
8857: * use an intervening space, i.e., [f]\longuparrow~[g].
8858: * In text use two spaces {\rm~[f]\longuparrow~~[g]}.
8859: * ======================================================================= */
8860: /* --- entry point --- */
8861: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
8862: int drctn, int isBig, int arg3 )
8863: {
8864: /* -------------------------------------------------------------------------
8865: Allocations and Declarations
8866: -------------------------------------------------------------------------- */
8867: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
8868: char *texsubexpr(), heightarg[256]; /* parse for optional [height] */
8869: char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
8870: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
8871: subraster *rastcat(); /* cat superscript left, sub right */
1.5 ! raeburn 8872: int evalterm(); /* evaluate [arg], {arg} */
1.1 albertel 8873: int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
8874: int islimits = 1; /*true to handle limits internally*/
8875: int limsize = size-1; /* font size for limits */
8876: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
8877: /* -------------------------------------------------------------------------
8878: construct blank subraster, and return it to caller
8879: -------------------------------------------------------------------------- */
8880: /* --- check for optional height arg and replace default height --- */
8881: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
8882: { int heightval; /* test height before using it */
8883: *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
8884: heightval = /* convert [height] to integer */
1.5 ! raeburn 8885: (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
1.1 albertel 8886: if ( heightval>=2 && heightval<=600 ) /* sanity check */
8887: height = heightval; } /* replace deafault height */
8888: /* --- now parse for limits, and bump expression past it(them) --- */
8889: if ( islimits ) /* handling limits internally */
8890: { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
8891: if ( *sub != '\000' ) /*have a subscript following arrow*/
8892: subsp = rasterize(sub,limsize); /* so try to rasterize subscript */
8893: if ( *super != '\000' ) /*have superscript following arrow*/
8894: supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
8895: /* --- set width based on height --- */
8896: width = min2(17,max2(9,(height+2)/4)); /* width based on height */
8897: width = 1 + (width/2)*2; /* always force odd width */
8898: /* --- generate arrow subraster --- */
8899: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
8900: == NULL ) goto end_of_job; /* and quit if we failed */
8901: /* --- init arrow subraster parameters --- */
8902: arrowsp->size = size; /*propagate base font size forward*/
8903: arrowsp->baseline = height-1; /* set baseline at bottom of arrow */
8904: /* --- add limits above/below arrow, as necessary --- */
8905: if ( supsp != NULL ) /* cat superscript to left of arrow*/
8906: { int supht = (supsp->image)->height, /* superscript height */
8907: deltab = (1+abs(height-supht))/2; /* baseline difference to center */
8908: supsp->baseline = supht-1; /* force script baseline to bottom */
8909: if ( supht <= height ) /* arrow usually taller than script*/
8910: arrowsp->baseline -= deltab; /* so bottom of script goes here */
8911: else supsp->baseline -= deltab; /* else bottom of arrow goes here */
8912: if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
8913: == NULL ) goto end_of_job; } /* quit if failed */
8914: if ( subsp != NULL ) /* cat subscript to right of arrow */
8915: { int subht = (subsp->image)->height, /* subscript height */
8916: deltab = (1+abs(height-subht))/2; /* baseline difference to center */
8917: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
8918: subsp->baseline = subht-1; /* force script baseline to bottom */
8919: if ( subht <= height ) /* arrow usually taller than script*/
8920: arrowsp->baseline -= deltab; /* so bottom of script goes here */
8921: else subsp->baseline -= deltab; /* else bottom of arrow goes here */
8922: if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
8923: == NULL ) goto end_of_job; } /* quit if failed */
8924: /* --- return arrow (or NULL) to caller --- */
8925: end_of_job:
8926: arrowsp->baseline = height-1; /* reset arrow baseline to bottom */
8927: return ( arrowsp );
8928: } /* --- end-of-function rastuparrow() --- */
8929:
8930:
8931: /* ==========================================================================
8932: * Function: rastoverlay (expression, size, basesp, overlay, offset2, arg3)
8933: * Purpose: overlays one raster on another
8934: * --------------------------------------------------------------------------
8935: * Arguments: expression (I/O) char ** to first char of null-terminated
8936: * string immediately following overlay \cmd to
8937: * be rasterized, and returning ptr immediately
8938: * following last character processed.
1.5 ! raeburn 8939: * size (I) int containing 0-7 default font size
1.1 albertel 8940: * basesp (I) subraster * to character (or subexpression)
8941: * immediately preceding overlay \cmd
8942: * (unused, but passed for consistency)
8943: * overlay (I) int containing 1 to overlay / (e.g., \not)
8944: * or NOVALUE to pick up 2nd arg from expression
8945: * offset2 (I) int containing #pixels to horizontally offset
8946: * overlay relative to underlying symbol,
8947: * positive(right) or negative or 0,
8948: * or NOVALUE to pick up optional [offset] arg
8949: * arg3 (I) int unused
8950: * --------------------------------------------------------------------------
8951: * Returns: ( subraster * ) ptr to subraster corresponding to composite,
8952: * or NULL for any parsing error
8953: * --------------------------------------------------------------------------
8954: * Notes: o
8955: * ======================================================================= */
8956: /* --- entry point --- */
8957: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
8958: int overlay, int offset2, int arg3 )
8959: {
8960: /* -------------------------------------------------------------------------
8961: Allocations and Declarations
8962: -------------------------------------------------------------------------- */
8963: char *texsubexpr(), /*parse expression for base,overlay*/
8964: expr1[512], expr2[512]; /* base, overlay */
8965: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
8966: *new_subraster(); /*explicitly alloc sp2 if necessary*/
8967: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
1.5 ! raeburn 8968: int isalign = 0; /* true to align baselines */
1.1 albertel 8969: int line_raster(); /* draw diagonal for \Not */
1.5 ! raeburn 8970: int evalterm(); /* evaluate [arg], {arg} */
1.1 albertel 8971: /* -------------------------------------------------------------------------
8972: Obtain base, and maybe overlay, and rasterize them
8973: -------------------------------------------------------------------------- */
8974: /* --- check for optional offset2 arg --- */
8975: if ( offset2 == NOVALUE ) /* only if not explicitly specified*/
8976: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
8977: { int offsetval; /* test before using it */
8978: *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
1.5 ! raeburn 8979: offsetval = /* convert [offset2] to int */
! 8980: (int)(((double)evalterm(mimestore,expr2))+0.5);
1.1 albertel 8981: if ( abs(offsetval) <= 25 ) /* sanity check */
8982: offset2 = offsetval; } /* replace deafault */
8983: if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
8984: /* --- parse for base, bump expression past it, and rasterize it --- */
8985: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
1.5 ! raeburn 8986: if ( isempty(expr1) ) goto end_of_job; /* nothing to overlay, so quit */
! 8987: rastlift1 = rastlift = 0; /* reset all raisebox() lifts */
! 8988: if ( strstr(expr1,"\\raise") != NULL ) /* have a \raisebox */
! 8989: isalign = 2; /* so align baselines */
1.1 albertel 8990: if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
8991: == NULL ) goto end_of_job; /* quit if failed to rasterize */
8992: overlaysp = sp1; /*in case we return with no overlay*/
1.5 ! raeburn 8993: rastlift1 = rastlift; /* save lift for base expression */
1.1 albertel 8994: /* --- get overlay expression, and rasterize it --- */
8995: if ( overlay == NOVALUE ) /* get overlay from input stream */
8996: { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
1.5 ! raeburn 8997: if ( !isempty(expr2) ) { /* have an overlay */
! 8998: if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
! 8999: isalign = 2; /* so align baselines */
! 9000: sp2 = rasterize(expr2,size); } } /* rasterize overlay expression */
! 9001: else /* use specific built-in overlay */
1.1 albertel 9002: switch ( overlay )
9003: {
9004: default: break;
9005: case 1: /* e.g., \not overlays slash */
9006: sp2 = rasterize("/",size+1); /* rasterize overlay expression */
1.5 ! raeburn 9007: isalign = 0; /* automatically handled correctly */
1.1 albertel 9008: offset2 = max2(1,size-3); /* push / right a bit */
9009: offset2 = 0;
9010: break;
9011: case 2: /* e.g., \Not draws diagonal */
9012: sp2 = NULL; /* no overlay required */
1.5 ! raeburn 9013: isalign = 0; /* automatically handled correctly */
1.1 albertel 9014: if ( overlaysp != NULL ) /* check that we have raster */
9015: { raster *rp = overlaysp->image; /* raster to be \Not-ed */
9016: int width=rp->width, height=rp->height; /* raster dimensions */
9017: if ( 0 ) /* diagonal within bounding box */
9018: line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
9019: else /* construct "wide" diagonal */
9020: { int margin=3; /* desired extra margin width */
9021: sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
9022: if ( sp2 != NULL ) /* allocated successfully */
9023: line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
9024: break;
9025: case 3: /* e.g., \sout for strikeout */
9026: sp2 = NULL; /* no overlay required */
9027: if ( overlaysp != NULL ) /* check that we have raster */
1.5 ! raeburn 9028: { raster *rp = overlaysp->image; /* raster to be \sout-ed */
1.1 albertel 9029: int width=rp->width, height=rp->height; /* raster dimensions */
1.5 ! raeburn 9030: int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
1.1 albertel 9031: int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
9032: if ( 1 ) /* strikeout within bounding box */
9033: line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
9034: break;
9035: } /* --- end-of-switch(overlay) --- */
9036: if ( sp2 == NULL ) goto end_of_job; /*return sp1 if failed to rasterize*/
9037: /* -------------------------------------------------------------------------
9038: construct composite overlay
9039: -------------------------------------------------------------------------- */
1.5 ! raeburn 9040: overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
1.1 albertel 9041: end_of_job:
9042: return ( overlaysp );
9043: } /* --- end-of-function rastoverlay() --- */
9044:
9045:
9046: /* ==========================================================================
9047: * Function: rastfrac ( expression, size, basesp, isfrac, arg2, arg3 )
9048: * Purpose: \frac,\atop handler, returns a subraster corresponding to
9049: * expression (immediately following \frac,\atop) at font size
9050: * --------------------------------------------------------------------------
9051: * Arguments: expression (I/O) char ** to first char of null-terminated
9052: * string immediately following \frac to be
9053: * rasterized, and returning ptr immediately
9054: * following last character processed.
1.5 ! raeburn 9055: * size (I) int containing 0-7 default font size
1.1 albertel 9056: * basesp (I) subraster * to character (or subexpression)
9057: * immediately preceding \frac
9058: * (unused, but passed for consistency)
9059: * isfrac (I) int containing true to draw horizontal line
9060: * between numerator and denominator,
9061: * or false not to draw it (for \atop).
9062: * arg2 (I) int unused
9063: * arg3 (I) int unused
9064: * --------------------------------------------------------------------------
9065: * Returns: ( subraster * ) ptr to subraster corresponding to fraction,
9066: * or NULL for any parsing error
9067: * --------------------------------------------------------------------------
9068: * Notes: o
9069: * ======================================================================= */
9070: /* --- entry point --- */
9071: subraster *rastfrac ( char **expression, int size, subraster *basesp,
9072: int isfrac, int arg2, int arg3 )
9073: {
9074: /* -------------------------------------------------------------------------
9075: Allocations and Declarations
9076: -------------------------------------------------------------------------- */
9077: char *texsubexpr(), /*parse expression for numer,denom*/
1.3 albertel 9078: numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
1.1 albertel 9079: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
9080: subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */
9081: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
9082: int width=0, /* width of constructed raster */
9083: numheight=0; /* height of numerator */
9084: int baseht=0, baseln=0; /* height,baseline of base symbol */
9085: /*int istweak = 1;*/ /*true to tweak baseline alignment*/
9086: int rule_raster(), /* draw horizontal line for frac */
9087: lineheight = 1; /* thickness of fraction line */
1.2 albertel 9088: int vspace = (size>2?2:1); /*vertical space between components*/
1.1 albertel 9089: int delete_subraster(); /*free work areas in case of error*/
9090: int type_raster(); /* display debugging output */
9091: /* -------------------------------------------------------------------------
9092: Obtain numerator and denominator, and rasterize them
9093: -------------------------------------------------------------------------- */
9094: /* --- parse for numerator,denominator and bump expression past them --- */
9095: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
9096: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
9097: if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
9098: goto end_of_job; /* nothing to do, so quit */
9099: /* --- rasterize numerator, denominator --- */
9100: if ( *numer != '\000' ) /* have a numerator */
9101: if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
9102: == NULL ) goto end_of_job; /* and quit if failed */
9103: if ( *denom != '\000' ) /* have a denominator */
9104: if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
9105: == NULL ) /* failed */
9106: { if ( numsp != NULL ) /* already rasterized numerator */
9107: delete_subraster(numsp); /* so free now-unneeded numerator */
9108: goto end_of_job; } /* and quit */
9109: /* --- if one componenet missing, use a blank space for it --- */
9110: if ( numsp == NULL ) /* no numerator given */
9111: numsp = rasterize("[?]",size-1); /* missing numerator */
9112: if ( densp == NULL ) /* no denominator given */
9113: densp = rasterize("[?]",size-1); /* missing denominator */
9114: /* --- check that we got both components --- */
9115: if ( numsp==NULL || densp==NULL ) /* some problem */
9116: { delete_subraster(numsp); /*delete numerator (if it existed)*/
9117: delete_subraster(densp); /*delete denominator (if it existed)*/
9118: goto end_of_job; } /* and quit */
9119: /* --- get height of numerator (to determine where line belongs) --- */
9120: numheight = (numsp->image)->height; /* get numerator's height */
9121: /* -------------------------------------------------------------------------
9122: construct raster with numerator stacked over denominator
9123: -------------------------------------------------------------------------- */
9124: /* --- construct raster with numer/denom --- */
9125: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
9126: == NULL ) /* failed to construct numer/denom */
9127: { delete_subraster(numsp); /* so free now-unneeded numerator */
9128: delete_subraster(densp); /* and now-unneeded denominator */
9129: goto end_of_job; } /* and then quit */
9130: /* --- determine width of constructed raster --- */
9131: width = (fracsp->image)->width; /*just get width of embedded image*/
9132: /* --- initialize subraster parameters --- */
9133: fracsp->size = size; /* propagate font size forward */
9134: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
1.3 albertel 9135: fracsp->type = FRACRASTER; /* signal \frac image */
1.1 albertel 9136: if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */
9137: { baseht = (basesp->image)->height; /* height of base symbol */
9138: baseln = basesp->baseline; /* and its baseline */
9139: } /* --- end-of-if(basesp!=NULL) --- */
9140: /* -------------------------------------------------------------------------
9141: draw horizontal line between numerator and denominator
9142: -------------------------------------------------------------------------- */
1.3 albertel 9143: fraccenterline = numheight+vspace; /* signal that we have a \frac */
1.1 albertel 9144: if ( isfrac ) /*line for \frac, but not for \atop*/
1.3 albertel 9145: rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
1.1 albertel 9146: /* -------------------------------------------------------------------------
9147: return final result to caller
9148: -------------------------------------------------------------------------- */
9149: end_of_job:
9150: if ( msgfp!=NULL && msglevel>=99 )
9151: { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
9152: if ( fracsp != NULL ) /* have a constructed raster */
9153: type_raster(fracsp->image,msgfp); } /* display constructed raster */
9154: return ( fracsp );
9155: } /* --- end-of-function rastfrac() --- */
9156:
9157:
9158: /* ==========================================================================
9159: * Function: rastackrel ( expression, size, basesp, base, arg2, arg3 )
9160: * Purpose: \stackrel handler, returns a subraster corresponding to
9161: * stacked relation
9162: * --------------------------------------------------------------------------
9163: * Arguments: expression (I/O) char ** to first char of null-terminated
9164: * string immediately following \stackrel to be
9165: * rasterized, and returning ptr immediately
9166: * following last character processed.
1.5 ! raeburn 9167: * size (I) int containing 0-7 default font size
1.1 albertel 9168: * basesp (I) subraster * to character (or subexpression)
9169: * immediately preceding \stackrel
9170: * (unused, but passed for consistency)
9171: * base (I) int containing 1 if upper/first subexpression
9172: * is base relation, or 2 if lower/second is
9173: * arg2 (I) int unused
9174: * arg3 (I) int unused
9175: * --------------------------------------------------------------------------
9176: * Returns: ( subraster * ) ptr to subraster corresponding to stacked
9177: * relation, or NULL for any parsing error
9178: * --------------------------------------------------------------------------
9179: * Notes: o
9180: * ======================================================================= */
9181: /* --- entry point --- */
9182: subraster *rastackrel ( char **expression, int size, subraster *basesp,
9183: int base, int arg2, int arg3 )
9184: {
9185: /* -------------------------------------------------------------------------
9186: Allocations and Declarations
9187: -------------------------------------------------------------------------- */
1.3 albertel 9188: char *texsubexpr(), /*parse expression for upper,lower*/
9189: upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */
1.1 albertel 9190: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
9191: subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */
9192: int upsize = (base==1? size:size-1), /* font size for upper component */
9193: lowsize = (base==2? size:size-1); /* font size for lower component */
9194: int vspace = 1; /*vertical space between components*/
9195: int delete_subraster(); /*free work areas in case of error*/
9196: /* -------------------------------------------------------------------------
9197: Obtain numerator and denominator, and rasterize them
9198: -------------------------------------------------------------------------- */
9199: /* --- parse for numerator,denominator and bump expression past them --- */
9200: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
9201: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
9202: if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
9203: goto end_of_job; /* nothing to do, so quit */
9204: /* --- rasterize upper, lower --- */
9205: if ( *upper != '\000' ) /* have upper component */
9206: if ( (upsp = rasterize(upper,upsize)) /* so rasterize upper component */
9207: == NULL ) goto end_of_job; /* and quit if failed */
9208: if ( *lower != '\000' ) /* have lower component */
9209: if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
9210: == NULL ) /* failed */
9211: { if ( upsp != NULL ) /* already rasterized upper */
9212: delete_subraster(upsp); /* so free now-unneeded upper */
9213: goto end_of_job; } /* and quit */
9214: /* -------------------------------------------------------------------------
9215: construct stacked relation raster
9216: -------------------------------------------------------------------------- */
9217: /* --- construct stacked relation --- */
9218: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
9219: == NULL ) goto end_of_job; /* quit if failed */
9220: /* --- initialize subraster parameters --- */
9221: relsp->size = size; /* propagate font size forward */
9222: /* -------------------------------------------------------------------------
9223: return final result to caller
9224: -------------------------------------------------------------------------- */
9225: end_of_job:
9226: return ( relsp );
9227: } /* --- end-of-function rastackrel() --- */
9228:
9229:
9230: /* ==========================================================================
9231: * Function: rastmathfunc ( expression, size, basesp, base, arg2, arg3 )
9232: * Purpose: \log, \lim, etc handler, returns a subraster corresponding
9233: * to math functions
9234: * --------------------------------------------------------------------------
9235: * Arguments: expression (I/O) char ** to first char of null-terminated
9236: * string immediately following \mathfunc to be
9237: * rasterized, and returning ptr immediately
9238: * following last character processed.
1.5 ! raeburn 9239: * size (I) int containing 0-7 default font size
1.1 albertel 9240: * basesp (I) subraster * to character (or subexpression)
9241: * immediately preceding \mathfunc
9242: * (unused, but passed for consistency)
9243: * mathfunc (I) int containing 1=arccos, 2=arcsin, etc.
9244: * islimits (I) int containing 1 if function may have
9245: * limits underneath, e.g., \lim_{n\to\infty}
9246: * arg3 (I) int unused
9247: * --------------------------------------------------------------------------
9248: * Returns: ( subraster * ) ptr to subraster corresponding to mathfunc,
9249: * or NULL for any parsing error
9250: * --------------------------------------------------------------------------
9251: * Notes: o
9252: * ======================================================================= */
9253: /* --- entry point --- */
9254: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
9255: int mathfunc, int islimits, int arg3 )
9256: {
9257: /* -------------------------------------------------------------------------
9258: Allocations and Declarations
9259: -------------------------------------------------------------------------- */
9260: char *texscripts(), /* parse expression for _limits */
1.3 albertel 9261: func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
1.1 albertel 9262: char *texsubexpr(), /* parse expression for arg */
1.3 albertel 9263: funcarg[MAXTOKNSZ+1]; /* optional func arg */
1.1 albertel 9264: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
9265: subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
9266: int limsize = size-1; /* font size for limits */
9267: int vspace = 1; /*vertical space between components*/
9268: int delete_subraster(); /*free work areas in case of error*/
9269: /* --- table of function names by mathfunc number --- */
9270: static int numnames = 34; /* number of names in table */
9271: static char *funcnames[] = {
9272: "error", /* 0 index is illegal/error bucket*/
9273: "arccos", "arcsin", "arctan", /* 1 - 3 */
9274: "arg", "cos", "cosh", /* 4 - 6 */
9275: "cot", "coth", "csc", /* 7 - 9 */
9276: "deg", "det", "dim", /* 10 - 12 */
9277: "exp", "gcd", "hom", /* 13 - 15 */
9278: "inf", "ker", "lg", /* 16 - 18 */
9279: "lim", "liminf", "limsup", /* 19 - 21 */
9280: "ln", "log", "max", /* 22 - 24 */
9281: "min", "Pr", "sec", /* 25 - 27 */
9282: "sin", "sinh", "sup", /* 28 - 30 */
9283: "tan", "tanh", /* 31 - 32 */
9284: /* --- extra mimetex funcnames --- */
9285: "tr", /* 33 */
9286: "pmod" /* 34 */
9287: } ;
9288: /* -------------------------------------------------------------------------
9289: set up and rasterize function name in \rm
9290: -------------------------------------------------------------------------- */
9291: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
9292: switch ( mathfunc ) /* check for special processing */
9293: {
9294: default: /* no special processing */
9295: strcpy(func,"{\\rm~"); /* init string with {\rm~ */
9296: strcat(func,funcnames[mathfunc]); /* concat function name */
9297: strcat(func,"}"); /* and add terminating } */
9298: break;
9299: case 34: /* \pmod{x} --> (mod x) */
9300: /* --- parse for \pmod{arg} argument --- */
9301: *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
9302: strcpy(func,"{\\({\\rm~mod}"); /* init with {\left({\rm~mod} */
9303: strcat(func,"\\hspace2"); /* concat space */
9304: strcat(func,funcarg); /* and \pmodargument */
9305: strcat(func,"\\)}"); /* and add terminating \right)} */
9306: break;
9307: } /* --- end-of-switch(mathfunc) --- */
9308: if ( (funcsp = rasterize(func,size)) /* rasterize function name */
9309: == NULL ) goto end_of_job; /* and quit if failed */
9310: mathfuncsp = funcsp; /* just return funcsp if no limits */
9311: if ( !islimits ) goto end_of_job; /* treat any subscript normally */
9312: /* -------------------------------------------------------------------------
9313: Obtain limits, if permitted and if provided, and rasterize them
9314: -------------------------------------------------------------------------- */
9315: /* --- parse for subscript limits, and bump expression past it(them) --- */
9316: *expression = texscripts(*expression,limits,limits,1);
9317: if ( *limits=='\000') goto end_of_job; /* no limits, nothing to do, quit */
9318: /* --- rasterize limits --- */
9319: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
9320: == NULL ) goto end_of_job; /* and quit if failed */
9321: /* -------------------------------------------------------------------------
9322: construct func atop limits
9323: -------------------------------------------------------------------------- */
9324: /* --- construct func atop limits --- */
9325: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
9326: == NULL ) goto end_of_job; /* quit if failed */
9327: /* --- initialize subraster parameters --- */
9328: mathfuncsp->size = size; /* propagate font size forward */
9329: /* -------------------------------------------------------------------------
9330: return final result to caller
9331: -------------------------------------------------------------------------- */
9332: end_of_job:
9333: return ( mathfuncsp );
9334: } /* --- end-of-function rastmathfunc() --- */
9335:
9336:
9337: /* ==========================================================================
9338: * Function: rastsqrt ( expression, size, basesp, arg1, arg2, arg3 )
9339: * Purpose: \sqrt handler, returns a subraster corresponding to
9340: * expression (immediately following \sqrt) at font size
9341: * --------------------------------------------------------------------------
9342: * Arguments: expression (I/O) char ** to first char of null-terminated
9343: * string immediately following \sqrt to be
9344: * rasterized, and returning ptr immediately
9345: * following last character processed.
1.5 ! raeburn 9346: * size (I) int containing 0-7 default font size
1.1 albertel 9347: * basesp (I) subraster * to character (or subexpression)
9348: * immediately preceding \accent
9349: * (unused, but passed for consistency)
9350: * arg1 (I) int unused
9351: * arg2 (I) int unused
9352: * arg3 (I) int unused
9353: * --------------------------------------------------------------------------
9354: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
9355: * or NULL for any parsing error
9356: * (expression ptr unchanged if error occurs)
9357: * --------------------------------------------------------------------------
9358: * Notes: o
9359: * ======================================================================= */
9360: /* --- entry point --- */
9361: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
9362: int arg1, int arg2, int arg3 )
9363: {
9364: /* -------------------------------------------------------------------------
9365: Allocations and Declarations
9366: -------------------------------------------------------------------------- */
1.3 albertel 9367: char *texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
9368: rootarg[MAXSUBXSZ+1]; /* optional \sqrt[rootarg]{...} */
1.1 albertel 9369: subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */
9370: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
9371: *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
9372: int sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
9373: rootheight=0, rootwidth=0, /* height,width of rootarg raster */
9374: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
9375: int rastput(); /* put subexpr in constructed sqrt */
9376: int overspace = 2; /*space between subexpr and overbar*/
9377: int delete_subraster(); /* free work areas */
9378: /* -------------------------------------------------------------------------
9379: Obtain subexpression to be sqrt-ed, and rasterize it
9380: -------------------------------------------------------------------------- */
9381: /* --- first check for optional \sqrt[rootarg]{...} --- */
9382: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
9383: { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
9384: if ( *rootarg != '\000' ) /* got rootarg */
9385: if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
9386: != NULL ) /* rasterized successfully */
9387: { rootheight = (rootsp->image)->height; /* get height of rootarg */
9388: rootwidth = (rootsp->image)->width; } /* and its width */
9389: } /* --- end-of-if(**expression=='[') --- */
9390: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
9391: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9392: if ( *subexpr == '\000' ) /* couldn't get subexpression */
9393: goto end_of_job; /* nothing to do, so quit */
9394: /* --- rasterize subexpression to be accented --- */
9395: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
9396: == NULL ) goto end_of_job; /* quit if failed */
9397: /* -------------------------------------------------------------------------
9398: determine height and width of sqrt raster to be constructed
9399: -------------------------------------------------------------------------- */
9400: /* --- first get height and width of subexpr --- */
9401: subheight = (subsp->image)->height; /* height of subexpr */
9402: subwidth = (subsp->image)->width; /* and its width */
9403: pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
9404: /* --- determine height and width of sqrt to contain subexpr --- */
9405: sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
1.5 ! raeburn 9406: surdwidth = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
1.1 albertel 9407: sqrtwidth = subwidth + surdwidth + 1; /* total width */
9408: /* -------------------------------------------------------------------------
9409: construct sqrt (with room to move in subexpr) and embed subexpr in it
9410: -------------------------------------------------------------------------- */
9411: /* --- construct sqrt --- */
1.5 ! raeburn 9412: if ( (sqrtsp=accent_subraster(SQRTACCENT,
! 9413: (rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
1.1 albertel 9414: == NULL ) goto end_of_job; /* quit if failed to build sqrt */
9415: /* --- embed subexpr in sqrt at lower-right corner--- */
9416: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
9417: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
9418: /* --- "embed" rootarg at upper-left --- */
9419: if ( rootsp != NULL ) /*have optional \sqrt[rootarg]{...}*/
9420: {
9421: /* --- allocate full raster to contain sqrtsp and rootsp --- */
9422: int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
9423: fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
9424: subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
9425: if ( fullsp != NULL ) /* allocated successfully */
9426: { /* --- embed sqrtsp exactly at lower-right corner --- */
9427: rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
9428: fullheight-sqrtheight,fullwidth-sqrtwidth,1);
9429: /* --- embed rootsp near upper-left, nestled above leading surd --- */
9430: rastput(fullsp->image,rootsp->image,
9431: 0,max2(0,surdwidth-rootwidth-2-size),0);
9432: /* --- replace sqrtsp with fullsp --- */
9433: delete_subraster(sqrtsp); /* free original sqrtsp */
9434: sqrtsp = fullsp; /* and repoint it to fullsp instead*/
9435: sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
9436: } /* --- end-of-if(rootsp!=NULL) --- */
9437: /* --- initialize subraster parameters --- */
9438: sqrtsp->size = size; /* propagate font size forward */
9439: /* -------------------------------------------------------------------------
9440: free unneeded component subrasters and return final result to caller
9441: -------------------------------------------------------------------------- */
9442: end_of_job:
9443: if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
9444: return ( sqrtsp );
9445: } /* --- end-of-function rastsqrt() --- */
9446:
9447:
9448: /* ==========================================================================
9449: * Function: rastaccent (expression,size,basesp,accent,isabove,isscript)
9450: * Purpose: \hat, \vec, \etc handler, returns a subraster corresponding
9451: * to expression (immediately following \accent) at font size
9452: * --------------------------------------------------------------------------
9453: * Arguments: expression (I/O) char ** to first char of null-terminated
9454: * string immediately following \accent to be
9455: * rasterized, and returning ptr immediately
9456: * following last character processed.
1.5 ! raeburn 9457: * size (I) int containing 0-7 default font size
1.1 albertel 9458: * basesp (I) subraster * to character (or subexpression)
9459: * immediately preceding \accent
9460: * (unused, but passed for consistency)
9461: * accent (I) int containing HATACCENT or VECACCENT, etc,
9462: * between numerator and denominator,
9463: * or false not to draw it (for \over).
9464: * isabove (I) int containing true if accent is above
9465: * expression to be accented, or false
9466: * if accent is below (e.g., underbrace)
9467: * isscript (I) int containing true if sub/superscripts
9468: * allowed (for under/overbrace), or 0 if not.
9469: * --------------------------------------------------------------------------
9470: * Returns: ( subraster * ) ptr to subraster corresponding to expression,
9471: * or NULL for any parsing error
9472: * (expression ptr unchanged if error occurs)
9473: * --------------------------------------------------------------------------
9474: * Notes: o Also handles \overbrace{}^{} and \underbrace{}_{} by way
9475: * of isabove and isscript args.
9476: * ======================================================================= */
9477: /* --- entry point --- */
9478: subraster *rastaccent ( char **expression, int size, subraster *basesp,
9479: int accent, int isabove, int isscript )
9480: {
9481: /* -------------------------------------------------------------------------
9482: Allocations and Declarations
9483: -------------------------------------------------------------------------- */
1.3 albertel 9484: char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
1.1 albertel 9485: char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
1.3 albertel 9486: subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */
1.1 albertel 9487: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
9488: subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
9489: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
1.5 ! raeburn 9490: int accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
1.1 albertel 9491: subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
9492: int delete_subraster(); /*free work areas in case of error*/
9493: int vspace = 0; /*vertical space between accent,sub*/
9494: /* -------------------------------------------------------------------------
9495: Obtain subexpression to be accented, and rasterize it
9496: -------------------------------------------------------------------------- */
9497: /* --- parse for subexpr to be accented, and bump expression past it --- */
9498: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9499: if ( *subexpr=='\000' ) /* couldn't get subexpression */
9500: goto end_of_job; /* nothing to do, so quit */
9501: /* --- rasterize subexpression to be accented --- */
9502: if ( (subsp = rasterize(subexpr,size)) /*rasterize subexpr at original size*/
9503: == NULL ) goto end_of_job; /* quit if failed */
9504: /* -------------------------------------------------------------------------
9505: determine desired accent width and height
9506: -------------------------------------------------------------------------- */
9507: /* --- first get height and width of subexpr --- */
9508: subheight = (subsp->image)->height; /* height of subexpr */
9509: subwidth = (subsp->image)->width; /* and its width is overall width */
9510: pixsz = (subsp->image)->pixsz; /* original pixsz remains constant */
9511: /* --- determine desired width, height of accent --- */
9512: accwidth = subwidth; /* same width as subexpr */
1.2 albertel 9513: accheight = 4; /* default for bars */
1.1 albertel 9514: switch ( accent )
9515: { default: break; /* default okay */
9516: case DOTACCENT: case DDOTACCENT:
1.2 albertel 9517: accheight = (size<4? 3:4); /* default for dots */
1.1 albertel 9518: break;
1.2 albertel 9519: case VECACCENT:
9520: vspace = 1; /* set 1-pixel vertical space */
1.5 ! raeburn 9521: accdir = isscript; /* +1=right,-1=left,0=lr; +10for==>*/
! 9522: isscript = 0; /* >>don't<< signal sub/supscript */
1.2 albertel 9523: case HATACCENT:
1.1 albertel 9524: accheight = 7; /* default */
9525: if ( subwidth < 10 ) accheight = 5; /* unless small width */
9526: else if ( subwidth > 25 ) accheight = 9; /* or large */
9527: break;
9528: } /* --- end-of-switch(accent) --- */
9529: accheight = min2(accheight,subheight); /*never higher than accented subexpr*/
9530: /* -------------------------------------------------------------------------
9531: construct accent, and construct subraster with accent over (or under) subexpr
9532: -------------------------------------------------------------------------- */
9533: /* --- first construct accent --- */
1.5 ! raeburn 9534: if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
1.1 albertel 9535: == NULL ) goto end_of_job; /* quit if failed to build accent */
9536: /* --- now stack accent above (or below) subexpr, and free both args --- */
9537: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
9538: : rastack(accsp,subsp,2,vspace,1,3)); /*accent below subexpr*/
9539: if ( accsubsp == NULL ) /* failed to stack accent */
9540: { delete_subraster(subsp); /* free unneeded subsp */
9541: delete_subraster(accsp); /* and unneeded accsp */
9542: goto end_of_job; } /* and quit */
9543: /* -------------------------------------------------------------------------
9544: look for super/subscript (annotation for over/underbrace)
9545: -------------------------------------------------------------------------- */
9546: /* --- first check whether accent permits accompanying annotations --- */
9547: if ( !isscript ) goto end_of_job; /* no annotations for this accent */
9548: /* --- now get scripts if there actually are any --- */
9549: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
9550: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
9551: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
9552: /* --- rasterize script annotation at size-2 --- */
9553: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
9554: == NULL ) goto end_of_job; /* quit if failed */
9555: /* --- stack annotation above (or below) accent, and free both args --- */
9556: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
9557: : rastack(scrsp,accsubsp,2,0,1,3)); /* accent below base */
9558: /* -------------------------------------------------------------------------
9559: return final result to caller
9560: -------------------------------------------------------------------------- */
9561: end_of_job:
9562: if ( accsubsp != NULL ) /* initialize subraster parameters */
9563: accsubsp->size = size; /* propagate font size forward */
9564: return ( accsubsp );
9565: } /* --- end-of-function rastaccent() --- */
9566:
9567:
9568: /* ==========================================================================
1.2 albertel 9569: * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
1.1 albertel 9570: * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
9571: * to char(s) within {}'s rendered at size
9572: * --------------------------------------------------------------------------
9573: * Arguments: expression (I/O) char ** to first char of null-terminated
9574: * string immediately following \font to be
9575: * rasterized, and returning ptr immediately
9576: * following last character processed.
1.5 ! raeburn 9577: * size (I) int containing 0-7 default font size
1.1 albertel 9578: * basesp (I) subraster * to character (or subexpression)
9579: * immediately preceding \accent
9580: * (unused, but passed for consistency)
1.2 albertel 9581: * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
1.1 albertel 9582: * arg2 (I) int unused
9583: * arg3 (I) int unused
9584: * --------------------------------------------------------------------------
9585: * Returns: ( subraster * ) ptr to subraster corresponding to chars
9586: * between {}'s, or NULL for any parsing error
9587: * --------------------------------------------------------------------------
9588: * Notes: o
9589: * ======================================================================= */
9590: /* --- entry point --- */
9591: subraster *rastfont ( char **expression, int size, subraster *basesp,
1.2 albertel 9592: int ifontnum, int arg2, int arg3 )
1.1 albertel 9593: {
9594: /* -------------------------------------------------------------------------
9595: Allocations and Declarations
9596: -------------------------------------------------------------------------- */
1.3 albertel 9597: char *texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
9598: subexpr[MAXSUBXSZ+1]; /* turn \cal{AB} into \calA\calB */
1.1 albertel 9599: char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
1.2 albertel 9600: char *name = NULL; /* fontinfo[ifontnum].name */
9601: int family = 0, /* fontinfo[ifontnum].family */
9602: istext = 0, /* fontinfo[ifontnum].istext */
9603: class = 0; /* fontinfo[ifontnum].class */
1.1 albertel 9604: subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
9605: *rastflags(); /* or just set flag to switch font */
1.2 albertel 9606: int oldsmashmargin = smashmargin; /* turn off smash in text mode */
9607: #if 0
1.1 albertel 9608: /* --- fonts recognized by rastfont --- */
1.5 ! raeburn 9609: static int nfonts = 11; /* legal font #'s are 1...nfonts */
1.1 albertel 9610: static struct {char *name; int class;}
9611: fonts[] =
9612: { /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
1.2 albertel 9613: { "\\math", 0 },
9614: { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
9615: { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
9616: { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
9617: { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
9618: { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
9619: { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
1.3 albertel 9620: { "\\mathrm", -1 }, /*(7) \mathrm */
9621: { "\\cyr", -1 }, /*(8) \cyr */
1.5 ! raeburn 9622: { "\\textgreek",-1 }, /*(9) \textgreek */
! 9623: { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
! 9624: { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
1.1 albertel 9625: { NULL, 0 }
9626: } ; /* --- end-of-fonts[] --- */
1.2 albertel 9627: #endif
1.1 albertel 9628: /* -------------------------------------------------------------------------
9629: first get font name and class to determine type of conversion desired
9630: -------------------------------------------------------------------------- */
1.2 albertel 9631: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
9632: name = fontinfo[ifontnum].name; /* font name */
9633: family = fontinfo[ifontnum].family; /* font family */
9634: istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
9635: class = fontinfo[ifontnum].class; /* font class */
9636: if ( istext ) /* text (respect blanks) */
1.3 albertel 9637: { mathsmashmargin = smashmargin; /* needed for \text{if $n-m$ even} */
9638: smashmargin = 0; } /* don't smash internal blanks */
1.1 albertel 9639: /* -------------------------------------------------------------------------
9640: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
9641: -------------------------------------------------------------------------- */
1.2 albertel 9642: if ( 1 || class<0 ) /* not character-by-character */
1.1 albertel 9643: {
9644: /* ---
9645: if \font not immediately followed by { then it has no arg, so just set flag
9646: ------------------------------------------------------------------------ */
9647: if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
9648: {
9649: if ( msgfp!=NULL && msglevel>=99 )
1.2 albertel 9650: fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
9651: fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
1.1 albertel 9652: goto end_of_job;
9653: } /* --- end-of-if(*(*expression)!='{') --- */
9654: /* ---
9655: convert \font{abc} --> {\font~abc}
9656: ---------------------------------- */
9657: /* --- parse for {fontchars} arg, and bump expression past it --- */
9658: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
9659: if ( msgfp!=NULL && msglevel>=99 )
9660: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
9661: /* --- convert all fontchars at the same time --- */
9662: strcpy(subexpr,"{"); /* start off with opening { */
9663: strcat(subexpr,name); /* followed by font name */
9664: strcat(subexpr,"~"); /* followed by whitespace */
9665: strcat(subexpr,fontchars); /* followed by all the chars */
9666: strcat(subexpr,"}"); /* terminate with closing } */
9667: } /* --- end-of-if(class<0) --- */
9668: else /* character-by-character */
9669: {
9670: /* ---
9671: convert ABC to \calA\calB\calC
9672: ------------------------------ */
9673: int isprevchar=0; /* true if prev char converted */
9674: /* --- parse for {fontchars} arg, and bump expression past it --- */
9675: *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
9676: if ( msgfp!=NULL && msglevel>=99 )
9677: fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
9678: /* --- convert fontchars one at a time --- */
9679: strcpy(subexpr,"{\\rm~"); /* start off with opening {\rm */
9680: strcpy(subexpr,"{"); /* nope, just start off with { */
9681: for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
9682: {
9683: if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
1.2 albertel 9684: { if ( 0 || istext ) /* and we're in a text mode font */
1.1 albertel 9685: strcat(subexpr,"\\;"); } /* so respect whitespace */
9686: else /* char to be displayed in font */
9687: { int exprlen = 0; /* #chars in subexpr before fchar */
9688: int isinclass = 0; /* set true if fchar in font class */
1.2 albertel 9689: /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
1.1 albertel 9690: switch ( class ) /* check if fchar is in font class */
9691: { default: break; /* no chars in unrecognized class */
9692: case 1: if ( isupper((int)fchar) ) isinclass=1; break;
9693: case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
9694: case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
9695: case 4: if ( islower((int)fchar) ) isinclass=1; break;
9696: case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
9697: case 9: isinclass=1; break; }
9698: if ( isinclass ) /* convert current char to \font */
9699: { strcat(subexpr,name); /* by prefixing it with font name */
9700: isprevchar = 1; } /* and set flag to signal separator*/
9701: else /* current char not in \font */
9702: { if ( isprevchar ) /* extra separator only after \font*/
9703: if ( isalpha(fchar) ) /* separator only before alpha */
9704: strcat(subexpr,"~"); /* need separator after \font */
9705: isprevchar = 0; } /* reset flag for next char */
9706: exprlen = strlen(subexpr); /* #chars so far */
9707: subexpr[exprlen] = fchar; /*fchar immediately after \fontname*/
9708: subexpr[exprlen+1] = '\000'; } /* replace terminating '\0' */
9709: } /* --- end-of-for(pfchars) --- */
9710: strcat(subexpr,"}"); /* add closing } */
9711: } /* --- end-of-if/else(class<0) --- */
9712: /* -------------------------------------------------------------------------
9713: rasterize subexpression containing chars to be rendered at font
9714: -------------------------------------------------------------------------- */
9715: if ( msgfp!=NULL && msglevel>=99 )
9716: fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
9717: if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
9718: == NULL ) goto end_of_job; /* and quit if failed */
9719: /* -------------------------------------------------------------------------
9720: back to caller with chars rendered in font
9721: -------------------------------------------------------------------------- */
9722: end_of_job:
1.2 albertel 9723: smashmargin = oldsmashmargin; /* restore smash */
1.3 albertel 9724: mathsmashmargin = SMASHMARGIN; /* this one probably not necessary */
1.2 albertel 9725: if ( istext && fontsp!=NULL ) /* raster contains text mode font */
9726: fontsp->type = blanksignal; /* signal nosmash */
1.1 albertel 9727: return ( fontsp ); /* chars rendered in font */
9728: } /* --- end-of-function rastfont() --- */
9729:
9730:
9731: /* ==========================================================================
9732: * Function: rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
9733: * Purpose: \begin{}...\end{} handler, returns a subraster corresponding
9734: * to array expression within environment, i.e., rewrites
9735: * \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
9736: * --------------------------------------------------------------------------
9737: * Arguments: expression (I/O) char ** to first char of null-terminated
9738: * string immediately following \begin to be
9739: * rasterized, and returning ptr immediately
9740: * following last character processed.
1.5 ! raeburn 9741: * size (I) int containing 0-7 default font size
1.1 albertel 9742: * basesp (I) subraster * to character (or subexpression)
9743: * immediately preceding \begin
9744: * (unused, but passed for consistency)
9745: * arg1 (I) int unused
9746: * arg2 (I) int unused
9747: * arg3 (I) int unused
9748: * --------------------------------------------------------------------------
9749: * Returns: ( subraster * ) ptr to subraster corresponding to array
9750: * expression, or NULL for any parsing error
9751: * --------------------------------------------------------------------------
9752: * Notes: o
9753: * ======================================================================= */
9754: /* --- entry point --- */
9755: subraster *rastbegin ( char **expression, int size, subraster *basesp,
9756: int arg1, int arg2, int arg3 )
9757: {
9758: /* -------------------------------------------------------------------------
9759: Allocations and Declarations
9760: -------------------------------------------------------------------------- */
1.3 albertel 9761: char *texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
1.1 albertel 9762: *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
9763: char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
9764: int strreplace(); /* replace substring in string */
9765: char *strchange(); /*\begin...\end --> {\begin...\end}*/
9766: char *delims = (char *)NULL; /* mdelims[ienviron] */
9767: subraster *rasterize(), *sp=NULL; /* rasterize environment */
9768: int ienviron = 0; /* environs[] index */
9769: int nbegins = 0; /* #\begins nested beneath this one*/
9770: int envlen=0, sublen=0; /* #chars in environ, subexpr */
9771: static int blevel = 0; /* \begin...\end nesting level */
9772: static char *mdelims[] = { NULL, NULL, NULL, NULL,
9773: "()","[]","{}","||","==", /* for pbBvVmatrix */
1.3 albertel 9774: NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
1.1 albertel 9775: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
9776: static char *environs[] = { /* types of environments we process*/
9777: "eqnarray", /* 0 eqnarray environment */
9778: "array", /* 1 array environment */
9779: "matrix", /* 2 array environment */
9780: "tabular", /* 3 array environment */
9781: "pmatrix", /* 4 ( ) */
9782: "bmatrix", /* 5 [ ] */
9783: "Bmatrix", /* 6 { } */
9784: "vmatrix", /* 7 | | */
9785: "Vmatrix", /* 8 || || */
9786: "gather", /* 9 gather environment */
9787: "align", /* 10 align environment */
9788: "verbatim", /* 11 verbatim environment */
9789: "picture", /* 12 picture environment */
1.3 albertel 9790: "cases", /* 13 cases environment */
9791: "equation", /* 14 for \begin{equation} */
1.1 albertel 9792: NULL }; /* trailer */
9793: /* -------------------------------------------------------------------------
9794: determine type of environment we're beginning
9795: -------------------------------------------------------------------------- */
9796: /* --- first bump nesting level --- */
9797: blevel++; /* count \begin...\begin...'s */
9798: /* --- \begin must be followed by {type_of_environment} --- */
9799: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
9800: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
9801: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
1.5 ! raeburn 9802: {strsqueeze(delims,1);} /* treat it as environment */
1.1 albertel 9803: /* --- look up environment in our table --- */
9804: for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
9805: if ( environs[ienviron] == NULL ) /* found NULL before match */
9806: goto end_of_job; /* so quit */
9807: else /* see if we have an exact match */
9808: if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
9809: break; /* leave loop with ienviron index */
9810: /* --- accumulate any additional params for this environment --- */
9811: *subexpr = '\000'; /* reset subexpr to empty string */
9812: delims = mdelims[ienviron]; /* mdelims[] string for ienviron */
9813: if ( delims != NULL ) /* add appropriate opening delim */
9814: { strcpy(subexpr,"\\"); /* start with \ for (,[,{,|,= */
9815: strcat(subexpr,delims); /* then add opening delim */
9816: subexpr[2] = '\000'; } /* remove extraneous closing delim */
9817: switch ( ienviron )
9818: {
9819: default: goto end_of_job; /* environ not implemented yet */
9820: case 0: /* \begin{eqnarray} */
9821: strcpy(subexpr,"\\array{rcl$"); /* set default rcl for eqnarray */
9822: break;
9823: case 1: case 2: case 3: /* \begin{array} followed by {lcr} */
9824: strcpy(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
9825: skipwhite(exprptr); /* bump to next non-white char */
9826: if ( *exprptr == '{' ) /* assume we have {lcr} argument */
9827: { exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
9828: if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
9829: strcat(subexpr,"$"); } /* add terminating $ to lcr */
9830: break;
9831: case 4: case 5: case 6: /* \begin{pmatrix} or b,B,v,Vmatrix */
9832: case 7: case 8:
9833: strcat(subexpr,"\\array{"); /*start with mimeTeX \array{ command*/
9834: break;
9835: case 9: /* gather */
9836: strcat(subexpr,"\\array{c$"); /* center equations */
9837: break;
9838: case 10: /* align */
9839: strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
9840: break;
9841: case 11: /* verbatim */
9842: strcat(subexpr,"{\\rm "); /* {\rm ...} */
9843: /*strcat(subexpr,"\\\\{\\rm ");*/ /* \\{\rm } doesn't work in context */
9844: break;
9845: case 12: /* picture */
9846: strcat(subexpr,"\\picture"); /* picture environment */
9847: skipwhite(exprptr); /* bump to next non-white char */
9848: if ( *exprptr == '(' ) /*assume we have (width,height) arg*/
9849: { exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
9850: if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
9851: strcat(subexpr,"{"); /* opening { after (width,height) */
9852: break;
1.3 albertel 9853: case 13: /* cases */
9854: strcat(subexpr,"\\array{ll$"); /* a&b \\ c&d etc */
9855: break;
9856: case 14: /* \begin{equation} */
9857: strcat(subexpr,"{"); /* just enclose expression in {}'s */
9858: break;
1.1 albertel 9859: } /* --- end-of-switch(ienviron) --- */
9860: /* -------------------------------------------------------------------------
9861: locate matching \end{...}
9862: -------------------------------------------------------------------------- */
9863: /* --- first \end following \begin --- */
9864: if ( (endptr=strstr(exprptr,endtoken)) /* find 1st \end following \begin */
9865: == NULL ) goto end_of_job; /* and quit if no \end found */
9866: /* --- find matching endptr by pushing past any nested \begin's --- */
9867: begptr = exprptr; /* start after first \begin{...} */
9868: while ( 1 ) /*break when we find matching \end*/
9869: {
9870: /* --- first, set ptr to closing } terminating current \end{...} --- */
9871: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
9872: == NULL ) goto end_of_job; /* and quit if no } found */
9873: /* -- locate next nested \begin --- */
9874: if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
9875: == NULL ) break; /*no more, so we have matching \end*/
9876: begptr += strlen(begtoken); /* push ptr past token */
9877: if ( begptr >= endptr ) break; /* past endptr, so not nested */
9878: /* --- have nested \begin, so push forward to next \end --- */
9879: nbegins++; /* count another nested \begin */
9880: if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
9881: == NULL ) goto end_of_job; /* and quit if none found */
9882: } /* --- end-of-while(1) --- */
9883: /* --- push expression past closing } of \end{} --- */
9884: *expression = braceptr+1; /* resume processing after } */
9885: /* -------------------------------------------------------------------------
9886: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
9887: -------------------------------------------------------------------------- */
9888: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
9889: sublen = strlen(subexpr); /* #chars in "preamble" */
9890: envlen = (int)(endptr-exprptr); /* #chars between \begin{}{}...\end */
9891: memcpy(subexpr+sublen,exprptr,envlen); /*concatanate environ after subexpr*/
9892: subexpr[sublen+envlen] = '\000'; /* and null-terminate */
9893: if ( 2 > 1 ) /* always... */
9894: strcat(subexpr,"}"); /* ...followed by terminating } */
9895: /* --- add terminating \right), etc, if necessary --- */
9896: if ( delims != (char *)NULL ) /* need closing delim */
9897: { strcat(subexpr,"\\"); /* start with \ for ),],},|,= */
9898: strcat(subexpr,delims+1); } /* add appropriate closing delim */
9899: /* -------------------------------------------------------------------------
9900: change nested \begin...\end to {\begin...\end} so \array{} can handle them
9901: -------------------------------------------------------------------------- */
9902: if ( nbegins > 0 ) /* have nested begins */
9903: if ( blevel < 2 ) /* only need to do this once */
9904: {
9905: begptr = subexpr; /* start at beginning of subexpr */
9906: while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
9907: { strchange(0,begptr,"{"); /* \begin --> {\begin */
9908: begptr += strlen(begtoken); } /* continue past {\begin */
9909: endptr = subexpr; /* start at beginning of subexpr */
9910: while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
9911: if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
9912: == NULL ) goto end_of_job; /* and quit if no } found */
9913: else /* found terminating } */
9914: { strchange(0,braceptr,"}"); /* \end{...} --> \end{...}} */
9915: endptr = braceptr+1; } /* continue past \end{...} */
9916: } /* --- end-of-if(nbegins>0) --- */
9917: /* -------------------------------------------------------------------------
9918: post process as necessary
9919: -------------------------------------------------------------------------- */
9920: switch ( ienviron )
9921: {
9922: default: break; /* no post-processing required */
9923: case 10: /* align */
9924: strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
9925: strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
9926: strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
9927: strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
9928: strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
9929: strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
9930: strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
9931: if ( nbegins < 1 ) /* don't modify nested arrays */
9932: strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
9933: strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
9934: strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
9935: strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
9936: strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
9937: strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
9938: break;
9939: case 11: /* verbatim */
9940: strreplace(subexpr,"\n","\\\\",0); /* xlate \n newline to latex \\ */
9941: /*strcat(subexpr,"\\\\");*/ /* add final latex \\ newline */
9942: break;
9943: case 12: /* picture */
9944: strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
9945: strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
9946: strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
9947: break;
9948: } /* --- end-of-switch(ienviron) --- */
9949: /* -------------------------------------------------------------------------
9950: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
9951: -------------------------------------------------------------------------- */
9952: /* --- debugging output --- */
9953: if ( msgfp!=NULL && msglevel>=99 )
9954: fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
9955: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
9956: sp = rasterize(subexpr,size); /* rasterize subexpr */
9957: end_of_job:
9958: blevel--; /* decrement \begin nesting level */
9959: return ( sp ); /* back to caller with sp or NULL */
9960: } /* --- end-of-function rastbegin() --- */
9961:
9962:
9963: /* ==========================================================================
9964: * Function: rastarray ( expression, size, basesp, arg1, arg2, arg3 )
9965: * Purpose: \array handler, returns a subraster corresponding to array
9966: * expression (immediately following \array) at font size
9967: * --------------------------------------------------------------------------
9968: * Arguments: expression (I/O) char ** to first char of null-terminated
9969: * string immediately following \array to be
9970: * rasterized, and returning ptr immediately
9971: * following last character processed.
1.5 ! raeburn 9972: * size (I) int containing 0-7 default font size
1.1 albertel 9973: * basesp (I) subraster * to character (or subexpression)
9974: * immediately preceding \array
9975: * (unused, but passed for consistency)
9976: * arg1 (I) int unused
9977: * arg2 (I) int unused
9978: * arg3 (I) int unused
9979: * --------------------------------------------------------------------------
9980: * Returns: ( subraster * ) ptr to subraster corresponding to array
9981: * expression, or NULL for any parsing error
9982: * --------------------------------------------------------------------------
9983: * Notes: o Summary of syntax...
9984: * \array{3,lcrBC$a&b&c\\d&e&f\\etc}
9985: * o The 3,lcrBC$ part is an optional "preamble". The lcr means
9986: * what you think, i.e., "horizontal" left,center,right
9987: * justification down corresponding column. The new BC means
9988: * "vertical" baseline,center justification across corresponding
9989: * row. The leading 3 specifies the font size 0-4 to be used.
9990: * You may also specify +1,-1,+2,-2, etc, which is used as an
9991: * increment to the current font size, e.g., -1,lcr$ uses
9992: * one font size smaller than current. Without a leading
9993: * + or -, the font size is "absolute".
9994: * o The preamble can also be just lcrBC$ without a leading
9995: * size-part, or just 3$ without a trailing lcrBC-part.
9996: * The default size is whatever is current, and the
9997: * default justification is c(entered) and B(aseline).
9998: * ======================================================================= */
9999: /* --- entry point --- */
10000: subraster *rastarray ( char **expression, int size, subraster *basesp,
10001: int arg1, int arg2, int arg3 )
10002: {
10003: /* -------------------------------------------------------------------------
10004: Allocations and Declarations
10005: -------------------------------------------------------------------------- */
1.3 albertel 10006: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
10007: subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
10008: token[MAXTOKNSZ+1], *tokptr=token, /* subexpr token to rasterize */
1.1 albertel 10009: *preamble(), *preptr=token; /*process optional size,lcr preamble*/
10010: char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
1.5 ! raeburn 10011: int maxarraysz = 63; /* max #rows, cols */
1.1 albertel 10012: 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 */
10013: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10014: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10015: hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
10016: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10017: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10018: vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
10019: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10020: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10021: colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10022: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10023: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10024: rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10025: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10026: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10027: fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10028: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10029: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10030: fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10031: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10032: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10033: rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
10034: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10035: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1.5 ! raeburn 10036: vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
! 10037: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
! 10038: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1.1 albertel 10039: rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10040: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10041: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10042: static int /* --- propagate global values across arrays --- */
10043: 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 */
10044: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10045: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10046: gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10047: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10048: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10049: growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10050: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10051: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10052: gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10053: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10054: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10055: gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10056: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10057: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10058: growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10059: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10060: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10061: int rowglobal=0, colglobal=0, /* true to set global values */
10062: rowpropagate=0, colpropagate=0; /* true if propagating values */
10063: int irow,nrows=0, icol,ncols[65], /*#rows in array, #cols in each row*/
10064: maxcols=0; /* max# cols in any single row */
10065: int itoken, ntokens=0, /* index, total #tokens in array */
10066: subtoklen=0, /* strlen of {...} subtoken */
10067: istokwhite=1, /* true if token all whitespace */
10068: nnonwhite=0; /* #non-white tokens */
10069: int isescape=0,wasescape=0, /* current,prev chars escape? */
10070: ischarescaped=0, /* is current char escaped? */
10071: nescapes=0; /* #consecutive escapes */
10072: subraster *rasterize(), *toksp[1025], /* rasterize tokens */
10073: *new_subraster(), *arraysp=NULL; /* subraster for entire array */
10074: raster *arrayrp=NULL; /* raster for entire array */
10075: int delete_subraster(); /* free toksp[] workspace at eoj */
10076: int rowspace=2, colspace=4, /* blank space between rows, cols */
10077: hspace=1, vspace=1; /*space to accommodate hline,vline*/
10078: int width=0, height=0, /* width,height of array */
10079: leftcol=0, toprow=0; /*upper-left corner for cell in it*/
10080: int rastput(); /* embed tokens/cells in array */
10081: int rule_raster(); /* draw hlines and vlines in array */
10082: char *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
10083: char *texchar(), hltoken[1025]; /* extract \hline from token */
10084: int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
10085: int isnewrow=1; /* true for new row */
10086: int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
1.5 ! raeburn 10087: int evalterm(), evalue=0; /* evaluate [arg], {arg} */
! 10088: static int mydaemonlevel = 0; /* check against global daemonlevel*/
1.1 albertel 10089: /* -------------------------------------------------------------------------
10090: Macros to determine extra raster space required for vline/hline
10091: -------------------------------------------------------------------------- */
10092: #define vlinespace(icol) \
10093: ( vline[icol] == 0? 0 : /* no vline so no space needed */ \
10094: ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
10095: #define hlinespace(irow) \
10096: ( hline[irow] == 0? 0 : /* no hline so no space needed */ \
10097: ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
10098: /* -------------------------------------------------------------------------
10099: Obtain array subexpression
10100: -------------------------------------------------------------------------- */
10101: /* --- parse for array subexpression, and bump expression past it --- */
10102: subexpr[1] = *subexpr = ' '; /* set two leading blanks */
10103: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
10104: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display array */
10105: fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
10106: if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */
10107: goto end_of_job; /* nothing to do, so quit */
10108: /* -------------------------------------------------------------------------
1.5 ! raeburn 10109: reset static arrays if main re-entered as daemon (or dll)
! 10110: -------------------------------------------------------------------------- */
! 10111: if ( mydaemonlevel != daemonlevel ) { /* main re-entered */
! 10112: for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
! 10113: gjustify[icol] = gcolwidth[icol] = growheight[icol] =
! 10114: gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
! 10115: mydaemonlevel = daemonlevel; } /* update mydaemonlevel */
! 10116: /* -------------------------------------------------------------------------
1.1 albertel 10117: process optional size,lcr preamble if present
10118: -------------------------------------------------------------------------- */
10119: /* --- reset size, get lcr's, and push exprptr past preamble --- */
10120: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
10121: /* --- init with global values --- */
10122: for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
10123: justify[icol] = gjustify[icol]; /* -1,0,+1 = l,c,r */
10124: colwidth[icol] = gcolwidth[icol]; /* column width */
10125: rowheight[icol] = growheight[icol]; /* row height */
10126: fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
10127: fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
10128: rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
10129: /* --- process lcr's, etc in preamble --- */
10130: itoken = 0; /* debugging flag */
10131: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, display preamble */
10132: if ( *preptr != '\000' ) /* if we have one */
10133: fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
10134: preptr);
10135: irow = icol = 0; /* init lcr counts */
10136: while ( *preptr != '\000' ) /* check preamble text for lcr */
10137: {
10138: char prepchar = *preptr; /* current preamble character */
10139: int prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
10140: if ( irow<maxarraysz && icol<maxarraysz )
10141: switch ( /*tolower*/(prepchar) )
10142: { default: break; /* just flush unrecognized chars */
10143: case 'l': justify[icol] = (-1); /*left-justify this column*/
10144: if (colglobal) gjustify[irow] = justify[irow]; break;
10145: case 'c': justify[icol] = (0); /* center this column */
10146: if (colglobal) gjustify[irow] = justify[irow]; break;
10147: case 'r': justify[icol] = (+1); /* right-justify this col */
10148: if (colglobal) gjustify[irow] = justify[irow]; break;
10149: case '|': vline[icol] += 1; break; /* solid vline left of col */
10150: case '.': vline[icol] = (-1); break; /*dashed vline left of col */
10151: case 'b': prepchar='B'; prepcase=2; /* alias for B */
10152: case 'B': break; /* baseline-justify row */
10153: case 'v': prepchar='C'; prepcase=2; /* alias for C */
10154: case 'C': rowcenter[irow] = 1; /* vertically center row */
10155: if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
10156: case 'g': colglobal=1; prepcase=0; break; /* set global col values */
10157: case 'G': rowglobal=1; prepcase=0; break; /* set global row values */
10158: case '#': colglobal=rowglobal=1; break; } /* set global col,row vals */
10159: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10160: fprintf(msgfp," %c[%d]",prepchar,
10161: (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
10162: preptr++; /* check next char for lcr */
10163: itoken++; /* #lcr's processed (debugging only)*/
10164: /* --- check for number or +number specifying colwidth or rowheight --- */
10165: if ( prepcase != 0 ) /* only check upper,lowercase */
10166: {
10167: int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
1.3 albertel 10168: if ( ispropagate ) { /* set row or col propagation */
1.1 albertel 10169: if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
1.3 albertel 10170: else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
1.1 albertel 10171: if ( !colpropagate && prepcase == 1 )
10172: { colwidth[icol] = 0; /* reset colwidth */
10173: fixcolsize[icol] = 0; } /* reset width flag */
10174: if ( !rowpropagate && prepcase == 2 )
10175: { rowheight[irow] = 0; /* reset row height */
10176: fixrowsize[irow] = 0; } /* reset height flag */
10177: if ( ispropagate ) preptr++; /* bump past leading + */
10178: if ( isdigit(*preptr) ) /* digit follows character */
10179: { char *endptr = NULL; /* preptr set to 1st char after num*/
10180: int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
10181: char *whchars="?wh"; /* debugging width/height labels */
10182: preptr = endptr; /* skip over all digits */
10183: if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
10184: int index; /* icol,irow...maxarraysz index */
10185: if ( prepcase == 1 ) /* lowercase signifies colwidth */
10186: for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
10187: colwidth[index] = size; /* set colwidth to fixed size */
10188: fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
10189: justify[index] = justify[icol]; /* and propagate justification */
10190: if ( colglobal ) { /* set global values */
10191: gcolwidth[index] = colwidth[index]; /* set global col width */
10192: gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
10193: gjustify[index] = justify[icol]; } /* set global col justify */
10194: if ( !ispropagate ) break; } /* don't propagate */
10195: else /* uppercase signifies rowheight */
10196: for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
10197: rowheight[index] = size; /* set rowheight to size */
10198: fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
10199: rowcenter[index] = rowcenter[irow]; /* and propagate row center */
10200: if ( rowglobal ) { /* set global values */
10201: growheight[index] = rowheight[index]; /* set global row height */
10202: gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
10203: growcenter[index] = rowcenter[irow]; } /*set global row center*/
10204: if ( !ispropagate ) break; } /* don't propagate */
10205: } /* --- end-of-if(size>=3&&size<=500) --- */
10206: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10207: fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
10208: (prepcase==1?colwidth[icol]:rowheight[irow]),
10209: (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
10210: } /* --- end-of-if(isdigit()) --- */
10211: } /* --- end-of-if(prepcase!=0) --- */
10212: if ( prepcase == 1 ) icol++; /* bump col if lowercase lcr */
10213: else if ( prepcase == 2 ) irow++; /* bump row if uppercase BC */
10214: } /* --- end-of-while(*preptr!='\000') --- */
10215: if ( msglevel>=29 && msgfp!=NULL ) /* debugging, emit final newline */
10216: if ( itoken > 0 ) /* if we have preamble */
10217: fprintf(msgfp,"\n");
10218: /* -------------------------------------------------------------------------
10219: tokenize and rasterize components a & b \\ c & d \\ etc of subexpr
10220: -------------------------------------------------------------------------- */
10221: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
1.5 ! raeburn 10222: nrows = 0; /* start with top row */
1.1 albertel 10223: ncols[nrows] = 0; /* no tokens/cols in top row yet */
10224: while ( 1 ) /* scan chars till end */
10225: {
10226: /* --- local control flags --- */
10227: int iseox = (*exprptr == '\000'), /* null signals end-of-expression */
10228: iseor = iseox, /* \\ or eox signals end-of-row */
10229: iseoc = iseor; /* & or eor signals end-of-col */
10230: /* --- check for escapes --- */
10231: isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
10232: wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
10233: nescapes = (wasescape?nescapes+1:0); /* # preceding consecutive escapes */
10234: ischarescaped = (nescapes%2==0?0:1); /* is current char escaped? */
10235: /* -----------------------------------------------------------------------
10236: check for {...} subexpression starting from where we are now
10237: ------------------------------------------------------------------------ */
10238: if ( *exprptr == '{' /* start of {...} subexpression */
10239: && !ischarescaped ) /* if not escaped \{ */
10240: {
10241: subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
10242: subtoklen = strlen(subtok); /* #chars in {...} */
10243: memcpy(tokptr,exprptr,subtoklen); /* copy {...} to accumulated token */
10244: tokptr += subtoklen; /* bump tokptr to end of token */
10245: exprptr += subtoklen; /* and bump exprptr past {...} */
10246: istokwhite = 0; /* signal non-empty token */
10247: continue; /* continue with char after {...} */
10248: } /* --- end-of-if(*exprptr=='{') --- */
10249: /* -----------------------------------------------------------------------
10250: check for end-of-row(\\) and/or end-of-col(&)
10251: ------------------------------------------------------------------------ */
10252: /* --- check for (escaped) end-of-row delimiter --- */
10253: if ( isescape && !ischarescaped ) /* current char is escaped */
10254: if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
10255: || *(exprptr+1) == '\000' ) /* or a pathological null */
10256: { iseor = 1; /* so set end-of-row flag */
10257: wasescape=isescape=nescapes = 0; } /* reset flags for new row */
10258: /* --- check for end-of-col delimiter --- */
10259: if (iseor /* end-of-row signals end-of-col */
10260: || (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
10261: iseoc = 1; /* so set end-of-col flag */
10262: /* -----------------------------------------------------------------------
10263: rasterize completed token
10264: ------------------------------------------------------------------------ */
10265: if ( iseoc ) /* we have a completed token */
10266: {
10267: *tokptr = '\000'; /* first, null-terminate token */
1.5 ! raeburn 10268: /* --- check first token in row for [len] and/or \hline or \hdash --- */
1.1 albertel 10269: ishonly = 0; /*init for token not only an \hline*/
10270: if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/
10271: {
10272: tokptr=token; skipwhite(tokptr); /* skip whitespace after // */
1.5 ! raeburn 10273: /* --- first check for optional [len] --- */
! 10274: if ( *tokptr == '[' ) { /* have [len] if leading char is [ */
! 10275: /* ---parse [len] and bump tokptr past it, interpret as double--- */
! 10276: char lenexpr[128]; int len; /* chars between [...] as int */
! 10277: tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
! 10278: if ( *lenexpr != '\000' ) { /* got [len] expression */
! 10279: evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
! 10280: len = iround(unitlength*((double)evalue)); /* len in pixels */
! 10281: if ( len>=(-63) && len<=255 ) { /* sanity check */
! 10282: vrowspace[nrows] = len; /* extra vspace before this row */
! 10283: strsqueezep(token,tokptr); /* flush [len] from token */
! 10284: tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
! 10285: } /* --- end-of-if(*tokptr=='[') --- */
! 10286: /* --- now check for \hline or \hdash --- */
1.1 albertel 10287: tokptr = texchar(tokptr,hltoken); /* extract first char from token */
10288: hltoklen = strlen(hltoken); /* length of first char */
1.3 albertel 10289: if ( hltoklen >= minhltoklen ) { /*token must be at least \hl or \hd*/
1.1 albertel 10290: if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
10291: hline[nrows] += 1; /* bump \hline count for row */
10292: else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
1.3 albertel 10293: hline[nrows] = (-1); } /* set \hdash flag for row */
1.1 albertel 10294: if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
10295: { skipwhite(tokptr); /* flush whitespace after \hline */
10296: if ( *tokptr == '\000' /* end-of-expression after \hline */
10297: || isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
1.2 albertel 10298: { istokwhite = 1; /* so token contains \hline only */
10299: if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
1.1 albertel 10300: else /* token contains more than \hline */
1.5 ! raeburn 10301: {strsqueezep(token,tokptr);} } /* so flush \hline */
1.1 albertel 10302: } /* --- end-of-if(ncols[nrows]==0) --- */
10303: /* --- rasterize completed token --- */
10304: toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
10305: rasterize(token,size)); /* rasterize non-empty token */
10306: if ( toksp[ntokens] != NULL ) /* have a rasterized token */
10307: nnonwhite++; /* bump rasterized token count */
10308: /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
10309: if ( toksp[ntokens] != NULL ) /* we have a rasterized token */
10310: {
10311: /* --- update max token "height" in current row, and baseline --- */
10312: int twidth = ((toksp[ntokens])->image)->width, /* width of token */
10313: theight = ((toksp[ntokens])->image)->height, /* height of token */
10314: tbaseln = (toksp[ntokens])->baseline, /* baseline of token */
10315: rheight = rowheight[nrows], /* current max height for row */
10316: rbaseln = rowbaseln[nrows]; /* current baseline for max height */
10317: if ( 0 || fixrowsize[nrows]==0 ) /* rowheight not fixed */
10318: rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
10319: max2(rbaseln+1,tbaseln+1) /* max height above baseline */
10320: + max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
10321: rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
10322: /* --- update max token width in current column --- */
10323: icol = ncols[nrows]; /* current column index */
10324: if ( 0 || fixcolsize[icol]==0 ) /* colwidth not fixed */
10325: colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
10326: } /* --- end-of-if(toksp[]!=NULL) --- */
10327: /* --- bump counters --- */
10328: if ( !ishonly ) /* don't count only an \hline */
1.5 ! raeburn 10329: if ( ncols[nrows] < maxarraysz ) /* don't overflow arrays */
! 10330: { ntokens++; /* bump total token count */
! 10331: ncols[nrows] += 1; } /* and bump #cols in current row */
1.1 albertel 10332: /* --- get ready for next token --- */
10333: tokptr = token; /* reset ptr for next token */
10334: istokwhite = 1; /* next token starts all white */
10335: } /* --- end-of-if(iseoc) --- */
10336: /* -----------------------------------------------------------------------
10337: bump row as necessary
10338: ------------------------------------------------------------------------ */
10339: if ( iseor ) /* we have a completed row */
10340: {
10341: maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
10342: if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
1.5 ! raeburn 10343: if ( nrows < maxarraysz ) /* don't overflow arrays */
! 10344: nrows++; /* bump row count */
1.1 albertel 10345: ncols[nrows] = 0; /* no cols in this row yet */
10346: if ( !iseox ) /* don't have a null yet */
10347: { exprptr++; /* bump past extra \ in \\ delim */
10348: iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
10349: isnewrow = 1; /* signal start of new row */
10350: } /* --- end-of-if(iseor) --- */
10351: else
10352: isnewrow = 0; /* no longer first col of new row */
10353: /* -----------------------------------------------------------------------
10354: quit when done, or accumulate char in token and proceed to next char
10355: ------------------------------------------------------------------------ */
10356: /* --- quit when done --- */
10357: if ( iseox ) break; /* null terminator signalled done */
10358: /* --- accumulate chars in token --- */
10359: if ( !iseoc ) /* don't accumulate delimiters */
10360: { *tokptr++ = *exprptr; /* accumulate non-delim char */
10361: if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
10362: istokwhite = 0; } /* so reset flag to rasterize it */
10363: /* --- ready for next char --- */
10364: exprptr++; /* bump ptr */
10365: } /* --- end-of-while(*exprptr!='\000') --- */
10366: /* --- make sure we got something to do --- */
10367: if ( nnonwhite < 1 ) /* completely empty array */
10368: goto end_of_job; /* NULL back to caller */
10369: /* -------------------------------------------------------------------------
10370: determine dimensions of array raster and allocate it
10371: -------------------------------------------------------------------------- */
10372: /* --- adjust colspace --- */
10373: colspace = 2 + 2*size; /* temp kludge */
10374: /* --- reset propagated sizes at boundaries of array --- */
10375: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
10376: /* --- determine width of array raster --- */
10377: width = colspace*(maxcols-1); /* empty space between cols */
10378: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10379: fprintf(msgfp,"rastarray> %d cols, widths: ",maxcols);
10380: for ( icol=0; icol<=maxcols; icol++ ) /* and for each col */
10381: { width += colwidth[icol]; /*width of this col (0 for maxcols)*/
10382: width += vlinespace(icol); /*plus space for vline, if present*/
10383: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10384: fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
10385: /* --- determine height of array raster --- */
10386: height = rowspace*(nrows-1); /* empty space between rows */
10387: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10388: fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
10389: for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
10390: { height += rowheight[irow]; /*height of this row (0 for nrows)*/
1.5 ! raeburn 10391: height += vrowspace[irow]; /*plus extra //[len], if present*/
1.1 albertel 10392: height += hlinespace(irow); /*plus space for hline, if present*/
10393: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10394: fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
10395: /* --- allocate subraster and raster for array --- */
10396: if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10397: fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
10398: width,colspace, height,rowspace);
10399: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
10400: == NULL ) goto end_of_job; /* quit if failed */
10401: /* --- initialize subraster parameters --- */
10402: arraysp->type = IMAGERASTER; /* image */
10403: arraysp->symdef = NULL; /* not applicable for image */
10404: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
10405: arraysp->size = size; /* size (probably unneeded) */
10406: arrayrp = arraysp->image; /* raster embedded in subraster */
10407: /* -------------------------------------------------------------------------
10408: embed tokens/cells in array
10409: -------------------------------------------------------------------------- */
10410: itoken = 0; /* start with first token */
10411: toprow = 0; /* start at top row of array */
10412: for ( irow=0; irow<=nrows; irow++ ) /*tokens were accumulated row-wise*/
10413: {
10414: /* --- initialization for row --- */
10415: int baseline = rowbaseln[irow]; /* baseline for this row */
10416: if ( hline[irow] != 0 ) /* need hline above this row */
10417: { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
10418: if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
10419: rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
10420: if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
1.5 ! raeburn 10421: toprow += vrowspace[irow]; /* extra //[len] space above irow */
! 10422: if ( toprow < 0 ) toprow = 0; /* check for large negative [-len] */
1.1 albertel 10423: toprow += hlinespace(irow); /* space for hline above irow */
10424: leftcol = 0; /* start at leftmost column */
10425: for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
10426: {
10427: subraster *tsp = toksp[itoken]; /* token that belongs in this cell */
1.2 albertel 10428: /* --- first adjust leftcol for vline to left of icol, if present ---- */
10429: leftcol += vlinespace(icol); /* space for vline to left of col */
10430: /* --- now rasterize cell ---- */
1.1 albertel 10431: if ( tsp != NULL ) /* have a rasterized cell token */
10432: {
10433: /* --- local parameters --- */
10434: int cwidth = colwidth[icol], /* total column width */
10435: twidth = (tsp->image)->width, /* token width */
10436: theight= (tsp->image)->height, /* token height */
10437: tokencol = 0, /*H offset (init for left justify)*/
10438: tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
10439: /* --- adjust leftcol for vline to left of icol, if present ---- */
1.2 albertel 10440: /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
1.1 albertel 10441: /* --- reset justification (if not left-justified) --- */
10442: if ( justify[icol] == 0 ) /* but user wants it centered */
10443: tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
10444: else if ( justify[icol] == 1 ) /* or user wants right-justify */
10445: tokencol = cwidth-twidth; /* so put entire margin at left */
10446: /* --- reset vertical centering (if not baseline-aligned) --- */
10447: if ( rowcenter[irow] ) /* center cells in row vertically */
10448: tokenrow = (rowheight[irow]-theight)/2; /* center row */
10449: /* --- embed token raster at appropriate place in array raster --- */
10450: rastput(arrayrp,tsp->image, /* overlay cell token in array */
10451: toprow+ tokenrow, /*with aligned baseline or centered*/
10452: leftcol+tokencol, 1); /* and justified as requested */
10453: } /* --- end-of-if(tsp!=NULL) --- */
10454: itoken++; /* bump index for next cell */
10455: leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
10456: /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
10457: } /* --- end-of-for(icol) --- */
10458: toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
10459: } /* --- end-of-for(irow) --- */
10460: /* -------------------------------------------------------------------------
10461: draw vlines as necessary
10462: -------------------------------------------------------------------------- */
10463: leftcol = 0; /* start at leftmost column */
10464: for ( icol=0; icol<=maxcols; icol++ ) /* check each col for a vline */
10465: {
10466: if ( vline[icol] != 0 ) /* need vline to left of this col */
10467: { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
10468: if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
10469: rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
10470: leftcol += vlinespace(icol); /* space for vline to left of col */
10471: if ( icol < maxcols ) /* don't address past end of array */
10472: leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
10473: } /* --- end-of-for(icol) --- */
10474: /* -------------------------------------------------------------------------
10475: free workspace and return final result to caller
10476: -------------------------------------------------------------------------- */
10477: end_of_job:
10478: /* --- free workspace --- */
10479: if ( ntokens > 0 ) /* if we have workspace to free */
10480: while ( --ntokens >= 0 ) /* free each token subraster */
10481: if ( toksp[ntokens] != NULL ) /* if we rasterized this cell */
10482: delete_subraster(toksp[ntokens]); /* then free it */
10483: /* --- return final result to caller --- */
10484: return ( arraysp );
10485: } /* --- end-of-function rastarray() --- */
10486:
10487:
10488: /* ==========================================================================
10489: * Function: rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
10490: * Purpose: \picture handler, returns subraster corresponding to picture
10491: * expression (immediately following \picture) at font size
10492: * --------------------------------------------------------------------------
10493: * Arguments: expression (I/O) char ** to first char of null-terminated
10494: * string immediately following \picture to be
10495: * rasterized, and returning ptr immediately
10496: * following last character processed.
1.5 ! raeburn 10497: * size (I) int containing 0-7 default font size
1.1 albertel 10498: * basesp (I) subraster * to character (or subexpression)
10499: * immediately preceding \picture
10500: * (unused, but passed for consistency)
10501: * arg1 (I) int unused
10502: * arg2 (I) int unused
10503: * arg3 (I) int unused
10504: * --------------------------------------------------------------------------
10505: * Returns: ( subraster * ) ptr to subraster corresponding to picture
10506: * expression, or NULL for any parsing error
10507: * --------------------------------------------------------------------------
10508: * Notes: o Summary of syntax...
10509: * \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
10510: * o
10511: * ======================================================================= */
10512: /* --- entry point --- */
10513: subraster *rastpicture ( char **expression, int size, subraster *basesp,
10514: int arg1, int arg2, int arg3 )
10515: {
10516: /* -------------------------------------------------------------------------
10517: Allocations and Declarations
10518: -------------------------------------------------------------------------- */
10519: char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
10520: putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
1.5 ! raeburn 10521: pream[96], *preptr, /* optional put preamble */
1.1 albertel 10522: picelem[1025]; /* picture element following put */
10523: subraster *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
10524: *new_subraster(), *picturesp=NULL, /* subraster for entire picture */
10525: *oldworkingbox = workingbox; /* save working box on entry */
10526: raster *picturerp=NULL; /* raster for entire picture */
10527: int delete_subraster(); /* free picelemsp[] workspace */
10528: int pixsz = 1; /* pixels are one bit each */
1.5 ! raeburn 10529: double x=0.0,y=0.0, /* x,y-coords for put,multiput*/
1.1 albertel 10530: xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
10531: int width=0, height=0, /* #pixels width,height of picture */
10532: ewidth=0, eheight=0, /* pic element width,height */
10533: ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
10534: num=1, inum; /* number reps, index of element */
1.5 ! raeburn 10535: int evalterm(); /* evaluate [arg] and {arg}'s */
1.1 albertel 10536: int iscenter=0; /* center or lowerleft put position*/
10537: int *oldworkingparam = workingparam, /* save working param on entry */
10538: origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
10539: int rastput(); /* embed elements in picture */
10540: int type_raster(); /* display debugging output */
10541: /* -------------------------------------------------------------------------
10542: First obtain (width,height) arguments immediately following \picture command
10543: -------------------------------------------------------------------------- */
10544: /* --- parse for (width,height) arguments, and bump expression past it --- */
10545: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
10546: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
10547: /* --- now interpret width,height returned in putexpr --- */
10548: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
10549: *putptr = '\000'; /* found it, so replace ',' by '\0'*/
1.5 ! raeburn 10550: width=height = eround(putexpr); /*width pixels*/
1.1 albertel 10551: if ( putptr != NULL ) /* 2nd arg, if present, is height */
1.5 ! raeburn 10552: height = eround(putptr+1); /*in pixels*/
1.1 albertel 10553: /* -------------------------------------------------------------------------
10554: Then obtain entire picture {...} subexpression following (width,height)
10555: -------------------------------------------------------------------------- */
10556: /* --- parse for picture subexpression, and bump expression past it --- */
10557: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
10558: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
10559: /* -------------------------------------------------------------------------
10560: allocate subraster and raster for complete picture
10561: -------------------------------------------------------------------------- */
10562: /* --- sanity check on width,height args --- */
10563: if ( width < 2 || width > 600
10564: || height < 2 || height > 600 ) goto end_of_job;
10565: /* --- allocate and initialize subraster for constructed picture --- */
10566: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
10567: == NULL ) goto end_of_job; /* quit if failed */
10568: workingbox = picturesp; /* set workingbox to our picture */
10569: /* --- initialize picture subraster parameters --- */
10570: picturesp->type = IMAGERASTER; /* image */
10571: picturesp->symdef = NULL; /* not applicable for image */
10572: picturesp->baseline = height/2 + 2; /* is a little above center good? */
10573: picturesp->size = size; /* size (probably unneeded) */
10574: picturerp = picturesp->image; /* raster embedded in subraster */
10575: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10576: fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
10577: /* -------------------------------------------------------------------------
10578: parse out each picture element, rasterize it, and place it in picture
10579: -------------------------------------------------------------------------- */
10580: while ( *picptr != '\000' ) /* until we run out of pic_elems */
10581: {
10582: /* -----------------------------------------------------------------------
10583: first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
10584: ------------------------------------------------------------------------ */
10585: /* --- init default values in case not explicitly supplied in args --- */
10586: x=y=0.0; xinc=yinc=0.0; num=1; /* init default values */
10587: iscenter = origin = 0; /* center, origin */
10588: /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
10589: while ( *picptr != '\000' ) /* skip invalid chars preceding ( */
10590: if ( *picptr == '(' ) break; /* found opening ( */
10591: else picptr++; /* else skip invalid char */
10592: picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
10593: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
10594: /* --- first look for $-terminated or for any non-digit preamble --- */
10595: *pream = '\000'; /* init preamble as empty string */
10596: if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
10597: { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
1.5 ! raeburn 10598: strninit(pream,putexpr,92); } /* copy leading preamble from put */
1.1 albertel 10599: else /* look for any non-digit preamble */
1.5 ! raeburn 10600: { int npream = 0; /* #chars in preamble */
! 10601: for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ )
1.1 albertel 10602: if ( *putptr == '\000' /* end-of-putdata signalled */
1.5 ! raeburn 10603: || !isalpha((int)(*putptr)) /* or found non-alpha char */
! 10604: || npream > 92 ) break; /* or preamble too long */
1.1 albertel 10605: else *preptr++ = *putptr; /* copy alpha char to preamble */
10606: *preptr = '\000'; } /* null-terminate preamble */
10607: /* --- interpret preamble --- */
10608: for ( preptr=pream; ; preptr++ ) /* examine each preamble char */
10609: if ( *preptr == '\000' ) break; /* end-of-preamble signalled */
10610: else switch ( tolower(*preptr) ) /* check lowercase preamble char */
10611: {
10612: default: break; /* unrecognized flag */
10613: case 'c': iscenter=1; break; /* center pic_elem at x,y coords */
10614: } /* --- end-of-switch --- */
10615: /* --- interpret x,y;xinc,yinc;num following preamble --- */
10616: if ( *putptr != '\000' ) /*check for put data after preamble*/
10617: {
10618: /* --- first squeeze preamble out of put expression --- */
1.5 ! raeburn 10619: if ( *pream != '\000' ) /* have preamble */
! 10620: {strsqueezep(putexpr,putptr);} /* squeeze it out */
1.1 albertel 10621: /* --- interpret x,y --- */
10622: if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
10623: *multptr = '\000'; /* replace semicolon by '\0' */
10624: if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
10625: *putptr = '\000'; /* replace comma by '\0' */
10626: if ( *putexpr != '\000' ) /* leading , may be placeholder */
1.5 ! raeburn 10627: x = (double)(eround(putexpr)); /* x coord in pixels*/
1.1 albertel 10628: if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
1.5 ! raeburn 10629: y = (double)(eround(putptr+1)); /* in pixels */
1.1 albertel 10630: /* --- interpret xinc,yinc,num if we have a multiput --- */
10631: if ( multptr != NULL ) /* found ';' signalling multiput */
10632: {
10633: if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
10634: *preptr = '\000'; /* replace ';' by '\0' */
10635: if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
10636: *putptr = '\000'; /* replace ',' by '\0' */
10637: if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
1.5 ! raeburn 10638: xinc = (double)(eround(multptr+1)); /* xinc in pixels */
1.1 albertel 10639: if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
1.5 ! raeburn 10640: yinc = (double)(eround(putptr+1)); /* in user pixels */
1.1 albertel 10641: num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
10642: } /* --- end-of-if(multptr!=NULL) --- */
10643: } /* --- end-of-if(*preptr!='\000') --- */
10644: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10645: fprintf(msgfp,
10646: "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
10647: pream,x,y,xinc,yinc,num);
10648: /* -----------------------------------------------------------------------
10649: now obtain {...} picture element following [multi]put, and rasterize it
10650: ------------------------------------------------------------------------ */
10651: /* --- parse for {...} picture element and bump picptr past it --- */
10652: picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
10653: if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
10654: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10655: fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
10656: /* --- rasterize picture element --- */
10657: origin = 0; /* init origin as working param */
10658: workingparam = &origin; /* and point working param to it */
10659: picelemsp = rasterize(picelem,size); /* rasterize picture element */
10660: if ( picelemsp == NULL ) continue; /* failed to rasterize, skip elem */
10661: ewidth = (picelemsp->image)->width; /* width of element, in pixels */
10662: eheight = (picelemsp->image)->height; /* height of element, in pixels */
10663: if ( origin == 55 ) iscenter = 1; /* origin set to (.5,.5) for center*/
10664: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10665: { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
10666: ewidth,eheight,origin,num);
10667: if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
10668: /* -----------------------------------------------------------------------
10669: embed element in picture (once, or multiple times if requested)
10670: ------------------------------------------------------------------------ */
10671: for ( inum=0; inum<num; inum++ ) /* once, or for num repetitions */
10672: {
10673: /* --- set x,y-coords for this iteration --- */
10674: ix = iround(x); iy = iround(y); /* round x,y to nearest integer */
10675: if ( iscenter ) /* place center of element at x,y */
10676: { xpos = ix - ewidth/2; /* x picture coord to center elem */
10677: ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
10678: else /* default places lower-left at x,y*/
10679: { xpos = ix; /* set x pixel coord for left */
10680: if ( origin==10 || origin==11 ) /* x,yinc's are -+ or -- */
10681: xpos = ix - ewidth; /* so set for right instead */
10682: ypos = height - iy - eheight; /* set y pixel coord for lower */
10683: if ( origin==1 || origin==11 ) /* x,yinc's are +- or -- */
10684: ypos = height - iy; } /* so set for upper instead */
10685: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10686: fprintf(msgfp,
10687: "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
10688: inum,x,y,ix,iy,xpos,ypos);
10689: /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
10690: if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
10691: /* --- apply increment --- */
10692: if ( xinc==0. && yinc==0. ) break; /* quit if both increments zero */
10693: x += xinc; y += yinc; /* increment coords for next iter */
10694: } /* --- end-of-for(inum) --- */
10695: /* --- free picture element subraster after embedding it in picture --- */
10696: delete_subraster(picelemsp); /* done with subraster, so free it */
10697: } /* --- end-of-while(*picptr!=0) --- */
10698: /* -------------------------------------------------------------------------
10699: return picture constructed from pic_elements to caller
10700: -------------------------------------------------------------------------- */
10701: end_of_job:
10702: workingbox = oldworkingbox; /* restore original working box */
10703: workingparam = oldworkingparam; /* restore original working param */
10704: return ( picturesp ); /* return our picture to caller */
10705: } /* --- end-of-function rastpicture() --- */
10706:
10707:
10708: /* ==========================================================================
10709: * Function: rastline ( expression, size, basesp, arg1, arg2, arg3 )
10710: * Purpose: \line handler, returns subraster corresponding to line
10711: * parameters (xinc,yinc){xlen}
10712: * --------------------------------------------------------------------------
10713: * Arguments: expression (I/O) char ** to first char of null-terminated
10714: * string immediately following \line to be
10715: * rasterized, and returning ptr immediately
10716: * following last character processed.
1.3 albertel 10717: * size (I) int containing 0-7 default font size
1.1 albertel 10718: * basesp (I) subraster * to character (or subexpression)
10719: * immediately preceding \line
10720: * (unused, but passed for consistency)
10721: * arg1 (I) int unused
10722: * arg2 (I) int unused
10723: * arg3 (I) int unused
10724: * --------------------------------------------------------------------------
10725: * Returns: ( subraster * ) ptr to subraster corresponding to line
10726: * requested, or NULL for any parsing error
10727: * --------------------------------------------------------------------------
10728: * Notes: o Summary of syntax...
10729: * \line(xinc,yinc){xlen}
10730: * o if {xlen} not given, then it's assumed xlen = |xinc|
10731: * ======================================================================= */
10732: /* --- entry point --- */
10733: subraster *rastline ( char **expression, int size, subraster *basesp,
10734: int arg1, int arg2, int arg3 )
10735: {
10736: /* -------------------------------------------------------------------------
10737: Allocations and Declarations
10738: -------------------------------------------------------------------------- */
10739: char *texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
10740: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
10741: /*char *origexpression = *expression;*/ /*original expression after \line*/
10742: int pixsz = 1; /* pixels are one bit each */
1.2 albertel 10743: int thickness = 1; /* line thickness */
1.5 ! raeburn 10744: double xinc=0.0, yinc=0.0, /* x,y-increments for line, */
1.1 albertel 10745: xlen=0.0, ylen=0.0; /* x,y lengths for line */
1.2 albertel 10746: int width=0, height=0, /* #pixels width,height of line */
10747: rwidth=0, rheight=0; /*alloc width,height plus thickness*/
1.5 ! raeburn 10748: int evalterm(); /* evaluate [arg] and {arg}'s */
1.1 albertel 10749: int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
10750: origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
10751: int line_raster(); /* draw line in linesp->image */
10752: /* -------------------------------------------------------------------------
10753: obtain (xinc,yinc) arguments immediately following \line command
10754: -------------------------------------------------------------------------- */
10755: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
10756: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
10757: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
1.2 albertel 10758: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
10759: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
10760: { *xptr = '\000'; /* terminate linexpr at ; */
1.5 ! raeburn 10761: thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
1.1 albertel 10762: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
10763: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
10764: if ( *linexpr != '\000' ) /* check against missing 1st arg */
1.5 ! raeburn 10765: xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
1.1 albertel 10766: if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
1.5 ! raeburn 10767: yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
1.1 albertel 10768: /* -------------------------------------------------------------------------
10769: obtain optional {xlen} following (xinc,yinc), and calculate ylen
10770: -------------------------------------------------------------------------- */
10771: /* --- check if {xlen} given --- */
10772: if ( *(*expression) == '{' ) /*have {xlen} if leading char is { */
10773: {
10774: /* --- parse {xlen} and bump expression past it, interpret as double --- */
10775: *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
10776: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
1.5 ! raeburn 10777: xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
1.1 albertel 10778: /* --- set other values accordingly --- */
10779: if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
10780: if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
10781: else xlen = 0.0; /* can't have xlen if xinc=0 */
10782: } /* --- end-of-if(*(*expression)=='{') --- */
10783: /* -------------------------------------------------------------------------
10784: calculate width,height, etc, based on xlen,ylen, etc
10785: -------------------------------------------------------------------------- */
10786: /* --- force lengths positive --- */
10787: xlen = absval(xlen); /* force xlen positive */
10788: ylen = absval(ylen); /* force ylen positive */
10789: /* --- calculate corresponding lengths in pixels --- */
1.2 albertel 10790: width = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
10791: height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
10792: rwidth = width + (ylen<0.001?0:max2(0,thickness-1));
10793: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
1.1 albertel 10794: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
10795: if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
10796: if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
10797: origin = isright*10 + istop; /* interpret 0=(0,0), 11=(1,1), etc*/
10798: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
10799: fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
10800: width,height,origin,xinc,yinc);
10801: /* -------------------------------------------------------------------------
1.3 albertel 10802: allocate subraster and raster for line
1.1 albertel 10803: -------------------------------------------------------------------------- */
1.2 albertel 10804: /* --- sanity check on width,height,thickness args --- */
1.1 albertel 10805: if ( width < 1 || width > 600
1.2 albertel 10806: || height < 1 || height > 600
10807: || thickness<1||thickness>25 ) goto end_of_job;
1.1 albertel 10808: /* --- allocate and initialize subraster for constructed line --- */
1.2 albertel 10809: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
1.1 albertel 10810: == NULL ) goto end_of_job; /* quit if failed */
10811: /* --- initialize line subraster parameters --- */
10812: linesp->type = IMAGERASTER; /* image */
10813: linesp->symdef = NULL; /* not applicable for image */
1.2 albertel 10814: linesp->baseline = height/2 + 2 /* is a little above center good? */
10815: + (rheight-height)/2; /* account for line thickness too */
1.1 albertel 10816: linesp->size = size; /* size (probably unneeded) */
10817: /* -------------------------------------------------------------------------
10818: draw the line
10819: -------------------------------------------------------------------------- */
10820: line_raster ( linesp->image, /* embedded raster image */
10821: (istop? 0 : height-1), /* row0, from bottom or top */
10822: (isright? width-1 : 0), /* col0, from left or right */
10823: (istop? height-1 : 0), /* row1, to top or bottom */
10824: (isright? 0 : width-1), /* col1, to right or left */
1.2 albertel 10825: thickness ); /* line thickness (usually 1 pixel)*/
1.1 albertel 10826: /* -------------------------------------------------------------------------
10827: return constructed line to caller
10828: -------------------------------------------------------------------------- */
10829: end_of_job:
10830: if ( workingparam != NULL ) /* caller wants origin */
10831: *workingparam = origin; /* return origin corner to caller */
10832: return ( linesp ); /* return line to caller */
10833: } /* --- end-of-function rastline() --- */
10834:
10835:
10836: /* ==========================================================================
1.3 albertel 10837: * Function: rastrule ( expression, size, basesp, arg1, arg2, arg3 )
10838: * Purpose: \rule handler, returns subraster corresponding to rule
10839: * parameters [lift]{width}{height}
10840: * --------------------------------------------------------------------------
10841: * Arguments: expression (I/O) char ** to first char of null-terminated
10842: * string immediately following \rule to be
10843: * rasterized, and returning ptr immediately
10844: * following last character processed.
10845: * size (I) int containing 0-7 default font size
10846: * basesp (I) subraster * to character (or subexpression)
10847: * immediately preceding \rule
10848: * (unused, but passed for consistency)
10849: * arg1 (I) int unused
10850: * arg2 (I) int unused
10851: * arg3 (I) int unused
10852: * --------------------------------------------------------------------------
10853: * Returns: ( subraster * ) ptr to subraster corresponding to rule
10854: * requested, or NULL for any parsing error
10855: * --------------------------------------------------------------------------
10856: * Notes: o Summary of syntax...
10857: * \rule[lift]{width}{height}
10858: * o if [lift] not given, then bottom of rule on baseline
10859: * o if width=0 then you get an invisible strut 1 (one) pixel wide
10860: * ======================================================================= */
10861: /* --- entry point --- */
10862: subraster *rastrule ( char **expression, int size, subraster *basesp,
10863: int arg1, int arg2, int arg3 )
10864: {
10865: /* -------------------------------------------------------------------------
10866: Allocations and Declarations
10867: -------------------------------------------------------------------------- */
10868: char *texsubexpr(), rulexpr[257]; /* rule[lift]{wdth}{hgt} */
10869: subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
10870: int pixsz = 1; /* pixels are one bit each */
10871: int lift=0, width=0, height=0; /* default rule parameters */
1.5 ! raeburn 10872: double dval; /* convert ascii params to doubles */
1.3 albertel 10873: int rwidth=0, rheight=0; /* alloc width, height plus lift */
10874: int rule_raster(); /* draw rule in rulesp->image */
1.5 ! raeburn 10875: int evalterm(); /* evaluate args */
1.3 albertel 10876: /* -------------------------------------------------------------------------
10877: Obtain lift,width,height
10878: -------------------------------------------------------------------------- */
10879: /* --- check for optional lift arg --- */
10880: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
10881: { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
1.5 ! raeburn 10882: dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
1.3 albertel 10883: if ( dval <= 99 && dval >= (-99) ) /* sanity check */
10884: lift = iround(unitlength*dval); } /* scale by unitlength and round */
10885: /* --- parse for width --- */
10886: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
10887: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
1.5 ! raeburn 10888: dval = evalterm(mimestore,rulexpr); /* convert {width} to int */
1.3 albertel 10889: if ( dval <= 500 && dval >= 0 ) /* sanity check */
10890: width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
10891: /* --- parse for height --- */
10892: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
10893: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
1.5 ! raeburn 10894: dval = evalterm(mimestore,rulexpr); /* convert {height} to int */
1.3 albertel 10895: if ( dval <= 500 && dval > 0 ) /* sanity check */
10896: height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
10897: /* --- raster width,height in pixels --- */
10898: rwidth = max2(1,width); /* raster must be at least 1 pixel*/
10899: rheight = height + (lift>=0?lift: /* raster height plus lift */
10900: (-lift<height?0:-lift-height+1)); /* may need empty space above rule */
10901: /* -------------------------------------------------------------------------
10902: allocate subraster and raster for rule
10903: -------------------------------------------------------------------------- */
10904: /* --- sanity check on width,height,thickness args --- */
10905: if ( rwidth < 1 || rwidth > 600
10906: || rheight < 1 || rheight > 600 ) goto end_of_job;
10907: /* --- allocate and initialize subraster for constructed rule --- */
10908: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
10909: == NULL ) goto end_of_job; /* quit if failed */
10910: /* --- initialize line subraster parameters --- */
10911: rulesp->type = IMAGERASTER; /* image */
10912: rulesp->symdef = NULL; /* not applicable for image */
10913: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
10914: rulesp->size = size; /* size (probably unneeded) */
10915: /* -------------------------------------------------------------------------
10916: draw the rule
10917: -------------------------------------------------------------------------- */
10918: rule_raster ( rulesp->image, /* embedded raster image */
10919: (-lift<height?0:rheight-height), /* topmost row for top-left corner*/
10920: 0, /* leftmost col for top-left corner*/
10921: width, /* rule width */
10922: height, /* rule height */
10923: ( width>0? 0:4 ) ); /* rule type */
10924: /* -------------------------------------------------------------------------
10925: return constructed rule to caller
10926: -------------------------------------------------------------------------- */
10927: end_of_job:
10928: return ( rulesp ); /* return rule to caller */
10929: } /* --- end-of-function rastrule() --- */
10930:
10931:
10932: /* ==========================================================================
1.1 albertel 10933: * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
10934: * Purpose: \circle handler, returns subraster corresponding to ellipse
10935: * parameters (xdiam[,ydiam])
10936: * --------------------------------------------------------------------------
10937: * Arguments: expression (I/O) char ** to first char of null-terminated
10938: * string immediately following \circle to be
10939: * rasterized, and returning ptr immediately
10940: * following last character processed.
1.5 ! raeburn 10941: * size (I) int containing 0-7 default font size
1.1 albertel 10942: * basesp (I) subraster * to character (or subexpression)
10943: * immediately preceding \circle
10944: * (unused, but passed for consistency)
10945: * arg1 (I) int unused
10946: * arg2 (I) int unused
10947: * arg3 (I) int unused
10948: * --------------------------------------------------------------------------
10949: * Returns: ( subraster * ) ptr to subraster corresponding to ellipse
10950: * requested, or NULL for any parsing error
10951: * --------------------------------------------------------------------------
10952: * Notes: o Summary of syntax...
10953: * \circle(xdiam[,ydiam])
10954: * o
10955: * ======================================================================= */
10956: /* --- entry point --- */
10957: subraster *rastcircle ( char **expression, int size, subraster *basesp,
10958: int arg1, int arg2, int arg3 )
10959: {
10960: /* -------------------------------------------------------------------------
10961: Allocations and Declarations
10962: -------------------------------------------------------------------------- */
10963: char *texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
10964: char *qptr=NULL, quads[256]="1234"; /* default to draw all quadrants */
10965: double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/
10966: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
10967: int pixsz = 1; /* pixels are one bit each */
1.5 ! raeburn 10968: double xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
1.1 albertel 10969: int width=0, height=0; /* #pixels width,height of ellipse */
10970: int thickness = 1; /* drawn lines are one pixel thick */
1.5 ! raeburn 10971: int evalterm(); /* evaluate [arg],{arg} expressions*/
1.1 albertel 10972: int origin = 55; /* force origin centered */
10973: int circle_raster(), /* draw ellipse in circsp->image */
10974: circle_recurse(); /* for theta0,theta1 args */
10975: /* -------------------------------------------------------------------------
10976: obtain (xdiam[,ydiam]) arguments immediately following \circle command
10977: -------------------------------------------------------------------------- */
10978: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
1.5 ! raeburn 10979: *expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
1.1 albertel 10980: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
10981: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
10982: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
10983: { *qptr = '\000'; /* replace semicolon by '\0' */
1.5 ! raeburn 10984: strninit(quads,qptr+1,128); /* save user-requested quads */
1.1 albertel 10985: if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
10986: { *qptr = '\000'; /* replace , with null */
1.5 ! raeburn 10987: theta0 = (double)evalterm(mimestore,quads); /* theta0 precedes , */
! 10988: theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
1.1 albertel 10989: qptr = NULL; } /* signal thetas instead of quads */
10990: else
10991: qptr = quads; } /* set qptr arg for circle_raster()*/
10992: else /* no ;quads at all */
10993: qptr = quads; /* default to all 4 quadrants */
10994: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
10995: *xptr = '\000'; /* found it, so replace ',' by '\0'*/
1.5 ! raeburn 10996: xdiam = ydiam = /* xdiam=ydiam in user units */
! 10997: (double)evalterm(mimestore,circexpr); /* evaluate expression */
1.1 albertel 10998: if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
1.5 ! raeburn 10999: ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
1.1 albertel 11000: /* -------------------------------------------------------------------------
11001: calculate width,height, etc
11002: -------------------------------------------------------------------------- */
11003: /* --- calculate width,height in pixels --- */
11004: width = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
11005: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
11006: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
11007: fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
11008: width,height,(qptr==NULL?"default":qptr));
11009: /* -------------------------------------------------------------------------
11010: allocate subraster and raster for complete picture
11011: -------------------------------------------------------------------------- */
11012: /* --- sanity check on width,height args --- */
11013: if ( width < 1 || width > 600
11014: || height < 1 || height > 600 ) goto end_of_job;
11015: /* --- allocate and initialize subraster for constructed ellipse --- */
11016: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11017: == NULL ) goto end_of_job; /* quit if failed */
11018: /* --- initialize ellipse subraster parameters --- */
11019: circsp->type = IMAGERASTER; /* image */
11020: circsp->symdef = NULL; /* not applicable for image */
11021: circsp->baseline = height/2 + 2; /* is a little above center good? */
11022: circsp->size = size; /* size (probably unneeded) */
11023: /* -------------------------------------------------------------------------
11024: draw the ellipse
11025: -------------------------------------------------------------------------- */
11026: if ( qptr != NULL ) /* have quads */
11027: circle_raster ( circsp->image, /* embedded raster image */
11028: 0, 0, /* row0,col0 are upper-left corner */
11029: height-1, width-1, /* row1,col1 are lower-right */
11030: thickness, /* line thickness is 1 pixel */
11031: qptr ); /* "1234" quadrants to be drawn */
11032: else /* have theta0,theta1 */
11033: circle_recurse ( circsp->image, /* embedded raster image */
11034: 0, 0, /* row0,col0 are upper-left corner */
11035: height-1, width-1, /* row1,col1 are lower-right */
11036: thickness, /* line thickness is 1 pixel */
11037: theta0,theta1 ); /* theta0,theta1 arc to be drawn */
11038: /* -------------------------------------------------------------------------
11039: return constructed ellipse to caller
11040: -------------------------------------------------------------------------- */
11041: end_of_job:
11042: if ( workingparam != NULL ) /* caller wants origin */
11043: *workingparam = origin; /* return center origin to caller */
11044: return ( circsp ); /* return ellipse to caller */
11045: } /* --- end-of-function rastcircle() --- */
11046:
11047:
11048: /* ==========================================================================
11049: * Function: rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
11050: * Purpose: \bezier handler, returns subraster corresponding to bezier
11051: * parameters (col0,row0)(col1,row1)(colt,rowt)
11052: * --------------------------------------------------------------------------
11053: * Arguments: expression (I/O) char ** to first char of null-terminated
11054: * string immediately following \bezier to be
11055: * rasterized, and returning ptr immediately
11056: * following last character processed.
1.5 ! raeburn 11057: * size (I) int containing 0-7 default font size
1.1 albertel 11058: * basesp (I) subraster * to character (or subexpression)
11059: * immediately preceding \bezier
11060: * (unused, but passed for consistency)
11061: * arg1 (I) int unused
11062: * arg2 (I) int unused
11063: * arg3 (I) int unused
11064: * --------------------------------------------------------------------------
11065: * Returns: ( subraster * ) ptr to subraster corresponding to bezier
11066: * requested, or NULL for any parsing error
11067: * --------------------------------------------------------------------------
11068: * Notes: o Summary of syntax...
11069: * \bezier(col1,row1)(colt,rowt)
11070: * o col0=0,row0=0 assumed, i.e., given by
11071: * \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
11072: * ======================================================================= */
11073: /* --- entry point --- */
11074: subraster *rastbezier ( char **expression, int size, subraster *basesp,
11075: int arg1, int arg2, int arg3 )
11076: {
11077: /* -------------------------------------------------------------------------
11078: Allocations and Declarations
11079: -------------------------------------------------------------------------- */
11080: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
11081: char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
11082: double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
11083: rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
11084: rmin=0.0, cmin=0.0, /* minimum r,c */
11085: rmax=0.0, cmax=0.0, /* maximum r,c */
11086: rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
11087: r=0.0, c=0.0; /* some point */
1.5 ! raeburn 11088: int evalterm(); /* evaluate [arg],{arg} expressions*/
1.1 albertel 11089: int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
11090: int width=0, height=0; /* dimensions of bezier raster */
11091: int pixsz = 1; /* pixels are one bit each */
11092: /*int thickness = 1;*/ /* drawn lines are one pixel thick */
11093: int origin = 0; /*c's,r's reset to lower-left origin*/
11094: int bezier_raster(); /* draw bezier in bezsp->image */
11095: /* -------------------------------------------------------------------------
11096: obtain (c1,r1)(ct,rt) args immediately following \bezier command
11097: -------------------------------------------------------------------------- */
11098: for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0,r0 1=c1,r1 2=ct,rt */
11099: {
11100: /* --- parse for (r,c) args, and bump expression past them all --- */
11101: *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
11102: if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
11103: /* --- now interpret (r,c) returned in bezexpr --- */
11104: c = r = 0.0; /* init x-coord=col, y-coord=row */
11105: if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
11106: { *xptr = '\000'; /* found it, so replace ',' by '\0'*/
1.5 ! raeburn 11107: /* --- row=y-coord in pixels --- */
! 11108: r = unitlength*((double)evalterm(mimestore,xptr+1)); }
! 11109: /* --- col=x-coord in pixels --- */
! 11110: c = unitlength*((double)evalterm(mimestore,bezexpr));
1.1 albertel 11111: /* --- store r,c --- */
11112: switch ( iarg )
11113: { case 0: r0=r; c0=c; break;
11114: case 1: r1=r; c1=c; break;
11115: case 2: rt=r; ct=c; break; }
11116: } /* --- end-of-for(iarg) --- */
11117: /* --- determine midpoint and maximum,minimum points --- */
11118: rmid = 0.5*(rt + 0.5*(r0+r1)); /* y-coord at middle of bezier */
11119: cmid = 0.5*(ct + 0.5*(c0+c1)); /* x-coord at middle of bezier */
11120: rmin = min3(r0,r1,rmid); /* lowest row */
11121: cmin = min3(c0,c1,cmid); /* leftmost col */
11122: rmax = max3(r0,r1,rmid); /* highest row */
11123: cmax = max3(c0,c1,cmid); /* rightmost col */
11124: rdelta = rmax-rmin; /* height */
11125: cdelta = cmax-cmin; /* width */
11126: /* --- rescale coords so we start at 0,0 --- */
11127: r0 -= rmin; c0 -= cmin; /* rescale r0,c0 */
11128: r1 -= rmin; c1 -= cmin; /* rescale r1,c1 */
11129: rt -= rmin; ct -= cmin; /* rescale rt,ct */
11130: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
11131: r0 = rdelta - r0 + 1; /* map 0-->height-1, height-1-->0 */
11132: r1 = rdelta - r1 + 1;
11133: rt = rdelta - rt + 1;
11134: /* --- determine width,height of raster needed for bezier --- */
11135: width = (int)(cdelta + 0.9999) + 1; /* round width up */
11136: height = (int)(rdelta + 0.9999) + 1; /* round height up */
11137: if ( msgfp!=NULL && msglevel>=29 ) /* debugging */
11138: fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
11139: "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
11140: width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
11141: /* -------------------------------------------------------------------------
11142: allocate raster
11143: -------------------------------------------------------------------------- */
11144: /* --- sanity check on width,height args --- */
11145: if ( width < 1 || width > 600
11146: || height < 1 || height > 600 ) goto end_of_job;
11147: /* --- allocate and initialize subraster for constructed bezier --- */
11148: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11149: == NULL ) goto end_of_job; /* quit if failed */
11150: /* --- initialize bezier subraster parameters --- */
11151: bezsp->type = IMAGERASTER; /* image */
11152: bezsp->symdef = NULL; /* not applicable for image */
11153: bezsp->baseline = height/2 + 2; /* is a little above center good? */
11154: bezsp->size = size; /* size (probably unneeded) */
11155: /* -------------------------------------------------------------------------
11156: draw the bezier
11157: -------------------------------------------------------------------------- */
11158: bezier_raster ( bezsp->image, /* embedded raster image */
11159: r0, c0, /* row0,col0 are lower-left corner */
11160: r1, c1, /* row1,col1 are upper-right */
11161: rt, ct ); /* bezier tangent point */
11162: /* -------------------------------------------------------------------------
11163: return constructed bezier to caller
11164: -------------------------------------------------------------------------- */
11165: end_of_job:
11166: if ( workingparam != NULL ) /* caller wants origin */
11167: *workingparam = origin; /* return center origin to caller */
11168: return ( bezsp ); /* return bezier to caller */
11169: } /* --- end-of-function rastbezier() --- */
11170:
11171:
11172: /* ==========================================================================
11173: * Function: rastraise ( expression, size, basesp, arg1, arg2, arg3 )
11174: * Purpose: \raisebox{lift}{subexpression} handler, returns subraster
11175: * containing subexpression with its baseline "lifted" by lift
11176: * pixels, scaled by \unitlength, or "lowered" if lift arg
11177: * negative
11178: * --------------------------------------------------------------------------
11179: * Arguments: expression (I/O) char ** to first char of null-terminated
11180: * string immediately following \raisebox to be
11181: * rasterized, and returning ptr immediately
11182: * following last character processed.
1.3 albertel 11183: * size (I) int containing 0-7 default font size
1.1 albertel 11184: * basesp (I) subraster * to character (or subexpression)
1.3 albertel 11185: * immediately preceding \raisebox
1.1 albertel 11186: * (unused, but passed for consistency)
11187: * arg1 (I) int unused
11188: * arg2 (I) int unused
11189: * arg3 (I) int unused
11190: * --------------------------------------------------------------------------
11191: * Returns: ( subraster * ) ptr to subraster corresponding to \raisebox
11192: * requested, or NULL for any parsing error
11193: * --------------------------------------------------------------------------
11194: * Notes: o Summary of syntax...
11195: * \raisebox{lift}{subexpression}
11196: * o
11197: * ======================================================================= */
11198: /* --- entry point --- */
11199: subraster *rastraise ( char **expression, int size, subraster *basesp,
11200: int arg1, int arg2, int arg3 )
11201: {
11202: /* -------------------------------------------------------------------------
11203: Allocations and Declarations
11204: -------------------------------------------------------------------------- */
1.3 albertel 11205: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
1.1 albertel 11206: subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
11207: int lift=0; /* amount to raise/lower baseline */
1.5 ! raeburn 11208: int evalterm(); /* evaluate [arg],{arg} expressions*/
1.1 albertel 11209: /* -------------------------------------------------------------------------
11210: obtain {lift} argument immediately following \raisebox command
11211: -------------------------------------------------------------------------- */
1.5 ! raeburn 11212: rastlift = 0; /* reset global lift adjustment */
1.1 albertel 11213: /* --- parse for {lift} arg, and bump expression past it --- */
11214: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
11215: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
1.5 ! raeburn 11216: lift = eround(liftexpr); /* {lift} to integer */
1.1 albertel 11217: if ( abs(lift) > 200 ) lift=0; /* sanity check */
11218: /* -------------------------------------------------------------------------
11219: obtain {subexpr} argument after {lift}, and rasterize it
11220: -------------------------------------------------------------------------- */
11221: /* --- parse for {subexpr} arg, and bump expression past it --- */
11222: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11223: /* --- rasterize subexpression to be raised/lowered --- */
11224: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
11225: == NULL ) goto end_of_job; /* and quit if failed */
11226: /* -------------------------------------------------------------------------
11227: raise/lower baseline and return it to caller
11228: -------------------------------------------------------------------------- */
11229: /* --- raise/lower baseline --- */
11230: raisesp->baseline += lift; /* new baseline (no height checks) */
1.5 ! raeburn 11231: rastlift = lift; /* set global to signal adjustment */
1.1 albertel 11232: /* --- return raised subexpr to caller --- */
11233: end_of_job:
11234: return ( raisesp ); /* return raised subexpr to caller */
11235: } /* --- end-of-function rastraise() --- */
11236:
11237:
11238: /* ==========================================================================
11239: * Function: rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
11240: * Purpose: \rotatebox{degrees}{subexpression} handler, returns subraster
11241: * containing subexpression rotated by degrees (counterclockwise
11242: * if degrees positive)
11243: * --------------------------------------------------------------------------
11244: * Arguments: expression (I/O) char ** to first char of null-terminated
11245: * string immediately following \rotatebox to be
11246: * rasterized, and returning ptr immediately
11247: * following last character processed.
1.3 albertel 11248: * size (I) int containing 0-7 default font size
1.1 albertel 11249: * basesp (I) subraster * to character (or subexpression)
11250: * immediately preceding \rotatebox
11251: * (unused, but passed for consistency)
11252: * arg1 (I) int unused
11253: * arg2 (I) int unused
11254: * arg3 (I) int unused
11255: * --------------------------------------------------------------------------
11256: * Returns: ( subraster * ) ptr to subraster corresponding to \rotatebox
11257: * requested, or NULL for any parsing error
11258: * --------------------------------------------------------------------------
11259: * Notes: o Summary of syntax...
11260: * \rotatebox{degrees}{subexpression}
11261: * o
11262: * ======================================================================= */
11263: /* --- entry point --- */
11264: subraster *rastrotate ( char **expression, int size, subraster *basesp,
11265: int arg1, int arg2, int arg3 )
11266: {
11267: /* -------------------------------------------------------------------------
11268: Allocations and Declarations
11269: -------------------------------------------------------------------------- */
1.3 albertel 11270: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
1.1 albertel 11271: subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */
11272: raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
11273: int delete_raster(); /* delete intermediate rasters */
11274: int baseline=0; /* baseline of rasterized image */
1.5 ! raeburn 11275: double degrees=0.0, ipart,fpart; /* degrees to be rotated */
1.1 albertel 11276: int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
11277: int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
1.5 ! raeburn 11278: int evalterm(); /* evaluate [arg],{arg} expressions*/
1.1 albertel 11279: /* -------------------------------------------------------------------------
11280: obtain {degrees} argument immediately following \rotatebox command
11281: -------------------------------------------------------------------------- */
11282: /* --- parse for {degrees} arg, and bump expression past it --- */
11283: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
11284: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
1.5 ! raeburn 11285: degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
1.1 albertel 11286: if ( degrees < 0.0 ) /* clockwise rotation desired */
11287: { degrees = -degrees; /* flip sign so degrees positive */
11288: isneg = 1; } /* and set flag to indicate flip */
11289: fpart = modf(degrees,&ipart); /* integer and fractional parts */
11290: ipart = (double)(((int)degrees)%360); /* degrees mod 360 */
11291: degrees = ipart + fpart; /* restore fractional part */
11292: if ( isneg ) /* if clockwise rotation requested */
11293: degrees = 360.0 - degrees; /* do equivalent counterclockwise */
11294: idegrees = (int)(degrees+0.5); /* integer degrees */
11295: n90 = idegrees/90; /* degrees is n90 multiples of 90 */
11296: isn90 = (90*n90==idegrees); /*true if degrees is multiple of 90*/
11297: isn90 = 1; /* forced true for time being */
11298: /* -------------------------------------------------------------------------
11299: obtain {subexpr} argument after {degrees}, and rasterize it
11300: -------------------------------------------------------------------------- */
11301: /* --- parse for {subexpr} arg, and bump expression past it --- */
11302: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11303: /* --- rasterize subexpression to be rotated --- */
11304: if ( (rotsp = rasterize(subexpr,size)) /* rasterize subexpression */
11305: == NULL ) goto end_of_job; /* and quit if failed */
11306: /* --- return unmodified image if no rotation requested --- */
11307: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
11308: /* --- extract params for image to be rotated --- */
11309: rotrp = rotsp->image; /* unrotated rasterized image */
11310: baseline = rotsp->baseline; /* and baseline of that image */
11311: /* -------------------------------------------------------------------------
11312: rotate by multiples of 90 degrees
11313: -------------------------------------------------------------------------- */
11314: if ( isn90 ) /* rotation by multiples of 90 */
11315: if ( n90 > 0 ) /* do nothing for 0 degrees */
11316: {
11317: n90 = 4-n90; /* rasrot() rotates clockwise */
11318: while ( n90 > 0 ) /* still have remaining rotations */
11319: { raster *nextrp = rastrot(rotrp); /* rotate raster image */
11320: if ( nextrp == NULL ) break; /* something's terribly wrong */
11321: delete_raster(rotrp); /* free previous raster image */
11322: rotrp = nextrp; /* and replace it with rotated one */
11323: n90--; } /* decrement remaining count */
11324: } /* --- end-of-if(isn90) --- */
11325: /* -------------------------------------------------------------------------
11326: requested rotation not multiple of 90 degrees
11327: -------------------------------------------------------------------------- */
11328: if ( !isn90 ) /* explicitly construct rotation */
11329: { ; } /* not yet implemented */
11330: /* -------------------------------------------------------------------------
11331: re-populate subraster envelope with rotated image
11332: -------------------------------------------------------------------------- */
11333: /* --- re-init various subraster parameters, embedding raster in it --- */
11334: if ( rotrp != NULL ) /* rotated raster constructed okay */
11335: { rotsp->type = IMAGERASTER; /* signal constructed image */
11336: rotsp->image = rotrp; /* raster we just constructed */
11337: /* --- now try to guess pleasing baseline --- */
1.3 albertel 11338: if ( idegrees > 2 ) { /* leave unchanged if unrotated */
1.1 albertel 11339: if ( strlen(subexpr) < 3 /* we rotated a short expression */
11340: || abs(idegrees-180) < 3 ) /* or just turned it upside-down */
11341: baseline = rotrp->height - 1; /* so set with nothing descending */
11342: else /* rotated a long expression */
1.3 albertel 11343: baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
1.1 albertel 11344: rotsp->baseline = baseline; } /* set baseline as calculated above*/
11345: /* --- return rotated subexpr to caller --- */
11346: end_of_job:
11347: return ( rotsp ); /*return rotated subexpr to caller*/
11348: } /* --- end-of-function rastrotate() --- */
11349:
11350:
11351: /* ==========================================================================
1.5 ! raeburn 11352: * Function: rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
! 11353: * Purpose: \magnify{magstep}{subexpression} handler, returns subraster
! 11354: * containing magnified subexpression
! 11355: * --------------------------------------------------------------------------
! 11356: * Arguments: expression (I/O) char ** to first char of null-terminated
! 11357: * string immediately following \reflectbox to
! 11358: * be rasterized, and returning ptr immediately
! 11359: * following last character processed.
! 11360: * size (I) int containing 0-7 default font size
! 11361: * basesp (I) subraster * to character (or subexpression)
! 11362: * immediately preceding \reflectbox
! 11363: * (unused, but passed for consistency)
! 11364: * arg1 (I) int unused
! 11365: * arg2 (I) int unused
! 11366: * arg3 (I) int unused
! 11367: * --------------------------------------------------------------------------
! 11368: * Returns: ( subraster * ) ptr to subraster corresponding to \magnify
! 11369: * requested, or NULL for any parsing error
! 11370: * --------------------------------------------------------------------------
! 11371: * Notes: o Summary of syntax...
! 11372: * \magnify{magstep}{subexpression}
! 11373: * o
! 11374: * ======================================================================= */
! 11375: /* --- entry point --- */
! 11376: subraster *rastmagnify ( char **expression, int size, subraster *basesp,
! 11377: int arg1, int arg2, int arg3 )
! 11378: {
! 11379: /* -------------------------------------------------------------------------
! 11380: Allocations and Declarations
! 11381: -------------------------------------------------------------------------- */
! 11382: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
! 11383: subraster *rasterize(), *magsp=NULL; /* subraster for magnified subexpr */
! 11384: raster *rastmag(), *magrp=NULL; /* magnify subraster->image */
! 11385: int magstep = 1; /* default magnification */
! 11386: int delete_raster(); /* delete intermediate raster */
! 11387: int baseline=0; /* baseline of rasterized image */
! 11388: /* -------------------------------------------------------------------------
! 11389: obtain {magstep} argument immediately following \magnify command
! 11390: -------------------------------------------------------------------------- */
! 11391: /* --- parse for {magstep} arg, and bump expression past it --- */
! 11392: *expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
! 11393: magstep = atoi(magexpr); /* convert {magstep} to int */
! 11394: if ( magstep<1 || magstep>10 ) /* check magstep input */
! 11395: magstep = 1; /* back to default if illegal */
! 11396: /* -------------------------------------------------------------------------
! 11397: obtain {subexpr} argument after {magstep}, and rasterize it
! 11398: -------------------------------------------------------------------------- */
! 11399: /* --- parse for {subexpr} arg, and bump expression past it --- */
! 11400: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
! 11401: /* --- rasterize subexpression to be reflected --- */
! 11402: if ( (magsp = rasterize(subexpr,size)) /* rasterize subexpression */
! 11403: == NULL ) goto end_of_job; /* and quit if failed */
! 11404: /* --- return unmodified image if no magnification requested --- */
! 11405: if ( magstep<=1 ) goto end_of_job; /* don't bother magnifying image */
! 11406: /* --- extract params for image to be magnified --- */
! 11407: magrp = magsp->image; /* unmagnified rasterized image */
! 11408: baseline = magsp->baseline; /* and baseline of that image */
! 11409: /* -------------------------------------------------------------------------
! 11410: magnify image and adjust its parameters
! 11411: -------------------------------------------------------------------------- */
! 11412: /* --- magnify image --- */
! 11413: magrp = rastmag(magsp->image,magstep); /* magnify raster image */
! 11414: if ( magrp == NULL ) goto end_of_job; /* failed to magnify image */
! 11415: delete_raster(magsp->image); /* free original raster image */
! 11416: magsp->image = magrp; /*and replace it with magnified one*/
! 11417: /* --- adjust parameters --- */
! 11418: baseline *= magstep; /* scale baseline */
! 11419: if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */
! 11420: magsp->baseline = baseline; /*reset baseline of magnified image*/
! 11421: /* --- return magnified subexpr to caller --- */
! 11422: end_of_job:
! 11423: return ( magsp ); /*back to caller with magnified expr*/
! 11424: } /* --- end-of-function rastmagnify() --- */
! 11425:
! 11426:
! 11427: /* ==========================================================================
1.3 albertel 11428: * Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
11429: * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster
11430: * containing subexpression reflected horizontally (i.e., around
11431: * vertical axis, |_ becomes _|) if [axis] not given or axis=1,
11432: * or reflected vertically if axis=2 given.
11433: * --------------------------------------------------------------------------
11434: * Arguments: expression (I/O) char ** to first char of null-terminated
11435: * string immediately following \reflectbox to
11436: * be rasterized, and returning ptr immediately
11437: * following last character processed.
11438: * size (I) int containing 0-7 default font size
11439: * basesp (I) subraster * to character (or subexpression)
11440: * immediately preceding \reflectbox
11441: * (unused, but passed for consistency)
11442: * arg1 (I) int unused
11443: * arg2 (I) int unused
11444: * arg3 (I) int unused
11445: * --------------------------------------------------------------------------
11446: * Returns: ( subraster * ) ptr to subraster corresponding to \reflectbox
11447: * requested, or NULL for any parsing error
11448: * --------------------------------------------------------------------------
11449: * Notes: o Summary of syntax...
11450: * \reflectbox[axis]{subexpression}
11451: * o
11452: * ======================================================================= */
11453: /* --- entry point --- */
11454: subraster *rastreflect ( char **expression, int size, subraster *basesp,
11455: int arg1, int arg2, int arg3 )
11456: {
11457: /* -------------------------------------------------------------------------
11458: Allocations and Declarations
11459: -------------------------------------------------------------------------- */
11460: char *texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
11461: subraster *rasterize(), *refsp=NULL; /* subraster for reflected subexpr */
11462: raster *rastref(), *refrp=NULL; /* reflect subraster->image */
11463: int axis = 1; /* default horizontal reflection */
11464: int delete_raster(); /* delete intermediate raster */
11465: int baseline=0; /* baseline of rasterized image */
11466: /* -------------------------------------------------------------------------
11467: obtain [axis] argument immediately following \reflectbox command, if given
11468: -------------------------------------------------------------------------- */
11469: /* --- check for optional [axis] arg --- */
11470: if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
11471: { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
11472: axis = atoi(axisexpr); /* convert [axis] to int */
11473: if ( axis<1 || axis>2 ) /* check axis input */
11474: axis = 1; } /* back to default if illegal */
11475: /* -------------------------------------------------------------------------
11476: obtain {subexpr} argument after optional [axis], and rasterize it
11477: -------------------------------------------------------------------------- */
11478: /* --- parse for {subexpr} arg, and bump expression past it --- */
11479: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11480: /* --- rasterize subexpression to be reflected --- */
11481: if ( (refsp = rasterize(subexpr,size)) /* rasterize subexpression */
11482: == NULL ) goto end_of_job; /* and quit if failed */
11483: /* --- return unmodified image if no reflection requested --- */
11484: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
11485: /* --- extract params for image to be reflected --- */
11486: refrp = refsp->image; /* unreflected rasterized image */
11487: baseline = refsp->baseline; /* and baseline of that image */
11488: /* -------------------------------------------------------------------------
11489: reflect image and adjust its parameters
11490: -------------------------------------------------------------------------- */
11491: /* --- reflect image --- */
11492: refrp = rastref(refsp->image,axis); /* reflect raster image */
11493: if ( refrp == NULL ) goto end_of_job; /* failed to reflect image */
11494: delete_raster(refsp->image); /* free original raster image */
11495: refsp->image = refrp; /*and replace it with reflected one*/
11496: /* --- adjust parameters --- */
11497: if ( axis == 2 ) /* for vertical reflection */
11498: baseline = refrp->height - 1; /* set with nothing descending */
11499: refsp->baseline = baseline; /* reset baseline of reflected image*/
11500: /* --- return reflected subexpr to caller --- */
11501: end_of_job:
11502: return ( refsp ); /*back to caller with reflected expr*/
11503: } /* --- end-of-function rastreflect() --- */
11504:
11505:
11506: /* ==========================================================================
1.1 albertel 11507: * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
11508: * Purpose: \fbox{subexpression} handler, returns subraster
11509: * containing subexpression with frame box drawn around it
11510: * --------------------------------------------------------------------------
11511: * Arguments: expression (I/O) char ** to first char of null-terminated
11512: * string immediately following \fbox to be
11513: * rasterized, and returning ptr immediately
11514: * following last character processed.
1.5 ! raeburn 11515: * size (I) int containing 0-7 default font size
1.1 albertel 11516: * basesp (I) subraster * to character (or subexpression)
11517: * immediately preceding \fbox
11518: * (unused, but passed for consistency)
11519: * arg1 (I) int unused
11520: * arg2 (I) int unused
11521: * arg3 (I) int unused
11522: * --------------------------------------------------------------------------
11523: * Returns: ( subraster * ) ptr to subraster corresponding to \fbox
11524: * requested, or NULL for any parsing error
11525: * --------------------------------------------------------------------------
11526: * Notes: o Summary of syntax...
11527: * \fbox[width][height]{subexpression}
11528: * o
11529: * ======================================================================= */
11530: /* --- entry point --- */
11531: subraster *rastfbox ( char **expression, int size, subraster *basesp,
11532: int arg1, int arg2, int arg3 )
11533: {
11534: /* -------------------------------------------------------------------------
11535: Allocations and Declarations
11536: -------------------------------------------------------------------------- */
1.3 albertel 11537: char *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
1.1 albertel 11538: subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
11539: raster *border_raster(), *bp=NULL; /* framed image raster */
1.5 ! raeburn 11540: int evalterm(), evalue=0; /* interpret [width][height] */
! 11541: int fwidth=6, fthick=1, /*extra frame width, line thickness*/
! 11542: fsides=0; /* frame sides: 1=left,2=top,4=right,8=bot */
1.1 albertel 11543: int width=(-1), height=(-1), /* optional [width][height] args */
11544: iscompose = 0; /* set true if optional args given */
11545: /* -------------------------------------------------------------------------
11546: obtain optional [width][height] arguments immediately following \fbox
11547: -------------------------------------------------------------------------- */
11548: /* --- first check for optional \fbox[width] --- */
1.5 ! raeburn 11549: if ( *(*expression) == '[' ) { /* check for []-enclosed width arg */
! 11550: *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
! 11551: if ( !isempty(widtharg) ) { /* got widtharg */
! 11552: char *comma = strchr(widtharg,','); /* look for [width,sides] */
! 11553: if ( comma == (char *)NULL ) /* no comma */
! 11554: comma = strchr(widtharg,';'); /* permit semicolon [width;sides] */
! 11555: if ( comma != (char *)NULL ) { /* optional [width,fsides] found */
! 11556: fsides = atoi(comma+1); /* interpret fsides after comma */
! 11557: if ( size < 5 ) /* for smaller fonts */
! 11558: { fwidth = 2; fthick = 1; } /* tighten frame, thinner accent */
! 11559: else { fwidth = 3; fthick = 2; } /* loosen frame, thicken accent */
! 11560: *comma = '\000'; /* null-terminate width at comma */
! 11561: trimwhite(widtharg); } /*remove leading/trailing whitespace*/
! 11562: if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
! 11563: height = 1; /* default explicit height, too */
! 11564: if ( fsides == 0 ) { /* a normal framebox */
! 11565: evalue = eround(widtharg); /* interpret and scale width */
! 11566: width = max2(1,evalue); /* must be >0 */
! 11567: fwidth = 2; iscompose = 1; }
! 11568: else /* absolute pixels for "accents" */
! 11569: width = evalterm(mimestore,widtharg); }
! 11570: } /* --- end-of-if(!isempty(widtharg)) --- */
1.1 albertel 11571: } /* --- end-of-if(**expression=='[') --- */
1.5 ! raeburn 11572: if ( width > 0 || fsides > 0) /* found leading [width], so... */
1.1 albertel 11573: if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
11574: { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
1.5 ! raeburn 11575: if ( !isempty(widtharg) ) { /* got widtharg */
! 11576: if ( fsides == 0 ) { /* a normal framebox */
! 11577: evalue = eround(widtharg); /* interpret and scale height */
! 11578: height = max2(1,evalue); /* must be >0 */
! 11579: fwidth = 0; } /* no extra border */
! 11580: else /* absolute pixels for "accents" */
! 11581: height = evalterm(mimestore,widtharg); }
1.1 albertel 11582: } /* --- end-of-if(**expression=='[') --- */
11583: /* -------------------------------------------------------------------------
11584: obtain {subexpr} argument
11585: -------------------------------------------------------------------------- */
11586: /* --- parse for {subexpr} arg, and bump expression past it --- */
11587: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11588: /* --- rasterize subexpression to be framed --- */
11589: if ( width<0 || height<0 ) /* no explicit dimensions given */
11590: { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
11591: == NULL ) goto end_of_job; } /* and quit if failed */
11592: else
11593: { char composexpr[8192]; /* compose subexpr with empty box */
1.5 ! raeburn 11594: sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
1.1 albertel 11595: width,height,subexpr);
11596: if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
11597: == NULL ) goto end_of_job; } /* and quit if failed */
11598: /* -------------------------------------------------------------------------
11599: draw frame, reset params, and return it to caller
11600: -------------------------------------------------------------------------- */
11601: /* --- draw border --- */
1.5 ! raeburn 11602: if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
1.1 albertel 11603: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
11604: == NULL ) goto end_of_job; /* draw border and quit if failed */
11605: /* --- replace original image and raise baseline to accommodate frame --- */
11606: framesp->image = bp; /* replace image with framed one */
11607: if ( !iscompose ) /* simple border around subexpr */
11608: framesp->baseline += fwidth; /* so just raise baseline */
11609: else
11610: framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
11611: /* --- return framed subexpr to caller --- */
11612: end_of_job:
11613: return ( framesp ); /* return framed subexpr to caller */
11614: } /* --- end-of-function rastfbox() --- */
11615:
11616:
11617: /* ==========================================================================
11618: * Function: rastinput ( expression, size, basesp, arg1, arg2, arg3 )
11619: * Purpose: \input{filename} handler, reads filename and returns
11620: * subraster containing image of expression read from filename
11621: * --------------------------------------------------------------------------
11622: * Arguments: expression (I/O) char ** to first char of null-terminated
11623: * string immediately following \input to be
11624: * rasterized, and returning ptr immediately
11625: * following last character processed.
1.5 ! raeburn 11626: * size (I) int containing 0-7 default font size
1.1 albertel 11627: * basesp (I) subraster * to character (or subexpression)
11628: * immediately preceding \input
11629: * (unused, but passed for consistency)
11630: * arg1 (I) int unused
11631: * arg2 (I) int unused
11632: * arg3 (I) int unused
11633: * --------------------------------------------------------------------------
11634: * Returns: ( subraster * ) ptr to subraster corresponding to expression
11635: * in filename, or NULL for any parsing error
11636: * --------------------------------------------------------------------------
11637: * Notes: o Summary of syntax...
1.5 ! raeburn 11638: * \input{filename} reads entire file named filename
! 11639: * \input{filename:tag} reads filename, but returns only
! 11640: * those characters between <tag>...</tag> in that file.
1.1 albertel 11641: * o
11642: * ======================================================================= */
11643: /* --- entry point --- */
11644: subraster *rastinput ( char **expression, int size, subraster *basesp,
11645: int arg1, int arg2, int arg3 )
11646: {
11647: /* -------------------------------------------------------------------------
11648: Allocations and Declarations
11649: -------------------------------------------------------------------------- */
1.3 albertel 11650: char *texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
1.1 albertel 11651: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
11652: int status, rastreadfile(); /* read input file */
11653: int format=0, npts=0; /* don't reformat (numerical) input */
1.5 ! raeburn 11654: int isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
! 11655: /*int evalterm();*/ /* evaluate expressions */
! 11656: char *inputpath = INPUTPATH; /* permitted \input{} paths for any user */
! 11657: int isstrstr(); /* search for valid inputpath in filename */
1.3 albertel 11658: char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
1.1 albertel 11659: *mimeprep(), /* preprocess inputted data */
1.2 albertel 11660: *dbltoa(), *reformat=NULL; /* reformat numerical input */
1.1 albertel 11661: /* -------------------------------------------------------------------------
11662: obtain [tag]{filename} argument
11663: -------------------------------------------------------------------------- */
11664: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
11665: if ( *(*expression) == '[' ) /* check for []-enclosed value */
1.3 albertel 11666: { char argfld[MAXTOKNSZ+1]; /* optional argument field */
1.5 ! raeburn 11667: *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
1.2 albertel 11668: if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
11669: { format = 1; /* signal dtoa()/dbltoa() format */
1.1 albertel 11670: if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
11671: npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
1.5 ! raeburn 11672: if ( format == 0 ) { /* reformat not requested */
! 11673: strninit(tag,argfld,1020); } } /* so interpret arg as tag */
1.1 albertel 11674: /* --- parse for {filename} arg, and bump expression past it --- */
1.5 ! raeburn 11675: *expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
1.1 albertel 11676: /* --- check for alternate filename:tag --- */
1.5 ! raeburn 11677: if ( !isempty(filename) /* got filename */
! 11678: /*&& isempty(tag)*/ ) /* but no [tag] */
1.1 albertel 11679: { char *delim = strchr(filename,':'); /* look for : in filename:tag */
11680: if ( delim != (char *)NULL ) /* found it */
11681: { *delim = '\000'; /* null-terminate filename at : */
1.5 ! raeburn 11682: strninit(tag,delim+1,1020); } } /* and stuff after : is tag */
! 11683: /* --- check filename for an inputpath valid for all users --- */
! 11684: if ( !isinput /* if this user can't \input{} */
! 11685: && !isempty(filename) /* and we got a filename */
! 11686: && !isempty(inputpath) ) /* and an inputpath */
! 11687: if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */
! 11688: isinput = 1; /* okay to \input{} this filename */
! 11689: /* --- guard against recursive runaway (e.g., file \input's itself) --- */
! 11690: if ( ++ninputcmds > 8 ) /* max \input's per expression */
! 11691: isinput = 0; /* flip flag off after the max */
1.1 albertel 11692: /* --------------------------------------------------------------------------
1.5 ! raeburn 11693: Read file (and convert to numeric if [dtoa] option was given)
! 11694: -------------------------------------------------------------------------- */
! 11695: if ( isinput ) { /* user permitted to use \input{} */
! 11696: status = rastreadfile(filename,0,tag,subexpr); /* read file */
! 11697: if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
! 11698: /* --- rasterize input subexpression --- */
! 11699: mimeprep(subexpr); /* preprocess subexpression */
! 11700: if ( format == 1 ) { /* dtoa()/dbltoa() */
! 11701: double d = strtod(subexpr,NULL); /* interpret subexpr as double */
! 11702: if ( d != 0.0 ) /* conversion to double successful */
! 11703: if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
! 11704: strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
! 11705: } /* --- end-of-if(isinput) --- */
! 11706: /* --------------------------------------------------------------------------
! 11707: emit error message for unauthorized users trying to use \input{}
! 11708: -------------------------------------------------------------------------- */
! 11709: else { /* inputseclevel > seclevel */
! 11710: sprintf(subexpr,
! 11711: "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
! 11712: (isempty(filename)?"???":filename));
! 11713: } /* --- end-of-if/else(isinput) --- */
! 11714: /* --------------------------------------------------------------------------
! 11715: Rasterize constructed subexpression
1.1 albertel 11716: -------------------------------------------------------------------------- */
11717: inputsp = rasterize(subexpr,size); /* rasterize subexpression */
11718: /* --- return input image to caller --- */
11719: end_of_job:
11720: return ( inputsp ); /* return input image to caller */
11721: } /* --- end-of-function rastinput() --- */
11722:
11723:
11724: /* ==========================================================================
11725: * Function: rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
11726: * Purpose: \counter[value]{filename} handler, returns subraster
11727: * containing image of counter value read from filename
11728: * (or optional [value]), and increments counter
11729: * --------------------------------------------------------------------------
11730: * Arguments: expression (I/O) char ** to first char of null-terminated
11731: * string immediately following \counter to be
11732: * rasterized, and returning ptr immediately
11733: * following last character processed.
1.5 ! raeburn 11734: * size (I) int containing 0-7 default font size
1.1 albertel 11735: * basesp (I) subraster * to character (or subexpression)
11736: * immediately preceding \counter
11737: * (unused, but passed for consistency)
11738: * arg1 (I) int unused
11739: * arg2 (I) int unused
11740: * arg3 (I) int unused
11741: * --------------------------------------------------------------------------
11742: * Returns: ( subraster * ) ptr to subraster corresponding to \counter
11743: * requested, or NULL for any parsing error
11744: * --------------------------------------------------------------------------
11745: * Notes: o Summary of syntax...
1.2 albertel 11746: * \counter[value][logfile]{filename:tag}
11747: * o :tag is optional
1.1 albertel 11748: * ======================================================================= */
11749: /* --- entry point --- */
11750: subraster *rastcounter ( char **expression, int size, subraster *basesp,
11751: int arg1, int arg2, int arg3 )
11752: {
11753: /* -------------------------------------------------------------------------
11754: Allocations and Declarations
11755: -------------------------------------------------------------------------- */
11756: char *texsubexpr(), filename[1024]="\000", /* counter file */
1.3 albertel 11757: logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
1.1 albertel 11758: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
11759: FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
1.2 albertel 11760: int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
1.5 ! raeburn 11761: iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
1.2 albertel 11762: isstrict = 1; /* true to only write to existing files */
1.3 albertel 11763: char text[MAXFILESZ] = "1_", /* only line in counter file without tags */
1.1 albertel 11764: *delim = NULL, /* delimiter in text */
1.2 albertel 11765: utext[128] = "1_", /* default delimiter */
1.1 albertel 11766: *udelim = utext+1; /* underscore delimiter */
1.2 albertel 11767: char *rasteditfilename(), /* edit log file name */
11768: *timestamp(), /* timestamp for logging */
11769: *dbltoa(); /* double to comma-separated ascii */
1.1 albertel 11770: int counter = 1, /* atoi(text) (after _ removed, if present) */
1.2 albertel 11771: value = 1, /* optional [value] argument */
11772: gotvalue = 0, /* set true if [value] supplied */
11773: isdelta = 0, /* set true if [+value] or [-value] is delta*/
1.1 albertel 11774: ordindex = (-1); /* ordinal[] index to append ordinal suffix */
11775: /*--- ordinal suffixes based on units digit of counter ---*/
11776: static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
11777: static char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
11778: static int commentvar = 1; /* logvars[commentvar] replaced by comment */
11779: /* -------------------------------------------------------------------------
11780: first obtain optional [value][logfile] args immediately following \counter
11781: -------------------------------------------------------------------------- */
11782: /* --- first check for optional \counter[value] --- */
11783: if ( *(*expression) == '[' ) /* check for []-enclosed value */
1.2 albertel 11784: { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
11785: if ( *text != '\000' ) /* got counter value (or logfile) */
1.3 albertel 11786: if ( strlen(text) >= 1 ) { /* and it's not an empty string */
1.2 albertel 11787: if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
11788: gotvalue = 1; /* signal we got optional value */
11789: else /* not +-digit, so must be logfile */
1.3 albertel 11790: strcpy(logfile,text); } /* so just copy it */
1.1 albertel 11791: } /* --- end-of-if(**expression=='[') --- */
11792: /* --- next check for optional \counter[][logfile] --- */
11793: if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
11794: { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
1.2 albertel 11795: if ( *filename != '\000' ) /* got logfile (or counter value) */
1.3 albertel 11796: if ( strlen(filename) >= 1 ) { /* and it's not an empty string */
1.2 albertel 11797: if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
11798: || gotvalue ) /* or we already got counter value */
11799: strcpy(logfile,filename); /* so just copy it */
11800: else /* leading +-digit must be value */
11801: { strcpy(text,filename); /* copy value to text line */
1.3 albertel 11802: gotvalue = 1; } } /* and signal we got optional value*/
1.1 albertel 11803: } /* --- end-of-if(**expression=='[') --- */
1.2 albertel 11804: /* --- evaluate [value] if present --- */
11805: if ( gotvalue ) { /*leading +-digit should be in text*/
11806: if ( *text == '+' ) isdelta = (+1); /* signal adding */
11807: if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
11808: value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
11809: if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
11810: counter = value; /* re-init counter */
11811: } /* --- end-of-if(gotvalue) --- */
1.1 albertel 11812: /* -------------------------------------------------------------------------
11813: obtain counter {filename} argument
11814: -------------------------------------------------------------------------- */
11815: /* --- parse for {filename} arg, and bump expression past it --- */
11816: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
11817: /* --- check for counter filename:tag --- */
11818: if ( *filename != '\000' ) /* got filename */
11819: if ( (delim=strchr(filename,':')) /* look for : in filename:tag */
11820: != (char *)NULL ) /* found it */
11821: { *delim = '\000'; /* null-terminate filename at : */
11822: strcpy(tag,delim+1); } /* and stuff after : is tag */
11823: /* --------------------------------------------------------------------------
1.5 ! raeburn 11824: emit error message for unauthorized users trying to use \counter{}
! 11825: -------------------------------------------------------------------------- */
! 11826: if ( !iscounter ) { /* counterseclevel > seclevel */
! 11827: sprintf(text,
! 11828: "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
! 11829: (isempty(filename)?"???":filename));
! 11830: goto rasterize_counter; /* rasterize error message */
! 11831: } /* --- end-of-if(!iscounter) --- */
! 11832: /* --------------------------------------------------------------------------
1.1 albertel 11833: Read and parse file, increment and rewrite counter (with optional underscore)
11834: -------------------------------------------------------------------------- */
11835: if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
11836: {
11837: /* --- read and interpret first (and only) line from counter file --- */
1.2 albertel 11838: if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
11839: if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
11840: { char *vdelim = NULL; /* underscore delim from file */
11841: double fileval = strtod(text,&vdelim); /* value and delim from file */
11842: counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
11843: counter += value; /* bump count by 1 or add/sub delta*/
11844: if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
1.1 albertel 11845: /* --- check for ordinal suffix --- */
11846: if ( udelim != (char *)NULL ) /* have some delim after value */
11847: if ( *udelim == '_' ) /* underscore signals ordinal */
1.2 albertel 11848: { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
11849: ordindex = abscount%10; /* least significant digit */
11850: if ( abscount >= 10 ) /* counter is 10 or greater */
11851: if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
1.1 albertel 11852: ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
11853: /* --- rewrite counter file --- */
1.2 albertel 11854: if ( status >= 0 ) /* file was read okay */
11855: { sprintf(text,"%d",counter); /*build image of incremented counter*/
11856: if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
11857: if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
11858: status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
1.1 albertel 11859: } /* --- end-of-if(strlen(filename)>1) --- */
11860: /* --------------------------------------------------------------------------
11861: log counter request
11862: -------------------------------------------------------------------------- */
11863: if ( strlen(logfile) > 1 ) /* optional [logfile] given */
11864: {
11865: char comment[1024] = "\000", /* embedded comment, logfile:comment*/
11866: *commptr = strchr(logfile,':'); /* check for : signalling comment */
1.2 albertel 11867: int islogokay = 1; /* logfile must exist if isstrict */
1.1 albertel 11868: if ( commptr != NULL ) /* have embedded comment */
11869: { strcpy(comment,commptr+1); /* comment follows : */
11870: *commptr = '\000'; } /* null-terminate actual logfile */
1.2 albertel 11871: strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
11872: if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
11873: else if ( isstrict ) { /*okay, but only write if it exists*/
11874: if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
11875: islogokay = 0; /* so don't write log file */
11876: else fclose(logfp); } /* close file opened for test read */
11877: if ( islogokay ) /* okay to write logfile */
11878: if ( (logfp = fopen(logfile,"a")) /* open logfile */
11879: != (FILE *)NULL ) { /* opened successfully for append */
11880: int ilog=0; /* logvars[] index */
11881: fprintf(logfp,"%s ",timestamp(TZDELTA,0)); /* first emit timestamp */
11882: if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
11883: else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
11884: fprintf(logfp,"=%d",counter); /* emit counter value */
11885: if ( status < 1 ) /* read or re-write failed */
11886: fprintf(logfp,"(%s %d)","error status",status); /* emit error */
11887: for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
11888: if ( ilog == commentvar /* replace with comment... */
11889: && commptr != NULL ) /* ...if available */
11890: fprintf(logfp," %.256s",comment); /* log embedded comment */
11891: else
11892: { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
11893: fprintf(logfp," %.64s", /* log variable */
1.1 albertel 11894: (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
1.2 albertel 11895: fprintf(logfp,"\n"); /* terminating newline */
11896: fclose(logfp); /* close logfile */
11897: } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
1.1 albertel 11898: } /* --- end-of-if(strlen(logfile)>1) --- */
11899: /* --------------------------------------------------------------------------
11900: construct counter expression and rasterize it
11901: -------------------------------------------------------------------------- */
11902: /* --- construct expression --- */
11903: /*sprintf(text,"%d",counter);*/ /* start with counter */
1.2 albertel 11904: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
1.1 albertel 11905: if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
11906: { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
11907: strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
11908: strcat(text,"}}"); } /* finish with }} */
11909: /* --- rasterize it --- */
1.5 ! raeburn 11910: rasterize_counter:
! 11911: countersp = rasterize(text,size); /* rasterize counter subexpression */
1.1 albertel 11912: /* --- return counter image to caller --- */
11913: /*end_of_job:*/
11914: return ( countersp ); /* return counter image to caller */
11915: } /* --- end-of-function rastcounter() --- */
11916:
11917:
11918: /* ==========================================================================
1.5 ! raeburn 11919: * Function: rasteval ( expression, size, basesp, arg1, arg2, arg3 )
! 11920: * Purpose: handle \eval
! 11921: * --------------------------------------------------------------------------
! 11922: * Arguments: expression (I/O) char ** to first char of null-terminated
! 11923: * string immediately following \eval,
! 11924: * and returning ptr immediately
! 11925: * following last character processed.
! 11926: * size (I) int containing 0-7 default font size
! 11927: * basesp (I) subraster * to character (or subexpression)
! 11928: * immediately preceding \eval
! 11929: * (unused, but passed for consistency)
! 11930: * arg1 (I) int unused
! 11931: * arg2 (I) int unused
! 11932: * arg3 (I) int unused
! 11933: * --------------------------------------------------------------------------
! 11934: * Returns: ( subraster * ) subraster ptr to date stamp
! 11935: * --------------------------------------------------------------------------
! 11936: * Notes: o
! 11937: * ======================================================================= */
! 11938: /* --- entry point --- */
! 11939: subraster *rasteval ( char **expression, int size, subraster *basesp,
! 11940: int arg1, int arg2, int arg3 )
! 11941: {
! 11942: /* -------------------------------------------------------------------------
! 11943: Allocations and Declarations
! 11944: -------------------------------------------------------------------------- */
! 11945: char *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
! 11946: subraster *rasterize(), *evalsp=NULL; /* rasterize evaluated expression */
! 11947: int evalterm(), value=0; /* evaluate expression */
! 11948: /* -------------------------------------------------------------------------
! 11949: Parse for subexpr to be \eval-uated, and bump expression past it
! 11950: -------------------------------------------------------------------------- */
! 11951: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
! 11952: if ( *subexpr == '\000' ) /* couldn't get subexpression */
! 11953: goto end_of_job; /* nothing to do, so quit */
! 11954: /* -------------------------------------------------------------------------
! 11955: Evaluate expression, ascii-ize integer result, rasterize it
! 11956: -------------------------------------------------------------------------- */
! 11957: /* --- evaluate expression --- */
! 11958: value = evalterm(mimestore,subexpr); /* evaluate expression */
! 11959: /* --- ascii-ize it --- */
! 11960: sprintf(subexpr,"%d",value); /* ascii version of value */
! 11961: /* --- rasterize ascii-ized expression value --- */
! 11962: evalsp = rasterize(subexpr,size); /* rasterize evaluated expression */
! 11963: /* --- return evaluated expression raster to caller --- */
! 11964: end_of_job:
! 11965: return ( evalsp ); /* return evaluated expr to caller */
! 11966: } /* --- end-of-function rasteval() --- */
! 11967:
! 11968:
! 11969: /* ==========================================================================
1.2 albertel 11970: * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
11971: * Purpose: handle \today
11972: * --------------------------------------------------------------------------
11973: * Arguments: expression (I/O) char ** to first char of null-terminated
11974: * string immediately following \today,
11975: * and returning ptr immediately
11976: * following last character processed.
1.5 ! raeburn 11977: * size (I) int containing 0-7 default font size
1.2 albertel 11978: * basesp (I) subraster * to character (or subexpression)
11979: * immediately preceding \today
11980: * (unused, but passed for consistency)
11981: * arg1 (I) int unused
11982: * arg2 (I) int unused
11983: * arg3 (I) int unused
11984: * --------------------------------------------------------------------------
11985: * Returns: ( subraster * ) subraster ptr to date stamp
11986: * --------------------------------------------------------------------------
11987: * Notes: o
11988: * ======================================================================= */
11989: /* --- entry point --- */
11990: subraster *rasttoday ( char **expression, int size, subraster *basesp,
11991: int arg1, int arg2, int arg3 )
11992: {
11993: /* -------------------------------------------------------------------------
11994: Allocations and Declarations
11995: -------------------------------------------------------------------------- */
11996: char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
11997: char *timestamp(), *today=optarg; /* timestamp to be rasterized */
11998: subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
11999: int ifmt=1, tzdelta=0; /* default timestamp() args */
12000: /* -------------------------------------------------------------------------
12001: Get optional args \today[+/-tzdelta,ifmt]
12002: -------------------------------------------------------------------------- */
12003: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
12004: if ( *(*expression) == '[' ) /* check for []-enclosed value */
12005: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
12006: if ( *optarg != '\000' ) /* got optional arg */
12007: { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
12008: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
12009: if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
12010: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
12011: { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
12012: if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
12013: tzdelta = atoi(arg); /* so interpret arg as tzdelta */
12014: else ifmt = atoi(arg); } /* else interpret args as ifmt */
12015: } /* --- end-of-if(*optarg!='\0') --- */
12016: } /* --- end-of-if(**expression=='[') --- */
12017: /* -------------------------------------------------------------------------
12018: Get timestamp and rasterize it
12019: -------------------------------------------------------------------------- */
12020: strcpy(today,"\\text{"); /* rasterize timestamp as text */
12021: strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
12022: strcat(today,"}"); /* terminate \text{} braces */
12023: todaysp = rasterize(today,size); /* rasterize timestamp */
12024: /* --- return timestamp raster to caller --- */
12025: /*end_of_job:*/
12026: return ( todaysp ); /* return timestamp to caller */
12027: } /* --- end-of-function rasttoday() --- */
12028:
12029:
12030: /* ==========================================================================
12031: * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
12032: * Purpose: handle \calendar
12033: * --------------------------------------------------------------------------
12034: * Arguments: expression (I/O) char ** to first char of null-terminated
12035: * string immediately following \calendar
12036: * and returning ptr immediately
12037: * following last character processed.
1.5 ! raeburn 12038: * size (I) int containing 0-7 default font size
1.2 albertel 12039: * basesp (I) subraster * to character (or subexpression)
12040: * immediately preceding \calendar
12041: * (unused, but passed for consistency)
12042: * arg1 (I) int unused
12043: * arg2 (I) int unused
12044: * arg3 (I) int unused
12045: * --------------------------------------------------------------------------
12046: * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
12047: * --------------------------------------------------------------------------
12048: * Notes: o
12049: * ======================================================================= */
12050: /* --- entry point --- */
12051: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
12052: int arg1, int arg2, int arg3 )
12053: {
12054: /* -------------------------------------------------------------------------
12055: Allocations and Declarations
12056: -------------------------------------------------------------------------- */
12057: char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
12058: char *calendar(), *calstr=NULL; /* calendar to be rasterized */
12059: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
12060: int year=0,month=0,day=0, argval=0; /* default calendar() args */
12061: /* -------------------------------------------------------------------------
12062: Get optional args \today[+/-tzdelta,ifmt]
12063: -------------------------------------------------------------------------- */
12064: /* --- check for optional \calendar[year,month] --- */
12065: if ( *(*expression) == '[' ) /* check for []-enclosed value */
12066: { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
12067: if ( *optarg != '\000' ) /* got optional arg */
12068: { char *comma = strchr(optarg,','), /* comma between year,month */
12069: *comma2 = NULL; /* second comma before day */
12070: int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
12071: if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
12072: if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
12073: { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
12074: for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
12075: { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
12076: argval = atoi(arg); /* interpret arg as integer */
12077: if ( iarg < 3 ) /* first two args are month,year */
12078: {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
12079: else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
12080: else /* only 3rd arg can be day */
12081: if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
12082: } /* --- end-of-if(*optarg!='\0') --- */
12083: } /* --- end-of-if(**expression=='[') --- */
12084: /* -------------------------------------------------------------------------
12085: Get calendar string and rasterize it
12086: -------------------------------------------------------------------------- */
12087: if ( msgfp!= NULL && msglevel>=9 )
12088: fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
12089: year,month,day);
12090: calstr = calendar(year,month,day); /* get calendar string */
12091: calendarsp = rasterize(calstr,size); /* rasterize calendar string */
12092: /* --- return calendar raster to caller --- */
12093: /*end_of_job:*/
12094: return ( calendarsp ); /* return calendar to caller */
12095: } /* --- end-of-function rastcalendar() --- */
12096:
12097:
12098: /* ==========================================================================
1.5 ! raeburn 12099: * Function: rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
! 12100: * Purpose: handle \environment
! 12101: * --------------------------------------------------------------------------
! 12102: * Arguments: expression (I/O) char ** to first char of null-terminated
! 12103: * string immediately following \environment
! 12104: * and returning ptr immediately
! 12105: * following last character processed (in this
! 12106: * case, \environment takes no arguments, so
! 12107: * expression is returned unchanged).
! 12108: * size (I) int containing 0-7 default font size
! 12109: * basesp (I) subraster * to character (or subexpression)
! 12110: * immediately preceding \environment
! 12111: * (unused, but passed for consistency)
! 12112: * arg1 (I) int unused
! 12113: * arg2 (I) int unused
! 12114: * arg3 (I) int unused
! 12115: * --------------------------------------------------------------------------
! 12116: * Returns: ( subraster * ) subraster ptr to rendered environment image
! 12117: * --------------------------------------------------------------------------
! 12118: * Notes: o
! 12119: * ======================================================================= */
! 12120: /* --- entry point --- */
! 12121: subraster *rastenviron ( char **expression, int size, subraster *basesp,
! 12122: int arg1, int arg2, int arg3 )
! 12123: {
! 12124: /* -------------------------------------------------------------------------
! 12125: Allocations and Declarations
! 12126: -------------------------------------------------------------------------- */
! 12127: char *texsubexpr(), optarg[255]; /* optional [...] args (for future)*/
! 12128: char environstr[8192] = "\000", /* string for all environment vars */
! 12129: environvar[1024] = "\000"; /* one environment variable */
! 12130: char *strwrap(), /* wrap long lines */
! 12131: *strdetex(), /* removes/replaces any math chars */
! 12132: *environptr = NULL; /* ptr to preprocessed environvar */
! 12133: char *mimeprep(); /* preprocess environvar string */
! 12134: int unescape_url(); /* convert all %xx's to chars */
! 12135: int isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
! 12136: int maxvarlen = 512, /* max chars in environment var */
! 12137: maxenvlen = 6400, /* max chars in entire string */
! 12138: wraplen = 48; /* strwrap() wrap lines at 48 chars*/
! 12139: int ienv = 0; /* environ[] index */
! 12140: subraster *rasterize(), *environsp=NULL; /* rasterize environment string */
! 12141: /* -------------------------------------------------------------------------
! 12142: Get args
! 12143: -------------------------------------------------------------------------- */
! 12144: /* --- check for optional \environment args --- */
! 12145: if ( 1 ) /* there aren't any args (yet) */
! 12146: if ( *(*expression) == '[' ) { /* check for []-enclosed value */
! 12147: *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
! 12148: if ( *optarg != '\000' ) { ; /* got optional arg, so process it */
! 12149: wraplen = atoi(optarg); /* interpret \environment[wraplen] */
! 12150: if ( wraplen < 1 ) wraplen = 8; /* set minimum */
! 12151: } /* --- end-of-if(*optarg!='\0') --- */
! 12152: } /* --- end-of-if(**expression=='[') --- */
! 12153: /* --------------------------------------------------------------------------
! 12154: emit error message for unauthorized users trying to use \environ
! 12155: -------------------------------------------------------------------------- */
! 12156: if ( !isenviron ) { /* environseclevel > seclevel */
! 12157: sprintf(environstr,
! 12158: "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
! 12159: goto rasterize_environ; /* rasterize error message */
! 12160: } /* --- end-of-if(!isenviron) --- */
! 12161: /* -------------------------------------------------------------------------
! 12162: Accumulate environment variables and rasterize string containing them
! 12163: -------------------------------------------------------------------------- */
! 12164: *environstr = '\000'; /* reset environment string */
! 12165: strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
! 12166: for ( ienv=0; ; ienv++ ) { /* loop over environ[] strings */
! 12167: if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
! 12168: if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
! 12169: strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
! 12170: if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
! 12171: strcat(environvar,"..."); /* so add an ellipsis */
! 12172: unescape_url(environvar,0); /* convert all %xx's to chars */
! 12173: environptr = strdetex(environvar,1); /* remove/replace any math chars */
! 12174: strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
! 12175: environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
! 12176: strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
! 12177: mimeprep(environvar); /* preprocess environvar string */
! 12178: if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
! 12179: sprintf(environstr+strlen(environstr), /* display environment string */
! 12180: " %2d. %s\\\\\n", ienv+1,environvar);
! 12181: if ( msgfp!= NULL && msglevel>=9 )
! 12182: fprintf(msgfp,"rastenviron> %2d. %.256s\n",
! 12183: ienv+1,/*environ[ienv]*/environvar);
! 12184: if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
! 12185: } /* --- end-of-for(ienv) --- */
! 12186: strcat(environstr,"}}"); /* end {\text{...}} mode */
! 12187: rasterize_environ:
! 12188: environsp = rasterize(environstr,size); /* rasterize environment string */
! 12189: /* --- return environment raster to caller --- */
! 12190: /*end_of_job:*/
! 12191: return ( environsp ); /* return environment to caller */
! 12192: } /* --- end-of-function rastenviron() --- */
! 12193:
! 12194:
! 12195: /* ==========================================================================
! 12196: * Function: rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
! 12197: * Purpose: handle \message
! 12198: * --------------------------------------------------------------------------
! 12199: * Arguments: expression (I/O) char ** to first char of null-terminated
! 12200: * string immediately following \message
! 12201: * and returning ptr immediately
! 12202: * following last character processed.
! 12203: * size (I) int containing 0-7 default font size
! 12204: * basesp (I) subraster * to character (or subexpression)
! 12205: * immediately preceding \mesasge
! 12206: * (unused, but passed for consistency)
! 12207: * arg1 (I) int unused
! 12208: * arg2 (I) int unused
! 12209: * arg3 (I) int unused
! 12210: * --------------------------------------------------------------------------
! 12211: * Returns: ( subraster * ) subraster ptr to rendered message image
! 12212: * --------------------------------------------------------------------------
! 12213: * Notes: o
! 12214: * ======================================================================= */
! 12215: /* --- entry point --- */
! 12216: subraster *rastmessage ( char **expression, int size, subraster *basesp,
! 12217: int arg1, int arg2, int arg3 )
! 12218: {
! 12219: /* -------------------------------------------------------------------------
! 12220: Allocations and Declarations
! 12221: -------------------------------------------------------------------------- */
! 12222: char *texsubexpr(), amsg[256]="\000"; /* message number text */
! 12223: int imsg = 0; /* default message number */
! 12224: char msg[4096];
! 12225: subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */
! 12226: int strreplace(); /*replace SERVER_NAME in refmsgnum*/
! 12227: int reflevels = REFLEVELS; /* #topmost levels to match */
! 12228: char *urlprune(); /*prune referer_match in refmsgnum*/
! 12229: char *strdetex(); /* remove math chars from messages */
! 12230: char *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */
! 12231: *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */
! 12232: *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
! 12233: (!isempty(server_name)?server_name:(NULL))); /* or server_name */
! 12234: /* -------------------------------------------------------------------------
! 12235: obtain message {amsg} argument
! 12236: -------------------------------------------------------------------------- */
! 12237: /* --- parse for {amsg} arg, and bump expression past it --- */
! 12238: *expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
! 12239: /* --- interpret argument --- */
! 12240: if ( *amsg != '\000' ) { /* got amsg arg */
! 12241: imsg = atoi(amsg); /* interpret as an int */
! 12242: if ( imsg < 0 /* if too small */
! 12243: || imsg > maxmsgnum ) /* or too big */
! 12244: imsg = 0; } /* default to first message */
! 12245: /* --- retrieve requested message --- */
! 12246: strninit(msg,msgtable[imsg],4095); /* local copy of message */
! 12247: /* --- process as necessary --- */
! 12248: if ( imsg == refmsgnum) { /* urlncmp() failed to validate */
! 12249: if ( reflevels > 0 ) /* have #levels to validate */
! 12250: strreplace(msg,"SERVER_NAME", /* replace SERVER_NAME */
! 12251: strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
! 12252: } /* --- end-of-switch(imsg) --- */
! 12253: /* --- rasterize requested message --- */
! 12254: messagesp = rasterize(msg,size); /* rasterize message string */
! 12255: /* --- return message raster to caller --- */
! 12256: /*end_of_job:*/
! 12257: return ( messagesp ); /* return message to caller */
! 12258: } /* --- end-of-function rastmessage() --- */
! 12259:
! 12260:
! 12261: /* ==========================================================================
1.1 albertel 12262: * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
12263: * Purpose: no op -- flush \escape without error
12264: * --------------------------------------------------------------------------
12265: * Arguments: expression (I/O) char ** to first char of null-terminated
12266: * string immediately following \escape to be
12267: * flushed, and returning ptr immediately
12268: * following last character processed.
12269: * size (I) int containing 0-5 default font size
12270: * basesp (I) subraster * to character (or subexpression)
1.2 albertel 12271: * immediately preceding \escape
1.1 albertel 12272: * (unused, but passed for consistency)
12273: * nargs (I) int containing number of {}-args after
12274: * \escape to be flushed along with it
12275: * arg2 (I) int unused
12276: * arg3 (I) int unused
12277: * --------------------------------------------------------------------------
12278: * Returns: ( subraster * ) NULL subraster ptr
12279: * --------------------------------------------------------------------------
12280: * Notes: o
12281: * ======================================================================= */
12282: /* --- entry point --- */
12283: subraster *rastnoop ( char **expression, int size, subraster *basesp,
12284: int nargs, int arg2, int arg3 )
12285: {
12286: /* -------------------------------------------------------------------------
12287: Allocations and Declarations
12288: -------------------------------------------------------------------------- */
1.3 albertel 12289: char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
1.1 albertel 12290: subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */
12291: /* --- flush accompanying args if necessary --- */
12292: if ( nargs != NOVALUE /* not unspecified */
12293: && nargs > 0 ) /* and args to be flushed */
12294: while ( --nargs >= 0 ) /* count down */
12295: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
12296: /* --- return null ptr to caller --- */
12297: /*end_of_job:*/
12298: return ( noopsp ); /* return NULL ptr to caller */
12299: } /* --- end-of-function rastnoop() --- */
12300:
12301:
12302: /* ==========================================================================
12303: * Function: rastopenfile ( filename, mode )
12304: * Purpose: Opens filename[.tex] in mode, returning FILE *
12305: * --------------------------------------------------------------------------
12306: * Arguments: filename (I/O) char * to null-terminated string containing
12307: * name of file to open (preceded by path
12308: * relative to mimetex executable)
12309: * If fopen() fails, .tex appeneded,
12310: * and returned if that fopen() succeeds
12311: * mode (I) char * to null-terminated string containing
12312: * fopen() mode
12313: * --------------------------------------------------------------------------
12314: * Returns: ( FILE * ) pointer to opened file, or NULL if error
12315: * --------------------------------------------------------------------------
12316: * Notes: o
12317: * ======================================================================= */
12318: /* --- entry point --- */
12319: FILE *rastopenfile ( char *filename, char *mode )
12320: {
12321: /* -------------------------------------------------------------------------
12322: Allocations and Declarations
12323: -------------------------------------------------------------------------- */
12324: FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
1.3 albertel 12325: char texfile[2050] = "\000", /* local, edited copy of filename */
1.2 albertel 12326: *rasteditfilename(), /* prepend pathprefix if necessary */
1.3 albertel 12327: amode[512] = "r"; /* test open mode if arg mode=NULL */
1.2 albertel 12328: int ismode = 0; /* true of mode!=NULL */
1.1 albertel 12329: /* --------------------------------------------------------------------------
12330: Check mode and open file
12331: -------------------------------------------------------------------------- */
1.2 albertel 12332: /* --- edit filename --- */
1.3 albertel 12333: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
12334: texfile[2047] = '\000'; /* make sure it's null terminated */
1.1 albertel 12335: /* --- check mode --- */
12336: if ( mode != (char *)NULL ) /* caller passed mode arg */
12337: if ( *mode != '\000' ) /* and it's not an empty string */
12338: { ismode = 1; /* so flip mode flag true */
1.3 albertel 12339: strncpy(amode,mode,254); /* and replace "r" with caller's */
12340: amode[254] = '\000'; /* make sure it's null terminated */
1.1 albertel 12341: compress(amode,' '); } /* remove embedded blanks */
12342: /* --- open filename or filename.tex --- */
12343: if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/
12344: if ( (fp = fopen(texfile,amode)) /* try opening given filename */
12345: == NULL ) /* failed to open given filename */
12346: { strcpy(filename,texfile); /* signal possible filename error */
12347: strcat(texfile,".tex"); /* but first try adding .tex */
12348: if ( (fp = fopen(texfile,amode)) /* now try opening filename.tex */
12349: != NULL ) /* filename.tex succeeded */
12350: strcpy(filename,texfile); } /* replace caller's filename */
12351: /* --- close file if only opened to check name --- */
12352: if ( !ismode && fp!=NULL ) /* no mode, so just checking */
12353: fclose(fp); /* close file, fp signals success */
12354: /* --- return fp or NULL to caller --- */
12355: /*end_of_job:*/
12356: if ( msglevel>=9 && msgfp!=NULL ) /* debuging */
12357: { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
12358: filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
12359: return ( fp ); /* return fp or NULL to caller */
12360: } /* --- end-of-function rastopenfile() --- */
12361:
12362:
12363: /* ==========================================================================
1.2 albertel 12364: * Function: rasteditfilename ( filename )
12365: * Purpose: edits filename to remove security problems,
12366: * e.g., removes all ../'s and ..\'s.
12367: * --------------------------------------------------------------------------
12368: * Arguments: filename (I) char * to null-terminated string containing
12369: * name of file to be edited
12370: * --------------------------------------------------------------------------
12371: * Returns: ( char * ) pointer to edited filename,
12372: * or empty string "\000" if any problem
12373: * --------------------------------------------------------------------------
12374: * Notes: o
12375: * ======================================================================= */
12376: /* --- entry point --- */
12377: char *rasteditfilename ( char *filename )
12378: {
12379: /* -------------------------------------------------------------------------
12380: Allocations and Declarations
12381: -------------------------------------------------------------------------- */
1.3 albertel 12382: static char editname[2050]; /*edited filename returned to caller*/
1.2 albertel 12383: char *strchange(); /* prepend pathprefix if necessary */
12384: int strreplace(), /* remove ../'s and ..\'s */
12385: isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
12386: /* --------------------------------------------------------------------------
12387: edit filename
12388: -------------------------------------------------------------------------- */
12389: /* --- first check filename arg --- */
12390: *editname = '\000'; /* init edited name as empty string*/
12391: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
12392: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
12393: /* --- init edited filename --- */
12394: strcpy(editname,filename); /* init edited name as input name */
12395: compress(editname,' '); /* remove embedded blanks */
12396: /* --- remove leading or embedded ....'s --- */
12397: while ( strreplace(editname,"....",NULL,0) > 0 ) ; /* squeeze out ....'s */
12398: /* --- remove leading / and \ and dots (and blanks) --- */
12399: if ( *editname != '\000' ) /* still have chars in filename */
12400: while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
1.5 ! raeburn 12401: {strsqueeze(editname,1);} /* so flush leading / or \ (or ' ')*/
1.2 albertel 12402: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
12403: /* --- remove leading or embedded ../'s and ..\'s --- */
12404: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */
12405: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
12406: while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* and ../'s again */
12407: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
12408: if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
12409: strchange(0,editname,pathprefix); /* so prepend prefix */
12410: end_of_job:
12411: return ( editname ); /* back with edited filename */
12412: } /* --- end-of-function rasteditfilename() --- */
12413:
12414:
12415: /* ==========================================================================
12416: * Function: rastreadfile ( filename, islock, tag, value )
1.1 albertel 12417: * Purpose: Read filename, returning value as string
12418: * between <tag>...</tag> or entire file if tag=NULL passed.
12419: * --------------------------------------------------------------------------
12420: * Arguments: filename (I) char * to null-terminated string containing
12421: * name of file to read (preceded by path
12422: * relative to mimetex executable)
1.2 albertel 12423: * islock (I) int containing 1 to lock file while reading
12424: * (hopefully done by opening in "r+" mode)
1.1 albertel 12425: * tag (I) char * to null-terminated string containing
12426: * html-like tagname. File contents between
12427: * <tag> and </tag> will be returned, or
12428: * entire file if tag=NULL passed.
12429: * value (O) char * returning value between <tag>...</tag>
12430: * or entire file if tag=NULL.
12431: * --------------------------------------------------------------------------
12432: * Returns: ( int ) 1=okay, 0=some error
12433: * --------------------------------------------------------------------------
12434: * Notes: o
12435: * ======================================================================= */
12436: /* --- entry point --- */
1.2 albertel 12437: int rastreadfile ( char *filename, int islock, char *tag, char *value )
1.1 albertel 12438: {
12439: /* -------------------------------------------------------------------------
12440: Allocations and Declarations
12441: -------------------------------------------------------------------------- */
12442: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3 albertel 12443: char texfile[1024] = "\000", /* local copy of input filename */
12444: text[MAXLINESZ+1]; /* line from input file */
12445: char *tagp, tag1[1024], tag2[1024]; /* left <tag> and right <tag/> */
12446: int vallen=0, maxvallen=MAXFILESZ; /* #chars in value, max allowed */
1.2 albertel 12447: int status = (-1); /* status returned, 1=okay */
1.1 albertel 12448: int tagnum = 0; /* tag we're looking for */
1.2 albertel 12449: /*int islock = 1;*/ /* true to lock file */
1.1 albertel 12450: /* --------------------------------------------------------------------------
12451: Open file
12452: -------------------------------------------------------------------------- */
12453: /* --- first check output arg --- */
12454: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
12455: *value = '\000'; /* init buffer with empty string */
12456: /* --- open filename or filename.tex --- */
12457: if ( filename != (char *)NULL ) /* make sure we got filename arg */
1.3 albertel 12458: { strncpy(texfile,filename,1023); /* local copy of filename */
12459: texfile[1023] = '\000'; /* make sure it's null terminated */
1.2 albertel 12460: fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
1.1 albertel 12461: /* --- check that file opened --- */
12462: if ( fp == (FILE *)NULL ) /* failed to open file */
12463: { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
12464: goto end_of_job; } /* return error message to caller */
1.2 albertel 12465: status = 0; /* file opened successfully */
12466: if ( islock ) rewind(fp); /* start at beginning of file */
1.1 albertel 12467: /* --------------------------------------------------------------------------
12468: construct <tag>'s
12469: -------------------------------------------------------------------------- */
12470: if ( tag != (char *)NULL ) /* caller passed tag arg */
12471: if ( *tag != '\000' ) /* and it's not an empty string */
12472: { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
12473: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
12474: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
12475: compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
12476: tagnum = 1; } /* signal that we have tag */
12477: /* --------------------------------------------------------------------------
12478: Read file, concatnate lines
12479: -------------------------------------------------------------------------- */
1.3 albertel 12480: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
1.1 albertel 12481: switch ( tagnum ) { /* look for left- or right-tag */
1.2 albertel 12482: case 0: status = 1; break; /* no tag to look for */
1.1 albertel 12483: case 1: /* looking for opening left <tag> */
12484: if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
1.5 ! raeburn 12485: tagp += strlen(tag1); /* first char past tag */
! 12486: strsqueezep(text,tagp); /*shift out preceding text and tag*/
1.1 albertel 12487: tagnum = 2; /*now looking for closing right tag*/
12488: case 2: /* looking for closing right </tag> */
12489: if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
12490: *tagp = '\000'; /* terminate line at tag */
12491: tagnum = 3; /* done after this line */
1.2 albertel 12492: status = 1; /* successfully read tag */
1.1 albertel 12493: break;
12494: } /* ---end-of-switch(tagnum) --- */
12495: if ( tagnum != 1 ) { /* no tag or left tag already found*/
12496: int textlen = strlen(text); /* #chars in current line */
12497: if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
12498: strcat(value,text); /* concat line to end of value */
12499: vallen += textlen; /* bump length */
12500: if ( tagnum > 2 ) break; } /* found right tag, so we're done */
12501: } /* --- end-of-while(fgets()!=NULL) --- */
12502: if ( tagnum<1 || tagnum>2 ) status=1; /* okay if no tag or we found tag */
12503: fclose ( fp ); /* close input file after reading */
12504: /* --- return value and status to caller --- */
12505: end_of_job:
12506: return ( status ); /* return status to caller */
12507: } /* --- end-of-function rastreadfile() --- */
12508:
12509:
12510: /* ==========================================================================
12511: * Function: rastwritefile ( filename, tag, value, isstrict )
12512: * Purpose: Re/writes filename, replacing string between <tag>...</tag>
12513: * with value, or writing entire file as value if tag=NULL.
12514: * --------------------------------------------------------------------------
12515: * Arguments: filename (I) char * to null-terminated string containing
12516: * name of file to write (preceded by path
12517: * relative to mimetex executable)
12518: * tag (I) char * to null-terminated string containing
12519: * html-like tagname. File contents between
12520: * <tag> and </tag> will be replaced, or
12521: * entire file written if tag=NULL passed.
12522: * value (I) char * containing string replacing value
12523: * between <tag>...</tag> or replacing entire
12524: * file if tag=NULL.
12525: * isstrict (I) int containing 1 to only rewrite existing
12526: * files, or 0 to create new file if necessary.
12527: * --------------------------------------------------------------------------
12528: * Returns: ( int ) 1=okay, 0=some error
12529: * --------------------------------------------------------------------------
12530: * Notes: o
12531: * ======================================================================= */
12532: /* --- entry point --- */
12533: int rastwritefile( char *filename, char *tag, char *value, int isstrict )
12534: {
12535: /* -------------------------------------------------------------------------
12536: Allocations and Declarations
12537: -------------------------------------------------------------------------- */
12538: FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3 albertel 12539: char texfile[1024] = "\000", /* local copy of input filename */
12540: filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */
12541: tag1[1024], tag2[1024], /* left <tag> and right <tag/> */
1.1 albertel 12542: *strchange(), /* put value between <tag>...</tag>*/
12543: *timestamp(); /* log modification time */
12544: int istag=0, rastreadfile(), /* read file if tag!=NULL */
12545: /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
12546: isnewfile = 0, /* true if writing new file */
12547: status = 0; /* status returned, 1=okay */
1.2 albertel 12548: int istimestamp = 0; /* true to update <timestamp> tag */
1.1 albertel 12549: /* --------------------------------------------------------------------------
12550: check args
12551: -------------------------------------------------------------------------- */
12552: /* --- check filename and value --- */
12553: if ( filename == (char *)NULL /* quit if no filename arg supplied*/
12554: || value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
12555: if ( strlen(filename) < 2 /* quit if unreasonable filename */
12556: || *value == '\000' ) goto end_of_job; /* or empty value string supplied */
12557: /* --- establish filename[.tex] --- */
1.3 albertel 12558: strncpy(texfile,filename,1023); /* local copy of input filename */
12559: texfile[1023] = '\000'; /* make sure it's null terminated */
1.1 albertel 12560: if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */
12561: == (FILE *)NULL ) /* can't open, so write new file */
12562: { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */
12563: isnewfile = 1; } /* signal we're writing new file */
12564: /* --- check whether tag supplied by caller --- */
12565: if ( tag != (char *)NULL ) /* caller passed tag argument */
12566: if ( *tag != '\000' ) /* and it's not an empty string */
12567: { istag = 1; /* so flip tag flag true */
12568: strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin tags with < and </ */
12569: strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
12570: strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
12571: compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
12572: /* --------------------------------------------------------------------------
12573: read existing file if just rewriting a single tag
12574: -------------------------------------------------------------------------- */
12575: /* --- read original file if only replacing a tag within it --- */
12576: *filebuff = '\000'; /* init as empty file */
12577: if ( !isnewfile ) /* if file already exists */
12578: if ( istag ) /* and just rewriting one tag */
1.2 albertel 12579: if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
12580: <= 0 ) goto end_of_job; /* signal error if failed to read */
1.1 albertel 12581: /* --------------------------------------------------------------------------
12582: construct new file data if needed (entire file replaced by value if no tag)
12583: -------------------------------------------------------------------------- */
12584: if ( istag ) /* only replacing tag in file */
12585: {
12586: /* --- find <tag> and </tag> in file --- */
12587: int tlen1=strlen(tag1), tlen2=strlen(tag2), flen; /*tag,buff lengths*/
12588: char *tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
12589: *tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
12590: /* --- if adding new <tag> just concatanate at end of file --- */
12591: if ( tagp1 == (char *)NULL ) /* add new tag to file */
12592: {
12593: /* --- preprocess filebuff --- */
12594: if ( tagp2 != (char *)NULL ) /* apparently have ...</tag> */
1.5 ! raeburn 12595: {strsqueezep(filebuff,tagp2+tlen2);} /* remove ...</tag> */
1.1 albertel 12596: if ( (flen = strlen(filebuff)) /* #chars currently in buffer */
12597: > 0 ) /* we have non-empty buffer */
12598: if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
12599: if(0)strcat(filebuff,"\n"); /* so add one before new tag */
12600: /* --- add new tag --- */
12601: strcat(filebuff,tag1); /* add opening <tag> */
12602: strcat(filebuff,value); /* then value */
12603: strcat(filebuff,tag2); /* finally closing </tag> */
12604: strcat(filebuff,"\n"); /* newline at end of file */
12605: } /* --- end-of-if(tagp1==NULL) --- */
12606: else /* found existing opening <tag> */
12607: {
12608: if ( tagp2 == NULL ) /* apparently have <tag>... */
12609: { *(tagp1+tlen1) = '\000'; /* so get rid of trailing ... */
12610: strcat(filebuff,value); /* then concatanate value */
12611: strcat(filebuff,tag2); } /* and finally closing </tag> */
12612: else /* else have <tag>...<tag/> */
12613: if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
12614: >= 0 ) /* usually <tag> precedes </tag> */
12615: strchange(flen,tagp1+tlen1,value); /* change ...'s to value */
12616: else /* weirdly, </tag> precedes <tag> */
1.3 albertel 12617: { char fbuff[4096]; /* field buff for <tag>value</tag> */
1.1 albertel 12618: if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
12619: <= 0 ) goto end_of_job; /* must be internal error */
12620: strcpy(fbuff,tag1); /* set opening <tag> */
12621: strcat(fbuff,value); /* then value */
12622: strcat(fbuff,tag2); /* finally closing </tag> */
12623: strchange(flen,tagp2,fbuff); } /* replace original </tag>...<tag> */
12624: } /* --- end-of-if/else(tagp1==NULL) --- */
12625: } /* --- end-of-if(istag) --- */
12626: /* --------------------------------------------------------------------------
12627: rewrite file and return to caller
12628: -------------------------------------------------------------------------- */
12629: /* --- first open file for write --- */
12630: if ( (fp=rastopenfile(texfile,"w")) /* open for write */
12631: == (FILE *)NULL ) goto end_of_job; /* signal error if can't open */
12632: /* --- rewrite and close file --- */
12633: if ( fputs((istag?filebuff:value),fp) /* write filebuff or value */
12634: != EOF ) status = 1; /* signal success if succeeded */
12635: fclose ( fp ); /* close output file after writing */
12636: /* --- modify timestamp --- */
1.2 albertel 12637: if ( status > 0 ) /*forget timestamp if write failed*/
12638: if ( istimestamp ) /* if we're updating timestamp */
12639: if ( istag ) /* only log time in tagged file */
12640: if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
12641: { char fbuff[2048]; /* field buff <timestamp> value */
12642: strcpy(fbuff,tag); /* tag modified */
12643: strcat(fbuff," modified at "); /* spacer */
12644: strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
12645: status = rastwritefile(filename,"timestamp",fbuff,1); }
1.1 albertel 12646: /* --- return status to caller --- */
12647: end_of_job:
12648: return ( status ); /* return status to caller */
12649: } /* --- end-of-function rastwritefile() --- */
12650:
12651:
12652: /* ==========================================================================
1.2 albertel 12653: * Function: calendar ( year, month, day )
12654: * Purpose: returns null-terminated character string containing
12655: * \begin{array}...\end{array} for the one-month calendar
12656: * specified by year=1973...2099 and month=1...12.
12657: * If either arg out-of-range, today's value is used.
12658: * --------------------------------------------------------------------------
12659: * Arguments: year (I) int containing 1973...2099 or 0 for current
12660: * year
12661: * month (I) int containing 1...12 or 0 for current month
12662: * day (I) int containing day to emphasize or 0
12663: * --------------------------------------------------------------------------
12664: * Returns: ( char * ) char ptr to null-terminated buffer
12665: * containing \begin{array}...\end{array}
12666: * string that will render calendar for
12667: * requested month, or NULL for any error.
12668: * --------------------------------------------------------------------------
12669: * Notes: o
12670: * ======================================================================= */
12671: /* --- entry point --- */
12672: char *calendar( int year, int month, int day )
12673: {
12674: /* -------------------------------------------------------------------------
12675: Allocations and Declarations
12676: -------------------------------------------------------------------------- */
12677: static char calbuff[4096]; /* calendar returned to caller */
12678: time_t time_val = (time_t)(0); /* binary value returned by time() */
12679: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
12680: int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
12681: int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
12682: char aval[64]; /* ascii day or 4-digit year */
12683: /* --- calendar data --- */
12684: static char *monthnames[] = { "?", "January", "February", "March", "April",
12685: "May", "June", "July", "August", "September", "October",
12686: "November", "December", "?" } ;
12687: static int modays[] =
12688: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
12689: /* -------------------------------------------------------------------------
12690: initialization
12691: -------------------------------------------------------------------------- */
12692: /* --- get current date/time --- */
12693: time((time_t *)(&time_val)); /* get date and time */
12694: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
12695: yy = 1900 + (int)(tmstruct->tm_year); /* current four-digit year */
12696: mm = 1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
12697: dd = (int)(tmstruct->tm_mday); /* current day, 1-31 */
12698: /* --- check args --- */
12699: if ( year<1973 || year>2099 ) year = yy; /* current year if out-of-bounds */
12700: if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
12701: if ( month==mm && year==yy && day==0 ) /* current month and default day */
12702: day = dd; /* emphasize current day */
12703: modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
12704: /* --- initialize calendar string --- */
12705: strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
12706: strcat(calbuff,"\\small\\text{"); /* month set in roman */
12707: strcat(calbuff,monthnames[month]); /* insert month name */
12708: strcat(calbuff," }"); /* add a space */
12709: sprintf(aval,"%d",year); /* convert year to ascii */
12710: strcat(calbuff,aval); /* add year */
12711: strcat(calbuff,"\\\\"); /* end top row */
12712: strcat(calbuff, /* now begin calendar arrayr */
12713: "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
12714: "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
12715: "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
12716: "\\tiny\\text{Sat} \\\\ \\hline " );
12717: /* -------------------------------------------------------------------------
12718: generate calendar
12719: -------------------------------------------------------------------------- */
12720: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
12721: {
12722: /* --- get day-of-week for this day --- */
12723: iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
12724: if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
12725: /* --- may need empty cells at beginning of month --- */
12726: if ( idd == 1 ) /* first day of month */
12727: if ( iday > 0 ) /* need to skip cells */
12728: { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
12729: aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
12730: strcat(calbuff,aval); } /* add skip string to buffer */
12731: /* --- add idd to current cell --- */
12732: sprintf(aval,"%d",idd); /* convert idd to ascii */
12733: if ( idd == day /* emphasize today's date */
12734: /*&& month==mm && year==yy*/ ) /* only if this month's calendar */
12735: { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
12736: strcat(calbuff,aval); /* put in idd */
12737: strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
12738: else /* not today's date */
12739: strcat(calbuff,aval); /* so just put in idd */
12740: /* --- terminate cell --- */
1.3 albertel 12741: if ( idd < modays[month] ) { /* not yet end-of-month */
1.2 albertel 12742: if ( iday < 6 ) /* still have days left in week */
12743: strcat(calbuff,"&"); /* new cell in same week */
12744: else /* reached end-of-week */
1.3 albertel 12745: strcat(calbuff,"\\\\ \\hline"); } /* so start new week */
1.2 albertel 12746: } /* --- end-of-for(idd) --- */
12747: strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
12748: /* --- return calendar to caller --- */
12749: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
12750: return ( calbuff ); /* back to caller with calendar */
12751: } /* --- end-of-function calendar() --- */
12752:
12753:
12754: /* ==========================================================================
12755: * Function: timestamp ( tzdelta, ifmt )
1.1 albertel 12756: * Purpose: returns null-terminated character string containing
12757: * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
12758: * --------------------------------------------------------------------------
1.2 albertel 12759: * Arguments: tzdelta (I) integer, positive or negative, containing
12760: * containing number of hours to be added or
12761: * subtracted from system time (to accommodate
12762: * your desired time zone).
12763: * ifmt (I) integer containing 0 for default format
1.1 albertel 12764: * --------------------------------------------------------------------------
12765: * Returns: ( char * ) ptr to null-terminated buffer
12766: * containing current date:time stamp
12767: * --------------------------------------------------------------------------
12768: * Notes: o
12769: * ======================================================================= */
12770: /* --- entry point --- */
1.2 albertel 12771: char *timestamp( int tzdelta, int ifmt )
1.1 albertel 12772: {
12773: /* -------------------------------------------------------------------------
12774: Allocations and Declarations
12775: -------------------------------------------------------------------------- */
1.2 albertel 12776: static char timebuff[256]; /* date:time buffer back to caller */
1.1 albertel 12777: /*long time_val = 0L;*/ /* binary value returned by time() */
12778: time_t time_val = (time_t)(0); /* binary value returned by time() */
12779: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
1.2 albertel 12780: int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
1.5 ! raeburn 12781: month=0, day=0, /* adjust day and month for delta */
! 12782: minute=0,second=0; /* minute and second not adjusted */
1.2 albertel 12783: int tzadjust(); /* time zone adjustment function */
12784: int daynumber(); /* #days since Jan 1, 1973 */
12785: static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
12786: "Thursday", "Friday", "Saturday", "Sunday" } ;
12787: static char *monthnames[] = { "?", "January", "February", "March", "April",
12788: "May", "June", "July", "August", "September", "October",
12789: "November", "December", "?" } ;
1.1 albertel 12790: /* -------------------------------------------------------------------------
12791: get current date:time, adjust values, and and format stamp
12792: -------------------------------------------------------------------------- */
1.2 albertel 12793: /* --- first init returned timebuff in case of any error --- */
12794: *timebuff = '\000';
1.1 albertel 12795: /* --- get current date:time --- */
12796: time((time_t *)(&time_val)); /* get date and time */
12797: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
1.2 albertel 12798: /* --- extract fields --- */
12799: year = (int)(tmstruct->tm_year); /* local copy of year, 0=1900 */
12800: month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
12801: day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */
12802: hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */
1.5 ! raeburn 12803: minute= (int)(tmstruct->tm_min); /* local copy of minute,0-59 */
! 12804: second= (int)(tmstruct->tm_sec); /* local copy of second,0-59 */
1.2 albertel 12805: /* --- adjust year --- */
1.1 albertel 12806: year += 1900; /* set century in year */
1.2 albertel 12807: /* --- adjust for timezone --- */
12808: tzadjust(tzdelta,&year,&month,&day,&hour);
12809: /* --- check params --- */
12810: if ( hour<0 || hour>23
12811: || day<1 || day>31
12812: || month<1 || month>12
12813: || year<1973 ) goto end_of_job;
12814: /* --- adjust hour for am/pm --- */
12815: switch ( ifmt )
12816: {
12817: default:
12818: case 0:
12819: if ( hour < 12 ) /* am check */
12820: { ispm=0; /* reset pm flag */
12821: if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
12822: if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
12823: break;
12824: } /* --- end-of-switch(ifmt) --- */
1.1 albertel 12825: /* --- format date:time stamp --- */
1.2 albertel 12826: switch ( ifmt )
12827: {
12828: default:
12829: case 0: /* --- 2005-03-05:11:49:59am --- */
12830: sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
1.5 ! raeburn 12831: hour,minute,second,((ispm)?"pm":"am"));
1.2 albertel 12832: break;
12833: case 1: /* --- Saturday, March 5, 2005 --- */
12834: sprintf(timebuff,"%s, %s %d, %d",
12835: daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
12836: break;
12837: case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
12838: sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
12839: daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
1.5 ! raeburn 12840: hour,minute,second,((ispm)?"pm":"am"));
1.2 albertel 12841: break;
12842: case 3: /* --- 11:49:59am --- */
12843: sprintf(timebuff,"%d:%02d:%02d%s",
1.5 ! raeburn 12844: hour,minute,second,((ispm)?"pm":"am"));
! 12845: break;
! 12846: case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
! 12847: sprintf(timebuff,"%d%02d%02d%02d%02d",
! 12848: month,day,hour,minute,second);
1.2 albertel 12849: break;
12850: } /* --- end-of-switch(ifmt) --- */
12851: end_of_job:
12852: return ( timebuff ); /* return stamp to caller */
1.1 albertel 12853: } /* --- end-of-function timestamp() --- */
12854:
12855:
12856: /* ==========================================================================
1.2 albertel 12857: * Function: tzadjust ( tzdelta, year, month, day, hour )
12858: * Purpose: Adjusts hour, and day,month,year if necessary,
12859: * by delta increment to accommodate your time zone.
12860: * --------------------------------------------------------------------------
12861: * Arguments: tzdelta (I) integer, positive or negative, containing
12862: * containing number of hours to be added or
12863: * subtracted from given time (to accommodate
12864: * your desired time zone).
12865: * year (I) addr of int containing 4-digit year
12866: * month (I) addr of int containing month 1=Jan - 12=Dec.
12867: * day (I) addr of int containing day 1-31 for Jan.
12868: * hour (I) addr of int containing hour 0-23
12869: * Returns: ( int ) 1 for success, or 0 for error
12870: * --------------------------------------------------------------------------
12871: * Notes: o
12872: * ======================================================================= */
12873: /* --- entry point --- */
12874: int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
12875: {
12876: /* --------------------------------------------------------------------------
12877: Allocations and Declarations
12878: -------------------------------------------------------------------------- */
12879: int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
12880: /* --- calendar data --- */
12881: static int modays[] =
12882: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
12883: /* --------------------------------------------------------------------------
12884: check args
12885: -------------------------------------------------------------------------- */
12886: if ( mm<1 || mm>12 ) return(-1); /* bad month */
12887: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
12888: if ( hh<0 || hh>23 ) return(-1); /* bad hour */
12889: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
12890: /* --------------------------------------------------------------------------
12891: make adjustments
12892: -------------------------------------------------------------------------- */
12893: /* --- adjust hour --- */
12894: hh += tzdelta; /* apply caller's delta */
12895: /* --- adjust for feb 29 --- */
12896: modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
12897: /* --- adjust day --- */
12898: if ( hh < 0 ) /* went to preceding day */
12899: { dd--; hh += 24; }
12900: if ( hh > 23 ) /* went to next day */
12901: { dd++; hh -= 24; }
12902: /* --- adjust month --- */
12903: if ( dd < 1 ) /* went to preceding month */
12904: { mm--; dd = modays[mm]; }
12905: if ( dd > modays[mm] ) /* went to next month */
12906: { mm++; dd = 1; }
12907: /* --- adjust year --- */
12908: if ( mm < 1 ) /* went to preceding year */
12909: { yy--; mm = 12; dd = modays[mm]; }
12910: if ( mm > 12 ) /* went to next year */
12911: { yy++; mm = 1; dd = 1; }
12912: /* --- back to caller --- */
12913: *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
12914: return ( 1 );
12915: } /* --- end-of-function tzadjust() --- */
12916:
12917:
12918: /* ==========================================================================
12919: * Function: daynumber ( year, month, day )
12920: * Purpose: Returns number of actual calendar days from Jan 1, 1973
12921: * to the given date (e.g., bvdaynumber(1974,1,1)=365).
12922: * --------------------------------------------------------------------------
12923: * Arguments: year (I) int containing year -- may be either 1995 or
12924: * 95, or may be either 2010 or 110 for those
12925: * years.
12926: * month (I) int containing month, 1=Jan thru 12=Dec.
12927: * day (I) int containing day of month, 1-31 for Jan, etc.
12928: * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
12929: * or -1 for error (e.g., year<1973).
12930: * --------------------------------------------------------------------------
12931: * Notes: o
12932: * ======================================================================= */
12933: /* --- entry point --- */
12934: int daynumber ( int year, int month, int day )
12935: {
12936: /* --------------------------------------------------------------------------
12937: Allocations and Declarations
12938: -------------------------------------------------------------------------- */
12939: /* --- returned value (note: returned as a default "int") --- */
12940: int ndays; /* #days since jan 1, year0 */
12941: /* --- initial conditions --- */
12942: static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
12943: days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
12944: days1yr = 365;
12945: /* --- table of accumulated days per month (last index not used) --- */
12946: static int modays[] =
12947: { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
12948: /* --- variables for #days since day0 --- */
12949: int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
12950: /* --------------------------------------------------------------------------
12951: Check input
12952: -------------------------------------------------------------------------- */
12953: if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
12954: return ( -1 ); /* otherwise, forget it */
12955: if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
12956: /* --------------------------------------------------------------------------
12957: Find #days since jan 1, 1973
12958: -------------------------------------------------------------------------- */
12959: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
12960: nyears = year - year0; /* #years since year0 */
12961: if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
12962: nfouryrs = nyears/4; /* #complete four-year periods */
12963: nyears -= (4*nfouryrs); /* remainder excluding current year*/
12964: /* --- #days from jan 1, year0 till jan 1, this year --- */
12965: ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
12966: + (days1yr*nyears); /* +remaining days */
12967: /*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
12968: /* --- add #days within current year --- */
12969: ndays += (modays[month-1] + (day-1));
12970: /* --- may need an extra day if current year is a leap year --- */
12971: if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
12972: { if ( month > 2 ) /* past feb so need an extra day */
12973: /*if ( year != 100 )*/ /* unless it's 2000AD */
12974: ndays++; } /* so add it in */
12975: return ( (int)(ndays) ); /* #days back to caller */
12976: } /* --- end-of-function daynumber() --- */
12977:
12978:
12979: /* ==========================================================================
1.5 ! raeburn 12980: * Function: strwrap ( s, linelen, tablen )
! 12981: * Purpose: Inserts \n's and spaces in (a copy of) s to wrap lines
! 12982: * at linelen and indent them by tablen.
! 12983: * --------------------------------------------------------------------------
! 12984: * Arguments: s (I) char * to null-terminated string
! 12985: * to be wrapped.
! 12986: * linelen (I) int containing maximum linelen
! 12987: * between \\'s.
! 12988: * tablen (I) int containing number of spaces to indent
! 12989: * lines. 0=no indent. Positive means
! 12990: * only indent first line and not others.
! 12991: * Negative means indent all lines except first.
! 12992: * --------------------------------------------------------------------------
! 12993: * Returns: ( char * ) ptr to "line-wrapped" copy of s
! 12994: * or "" (empty string) for any error.
! 12995: * --------------------------------------------------------------------------
! 12996: * Notes: o The returned copy of s has embedded \\'s as necessary
! 12997: * to wrap lines at linelen. Any \\'s in the input copy
! 12998: * are removed first. If (and only if) the input s contains
! 12999: * a terminating \\ then so does the returned copy.
! 13000: * o The returned pointer addresses a static buffer,
! 13001: * so don't call strwrap() again until you're finished
! 13002: * with output from the preceding call.
! 13003: * o Modified for mimetex from original version written
! 13004: * for mathtex (where \n in verbatim mode instead of \\
! 13005: * produced linebreaks).
! 13006: * ======================================================================= */
! 13007: /* --- entry point --- */
! 13008: char *strwrap ( char *s, int linelen, int tablen )
! 13009: {
! 13010: /* -------------------------------------------------------------------------
! 13011: Allocations and Declarations
! 13012: -------------------------------------------------------------------------- */
! 13013: static char sbuff[4096]; /* line-wrapped copy of s */
! 13014: char *sol = sbuff; /* ptr to start of current line*/
! 13015: char tab[32] = " "; /* tab string */
! 13016: int strreplace(); /* remove \n's */
! 13017: char *strchange(); /* add \n's and indent space */
! 13018: int finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
! 13019: int istab = (tablen>0?1:0), /* init true to indent first line */
! 13020: iswhite = 0; /* true if line break on whitespace*/
! 13021: int rhslen = 0, /* remaining right hand side length*/
! 13022: thislen = 0, /* length of current line segment */
! 13023: thistab = 0, /* length of tab on current line */
! 13024: wordlen = 0; /* length to next whitespace char */
! 13025: /* -------------------------------------------------------------------------
! 13026: Make a clean copy of s
! 13027: -------------------------------------------------------------------------- */
! 13028: /* --- check input --- */
! 13029: *sbuff = '\000'; /* initialize in case of error */
! 13030: if ( isempty(s) ) goto end_of_job; /* no input */
! 13031: if ( tablen < 0 ) tablen = (-tablen); /* set positive tablen */
! 13032: if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
! 13033: tab[min2(tablen,16)] = '\000'; /* null-terminate tab string */
! 13034: tablen = strlen(tab); /* reset to actual tab length */
! 13035: finalnewline = 0; /* turned off for mimetex version */
! 13036: /* --- start with copy of s --- */
! 13037: strninit(sbuff,s,3000); /* leave room for \n's and tabs */
! 13038: if ( linelen < 1 ) goto end_of_job; /* can't do anything */
! 13039: trimwhite(sbuff); /*remove leading/trailing whitespace*/
! 13040: strreplace(sbuff,"\n"," ",0); /* remove any original \n's */
! 13041: strreplace(sbuff,"\r"," ",0); /* remove any original \r's */
! 13042: strreplace(sbuff,"\t"," ",0); /* remove any original \t's */
! 13043: strreplace(sbuff,"\f"," ",0); /* remove any original \f's */
! 13044: strreplace(sbuff,"\v"," ",0); /* remove any original \v's */
! 13045: strreplace(sbuff,"\\\\"," ",0); /* remove any original \\'s */
! 13046: /* -------------------------------------------------------------------------
! 13047: Insert \\'s and spaces as needed
! 13048: -------------------------------------------------------------------------- */
! 13049: while ( 1 ) { /* till end-of-line */
! 13050: /* --- init --- */
! 13051: trimwhite(sol); /*remove leading/trailing whitespace*/
! 13052: thislen = thistab = 0; /* no chars in current line yet */
! 13053: if ( istab && tablen>0 ) { /* need to indent this line */
! 13054: strchange(0,sol,tab); /* insert indent at start of line */
! 13055: thistab = tablen; } /* line starts with whitespace tab */
! 13056: if ( sol == sbuff ) istab = 1-istab; /* flip tab flag after first line */
! 13057: sol += thistab; /* skip tab */
! 13058: rhslen = strlen(sol); /* remaining right hand side chars */
! 13059: if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
! 13060: if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
! 13061: fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
! 13062: fflush(msgfp); }
! 13063: /* --- look for last whitespace preceding linelen --- */
! 13064: while ( 1 ) { /* till we exceed linelen */
! 13065: wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/
! 13066: if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
! 13067: goto end_of_job; /* so nothing more we can do */
! 13068: if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
! 13069: if ( thislen > 0 ) break; /* but make sure line has one word */
! 13070: thislen += (wordlen+1); } /* ptr past next whitespace char */
! 13071: if ( thislen < 1 ) break; /* line will have one too-long word*/
! 13072: /*sol[thislen-1] = '\n';*/ /* replace last space with newline */
! 13073: /*sol += thislen;*/ /* next line starts after newline */
! 13074: iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
! 13075: strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
! 13076: sol += (thislen+2-iswhite); /* next line starts after \\ */
! 13077: } /* --- end-of-while(1) --- */
! 13078: end_of_job:
! 13079: if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
! 13080: return ( sbuff ); /* back with clean copy of s */
! 13081: } /* --- end-of-function strwrap() --- */
! 13082:
! 13083:
! 13084: /* ==========================================================================
! 13085: * Function: strnlower ( s, n )
! 13086: * Purpose: lowercase the first n chars of string s
! 13087: * --------------------------------------------------------------------------
! 13088: * Arguments: s (I/O) (char *)pointer to null-terminated string
! 13089: * whose chars are to be lowercased
! 13090: * n (I) int containing max number of chars to be
! 13091: * lowercased (less than n will be lowercased
! 13092: * if terminating '\000' found first)
! 13093: * If n<=0 (or n>=strlen(s)) then the entire
! 13094: * string s will be lowercased
! 13095: * --------------------------------------------------------------------------
! 13096: * Returns: ( char * ) s (always same as input)
! 13097: * --------------------------------------------------------------------------
! 13098: * Notes: o
! 13099: * ======================================================================= */
! 13100: /* --- entry point --- */
! 13101: char *strnlower ( char *s, int n )
! 13102: {
! 13103: /* -------------------------------------------------------------------------
! 13104: lowercase s
! 13105: -------------------------------------------------------------------------- */
! 13106: char *p = s; /* save s for return to caller */
! 13107: if ( !isempty(s) ) /* check for valid input */
! 13108: while ( *p != '\000' ) { /* lowercase each char till end */
! 13109: *p = tolower(*p); /* lowercase this char */
! 13110: if ( n > 0 ) /* only lowercase first n chars */
! 13111: if ( --n < 1 ) break; /* quit when we're done */
! 13112: p++; } /* proceed to next char */
! 13113: return ( s ); /* back to caller with s */
! 13114: } /* --- end-of-function strnlower() --- */
! 13115:
! 13116:
! 13117: /* ==========================================================================
! 13118: * Function: urlprune ( url, n )
! 13119: * Purpose: Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
! 13120: * (if n=2 only ghi.com is returned, or if n=-1 only "ghi")
! 13121: * --------------------------------------------------------------------------
! 13122: * Arguments: url (I) char * to null-terminated string
! 13123: * containing url to be pruned
! 13124: * n (i) int containing number of levels retained
! 13125: * in pruned url. If n<0 its abs() is used,
! 13126: * but the topmost level (usually .com, .org,
! 13127: * etc) is omitted. That is, if n=2 would
! 13128: * return "ghi.com" then n=-1 returns "ghi".
! 13129: * n=0 retains all levels.
! 13130: * --------------------------------------------------------------------------
! 13131: * Returns: ( char * ) pointer to (static) null-terminated string
! 13132: * containing pruned url with the first n
! 13133: * top-level domain, e.g., for n=2,
! 13134: * http://abc.def.ghi.com/etc returns ghi.com,
! 13135: * or an empty string "\000" for any error
! 13136: * --------------------------------------------------------------------------
! 13137: * Notes: o
! 13138: * ======================================================================= */
! 13139: /* --- entry point --- */
! 13140: char *urlprune ( char *url, int n )
! 13141: {
! 13142: /* -------------------------------------------------------------------------
! 13143: Allocations and Declarations
! 13144: -------------------------------------------------------------------------- */
! 13145: static char pruned[2048]; /* pruned url returned to caller */
! 13146: char *purl = /*NULL*/pruned; /* ptr to pruned, init for error */
! 13147: char *delim = NULL; /* delimiter separating components */
! 13148: char *strnlower(); /* lowercase a string */
! 13149: int istruncate = (n<0?1:0); /*true to truncate .com from pruned*/
! 13150: int ndots = 0; /* number of dots found in url */
! 13151: /* -------------------------------------------------------------------------
! 13152: prune the url
! 13153: -------------------------------------------------------------------------- */
! 13154: /* --- first check input --- */
! 13155: *pruned = '\000'; /* init for error */
! 13156: if ( isempty(url) ) goto end_of_job; /* missing input, so return NULL */
! 13157: if ( n < 0 ) n = (-n); /* flip n positive */
! 13158: if ( n == 0 ) n = 999; /* retain all levels of url */
! 13159: /* --- preprocess url --- */
! 13160: strninit(pruned,url,2032); /* copy url to our static buffer */
! 13161: strlower(pruned); /* lowercase it and... */
! 13162: trimwhite(pruned); /*remove leading/trailing whitespace*/
! 13163: /* --- first remove leading http:// --- */
! 13164: if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
! 13165: if ( ((int)(delim-pruned)) <= 8 ) { /* make sure it's a prefix */
! 13166: strsqueezep(pruned,delim+3); /* squeeze out leading http:// */
! 13167: trimwhite(pruned); } /*remove leading/trailing whitespace*/
! 13168: /* --- next remove leading www. --- */
! 13169: if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
! 13170: if ( ((int)(delim-pruned)) == 0 ) { /* make sure it's the leading chars*/
! 13171: strsqueezep(pruned,delim+4); /* squeeze out leading www. */
! 13172: trimwhite(pruned); } /*remove leading/trailing whitespace*/
! 13173: /* --- finally remove leading / and everything following it --- */
! 13174: if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
! 13175: *delim = '\000'; /* null-terminate url at first / */
! 13176: if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */
! 13177: /* --- count dots from back of url --- */
! 13178: delim = pruned + strlen(pruned); /*ptr to '\000' terminating pruned*/
! 13179: while ( ((int)(delim-pruned)) > 0 ) { /* don't back up before first char */
! 13180: delim--; /* ptr to preceding character */
! 13181: if ( *delim != '.' ) continue; /* not a dot, so keep looking */
! 13182: ndots++; /* count another dot found */
! 13183: if ( istruncate ) { /* remove trailing .com */
! 13184: istruncate = 0; /* don't truncate any more dots */
! 13185: *delim = '\000'; /* truncate pruned url */
! 13186: ndots = 0; } /* and reset dot count */
! 13187: if ( ndots >= n ) { /* have all requested levels */
! 13188: strsqueezep(pruned,delim+1); /* squeeze out leading levels */
! 13189: break; } /* and we're done */
! 13190: } /* --- end-of-while() --- */
! 13191: purl = pruned; /*completed okay, return pruned url*/
! 13192: end_of_job:
! 13193: return ( purl ); /* back with pruned url */
! 13194: } /* --- end-of-function urlprune() --- */
! 13195:
! 13196:
! 13197: /* ==========================================================================
! 13198: * Function: urlncmp ( url1, url2, n )
! 13199: * Purpose: Compares the n topmost levels of two urls
! 13200: * --------------------------------------------------------------------------
! 13201: * Arguments: url1 (I) char * to null-terminated string
! 13202: * containing url to be compared with url2
! 13203: * url2 (I) char * to null-terminated string
! 13204: * containing url to be compared with url1
! 13205: * n (I) int containing number of top levels
! 13206: * to compare, or 0 to compare them all.
! 13207: * n<0 compares that many top levels excluding
! 13208: * the last, i.e., for n=-1, xxx.com and xxx.org
! 13209: * would be considered a match
! 13210: * --------------------------------------------------------------------------
! 13211: * Returns: ( int ) 1 if url's match, or
! 13212: * 0 if not.
! 13213: * --------------------------------------------------------------------------
! 13214: * Notes: o
! 13215: * ======================================================================= */
! 13216: /* --- entry point --- */
! 13217: int urlncmp ( char *url1, char *url2, int n )
! 13218: {
! 13219: /* -------------------------------------------------------------------------
! 13220: Allocations and Declarations
! 13221: -------------------------------------------------------------------------- */
! 13222: char *urlprune(), *prune=NULL, /* prune url's */
! 13223: prune1[4096], prune2[4096]; /* pruned copies of url1,url2 */
! 13224: int ismatch = 0; /* true if url's match */
! 13225: /* -------------------------------------------------------------------------
! 13226: prune url's and compare the pruned results
! 13227: -------------------------------------------------------------------------- */
! 13228: /* --- check input --- */
! 13229: if ( isempty(url1) /*make sure both url1,url2 supplied*/
! 13230: || isempty(url2) ) goto end_of_job; /* missing input, so return 0 */
! 13231: /* --- prune url's --- */
! 13232: prune = urlprune(url1,n); /* ptr to pruned version of url1 */
! 13233: if ( isempty(prune) ) goto end_of_job; /* some problem with url1 */
! 13234: strninit(prune1,prune,4064); /* local copy of pruned url1 */
! 13235: prune = urlprune(url2,n); /* ptr to pruned version of url2 */
! 13236: if ( isempty(prune) ) goto end_of_job; /* some problem with url2 */
! 13237: strninit(prune2,prune,4064); /* local copy of pruned url2 */
! 13238: /* --- compare pruned url's --- */
! 13239: if ( strcmp(prune1,prune2) == 0 ) /* pruned url's are identical */
! 13240: ismatch = 1; /* signal match to caller */
! 13241: end_of_job:
! 13242: return ( ismatch ); /*back with #matching url components*/
! 13243: } /* --- end-of-function urlncmp() --- */
! 13244:
! 13245:
! 13246: /* ==========================================================================
1.2 albertel 13247: * Function: dbltoa ( dblval, npts )
1.1 albertel 13248: * Purpose: Converts double to ascii, in financial format
13249: * (e.g., comma-separated and negatives enclosed in ()'s).
13250: * -------------------------------------------------------------------------
13251: * Arguments: dblval (I) double containing value to be converted.
13252: * npts (I) int containing #places after decimal point
13253: * to be displayed in returned string.
13254: * Returns: ( char * ) null-terminated string containing
13255: * double converted to financial format.
13256: * -------------------------------------------------------------------------
13257: * Notes: o
13258: * ======================================================================= */
13259: /* --- entry point --- */
1.2 albertel 13260: char *dbltoa ( double dblval, int npts )
13261: /* double dblval;
13262: int npts; */
1.1 albertel 13263: {
13264: /* -------------------------------------------------------------------------
13265: Allocations and Declarations
13266: ------------------------------------------------------------------------- */
1.3 albertel 13267: static char finval[256]; /* buffer returned to caller */
1.1 albertel 13268: static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
13269: char *finptr = finval; /* ptr to next char being converted*/
13270: double floor(); /* integer which is glb(double) */
13271: double dbldigit; /* to shift out digits from dblval */
13272: int digit; /* one digit from dblval */
13273: int isneg = 0; /* reset true if dblval negative */
13274: int ifrac = 0; /* npts fractional digits of dblval*/
13275: char digits[64]; int ndigits=0; /* all the digits [0]=least signif */
13276: /* -------------------------------------------------------------------------
13277: Check sign
13278: ------------------------------------------------------------------------- */
13279: if ( dblval < 0.0 ) /* got a negative value to convert */
13280: { isneg=1; dblval=(-dblval); } /* set flag and make it positive */
13281: /* -------------------------------------------------------------------------
13282: Get fractional part of dblval if required
13283: ------------------------------------------------------------------------- */
13284: if ( npts > 0 )
13285: { int ipts = npts; /* loop index */
13286: dbldigit = dblval-floor(dblval); /* fractional part as double */
13287: digit = 1; /* check if rounded frac > 1 */
13288: while ( --ipts >= 0 ) /* count down */
13289: { dbldigit *= 10.0; /* shift left one digit at a time */
13290: digit *= 10; } /* and keep max up-to-date */
13291: ifrac = (int)(dbldigit + 0.5); /* store fractional part as integer*/
13292: if ( ifrac >= digit ) /* round to next whole number */
13293: { dblval++; ifrac=0; } /* bump val, reset frac to zero */
13294: } /* --- end-of-if(npts>0) --- */
13295: else dblval += 0.5; /* no frac, round to nearest whole */
13296: /* -------------------------------------------------------------------------
13297: Get whole digits
13298: ------------------------------------------------------------------------- */
13299: dblval = floor(dblval); /* get rid of fractional part */
13300: while ( dblval > 0.0 ) /* still have data digits remaining*/
13301: { dbldigit = floor(dblval/10.0); /* shift out next digit */
13302: digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
13303: if ( digit<0 || digit>9 ) digit=10; /* index check */
13304: digits[ndigits++] = digittbl[digit]; /* store ascii digit */
13305: dblval = dbldigit; } /* ready for next digit */
13306: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
13307: /* -------------------------------------------------------------------------
13308: Format whole part from digits[] array
13309: ------------------------------------------------------------------------- */
13310: if ( isneg ) *finptr++ = '('; /* leading paren for negative value*/
13311: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
13312: { *finptr++ = digits[digit]; /* store digit */
13313: if ( digit>0 && digit%3==0 ) /* need a comma */
13314: *finptr++ = ','; } /* put in separating comma */
13315: /* -------------------------------------------------------------------------
13316: Format fractional part using ifrac
13317: ------------------------------------------------------------------------- */
13318: if ( npts > 0 )
13319: { *finptr++ = '.'; /* start with decimal point */
13320: sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
13321: finptr += npts; } /* bump ptr past fractional digits */
13322: /* -------------------------------------------------------------------------
13323: End-of-Job
13324: ------------------------------------------------------------------------- */
13325: if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
13326: *finptr = '\000'; /* null-terminate converted double */
13327: return ( finval ); /* converted double back to caller */
1.2 albertel 13328: } /* --- end-of-function dbltoa() --- */
1.1 albertel 13329:
13330:
13331: /* ==========================================================================
13332: * Function: aalowpass ( rp, bytemap, grayscale )
13333: * Purpose: calculates a lowpass anti-aliased bytemap
13334: * for rp->bitmap, with each byte 0...grayscale-1
13335: * --------------------------------------------------------------------------
13336: * Arguments: rp (I) raster * to raster whose bitmap
13337: * is to be anti-aliased
13338: * bytemap (O) intbyte * to bytemap, calculated
13339: * by applying lowpass filter to rp->bitmap,
13340: * and returned (as you'd expect) in 1-to-1
13341: * addressing correspondence with rp->bitmap
13342: * grayscale (I) int containing number of grayscales
13343: * to be calculated, 0...grayscale-1
13344: * (should typically be given as 256)
13345: * --------------------------------------------------------------------------
13346: * Returns: ( int ) 1=success, 0=any error
13347: * --------------------------------------------------------------------------
13348: * Notes: o If the center point of the box being averaged is black,
13349: * then the entire "average" is forced black (grayscale-1)
13350: * regardless of the surrounding points. This is my attempt
13351: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
13352: * lines, which would otherwise turn from black to a gray shade.
13353: * o Also, while the weights for neighbor points are fixed,
13354: * you may adjust the center point weight on the compile line.
13355: * A higher weight sharpens the resulting anti-aliased image;
13356: * lower weights blur it out more (but keep the "center" black
13357: * as per the preceding note).
13358: * ======================================================================= */
13359: /* --- entry point --- */
13360: int aalowpass (raster *rp, intbyte *bytemap, int grayscale)
13361: {
13362: /* -------------------------------------------------------------------------
13363: Allocations and Declarations
13364: -------------------------------------------------------------------------- */
13365: int status = 1; /* 1=success, 0=failure to caller */
13366: pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
13367: int irow=0, icol=0; /* rp->height, rp->width indexes */
13368: int weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
13369: int adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
13370: int totwts = 0; /* sum of all weights in matrix */
13371: int isforceavg = 1, /*force avg black if center black?*/
13372: isminmaxwts = 1, /*use wts or #pts for min/max test */
13373: blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
13374: /* -------------------------------------------------------------------------
13375: Initialization
13376: -------------------------------------------------------------------------- */
13377: /* --- calculate total weights --- */
13378: weights[4]= centerwt; /* weight for center point */
13379: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
13380: totwts = centerwt + 4*(1+adjacentwt); /* tot is center plus neighbors */
13381: /* -------------------------------------------------------------------------
13382: Calculate bytemap as 9-point weighted average over bitmap
13383: -------------------------------------------------------------------------- */
13384: for ( irow=0; irow<rp->height; irow++ )
13385: for ( icol=0; icol<rp->width; icol++ )
13386: {
13387: int ipixel = icol + irow*(rp->width); /* center pixel index */
13388: int jrow=0, jcol=0, /* box around ipixel */
13389: bitval = 0, /* value of bit/pixel at jrow,jcol */
13390: iscenter = 0, /* set true if center pixel black */
13391: nadjacent=0, wadjacent=0, /* #adjacent black pixels, their wts*/
13392: ngaps = 0, /* #gaps in 8 pixels around center */
13393: iwt=(-1), sumwts=0; /* weights index, sum in-bound wts */
13394: char adjmatrix[8]; /* adjacency "matrix" */
13395: memset(adjmatrix,0,8); /* zero out adjacency matrix */
13396: bytemap[ipixel] = 0; /* init pixel white */
13397: /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
13398: for ( jrow=irow-1; jrow<=irow+1; jrow++ ) /* jrow = irow-1...irow+1 */
13399: for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
13400: {
13401: int jpixel = jcol + jrow*(rp->width); /* averaging index */
13402: iwt++; /*always bump weight index*/
13403: if ( jrow<0 || jrow>=rp->height /* if row out pf bounds */
13404: || jcol<0 || jcol>=rp->width ) /* or col out of bounds */
13405: continue; /* ignore this point */
13406: bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
13407: if ( bitval ) /* this is a black pixel */
13408: { if ( jrow==irow && jcol==icol ) /* and this is center point */
13409: iscenter = 1; /* set flag for center point black */
13410: else /* adjacent point black */
13411: { nadjacent++; /* bump adjacent black count */
13412: adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
13413: wadjacent += weights[iwt]; } /* sum weights for black pixels */
13414: sumwts += weights[iwt]; /* and sum weights for all pixels */
13415: } /* --- end-of-for(jrow,jcol) --- */
13416: /* --- count gaps --- */
13417: ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
13418: for ( iwt=0; iwt<7; iwt++ ) /* clockwise around adjacency */
13419: if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
13420: ngaps /= 2; /*each gap has 2 black/white flips*/
13421: /* --- anti-alias pixel, but leave it black if it was already black --- */
13422: if ( isforceavg && iscenter ) /* force avg if center point black */
13423: bytemap[ipixel] = grayscale-1; /* so force grayscale-1=black */
13424: else /* center point not black */
13425: if ( ngaps <= 2 ) /*don't darken checkerboarded pixel*/
13426: { bytemap[ipixel] = /* 0=white ... grayscale-1=black */
13427: ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
13428: if ( blackscale > 0 /* blackscale kludge turned on */
13429: && bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
13430: bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
13431: /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
1.3 albertel 13432: if ( !iscenter ) { /* apply min/maxadjacent test */
1.1 albertel 13433: if ( isminmaxwts ) /* min/max refer to adjacent weights*/
13434: { if ( wadjacent < minadjacent /* wts of adjacent points too low */
13435: || wadjacent > maxadjacent ) /* or too high */
13436: bytemap[ipixel] = 0; } /* so leave point white */
13437: else /* min/max refer to #adjacent points*/
13438: { if ( nadjacent < minadjacent /* too few adjacent points black */
13439: || nadjacent > maxadjacent ) /* or too many */
1.3 albertel 13440: bytemap[ipixel] = 0; } } /* so leave point white */
1.1 albertel 13441: } /* --- end-of-for(irow,icol) --- */
13442: /* -------------------------------------------------------------------------
13443: Back to caller with gray-scale anti-aliased bytemap
13444: -------------------------------------------------------------------------- */
13445: /*end_of_job:*/
13446: return ( status );
13447: } /* --- end-of-function aalowpass() --- */
13448:
13449:
13450: /* ==========================================================================
13451: * Function: aapnm ( rp, bytemap, grayscale )
13452: * Purpose: calculates a lowpass anti-aliased bytemap
13453: * for rp->bitmap, with each byte 0...grayscale-1,
13454: * based on the pnmalias.c algorithm
13455: * --------------------------------------------------------------------------
13456: * Arguments: rp (I) raster * to raster whose bitmap
13457: * is to be anti-aliased
13458: * bytemap (O) intbyte * to bytemap, calculated
13459: * by applying pnm-based filter to rp->bitmap,
13460: * and returned (as you'd expect) in 1-to-1
13461: * addressing correspondence with rp->bitmap
13462: * grayscale (I) int containing number of grayscales
13463: * to be calculated, 0...grayscale-1
13464: * (should typically be given as 256)
13465: * --------------------------------------------------------------------------
13466: * Returns: ( int ) 1=success, 0=any error
13467: * --------------------------------------------------------------------------
13468: * Notes: o Based on the pnmalias.c algorithm in the netpbm package
13469: * on sourceforge.
13470: * ======================================================================= */
13471: /* --- entry point --- */
13472: int aapnm (raster *rp, intbyte *bytemap, int grayscale)
13473: {
13474: /* -------------------------------------------------------------------------
13475: Allocations and Declarations
13476: -------------------------------------------------------------------------- */
13477: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
13478: int width=rp->width, height=rp->height, /* width, height of raster */
13479: icol = 0, irow = 0, /* width, height indexes */
13480: imap = (-1); /* pixel index = icol + irow*width */
13481: int bgbitval=0, fgbitval=1; /* background, foreground bitval */
1.3 albertel 13482: int isfirstaa = 1; /*debugging switch signals 1st pixel*/
1.1 albertel 13483: #if 0
13484: int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
13485: int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
13486: #endif
13487: int totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
13488: int isresetparams = 1, /* true to set antialiasing params */
13489: isfgalias = 1, /* true to antialias fg bits */
13490: isfgonly = 0, /* true to only antialias fg bits */
13491: isbgalias = 0, /* true to antialias bg bits */
13492: isbgonly = 0; /* true to only antialias bg bits */
13493: /* -------------------------------------------------------------------------
13494: Initialization
13495: -------------------------------------------------------------------------- */
13496: /* --- check for bold light --- */
13497: if ( 0 )
13498: { if ( weightnum > 2 ) { isbgalias=1; } /* simulate bold */
13499: if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
13500: /* --- reset wts[], etc, and calculate total weights --- */
13501: if ( isresetparams ) /* wts[], etc taken from params */
13502: { int iwt=0; /* wts[iwt] index */
13503: wts[4]= centerwt; /* weight for center point */
13504: wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
13505: wts[0]=wts[2]=wts[6]=wts[8] = cornerwt; /* and corner points */
13506: for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
13507: isfgalias = fgalias; /* set isfgalias */
13508: isfgonly = fgonly; /* set isfgonly */
13509: isbgalias = bgalias; /* set isbgalias */
13510: isbgonly = bgonly; } /* set isbgonly */
13511: /* -------------------------------------------------------------------------
13512: Calculate bytemap as 9-point weighted average over bitmap
13513: -------------------------------------------------------------------------- */
13514: for ( irow=0; irow<height; irow++ )
13515: for ( icol=0; icol<width; icol++ )
13516: {
13517: /* --- local allocations and declarations --- */
13518: int bitval=0, /* value of rp bit at irow,icol */
13519: nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
13520: ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0; /*compass pt names*/
13521: int isbgedge=0, isfgedge=0; /*does pixel border a bg or fg edge*/
13522: int aabyteval=0; /* antialiased (or unchanged) value*/
13523: /* --- bump imap index and get center bit value --- */
13524: imap++; /* imap = icol + irow*width */
13525: bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
13526: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
13527: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
13528: /* --- check if we're antialiasing this pixel --- */
13529: if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
13530: || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
13531: continue; /* leave default and do next bit */
13532: /* --- get surrounding bits --- */
13533: if ( irow > 0 ) /* nn (north) bit available */
13534: nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
13535: if ( irow < height-1 ) /* ss (south) bit available */
13536: ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
13537: if ( icol > 0 ) /* ww (west) bit available */
13538: { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
13539: if ( irow > 0 ) /* nw bit available */
13540: nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
13541: if ( irow < height-1 ) /* sw bit available */
13542: swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
13543: if ( icol < width-1 ) /* ee (east) bit available */
13544: { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
13545: if ( irow > 0 ) /* ne bit available */
13546: nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
13547: if ( irow < height-1 ) /* se bit available */
13548: sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
13549: /* --- check for edges --- */
13550: isbgedge = /* current pixel borders a bg edge */
13551: (nnbitval==bgbitval && eebitval==bgbitval) || /*upper-right edge*/
13552: (eebitval==bgbitval && ssbitval==bgbitval) || /*lower-right edge*/
13553: (ssbitval==bgbitval && wwbitval==bgbitval) || /*lower-left edge*/
13554: (wwbitval==bgbitval && nnbitval==bgbitval) ; /*upper-left edge*/
13555: isfgedge = /* current pixel borders an fg edge*/
13556: (nnbitval==fgbitval && eebitval==fgbitval) || /*upper-right edge*/
13557: (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/
13558: (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left edge*/
13559: (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left edge*/
1.2 albertel 13560: /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
13561: if ( 1 ) { /* true to perform test */
13562: int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
13563: isbghorz = /* top or bottom edge is all bg */
13564: (nwbitval+nnbitval+nebitval == 3*bgbitval) || /* top edge bg */
13565: (swbitval+ssbitval+sebitval == 3*bgbitval) ; /* bottom edge bg */
13566: isfghorz = /* top or bottom edge is all fg */
13567: (nwbitval+nnbitval+nebitval == 3*fgbitval) || /* top edge fg */
13568: (swbitval+ssbitval+sebitval == 3*fgbitval) ; /* bottom edge fg */
13569: isbgvert = /* left or right edge is all bg */
13570: (nwbitval+wwbitval+swbitval == 3*bgbitval) || /* left edge bg */
13571: (nebitval+eebitval+sebitval == 3*bgbitval) ; /* right edge bg */
13572: isfgvert = /* left or right edge is all bg */
13573: (nwbitval+wwbitval+swbitval == 3*fgbitval) || /* left edge fg */
13574: (nebitval+eebitval+sebitval == 3*fgbitval) ; /* right edge fg */
13575: if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
13576: || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
13577: continue; /* don't antialias */
13578: } /* --- end-of-if(1) --- */
13579: /* --- check #gaps for checkerboard (added by j.forkosh) --- */
13580: if ( 0 ) { /* true to perform test */
13581: int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
13582: if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
13583: if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
13584: if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
13585: if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
13586: if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
13587: if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
13588: if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
13589: if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
13590: if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
13591: if ( ngaps<mingaps || ngaps>maxgaps ) continue;
13592: } /* --- end-of-if(1) --- */
1.1 albertel 13593: /* --- antialias if necessary --- */
13594: if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
13595: || (isfgalias && isfgedge) /* alias pixel surrounding fg */
13596: || (isbgedge && isfgedge) ) /* neighboring fg and bg pixel */
13597: {
13598: int aasumval = /* sum wts[]*bitmap[] */
13599: wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
13600: wts[3]*wwbitval + wts[4]*bitval + wts[5]*eebitval +
13601: wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
13602: double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
13603: aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
13604: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
1.3 albertel 13605: if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
13606: "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
13607: (isfirstaa?"aapnm algorithm":"aapnm"),
1.1 albertel 13608: irow,icol,imap, aawtval,aabyteval);
1.3 albertel 13609: isfirstaa = 0; }
1.1 albertel 13610: } /* --- end-of-if(isedge) --- */
13611: } /* --- end-of-for(irow,icol) --- */
13612: /* -------------------------------------------------------------------------
13613: Back to caller with gray-scale anti-aliased bytemap
13614: -------------------------------------------------------------------------- */
13615: /*end_of_job:*/
13616: return ( 1 );
13617: } /* --- end-of-function aapnm() --- */
13618:
13619:
13620: /* ==========================================================================
1.3 albertel 13621: * Function: aapnmlookup ( rp, bytemap, grayscale )
13622: * Purpose: calculates a lowpass anti-aliased bytemap
13623: * for rp->bitmap, with each byte 0...grayscale-1,
13624: * based on the pnmalias.c algorithm.
13625: * This version uses aagridnum() and aapatternnum() lookups
13626: * to interpret 3x3 lowpass pixel grids.
13627: * --------------------------------------------------------------------------
13628: * Arguments: rp (I) raster * to raster whose bitmap
13629: * is to be anti-aliased
13630: * bytemap (O) intbyte * to bytemap, calculated
13631: * by applying pnm-based filter to rp->bitmap,
13632: * and returned (as you'd expect) in 1-to-1
13633: * addressing correspondence with rp->bitmap
13634: * grayscale (I) int containing number of grayscales
13635: * to be calculated, 0...grayscale-1
13636: * (should typically be given as 256)
13637: * --------------------------------------------------------------------------
13638: * Returns: ( int ) 1=success, 0=any error
13639: * --------------------------------------------------------------------------
13640: * Notes: o Based on the pnmalias.c algorithm in the netpbm package
13641: * on sourceforge.
13642: * o This version uses aagridnum() and aapatternnum() lookups
13643: * to interpret 3x3 lowpass pixel grids.
13644: * ======================================================================= */
13645: /* --- entry point --- */
13646: int aapnmlookup (raster *rp, intbyte *bytemap, int grayscale)
13647: {
13648: /* -------------------------------------------------------------------------
13649: Allocations and Declarations
13650: -------------------------------------------------------------------------- */
13651: int width=rp->width, height=rp->height, /* width, height of raster */
13652: icol = 0, irow = 0, /* width, height indexes */
13653: imap = (-1); /* pixel index = icol + irow*width */
13654: int bgbitval=0, fgbitval=1; /* background, foreground bitval */
13655: int isfirstaa = 1; /*debugging switch signals 1st pixel*/
13656: int aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt,
13657: totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/
13658: int isfgalias = fgalias, /*(1) true to antialias fg bits */
13659: isfgonly = fgonly, /*(0) true to only antialias fg bits*/
13660: isbgalias = bgalias, /*(0) true to antialias bg bits */
13661: isbgonly = bgonly; /*(0) true to only antialias bg bits*/
13662: int gridnum=(-1), aagridnum(), /* grid# for 3x3 grid at irow,icol */
13663: patternum=(-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
13664: int aapatterns(); /* to antialias special patterns */
13665: /* ---
13666: * pattern number data
13667: * ------------------- */
13668: /* --- number of adjacent fg pixels set in pattern --- */
13669: static int nadjacents[] = { -1, /* #adjacent fg pixels for pattern */
13670: 0, 4, 0, 1, 4, 3, 1, 0, 1, 0, /* 1-10 */
13671: 2, 2, 3, 4, 3, 4, 2, 2, 1, 2, /* 11-20 */
13672: 1, 2, 1, 2, 0, 1, 3, 2, 3, 2, /* 21-30 */
13673: 3, 2, 3, 2, 4, 3, 1, 2, 2, 2, /* 31-40 */
13674: 2, 1, 2, 2, 3, 0, 3, 2, 2, 1, 4, /* 41-51 */
13675: -1 } ; /* --- end-of-nadjacents[] --- */
13676: /* --- number of corner fg pixels set in pattern --- */
13677: static int ncorners[] = { -1, /* #corner fg pixels for pattern */
13678: 0, 4, 1, 0, 3, 4, 1, 2, 1, 2, /* 1-10 */
13679: 0, 0, 3, 2, 3, 2, 4, 4, 2, 1, /* 11-20 */
13680: 2, 1, 2, 1, 3, 2, 0, 1, 2, 3, /* 21-30 */
13681: 2, 3, 2, 3, 1, 2, 4, 3, 2, 2, /* 31-40 */
13682: 2, 3, 2, 2, 1, 4, 1, 2, 2, 3, 0, /* 41-51 */
13683: -1 } ; /* --- end-of-ncorners[] --- */
13684: /* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */
13685: static int horzedges[] = { -1, /* 0,1,2 = horz bg,fg,both edge */
13686: 0, 1, 0, 0, 1, 1, 0, 0, 0, -1, /* 1-10 */
13687: 0, -1, 1, 1, 1, -1, 1, -1, 2, 0, /* 11-20 */
13688: -1, -1, -1, 0, -1, -1, -1, -1, 2, 1, /* 21-30 */
13689: -1, -1, -1, 1, -1, -1, -1, -1, 2, -1, /* 31-40 */
13690: -1, 1, 1, -1, -1, -1, 0, 0, -1, -1, -1, /* 41-51 */
13691: -1 } ; /* --- end-of-horzedges[] --- */
13692: /* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */
13693: static int vertedges[] = { -1, /* 0,1,2 = vert bg,fg,both edge */
13694: 0, 1, 0, 0, 1, 1, 0, -1, -1, -1, /* 1-10 */
13695: 0, 0, 1, -1, -1, -1, 1, 1, -1, -1, /* 11-20 */
13696: -1, 0, 0, 0, -1, -1, 0, -1, -1, -1, /* 21-30 */
13697: -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, /* 31-40 */
13698: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 41-51 */
13699: -1 } ; /* --- end-of-vertedges[] --- */
13700: /* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */
13701: static int diagedges[] = { -1, /* 0,1,2 = diag bg,fg,both edge */
13702: 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, /* 1-10 */
13703: 2, -1, 1, 1, 1, 1, 1, -1, 0, 2, /* 11-20 */
13704: 0, -1, 0, 2, 0, -1, 1, 1, 1, 1, /* 21-30 */
13705: 1, -1, 1, 2, 1, 1, -1, 1, 2, -1, /* 31-40 */
13706: 1, 0, -1, 2, 1, 0, 1, -1, 1, -1, 1, /* 41-51 */
13707: -1 } ; /* --- end-of-diagedges[] --- */
13708: /* -------------------------------------------------------------------------
13709: Calculate bytemap as 9-point weighted average over bitmap
13710: -------------------------------------------------------------------------- */
13711: for ( irow=0; irow<height; irow++ )
13712: for ( icol=0; icol<width; icol++ )
13713: {
13714: /* --- local allocations and declarations --- */
13715: int bitval=0, /* value of rp bit at irow,icol */
13716: isbgdiag=0, isfgdiag=0, /*does pixel border a bg or fg edge*/
13717: aabyteval=0; /* antialiased (or unchanged) value*/
13718: /* --- get gridnum and center bit value, init aabyteval --- */
13719: imap++; /* first set imap=icol + irow*width*/
13720: gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
13721: bitval = (gridnum&1); /* center bit set if gridnum odd */
13722: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
13723: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
13724: if ( gridnum<0 || gridnum>511 ) continue; /* gridnum out of bounds*/
13725: /* --- check if we're antialiasing this pixel --- */
13726: if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
13727: || (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
13728: continue; /* leave default and do next bit */
13729: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
13730: patternum = aapatternnum(gridnum); /* look up pattern number */
13731: if ( patternum<1 || patternum>51 ) continue; /* some internal error */
13732: /* --- special pattern number processing --- */
13733: if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale))
13734: >= 0 ) { /* special processing for pattern */
13735: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
13736: continue; } /* and continue with next pixel */
13737: /* --- check for diagonal edges --- */
13738: isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/
13739: diagedges[patternum]==0 );
13740: isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/
13741: diagedges[patternum]==1 );
13742: /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
13743: if ( 1 ) { /* true to perform test */
13744: int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */
13745: horzedge=horzedges[patternum], vertedge=vertedges[patternum];
13746: isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */
13747: isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */
13748: isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */
13749: isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */
13750: if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
13751: || (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
13752: continue; /* don't antialias */
13753: } /* --- end-of-if(1) --- */
13754: #if 0
13755: /* --- check #gaps for checkerboard (added by j.forkosh) --- */
13756: if ( 0 ) { /* true to perform test */
13757: int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
13758: if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
13759: if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
13760: if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
13761: if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
13762: if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
13763: if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
13764: if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
13765: if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
13766: if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
13767: if ( ngaps<mingaps || ngaps>maxgaps ) continue;
13768: } /* --- end-of-if(1) --- */
13769: #endif
13770: /* --- antialias if necessary --- */
13771: if ( (isbgalias && isbgdiag) /* alias pixel surrounding bg */
13772: || (isfgalias && isfgdiag) /* alias pixel surrounding fg */
13773: || (isbgdiag && isfgdiag) ) /* neighboring fg and bg pixel */
13774: {
13775: int aasumval = /* sum wts[]*bitmap[] */
13776: aacenterwt*bitval + /* apply centerwt to center pixel */
13777: aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */
13778: aacornerwt*ncorners[patternum]; /* and corners */
13779: double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
13780: aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
13781: bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
13782: if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
13783: "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d",
13784: (isfirstaa?"aapnmlookup algorithm":"aapnm"),
13785: irow,icol,imap, aawtval,aabyteval);
13786: if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */
13787: else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum);
13788: isfirstaa = 0; }
13789: } /* --- end-of-if(isedge) --- */
13790: } /* --- end-of-for(irow,icol) --- */
13791: /* -------------------------------------------------------------------------
13792: Back to caller with gray-scale anti-aliased bytemap
13793: -------------------------------------------------------------------------- */
13794: /*end_of_job:*/
13795: return ( 1 );
13796: } /* --- end-of-function aapnmlookup() --- */
13797:
13798:
13799: /* ==========================================================================
13800: * Function: aapatterns ( rp, irow, icol, gridnum, patternum, grayscale )
13801: * Purpose: For patterns requireing special processing,
13802: * calculates anti-aliased value for pixel at irow,icol,
13803: * whose surrounding 3x3 pixel grid is coded by gridnum
13804: * (which must correspond to a pattern requiring special
13805: * processing).
13806: * --------------------------------------------------------------------------
13807: * Arguments: rp (I) raster * to raster whose bitmap
13808: * is to be anti-aliased
13809: * irow (I) int containing row, 0...height-1,
13810: * of pixel to be antialiased
13811: * icol (I) int containing col, 0...width-1,
13812: * of pixel to be antialiased
13813: * gridnum (I) int containing 0...511 corresponding to
13814: * 3x3 pixel grid surrounding irow,icol
13815: * patternum (I) int containing 1...51 pattern# of
13816: * the 3x3 grid surrounding irow,icol
13817: * grayscale (I) int containing number of grayscales
13818: * to be calculated, 0...grayscale-1
13819: * (should typically be given as 256)
13820: * --------------------------------------------------------------------------
13821: * Returns: ( int ) 0...grayscale-1 for success,
13822: * -1 = error, or no special processing required
13823: * --------------------------------------------------------------------------
13824: * Notes: o
13825: * ======================================================================= */
13826: /* --- entry point --- */
13827: int aapatterns (raster *rp, int irow, int icol,
13828: int gridnum, int patternum, int grayscale)
13829: {
13830: /* -------------------------------------------------------------------------
13831: Allocations and Declarations
13832: -------------------------------------------------------------------------- */
13833: int aaval = (-1); /* antialiased value returned */
13834: int iscenter = (gridnum&1); /* true if center pixel set/black */
13835: int aapatternnum(), /* if patternum not supplied */
13836: aapattern1124(), /* routine for patterns #11,24 */
13837: aapattern19(), /* special routine for pattern #19 */
13838: aapattern20(), /* special routine for pattern #20 */
13839: aapattern39(); /* special routine for pattern #39 */
13840: /* -------------------------------------------------------------------------
13841: special pattern number processing
13842: -------------------------------------------------------------------------- */
13843: if ( 1 ) {
13844: if ( patternum < 1 ) /* pattern# not supplied by caller */
13845: patternum = aapatternnum(gridnum); /* so look it up ourselves */
13846: switch ( patternum ) {
13847: default: break; /* no special processing */
13848: case 11:
13849: case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break;
13850: case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break;
13851: case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break;
13852: case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break;
13853: /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */
13854: case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */
13855: } /* --- end-of-switch(patternum) --- */
13856: } /* --- end-of-if() --- */
13857: return ( aaval ); /* return antialiased val to caller*/
13858: } /* --- end-of-function aapatterns() --- */
13859:
13860:
13861: /* ==========================================================================
13862: * Function: aapattern1124 ( rp, irow, icol, gridnum, grayscale )
13863: * Purpose: calculates anti-aliased value for pixel at irow,icol,
13864: * whose surrounding 3x3 pixel grid is coded by gridnum
13865: * (which must correspond to pattern #11 or #24).
13866: * --------------------------------------------------------------------------
13867: * Arguments: rp (I) raster * to raster whose bitmap
13868: * is to be anti-aliased
13869: * irow (I) int containing row, 0...height-1,
13870: * of pixel to be antialiased
13871: * icol (I) int containing col, 0...width-1,
13872: * of pixel to be antialiased
13873: * gridnum (I) int containing 0...511 corresponding to
13874: * 3x3 pixel grid surrounding irow,icol
13875: * grayscale (I) int containing number of grayscales
13876: * to be calculated, 0...grayscale-1
13877: * (should typically be given as 256)
13878: * --------------------------------------------------------------------------
13879: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
13880: * --------------------------------------------------------------------------
13881: * Notes: o Handles the eight gridnum's
13882: * (gridnum/2 shown to eliminate irrelevant low-order bit)
13883: * --- --- -*- -*-
13884: * --* = 10 *-- = 18 --* = 72 *-- = 80 (pattern$11)
13885: * -*- -*- --- ---
13886: *
13887: * --- --- -** **-
13888: * --* = 11 *-- = 22 --* = 104 *-- = 208 (pattern$24)
13889: * -** **- --- ---
13890: * o For black * center pixel, using grid#10 as an example,
13891: * pixel stays --- antialiased ---*
13892: * black if -*** if part of -**
13893: * part of a -*- a diagonal -*-
13894: * corner, eg, * line, eg, *
13895: * ======================================================================= */
13896: /* --- entry point --- */
13897: int aapattern1124 (raster *rp, int irow, int icol,
13898: int gridnum, int grayscale)
13899: {
13900: /* -------------------------------------------------------------------------
13901: Allocations and Declarations
13902: -------------------------------------------------------------------------- */
13903: int aaval = (-1); /* antialiased value returned */
13904: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
13905: int patternum = 24; /* init for pattern#24 default */
13906: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
13907: int width=rp->width, height=rp->height; /* width, height of raster */
13908: int jrow=irow, jcol=icol; /* corner or diagonal row,col */
13909: int vertcornval=0, horzcornval=0, /* vertical, horizontal corner bits*/
13910: topdiagval=0, botdiagval=0, /* upper,lower diagonal pixel bits */
13911: cornval=0, diagval=0; /* vert+horzcorn, top+botdiag */
13912: int hdirection=99, vdirection=99, /* horz,vert corner direction */
13913: hturn=99,vturn=99, aafollowline(); /* follow corner till turns */
13914: /* -------------------------------------------------------------------------
13915: Check corner and diagonal pixels
13916: -------------------------------------------------------------------------- */
13917: if ( 0 ) goto end_of_job; /* true to turn off pattern1124 */
13918: switch ( gridnum/2 ) { /* check pattern#11,24 corner, diag*/
13919: default: goto end_of_job; /* not a pattern#11,24 gridnum */
13920: case 10: patternum=11; case 11:
13921: hdirection = 2; vdirection = -1; /* directions to follow corner */
13922: if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
13923: vertcornval = getlongbit(bitmap,(icol+jrow*width));
13924: if ( (icol-1) >= 0 ) /* lower diag left of center */
13925: botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
13926: if ( (jcol=icol+2) < width ) { /* horz corner right of center */
13927: horzcornval = getlongbit(bitmap,(jcol+irow*width));
13928: if ( (irow-1) >= 0 ) /* upper diag above center */
13929: topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
13930: break;
13931: case 18: patternum=11; case 22:
13932: hdirection = -2; vdirection = -1; /* directions to follow corner */
13933: if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
13934: vertcornval = getlongbit(bitmap,(icol+jrow*width));
13935: if ( (icol+1) < width ) /* lower diag right of center */
13936: botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
13937: if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
13938: horzcornval = getlongbit(bitmap,(jcol+irow*width));
13939: if ( (irow-1) >= 0 ) /* upper diag above center */
13940: topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
13941: break;
13942: case 72: patternum=11; case 104:
13943: hdirection = 2; vdirection = 1; /* directions to follow corner */
13944: if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
13945: vertcornval = getlongbit(bitmap,(icol+jrow*width));
13946: if ( (icol-1) >= 0 ) /* upper diag left of center */
13947: topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
13948: if ( (jcol=icol+2) < width ) { /* horz corner right of center */
13949: horzcornval = getlongbit(bitmap,(jcol+irow*width));
13950: if ( (irow+1) < height ) /* lower diag below center */
13951: botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
13952: break;
13953: case 80: patternum=11; case 208:
13954: hdirection = -2; vdirection = 1; /* directions to follow corner */
13955: if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
13956: vertcornval = getlongbit(bitmap,(icol+jrow*width));
13957: if ( (icol+1) < width ) /* upper diag right of center */
13958: topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
13959: if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
13960: horzcornval = getlongbit(bitmap,(jcol+irow*width));
13961: if ( (irow+1) < height ) /* lower diag below center */
13962: botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
13963: break;
13964: } /* --- end-of-switch(gridnum/2) --- */
13965: cornval = vertcornval+horzcornval; /* 0=no corner bits, 1, 2=both */
13966: diagval = topdiagval+botdiagval; /* 0=no diag bits, 1, 2=both */
13967: /* -------------------------------------------------------------------------
13968: Handle white center
13969: -------------------------------------------------------------------------- */
13970: if ( 1 && !iscenter ) { aaval = (patternum==11?51:64); goto end_of_job; }
13971: /* -------------------------------------------------------------------------
13972: Handle black center
13973: -------------------------------------------------------------------------- */
13974: if ( diagval > 1 ) aaval = ( patternum==24? 255:191 );
13975: else {
13976: hturn = aafollowline(rp,irow,icol,hdirection);
13977: vturn = aafollowline(rp,irow,icol,vdirection);
13978: if ( vturn*hdirection < 0 && hturn*vdirection < 0 )
13979: aaval = ( patternum==24? 255:191 );
13980: else aaval = grayscale-1; } /* actual corner */
13981: /* -------------------------------------------------------------------------
13982: Back to caller with grayscale antialiased value for pixel at irow,icol
13983: -------------------------------------------------------------------------- */
13984: end_of_job:
13985: if ( aaval >= 0 ) /* have antialiasing result */
13986: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
13987: "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, "
13988: "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n",
13989: irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval,
13990: vdirection,hdirection, vturn,hturn, aaval);
13991: return ( aaval ); /* back with antialiased value */
13992: } /* --- end-of-function aapattern1124() --- */
13993:
13994:
13995: /* ==========================================================================
13996: * Function: aapattern19 ( rp, irow, icol, gridnum, grayscale )
13997: * Purpose: calculates anti-aliased value for pixel at irow,icol,
13998: * whose surrounding 3x3 pixel grid is coded by gridnum
13999: * (which must correspond to pattern #19).
14000: * --------------------------------------------------------------------------
14001: * Arguments: rp (I) raster * to raster whose bitmap
14002: * is to be anti-aliased
14003: * irow (I) int containing row, 0...height-1,
14004: * of pixel to be antialiased
14005: * icol (I) int containing col, 0...width-1,
14006: * of pixel to be antialiased
14007: * gridnum (I) int containing 0...511 corresponding to
14008: * 3x3 pixel grid surrounding irow,icol
14009: * grayscale (I) int containing number of grayscales
14010: * to be calculated, 0...grayscale-1
14011: * (should typically be given as 256)
14012: * --------------------------------------------------------------------------
14013: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
14014: * --------------------------------------------------------------------------
14015: * Notes: o Handles the four gridnum's
14016: * (gridnum/2 shown to eliminate irrelevant low-order bit)
14017: * --- --* *-- ***
14018: * --- = 7 --* = 41 *-- = 148 --- = 224
14019: * *** --* *-- ---
14020: * ======================================================================= */
14021: /* --- entry point --- */
14022: int aapattern19 (raster *rp, int irow, int icol,
14023: int gridnum, int grayscale)
14024: {
14025: /* -------------------------------------------------------------------------
14026: Allocations and Declarations
14027: -------------------------------------------------------------------------- */
14028: int aaval = (-1); /* antialiased value returned */
14029: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
14030: int orientation = 1, /* 1=vertical, 2=horizontal */
14031: jrow=irow, jcol=icol; /* middle pixel of *** line */
14032: int turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */
14033: /* -------------------------------------------------------------------------
14034: Initialization and calculation of antialiased value
14035: -------------------------------------------------------------------------- */
14036: /* --- check input -- */
14037: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
14038: /* --- set params --- */
14039: switch ( gridnum/2 ) { /* check pattern#19 orientation */
14040: default: goto end_of_job; /* not a pattern#19 gridnum */
14041: case 7: orientation=2; jrow++; break;
14042: case 41: orientation=1; jcol++; break;
14043: case 148: orientation=1; jcol--; break;
14044: case 224: orientation=2; jrow--; break;
14045: } /* --- end-of-switch(gridnum/2) --- */
14046: /* --- get turns in both directions --- */
14047: if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job;
14048: if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job;
14049: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14050: /* --- weight pixel --- */
14051: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14052: /* -------------------------------------------------------------------------
14053: Back to caller with grayscale antialiased value for pixel at irow,icol
14054: -------------------------------------------------------------------------- */
14055: end_of_job:
14056: if ( aaval >= 0 ) /* have antialiasing result */
14057: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14058: "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n",
14059: irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval);
14060: return ( aaval ); /* back with antialiased value */
14061: } /* --- end-of-function aapattern19() --- */
14062:
14063:
14064: /* ==========================================================================
14065: * Function: aapattern20 ( rp, irow, icol, gridnum, grayscale )
14066: * Purpose: calculates anti-aliased value for pixel at irow,icol,
14067: * whose surrounding 3x3 pixel grid is coded by gridnum
14068: * (which must correspond to pattern #20).
14069: * --------------------------------------------------------------------------
14070: * Arguments: rp (I) raster * to raster whose bitmap
14071: * is to be anti-aliased
14072: * irow (I) int containing row, 0...height-1,
14073: * of pixel to be antialiased
14074: * icol (I) int containing col, 0...width-1,
14075: * of pixel to be antialiased
14076: * gridnum (I) int containing 0...511 corresponding to
14077: * 3x3 pixel grid surrounding irow,icol
14078: * grayscale (I) int containing number of grayscales
14079: * to be calculated, 0...grayscale-1
14080: * (should typically be given as 256)
14081: * --------------------------------------------------------------------------
14082: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
14083: * --------------------------------------------------------------------------
14084: * Notes: o Handles the eight gridnum's
14085: * (gridnum/2 shown to eliminate irrelevant low-order bit)
14086: * --- --- --* -*-
14087: * --* = 14 *-- = 19 --* = 42 --* = 73
14088: * **- -** -*- --*
14089: *
14090: * -*- -** *-- **-
14091: * *-- = 84 *-- = 112 *-- = 146 --* = 200
14092: * *-- --- -*- ---
14093: * ======================================================================= */
14094: /* --- entry point --- */
14095: int aapattern20 (raster *rp, int irow, int icol,
14096: int gridnum, int grayscale)
14097: {
14098: /* -------------------------------------------------------------------------
14099: Allocations and Declarations
14100: -------------------------------------------------------------------------- */
14101: int aaval = (-1); /* antialiased value returned */
14102: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
14103: int direction = 1, /* direction to follow ** line */
14104: jrow1=irow, jcol1=icol, /* coords of * */
14105: jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
14106: int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
14107: /* -------------------------------------------------------------------------
14108: Initialization and calculation of antialiased value
14109: -------------------------------------------------------------------------- */
14110: /* --- check input -- */
14111: if ( 1 ) goto end_of_job; /* don't want this one */
14112: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
14113: /* --- set params --- */
14114: switch ( gridnum/2 ) { /* check pattern#20 orientation */
14115: default: goto end_of_job; /* not a pattern#20 gridnum */
14116: case 14: direction=-2; jcol1++; jrow2++; break;
14117: case 19: direction=2; jcol1--; jrow2++; break;
14118: case 42: direction=1; jrow1++; jcol2++; break;
14119: case 73: direction=-1; jrow1--; jcol2++; break;
14120: case 84: direction=-1; jrow1--; jcol2--; break;
14121: case 112: direction=2; jcol1--; jrow2--; break;
14122: case 146: direction=1; jrow1++; jcol2--; break;
14123: case 200: direction=-2; jcol1++; jrow2--; break;
14124: } /* --- end-of-switch(gridnum/2) --- */
14125: /* --- get turns in both directions --- */
14126: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job;
14127: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job;
14128: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14129: /* --- weight pixel --- */
14130: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14131: /* -------------------------------------------------------------------------
14132: Back to caller with grayscale antialiased value for pixel at irow,icol
14133: -------------------------------------------------------------------------- */
14134: end_of_job:
14135: if ( aaval >= 0 ) /* have antialiasing result */
14136: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14137: "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
14138: irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
14139: return ( aaval ); /* back with antialiased value */
14140: } /* --- end-of-function aapattern20() --- */
14141:
14142:
14143: /* ==========================================================================
14144: * Function: aapattern39 ( rp, irow, icol, gridnum, grayscale )
14145: * Purpose: calculates anti-aliased value for pixel at irow,icol,
14146: * whose surrounding 3x3 pixel grid is coded by gridnum
14147: * (which must correspond to pattern #39).
14148: * --------------------------------------------------------------------------
14149: * Arguments: rp (I) raster * to raster whose bitmap
14150: * is to be anti-aliased
14151: * irow (I) int containing row, 0...height-1,
14152: * of pixel to be antialiased
14153: * icol (I) int containing col, 0...width-1,
14154: * of pixel to be antialiased
14155: * gridnum (I) int containing 0...511 corresponding to
14156: * 3x3 pixel grid surrounding irow,icol
14157: * grayscale (I) int containing number of grayscales
14158: * to be calculated, 0...grayscale-1
14159: * (should typically be given as 256)
14160: * --------------------------------------------------------------------------
14161: * Returns: ( int ) 0...grayscale-1 for success, -1=any error
14162: * --------------------------------------------------------------------------
14163: * Notes: o Handles the eight gridnum's
14164: * (gridnum/2 shown to eliminate irrelevant low-order bit)
14165: * --- --- --* -**
14166: * --* = 15 *-- = 23 --* = 43 --* = 105
14167: * *** *** -** --*
14168: *
14169: * **- *** *-- ***
14170: * *-- = 212 *-- = 240 *-- = 150 --* = 232
14171: * *-- --- **- ---
14172: * ======================================================================= */
14173: /* --- entry point --- */
14174: int aapattern39 (raster *rp, int irow, int icol,
14175: int gridnum, int grayscale)
14176: {
14177: /* -------------------------------------------------------------------------
14178: Allocations and Declarations
14179: -------------------------------------------------------------------------- */
14180: int aaval = (-1); /* antialiased value returned */
14181: int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
14182: int direction = 1, /* direction to follow ** line */
14183: jrow1=irow, jcol1=icol, /* coords of * */
14184: jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
14185: int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
14186: /* -------------------------------------------------------------------------
14187: Initialization and calculation of antialiased value
14188: -------------------------------------------------------------------------- */
14189: /* --- check input -- */
14190: if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
14191: /* --- set params --- */
14192: switch ( gridnum/2 ) { /* check pattern#39 orientation */
14193: default: goto end_of_job; /* not a pattern#39 gridnum */
14194: case 15: direction=-2; jcol1++; jrow2++; break;
14195: case 23: direction=2; jcol1--; jrow2++; break;
14196: case 43: direction=1; jrow1++; jcol2++; break;
14197: case 105: direction=-1; jrow1--; jcol2++; break;
14198: case 212: direction=-1; jrow1--; jcol2--; break;
14199: case 240: direction=2; jcol1--; jrow2--; break;
14200: case 150: direction=1; jrow1++; jcol2--; break;
14201: case 232: direction=-2; jcol1++; jrow2--; break;
14202: } /* --- end-of-switch(gridnum/2) --- */
14203: /* --- get turns directions (tunr1==1 signals inside corner) --- */
14204: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 )
14205: { aaval=0; goto end_of_job; }
14206: if ( 1 ) goto end_of_job; /* stop here for now */
14207: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction)) == 0 ) goto end_of_job;
14208: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
14209: /* --- weight pixel --- */
14210: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
14211: /* -------------------------------------------------------------------------
14212: Back to caller with grayscale antialiased value for pixel at irow,icol
14213: -------------------------------------------------------------------------- */
14214: end_of_job:
14215: if ( aaval >= 0 ) /* have antialiasing result */
14216: if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
14217: "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
14218: irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
14219: return ( aaval ); /* back with antialiased value */
14220: } /* --- end-of-function aapattern39() --- */
14221:
14222:
14223: /* ==========================================================================
14224: * Function: aafollowline ( rp, irow, icol, direction )
14225: * Purpose: starting with pixel at irow,icol, moves in
14226: * specified direction looking for a "turn"
14227: * --------------------------------------------------------------------------
14228: * Arguments: rp (I) raster * to raster containing pixel image
14229: * irow (I) int containing row, 0...height-1,
14230: * of first pixel
14231: * icol (I) int containing col, 0...width-1,
14232: * of first pixel
14233: * direction (I) int containing +1 to follow line up/north
14234: * (decreasing irow), -1 to follow line
14235: * down/south (increasing irow), +2 to follow
14236: * line right/east (increasing icol),
14237: * -2 to follow line left/west (decreasing icol)
14238: * --------------------------------------------------------------------------
14239: * Returns: ( int ) #rows or #cols traversed prior to turn,
14240: * or 0 if no turn detected (or for any error).
14241: * Sign is + if turn direction is right/east or
14242: * up/north, or is - if left/west or down/south.
14243: * --------------------------------------------------------------------------
14244: * Notes: o Here are some examples illustrating turn detection in
14245: * +2 (right/east) direction. Turns in other directions
14246: * are detected similarly/symmetrically. * denotes black
14247: * bits (usually fg), - denotes white bits (usually bg),
14248: * and ? denotes "don't care" bit (won't affect outcome).
14249: * Arrow --> points to start pixel denoted by irow,icol.
14250: *
14251: * *??? -??? turn=0 (no turn) is returned
14252: * -->*??? or -->-??? because the start pixel isn't
14253: * *??? -??? on an edge to begin with
14254: *
14255: * ---- **-- turn=0 returned because the
14256: * -->***- or -->***- line ends abruptly without
14257: * ---- ---- turning (even the second case)
14258: *
14259: * ---* ---* turn=0 returned because the
14260: * -->***- or -->**** line forms a Y or T rather
14261: * ---* ---* than turning
14262: *
14263: * ***- **** turn=+3 returned
14264: * -->***- or -->***- (outside corner)
14265: * ---- ----
14266: *
14267: * ***** ****- turn=-4 returned
14268: * -->***** or -->****- (inside corner)
14269: * ----* ----*
14270: *
14271: * ----* ----* turn=+4 returned
14272: * -->****- or -->***** (outside or inside corner)
14273: * ----- -----
14274: * ======================================================================= */
14275: /* --- entry point --- */
14276: int aafollowline (raster *rp, int irow, int icol, int direction)
14277: {
14278: /* -------------------------------------------------------------------------
14279: Allocations and Declarations
14280: -------------------------------------------------------------------------- */
14281: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
14282: int width=rp->width, height=rp->height; /* width, height of raster */
14283: int drow=0, dcol=0, /* delta row,col to follow line */
14284: jrow=irow, jcol=icol; /* current row,col following line */
14285: int bitval=1, /* value of rp bit at irow,icol */
14286: fgval=1, bgval=0, /* "fg" is whatever bitval is */
14287: bitminus=0, bitplus=0; /* value of left/down, right/up bit*/
14288: int isline=1, isedge=0; /*isline signals one-pixel wide line*/
14289: int turn = 0, /* detected turn back to caller */
14290: maxturn = maxfollow; /* don't follow more than max pixels*/
14291: /* -------------------------------------------------------------------------
14292: Initialization
14293: -------------------------------------------------------------------------- */
14294: /* --- check input --- */
14295: if ( irow<0 || irow>=height /* irow out-of-bounds */
14296: || icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
1.5 ! raeburn 14297: /* --- adjust maxturn for magstep --- */
! 14298: if ( 1 ) /* guard */
! 14299: if ( magstep > 1 && magstep <= 10 ) /* sanity check */
! 14300: maxturn *= magstep; /* factor in magnification */
1.3 albertel 14301: /* --- starting bit -- see if we're following a fg (usual), or bg line --- */
14302: bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
14303: fgval = bitval; bgval = (1-bitval); /* define "fg" as whatever bitval is*/
14304: /* --- set drow,dcol corresponding to desired direction --- */
14305: switch ( direction ) { /* determine drow,dcol for direction*/
14306: default: goto end_of_job; /* unrecognized direction arg */
14307: case 1: drow = (-1); break; /* follow line up/north */
14308: case -1: drow = 1; break; /* down/south */
14309: case 2: dcol = 1; break; /* right/east */
14310: case -2: dcol = (-1); break; } /* left/west */
14311: /* --- set bitminus and bitplus --- */
14312: if ( drow == 0 ) { /* we're following line right/left */
14313: if ( irow < height ) /* there's a pixel below current */
14314: bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */
14315: if ( irow > 0 ) /* there's a pixel above current */
14316: bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */
14317: if ( dcol == 0 ) { /* we're following line up/down */
14318: if ( icol < width ) /* there's a pixel to the right */
14319: bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */
14320: if ( icol > 0 ) /* there's a pixel to the left */
14321: bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */
14322: /* --- check for lack of line to follow --- */
14323: if ( bitval == bitplus /* starting pixel same as above */
14324: && bitval == bitminus ) /* and below (or right and left) */
14325: goto end_of_job; /* so there's no line to follow */
14326: /* --- set isline and isedge (already initted for isline) --- */
14327: if ( bitval == bitplus ) /* starting pixel same as above */
14328: { isedge = (-1); isline = 0; } /* so we're at an edge below */
14329: if ( bitval == bitminus ) /* starting pixel same as below */
14330: { isedge = 1; isline = 0; } /* so we're at an edge above */
14331: /* -------------------------------------------------------------------------
14332: follow line
14333: -------------------------------------------------------------------------- */
14334: while ( 1 ) { /* until turn found (or max) */
14335: /* --- local allocations and declarations --- */
14336: int dbitval=0, /* value of bit at jrow,jcol */
14337: dbitminus=0, dbitplus=0; /* value of left/down, right/up bit*/
14338: /* --- bump pixel count and indexes; check for max or end-of-raster --- */
14339: turn++; /* bump #pixels followed */
14340: jrow += drow; jcol += dcol; /* indexes of next pixel to check */
14341: if ( turn > maxturn /* already followed max #pixels */
14342: || jrow<0 || jrow>=height /* or jrow past end-of-raster */
14343: || jcol<0 || jcol>=width ) /* or jcol past end-of-raster */
14344: { turn = 0; goto end_of_job; } /* so quit without finding a turn */
14345: /* --- set current bit (dbitval) --- */
14346: dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/
14347: /* --- set dbitminus and dbitplus --- */
14348: if ( drow == 0 ) { /* we're following line right/left */
14349: if ( irow < height ) /* there's a pixel below current */
14350: dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */
14351: if ( irow > 0 ) /* there's a pixel above current */
14352: dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */
14353: if ( dcol == 0 ) { /* we're following line up/down */
14354: if ( icol < width ) /* there's a pixel to the right */
14355: dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */
14356: if ( icol > 0 ) /* there's a pixel to the left */
14357: dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */
14358: /* --- first check for abrupt end-of-line, or for T or Y --- */
14359: if ( isline != 0 ) /* abrupt end or T,Y must be a line*/
14360: if ( ( bgval == dbitval /* end-of-line if pixel flips to bg*/
14361: && bgval == dbitplus /* and bg same as above pixel */
14362: && bgval == dbitminus ) /* and below (or right and left) */
14363: || ( fgval == dbitplus /* T or Y if fg same as above pixel*/
14364: && fgval == dbitminus ) ) /* and below (or right and left) */
14365: { turn = 0; goto end_of_job; } /* so we're at a T or Y */
14366: /* --- check for turning line --- */
14367: if ( isline != 0 ) { /* turning line must be a line */
14368: if ( fgval == dbitminus ) /* turning down */
14369: { turn = -turn; goto end_of_job; } /* so return negative turn */
14370: else if ( fgval == dbitplus ) /* turning up */
14371: goto end_of_job; } /* so return positive turn */
14372: /* --- check for inside corner at edge --- */
14373: if ( isedge != 0 ) { /* inside corner must be a edge */
14374: if ( isedge < 0 && fgval == bitminus ) /* corner below */
14375: { turn = -turn; goto end_of_job; } /* so return negative turn */
14376: if ( isedge > 0 && fgval == bitplus ) /* corner above */
14377: goto end_of_job; } /* so return positive turn */
14378: /* --- check for abrupt end at edge --- */
14379: if ( isedge != 0 /* abrupt edge end must be an edge */
14380: && fgval == dbitval ) /* and line must not end */
14381: if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */
14382: || (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */
14383: { turn = 0; goto end_of_job; } /* so edge ended abruptly */
14384: /* --- check for outside corner at edge --- */
14385: if ( isedge != 0 /* outside corner must be a edge */
14386: && bgval == dbitval ) { /* and line must end */
14387: if ( isedge > 0 ) turn = -turn; /* outside turn down from edge above*/
14388: goto end_of_job; }
14389: } /* --- end-of-while(1) --- */
14390: /* -------------------------------------------------------------------------
14391: Back to caller with #rows or #cols traversed, and direction of detected turn
14392: -------------------------------------------------------------------------- */
14393: end_of_job:
14394: if ( msglevel>=99 && msgfp!=NULL ) /* debugging/diagnostic output */
14395: fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n",
14396: irow,icol,direction,turn);
14397: return ( turn );
14398: } /* --- end-of-function aafollowline() --- */
14399:
14400:
14401: /* ==========================================================================
14402: * Function: aagridnum ( rp, irow, icol )
14403: * Purpose: calculates gridnum, 0-511 (see Notes below),
14404: * for 3x3 grid centered at irow,icol
14405: * --------------------------------------------------------------------------
14406: * Arguments: rp (I) raster * to raster containing
14407: * bitmap image (to be anti-aliased)
14408: * irow (I) int containing row, 0...height-1,
14409: * at center of 3x3 grid
14410: * icol (I) int containing col, 0...width-1,
14411: * at center of 3x3 grid
14412: * --------------------------------------------------------------------------
14413: * Returns: ( int ) 0-511 grid number, or -1=any error
14414: * --------------------------------------------------------------------------
14415: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14416: * whose bit positions (and corresponding values) in gridnum are
14417: * 876 256 128 64
14418: * 504 = 32 1 16
14419: * 321 8 4 2
14420: * Thus, for example (*=pixel set/black, -=pixel not set/white),
14421: * *-- *-- -** (note that 209 is the
14422: * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset,
14423: * --* *** --- of 302)
14424: * o A set pixel is considered black, an unset pixel considered
14425: * white.
14426: * ======================================================================= */
14427: /* --- entry point --- */
14428: int aagridnum (raster *rp, int irow, int icol)
14429: {
14430: /* -------------------------------------------------------------------------
14431: Allocations and Declarations
14432: -------------------------------------------------------------------------- */
14433: pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
14434: int width=rp->width, height=rp->height, /* width, height of raster */
14435: imap = icol + irow*width; /* pixel index = icol + irow*width */
14436: int bitval=0, /* value of rp bit at irow,icol */
14437: nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
14438: ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0, /*compass pt names*/
14439: gridnum = (-1); /* grid# 0-511 for above 9 bits */
14440: /* -------------------------------------------------------------------------
14441: check input
14442: -------------------------------------------------------------------------- */
14443: if ( irow<0 || irow>=height /* irow out-of-bounds */
14444: || icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
14445: /* -------------------------------------------------------------------------
14446: get the 9 bits comprising the 3x3 grid centered at irow,icol
14447: -------------------------------------------------------------------------- */
14448: /* --- get center bit --- */
14449: bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
14450: /* --- get 8 surrounding bits --- */
14451: if ( irow > 0 ) /* nn (north) bit available */
14452: nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
14453: if ( irow < height-1 ) /* ss (south) bit available */
14454: ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
14455: if ( icol > 0 ) /* ww (west) bit available */
14456: { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
14457: if ( irow > 0 ) /* nw bit available */
14458: nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
14459: if ( irow < height-1 ) /* sw bit available */
14460: swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
14461: if ( icol < width-1 ) /* ee (east) bit available */
14462: { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
14463: if ( irow > 0 ) /* ne bit available */
14464: nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
14465: if ( irow < height-1 ) /* se bit available */
14466: sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
14467: /* --- set gridnum --- */
14468: gridnum = 0; /* clear all bits */
14469: if ( bitval ) gridnum = 1; /* set1bit(gridnum,0); */
14470: if ( nwbitval ) gridnum += 256; /* set1bit(gridnum,8); */
14471: if ( nnbitval ) gridnum += 128; /* set1bit(gridnum,7); */
14472: if ( nebitval ) gridnum += 64; /* set1bit(gridnum,6); */
14473: if ( wwbitval ) gridnum += 32; /* set1bit(gridnum,5); */
14474: if ( eebitval ) gridnum += 16; /* set1bit(gridnum,4); */
14475: if ( swbitval ) gridnum += 8; /* set1bit(gridnum,3); */
14476: if ( ssbitval ) gridnum += 4; /* set1bit(gridnum,2); */
14477: if ( sebitval ) gridnum += 2; /* set1bit(gridnum,1); */
14478: /* -------------------------------------------------------------------------
14479: Back to caller with gridnum coding 3x3 grid centered at irow,icol
14480: -------------------------------------------------------------------------- */
14481: end_of_job:
14482: return ( gridnum );
14483: } /* --- end-of-function aagridnum() --- */
14484:
14485:
14486: /* ==========================================================================
14487: * Function: aapatternnum ( gridnum )
14488: * Purpose: Looks up the pattern number 1...51
14489: * corresponding to the 3x3 pixel grid coded by gridnum 0=no
14490: * pixels set (white) to 511=all pixels set (black).
14491: * --------------------------------------------------------------------------
14492: * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
14493: * (see Notes below)
14494: * --------------------------------------------------------------------------
14495: * Returns: ( int ) 1 to 51, or -1=error
14496: * --------------------------------------------------------------------------
14497: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14498: * whose bit positions (and corresponding values) in gridnum are
14499: * 876 256 128 64
14500: * 504 = 32 1 16
14501: * 321 8 4 2
14502: * Thus, for example (*=pixel set/black, -=pixel not set/white),
14503: * *-- *-- -** (note that 209 is the
14504: * -*- = 259 *-- = 302 -** = 209 inverse, set<-->unset,
14505: * --* *** --- of 302)
14506: * o A set pixel is considered black, an unset pixel considered
14507: * white.
14508: * o Ignoring whether the center pixel is set or unset, and
14509: * taking rotation, reflection and inversion (set<-->unset)
14510: * symmetries into account, there are 32 unique pixel patterns.
14511: * If inversions are listed separately, there are 51 patterns.
14512: * o Here are the 51 unique patterns, with ? always denoting the
14513: * undetermined center pixel. At the upper-left corner of each
14514: * pattern is the "pattern index number" assigned to it in this
14515: * function. At the upper-right is the pattern's multiplicity,
14516: * i.e., the number of different patterns obtained by rotations
14517: * and reflection of the illustrated one. Inverse patters are
14518: * illustrated immediately beneath the original (the first three
14519: * four-pixel patterns have identical inverses).
14520: * -------------------------------------------------------------
14521: * No pixels set:
14522: * #1 1 (in this case, 1 signifies that rotation
14523: * --- and reflection give no different grids)
14524: * -?-
14525: * ---
14526: * Inverse, all eight pixels set
14527: * #2 1 (the inverse multiplicity is always the same)
14528: * ***
14529: * *?*
14530: * ***
14531: * -------------------------------------------------------------
14532: * One pixel set:
14533: * #3 4 #4 4
14534: * *-- -*-
14535: * -?- -?-
14536: * --- ---
14537: * Inverse, seven pixels set:
14538: * #5 4 #6 4
14539: * -** *-*
14540: * *?* *?*
14541: * *** ***
14542: * -------------------------------------------------------------
14543: * Two pixels set:
14544: * #7 8 #8 4 #9 8 10 2 11 4 12 2
14545: * **- *-* *-- *-- -*- -*-
14546: * -?- -?- -?* -?- -?* -?-
14547: * --- --- --- --* --- -*-
14548: * Inverse, six pixels set:
14549: * #13 8 14 4 15 8 16 2 17 4 18 2
14550: * --* -*- -** -** *-* *-*
14551: * *?* *?* *?- *?* *?- *?*
14552: * *** *** *** **- *** *-*
14553: * -------------------------------------------------------------
14554: * Three pixels set:
14555: * #19 4 20 8 21 8 22 8 23 8 24 4 25 4 26 4 27 4 28 4
14556: * *** **- **- **- **- **- *-* *-* -*- -*-
14557: * -?- -?* -?- -?- -?- *?- -?- -?- -?* -?*
14558: * --- --- --* -*- *-- --- --* -*- -*- *--
14559: * Inverse, five pixels set:
14560: * #29 4 30 8 31 8 32 8 33 8 34 4 35 4 36 4 37 4 38 4
14561: * --- --* --* --* --* --* -*- -*- *-* *-*
14562: * *?* *?- *?* *?* *?* -?* *?* *?* *?- *?-
14563: * *** *** **- *-* -** *** **- *-* *-* -**
14564: * -------------------------------------------------------------
14565: * Four pixels set (including inverses):
14566: * #39 8 40 4 41 8 42 8 43 4 44 4 45 8 46 1
14567: * *** **- **- *** *** **- **- *-*
14568: * -?* -?- -?* -?- -?- -?* -?* -?-
14569: * --- -** *-- --* -*- --* -*- *-*
14570: *
14571: * #47 8 48 4 49 4 50 8 51 1
14572: * --- --- --* --* -*-
14573: * *?* *?* *?- *?- *?*
14574: * **- *-* **- *-* -*-
14575: * ======================================================================= */
14576: /* --- entry point --- */
14577: int aapatternnum ( int gridnum )
14578: {
14579: /* -------------------------------------------------------------------------
14580: Allocations and Declarations
14581: -------------------------------------------------------------------------- */
14582: int pattern = (-1); /*pattern#, 1-51, for input gridnum*/
14583: /* ---
14584: * pattern number corresponding to input gridnum/2 code
14585: * ( gridnum/2 strips off gridnum's low bit because it's
14586: * the same pattern whether or not center pixel is set )
14587: * --- */
14588: static int patternnum[] = {
14589: 1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39, /* 0- 15 */
14590: 4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29, /* 16- 31 */
14591: 3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34, /* 32- 47 */
14592: 9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13, /* 48- 63 */
14593: 4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33, /* 64- 79 */
14594: 11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14, /* 80- 95 */
14595: 7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13, /* 96-111 */
14596: 20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5, /* 112-127 */
14597: 3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30, /* 128-143 */
14598: 7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13, /* 144-159 */
14599: 8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17, /* 160-175 */
14600: 23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6, /* 176-191 */
14601: 7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15, /* 192-207 */
14602: 24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5, /* 208-223 */
14603: 19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6, /* 224-239 */
14604: 39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2, /* 240-255 */
14605: -1 } ; /* --- end-of-patternnum[] --- */
14606: /* -------------------------------------------------------------------------
14607: look up pattern number for gridnum
14608: -------------------------------------------------------------------------- */
14609: /* --- first check input --- */
14610: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
14611: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
14612: pattern = patternnum[gridnum/2]; /* /2 strips off gridnum's low bit */
14613: if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */
14614: end_of_job:
14615: return ( pattern ); /* back to caller with pattern# */
14616: } /* --- end-of-function aapatternnum() --- */
14617:
14618:
14619: /* ==========================================================================
14620: * Function: aalookup ( gridnum )
14621: * Purpose: Looks up the grayscale value 0=white to 255=black
14622: * corresponding to the 3x3 pixel grid coded by gridnum 0=no
14623: * pixels set (white) to 511=all pixels set (black).
14624: * --------------------------------------------------------------------------
14625: * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
14626: * --------------------------------------------------------------------------
14627: * Returns: ( int ) 0=white to 255=black, or -1=error
14628: * --------------------------------------------------------------------------
14629: * Notes: o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
14630: * o A set pixel is considered black, an unset pixel considered
14631: * white. Likewise, the returned grayscale is 255 for black,
14632: * 0 for white. You'd more typically want to use 255-grayscale
14633: * so that 255 is white and 0 is black.
14634: * o The returned number is the (lowpass) antialiased grayscale
14635: * for the center pixel (gridnum bit 0) of the grid.
14636: * ======================================================================= */
14637: /* --- entry point --- */
14638: int aalookup ( int gridnum )
14639: {
14640: /* -------------------------------------------------------------------------
14641: Allocations and Declarations
14642: -------------------------------------------------------------------------- */
14643: int grayscale = (-1); /*returned grayscale, init for error*/
14644: int pattern = (-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
14645: int iscenter = gridnum&1; /*low-order bit set for center pixel*/
14646: /* --- gray scales --- */
14647: #define WHT 0
14648: #define LGT 64
14649: #define GRY 128
14650: #define DRK 192
14651: #define BLK 255
14652: #if 1
14653: /* ---
14654: * modified aapnm() grayscales (second try)
14655: * --- */
14656: /* --- grayscale for each pattern when center pixel set/black --- */
14657: static int grayscale1[] = { -1, /* [0] index not used */
14658: BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160, /* 1-10 */
14659: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */
14660: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
14661: BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
14662: 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
14663: 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
14664: -1 } ; /* --- end-of-grayscale1[] --- */
14665: /* --- grayscale for each pattern when center pixel not set/white --- */
14666: static int grayscale0[] = { -1, /* [0] index not used */
14667: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
14668: 64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
14669: /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
14670: WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
14671: 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
14672: 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
14673: -1 } ; /* --- end-of-grayscale0[] --- */
14674: #endif
14675: #if 0
14676: /* ---
14677: * modified aapnm() grayscales (first try)
14678: * --- */
14679: /* --- grayscale for each pattern when center pixel set/black --- */
14680: static int grayscale1[] = { -1, /* [0] index not used */
14681: BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK, /* 1-10 */
14682: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /* 1-10 */
14683: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
14684: BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191, /* 21-30 */
14685: /* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */ /* 21-30 */
14686: 204,BLK,204,BLK,217,204,BLK,191,GRY,BLK, /* 31-40 */
14687: /* 204,BLK,204,191,217,204,BLK,191,178,BLK, */ /* 31-40 */
14688: 178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204, /* 41-51 */
14689: /* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */
14690: -1 } ; /* --- end-of-grayscale1[] --- */
14691: /* --- grayscale for each pattern when center pixel not set/white --- */
14692: static int grayscale0[] = { -1, /* [0] index not used */
14693: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
14694: GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY, /* 11-20 */
14695: /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
14696: WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89, /* 21-30 */
14697: /* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */ /* 21-30 */
14698: 102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT, /* 31-40 */
14699: /* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */ /* 31-40 */
14700: 76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
14701: /* 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */
14702: -1 } ; /* --- end-of-grayscale0[] --- */
14703: #endif
14704: #if 0
14705: /* ---
14706: * these grayscales _exactly_ correspond to the aapnm() algorithm
14707: * --- */
14708: /* --- grayscale for each pattern when center pixel set/black --- */
14709: static int grayscale1[] = { -1, /* [0] index not used */
14710: BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, /* 1-10 */
14711: BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
14712: BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
14713: 204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
14714: 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
14715: -1 } ; /* --- end-of-grayscale1[] --- */
14716: /* --- grayscale for each pattern when center pixel not set/white --- */
14717: static int grayscale0[] = { -1, /* [0] index not used */
14718: WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /* 1-10 */
14719: 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
14720: WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
14721: 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
14722: 76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
14723: -1 } ; /* --- end-of-grayscale0[] --- */
14724: #endif
14725: /* -------------------------------------------------------------------------
14726: look up grayscale for gridnum
14727: -------------------------------------------------------------------------- */
14728: /* --- first check input --- */
14729: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
14730: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
14731: pattern = aapatternnum(gridnum); /* look up pattern number */
14732: if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */
14733: if ( ispatternnumcount ) { /* counts being accumulated */
14734: if (iscenter) patternnumcount1[pattern] += 1; /* bump diagnostic count */
14735: else patternnumcount0[pattern] += 1; }
14736: /* --- look up grayscale for this pattern --- */
14737: grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] );
14738: end_of_job:
14739: return ( grayscale ); /* back to caller with grayscale */
14740: } /* --- end-of-function aalookup() --- */
14741:
14742:
14743: /* ==========================================================================
14744: * Function: aalowpasslookup ( rp, bytemap, grayscale )
14745: * Purpose: calls aalookup() for each pixel in rp->bitmap
14746: * to create anti-aliased bytemap
14747: * --------------------------------------------------------------------------
14748: * Arguments: rp (I) raster * to raster whose bitmap
14749: * is to be anti-aliased
14750: * bytemap (O) intbyte * to bytemap, calculated
14751: * by calling aalookup() for each pixel
14752: * in rp->bitmap
14753: * grayscale (I) int containing number of grayscales
14754: * to be calculated, 0...grayscale-1
14755: * (should typically be given as 256)
14756: * --------------------------------------------------------------------------
14757: * Returns: ( int ) 1=success, 0=any error
14758: * --------------------------------------------------------------------------
14759: * Notes: o
14760: * ======================================================================= */
14761: /* --- entry point --- */
14762: int aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale)
14763: {
14764: /* -------------------------------------------------------------------------
14765: Allocations and Declarations
14766: -------------------------------------------------------------------------- */
14767: int width=rp->width, height=rp->height, /* width, height of raster */
14768: icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */
14769: int bgbitval=0 /*, fgbitval=1*/; /* background, foreground bitval */
14770: int bitval=0, /* value of rp bit at irow,icol */
14771: aabyteval=0; /* antialiased (or unchanged) value*/
14772: int gridnum=0, aagridnum(), /* grid# for 3x3 grid at irow,icol */
14773: aalookup(); /* table look up antialiased value*/
14774: /* -------------------------------------------------------------------------
14775: generate bytemap by table lookup for each pixel of bitmap
14776: -------------------------------------------------------------------------- */
14777: for ( irow=0; irow<height; irow++ )
14778: for ( icol=0; icol<width; icol++ )
14779: {
14780: /* --- get gridnum and center bit value, init aabyteval --- */
14781: gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
14782: bitval = (gridnum&1); /* center bit set if gridnum odd */
14783: aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
14784: imap++; /* first bump bitmap[] index */
14785: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
14786: /* --- look up antialiased value for this grid --- */
14787: aabyteval = aalookup(gridnum); /* look up on grid# */
14788: if ( aabyteval>=0 && aabyteval<=255 ) /* check for success */
14789: bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
14790: } /* --- end-of-for(irow,icol) --- */
14791: ispatternnumcount = 0; /* accumulate counts only once */
14792: /* -------------------------------------------------------------------------
14793: Back to caller with gray-scale anti-aliased bytemap
14794: -------------------------------------------------------------------------- */
14795: /*end_of_job:*/
14796: return ( 1 );
14797: } /* --- end-of-function aalowpasslookup() --- */
14798:
14799:
14800: /* ==========================================================================
1.1 albertel 14801: * Function: aasupsamp ( rp, aa, sf, grayscale )
14802: * Purpose: calculates a supersampled anti-aliased bytemap
14803: * for rp->bitmap, with each byte 0...grayscale-1
14804: * --------------------------------------------------------------------------
14805: * Arguments: rp (I) raster * to raster whose bitmap
14806: * is to be anti-aliased
14807: * aa (O) address of raster * to supersampled bytemap,
14808: * calculated by supersampling rp->bitmap
14809: * sf (I) int containing supersampling shrinkfactor
14810: * grayscale (I) int containing number of grayscales
14811: * to be calculated, 0...grayscale-1
14812: * (should typically be given as 256)
14813: * --------------------------------------------------------------------------
14814: * Returns: ( int ) 1=success, 0=any error
14815: * --------------------------------------------------------------------------
14816: * Notes: o If the center point of the box being averaged is black,
14817: * then the entire "average" is forced black (grayscale-1)
14818: * regardless of the surrounding points. This is my attempt
14819: * to avoid a "washed-out" appearance of thin (one-pixel-wide)
14820: * lines, which would otherwise turn from black to a gray shade.
14821: * ======================================================================= */
14822: /* --- entry point --- */
14823: int aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
14824: {
14825: /* -------------------------------------------------------------------------
14826: Allocations and Declarations
14827: -------------------------------------------------------------------------- */
14828: int status = 0; /* 1=success, 0=failure to caller */
14829: int rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
14830: heightrem=0, widthrem=0, /* rp+rem is a multiple of shrinkf */
14831: aaheight=0, aawidth=0, /* supersampled dimensions */
14832: aapixsz=8; /* output pixels are 8-bit bytes */
14833: int maxaaval=(-9999), /* max grayscale val set in matrix */
14834: isrescalemax=1; /* 1=rescale maxaaval to grayscale */
14835: int irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
14836: raster *aap=NULL, *new_raster(); /* raster for supersampled image */
14837: raster *aaweights(); /* get weight matrix applied to rp */
14838: static raster *aawts = NULL; /* aaweights() resultant matrix */
14839: static int prevshrink = NOVALUE, /* shrinkfactor from previous call */
14840: sumwts = 0; /* sum of weights */
14841: static int blackfrac = 40, /* force black if this many pts are */
14842: /*grayfrac = 20,*/
14843: maxwt = 10, /* max weight in weight matrix */
14844: minwtfrac=10, maxwtfrac=70; /* force light pts white, dark black*/
14845: int type_raster(), type_bytemap(); /* debugging display routines */
14846: int delete_raster(); /* delete old rasters */
14847: /* -------------------------------------------------------------------------
14848: Initialization
14849: -------------------------------------------------------------------------- */
14850: /* --- check args --- */
14851: if ( aa == NULL ) goto end_of_job; /* no ptr for return output arg */
14852: *aa = NULL; /* init null ptr for error return */
14853: if ( rp == NULL /* no ptr to input arg */
14854: || sf < 1 /* invalid shrink factor */
14855: || grayscale < 2 ) goto end_of_job; /* invalid grayscale */
14856: /* --- get weight matrix (or use current one) --- */
14857: if ( sf != prevshrink ) /* have new shrink factor */
14858: { if ( aawts != NULL ) /* have unneeded weight matrix */
14859: delete_raster(aawts); /* so free it */
14860: sumwts = 0; /* reinitialize sum of weights */
14861: aawts = aaweights(sf,sf); /* get new weight matrix */
14862: if ( aawts != NULL ) /* got weight matrix okay*/
14863: for ( jwt=0; jwt<sf; jwt++ ) /* for each row */
14864: for ( iwt=0; iwt<sf; iwt++ ) /* and each column */
14865: { int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
14866: if ( wt > maxwt ) /* don't overweight center pts */
14867: { wt = maxwt; /* scale it back */
14868: setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
14869: sumwts += wt; } /* add weight to sum */
14870: prevshrink = sf; } /* save new shrink factor */
14871: if ( msgfp!=NULL && msglevel>=999 )
14872: { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
14873: type_bytemap((intbyte *)aawts->pixmap,grayscale,
14874: aawts->width,aawts->height,msgfp); }
14875: /* --- calculate supersampled height,width and allocate output raster */
14876: heightrem = rpheight%sf; /* remainder after division... */
14877: widthrem = rpwidth%sf; /* ...by shrinkfactor */
14878: aaheight = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
14879: aawidth = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
14880: if ( msgfp!=NULL && msglevel>=999 )
14881: { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
14882: rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
14883: fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
14884: type_raster(rp,msgfp); } /* ascii image of rp raster */
14885: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
14886: == NULL ) goto end_of_job; /* quit if alloc fails */
14887: /* -------------------------------------------------------------------------
14888: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
14889: -------------------------------------------------------------------------- */
14890: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
14891: {
14892: for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
14893: {
14894: int aaval=0; /* weighted rpvals */
14895: int nrp=0, mrp=0; /* #rp bits set, #within matrix */
14896: for ( jwt=0; jwt<sf; jwt++ )
14897: for ( iwt=0; iwt<sf; iwt++ )
14898: {
14899: int i=irp+iwt, j=jrp+jwt; /* rp->pixmap point */
14900: int rpval = 0; /* rp->pixmap value at i,j */
14901: if ( i>=0 && i<rpwidth /* i within actual pixmap */
14902: && j>=0 && j<rpheight ) /* ditto j */
14903: { mrp++; /* count another bit within matrix */
14904: rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
14905: if ( rpval != 0 )
14906: { nrp++; /* count another bit set */
14907: aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
14908: } /* --- end-of-for(iwt,jwt) --- */
14909: if ( aaval > 0 ) /*normalize and rescale non-zero val*/
14910: { int aafrac = (100*aaval)/sumwts; /* weighted percent of black points */
14911: /*if((100*nrp)/mrp >=blackfrac)*/ /* many black interior pts */
14912: if( aafrac >= maxwtfrac ) /* high weight of sampledblack pts */
14913: aaval = grayscale-1; /* so set supersampled pt black */
14914: else if( aafrac <= minwtfrac ) /* low weight of sampledblack pts */
14915: aaval = 0; /* so set supersampled pt white */
14916: else /* rescale calculated weight */
14917: aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
14918: maxaaval = max2(maxaaval,aaval); /* largest aaval so far */
14919: if ( msgfp!=NULL && msglevel>=999 )
14920: fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
14921: " mrp,nrp=%d,%d aaval=%d\n",
14922: jrp,irp, jaa,iaa, mrp,nrp, aaval);
14923: if ( jaa<aaheight && iaa<aawidth ) /* bounds check */
14924: setpixel(aap,jaa,iaa,aaval); /*weighted val in supersamp raster*/
14925: else if( msgfp!=NULL && msglevel>=9 ) /* emit error if out-of-bounds */
14926: fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
14927: aaheight,aawidth, jaa,iaa);
14928: iaa++; /* bump aa col index */
14929: } /* --- end-of-for(irp) --- */
14930: jaa++; /* bump aa row index */
14931: } /* --- end-of-for(jrp) --- */
14932: /* --- rescale supersampled image so darkest points become black --- */
14933: if ( isrescalemax ) /* flag set to rescale maxaaval */
14934: {
14935: double scalef = ((double)(grayscale-1))/((double)maxaaval);
14936: for ( jaa=0; jaa<aaheight; jaa++ ) /* height */
14937: for ( iaa=0; iaa<aawidth; iaa++ ) /* width */
14938: { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
14939: aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
14940: aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
14941: if( aafrac >= blackfrac ) /* high weight of sampledblack pts */
14942: aaval = grayscale-1; /* so set supersampled pt black */
14943: else if( 0&&aafrac <= minwtfrac ) /* low weight of sampledblack pts */
14944: aaval = 0; /* so set supersampled pt white */
14945: setpixel(aap,jaa,iaa,aaval); } /* replace rescaled val in raster */
14946: } /* --- end-of-if(isrescalemax) --- */
14947: *aa = aap; /* return supersampled image*/
14948: status = 1; /* set successful status */
14949: if ( msgfp!=NULL && msglevel>=999 )
14950: { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
14951: type_bytemap((intbyte *)aap->pixmap,grayscale,
14952: aap->width,aap->height,msgfp); fflush(msgfp); }
14953: /* -------------------------------------------------------------------------
14954: Back to caller with gray-scale anti-aliased bytemap
14955: -------------------------------------------------------------------------- */
14956: end_of_job:
14957: return ( status );
14958: } /* --- end-of-function aasupsamp() --- */
14959:
14960:
14961: /* ==========================================================================
14962: * Function: aacolormap ( bytemap, nbytes, colors, colormap )
14963: * Purpose: searches bytemap, returning a list of its discrete values
14964: * in ascending order in colors[], and returning an "image"
1.5 ! raeburn 14965: * of bytemap (where values are replaced by colors[]
1.1 albertel 14966: * indexes) in colormap[].
14967: * --------------------------------------------------------------------------
14968: * Arguments: bytemap (I) intbyte * to bytemap containing
14969: * grayscale values (usually 0=white
14970: * through 255=black) for which colors[]
14971: * and colormap[] will be constructed.
14972: * nbytes (I) int containing #bytes in bytemap
14973: * (usually just #rows * #cols)
14974: * colors (O) intbyte * (to be interpreted as ints)
14975: * returning a list of the discrete/different
1.3 albertel 14976: * values in bytemap, in ascending value order,
14977: * and with gamma correction applied
1.1 albertel 14978: * colormap (O) intbyte * returning a bytemap "image",
14979: * i.e., in one-to-one pixel correspondence
14980: * with bytemap, but where the values have been
14981: * replaced with corresponding colors[] indexes.
14982: * --------------------------------------------------------------------------
14983: * Returns: ( int ) #colors in colors[], or 0 for any error
14984: * --------------------------------------------------------------------------
14985: * Notes: o
14986: * ======================================================================= */
14987: /* --- entry point --- */
14988: int aacolormap ( intbyte *bytemap, int nbytes,
14989: intbyte *colors, intbyte *colormap )
14990: {
14991: /* -------------------------------------------------------------------------
14992: Allocations and Declarations
14993: -------------------------------------------------------------------------- */
14994: int ncolors = 0, /* #different values in bytemap */
14995: igray, grayscale = 256; /* bytemap contains intbyte's */
14996: intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/
14997: int ibyte; /* bytemap/colormap index */
1.3 albertel 14998: int isscale = 0, /* true to scale largest val to 255*/
14999: isgamma = 1; /* true to apply gamma correction */
1.1 albertel 15000: int maxcolors = 0; /* maximum ncolors */
15001: /* -------------------------------------------------------------------------
15002: Accumulate colors[] from values occurring in bytemap
15003: -------------------------------------------------------------------------- */
15004: /* --- initialization --- */
15005: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
15006: == NULL ) goto end_of_job; /* signal error if malloc() failed */
15007: memset(bytevalues,0,grayscale); /* zero out bytevalues */
15008: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
15009: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
15010: bytevalues[(int)bytemap[ibyte]] = 1; /*use its value to index bytevalues*/
15011: /* --- collect the 1's indexes in colors[] --- */
15012: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
15013: if ( (int)bytevalues[igray] ) /*bytemap contains igray somewheres*/
15014: { colors[ncolors] = (intbyte)igray; /* so store igray in colors */
15015: bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
15016: if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
15017: bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
15018: ncolors++; } /* and bump #colors */
15019: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
15020: if ( isscale ) /* only rescale if requested */
15021: if ( ncolors > 1 ) /* and if not a "blank" raster */
15022: if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
15023: {
15024: /* --- multiply each colors[] by factor that scales largest to 255 --- */
15025: double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
15026: for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
15027: { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
15028: if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
15029: } /* --- end-of-if(isscale) --- */
1.3 albertel 15030: /* --- apply gamma correction --- */
15031: if ( isgamma /* only gamma correct if requested */
15032: && gammacorrection > 0.0001 ) /* and if we have gamma correction */
15033: if ( ncolors > 1 ) /* and if not a "blank" raster */
15034: if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
15035: {
15036: for ( igray=1; igray<ncolors; igray++ ) { /*gamma correct each colors[]*/
15037: int grayval = colors[igray], /* original 0=white to 255=black */
15038: gmax = grayscale-1; /* should be 255 */
15039: double dgray=((double)(gmax-grayval))/((double)gmax); /*0=black 1=white*/
15040: dgray = pow(dgray,(1.0/gammacorrection)); /* apply gamma correction */
15041: grayval = (int)( gmax*(1.0-dgray) + 0.5 ); /* convert back to grayval */
15042: colors[igray] = grayval; } /* store back in colors[] */
15043: } /* --- end-of-if(isgamma) --- */
1.1 albertel 15044: /* -------------------------------------------------------------------------
15045: Construct colormap
15046: -------------------------------------------------------------------------- */
15047: for ( ibyte=0; ibyte<nbytes; ibyte++ ) /* for each byte in bytemap */
15048: colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
15049: /* -------------------------------------------------------------------------
15050: back to caller with #colors, or 0 for any error
15051: -------------------------------------------------------------------------- */
15052: end_of_job:
15053: if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
15054: if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
15055: ncolors = maxcolors; /* return maximum to caller */
15056: return ( ncolors ); /* back with #colors, or 0=error */
15057: } /* --- end-of-function aacolormap() --- */
15058:
15059:
15060: /* ==========================================================================
15061: * Function: aaweights ( width, height )
15062: * Builds "canonical" weight matrix, width x height, in a raster
15063: * (see Notes below for discussion).
15064: * --------------------------------------------------------------------------
15065: * Arguments: width (I) int containing width (#cols) of returned
15066: * raster/matrix of weights
15067: * height (I) int containing height (#rows) of returned
15068: * raster/matrix of weights
15069: * --------------------------------------------------------------------------
15070: * Returns: ( raster * ) ptr to raster containing width x height
15071: * weight matrix, or NULL for any error
15072: * --------------------------------------------------------------------------
15073: * Notes: o For example, given width=7, height=5, builds the matrix
15074: * 1 2 3 4 3 2 1
15075: * 2 4 6 8 6 4 2
15076: * 3 6 9 12 9 6 3
15077: * 2 4 6 8 6 4 2
15078: * 1 2 3 4 3 2 1
15079: * If an even dimension given, the two center numbers stay
15080: * the same, e.g., 123321 for the top row if width=6.
15081: * o For an odd square n x n matrix, the sum of all n^2
15082: * weights will be ((n+1)/2)^4.
15083: * o The largest weight (in the allocated pixsz=8 raster) is 255,
15084: * so the largest square matrix is 31 x 31. Any weight that
15085: * tries to grow beyond 255 is held constant at 255.
15086: * o A new_raster(), pixsz=8, is allocated for the caller.
15087: * To avoid memory leaks, be sure to delete_raster() when done.
15088: * ======================================================================= */
15089: /* --- entry point --- */
15090: raster *aaweights ( int width, int height )
15091: {
15092: /* -------------------------------------------------------------------------
15093: Allocations and Declarations
15094: -------------------------------------------------------------------------- */
15095: raster *new_raster(), *weights=NULL; /* raster of weights returned */
15096: int irow=0, icol=0, /* height, width indexes */
15097: weight = 0; /*running weight, as per Notes above*/
15098: /* -------------------------------------------------------------------------
15099: Initialization
15100: -------------------------------------------------------------------------- */
15101: /* --- allocate raster for weights --- */
15102: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
15103: == NULL ) goto end_of_job; /* return NULL error if failed */
15104: /* -------------------------------------------------------------------------
15105: Fill weight matrix, as per Notes above
15106: -------------------------------------------------------------------------- */
15107: for ( irow=0; irow<height; irow++ ) /* outer loop over rows */
15108: for ( icol=0; icol<width; icol++ ) /* inner loop over cols */
15109: {
15110: int jrow = height-irow-1, /* backwards irow, height-1,...,0 */
15111: jcol = width-icol-1; /* backwards icol, width-1,...,0 */
15112: weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
15113: if ( aaalgorithm == 1 ) weight=1; /* force equal weights */
15114: setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
15115: } /* --- end-of-for(irow,icol) --- */
15116: end_of_job:
15117: return ( weights ); /* back with weights or NULL=error */
15118: } /* --- end-of-function aaweights() --- */
15119:
15120:
15121: /* ==========================================================================
15122: * Function: aawtpixel ( image, ipixel, weights, rotate )
15123: * Purpose: Applies matrix of weights to the pixels
15124: * surrounding ipixel in image, rotated clockwise
15125: * by rotate degrees (typically 0 or 30).
15126: * --------------------------------------------------------------------------
15127: * Arguments: image (I) raster * to bitmap (though it can be bytemap)
15128: * containing image with pixels to be averaged.
15129: * ipixel (I) int containing index (irow*width+icol) of
15130: * center pixel of image for weighted average.
15131: * weights (I) raster * to bytemap of relative weights
15132: * (0-255), whose dimensions (usually odd width
15133: * and odd height) determine the "subgrid" of
15134: * image surrounding ipixel to be averaged.
15135: * rotate (I) int containing degrees clockwise rotation
15136: * (typically 0 or 30), i.e., imagine weights
15137: * rotated clockwise and then averaging the
15138: * image pixels "underneath" it now.
15139: * --------------------------------------------------------------------------
15140: * Returns: ( int ) 0-255 weighted average, or -1 for any error
15141: * --------------------------------------------------------------------------
15142: * Notes: o The rotation matrix used (when requested) is
15143: * / x' \ / cos(theta) sin(theta)/a \ / x \
15144: * | | = | | | |
15145: * \ y' / \ -a*sin(theta) cos(theta) / \ y /
15146: * where a=1 (current default) is the pixel (not screen)
15147: * aspect ratio width:height, and theta is rotate (converted
15148: * from degrees to radians). Then x=col,y=row are the integer
15149: * pixel coords relative to the input center ipixel, and
15150: * x',y' are rotated coords which aren't necessarily integer.
15151: * The actual pixel used is the one nearest x',y'.
15152: * ======================================================================= */
15153: /* --- entry point --- */
15154: int aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
15155: {
15156: /* -------------------------------------------------------------------------
15157: Allocations and Declarations
15158: -------------------------------------------------------------------------- */
15159: int aaimgval = 0, /* weighted avg returned to caller */
15160: totwts=0, sumwts=0; /* total of all wts, sum wts used */
15161: int pixsz = image->pixsz, /* #bits per image pixel */
15162: black1=1, black8=255, /* black for 1-bit, 8-bit pixels */
15163: black = (pixsz==1? black1:black8), /* black value for our image */
15164: scalefactor = (black1+black8-black), /* only scale 1-bit images */
15165: iscenter = 0; /* set true if center pixel black */
15166: /* --- grid dimensions and indexes --- */
15167: int wtheight = weights->height, /* #rows in weight matrix */
15168: wtwidth = weights->width, /* #cols in weight matrix */
15169: imgheight = image->height, /* #rows in image */
15170: imgwidth = image->width; /* #cols in image */
15171: int wtrow, wtrow0 = wtheight/2, /* center row index for weights */
15172: wtcol, wtcol0 = wtwidth/2, /* center col index for weights */
15173: imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
15174: imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
15175: /* --- rotated grid variables --- */
15176: static int prevrotate = 0; /* rotate from previous call */
15177: static double costheta = 1.0, /* cosine for previous rotate */
15178: sintheta = 0.0; /* and sine for previous rotate */
15179: double a = 1.0; /* default aspect ratio */
15180: /* -------------------------------------------------------------------------
15181: Initialization
15182: -------------------------------------------------------------------------- */
15183: /* --- refresh trig functions for rotate when it changes --- */
15184: if ( rotate != prevrotate ) /* need new sine/cosine */
15185: { costheta = cos(((double)rotate)/57.29578); /*cos of rotate in radians*/
15186: sintheta = sin(((double)rotate)/57.29578); /*sin of rotate in radians*/
15187: prevrotate = rotate; } /* save current rotate as prev */
15188: /* -------------------------------------------------------------------------
15189: Calculate aapixel as weighted average over image points around ipixel
15190: -------------------------------------------------------------------------- */
15191: for ( wtrow=0; wtrow<wtheight; wtrow++ )
15192: for ( wtcol=0; wtcol<wtwidth; wtcol++ )
15193: {
15194: /* --- allocations and declarations --- */
15195: int wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
15196: int drow = wtrow - wtrow0, /* delta row offset from center */
15197: dcol = wtcol - wtcol0; /* delta col offset from center */
15198: int iscenter = 0; /* set true if center point black */
15199: /* --- initialization --- */
15200: totwts += wt; /* sum all weights */
15201: /* --- rotate (if requested) --- */
15202: if ( rotate != 0 ) /* non-zero rotation */
15203: {
15204: /* --- apply rotation matrix to (x=dcol,y=drow) --- */
15205: double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
15206: dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
15207: dy = -a*dx*sintheta + dy*costheta; /* dy becomes new dy' */
15208: dx = dtemp; /* just for notational convenience */
15209: /* --- replace original (drow,dcol) with nearest rotated point --- */
15210: drow = (int)(dy+0.5); /* round dy for nearest row */
15211: dcol = (int)(dx+0.5); /* round dx for nearest col */
15212: } /* --- end-of-if(rotate!=0) --- */
15213: /* --- select image pixel to be weighted --- */
15214: imgrow = imgrow0 + drow; /*apply displacement to center row*/
15215: imgcol = imgcol0 + dcol; /*apply displacement to center col*/
15216: /* --- if pixel in bounds, accumulate weighted average --- */
15217: if ( imgrow>=0 && imgrow<imgheight ) /* row is in bounds */
15218: if ( imgcol>=0 && imgcol<imgwidth ) /* and col is in bounds */
15219: {
15220: /* --- accumulate weighted average --- */
15221: int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
15222: aaimgval += wt*imgval; /* weighted sum of image values */
15223: sumwts += wt; /* and also sum weights used */
15224: /* --- check if center image pixel black --- */
15225: if ( drow==0 && dcol==0 ) /* this is center ipixel */
15226: if ( imgval==black ) /* and it's black */
15227: iscenter = 1; /* so set black center flag true */
15228: } /* --- end-of-if(bounds checks ok) --- */
15229: } /* --- end-of-for(irow,icol) --- */
15230: if ( 0 && iscenter ) /* center point is black */
15231: aaimgval = black8; /* so force average black */
15232: else /* center point not black */
15233: aaimgval = /* 0=white ... black */
15234: ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
15235: /*end_of_job:*/
15236: return ( aaimgval );
15237: } /* --- end-of-function aawtpixel() --- */
1.3 albertel 15238:
15239:
15240: /* ==========================================================================
15241: * Function: mimetexsetmsg ( newmsglevel, newmsgfp )
15242: * Purpose: Sets msglevel and msgfp, usually called from
15243: * an external driver (i.e., DRIVER not defined
15244: * in this module).
15245: * --------------------------------------------------------------------------
15246: * Arguments: newmsglevel (I) int containing new msglevel
15247: * (unchanged if newmsglevel<0)
15248: * newmsgfp (I) FILE * containing new msgfp
15249: * (unchanged if newmsgfp=NULL)
15250: * --------------------------------------------------------------------------
15251: * Returns: ( int ) always 1
15252: * --------------------------------------------------------------------------
15253: * Notes: o
15254: * ======================================================================= */
15255: /* --- entry point --- */
15256: int mimetexsetmsg ( int newmsglevel, FILE *newmsgfp )
15257: {
15258: /* -------------------------------------------------------------------------
15259: set msglevel and msgfp
15260: -------------------------------------------------------------------------- */
15261: if ( newmsglevel >= 0 ) msglevel = newmsglevel;
15262: if ( newmsgfp != NULL ) msgfp = newmsgfp;
15263: return ( 1 );
15264: } /* --- end-of-function mimetexsetmsg() --- */
1.1 albertel 15265: #endif /* PART3 */
15266:
15267: /* ---
15268: * PART1
15269: * ------ */
15270: #if !defined(PARTS) || defined(PART1)
15271: #ifdef DRIVER
15272: /* ==========================================================================
15273: * Function: main() driver for mimetex.c
15274: * Purpose: emits a mime xbitmap or gif image of a LaTeX math expression
15275: * entered either as
15276: * (1) html query string from a browser (most typical), or
15277: * (2) a query string from an html <form method="get">
15278: * whose <input name="formdata"> (mostly for demo), or
15279: * (3) command-line arguments (mostly to test).
15280: * If no input supplied, expression defaults to "f(x)=x^2",
15281: * treated as test (input method 3).
15282: * If args entered on command-line (or if no input supplied),
15283: * output is (usually) human-viewable ascii raster images on
15284: * stdout rather than the usual mime xbitmaps or gif images.
15285: * --------------------------------------------------------------------------
15286: * Command-Line Arguments:
15287: * When running mimeTeX from the command-line, rather than
15288: * from a browser, syntax is
15289: * ./mimetex [-d ] dump gif to stdout
15290: * [expression expression, e.g., x^2+y^2,
15291: * |-f input_file] or read expression from file
15292: * [-m msglevel] verbosity of debugging output
15293: * [-s fontsize] default fontsize, 0-5
15294: * -d Rather than ascii debugging output, mimeTeX dumps the
15295: * actual gif (or xbitmap) to stdout, e.g.,
15296: * ./mimetex -d x^2+y^2 > expression.gif
15297: * creates a gif file containing an image of x^2+y^2
15298: * -f Reads expression from input_file, and automatically
15299: * assumes -d switch. The input_file may contain the
15300: * expression on one line or spread out over many lines.
15301: * MimeTeX will concatanate all lines from input_file
15302: * to construct one long expression. Blanks, tabs, and
15303: * newlines will just be ignored.
15304: * -m 0-99, controls verbosity level for debugging output
15305: * (usually used only while testing code).
15306: * -s Font size, 0-5. As usual, the font size can
15307: * also be specified in the expression by a leading
15308: * preamble terminated by $, e.g., 3$f(x)=x^2 displays
15309: * f(x)=x^2 at font size 3. Default font size is 2.
15310: * --------------------------------------------------------------------------
15311: * Exits: 0=success, 1=some error
15312: * --------------------------------------------------------------------------
15313: * Notes: o For an executable that emits mime xbitmaps, compile as
15314: * cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
15315: * or, alternatively, for an executable that emits gif images
15316: * cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
15317: * or for gif images with anti-aliasing
15318: * cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
15319: * See Notes at top of file for other compile-line -D options.
15320: * o Move executable to your cgi-bin directory and either
15321: * point your browser to it directly in the form
15322: * http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
15323: * or put a tag in your html document of the form
15324: * <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
15325: * border=0 align=absmiddle>
15326: * where f(x)=x^2 (or any other expression) will be displayed
15327: * either as a mime xbitmap or gif image (as per -D flag).
15328: * ======================================================================= */
15329:
15330: /* -------------------------------------------------------------------------
15331: header files and other data
15332: -------------------------------------------------------------------------- */
15333: /* --- (additional) standard headers --- */
15334: /* --- other data --- */
15335: #ifdef DUMPENVIRON
15336: extern char **environ; /* environment information */
15337: #endif
15338:
15339: /* -------------------------------------------------------------------------
15340: globals for gif and png callback functions
15341: -------------------------------------------------------------------------- */
15342: GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */
15343: GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */
1.5 ! raeburn 15344: GLOBAL(int,raster_width,0); /* width of final/displayed image */
! 15345: GLOBAL(int,raster_height,0); /* height of final/displayed image */
! 15346: GLOBAL(int,raster_baseline,0); /* baseline of final/displayed image*/
1.1 albertel 15347: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
15348: #ifdef AA /* if anti-aliasing requested */
15349: #define ISAAVALUE 1 /* turn flag on */
15350: #else
15351: #define ISAAVALUE 0 /* else turn flag off */
15352: #endif
15353: GLOBAL(int,isaa,ISAAVALUE); /* set anti-aliasing flag */
15354:
15355: /* -------------------------------------------------------------------------
15356: logging data structure, and default data to be logged
15357: -------------------------------------------------------------------------- */
15358: /* --- logging data structure --- */
15359: #define logdata struct logdata_struct /* "typedef" for logdata_struct*/
15360: logdata
15361: {
15362: /* -----------------------------------------------------------------------
15363: environment variable name, max #chars to display, min msglevel to display
15364: ------------------------------------------------------------------------ */
15365: char *name; /* environment variable name */
15366: int maxlen; /* max #chars to display */
15367: int msglevel; /* min msglevel to display data */
15368: } ; /* --- end-of-logdata_struct --- */
15369: /* --- data logged by mimeTeX --- */
15370: STATIC logdata mimelog[]
15371: #ifdef INITVALS
15372: =
15373: {
15374: /* ------ variable ------ maxlen msglevel ----- */
15375: { "QUERY_STRING", 999, 4 },
15376: { "REMOTE_ADDR", 999, 3 },
15377: { "HTTP_REFERER", 999, 3 },
15378: { "REQUEST_URI", 999, 5 },
15379: { "HTTP_USER_AGENT", 999, 3 },
15380: { "HTTP_X_FORWARDED_FOR", 999, 3 },
15381: { NULL, -1, -1 } /* trailer record */
15382: } /* --- end-of-mimelog[] --- */
15383: #endif
15384: ;
15385:
15386:
15387: /* --- entry point --- */
15388: int main ( int argc, char *argv[]
15389: #ifdef DUMPENVP
15390: , char *envp[]
15391: #endif
15392: )
15393: {
15394: /* -------------------------------------------------------------------------
15395: Allocations and Declarations
15396: -------------------------------------------------------------------------- */
15397: /* --- expression to be emitted --- */
1.3 albertel 15398: static char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */
1.1 albertel 15399: char *expression = exprbuffer; /* ptr to expression */
15400: int size = NORMALSIZE; /* default font size */
15401: char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
15402: char *mimeprep(); /* preprocess expression */
15403: int unescape_url(); /* convert %xx's to ascii chars */
15404: int emitcache(); /* emit cached image if it exists */
15405: int isquery = 0, /* true if input from QUERY_STRING */
15406: isqempty = 0, /* true if query string empty */
1.2 albertel 15407: isqforce = 0, /* true to force query emulation */
1.1 albertel 15408: isqlogging = 0, /* true if logging in query mode */
15409: isformdata = 0, /* true if input from html form */
1.2 albertel 15410: isinmemory = 1, /* true to generate image in memory*/
15411: isdumpimage = 0, /* true to dump image on stdout */
15412: isdumpbuffer = 0; /* true to dump to memory buffer */
1.1 albertel 15413: /* --- rasterization --- */
1.2 albertel 15414: subraster *rasterize(), *sp=NULL; /* rasterize expression */
15415: raster *border_raster(), *bp=NULL; /* put a border around raster */
15416: int delete_subraster(); /* for clean-up at end-of-job */
1.1 albertel 15417: int type_raster(), type_bytemap(), /* screen dump function prototypes */
15418: xbitmap_raster(); /* mime xbitmap output function */
15419: /* --- http_referer --- */
15420: char *referer = REFERER; /* http_referer must contain this */
1.5 ! raeburn 15421: char *inputreferer = INPUTREFERER; /*http_referer's permitted to \input*/
! 15422: int reflevels = REFLEVELS, urlncmp(); /* cmp http_referer,server_name */
! 15423: int strreplace(); /* replace SERVER_NAME in errmsg */
! 15424: char *urlprune(); /* prune referer_match */
1.1 albertel 15425: struct { char *referer; int msgnum; } /* http_referer can't contain this */
15426: denyreferer[] = { /* referer table to deny access to */
15427: #ifdef DENYREFERER
15428: #include DENYREFERER /* e.g., {"",1}, for no referer */
15429: #endif
15430: { NULL, -999 } }; /* trailer */
1.5 ! raeburn 15431: char *http_referer = getenv("HTTP_REFERER"), /* referer using mimeTeX */
! 15432: *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */
! 15433: *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */
! 15434: *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
! 15435: (!isempty(server_name)?server_name:(NULL))); /* or server_name */
! 15436: int ishttpreferer = (isempty(http_referer)?0:1);
1.1 albertel 15437: int isstrstr(); /* search http_referer for referer */
15438: int isinvalidreferer = 0; /* true for inavlid referer */
15439: int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/
15440: /* --- gif --- */
15441: #if defined(GIF)
15442: int GetPixel(); /* feed pixels to gifsave library */
15443: int GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
15444: void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
15445: #endif
15446: char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
1.3 albertel 15447: gif_buffer[MAXGIFSZ] = "\000", /* or gif written in memory buffer */
1.1 albertel 15448: cachefile[256] = "\000", /* full path and name to cache file*/
15449: *md5str(); /* md5 has of expression */
15450: int maxage = 7200; /* max-age is two hours */
1.4 riegler 15451: int valign = (-9999); /*Vertical-Align:baseline-(height-1)*/
1.2 albertel 15452: /* --- pbm/pgm (-g switch) --- */
15453: int ispbmpgm = 0; /* true to write pbm/pgm file */
15454: int type_pbmpgm(), ptype=0; /* entry point, graphic format */
15455: char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
1.1 albertel 15456: /* --- anti-aliasing --- */
15457: intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
15458: colors[256]; /* grayscale vals in bytemap */
15459: int aalowpass(), aapnm(), /*lowpass filters for anti-aliasing*/
15460: grayscale = 256; /* 0-255 grayscales in 8-bit bytes */
15461: int ncolors=2, /* #colors (2=b&w) */
15462: aacolormap(); /* build colormap from bytemap */
1.3 albertel 15463: int ipattern; /*patternnumcount[] index diagnostic*/
1.5 ! raeburn 15464: /* --- advertisement preprocessing --- */
! 15465: int advertisement(), crc16(); /*wrap expression in advertisement*/
! 15466: char *adtemplate = NULL; /* usually use default message */
! 15467: char *host_showad = HOST_SHOWAD; /* show ads only on this host */
1.1 albertel 15468: /* --- messages --- */
15469: char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/
15470: cachelog[256] = CACHELOG; /* cached image log in cachepath/ */
15471: char *timestamp(); /* time stamp for logged messages */
1.5 ! raeburn 15472: char *strdetex(); /* remove math chars from messages */
1.1 albertel 15473: int logger(); /* logs environ variables */
15474: int ismonth(); /* check argv[0] for current month */
15475: char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */
15476: char *dashes = /* separates logfile entries */
15477: "--------------------------------------------------------------------------";
1.5 ! raeburn 15478: char *invalid_referer_msg = msgtable[invmsgnum]; /*msg to invalid referer*/
! 15479: char *invalid_referer_match = msgtable[refmsgnum]; /*referer isn't host*/
1.1 albertel 15480: /* -------------------------------------------------------------------------
15481: initialization
15482: -------------------------------------------------------------------------- */
15483: /* --- run optional system command string --- */
15484: #ifdef SYSTEM
15485: system(SYSTEM);
15486: #endif
15487: /* --- set global variables --- */
1.5 ! raeburn 15488: daemonlevel++; /* signal other funcs to reset */
1.1 albertel 15489: msgfp = stdout; /* for comamnd-line mode output */
15490: isss = issupersampling; /* set supersampling flag */
1.4 riegler 15491: isemitcontenttype = 1; /* true to emit mime content-type */
15492: iscachecontenttype = 0; /* true to cache mime content-type */
15493: *contenttype = '\000'; /* reset content-type:, etc. cache */
1.5 ! raeburn 15494: isnomath = 0; /* true to inhibit math mode */
! 15495: seclevel = SECURITY; /* overall security level */
! 15496: inputseclevel = INPUTSECURITY; /* security level for \input{} */
! 15497: counterseclevel = COUNTERSECURITY; /* security level for \counter{} */
! 15498: environseclevel = ENVIRONSECURITY; /* security level for \environ */
! 15499: ninputcmds = 0; /* reset count of \input commands */
! 15500: exitstatus=0; errorstatus=ERRORSTATUS; /* reset exit/error status */
1.4 riegler 15501: iscaching = ISCACHING; /* true if caching images */
15502: if ( iscaching ) { /* images are being cached */
15503: strcpy(cachepath,CACHEPATH); /* relative path to cached files */
15504: if ( *cachepath == '%' ) { /* leading % signals cache headers */
15505: iscachecontenttype = 1; /* signal caching mime content-type*/
1.5 ! raeburn 15506: strsqueeze(cachepath,1); } } /* and squeeze out leading % char */
1.2 albertel 15507: gifSize = 0; /* signal that image not in memory */
1.4 riegler 15508: fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */
15509: bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */
1.1 albertel 15510: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
1.3 albertel 15511: for ( ipattern=1; ipattern<=51; ipattern++ )
15512: patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
1.1 albertel 15513: /* ---
15514: * check QUERY_STRING query for expression overriding command-line arg
15515: * ------------------------------------------------------------------- */
15516: if ( query != NULL ) /* check query string from environ */
1.5 ! raeburn 15517: if ( strlen(query) >= 1 ) { /* caller gave us a query string */
! 15518: strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
! 15519: expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
! 15520: if ( 0 ) /*true to remove leading whitespace*/
! 15521: while ( isspace(*expression) && *expression!='\000' )
! 15522: {strsqueeze(expression,1);} /* squeeze out white space */
! 15523: isquery = 1; } /* and set isquery flag */
! 15524: if ( !isquery ) { /* empty query string */
! 15525: char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
! 15526: *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
! 15527: if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */
! 15528: isquery = 1; /* set flag to signal query */
! 15529: if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */
! 15530: strcpy(expression, /* and give user an error message */
! 15531: "\\red\\small\\rm\\fbox{\\begin{gather}\\LaTeX~expression~not~supplied"
! 15532: "\\\\i.e.,~no~?query\\_string~given~to~mimetex.cgi\\end{gather}}"); }
! 15533: isqempty = 1; /* signal empty query string */
1.1 albertel 15534: } /* --- end-of-if(!isquery) --- */
15535: /* ---
15536: * process command-line input args (if not a query)
15537: * ------------------------------------------------ */
15538: if ( !isquery /* don't have an html query string */
15539: || ( /*isqempty &&*/ argc>1) ) /* or have command-line args */
15540: {
15541: char *argsignal = ARGSIGNAL, /* signals start of mimeTeX args */
15542: stopsignal[32] = "--"; /* default Unix end-of-args signal */
15543: int iarg=0, argnum=0, /*argv[] index for command-line args*/
15544: exprarg = 0, /* argv[] index for expression */
15545: infilearg = 0, /* argv[] index for infile */
15546: nswitches = 0, /* number of -switches */
15547: isstopsignal = 0, /* true after stopsignal found */
15548: isstrict = 1/*iswindows*/, /* true for strict arg checking */
15549: /*nb, windows has apache "x -3" bug*/
15550: nargs=0, nbadargs=0, /* number of arguments, bad ones */
15551: maxbadargs = (isstrict?0:1), /*assume query if too many bad args*/
15552: isgoodargs = 0; /* true to accept command-line args*/
15553: if ( argsignal != NULL ) /* if compiled with -DARGSIGNAL */
15554: while ( argc > ++iarg ) /* check each argv[] for argsignal */
15555: if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
15556: { argnum = iarg; /* got it, start parsing next arg */
15557: break; } /* stop looking for argsignal */
15558: while ( argc > ++argnum ) /* check for switches and values, */
15559: {
15560: nargs++; /* count another command-line arg */
15561: if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
15562: { isstopsignal = 1; /* so set stopsignal flag */
15563: continue; } /* and get expression after it */
15564: if ( !isstopsignal /* haven't seen stopsignal switch */
15565: && *argv[argnum] == '-' ) /* and have some '-' switch */
15566: {
1.2 albertel 15567: char *field = argv[argnum] + 1; /* ptr to char(s) following - */
15568: char flag = tolower(*field); /* single char following '-' */
15569: int arglen = strlen(field); /* #chars following - */
1.1 albertel 15570: argnum++; /* arg following flag/switch is usually its value */
15571: nswitches++; /* another switch on command line */
1.2 albertel 15572: if ( isstrict && /* if strict checking then... */
15573: !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
1.1 albertel 15574: { nbadargs++; argnum--; } /* so ignore longer -xxx switch */
15575: else /* process single-char -x switch */
15576: switch ( flag ) { /* see what user wants to tell us */
15577: /* --- ignore uninterpreted flag --- */
15578: default: nbadargs++; argnum--; break;
15579: /* --- adjustable program parameters (not checking input) --- */
1.2 albertel 15580: case 'b': isdumpimage++; isdumpbuffer++; argnum--; break;
1.1 albertel 15581: case 'd': isdumpimage++; argnum--; break;
15582: case 'e': isdumpimage++; gif_outfile=argv[argnum]; break;
15583: case 'f': isdumpimage++; infilearg=argnum; break;
1.2 albertel 15584: case 'g': ispbmpgm++;
15585: if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
15586: if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
15587: else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
1.5 ! raeburn 15588: case 'm': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break;
1.3 albertel 15589: case 'o': istransparent = (istransparent?0:1); argnum--; break;
1.2 albertel 15590: case 'q': isqforce = 1; argnum--; break;
1.5 ! raeburn 15591: case 's': if ( argnum < argc ) size = atoi(argv[argnum]); break;
1.1 albertel 15592: } /* --- end-of-switch(flag) --- */
15593: } /* --- end-of-if(*argv[argnum]=='-') --- */
15594: else /* expression if arg not a -flag */
15595: if ( infilearg == 0 ) /* no infile arg yet */
15596: { if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
15597: exprarg = argnum; /* but take last expression */
15598: /*infilearg = (-1);*/ } /* and set infilearg */
15599: else nbadargs++; /* infile and expression invalid */
15600: } /* --- end-of-while(argc>++argnum) --- */
15601: if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
1.2 albertel 15602: { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
15603: argc,progname,nargs,nbadargs);
15604: fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
15605: cachepath,pathprefix); }
1.1 albertel 15606: /* ---
15607: * decide whether command-line input overrides query_string
15608: * -------------------------------------------------------- */
15609: if ( isdumpimage > 2 ) nbadargs++; /* duplicate/conflicting -switch */
15610: isgoodargs = ( !isstrict /*good if not doing strict checking*/
15611: || !isquery /* or if no query, must use args */
15612: || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
15613: /* ---
15614: * take expression from command-line args
15615: * -------------------------------------- */
15616: if ( isgoodargs && exprarg > 0 /* good expression on command line */
15617: && infilearg <= 0 ) /* and not given in input file */
15618: if ( !isquery /* no conflict if no query_string */
15619: || nswitches > 0 ) /* explicit -switch(es) also given */
1.3 albertel 15620: { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/
15621: expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
1.1 albertel 15622: isquery = 0; } /* and not from a query_string */
15623: /* ---
15624: * or read expression from input file
15625: * ---------------------------------- */
15626: if ( isgoodargs && infilearg > 0 ) /* have a good -f arg */
15627: {
15628: FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
15629: if ( infile != (FILE *)NULL ) /* opened input file successfully */
1.3 albertel 15630: { char instring[MAXLINESZ+1]; /* line from file */
15631: int exprsz = 0; /* total #bytes read from file */
1.1 albertel 15632: isquery = 0; /* file input, not a query_string */
15633: *expression = '\000'; /* start expresion as empty string */
1.3 albertel 15634: while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/
15635: if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */
15636: strcat(expression,instring); /* concat line to end of expression*/
15637: exprsz += strlen(instring); } /* update expression buffer length */
1.1 albertel 15638: fclose ( infile ); } /*close input file after reading expression*/
15639: } /* --- end-of-if(infilearg>0) --- */
1.2 albertel 15640: /* ---
1.3 albertel 15641: * xlate +++'s to blanks only if query
15642: * ----------------------------------- */
15643: if ( !isquery ) isplusblank = 0; /* don't xlate +++'s to blanks */
15644: /* ---
1.2 albertel 15645: * check if emulating query (for testing)
15646: * -------------------------------------- */
15647: if ( isqforce ) isquery = 1; /* emulate query string processing */
15648: /* ---
15649: * check if emitting pbm/pgm graphic
15650: * --------------------------------- */
15651: if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
15652: if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
15653: if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
15654: { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
15655: gif_outfile = (char *)NULL; /* reset gif output file */
15656: /*isdumpimage--;*/ } /* and decrement -e count */
1.1 albertel 15657: } /* --- end-of-if(!isquery) --- */
15658: /* ---
15659: * check for <form> input
15660: * ---------------------- */
1.3 albertel 15661: if ( isquery ) { /* must be <form method="get"> */
1.1 albertel 15662: if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
15663: { char *delim=strchr(expression,'='); /* find equal following formdata */
15664: if ( delim != (char *)NULL ) /* found unescaped equal sign */
1.5 ! raeburn 15665: {strsqueezep(expression,delim+1);} /* so shift name= out */
1.1 albertel 15666: while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
15667: *delim = ' '; /* is "shorthand" for blank space */
15668: /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */
15669: unescape_url(expression,0); /* convert all %xx's to chars */
15670: unescape_url(expression,0); /* repeat */
1.3 albertel 15671: if(0) msglevel = FORMLEVEL; /* msglevel for forms */
1.1 albertel 15672: isformdata = 1; } /* set flag to signal form data */
15673: else /* --- query, but not <form> input --- */
1.3 albertel 15674: unescape_url(expression,0); } /* convert _all_ %xx's to chars */
1.1 albertel 15675: /* ---
1.5 ! raeburn 15676: * check queries for prefixes/suffixes/embedded that might cause problems
! 15677: * ---------------------------------------------------------------------- */
! 15678: /* --- expression whose last char is \ --- */
! 15679: if ( lastchar(expression) == '\\' ) /* last char is backslash */
! 15680: strcat(expression," "); /* assume "\ " lost the final space*/
! 15681: /* ---
1.1 albertel 15682: * check queries for embedded prefixes signalling special processing
15683: * ----------------------------------------------------------------- */
15684: if ( isquery ) /* only check queries */
15685: {
15686: /* --- check for msglevel=###$ prefix --- */
15687: if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
15688: { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
15689: if ( delim != (char *)NULL ) /* check that we found delim */
15690: { *delim = '\000'; /* replace delim with null */
15691: if ( seclevel <= 9 ) /* permit msglevel specification */
15692: msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
1.5 ! raeburn 15693: strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
1.1 albertel 15694: /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
15695: if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
15696: { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
15697: if ( delim != (char *)NULL ) /* check that we found delim */
15698: { *delim = '\000'; /* replace delim with null */
15699: if ( seclevel <= 3 ) /* permit logfile specification */
15700: strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */
1.5 ! raeburn 15701: strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
1.1 albertel 15702: } /* --- end-of-if(isquery) --- */
15703: /* ---
15704: * log query (e.g., for debugging)
15705: * ------------------------------- */
15706: if ( isquery ) /* only log query_string's */
15707: if ( msglevel >= LOGLEVEL /* check if logging */
15708: && seclevel <= 5 ) /* and if logging permitted */
15709: if ( logfile != NULL ) /* if a logfile is given */
1.3 albertel 15710: if ( *logfile != '\000' ) { /*and if it's not an empty string*/
1.1 albertel 15711: if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */
15712: != NULL ) /* ignore logging if can't open */
15713: {
15714: /* --- default logging --- */
15715: logger(msgfp,msglevel,expression,mimelog); /* log query */
15716: /* --- additional debug logging (argv and environment) --- */
15717: if ( msglevel >= 9 ) /* log environment */
15718: { int i; /*char name[999],*value;*/
15719: fprintf(msgfp,"Command-line arguments...\n");
15720: if ( argc < 1 ) /* no command-line args */
15721: fprintf(msgfp," ...argc=%d, no argv[] variables\n",argc);
15722: else
15723: for ( i=0; i<argc; i++ ) /* display all argv[]'s */
15724: fprintf(msgfp," argv[%d] = \"%s\"\n",i,argv[i]);
15725: #ifdef DUMPENVP /* char *envp[] available for dump */
15726: fprintf(msgfp,"Environment variables (using envp[])...\n");
15727: if ( envp == (char **)NULL ) /* envp not provided */
15728: fprintf(msgfp," ...envp[] environment variables not available\n");
15729: else
15730: for ( i=0; ; i++ ) /* display all envp[]'s */
15731: if ( envp[i] == (char *)NULL ) break;
15732: else fprintf(msgfp," envp[%d] = \"%s\"\n",i,envp[i]);
15733: #endif /* --- DUMPENVP ---*/
15734: #ifdef DUMPENVIRON /* skip what should be redundant output */
15735: fprintf(msgfp,"Environment variables (using environ)...\n");
15736: if ( environ == (char **)NULL ) /* environ not provided */
15737: fprintf(msgfp," ...extern environ variables not available\n");
15738: else
15739: for ( i=0; ; i++ ) /*display environ[] and getenv()'s*/
15740: if ( environ[i] == (char *)NULL ) break;
15741: else {
15742: strcpy(name,environ[i]); /* set up name for getenv() arg */
15743: if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
15744: { *value = '\000'; /* got it, so null-terminate name */
15745: value = getenv(name); } /* and look up name using getenv() */
15746: else strcpy(name,"NULL"); /* missing = delim in environ[i] */
15747: fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
15748: i,environ[i],name,(value==NULL?"NULL":value));
15749: } /* --- end-of-if/else --- */
15750: #endif /* --- DUMPENVIRON ---*/
15751: } /* --- end-of-if(msglevel>=9) --- */
15752: /* --- close log file if no longer needed --- */
15753: if ( msglevel < DBGLEVEL ) /* logging, but not debugging */
15754: { fprintf(msgfp,"%s\n",dashes); /* so log separator line, */
15755: fclose(msgfp); /* close logfile immediately, */
15756: msgfp = NULL; } /* and reset msgfp pointer */
15757: else
15758: isqlogging = 1; /* set query logging flag */
15759: } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
15760: else /* couldn't open logfile */
1.3 albertel 15761: msglevel = 0; } /* can't emit messages */
1.1 albertel 15762: /* ---
15763: * prepend prefix to submitted expression
15764: * -------------------------------------- */
15765: if ( 1 || isquery ) /* queries or command-line */
15766: if ( *exprprefix != '\000' ) /* we have a prefix string */
15767: { int npref = strlen(exprprefix); /* #chars in prefix */
1.5 ! raeburn 15768: memmove(expression+npref+1,expression,strlen(expression)+1);/*make room*/
1.1 albertel 15769: memcpy(expression,exprprefix,npref); /* copy prefix into expression */
15770: expression[npref] = '{'; /* followed by { */
15771: strcat(expression,"}"); } /* and terminating } to balance { */
15772: /* ---
1.5 ! raeburn 15773: * check if http_referer is allowed to use this image and to use \input{}
! 15774: * ---------------------------------------------------------------------- */
! 15775: if ( isquery ) { /* not relevant if "interactive" */
! 15776: /* --- check -DREFERER=\"comma,separated,list\" of valid referers --- */
! 15777: if ( referer != NULL ) { /* compiled with -DREFERER=\"...\" */
! 15778: if ( strcmp(referer,"month") != 0 ) /* but it's *only* "month" signal */
! 15779: if ( ishttpreferer ) /* or called "standalone" */
! 15780: if ( !isstrstr(http_referer,referer,0) ) { /* invalid http_referer */
! 15781: expression = invalid_referer_msg; /* so give user error message */
! 15782: isinvalidreferer = 1; } } /* and signal invalid referer */
! 15783: else /* compiled without -DREFERER= */
! 15784: if ( reflevels > 0 ) { /*match referer unless -DREFLEVELS=0*/
! 15785: /* --- check topmost levels of http_referer against http_host --- */
! 15786: if ( ishttpreferer /* have http_referer */
! 15787: && !isempty(referer_match) ) /* and something to match it with */
! 15788: if ( !urlncmp(http_referer,referer_match,reflevels) ) { /*match failed*/
! 15789: strcpy(exprbuffer,invalid_referer_match); /* init error message */
! 15790: strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
! 15791: strdetex(urlprune(referer_match,reflevels),1),0);/*with referer_match*/
! 15792: isinvalidreferer = 1; } /* and signal invalid referer */
! 15793: } /* --- end-of-if(reflevels>0) --- */
! 15794: /* --- check -DINPUTREFERER=\"comma,separated,list\" of \input users --- */
! 15795: inputseclevel = INPUTSECURITY; /* set default input security */
! 15796: if ( inputreferer != NULL ) { /* compiled with -DINPUTREFERER= */
! 15797: if ( http_referer == NULL ) /* but no http_referer given */
! 15798: inputseclevel = (-1); /* unknown user can't \input{} */
! 15799: else /*have inputreferer and http_referer*/
! 15800: if ( !isstrstr(http_referer,inputreferer,0) ) /*http_referer can't \input*/
! 15801: inputseclevel = (-1); /* this known user can't \input{} */
! 15802: } /* --- end-of-if(inputreferer!=NULL) --- */
! 15803: } /* --- end-of-if(isquery) --- */
1.1 albertel 15804: /* ---
15805: * check if referer contains "month" signal
15806: * ---------------------------------------- */
15807: if ( isquery ) /* not relevant if "interactive" */
15808: if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */
15809: if ( !isinvalidreferer ) /* nor if already invalid referer */
15810: if ( strstr(referer,"month") != NULL ) /* month check requested */
15811: if ( !ismonth(progname) ) /* not executed as mimetexJan-Dec */
15812: { expression = invalid_referer_msg; /* so give user error message */
15813: isinvalidreferer = 1; } /* and signal invalid referer */
15814: /* ---
15815: * check if http_referer is to be denied access
15816: * -------------------------------------------- */
15817: if ( isquery ) /* not relevant if "interactive" */
15818: if ( !isinvalidreferer ) /* nor if already invalid referer */
15819: { int iref=0, msgnum=(-999); /* denyreferer index, message# */
15820: for ( iref=0; msgnum<0; iref++ ) { /* run through denyreferer[] table */
15821: char *deny = denyreferer[iref].referer; /* referer to be denied */
15822: if ( deny == NULL ) break; /* null signals end-of-table */
15823: if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
15824: {fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
15825: iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
15826: if ( *deny == '\000' ) /* signal to check for no referer */
15827: { if ( http_referer == NULL ) /* http_referer not supplied */
15828: msgnum = denyreferer[iref].msgnum; } /* so set message# */
15829: else /* have referer to check for */
15830: if ( http_referer != NULL ) /* and have referer to be checked */
15831: if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
15832: msgnum = denyreferer[iref].msgnum; /* so set message# */
15833: } /* --- end-of-for(iref) --- */
15834: if ( msgnum >= 0 ) /* deny access to this referer */
15835: { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
15836: expression = msgtable[msgnum]; /* set user error message */
15837: isinvalidreferer = 1; } /* and signal invalid referer */
15838: } /* --- end-of-if(!isinvalidreferer) --- */
15839: /* --- also check maximum query_string length if no http_referer given --- */
15840: if ( isquery ) /* not relevant if "interactive" */
15841: if ( !isinvalidreferer ) /* nor if already invalid referer */
15842: if ( !ishttpreferer ) /* no http_referer supplied */
1.5 ! raeburn 15843: if ( strlen(expression) > norefmaxlen ) { /* query_string too long */
! 15844: if ( isempty(referer_match) ) /* no referer_match to display */
! 15845: expression = invalid_referer_msg; /* set invalid http_referer message*/
! 15846: else { /* error with referer_match display*/
! 15847: strcpy(exprbuffer,invalid_referer_match); /* init error message */
! 15848: strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
! 15849: strdetex(urlprune(referer_match,reflevels),1),0); } /*with host_http*/
! 15850: isinvalidreferer = 1; } /* and signal invalid referer */
1.1 albertel 15851: /* ---
1.5 ! raeburn 15852: * check for "advertisement"
! 15853: * ------------------------- */
! 15854: /* --- check if advertisement messages only for one particular host --- */
! 15855: if ( !isempty(host_showad) ) /* messages only for this referer */
! 15856: if ( !isempty(referer_match) ) /* have HTTP_HOST or SERVER_NAME */
! 15857: if ( strstr(referer_match,host_showad) /* see if this host sees ad */
! 15858: == NULL ) /* not mimetex host for ad */
! 15859: adfrequency = 0; /* turn off advertisements */
! 15860: /* --- check for advertisement directive (\advertisement) --- */
! 15861: if ( strreplace(expression,"\\advertisement","",0) /*remove \advertisement*/
! 15862: >= 1 ) adfrequency = 1; /* force advertisement display */
! 15863: if ( adfrequency > 0 ) { /* advertising enabled */
! 15864: int npump = crc16(expression)%16; /* #times, 0-15, to pump rand() */
! 15865: srand(atoi(timestamp(TZDELTA,4))); /* init rand() with mmddhhmmss */
! 15866: while ( npump-- >= 0 ) rand(); /* pre-pump rand() before use */
! 15867: if ( (1+rand())%adfrequency == 0 ) { /* once every adfrequency calls */
! 15868: advertisement(expression,adtemplate); } } /*wrap expression in advert*/
! 15869: /* ---
! 15870: * check for image caching (and whether or not caching content type)
! 15871: * ----------------------------------------------------------------- */
1.2 albertel 15872: if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */
15873: || strstr(expression,"\\input") != NULL /* can't cache \input{} */
15874: || strstr(expression,"\\today") != NULL /* can't cache \today */
15875: || strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
15876: || strstr(expression,"\\nocach") != NULL /* no caching requested */
15877: || isformdata /* don't cache user form input */
1.1 albertel 15878: ) { iscaching = 0; /* so turn caching off */
1.2 albertel 15879: maxage = 5; } /* and set max-age to 5 seconds */
1.5 ! raeburn 15880: if ( strstr(expression,"\\depth") != NULL ) /* cache content-type lines */
! 15881: iscachecontenttype = 1; /* set flag to cache content-type */
! 15882: if ( strstr(expression,"\\nodepth") != NULL ) /* don't cache content-type */
! 15883: iscachecontenttype = 0; /*set flag to not cache content-type*/
1.1 albertel 15884: if ( isquery ) /* don't cache command-line images */
15885: if ( iscaching ) /* image caching enabled */
15886: {
15887: /* --- set up path to cached image file --- */
15888: char *md5hash = md5str(expression); /* md5 hash of expression */
15889: if ( md5hash == NULL ) /* failed for some reason */
15890: iscaching = 0; /* so turn off caching */
15891: else
15892: {
15893: strcpy(cachefile,cachepath); /* start with (relative) path */
15894: strcat(cachefile,md5hash); /* add md5 hash of expression */
15895: strcat(cachefile,".gif"); /* finish with .gif extension */
15896: gif_outfile = cachefile; /* signal GIF_Create() to cache */
1.2 albertel 15897: /* --- emit mime content-type line --- */
1.3 albertel 15898: if ( 0 && isemitcontenttype ) /* now done in emitcache() */
1.2 albertel 15899: { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
1.4 riegler 15900: if ( abs(valign) < 999 ) /* have vertical align */
15901: fprintf( stdout, "Vertical-Align: %d\n",valign );
1.2 albertel 15902: fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.1 albertel 15903: /* --- emit cached image if it already exists --- */
1.4 riegler 15904: if ( emitcache(cachefile,maxage,valign,0) > 0 ) /* cached image emitted */
1.1 albertel 15905: goto end_of_job; /* so nothing else to do */
15906: /* --- log caching request --- */
15907: if ( msglevel >= 1 /* check if logging */
15908: /*&& seclevel <= 5*/ ) /* and if logging permitted */
15909: if ( cachelog != NULL ) /* if a logfile is given */
15910: if ( *cachelog != '\000' ) /*and if it's not an empty string*/
15911: { char filename[256]; /* construct cachepath/cachelog */
15912: FILE *filefp=NULL; /* fopen(filename) */
15913: strcpy(filename,cachepath); /* start with (relative) path */
15914: strcat(filename,cachelog); /* add cache log filename */
15915: if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
15916: != NULL ) /* ignore logging if can't open */
15917: { int isreflogged = 0; /* set true if http_referer logged */
15918: fprintf(filefp,"%s %s\n", /* timestamp, md5 file */
1.2 albertel 15919: timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
1.1 albertel 15920: fprintf(filefp,"%s\n",expression); /* expression in filename */
15921: if ( http_referer != NULL ) /* show referer if we have one */
15922: if ( *http_referer != '\000' ) /* and if not an empty string*/
15923: { int loglen = strlen(dashes); /* #chars on line in log file*/
15924: char *refp = http_referer; /* line to be printed */
15925: isreflogged = 1; /* signal http_referer logged*/
15926: while ( 1 ) { /* printed in parts if needed*/
15927: fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
15928: if ( strlen(refp) <= loglen ) break; /* no more parts */
15929: refp += loglen; } } /* bump ptr to next part */
15930: if ( !isreflogged ) /* http_referer not logged */
15931: fprintf(filefp,"http://none\n"); /* so log dummy referer line */
15932: fprintf(filefp,"%s\n",dashes); /* separator line */
15933: fclose(filefp); } /* close logfile immediately */
15934: } /* --- end-of-if(cachelog!=NULL) --- */
15935: } /* --- end-of-if/else(md5hash==NULL) --- */
15936: } /* --- end-of-if(iscaching) --- */
15937: /* ---
15938: * emit copyright, gnu/gpl notice (if "interactive")
15939: * ------------------------------------------------- */
15940: if ( !isdumpimage ) /* don't mix ascii with image dump */
1.5 ! raeburn 15941: if ( (!isquery||isqlogging) && msgfp!=NULL ) { /* called from command line */
! 15942: fprintf(msgfp,"%s\n%s\n",copyright1,copyright2); /* display copyright */
! 15943: fprintf(msgfp,"Most recent revision: %s\n",REVISIONDATE); /*revision date*/
! 15944: } /* --- end-of-if(!isquery...) --- */
1.1 albertel 15945: /* -------------------------------------------------------------------------
15946: rasterize expression and put a border around it
15947: -------------------------------------------------------------------------- */
15948: /* --- preprocess expression, converting LaTeX constructs for mimeTeX --- */
1.4 riegler 15949: if ( expression != NULL ) { /* have expression to rasterize */
15950: expression = mimeprep(expression); } /* preprocess expression */
1.1 albertel 15951: /* --- double-check that we actually have an expression to rasterize --- */
1.5 ! raeburn 15952: if ( expression == NULL ) { /* nothing to rasterize */
! 15953: if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
! 15954: if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
! 15955: if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
! 15956: fprintf(msgfp,"No LaTeX expression to rasterize\n"); }
! 15957: goto end_of_job; } /* and then quit */
1.1 albertel 15958: /* --- rasterize expression --- */
1.5 ! raeburn 15959: if ( (sp = rasterize(expression,size)) == NULL ) { /* failed to rasterize */
! 15960: if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
! 15961: if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
! 15962: if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
! 15963: fprintf(msgfp,"Failed to rasterize %.2048s\n",expression); }
! 15964: if ( isquery ) { /* try to display failed expression*/
! 15965: char errormsg[4096]; /* buffer for failed expression */
! 15966: strcpy(errormsg, /* init error message */
! 15967: "\\red\\fbox{\\begin{gather}"
! 15968: "{\\rm~mi\\underline{meTeX~failed~to~render~your~expressi}on}\\\\[5]");
! 15969: strcat(errormsg,"{\\rm\\hspace{10}{"); /*render expression as \rm*/
! 15970: strcat(errormsg,strdetex(expression,0));/*add detexed expression to msg*/
! 15971: strcat(errormsg,"}\\hspace{10}}\\end{gather}}"); /* finish up */
! 15972: if ( (sp = rasterize(errormsg,1)) == NULL ) /*couldn't rasterize errmsg*/
! 15973: sp = rasterize( /* so rasterize generic error */
! 15974: "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1); }
! 15975: if ( sp == NULL ) goto end_of_job; /* re-check for err message failure*/
! 15976: magstep = 1; /* don't magstep error msgs */
! 15977: } /* --- end-of-if((sp=rasterize())==NULL) --- */
! 15978: /* --- magnify entire image here if we need >>bit<<map for pbm output --- */
! 15979: if ( !isaa || (ispbmpgm && ptype<2) ) { /*or use bytemapmag() below instead*/
! 15980: if ( magstep > 1 && magstep <= 10 ) { /* magnify entire bitmap image */
! 15981: raster *rastmag(), *magrp=NULL; /* bitmap magnify function */
! 15982: int baseline = sp->baseline; /* original image baseline */
! 15983: magrp = rastmag(sp->image,magstep); /* magnify raster image */
! 15984: if ( magrp != NULL ) { /* succeeded to magnify image */
! 15985: delete_raster(sp->image); /* free original raster image */
! 15986: sp->image = magrp; /*and replace it with magnified one*/
! 15987: /* --- adjust parameters --- */
! 15988: baseline *= magstep; /* scale baseline */
! 15989: if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */
! 15990: sp->baseline = baseline; } /*reset baseline of magnified image*/
! 15991: magstep = (-1); /*done, don't also use bytemapmag()*/
! 15992: } /* --- end-of-if(magstep) --- */
! 15993: } /* --- end-of-if(1||(ispbmpgm&&ptype<2)) --- */
1.1 albertel 15994: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
15995: if ( issupersampling ) /* no border needed for gifs */
15996: bp = sp->image; /* so just extract pixel map */
15997: else /* for mime xbitmaps must have... */
15998: bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
15999: sp->image = bitmap_raster = bp; /* global copy for gif,png output */
1.5 ! raeburn 16000: raster_width = bp->width; raster_height = bp->height; /* global copy */
! 16001: raster_baseline = sp->baseline; /* global copy (not needed) */
1.4 riegler 16002: if ( sp!=NULL && bp!=NULL ) { /* have raster */
1.5 ! raeburn 16003: valign= raster_baseline -(raster_height -1);/*#pixels for Vertical-Align:*/
1.4 riegler 16004: if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */
1.2 albertel 16005: if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
16006: type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
1.1 albertel 16007: /* -------------------------------------------------------------------------
16008: generate anti-aliased bytemap from (bordered) bitmap
16009: -------------------------------------------------------------------------- */
16010: if ( isaa ) /* we want anti-aliased bitmap */
16011: {
16012: /* ---
16013: * allocate bytemap and colormap as per width*height of bitmap
16014: * ----------------------------------------------------------- */
1.5 ! raeburn 16015: int nbytes = (raster_width)*(raster_height); /*#bytes for byte,colormap*/
1.1 albertel 16016: if ( isss ) /* anti-aliasing by supersampling */
16017: bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
16018: else /* need to allocate bytemap */
16019: if ( aaalgorithm == 0 ) /* anti-aliasing not wanted */
16020: isaa = 0; /* so signal no anti-aliasing */
16021: else /* anti-aliasing wanted */
16022: if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
16023: == NULL ) isaa = 0; /* reset flag if malloc failed */
16024: /* ---
16025: * now generate anti-aliased bytemap and colormap from bitmap
16026: * ---------------------------------------------------------- */
16027: if ( isaa ) /*re-check that we're anti-aliasing*/
16028: {
16029: /* ---
16030: * select anti-aliasing algorithm
16031: * ------------------------------ */
16032: if ( !isss ) /* generate bytemap for lowpass */
1.3 albertel 16033: switch ( aaalgorithm ) { /* choose antialiasing algorithm */
16034: default: isaa = 0; break; /* unrecognized algorithm */
16035: case 1: /* 1 for aalowpass() */
16036: if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/
16037: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
16038: break;
16039: case 2: /*2 for netpbm pnmalias.c algorithm*/
16040: if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
16041: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
16042: break;
16043: case 3: /*3 for aapnm() based on aagridnum()*/
16044: if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
16045: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
16046: break;
16047: case 4: /* 4 for aalookup() table lookup */
16048: if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */
16049: == 0 ) isaa = 0; /*failed, so turn off anti-aliasing*/
16050: break;
16051: } /* --- end-of-switch(aaalgorithm) --- */
16052: /* ---
16053: * emit aalookup() pattern# counts/percents diagnostics
16054: * ---------------------------------------------------- */
16055: if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/
16056: int pcount0=0, pcount1=0; /* init total w,b center counts */
16057: for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
16058: if ( ipattern > 1 ) /* ignore all-white squares */
16059: pcount0 += patternnumcount0[ipattern]; /* bump total white centers */
16060: pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */
16061: if ( pcount0+pcount1 > 0 ) /* have pcounts (using aalookup) */
16062: fprintf(msgfp, " aalookup() patterns excluding#1 white"
16063: " (%%'s are in tenths of a percent)...\n");
16064: for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
16065: int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern];
16066: if ( tot > 0 ) /* this pattern occurs in image */
16067: fprintf(msgfp,
16068: " pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n",
16069: ipattern, patternnumcount0[ipattern], (ipattern<=1? 999.99:
16070: 1000.*((double)patternnumcount0[ipattern])/((double)pcount0)),
16071: patternnumcount1[ipattern],
16072: 1000.*((double)patternnumcount1[ipattern])/((double)pcount1),
16073: tot, (ipattern<=1? 999.99:
16074: 1000.*((double)tot)/((double)(pcount0+pcount1))) ); }
16075: if ( pcount0+pcount1 > 0 ) /* true when using aalookup() */
16076: fprintf(msgfp,
16077: "all patterns: %7d +%7d =%7d total pixels\n",
16078: pcount0,pcount1,pcount0+pcount1); }
1.1 albertel 16079: /* ---
1.5 ! raeburn 16080: * apply magstep if requested and not already done to bitmap above
! 16081: * --------------------------------------------------------------- */
! 16082: if ( 1 ) { /* or use rastmag() above instead */
! 16083: if ( magstep > 1 && magstep <= 10 ) { /*magnify entire bytemap image*/
! 16084: intbyte *bytemapmag(), *magmap=NULL; /* bytemap magnify function */
! 16085: magmap=bytemapmag(bytemap_raster,raster_width,raster_height,magstep);
! 16086: if ( magmap != NULL ) { /* succeeded to magnify image */
! 16087: free(bytemap_raster); /* free original bytemap image */
! 16088: bytemap_raster = magmap; /*and replace it with magnified one*/
! 16089: /* --- adjust parameters --- */
! 16090: raster_width *= magstep; raster_height *= magstep; /*scale raster*/
! 16091: nbytes *= (magstep*magstep); /* scale total image size */
! 16092: if ( abs(valign) < 255 ) { /* valign okay */
! 16093: valign *= magstep; /* scale by magstep */
! 16094: if ( abs(valign) > 512 ) valign = (-9999); } /* sanity check */
! 16095: } /* --- end-of-if(magmap!=NULL) --- */
! 16096: magstep = (-1); /*done, don't also use bytemapmag()*/
! 16097: } /* --- end-of-if(magstep) --- */
! 16098: } /* --- end-of-if(1) --- */
! 16099: /* ---
1.1 albertel 16100: * finally, generate colors and colormap
16101: * ------------------------------------- */
1.5 ! raeburn 16102: if ( isaa ) /* have bytemap, so... */
! 16103: if ( (colormap_raster = (intbyte *)malloc(nbytes)) /*malloc colormap*/
! 16104: == NULL ) isaa = 0; /* reset flag if malloc failed */
! 16105: if ( isaa ) { /* we have byte/colormap_raster */
1.1 albertel 16106: ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
16107: if ( ncolors < 2 ) /* failed */
16108: { isaa = 0; /* so turn off anti-aliasing */
16109: ncolors = 2; } /* and reset for black&white */
16110: } /* --- end-of-if(isaa) --- */
1.2 albertel 16111: if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch */
16112: raster pbm_raster; /*construct arg for write_pbmpgm()*/
1.5 ! raeburn 16113: pbm_raster.width = raster_width; pbm_raster.height = raster_height;
1.2 albertel 16114: pbm_raster.pixsz = 8; pbm_raster.pixmap = (pixbyte *)bytemap_raster;
16115: type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
1.1 albertel 16116: } /* --- end-of-if(isaa) --- */
16117: } /* --- end-of-if(isaa) --- */
16118: /* -------------------------------------------------------------------------
16119: display results on msgfp if called from command line (usually for testing)
16120: -------------------------------------------------------------------------- */
16121: if ( (!isquery||isqlogging) || msglevel >= 99 ) /*command line or debuging*/
16122: if ( !isdumpimage ) /* don't mix ascii with image dump */
16123: {
16124: /* ---
16125: * display ascii image of rasterize()'s rasterized bitmap
16126: * ------------------------------------------------------ */
16127: if ( !isss ) /* no bitmap for supersampling */
16128: { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
16129: type_raster(bp,msgfp); } /* emit ascii image of raster */
16130: /* ---
16131: * display anti-aliasing results applied to rasterized bitmap
16132: * ---------------------------------------------------------- */
16133: if ( isaa ) /* if anti-aliasing applied */
16134: {
16135: int igray; /* colors[] index */
16136: /* --- anti-aliased bytemap image --- */
16137: if ( msgfp!=NULL && msglevel>=9 ) /* don't usually emit raw bytemap */
16138: { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
16139: "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
1.5 ! raeburn 16140: type_bytemap(bytemap_raster,grayscale,
! 16141: raster_width,raster_height,msgfp); }
1.1 albertel 16142: /* --- colormap image --- */
16143: fprintf(msgfp,"\nHex dump of colormap indexes, " /* emit colormap */
16144: "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
1.5 ! raeburn 16145: type_bytemap(colormap_raster,ncolors,
! 16146: raster_width,raster_height,msgfp);
1.1 albertel 16147: /* --- rgb values corresponding to colormap indexes */
16148: fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
16149: for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
16150: fprintf(msgfp,"%s%2x-->%3d", (igray%5?" ":"\n"),
16151: igray,(int)(colors[ncolors-1]-colors[igray]));
16152: fprintf(msgfp,"\n"); /* always needs a final newline */
16153: } /* --- end-of-if(isaa) --- */
16154: } /* --- end-of-if(!isquery||msglevel>=9) --- */
16155: /* -------------------------------------------------------------------------
16156: emit xbitmap or gif image, and exit
16157: -------------------------------------------------------------------------- */
16158: if ( isquery /* called from browser (usual) */
1.2 albertel 16159: || (isdumpimage && !ispbmpgm) /* or to emit gif dump of image */
1.1 albertel 16160: || msglevel >= 99 ) /* or for debugging */
16161: {
16162: int igray = 0; /* grayscale index */
16163: #if defined(GIF) /* compiled to emit gif */
16164: /* ------------------------------------------------------------------------
16165: emit GIF image
16166: ------------------------------------------------------------------------- */
1.2 albertel 16167: /* --- don't use memory buffer if outout file given --- */
16168: if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
1.4 riegler 16169: /* --- construct contenttype[] buffer containing mime headers --- */
16170: if ( 1 ) { /* always construct buffer */
16171: sprintf( contenttype, "Cache-Control: max-age=%d\n", maxage );
16172: /*sprintf(contenttype+strlen(contenttype),
16173: "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
16174: /*sprintf(contenttype+strlen(contenttype),
16175: "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n");*/
16176: if ( abs(valign) < 999 ) /* have Vertical-Align: header info*/
16177: sprintf( contenttype+strlen(contenttype),
16178: "Vertical-Align: %d\n", valign );
16179: sprintf( contenttype+strlen(contenttype),
16180: "Content-type: image/gif\n\n" ); }
1.1 albertel 16181: /* --- emit mime content-type line --- */
1.3 albertel 16182: if ( isemitcontenttype /* content-type lines wanted */
16183: && !isdumpimage /* don't mix ascii with image dump */
1.2 albertel 16184: && !isinmemory /* done below if in memory */
16185: && !iscaching ) /* done by emitcache() if caching */
1.4 riegler 16186: { fputs(contenttype,stdout); } /* emit content-type: header buffer*/
1.2 albertel 16187: /* --- write output to memory buffer, possibly for testing --- */
16188: if ( isinmemory /* want gif written to memory */
16189: || isdumpbuffer ) /*or dump memory buffer for testing*/
16190: if ( gif_outfile == NULL ) /* and don't already have a file */
16191: { *gif_buffer = '\000'; /* init buffer as empty string */
1.3 albertel 16192: memset(gif_buffer,0,MAXGIFSZ); /* zero out buffer */
1.2 albertel 16193: gif_outfile = gif_buffer; /* and point outfile to buffer */
16194: if ( isdumpbuffer ) /* buffer dump test requested */
16195: isdumpbuffer = 999; } /* so signal dumping to buffer */
1.1 albertel 16196: /* --- initialize gifsave library and colors --- */
16197: if ( msgfp!=NULL && msglevel>=999 )
1.2 albertel 16198: { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
1.5 ! raeburn 16199: raster_width,raster_height,ncolors); fflush(msgfp); }
1.1 albertel 16200: while ( 1 ) /* init gifsave lib, and retry if caching fails */
1.5 ! raeburn 16201: { int status = GIF_Create(gif_outfile,
! 16202: raster_width,raster_height, ncolors, 8);
1.1 albertel 16203: if ( status == 0 ) break; /* continue if succeeded */
16204: if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
16205: iscaching = 0; /* retry without cache file */
1.2 albertel 16206: isdumpbuffer = 0; /* reset isdumpbuffer signal */
16207: if ( isquery ) isinmemory = 1; /* force in-memory image generation*/
16208: if ( isinmemory ) { /* using memory buffer */
16209: gif_outfile = gif_buffer; /* emit images to memory buffer */
16210: *gif_outfile = '\000'; } /* empty string signals buffer */
16211: else { /* or */
16212: gif_outfile = (char *)NULL; /* emit images to stdout */
1.3 albertel 16213: if ( isemitcontenttype ) { /* content-type lines wanted */
16214: fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
16215: fprintf( stdout, "Content-type: image/gif\n\n" ); } }
1.2 albertel 16216: } /* --- end-of-while(1) --- */
1.1 albertel 16217: GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */
16218: if ( !isaa ) /* just b&w if not anti-aliased */
16219: { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
16220: colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
16221: else /* set grayscales for anti-aliasing */
16222: /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
16223: for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
16224: {
16225: /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
16226: double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
16227: /* --- r,g,b components go from background to foreground color --- */
16228: int red = iround(((double)bgred) +gfrac*((double)(fgred-bgred))),
16229: green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
16230: blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
16231: /* --- set color index number igray to rgb values gray,gray,gray --- */
16232: GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
16233: } /* --- end-of-for(igray) --- */
16234: /* --- set gif color#0 (background) transparent --- */
16235: if ( istransparent ) /* transparent background wanted */
16236: GIF_SetTransparent(0); /* set transparent background */
16237: if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
16238: /* --- emit compressed gif image (to stdout or cache file) --- */
16239: GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
16240: GIF_Close(); /* close file */
1.2 albertel 16241: if ( msgfp!=NULL && msglevel>=9 )
16242: { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
16243: fflush(msgfp); }
16244: /* --- may need to emit image from cached file or from memory --- */
16245: if ( isquery /* have an actual query string */
16246: || isdumpimage /* or dumping image */
16247: || msglevel >= 99 ) { /* or debugging */
16248: int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
16249: if ( iscaching ) /* caching enabled */
1.4 riegler 16250: emitcache(cachefile,maxage2,valign,0); /*emit cached image (hopefully)*/
1.2 albertel 16251: else if ( isinmemory ) /* or emit image from memory buffer*/
1.4 riegler 16252: emitcache(gif_buffer,maxage2,valign,1); } /*emitted from memory buffer*/
1.2 albertel 16253: /* --- for testing, may need to write image buffer to file --- */
16254: if ( isdumpbuffer > 99 ) /* gif image in memory buffer */
16255: if ( gifSize > 0 ) /* and it's not an empty buffer */
16256: { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
16257: if ( dumpfp != NULL ) /* file opened successfully */
16258: { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
16259: fclose(dumpfp); } /* and close file */
16260: } /* --- end-of-if(isdumpbuffer>99) --- */
1.1 albertel 16261: #else
16262: /* ------------------------------------------------------------------------
16263: emit mime XBITMAP image
16264: ------------------------------------------------------------------------- */
16265: xbitmap_raster(bp,stdout); /* default emits mime xbitmap */
16266: #endif
16267: } /* --- end-of-if(isquery) --- */
16268: /* --- exit --- */
16269: end_of_job:
1.2 albertel 16270: if ( !isss ) /*bytemap raster in sp for supersamp*/
16271: if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
1.1 albertel 16272: if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
1.2 albertel 16273: if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
16274: if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */
1.1 albertel 16275: if ( msgfp != NULL /* have message/log file open */
16276: && msgfp != stdout ) /* and it's not stdout */
1.2 albertel 16277: { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
16278: timestamp(TZDELTA,0));
1.1 albertel 16279: fprintf(msgfp,"%s\n",dashes); /* so log separator line */
16280: fclose(msgfp); } /* and close logfile */
1.2 albertel 16281: /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
16282: #if defined(_CRTDBG_MAP_ALLOC)
16283: _CrtDumpMemoryLeaks();
16284: #endif
16285: /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
16286: #if !defined(_USRDLL)
1.5 ! raeburn 16287: if ( errorstatus == 0 ) /*user doesn't want errors signalled*/
! 16288: exitstatus = 0; /* so reset error status */
! 16289: exit ( exitstatus );
1.2 albertel 16290: #endif
1.1 albertel 16291: } /* --- end-of-function main() --- */
16292:
16293: /* ==========================================================================
1.2 albertel 16294: * Function: CreateGifFromEq ( expression, gifFileName )
16295: * Purpose: shortcut method to create GIF file for expression,
16296: * with antialising and all other capabilities
16297: * --------------------------------------------------------------------------
16298: * Arguments: expression (I) char *ptr to null-terminated string
16299: * containing LaTeX expression to be rendred
16300: * gifFileName (I) char *ptr to null-terminated string
16301: * containing name of output gif file
16302: * --------------------------------------------------------------------------
16303: * Returns: ( int ) exit value from main (0 if successful)
16304: * --------------------------------------------------------------------------
16305: * Notes: o This function is the entry point when mimeTeX is built
16306: * as a Win32 DLL rather then a standalone app or CGI
16307: * o Contributed to mimeTeX by Shital Shah. See his homepage
16308: * http://www.shitalshah.com
16309: * o Shital discusses the mimeTeX Win32 DLL project at
16310: * http://www.codeproject.com/dotnet/Eq2Img.asp
16311: * and you can download his latest code from
16312: * http://www.shitalshah.com/dev/eq2img_all.zip
16313: * ======================================================================= */
16314: /* --- include function to expose Win32 DLL to outside world --- */
16315: #if defined(_USRDLL)
16316: extern _declspec(dllexport)int _cdecl
16317: CreateGifFromEq ( char *expression, char *gifFileName );
16318: #endif
16319: /* --- entry point --- */
16320: int CreateGifFromEq ( char *expression, char *gifFileName )
16321: {
16322: /* -------------------------------------------------------------------------
16323: Allocations and Declarations
16324: -------------------------------------------------------------------------- */
16325: int main(); /* main() akways returns an int */
16326: /* --- set constants --- */
16327: int argc = 4; /* count of args supplied to main() */
16328: char *argv[5] = /* command line args to run with -e option */
16329: { "MimeTeXWin32DLL", "-e", /* constant args */
16330: /*gifFileName, expression,*/ NULL, NULL, NULL };
16331: /* --- set argv[]'s not computable at load time --- */
16332: argv[2] = gifFileName; /* args are -e gifFileName */
16333: argv[3] = expression; /* and now -e gifFileName expression */
16334: /* -------------------------------------------------------------------------
16335: Run mimeTeX in command-line mode with -e (export) option, and then return
16336: -------------------------------------------------------------------------- */
16337: return main ( argc, argv
16338: #ifdef DUMPENVP
16339: , NULL
16340: #endif
16341: ) ;
16342: } /* --- end-of-function CreateGifFromEq() --- */
16343:
1.1 albertel 16344:
16345: /* ==========================================================================
16346: * Function: ismonth ( char *month )
16347: * Purpose: returns 1 if month contains current month "jan"..."dec".
16348: * --------------------------------------------------------------------------
16349: * Arguments: month (I) char * containing null-terminated string
16350: * in which "jan"..."dec" is (putatively)
16351: * contained as a substring.
16352: * --------------------------------------------------------------------------
16353: * Returns: ( int ) 1 if month contains current month,
16354: * 0 otherwise
16355: * --------------------------------------------------------------------------
16356: * Notes: o There's a three day "grace period", e.g., Dec 3 mtaches Nov.
16357: * ======================================================================= */
16358: /* --- entry point --- */
16359: int ismonth ( char *month )
16360: {
16361: /* -------------------------------------------------------------------------
16362: Allocations and Declarations
16363: -------------------------------------------------------------------------- */
16364: int isokay = 0; /*1 if month contains current month*/
16365: /*long time_val = 0L;*/ /* binary value returned by time() */
16366: time_t time_val = (time_t)(0); /* binary value returned by time() */
16367: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
16368: int imonth, mday; /* current month 1-12 and day 1-31 */
16369: int ngrace = 3; /* grace period */
16370: char lcmonth[128]="\000"; int i=0; /* lowercase month */
16371: static char *months[] = /* month must contain current one */
16372: {"dec","jan","feb","mar","apr","may","jun",
16373: "jul","aug","sep","oct","nov","dec","jan"};
16374: /* -------------------------------------------------------------------------
16375: get current date:time info, and check month
16376: -------------------------------------------------------------------------- */
16377: /* --- lowercase input month --- */
16378: if ( month != NULL ) /* check that we got input */
16379: for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
16380: lcmonth[i] = tolower(*month); /* lowerase each char in month */
16381: if ( i < 2 ) goto end_of_job; /* must be invalid input */
16382: lcmonth[i] = '\000'; /* null-terminate lcmonth[] */
16383: /* --- get current date:time --- */
16384: time((time_t *)(&time_val)); /* get date and time */
16385: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
16386: /* --- month and day --- */
16387: imonth = 1 + (int)(tmstruct->tm_mon); /* 1=jan ... 12=dec */
16388: mday = (int)(tmstruct->tm_mday); /* 1-31 */
16389: if ( imonth<1 || imonth>12 /* quit if month out-of-range */
16390: || mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
16391: /* --- check input month against current date --- */
16392: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
16393: if ( mday <= ngrace ) /* 1-3 within grace period */
16394: if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
16395: if ( mday >= 31-ngrace ) /* 28-31 within grace period */
16396: if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
16397: end_of_job:
16398: return ( isokay ); /*1 if month contains current month*/
16399: } /* --- end-of-function ismonth() --- */
16400:
16401:
16402: /* ==========================================================================
16403: * Function: logger ( fp, msglevel, message, logvars )
16404: * Purpose: Logs the environment variables specified in logvars
16405: * to fp if their msglevel is >= the passed msglevel.
16406: * --------------------------------------------------------------------------
16407: * Arguments: fp (I) FILE * to file containing log
16408: * msglevel (I) int containing logging message level
16409: * message (I) char * to optional message, or NULL
16410: * logvars (I) logdata * to array of environment variables
16411: * to be logged
16412: * --------------------------------------------------------------------------
16413: * Returns: ( int ) number of variables from logvars
16414: * that were actually logged
16415: * --------------------------------------------------------------------------
16416: * Notes: o
16417: * ======================================================================= */
16418: /* --- entry point --- */
16419: int logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
16420: {
16421: /* -------------------------------------------------------------------------
16422: Allocations and Declarations
16423: -------------------------------------------------------------------------- */
16424: int ilog=0, nlogged=0; /* logvars[] index, #vars logged */
16425: char *timestamp(); /* timestamp logged */
16426: char *value = NULL; /* getenv(name) to be logged */
16427: /* -------------------------------------------------------------------------
16428: Log each variable
16429: -------------------------------------------------------------------------- */
1.2 albertel 16430: fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
1.1 albertel 16431: if ( message != NULL ) /* optional message supplied */
16432: fprintf(fp," MESSAGE = %s\n",message); /* emit caller-supplied message */
16433: if ( logvars != (logdata *)NULL ) /* have logvars */
16434: for ( ilog=0; logvars[ilog].name != NULL; ilog++ ) /* till end-of-table */
16435: if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
16436: if ( (value=getenv(logvars[ilog].name)) /* getenv(name) to be logged */
16437: != NULL ) /* check that name exists */
16438: {
16439: fprintf(fp," %s = %.*s\n", /* emit variable name = value */
16440: logvars[ilog].name,logvars[ilog].maxlen,value);
16441: nlogged++; /* bump #vars logged */
16442: } /* --- end-of-for(ilog) --- */
16443: return ( nlogged ); /* back to caller */
16444: } /* --- end-of-function logger() --- */
16445:
1.5 ! raeburn 16446:
1.1 albertel 16447: /* ==========================================================================
1.4 riegler 16448: * Function: emitcache ( cachefile, maxage, valign, isbuffer )
1.1 albertel 16449: * Purpose: dumps bytes from cachefile to stdout
16450: * --------------------------------------------------------------------------
16451: * Arguments: cachefile (I) pointer to null-terminated char string
1.2 albertel 16452: * containing full path to file to be dumped,
16453: * or contains buffer of bytes to be dumped
1.4 riegler 16454: * maxage (I) int containing maxage, in seconds, for
1.2 albertel 16455: * http header, or -1 to not emit headers
1.4 riegler 16456: * valign (I) int containing Vertical-Align:, in pixels,
16457: * for http header, or <= -999 to not emit
1.2 albertel 16458: * isbuffer (I) 1 if cachefile is buffer of bytes to be
16459: * dumped
1.1 albertel 16460: * --------------------------------------------------------------------------
16461: * Returns: ( int ) #bytes dumped (0 signals error)
16462: * --------------------------------------------------------------------------
16463: * Notes: o
16464: * ======================================================================= */
16465: /* --- entry point --- */
1.4 riegler 16466: int emitcache ( char *cachefile, int maxage, int valign, int isbuffer )
1.1 albertel 16467: {
16468: /* -------------------------------------------------------------------------
16469: Allocations and Declarations
16470: -------------------------------------------------------------------------- */
1.2 albertel 16471: int nbytes=gifSize, readcachefile(); /* read cache file */
16472: FILE *emitptr = stdout; /* emit cachefile to stdout */
1.3 albertel 16473: unsigned char buffer[MAXGIFSZ+1]; /* bytes from cachefile */
1.2 albertel 16474: unsigned char *buffptr = buffer; /* ptr to buffer */
1.4 riegler 16475: int isvalign = (abs(valign)<999?1:0); /* true to emit Vertical-Align: */
16476: int iscontenttypecached = iscachecontenttype; /*true if headers cached*/
1.1 albertel 16477: /* -------------------------------------------------------------------------
16478: initialization
16479: -------------------------------------------------------------------------- */
16480: /* --- check that files opened okay --- */
1.2 albertel 16481: if ( emitptr == (FILE *)NULL ) /* failed to open emit file */
1.1 albertel 16482: goto end_of_job; /* so return 0 bytes to caller */
1.2 albertel 16483: /* --- read the file if necessary --- */
1.4 riegler 16484: if ( isbuffer ) { /* cachefile is buffer */
16485: buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */
16486: iscontenttypecached = 0; } /* and iscontenttypecached flag */
16487: else { /* cachefile is file name */
16488: if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
16489: < 1 ) goto end_of_job; } /* quit if file not read */
1.2 albertel 16490: /* --- first emit http headers if requested --- */
1.3 albertel 16491: if ( isemitcontenttype /* content-type lines enabled */
1.4 riegler 16492: && !iscontenttypecached /* and not in cached image */
1.3 albertel 16493: && maxage >= 0 ) /* caller wants http headers */
1.2 albertel 16494: { /* --- emit mime content-type line --- */
16495: fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
16496: fprintf( emitptr, "Content-Length: %d\n",nbytes );
1.4 riegler 16497: if ( isvalign ) /* Vertical-Align: header wanted */
16498: fprintf( emitptr, "Vertical-Align: %d\n",valign );
1.2 albertel 16499: fprintf( emitptr, "Content-type: image/gif\n\n" ); }
16500: /* -------------------------------------------------------------------------
16501: set stdout to binary mode (for Windows)
16502: -------------------------------------------------------------------------- */
1.1 albertel 16503: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */ /* doesn't work portably, */
16504: #ifdef WINDOWS /* so instead... */
16505: #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */
16506: if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
16507: == -1 ) ; /* handle error */ /* sets stdout to binary mode */
16508: #else /* setmode() not available */
16509: #if 1
16510: freopen ("CON", "wb", stdout); /* freopen() stdout binary */
16511: #else
16512: stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
16513: #endif
16514: #endif
16515: #endif
16516: /* -------------------------------------------------------------------------
16517: emit bytes from cachefile
16518: -------------------------------------------------------------------------- */
1.2 albertel 16519: /* --- write bytes to stdout --- */
16520: if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
16521: < nbytes ) /* failed to write all bytes */
16522: nbytes = 0; /* reset total count to 0 */
16523: end_of_job:
16524: return ( nbytes ); /* back with #bytes emitted */
16525: } /* --- end-of-function emitcache() --- */
16526:
1.5 ! raeburn 16527:
1.2 albertel 16528: /* ==========================================================================
16529: * Function: readcachefile ( cachefile, buffer )
16530: * Purpose: read cachefile into buffer
16531: * --------------------------------------------------------------------------
16532: * Arguments: cachefile (I) pointer to null-terminated char string
16533: * containing full path to file to be read
16534: * buffer (O) pointer to unsigned char string
16535: * returning contents of cachefile
16536: * (max 64000 bytes)
16537: * --------------------------------------------------------------------------
16538: * Returns: ( int ) #bytes read (0 signals error)
16539: * --------------------------------------------------------------------------
16540: * Notes: o
16541: * ======================================================================= */
16542: /* --- entry point --- */
16543: int readcachefile ( char *cachefile, unsigned char *buffer )
16544: {
16545: /* -------------------------------------------------------------------------
16546: Allocations and Declarations
16547: -------------------------------------------------------------------------- */
16548: FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
16549: unsigned char cachebuff[64]; /* bytes from cachefile */
16550: int buflen = 32, /* #bytes we try to read from file */
16551: nread = 0, /* #bytes actually read from file */
1.3 albertel 16552: maxbytes = MAXGIFSZ, /* max #bytes returned in buffer */
1.2 albertel 16553: nbytes = 0; /* total #bytes read */
16554: /* -------------------------------------------------------------------------
16555: initialization
16556: -------------------------------------------------------------------------- */
16557: /* --- check that files opened okay --- */
16558: if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
16559: /* --- check that output buffer provided --- */
16560: if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
16561: /* -------------------------------------------------------------------------
16562: read bytes from cachefile
16563: -------------------------------------------------------------------------- */
1.1 albertel 16564: while ( 1 )
16565: {
16566: /* --- read bytes from cachefile --- */
1.2 albertel 16567: nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
16568: if ( nbytes + nread > maxbytes ) /* block too big for buffer */
16569: nread = maxbytes - nbytes; /* so truncate it */
1.1 albertel 16570: if ( nread < 1 ) break; /* no bytes left in cachefile */
1.2 albertel 16571: /* --- store bytes in buffer --- */
16572: memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
16573: /* --- ready to read next block --- */
1.1 albertel 16574: nbytes += nread; /* bump total #bytes emitted */
16575: if ( nread < buflen ) break; /* no bytes left in cachefile */
1.2 albertel 16576: if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */
1.1 albertel 16577: } /* --- end-of-while(1) --- */
16578: end_of_job:
16579: if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
16580: return ( nbytes ); /* back with #bytes emitted */
1.2 albertel 16581: } /* --- end-of-function readcachefile() --- */
1.1 albertel 16582:
1.5 ! raeburn 16583:
! 16584: /* ==========================================================================
! 16585: * Function: advertisement ( expression, message )
! 16586: * Purpose: wrap expression in advertisement message
! 16587: * --------------------------------------------------------------------------
! 16588: * Arguments: expression (I/O) pointer to null-terminated char string
! 16589: * containing expression to be "wrapped",
! 16590: * and returning wrapped expression
! 16591: * message (I) pointer to null-terminated char string
! 16592: * containing template for advertisement
! 16593: * message, or NULL to use default message
! 16594: * --------------------------------------------------------------------------
! 16595: * Returns: ( int ) 1 if successful, 0=error
! 16596: * --------------------------------------------------------------------------
! 16597: * Notes: o
! 16598: * ======================================================================= */
! 16599: /* --- entry point --- */
! 16600: int advertisement ( char *expression, char *message )
! 16601: {
! 16602: /* -------------------------------------------------------------------------
! 16603: Allocations and Declarations
! 16604: -------------------------------------------------------------------------- */
! 16605: /* --- advertisement template --- */
! 16606: char *adtemplate =
! 16607: #if defined(ADVERTISEMENT) /* cc -DADVERTISEMENT=\"filename\" */
! 16608: #include ADVERTISEMENT /* filename with advertisement */
! 16609: #else /* formatted as illustrated below */
! 16610: "\\begin{gather} {\\small\\text \\fbox{\\begin{gather}"
! 16611: "mime\\TeX rendering courtesy of\\\\"
! 16612: "\\homepagetext \\end{gather}}}\\\\"
! 16613: " %%beginmath%% %%expression%% %%endmath%% \\end{gather}"
! 16614: #endif
! 16615: ; /* terminating semicolon */
! 16616: /* --- other variables --- */
! 16617: char adbuffer[MAXEXPRSZ+2048]; /*construct wrapped expression here*/
! 16618: char *beginmath = " ", /* start math mode */
! 16619: *endmath = " "; /* end math mode */
! 16620: int strreplace(); /* replace %%keywords%% with values*/
! 16621: /* -------------------------------------------------------------------------
! 16622: wrap expression in advertisement
! 16623: -------------------------------------------------------------------------- */
! 16624: /* --- start with template --- */
! 16625: if ( isempty(message) ) /* caller didn't supply message */
! 16626: message = adtemplate; /* so use default message */
! 16627: strcpy(adbuffer,message); /* copy message template to buffer */
! 16628: /* --- replace %%beginmath%%...%%endmath%% --- */
! 16629: strreplace(adbuffer,"%%beginmath%%",beginmath,0);
! 16630: strreplace(adbuffer,"%%endmath%%",endmath,0);
! 16631: /* --- replace %%expression%% in template with expression --- */
! 16632: strreplace(adbuffer,"%%expression%%",expression,0);
! 16633: /* --- replace original expression --- */
! 16634: strcpy(expression,adbuffer); /* expression now wrapped in ad */
! 16635: return ( 1 ); /* always just return 1 */
! 16636: } /* --- end-of-function advertisement() --- */
! 16637:
! 16638:
! 16639: /* ==========================================================================
! 16640: * Function: crc16 ( s )
! 16641: * Purpose: 16-bit crc of string s
! 16642: * --------------------------------------------------------------------------
! 16643: * Arguments: s (I) pointer to null-terminated char string
! 16644: * whose crc is desired
! 16645: * --------------------------------------------------------------------------
! 16646: * Returns: ( int ) 16-bit crc of s
! 16647: * --------------------------------------------------------------------------
! 16648: * Notes: o From Numerical Recipes in C, 2nd ed, page 900.
! 16649: * ======================================================================= */
! 16650: /* --- entry point --- */
! 16651: int crc16 ( char *s )
! 16652: {
! 16653: /* -------------------------------------------------------------------------
! 16654: Compute the crc
! 16655: -------------------------------------------------------------------------- */
! 16656: unsigned short crc = 0; /* returned crc */
! 16657: int ibit; /* for(ibit) eight one-bit shifts */
! 16658: while ( !isempty(s) ) { /* while there are still more chars*/
! 16659: crc = (crc ^ (*s)<<8); /* add next char */
! 16660: for ( ibit=0; ibit<8; ibit++ ) /* generator polynomial */
! 16661: if ( crc & 0x8000 ) { crc<<=1; crc=crc^4129; }
! 16662: else crc <<= 1;
! 16663: s++; /* next xhar */
! 16664: } /* --- end-of-while(!isempty(s)) --- */
! 16665: return ( (int)crc ); /* back to caller with crc */
! 16666: } /* --- end-of-function crc16() --- */
! 16667:
! 16668:
1.1 albertel 16669: /* ==========================================================================
16670: * Function: md5str ( instr )
16671: * Purpose: returns null-terminated character string containing
16672: * md5 hash of instr (input string)
16673: * --------------------------------------------------------------------------
16674: * Arguments: instr (I) pointer to null-terminated char string
16675: * containing input string whose md5 hash
16676: * is desired
16677: * --------------------------------------------------------------------------
16678: * Returns: ( char * ) ptr to null-terminated 32-character
16679: * md5 hash of instr
16680: * --------------------------------------------------------------------------
16681: * Notes: o Other md5 library functions are included below.
16682: * They're all taken from Christophe Devine's code,
16683: * which (as of 04-Aug-2004) is available from
16684: * http://www.cr0.net:8040/code/crypto/md5/
16685: * o The P,F,S macros in the original code are replaced
16686: * by four functions P1()...P4() to accommodate a problem
16687: * with Compaq's vax/vms C compiler.
16688: * ======================================================================= */
16689: /* --- #include "md5.h" --- */
16690: #ifndef uint8
16691: #define uint8 unsigned char
16692: #endif
16693: #ifndef uint32
16694: #define uint32 unsigned long int
16695: #endif
16696: typedef struct
16697: { uint32 total[2];
16698: uint32 state[4];
16699: uint8 buffer[64];
16700: } md5_context;
16701: void md5_starts( md5_context *ctx );
16702: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
16703: void md5_finish( md5_context *ctx, uint8 digest[16] );
16704: /* --- md5.h --- */
16705: #define GET_UINT32(n,b,i) \
16706: { (n) = ( (uint32) (b)[(i) ] ) \
16707: | ( (uint32) (b)[(i) + 1] << 8 ) \
16708: | ( (uint32) (b)[(i) + 2] << 16 ) \
16709: | ( (uint32) (b)[(i) + 3] << 24 ); }
16710: #define PUT_UINT32(n,b,i) \
16711: { (b)[(i) ] = (uint8) ( (n) ); \
16712: (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
16713: (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
16714: (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
16715: /* --- P,S,F macros defined as functions --- */
16716: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16717: { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
16718: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16719: return; }
16720: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16721: { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
16722: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16723: return; }
16724: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16725: { *a += (uint32)(b ^ c ^ d) + X[k] + t;
16726: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16727: return; }
16728: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
16729: { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
16730: *a = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
16731: return; }
16732:
16733: /* --- entry point (this one little stub written by me)--- */
16734: char *md5str( char *instr )
16735: { static char outstr[64];
16736: unsigned char md5sum[16];
16737: md5_context ctx;
16738: int j;
16739: md5_starts( &ctx );
16740: md5_update( &ctx, (uint8 *)instr, strlen(instr) );
16741: md5_finish( &ctx, md5sum );
16742: for( j=0; j<16; j++ )
16743: sprintf( outstr + j*2, "%02x", md5sum[j] );
16744: outstr[32] = '\000';
16745: return ( outstr ); }
16746:
16747: /* --- entry point (all md5 functions below by Christophe Devine) --- */
16748: void md5_starts( md5_context *ctx )
16749: { ctx->total[0] = 0;
16750: ctx->total[1] = 0;
16751: ctx->state[0] = 0x67452301;
16752: ctx->state[1] = 0xEFCDAB89;
16753: ctx->state[2] = 0x98BADCFE;
16754: ctx->state[3] = 0x10325476; }
16755:
16756: void md5_process( md5_context *ctx, uint8 data[64] )
16757: { uint32 X[16], A, B, C, D;
16758: GET_UINT32( X[0], data, 0 );
16759: GET_UINT32( X[1], data, 4 );
16760: GET_UINT32( X[2], data, 8 );
16761: GET_UINT32( X[3], data, 12 );
16762: GET_UINT32( X[4], data, 16 );
16763: GET_UINT32( X[5], data, 20 );
16764: GET_UINT32( X[6], data, 24 );
16765: GET_UINT32( X[7], data, 28 );
16766: GET_UINT32( X[8], data, 32 );
16767: GET_UINT32( X[9], data, 36 );
16768: GET_UINT32( X[10], data, 40 );
16769: GET_UINT32( X[11], data, 44 );
16770: GET_UINT32( X[12], data, 48 );
16771: GET_UINT32( X[13], data, 52 );
16772: GET_UINT32( X[14], data, 56 );
16773: GET_UINT32( X[15], data, 60 );
16774: A = ctx->state[0];
16775: B = ctx->state[1];
16776: C = ctx->state[2];
16777: D = ctx->state[3];
16778: P1( X, &A, B, C, D, 0, 7, 0xD76AA478 );
16779: P1( X, &D, A, B, C, 1, 12, 0xE8C7B756 );
16780: P1( X, &C, D, A, B, 2, 17, 0x242070DB );
16781: P1( X, &B, C, D, A, 3, 22, 0xC1BDCEEE );
16782: P1( X, &A, B, C, D, 4, 7, 0xF57C0FAF );
16783: P1( X, &D, A, B, C, 5, 12, 0x4787C62A );
16784: P1( X, &C, D, A, B, 6, 17, 0xA8304613 );
16785: P1( X, &B, C, D, A, 7, 22, 0xFD469501 );
16786: P1( X, &A, B, C, D, 8, 7, 0x698098D8 );
16787: P1( X, &D, A, B, C, 9, 12, 0x8B44F7AF );
16788: P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
16789: P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
16790: P1( X, &A, B, C, D, 12, 7, 0x6B901122 );
16791: P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
16792: P1( X, &C, D, A, B, 14, 17, 0xA679438E );
16793: P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
16794: P2( X, &A, B, C, D, 1, 5, 0xF61E2562 );
16795: P2( X, &D, A, B, C, 6, 9, 0xC040B340 );
16796: P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
16797: P2( X, &B, C, D, A, 0, 20, 0xE9B6C7AA );
16798: P2( X, &A, B, C, D, 5, 5, 0xD62F105D );
16799: P2( X, &D, A, B, C, 10, 9, 0x02441453 );
16800: P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
16801: P2( X, &B, C, D, A, 4, 20, 0xE7D3FBC8 );
16802: P2( X, &A, B, C, D, 9, 5, 0x21E1CDE6 );
16803: P2( X, &D, A, B, C, 14, 9, 0xC33707D6 );
16804: P2( X, &C, D, A, B, 3, 14, 0xF4D50D87 );
16805: P2( X, &B, C, D, A, 8, 20, 0x455A14ED );
16806: P2( X, &A, B, C, D, 13, 5, 0xA9E3E905 );
16807: P2( X, &D, A, B, C, 2, 9, 0xFCEFA3F8 );
16808: P2( X, &C, D, A, B, 7, 14, 0x676F02D9 );
16809: P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
16810: P3( X, &A, B, C, D, 5, 4, 0xFFFA3942 );
16811: P3( X, &D, A, B, C, 8, 11, 0x8771F681 );
16812: P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
16813: P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
16814: P3( X, &A, B, C, D, 1, 4, 0xA4BEEA44 );
16815: P3( X, &D, A, B, C, 4, 11, 0x4BDECFA9 );
16816: P3( X, &C, D, A, B, 7, 16, 0xF6BB4B60 );
16817: P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
16818: P3( X, &A, B, C, D, 13, 4, 0x289B7EC6 );
16819: P3( X, &D, A, B, C, 0, 11, 0xEAA127FA );
16820: P3( X, &C, D, A, B, 3, 16, 0xD4EF3085 );
16821: P3( X, &B, C, D, A, 6, 23, 0x04881D05 );
16822: P3( X, &A, B, C, D, 9, 4, 0xD9D4D039 );
16823: P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
16824: P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
16825: P3( X, &B, C, D, A, 2, 23, 0xC4AC5665 );
16826: P4( X, &A, B, C, D, 0, 6, 0xF4292244 );
16827: P4( X, &D, A, B, C, 7, 10, 0x432AFF97 );
16828: P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
16829: P4( X, &B, C, D, A, 5, 21, 0xFC93A039 );
16830: P4( X, &A, B, C, D, 12, 6, 0x655B59C3 );
16831: P4( X, &D, A, B, C, 3, 10, 0x8F0CCC92 );
16832: P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
16833: P4( X, &B, C, D, A, 1, 21, 0x85845DD1 );
16834: P4( X, &A, B, C, D, 8, 6, 0x6FA87E4F );
16835: P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
16836: P4( X, &C, D, A, B, 6, 15, 0xA3014314 );
16837: P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
16838: P4( X, &A, B, C, D, 4, 6, 0xF7537E82 );
16839: P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
16840: P4( X, &C, D, A, B, 2, 15, 0x2AD7D2BB );
16841: P4( X, &B, C, D, A, 9, 21, 0xEB86D391 );
16842: ctx->state[0] += A;
16843: ctx->state[1] += B;
16844: ctx->state[2] += C;
16845: ctx->state[3] += D; }
16846:
16847: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
16848: { uint32 left, fill;
16849: if( length < 1 ) return;
16850: left = ctx->total[0] & 0x3F;
16851: fill = 64 - left;
16852: ctx->total[0] += length;
16853: ctx->total[0] &= 0xFFFFFFFF;
16854: if( ctx->total[0] < length )
16855: ctx->total[1]++;
16856: if( left && length >= fill )
16857: { memcpy( (void *) (ctx->buffer + left),
16858: (void *) input, fill );
16859: md5_process( ctx, ctx->buffer );
16860: length -= fill;
16861: input += fill;
16862: left = 0; }
16863: while( length >= 64 )
16864: { md5_process( ctx, input );
16865: length -= 64;
16866: input += 64; }
16867: if( length >= 1 )
16868: memcpy( (void *) (ctx->buffer + left),
16869: (void *) input, length ); }
16870:
16871: void md5_finish( md5_context *ctx, uint8 digest[16] )
16872: { static uint8 md5_padding[64] =
16873: { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16874: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16875: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16876: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
16877: uint32 last, padn;
16878: uint32 high, low;
16879: uint8 msglen[8];
16880: high = ( ctx->total[0] >> 29 )
16881: | ( ctx->total[1] << 3 );
16882: low = ( ctx->total[0] << 3 );
16883: PUT_UINT32( low, msglen, 0 );
16884: PUT_UINT32( high, msglen, 4 );
16885: last = ctx->total[0] & 0x3F;
16886: padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
16887: md5_update( ctx, md5_padding, padn );
16888: md5_update( ctx, msglen, 8 );
16889: PUT_UINT32( ctx->state[0], digest, 0 );
16890: PUT_UINT32( ctx->state[1], digest, 4 );
16891: PUT_UINT32( ctx->state[2], digest, 8 );
16892: PUT_UINT32( ctx->state[3], digest, 12 ); }
16893: /* --- end-of-function md5str() and "friends" --- */
16894:
1.5 ! raeburn 16895:
1.1 albertel 16896: #if defined(GIF)
16897: /* ==========================================================================
16898: * Function: GetPixel ( int x, int y )
16899: * Purpose: callback for GIF_CompressImage() returning the
16900: * pixel at column x, row y
16901: * --------------------------------------------------------------------------
16902: * Arguments: x (I) int containing column=0...width-1
16903: * of desired pixel
16904: * y (I) int containing row=0...height-1
16905: * of desired pixel
16906: * --------------------------------------------------------------------------
16907: * Returns: ( int ) 0 or 1, if pixel at x,y is off or on
16908: * --------------------------------------------------------------------------
16909: * Notes: o
16910: * ======================================================================= */
16911: /* --- entry point --- */
16912: int GetPixel ( int x, int y )
16913: {
1.5 ! raeburn 16914: int ipixel = y*raster_width + x; /* pixel index for x,y-coords*/
1.1 albertel 16915: int pixval =0; /* value of pixel */
16916: if ( !isaa ) /* use bitmap if not anti-aliased */
16917: pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
16918: else /* else use anti-aliased grayscale*/
16919: pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
16920: if ( msgfp!=NULL && msglevel>=9999 ) /* dump pixel */
16921: { fprintf(msgfp,"GetPixel> x=%d, y=%d pixel=%d\n",x,y,pixval);
16922: fflush(msgfp); }
16923: return pixval;
16924: } /* --- end-of-function GetPixel() --- */
16925: #endif /* gif */
16926: #endif /* driver */
16927: #endif /* PART1 */
16928: /* ======================= END-OF-FILE MIMETEX.C ========================= */
16929:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>