V10/cmd/dimpress/orast.c
/*
*
* A few routines that read and process raster files supplied by Imagen.
*
* Impress, at least at the time these routines were written, only allowed
* us to use families 0 to 95. That imposes a limit on the total number of
* raster files that any one job can use, unless we do some kind of memory
* management, and that in turn restricts the number of entries in array
* fam_data[] to 96. Unless this limit is changed MAXFAMILY should never be
* defined to be greater than 95.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ext.h" /* external variable definitions */
#include "gen.h" /* definitions used by everyone */
#include "rast.h" /* raster file definitions */
#include "impcodes.h" /* Impress 2.0 opcodes */
Rst rst[] = RST_INIT; /* describes raster file structure */
Rastdata fam_data[MAXFAMILY+1]; /* data about raster files we've read */
int fam_num = 0; /* use this as next family number */
int next_fam = 0; /* next free spot in fam_data[] */
int cur_fam = 0; /* family number we're using right now */
int last_fam = MAXFAMILY+1; /* last one we told printer about */
Rastdata *fam = NULL; /* &fam_data[cur_fam] */
int rast_res = RAST_RES; /* raster table resolution */
/*****************************************************************************/
getfamdata(f, s)
char *f; /* find data for this font */
int s; /* in this point size */
{
int i; /* check fam_data[i] next */
/*
*
* Looks for the family data for font *f and size s in the fam_data[]
* data structure. Returns the index where it was found, or next_fam
* if not found.
*
*/
for ( i = 0; i < next_fam; i++ )
if ( fam_data[i].size == s && strcmp(f, fam_data[i].name) == 0 )
break;
return(i);
} /* End of getfamdata */
/*****************************************************************************/
getrastdata(f, s)
char *f; /* use this font's raster file */
int s; /* in this point size */
{
int i; /* loop index for setting families */
/*
*
* Called to find the raster file info for font f, size s in fam_data[].
* If it's not there it will be read in from the appropriate file in
* *rastdir and all the required info will be added to location
* next_fam in fam_data[].
*
* I'm assuming that each raster file contains at most 128 glyphs. If
* that's not the case we'll need to change the way things are done
* because Impress only allows 128 members in each family. Brian took
* account of of larger raster files in di10.c and it wouldn't be all
* that difficult to do it here too, but I don't think it's necessary.
*
*/
cur_fam = getfamdata(f, s); /* use this family next */
fam = &fam_data[cur_fam]; /* its entry in fam_data[] */
if ( cur_fam >= next_fam ) { /* didn't find it */
if ( next_fam > MAXFAMILY ) /* only use families 0 to 95 */
error(FATAL, "job uses too many raster files");
strncpy(fam->name, f, L_FNAME); /* initialze some of the fields */
fam->name[L_FNAME-1] = '\0';
fam->size = s;
fam->rst = NULL;
fam->advance = (int *) rastalloc((MAXMEMBER+1) * sizeof(int), FALSE);
for ( i = 0; i < ROT_COUNT; i++ ) {
fam->chused[i] = (unsigned char *) rastalloc((MAXMEMBER+BYTE)/BYTE, TRUE, 0);
fam->fam[i] = -1;
} /* End for */
next_fam++; /* can only use it once */
} /* End if */
if ( fam->rst == NULL ) /* need to read the whole raster file */
readrastfile(f, s);
} /* End of getrastdata */
/*****************************************************************************/
readrastfile(f, s)
char *f; /* name of the raster file */
int s; /* in this point size */
{
char name[100]; /* raster file pathname built up here */
int fd; /* file descriptor for *name */
struct stat buf; /* used to get raster file size */
/*
*
* Reads the raster file for font f in size s from directory *rastdir
* and initializes some of the required fields in fam_data[cur_fam].
*
* First we try to open the appropriate raster file. Then we determine
* its size in bytes and try to allocate enough memory for the whole file.
* After we've successfully read it we close the file and then initialize
* the rest of the fields in fam_data[cur_fam].
*
* I've made allowances for two types of raster file naming conventions.
* The most common is the one that troff's post-processor will be using.
* The raster file names all begin with the string "font." and end with
* the suffix size. For example the raster table for the Roman font in size
* 10 would be called R.10. The other convention is the one you'll find
* in all the raster files supplied directly from Imagen (as of 9/15 anyway).
* In this case the file names all begin with the prefix string "*font.r"
* and again end with the size. For example the raster file for font cmti
* in size 12 would be cmti.r12. Since this routine is used by programs that
* expect both conventions I've written it to try the two different raster
* file names before giving up.
*
*/
sprintf(name, "%s/%s.%d", rastdir, f, s);
if ( (fd = open(name, 0)) == -1 ) {
sprintf(name, "%s/%s.r%d", rastdir, f, s);
if ( (fd = open(name, 0)) == -1 )
error(FATAL, "can't open raster file for font %s, size %d", f, s);
} /* End if */
if ( fstat(fd, &buf) == -1 )
error(FATAL, "can't get file size for %s", name);
fam->rst = rastalloc(buf.st_size, FALSE);
if ( read(fd, fam->rst, buf.st_size) != buf.st_size )
error(FATAL, "can't read raster file %s", name);
close(fd); /* all done with it - for now anyway */
fam->glyphdir = getvalue(P_GLYDIR);
fam->first = getvalue(P_FIRSTGLY);
fam->last = getvalue(P_LASTGLY);
if ( (fam->mag = getvalue(P_MAG)) <= 0 )
fam->mag = 1000;
} /* End of readrastfile */
/*****************************************************************************/
freerast(count)
int count; /* max number of calls to free() */
{
int called; /* number of calls made so far */
int i; /* loop index */
/*
*
* Tries to free all the memory used to store raster file data for
* at most count entires in array fam_data[]. The actual number of
* calls to free() is returned to the caller. A return value of 0
* obviously means there's no more memory to release. We don't want to
* free the memory used to keep track of the characters we've downloaded
* because we may want to use this raster file later on and we'll need to
* know which glyphs have been downloaded.
*
* Although I don't expect to use this routine often, some of the
* raster files are huge and there will undoubtedly be a few jobs
* that won't be able to keep all the required raster data in memory
* at the same time.
*
*/
for ( called = 0, i = 0; i < next_fam && called < count; i++ )
if ( fam_data[i].rst != NULL ) {
free(fam_data[i].rst);
fam_data[i].rst = NULL;
called++;
} /* End if */
return(called);
} /* End of freerast */
/*****************************************************************************/
resetrast()
{
int i, j; /* loop indices - family and angle */
/*
*
* Called when we want to clear the fam_data[] array, free all the memory
* associated with each raster file, and reset all the variables used to
* access the raster file info. Not used when we're generating Impress
* commands used to print files, but it may be useful when we build and
* edit raster files and troff's ASCII font files - maybe it shouldn't
* be in this file.
*
*/
for ( i = 0; i < next_fam; i++ ) {
free(fam_data[i].rst);
free(fam_data[i].advance);
fam_data[i].name[0] = '\0';
fam_data[i].size = 0;
for ( j = 0; j < ROT_COUNT; j++ )
free(fam_data[i].chused[j]);
} /* End for */
next_fam = 0;
cur_fam = 0;
last_fam = -1;
fam = NULL;
} /* End of resetrast */
/*****************************************************************************/
char *rastalloc(size, init, val)
unsigned size; /* allocate this many bytes */
int init; /* if TRUE initialize bytes */
int val; /* to these values */
{
char *ptr; /* block of memory we just got */
int i; /* loop index for initialization */
/*
*
* Tries to allocate a block of memory that's large enough to be used for
* storage of size bytes. If init is TRUE the memory will be initialized
* to val. It's assumed that the memory is going to be used to store
* a raster file or other related data. We'll keep trying to get enough
* memory as long as some is being used for other raster files.
*
*/
while ( (ptr = (char *) malloc(size)) == NULL )
if ( freerast(1) == 0 )
error(FATAL, "can't allocate memory for raster file");
if ( init == TRUE ) /* set all bytes to val */
for ( i = 0; i < size; i++ )
*(ptr + i) = val;
return(ptr);
} /* End of rastalloc */
/*****************************************************************************/
download(c, advance, angle, fp)
int c; /* download this glyph? */
int advance; /* using this as the advance width */
int angle; /* glyph rotation - just 2 bits */
FILE *fp; /* Impress commands written here */
{
Glyph glyph; /* all of c's important data */
int map_size; /* bytes needed to store the bitmap */
int i; /* loop index for downloading glyph */
/*
*
* Downloads glyph number c, using the raster file fam->rst and advance
* as the character advance. All the Impress commands are written to FILE
* *fp. If advance is greater than or equal to zero we'll assume the caller
* wants to use the value as the character advance for the glyph, otherwise
* we'll get the value from the raster file. Returns the member number in
* the current family that corresponds to glyph c.
*
* I've added glyph rotation capabilities to this routine. The parameter
* angle selects the absolute orientation of the glyph. Each angle needs
* to be handled as a separate family. That means we'll want separate
* chused bitmaps and family numbers for each orientation. I've added
* the necessary fields to Rastdata. The fam[] field is new and really
* needed so we don't waste our limited family numbers on orientations
* that will never be used in the document. The Impress family is no
* longer the font's index in fam_data[].
*
* Does much of the stuff handled by xychar() in di10.c except for the
* positioning of the reference point and printing the member. I've tried
* to make things a little more general so I'm assuming that whoever calls
* it has done the appropriate positioning and will take care of printing
* the glyph.
*
*/
if ( c < fam->first || c > fam->last )
error(FATAL, "glyph %d out of range for font %s", c, fam->name);
if ( fam->fam[angle] != last_fam ) { /* using a new family now */
if ( fam->fam[angle] == -1 ) {
fam->fam[angle] = fam_num;
if ( (fam_num += 1 + fam->last/128) > MAXFAMILY )
error(FATAL, "job uses too many families");
} /* End if */
putc(ASF, fp);
putc(last_fam = fam->fam[angle] + c/128, fp);
} /* End if */
if ( ! checkbit(fam->chused[angle], c) ) { /* first use of this glyph */
setbit(fam->chused[angle], c); /* don't download it again */
fam->advance[c] = (advance < 0 ) ? PIXEL_WIDTH(getvalue(G_CHWIDTH, c), rast_res) : advance;
glyph.xref = getvalue(G_XREF, c); /* we'll need these soon */
glyph.yref = getvalue(G_YREF, c);
glyph.width = getvalue(G_WIDTH, c);
glyph.height = getvalue(G_HEIGHT, c);
glyph.advance = fam->advance[c];
glyph.bptr = fam->rst + getvalue(G_BPTR, c);
if ( angle != ROT_0 )
rotate(&glyph, angle);
if ( (map_size = glyph.height * ((glyph.width + BYTE - 1) / BYTE)) > 0 ) {
putc(ABGLY, fp); /* define the glyph */
putint((angle << 14) | (last_fam << 7) | c % 128, fp);
putint(glyph.advance, fp);
putint(glyph.width, fp);
putint(glyph.xref, fp);
putint(glyph.height, fp);
putint(glyph.yref, fp);
for ( i = map_size; i--; ) /* followed by its bitmap */
putc(*glyph.bptr++, fp);
} else error(NON_FATAL, "Bad glyph - font %s, glyph %d", fam->name, c);
} /* End if */
return(c % 128);
} /* End of download */
/*****************************************************************************/
unsigned getvalue(field, ch)
int field; /* position in rst[] */
int ch; /* character number - for glyphs */
{
register int n; /* number of bytes in the field */
register char *p; /* start of the field */
register unsigned value; /* result after decoding field */
/*
*
* Called to figure out the value field in the current family's raster file.
* Right now things don't work for strings, but they're not really important.
*
* You'll see quite a few calls to this routine without the parameter ch.
* It's only used when we're accessing the glyph directory so I've omitted
* passing a value when we're looking at the preamble.
*
*/
n = rst[field].size; /* number of bytes we want to grab */
p = fam->rst + rst[field].offset; /* right except for glyph directory */
if ( rst[field].glyphdir == TRUE ) /* it's really part of the directory */
p += GLYPH_PTR(ch);
value = (rst[field].type == INTEGER && (*p & 0200)) ? ~0 : 0;
for ( ; n > 0; n--, p++ )
value = (value << BYTE) | (*p & BMASK);
return(value);
} /* End of getvalue */
/*****************************************************************************/
checkbit(p, n)
char *p; /* start of the bitmap */
int n; /* see if this bit is set */
{
/*
*
* Returns the value (0 or 1) set in the n-th bit in the bitmap that
* starts at address p. Primarily used to keep track of which glyphs
* have already been downloaded, but it's also needed in the routine
* that dumps bitmaps out to files. It probably should be defined as
* a macro.
*
*/
return(((p[n / BYTE] >> ((BYTE - 1) - n % BYTE)) & 01));
} /* End of checkbit */
/*****************************************************************************/
setbit(p, n)
char *p; /* start of the bitmap - again */
int n; /* bit that we want to set in p[] */
{
/*
*
* Turns on the n-th bit in the bitmap that starts at address p. This
* routine is really only used to keep track of the glyphs that have
* already been downloaded. Again it could easily be defined as a macro.
*
*/
p[n / BYTE] |= 01 << ((BYTE - 1) - n % BYTE);
} /* End of setbit */
/*****************************************************************************/
dump_glyph(n, fp)
int n; /* glyph number we want to display */
FILE *fp; /* everything's written here */
{
int height; /* pixel height of the bitmap */
int width; /* width of the rows in pixels */
int xref; /* x coordinate of reference point */
int yref; /* same but for y coordinate */
int chwidth; /* width of the character */
int rwid; /* number of bytes per bitmap row */
int i, j; /* loop indices for the bitmap */
char *bptr; /* start of the glyph's bitmap */
/*
*
* Really just a debugging routine that's used to dump the bitmap and
* other glyph data for character n out to FILE *fp.
*
*/
height = getvalue(G_HEIGHT, n);
width = getvalue(G_WIDTH, n);
xref = getvalue(G_XREF, n);
yref = getvalue(G_YREF, n);
chwidth = getvalue(G_CHWIDTH, n);
bptr = fam->rst + getvalue(G_BPTR, n);
rwid = (width + BYTE - 1) / BYTE;
fprintf(fp, "GLYPH NUMBER %d Character Width = %d\n", n, PIXEL_WIDTH(chwidth, rast_res));
fprintf(fp, " Raster Data: height = %d, width = %d, xref = %d, yref = %d, chwidth = %d\n",height, width, xref, yref, chwidth);
for ( j = 0; j < height; j++ ) {
for ( i = 0; i < width; i++ )
if ( checkbit(bptr, j * rwid * BYTE + i) )
putc('X', fp);
else putc('.', fp);
putc('\n', fp);
} /* End for */
} /* End of dump_glyph */
/*****************************************************************************/