/* @(#)aglchar.c 17.1.1.1 (ES0-DMD) 01/25/02 17:33:34 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /* @(#)aglchar.c 17.1.1.1 (OAA-ASTRONET) 01/25/02 17:33:34 */ /* * HEADER : aglchar.c - Vers 3.6.006 - Feb 1994 - L. Fini, OAA * - Vers 3.6.005 - Oct 1992 - L. Fini, OAA */ /* CHARACTER GENERATOR */ #include #include #include "aglfonts.c" #define SLANTFACTOR 0.30 static struct AGL_font fonts[NFONTS]; /* character font descriptions */ static struct AGL_cmod chstack[NCHLEVELS]; static int chstack_pt=0; static int chstack_tp=FALSE; /* If TRUE: pop the stack on entry to */ /* AGL_mchar() */ static void AGL_chzero() /* Clear the metacharacter stack */ { /* To be called prior of beginning the */ chstack_pt=0; /* interpretation of a string so that */ chstack_tp=FALSE; /* nesting errors do not cumulate between */ } /* strings */ static void AGL_chpush(chmod) struct AGL_char *chmod; /* Character descriptor to push onto stack */ { if(chstack_ptmodifier.the_char; chstack[chstack_pt].font = chmod->modifier.font; chstack[chstack_pt].lwdt = chmod->modifier.lwdt; chstack[chstack_pt].mfact = chmod->modifier.mfact; chstack[chstack_pt].vshift = chmod->modifier.vshift; chstack[chstack_pt].slant = chmod->modifier.slant; chstack[chstack_pt].color = chmod->modifier.color; chstack_pt++; } } static void AGL_chpop(chmod) struct AGL_char *chmod; /* Character descriptor to pop from stack */ { if(chstack_pt-->0) { chmod->modifier.the_char = chstack[chstack_pt].the_char ; chmod->modifier.font = chstack[chstack_pt].font ; chmod->modifier.lwdt = chstack[chstack_pt].lwdt ; chmod->modifier.mfact = chstack[chstack_pt].mfact ; chmod->modifier.vshift = chstack[chstack_pt].vshift ; chmod->modifier.slant = chstack[chstack_pt].slant ; chmod->modifier.color = chstack[chstack_pt].color ; } } static char *mtable[] = { /* Metasequence table */ /* name command */ /* N.B.: Names must be sorted, values */ /* are separated by a single tab */ /* if there is a number after the */ /* tab it is a font switch for */ /* the following character only, */ /* If it is a "!" it is a perma- */ /* nent font switch. */ /* if it is a ? the following is */ /* a color code */ "AA 2A", "Alpha 1A", "Aquarius 4[", "Aries 4-", "Beta 1B", "Cancer 4:", "Capricorn 4@", "Chi 1X", "Delta 2D", "Earth 2)", "Epsilon 1E", "Eta 1H", "Gamma 2G", "Gemini 4/", "Iota 1I", "Jupiter 4%", "Kappa 1K", "Lambda 2L", "Leo 4;", "Libra 4=", "Mars 4$", "Mercury 4!", "Moon 4*", "Mu 1M", "Neptune 4(", "Nu 1N", "Omega 2W", "Omicron 1O", "PI 2P", "Phi 2F", "Pisces 4]", "Pluto 4)", "Psi 2Y", "Rho 1R", "Sagittarius 4?", "Saturn 4&", "Scorpio 4>", "Sigma 2S", "Sqrt 2^", "Tau 1T", "Taurus 4{", "Theta 2Q", "Upsilon 2U", "Uranus 4'", "Venus 4\"", "Virgo 4<", "Xi 2X", "Zeta 1Z", "aleph 2H", "alpha 2a", "asteroid 4,", "beta 2b", "bigcirc 2O", "black ?1", "blue ?4", "cents 2C", "chi 2c", "circ 3`", "clover 2@", "clubsuit 3~~", "comet 4+", "cyan ?7", "dag 2[", "ddag 2]", "default !0", "delta 2d", "diamond 3}", "div 2/", "downarrow 2N", "epsilon 2e", "equinox 4^", "equiv 2=", "eta 2h", "firtree 2v", "gamma 2g", "ge 2>", "greek !2", "green ?3", "hbar 2Z", "heart 3|", "infty 2B", "int 2:", "iota 2i", "italic !i", "kappa 2k", "lambda 2l", "larrow 2~~", "le 2<", "magenta ?6", "mp 2-", "mu 2m", "nabla 2J", "ne 2?", "nu 2n", "odot 2(", "oint 2;", "old !4", "omega 2w", "omicron 2o", "oplus 2)", "otimes 2K", "palmtree 2R", "paragraph 2&", "parallel 2|", "partial 2j", "perp 2T", "phi 2f", "pi 2p", "pm 2*", "propto 2,", "psi 2y", "red ?2", "rho 2r", "rightarrow 2_", "roman !1", "script !3", "shield 2V", "sigma 2s", "snow 4_", "spade 3}", "sqrt 2`", "sum 2$", "tau 2t", "theta 2q", "times 2*", "tiny !5", "uparrow 2M", "upsilon 2u", "varepsilon 2!", "varphi 2#", "vartheta 2\"", "white ?8", "xi 2x", "yellow ?5", "zeta 2z", }; static int my_cmp(s1,s2) /* Minimum maching string compare */ char *s1, *s2; { int ret=0; for(;;) { int c1= *s1++, c2= *s2++; if((c1=='\0')||isspace(c1)||(c2=='\0')) break; if((ret = c1-c2)!=0) break; } return ret; } #define LAST (sizeof(mtable)/sizeof(char *) - 1) static char *AGL_lseq(string,my_char) /* Execute a long metasequence */ char *string; struct AGL_char *my_char; /* Output modifier for characters */ { int first,last,middle; int cmp=(-1); char *pt; first=0; last=LAST; while (last>=first) { middle = (last+first)/2; if((cmp=my_cmp(string,mtable[middle]))==0) break; if(cmp < 0) last=middle-1; else first=middle+1; } if(cmp==0) { /* The sequence has been found */ pt=strchr(mtable[middle],' ')+1; switch(*pt) { case '!': /* font change */ pt++; if(*pt=='i') { my_char->modifier.slant=SLANTFACTOR; } else { my_char->modifier.font= *pt - '0'; my_char->modifier.the_char = '\0'; } break; case '?': /* Color change */ pt++; middle= *pt++-'0'; if((middle>=1)&&(middle<=8)) my_char->modifier.color= (AGL_status.actidx[AGL_status.devid]).colors[middle]; break; default: /* Return the character */ AGL_chpush(my_char); chstack_tp=TRUE; my_char->modifier.font= *pt++ - '0'; my_char->modifier.the_char = *pt; break; } pt=strchr(string,' '); /* skip after next blank in input string */ if(pt!=NULL) pt++; else pt=strchr(string,'\0'); } else pt=string; return pt; } /*****************************************************************************/ /* aglchrdf (Internal AGL use) */ /* Font file reading procedure */ /* A new character font structure is allocated with data read from "filnam" */ /* Font file structure is as follows: */ /* header (Header line to discard when reading) */ /* */ /* } */ /* } Totally */ /* } */ /* } */ /* ....... ....... ........ } */ /* ....... ....... ........ } lines */ /* ....... ....... ........ } */ /* code1 code2 code3 ...... } */ /* ..... ..... ..... ...... } Totally */ /* ..... ..... ..... ...... } */ /* ..... ..... ..... ...... } */ /* ..... ..... ..... ...... } */ /* ..... ..... ..... ...... } codes */ /* ..... ..... ..... ...... } */ /* ..... ..... ..... ...... } */ /* Where: is the first ascii code defined (usually 32 (space)) */ /* is the number of characters defined, so that the last */ /* ascii code defined is (+-1) */ /* This is also the number of three elements rows in the */ /* following part of the file */ /* is the number of codes used to describe character draw- */ /* ing coordinates and commands. It is equal to the number */ /* of codes in the third part of the file */ /* Number of pixels between characters (average) */ /* is the upward extension of an uppercase character with */ /* respect to the character origin (see below). It should */ /* be usually equal to the height of the character "l". An */ /* integer positive number of pixels. */ /* is the downward extension of charachters such as */ /* "p" and "q" with respect to the character origin (see */ /* below). An integer positive number of pixels. */ /* is a scaling factor to allow complex fonts to be speci- */ /* fied with optimum definition. The standard font width */ /* is 5, so if the medium width of a font is, say, 10, the */ /* scale factor should be set at 0.5 */ /* Then for each character: */ /* is the number of codes needed to define the caracter */ /* is the character width in pixel (including a 1 pixel */ /* space between this character and the next) */ /* is the index of the first code defining the character */ /* into the vector of codes (so that the index of the last */ /* one is: (+-1) */ /* Then all drawing codes follow. Drawing codes are polyline coordinate */ /* couples. Each polyline is terminated by the value 0. */ /* Coordinates are absolute with respect to the origin of the character box */ /* (the left bottom corner of a box ideally enclosing a capital letter). */ /* (x,y) coordinates are decimal numbers (in the range :[-127,127]) coded */ /* excess 128 (i.e.: coordinate 0 is 128, coordinate -127 is 1, coordinate */ /* +127 is 255. */ /* E.g.: the character "/" supposed to be drawn as a single line across the */ /* character box could be encoded as: */ /* 129 128 133 133 */ /* ^ ^ ^ ^ */ /* | | | | */ /* | | | | */ /* | | | | */ /* (1,0)------+---+ | | */ /* | | */ /* | | */ /* .. and draw to | | */ /* | | */ /* (5,5)--------------+---+ */ /* The corresponding would be 6 (5+1), and would be 4 */ static void aglchrdf(font) int font; /* Font slot to use */ { FILE *id; int j; extern void AG_DMSG(); extern FILE *AG_STDO(); int c1,c2,c3; int ch; float scale; AGL_status.errstat=FNTNUMERR; if ( (font<0 || font>=NFONTS) || (fonts[font].index != NULL) ) return; id = AG_STDO(fonts[font].fontnam,".nfn",0); if(id == NULL) { AG_DMSG("... not","found"); fonts[font].index = NULL; AGL_status.errstat=FNTFILERR; return; } while((ch=getc(id))=='#') /* Discard header lines */ while(getc(id)!='\n'); ungetc(ch,id); fscanf(id,"%d %d",&fonts[font].fcode, /* read first code and ... */ &fonts[font].nchars); /* number of codes */ fscanf(id,"%d",&fonts[font].nbytes); /* number of descr bytes */ fscanf(id,"%d",&c1); fscanf(id,"%d",&c2); fscanf(id,"%d",&c3); fscanf(id,"%f",&scale); fonts[font].fscale = scale; fonts[font].c_gap = c1; fonts[font].maxup = c2; fonts[font].maxdown = c3; fonts[font].index =(short *)calloc(fonts[font].nchars,sizeof(short)); fonts[font].chrwdt=(unsigned char *)calloc(fonts[font].nchars,sizeof(char)); fonts[font].dsclen=(unsigned char *)calloc(fonts[font].nchars,sizeof(char)); fonts[font].cbuffr=(unsigned char *)calloc(fonts[font].nbytes,sizeof(char)); if(fonts[font].cbuffr == NULL) { fclose(id); free((char *)fonts[font].index); free((char *)fonts[font].dsclen); free((char *)fonts[font].chrwdt); fonts[font].index=NULL; AGL_status.errstat=(MEMORYERR); return; } for(j=0;j=NFONTS) { AGL_status.errstat=FNTNUMERR; return NULL; } if(fonts[fontidx].index == NULL) aglchrdf(fontidx); if(AGL_status.errstat==AGLNOERR) return(fonts+fontidx); else return NULL; } #define OPEN_PAREN '{' #define CLOSED_PAREN '}' char *AGL_mchar(chstrg,my_char) /* Metacharacter interpretation. Returns */ /* pointer to the remaining part of the */ /* string, and modifiers into the output */ /* structure Together with the character */ /* to draw */ char *chstrg; /* input string */ struct AGL_char *my_char; /* Output modifier for characters */ { enum {CLEAN,BEGIN_SEQUENCE,LONG_SEQUENCE,END_STRING,END} status; int goon; int thischar; if(chstack_tp==TRUE) { /* Get back saved status */ AGL_chpop(my_char); chstack_tp=FALSE; } my_char->modifier.the_char = 0; if(chstrg == NULL) return NULL; if(*chstrg == '\0') return NULL; status=CLEAN; my_char->modifier.backspace=0; my_char->modifier.newline=0; goon=TRUE; do { switch(status) { case CLEAN: thischar = *chstrg++; switch(thischar) { case '\\': case '~': status=BEGIN_SEQUENCE; break; case '\0': status=END_STRING; break; default: my_char->modifier.the_char=thischar; status=END; break; } break; case BEGIN_SEQUENCE: thischar = *chstrg++; switch(thischar) { /* Check first character of sequence */ case BLANK: /* forced blank (e.g.: at beginning) */ case '\\': case '~': my_char->modifier.the_char=thischar; status=END; break; case '^': my_char->modifier.vshift += 0.5; my_char->modifier.mfact *= 0.833333333333; status=CLEAN; break; case '_': my_char->modifier.vshift -= 0.5; my_char->modifier.mfact *= 0.833333333333; status=CLEAN; break; case '<': /* Execute backspace */ my_char->modifier.backspace++; status=CLEAN; break; case '#': /* Include a marker */ sscanf(chstrg,"%d", &(my_char->modifier.the_char)); AGL_chpush(my_char); /* Save status to be */ chstack_tp=TRUE; /* Restored at next call */ my_char->modifier.font=SYMBFONT; while(*chstrg) if(isdigit(*chstrg)) chstrg++; else break; if(*chstrg==' ') chstrg++; else if(*chstrg != '\0') AGL_status.errstat=STRSTXWNG; status=END; break; case '+': my_char->modifier.mfact *= 1.2; status=CLEAN; break; case '-': my_char->modifier.mfact *= 0.833333333333; status=CLEAN; break; case '0': my_char->modifier.font=0; status=CLEAN; break; case '1': /* Select quality font */ my_char->modifier.font=1; status=CLEAN; break; case '2': /* Select greek font */ my_char->modifier.font=2; status=CLEAN; break; case '3': /* Select script font */ my_char->modifier.font=3; status=CLEAN; break; case '4': /* Select old english font */ my_char->modifier.font=4; status=CLEAN; break; case '5': /* Select tiny roman font */ my_char->modifier.font=5; status=CLEAN; break; case '[': /* use ticKer line width */ my_char->modifier.lwdt++; status=CLEAN; break; case ']': /* use thinner line width */ my_char->modifier.lwdt--; status=CLEAN; break; case 'n': /* Execute "newline" */ my_char->modifier.newline += 1; status=CLEAN; break; case '!': chstrg++; status=LONG_SEQUENCE; break; case '\0': /* Unexpected end of string */ status=END_STRING; break; case CLOSED_PAREN: AGL_chpop(my_char); status=CLEAN; break; case OPEN_PAREN: AGL_chpush(my_char); status=CLEAN; break; default: status=LONG_SEQUENCE; break; } break; case LONG_SEQUENCE: chstrg=AGL_lseq((chstrg-1),my_char); if(my_char->modifier.the_char=='\0') status=CLEAN; else status=END; break; case END_STRING: chstrg = NULL; goon=FALSE; break; case END: AGL_gcdes(my_char); /* Get the character descr. */ goon=FALSE; break; } } while (goon); return(chstrg); } /*****************************************************************************/ /* AGL_gcdes (Internal AGL use) */ /* Returns description of a given character into a suitable structure */ void AGL_gcdes(my_char) struct AGL_char *my_char; /* Returns required data */ /* my_char->the_char will be (-1) */ /* if the font is not available, or */ /* the first code availabe in the */ /* font, if the code is not in the */ /* font */ { int thechar=my_char->modifier.the_char; int thefont=my_char->modifier.font; double scale; my_char->modifier.the_char= -1; AGL_status.errstat=AGLNOERR; if(fonts[thefont].index == NULL) aglchrdf(thefont); if(AGL_status.errstat != AGLNOERR) return; if((thechar < fonts[thefont].fcode)|| (thechar >= (fonts[thefont].fcode+fonts[thefont].nchars))) thechar = fonts[thefont].fcode; my_char->modifier.the_char = thechar; thechar -= fonts[thefont].fcode; scale = fonts[thefont].fscale; my_char->descriptor.blk_gap = scale * fonts[thefont].c_gap; my_char->descriptor.heigth = scale * fonts[thefont].maxup; my_char->descriptor.down_ext= scale * fonts[thefont].maxdown; my_char->descriptor.width = scale * fonts[thefont].chrwdt[thechar]; my_char->descriptor.scale = scale; my_char->descriptor.ncodes = fonts[thefont].dsclen[thechar]; my_char->descriptor.codes = fonts[thefont].index[thechar] + fonts[thefont].cbuffr; } /*****************************************************************************/ /* AGL_strd (Internal AGL use) */ /* Returns dimensions in character generator coordinates of a given string */ /* The string is scanned to account for metacharacter interpretation. */ void AGL_strd(chstrg,font,twidth,theigth) char *chstrg; int font; double *twidth; double *theigth; /* +------- twidth ---------+ */ /* + *** * */ /* | * * * */ /* | * * * **** */ /* theigth | ***** **** * **** */ /* | * * * * * * * */ /* | * * **** **** **** */ /* | * */ /* | * */ /* | */ /* | */ /* | * **** * **** */ /* | * * * *** * */ /* | * **** * * */ /* | * * * * * */ /* | * * * * * * */ /* + ***** * * *** **** */ /* */ { struct AGL_char my_char; double hext=0.0, vext=0.0; double twd,thg; AGL_status.errstat = AGLNOERR; my_char.modifier.mfact = 1.0; my_char.modifier.font = font; my_char.modifier.vshift=0.0; my_char.modifier.backspace=0; my_char.modifier.newline=0; my_char.modifier.slant=0.0; my_char.descriptor.blk_gap = 0.0; my_char.descriptor.heigth = 0.0; my_char.descriptor.width = 0.0; my_char.descriptor.down_ext = 0.0; my_char.descriptor.scale = 0.0; my_char.descriptor.ncodes = 0; twd=0.0; thg=0.0; AGL_chzero(); /* Clean possible previous nesting errors */ while ((chstrg=AGL_mchar(chstrg,&my_char))!=NULL) { vext = my_char.descriptor.heigth; if(my_char.modifier.newline>0) { hext -= my_char.descriptor.blk_gap*my_char.modifier.mfact; twd=MAX(twd,hext); hext=0; thg += vext*2.0; vext=0; } else hext += my_char.descriptor.width*(1-my_char.modifier.backspace)*my_char.modifier.mfact; } hext -= my_char.descriptor.blk_gap*my_char.modifier.mfact; twd=MAX(twd,hext); thg += vext; *twidth = twd; *theigth = thg; AGL_chzero(); /* Clean possible nesting errors for subsequent uses */ } /*****************************************************************************/ /* AGL_ch2pl (Internal AGL use) */ /* Converts the given character into a sequence of polylines */ void AGL_ch2pl(my_char,flag) struct AGL_char *my_char; /* Character description structure */ /* (already filled by AGL_gcdes()) */ int flag; /* If 0 center the character */ { float *XV=my_char->descriptor.XB,*YV=my_char->descriptor.YB; int maxstrokes=MAX_STROKES; int maxcoords =CHARPOLYBUFLNG; int npt=my_char->descriptor.ncodes; unsigned char *cd=my_char->descriptor.codes; double xoff,yoff; int i; int goon; AGL_status.errstat = AGLNOERR; my_char->descriptor.nstrokes=0; if(flag==0) { xoff = -(my_char->descriptor.width*0.5); yoff = -(my_char->descriptor.heigth*0.5); } else { xoff=0.0; yoff=0.0; } for(goon=TRUE,i=0;(idescriptor.strokes[i])); for(;;) { /* filling i-th polyline */ double auxx,auxy; if(*cd == 0) { /* begin new stroke */ cd++;npt--; break; } if(--maxcoords<0) { AGL_status.errstat = CHGBUFOVSEV; return; } auxx = (int)(*cd++)-128; auxy = (int)(*cd++)-128; *XV++ = (auxx+auxy*my_char->modifier.slant) * my_char->descriptor.scale + xoff; *YV++ = auxy * my_char->descriptor.scale + yoff; cur_pts++; if((npt-=2)<=0){ /* End of character */ goon=FALSE; break; } } my_char->descriptor.strokes[i].nvects=cur_pts; my_char->descriptor.strokes[i].maxvects=cur_pts; } if(i>maxstrokes) AGL_status.errstat = CHGBUFOVSEV; else my_char->descriptor.nstrokes=i; } /*****************************************************************************/ /* AGL_cginit (Internal AGL use) */ /* Character generation structure initialization */ /* This routine is to be called at AGL initialization in order to set up */ /* character generator data structures and the standard AGL character font */ void AGL_cginit () { int i; for(i=0;i