--- loncom/cgi/mimeTeX/gfuntype.c 2005/02/28 19:08:11 1.1 +++ loncom/cgi/mimeTeX/gfuntype.c 2006/03/24 23:08:33 1.2 @@ -17,7 +17,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * -------------------------------------------------------------------------- * - * Program: gfuntype [-n fontname] [-m msglevel] [infile [outfile]] + * Program: gfuntype [-g gformat] [-u isnoname] [-m msglevel] + * [-n fontname] [infile [outfile]] * * Purpose: Parses output from gftype -i * and writes pixel bitmap data of the characters @@ -31,6 +32,13 @@ * (defaults to stdin if no filenames given) * outfile name of output file * (defaults to stdout if <2 filenames given) + * -g gformat gformat=1(default) for bitmap representation, + * or 2,3 for 8-bit,4-bit .gf-like compression, + * or 0 to choose smallest format. + * Add 10 (gformat=10,12,13,14) to embed scan + * line repeat counts in format. + * -u isnoname isnoname=1(default) to output symbols not + * defined/named in mimetex.h, or 0 to omit them * -m msglevel verbose if msglevel>=9 (vv if >=99) * -n fontname string used for fontname * (defaults to noname) @@ -46,6 +54,7 @@ * -------------------------------------------------------------------------- * Revision History: * 09/22/02 J.Forkosh Installation. + * 10/11/05 J.Forkosh .gf-style format options added. * ****************************************************************************/ @@ -62,13 +71,26 @@ standard headers, program parameters, gl #include "mimetex.h" /* --- parameters either -D defined on cc line, or defaulted here --- */ #ifndef MSGLEVEL -#define MSGLEVEL 0 + #define MSGLEVEL 0 +#endif +#ifndef GFORMAT + #define GFORMAT 1 +#endif +#ifndef ISREPEAT + #define ISREPEAT 1 #endif /* --- message level (verbose test) --- */ static int msglevel = MSGLEVEL; /* verbose if msglevel >= 9 */ static FILE *msgfp; /* verbose output goes here */ +/* --- output file format --- */ +static int isnoname = 1; /* true to output unnamed symbols */ +static char *noname = "(noname)"; /* char name used if lookup fails */ +static int gformat = GFORMAT; /* 1=bitmap, 2=.gf-like */ +static int isrepeat = ISREPEAT; /* true to store line repeat counts*/ +/* extern int imageformat; */ /* as per gformat, 1=bitmap,2=.gf */ /* --- miscellaneous other data --- */ #define CORNER_STUB ".<--" /* start of upper,lower-left line */ +#define BLANKCHAR_STUB "character is entirely blank" /* signals blank char */ #define TYPECAST "(pixbyte *)" /* typecast for pixmap string */ /* ========================================================================== @@ -120,7 +142,11 @@ while ( argc > ++argnum ) /* check for f /* --- no usage for clueless users yet --- */ default: exit(iserror); /* exit quietly for unrecognized input */ /* --- adjustable program parameters (not checking input) --- */ - case 'm': msglevel = atoi(argv[argnum]); break; + case 'g': gformat = atoi(argv[argnum]); + isrepeat = (gformat>=10?1:0); + gformat = gformat%10; break; + case 'u': isnoname = atoi(argv[argnum]); break; + case 'm': msglevel = atoi(argv[argnum]); break; case 'n': strcpy(fontname,argv[argnum]); break; } /* --- end-of-switch() --- */ } /* --- end-of-if(*argv[]=='-') --- */ @@ -136,8 +162,9 @@ msgfp = (outarg>0? stdout : stderr); /* fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */ /* --- display input args if verbose output --- */ if ( msglevel >= 9 ) /* verbose output requested */ - fprintf(msgfp,"gfuntype> infile=%s, outfile=%s, fontname=%s\n", - (inarg>0?argv[inarg]:"stdin"), (outarg>0?argv[outarg]:"stdout"), fontname); + fprintf(msgfp,"gfuntype> infile=%s outfile=%s, fontname=%s format=%d.%d\n", + (inarg>0?argv[inarg]:"stdin"), (outarg>0?argv[outarg]:"stdout"), + fontname, gformat,isrepeat); /* -------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ @@ -149,6 +176,10 @@ if ( inarg > 0 ) /* input from file, no if ( (infp = fopen(argv[inarg],"r")) == NULL ) /*try to open input file*/ { fprintf(msgfp,"gfuntype> can't open %s for read\n",argv[inarg]); goto end_of_job; } /* report error and quit */ +/* --- set format for mimetex.c functions --- */ +if ( gformat<0 || gformat>3 ) gformat=1; /* sanity check */ +/* if ( gformat == 1 ) imageformat = 1; */ /* force bitmap format */ +/* else gformat = imageformat = 2; */ /* or force .gf format */ /* -------------------------------------------------------------------------- process input file -------------------------------------------------------------------------- */ @@ -180,18 +211,23 @@ fprintf(outfp,"/%c --- fontdef for %s -- fprintf(outfp,"static\tchardef %c%s[] =\n {\n", ' ',fontname); /* --- write characters comprising font --- */ for ( charnum=0; charnum<256; charnum++ ) /*for each possible char in font*/ - if ( fontdef[charnum] != (chardef *)NULL ) /*check if char exists in font*/ - { if ( ++nchars > 1 ) /* bump count */ - fprintf(outfp,",\n"); /* and terminate preceding chardef */ - fprintf(outfp," /%c --- pixel bitmap for %s char#%d %s --- %c/\n", - '*',fontname,charnum,getcharname(fontname,charnum),'*'); - cstruct_chardef(fontdef[charnum],outfp,6); } /*emit chardef as struct*/ - else - if(0)fprintf(outfp,"NULL"); /* no character in this position */ + if ( fontdef[charnum] != (chardef *)NULL ) /*check if char exists in font*/ + { char *charname = getcharname(fontname,charnum); + if ( charname!=NULL || isnoname ) { /* char defined or want undefined */ + if ( ++nchars > 1 ) /* bump count */ + fprintf(outfp,",\n"); /* and terminate preceding chardef */ + fprintf(outfp," /%c --- pixel bitmap for %s char#%d %s --- %c/\n", + '*',fontname,charnum,(charname==NULL?noname:charname),'*'); + cstruct_chardef(fontdef[charnum],outfp,6); } /*emit chardef struct*/ + else + if(0)fprintf(outfp,"NULL"); /* no character in this position */ + } /* --- end-of-if(fontdef[]!=NULL) --- */ + else + if(0)fprintf(outfp,"NULL"); /* no character in this position */ /* --- write trailer chardef and closing brace --- */ fprintf(outfp,",\n"); /* finish up last map from loop */ fprintf(outfp," /%c --- trailer --- %c/\n",'*','*'); /* trailer... */ -fprintf(outfp," { -99, -999, 0,0,0,0, { 0,0,0, %s\"\\0\" } }\n", +fprintf(outfp," { -99, -999, 0,0,0,0, { 0,0,0,0, %s\"\\0\" } }\n", TYPECAST); fprintf(outfp," } ;\n"); /* terminating }; for fontdef */ /* -------------------------------------------------------------------------- @@ -230,23 +266,26 @@ chardef *new_chardef(), *nextchar=(chard int delete_chardef(); /* free allocated memory if error */ int findnextchar(), charnum,location; /* get header line for next char */ int rasterizechar(); /* ascii image --> raster pixmap */ -int parsecorner(); /* get col,row from ".<--" line */ +int parsestat=(-999), parsecorner(); /* get col,row from ".<--" line */ char *readaline(); /* read next line from fp */ /* -------------------------------------------------------------------------- initialization -------------------------------------------------------------------------- */ -/* --- find and interpret header line for next character --- */ -charnum = findnextchar(fp,&location); /* read and parse header line */ -if ( charnum < 0 ) goto error; /* eof or error, no more chars */ -/* --- allocate a new chardef struct and begin populating it --- */ -if ( (nextchar=new_chardef()) /* allocate a new chardef */ -== (chardef *)NULL ) goto error; /* and quit if we failed */ -nextchar->charnum = charnum; /* store charnum in struct */ -nextchar->location = location; /* and location */ -/* --- get upper-left corner line --- */ -if ( !parsecorner(readaline(fp), /* parse corner line */ -&(nextchar->toprow),&(nextchar->topleftcol)) ) /* row and col from line */ - goto error; /* and quit if failed */ +while ( parsestat == (-999) ) { /* flush entirely blank characters */ + /* --- find and interpret header line for next character --- */ + charnum = findnextchar(fp,&location); /* read and parse header line */ + if ( charnum < 0 ) goto error; /* eof or error, no more chars */ + /* --- allocate a new chardef struct and begin populating it --- */ + if ( nextchar == (chardef *)NULL ) /* haven't allocated chardef yet */ + if ( (nextchar=new_chardef()) /* allocate a new chardef */ + == (chardef *)NULL ) goto error; /* and quit if we failed */ + nextchar->charnum = charnum; /* store charnum in struct */ + nextchar->location = location; /* and location */ + /* --- get upper-left corner line --- */ + parsestat = parsecorner(readaline(fp), /* parse corner line */ + &(nextchar->toprow),&(nextchar->topleftcol)); /* row and col from line */ + } /* --- end-of-while(parsestat) --- */ +if ( !parsestat ) goto error; /* quit if parsecorner() failed */ /* -------------------------------------------------------------------------- interpret character image (and parse terminating corner line) -------------------------------------------------------------------------- */ @@ -276,6 +315,7 @@ end_of_job: * and returns the corresponding charname. * -------------------------------------------------------------------------- * Arguments: fontname (I) char * containing fontname for font family + * (from -n switch on command line) * charnum (I) int containing the character number * whose corresponding name is wanted. * Returns: ( char * ) ptr to character name @@ -290,14 +330,18 @@ char *getcharname ( char *fontname, int Allocations and Declarations -------------------------------------------------------------------------- */ /* --- recognized font family names and our corresponding numbers --- */ -static char *fnames[] = { "cmr","cmmi","cmsy","cmex",NULL }; -static int fnums[] = { CMR10,CMMI10,CMSY10,CMEX10, -1 }; -static char *noname = "(noname)"; /* char name returned if lookup fails */ +static char *fnames[] = /*font name from -n switch on command line*/ + { "cmr","cmmib","cmmi","cmsy","cmex","bbold","rsfs","stmary", NULL }; +static int fnums[] = /* corresponding mimetex fontfamily number*/ + { CMR10,CMMIB10,CMMI10,CMSY10,CMEX10,BBOLD10,RSFS10,STMARY10, -1 }; +static int offsets[] = /* symtable[ichar].charnum = charnum-offset*/ + { 0, 0, 0, 0, 0, 0, 65, 0, -1 }; /* --- other local declarations --- */ -char *charname = noname; /* character name returned to caller */ +char *charname = NULL; /* character name returned to caller */ char flower[99] = "noname"; /* lowercase caller's fontname */ -int ifamily = 0, /* fnames[] (and fnums[]) index */ - ichar = 0; +int ifamily = 0, /* fnames[] (and fnums[],offsets[]) index */ + offset = 0, /* offsets[ifamily] */ + ichar = 0; /* loop index */ /* -------------------------------------------------------------------------- lowercase caller's fontname and look it up in fnames[] -------------------------------------------------------------------------- */ @@ -310,6 +354,7 @@ if ( strlen(flower) < 2 ) goto end_of_jo for ( ifamily=0; ;ifamily++ ) /* check fnames[] for flower */ if ( fnames[ifamily] == NULL ) goto end_of_job; /* quit at end-of-table */ else if ( strstr(flower,fnames[ifamily]) != NULL ) break; /* found it */ +offset = offsets[ifamily]; /* symtable[ichar].charnum = charnum-offset*/ ifamily = fnums[ifamily]; /* xlate index to font family number */ /* -------------------------------------------------------------------------- now look up name for caller's charnum in ifamily, and return it to caller @@ -320,10 +365,12 @@ for ( ichar=0; ;ichar++ ) /*search symta else if ( symtable[ichar].family == ifamily /* found desired family */ && symtable[ichar].handler == NULL ) /* and char isn't a "dummy" */ - if ( symtable[ichar].charnum == charnum ) break; /* found charnum */ + if ( symtable[ichar].charnum == charnum-offset ) break; /*got charnum*/ /* --- return corresponding charname to caller --- */ charname = symtable[ichar].symbol; /* pointer to symbol name in table */ end_of_job: + if ( charname==NULL && isnoname ) /* want unnamed/undefined chars */ + charname = noname; /* so replace null return with noname */ return ( charname ); } /* --- end-of-function getcharname() --- */ @@ -406,16 +453,22 @@ int rasterizechar ( FILE *fp, raster *im Allocations and Declarations -------------------------------------------------------------------------- */ char *readaline(), *line; /* read next scan line for char from fp */ -unsigned char bitvec[512][64]; /* scan lines parsed (up to 512x512 bits) */ +unsigned char bitvec[1024][128]; /* scan lines parsed up to 1024x1024 bits */ +int bitcmp(); /* compare bit strings */ int height = 0, /* #scan lines in fp comprising char */ - width = 0, /* #chars on longest scan line */ - pixsz = 1; /* default #bits per pixel, 1=bitmap */ -int iscan, ipixel=0, /* bitvec[] index, raster pixel map index */ + width = 0; /* #chars on longest scan line */ +int iscan, /* bitvec[] index */ ibit; /* bit along scan (i.e., 0...width-1) */ int isokay = 0; /* returned status, init for failure */ +/* --- bitmap and .gf-formatted image info (we'll choose smallest) --- */ +int iformat = gformat; /*0=best, 1=bitmap, 2=8-bit.gf, 3=4-bit.gf*/ +unsigned char gfpixcount[2][65536]; /* .gf black/white flips (max=64K) */ +int npixcounts[2] = {9999999,9999999}; /* #counts for 8-bit,4-bit .gf */ +int nbytes1=9999999,nbytes2=9999999,nbytes3=9999999;/*#bytes for format*/ /* -------------------------------------------------------------------------- read lines till ".<--" terminator, and construct one vector[] int per line -------------------------------------------------------------------------- */ +memset(bitvec,0,128*1024); /* zero-fill bitvec[] */ while ( (line=readaline(fp)) != NULL ) /* read lines until eof */ { /* --- allocations and declarations --- */ @@ -424,7 +477,7 @@ while ( (line=readaline(fp)) != NULL ) / if ( memcmp(line,CORNER_STUB,strlen(CORNER_STUB)) == 0 ) /* corner line */ break; /* so done with loop */ /* --- parse line (encode asterisks comprising character image) --- */ - memset(bitvec[height],0,64); /* first zero out all bits */ + memset(bitvec[height],0,128); /* first zero out all bits */ for ( icol=0; icolpixmap != NULL ) /* hmm, somebody already allocated memory */ - free((void *)image->pixmap); /* just free it */ + free((void *)image->pixmap); /* so just free it */ image->width = width; /* set image width within raster struct */ image->height = height; /* and height */ -image->pixsz = pixsz; /* #bits per pixel, 1=bitmap or 8=bytemap */ -if ( (image->pixmap = (unsigned char *)malloc(pixmapsz(image))) -== NULL ) goto end_of_job; /* quit if failed to allocate pixmap */ +image->format = gformat; /* set format (will be reset below) */ +image->pixsz = 1; /* #bits per pixel (or #counts in .gf fmt) */ +if ( gformat==0 || gformat==1 ) /* bitmap representation allowed */ + { nbytes1 = pixmapsz(image); /* #bytes needed for bitmap */ + iformat = 1; } /* default to bitmap format */ +/* -------------------------------------------------------------------------- +perform .gf-like compression on image in bitvec +-------------------------------------------------------------------------- */ +if ( gformat == 0 /* choose optimal/smallest respresentation */ +|| gformat==2 || gformat==3 ) /* .gf-like compressed representation */ + { + /* --- try both 8-bits/count and 4-bits/count for best compression --- */ + int maxbitcount[2] = {254,14}; /* don't count too much in one byte */ + int repeatcmds[2] = {255,15}; /* opcode for repeat/duplicate count */ + int minbytes = 0; /* #bytes needed for smallest format */ + for ( iformat=2; iformat<=3; iformat++ ) { /* 2=8-bit packing, 3=4-bit */ + int gfbitcount = 0, /* count of consecutive gfbitval's */ + gfbitval = 0, /* begin with count of leading 0's */ + pixcount = 0; /* #packed bytes (#black/white flips) */ + unsigned char *gfcount = gfpixcount[iformat-2]; /*counts for this format*/ + if ( gformat!=0 && gformat!=iformat ) /* this format not allowed */ + continue; /* so just skip it */ + for ( iscan=0; iscan iscan+1 ) /* we're below next line */ + if (nextreps == jscan-iscan-2) /*no intervening non-identical lines*/ + if ( bitcmp(bitvec[iscan+1],bitvec[jscan],width) == 0 )/*identical*/ + nextreps++; } /* so bump next lline repeat count */ + /* --- set repeat command and count --- */ + if ( nrepeats > 0 ) { /* found repeated lines below current */ + int maxrepeats = maxbitcount[iformat-2]; /*max count/repeats per byte*/ + if ( nrepeats > maxrepeats ) nrepeats=maxrepeats; /* don't exceed max */ + {setbyfmt(iformat,gfcount,pixcount,repeatcmds[iformat-2]);} /*set cmd*/ + {setbyfmt(iformat,gfcount,pixcount+1,nrepeats);} /* set #repeats */ + pixcount += 2; } /* don't bump pixcount within macros */ + } /* --- end-of-if(isrepeat) --- */ + /* --- set bit counts for current scan line --- */ + for ( ibit=0; ibit= maxbitcount[iformat-2] ) { /* max count per byte */ + {setbyfmt(iformat,gfcount,pixcount,gfbitcount);} /*set byte or nibble*/ + clearbyfmt(iformat,gfcount,pixcount+1); /*followed by dummy 0 count*/ + pixcount += 2; /* don't bump pixcount within macros */ + gfbitcount = 0; } /* reset consecutive bit count */ + if ( bitval == gfbitval ) /* same bit val as preceding, or first new */ + gfbitcount++; /* so just count another pixel */ + } /* --- end-of-for(ibit) --- */ + /* --- adjust for repeated scan lines --- */ + iscan += nrepeats; /* skip repeated/duplicate scan lines */ + if ( nrepeats>0 || nextreps>0 ) /* emit count to align on full scan */ + if ( iscan < height-1 ) /* have another scan line below this one */ + if ( gfbitcount > 0 ) { /* should always have some final count */ + {setbyfmt(iformat,gfcount,pixcount,gfbitcount);} /*set byte or nibble*/ + pixcount++; /* don't bump pixcount within macro */ + gfbitcount = 0; /* reset consecutive bit count */ + if ( bitval == getlongbit(bitvec[iscan+1],0) ) { /* same bit value */ + clearbyfmt(iformat,gfcount,pixcount); /*so we need a dummy 0 count*/ + pixcount++; } /* don't bump pixcount within macros */ + else /* bitval flips at start of next line */ + gfbitval = 1-gfbitval; /* so flip bit to be counted */ + } /* --- end-of-if(nrepeats...gfbitcount>0) --- */ + } /* --- end-of-for(iscan) --- */ + /* --- store final count --- */ + if ( gfbitcount > 0 ) { /* have a final count */ + {setbyfmt(iformat,gfcount,pixcount,gfbitcount);} /*set byte or nibble*/ + pixcount++; } /* don't bump pixcount within macro */ + else /* ended exactly after maxbitcount? */ + if ( getbyfmt(iformat,gfcount,pixcount-1) == 0 )/*have dummy 0 trailer?*/ + pixcount--; /* remove unneeded dummy trailer */ + /* --- save count to choose smallest --- */ + npixcounts[iformat-2] = pixcount; /* save count */ + } /* --- end-of-for(iformat) --- */ + /* --- check for optimal/smallest format --- */ + nbytes2=npixcounts[0]; nbytes3=(1+npixcounts[1])/2; /* #bytes for count */ + iformat = (nbytes2pixmap = (unsigned char *)malloc(minbytes)) /* alloc pixmap */ + == NULL ) goto end_of_job; /* quit if failed to allocate pixmap */ + memcpy(image->pixmap,gfpixcount[iformat-2],minbytes); /*copy local counts*/ + image->format = iformat; /* signal byte counts or nibble counts */ + image->pixsz = npixcounts[iformat-2]; /*#counts in pixmap for gformat=2,3*/ + } /* --- end-of-if(iformat!=1) --- */ + } /* --- end-of-if(gformat==2) --- */ /* -------------------------------------------------------------------------- copy each integer in bitvec[] to raster pixmap, bit by bit -------------------------------------------------------------------------- */ -for ( iscan=0; iscanpixmap = (unsigned char *)malloc(pixmapsz(image))) + == NULL ) goto end_of_job; /* quit if failed to allocate pixmap */ + image->format = iformat; /* reset format */ + /* --- now store bit image in allocated raster --- */ + for ( iscan=0; iscanpixmap,ipixel); } @@ -455,6 +619,7 @@ for ( iscan=0; iscanpixmap,ipixel); } ipixel++; /* bump image raster pixel */ } /* --- end-of-for(iscan,ibit) --- */ + } /* --- end-of-if(gformat==1) --- */ /* -------------------------------------------------------------------------- done -------------------------------------------------------------------------- */ @@ -490,6 +655,10 @@ extract (col,row) field from line, and i -------------------------------------------------------------------------- */ /* --- first, check beginning of line --- */ if ( line == (char *)NULL ) goto end_of_job; /* no line supplied by caller */ +/* --- check for blank line --- */ +if ( strstr(line,BLANKCHAR_STUB) != NULL ) /* got entirely blank character */ + return ( -999 ); /* so return special -999 signal */ +/* --- check for corner --- */ if ( memcmp(line,CORNER_STUB,strlen(CORNER_STUB)) != 0 ) /*not valid corner*/ goto end_of_job; /* so quit */ /* --- extract col,row field from line --- */ @@ -532,14 +701,14 @@ char *readaline ( FILE *fp ) /* -------------------------------------------------------------------------- Allocations and Declarations -------------------------------------------------------------------------- */ -static char buffer[1024]; /* static buffer if caller supplies none */ +static char buffer[2048]; /* static buffer returned to caller */ char *fgets(), *bufptr=buffer; /* read line from fp */ char *strchr(), *delim; /* remove terminating newline */ /* -------------------------------------------------------------------------- Read line and strip trailing newline -------------------------------------------------------------------------- */ if ( fp != NULL ) /*if null, return previous line read*/ - if ( (bufptr=fgets(buffer,1023,fp)) /* read next line from fp */ + if ( (bufptr=fgets(buffer,2047,fp)) /* read next line from fp */ != NULL ) /* and check that we succeeded */ { if ( (delim=strchr(bufptr,'\n')) /* look for terminating newline */ @@ -548,5 +717,40 @@ if ( fp != NULL ) /*if null, return pr } /* --- end-of-if(fgets()!=NULL) --- */ return ( bufptr ); /*back to caller with buffer or null*/ } /* --- end-of-function readaline() --- */ + + +/* ========================================================================== + * Function: bitcmp ( bs1, bs2, n ) + * Purpose: compares the first n bits of two strings + * -------------------------------------------------------------------------- + * Arguments: bs1 (I) unsigned char * to first bit string + * bs2 (I) unsigned char * to second bit string + * n (I) int containing #bits to compare + * Returns: ( int ) 0 if first n bits are identical + * -1 if first unmatching bit of bs1 is 0 + * +1 if first unmatching bit of bs2 id 0 + * -------------------------------------------------------------------------- + * Notes: o + * ======================================================================= */ +/* --- entry point --- */ +int bitcmp ( unsigned char *bs1, unsigned char *bs2, int n ) +{ +/* -------------------------------------------------------------------------- +Allocations and Declarations +-------------------------------------------------------------------------- */ +int icmp = 0; /* returned to caller */ +int nbytes = n/8, /* #full bytes we can compare with memcmp()*/ + nbits = n%8, ibit=0; /* #trailing bits in last byte, index */ +/* -------------------------------------------------------------------------- +compare leading bytes, then trailing bits +-------------------------------------------------------------------------- */ +if ( nbytes > 0 ) icmp = memcmp(bs1,bs2,nbytes); /* compare leading bytes */ +if ( icmp == 0 ) /* leading bytes identical */ + if ( nbits > 0 ) /* and we have trailing bits */ + for ( ibit=0; ibit