#ifndef lint static char *rcs = "$Header: pk.c,v 1.1 88/01/15 13:04:52 simpson Rel $"; #endif /* $Log: pk.c,v $ * Revision 1.1 88/01/15 13:04:52 simpson * initial release * * Revision 0.1 87/12/11 18:31:09 simpson * beta test * */ #include #include #include #include "constants.h" #include "fontinfo.h" #include "pk.h" /* Routines pertaining to PK files. */ static int BitNumber; /* Count of bits read in a byte */ static int Byte; /* Byte currently reading bits or nybbles from */ static int RepeatCount; /* Count found during unpacking of packed number */ /* Resets the bit count so bit and nybble reading start on a byte boundary. */ static void resetbitcount() { BitNumber = 0; } /* Returns the next nybble from the file. This routine uses the cgetc() * function so an appropriate error routine will need to be set. */ static int getnyb(f) FILE *f; { if (BitNumber == 0) { Byte = cgetc(f); BitNumber = 8; } if (BitNumber == 8) { BitNumber -= 4; return Byte >> 4 & 0xF; } else { BitNumber -= 4; return Byte & 0xF; } } /* Returns true if the next bit is on, false otherwise. This routine uses the * cgetc() function so an appropriate error routine will need to be set. */ static Boolean getbit(f) FILE *f; { if (BitNumber == 0) { Byte = cgetc(f); BitNumber = 8; } return Byte & 1 << --BitNumber ? TRUE : FALSE; } /* Returns the next packed number from the file f. The global variable * RepeatCount is set appropriately if a repeat count is found. This routine * uses the cgetc() function so an appropriate error routine will need to be * set. */ static int pkpackednum(f, dynf) register FILE *f; register int dynf; { register int i, j; i = getnyb(f); if (i == 0) { do { j = getnyb(f); i++; } while (j == 0); while (i-- > 0) j = (j << 4) + getnyb(f); return j - 15 + ((13 - dynf) << 4) + dynf; } else if (i <= dynf) return i; else if (i < 14) return ((i - dynf - 1) << 4) + getnyb(f) + dynf + 1; else { if (i == 14) RepeatCount = pkpackednum(f, dynf); else RepeatCount = 1; return pkpackednum(f, dynf); } /* NOTREACHED */ } /* Returns information about a PK file or NULL if it couldn't open the file * or the file is an invalid PK file. The information returned is static and * is overwritten with each call. Downloadtype should be 'X' or 'Y' (see the * QMS Programming Manual). This function uses the cgetc() routine so an * appropriate error recovery function should be set. */ struct FontInfo *getfontinfo(filename, downloadtype) char *filename; int downloadtype; { unsigned long uinteger(), li, lk; long integer(), pl, cc; static struct FontInfo fi; FILE *f; int wordsneeded = 0, i, j, command, flag; int heightstorage[256][2]; /* 0=h,1=voff f/each char*/ int largestvoff; /* Largest voff of all chars */ if (!(f = fopen(filename, "r"))) return NULL; if (cgetc(f) != PKPRE || cgetc(f) != PKID) { (void)fclose(f); return NULL; } bzero((char *)&fi, sizeof(struct FontInfo)); bzero((char *)heightstorage, sizeof(heightstorage)); for (i = 0, j = cgetc(f); i < j; i++) fi.comment[i] = cgetc(f); fi.comment[i] = '\0'; fi.ds = integer(f, 4); fi.cs = integer(f, 4); fi.hppp = integer(f, 4); fi.vppp = integer(f, 4); fi.numchars = fi.qmsheight = largestvoff = 0; while ((command = cgetc(f)) != PKPOST) if (0 <= command && command <= 239) { flag = command; if ((flag & 0x7) == 0x7) { /* Long format preamble */ pl = integer(f, 4); cc = integer(f, 4); fi.chararray[cc].tfm = integer(f, 4); fi.chararray[cc].dx = integer(f, 4); fi.chararray[cc].dy = integer(f, 4); fi.chararray[cc].w = integer(f, 4); fi.chararray[cc].h = integer(f, 4); fi.chararray[cc].hoff = integer(f, 4); fi.chararray[cc].voff = integer(f, 4); (void)fseek(f, pl - 28, 1); } else if (0 <= (flag & 0x7) && (flag & 0x7) <= 3) { /* Short */ pl = cgetc(f) | (flag & 0x3) << 8; cc = cgetc(f); fi.chararray[cc].tfm = uinteger(f, 3); fi.chararray[cc].dx = fi.chararray[cc].dy = cgetc(f) * SPOINT; fi.chararray[cc].w = cgetc(f); fi.chararray[cc].h = cgetc(f); fi.chararray[cc].hoff = integer(f, 1); fi.chararray[cc].voff = integer(f, 1); (void)fseek(f, pl - 8, 1); } else { /* Extended short form */ pl = uinteger(f, 2) | (flag & 0x3) << 16; cc = cgetc(f); fi.chararray[cc].tfm = uinteger(f, 3); fi.chararray[cc].dx = fi.chararray[cc].dy = uinteger(f, 2) * SPOINT; fi.chararray[cc].w = uinteger(f, 2); fi.chararray[cc].h = uinteger(f, 2); fi.chararray[cc].hoff = integer(f, 2); fi.chararray[cc].voff = integer(f, 2); (void)fseek(f, pl - 13, 1); } assert(0 <= cc && cc <=255); fi.numchars++; fi.qmsheight = MAX(fi.qmsheight, fi.chararray[cc].h); largestvoff = MAX(largestvoff, fi.chararray[cc].voff); heightstorage[cc][0] = fi.chararray[cc].h; heightstorage[cc][1] = fi.chararray[cc].voff; if (downloadtype == 'Y') wordsneeded += (int)((fi.chararray[cc].h + 15.0) / 16.0) * fi.chararray[cc].w; else wordsneeded += (int)((fi.chararray[cc].w + 15.0) / 16.0) * fi.chararray[cc].h; } else switch (command) { case PKXXX1: case PKXXX2: case PKXXX3: case PKXXX4: for (li = 0, lk = uinteger(f, command - PKXXX1 + 1); li < lk; li++) (void)cgetc(f); break; case PKYYY: (void)uinteger(f, 4); break; case PKNOOP: break; default: (void)fclose(f); return NULL; } wordsneeded += 800.0 / 2.0 + (fi.numchars * (14.0 + 12.0)) / 2.0; fi.blocksize = CEILING((wordsneeded * 2.0) / 1024.0); fi.qmsbaseline = largestvoff; for (li = 0; li < 256; li++) fi.qmsheight = MAX(fi.qmsheight, largestvoff + (heightstorage[li][0] - heightstorage[li][1])); (void)fclose(f); return &fi; } /* This routine takes a PK font name and extracts the information * contained in its naming convention. PK fonts are named like * .pk. The first * parameter is the font name. This is the only input parameter. The * following output parameters are the name of the font (including the * optional design size), the name of the font (without the design size), * the design size and the magnification. For example, parameter * cmr10.200pk would return the values * fontname "cmr10" * shortfontname "cmr" * designsize 10 * magnification 200 * The function returns TRUE if it was successful parsing the name, FALSE * otherwise. The designsize returned will be -1 if it is left out. */ Boolean extractinfo(fullfontname, fontname, shortfontname, designsize, magnification) char *fullfontname; char *fontname; char *shortfontname; int *designsize; int *magnification; { char *p, *strcpy(); if (sscanf(fullfontname, "%[^0123456789.]", shortfontname) != 1) return FALSE; p = &fullfontname[strlen(shortfontname)]; if ('0' <= *p && *p <= '9') { if (sscanf(p, "%d", designsize) != 1) return FALSE; while ('0' <= *p && *p <= '9') p++; } else *designsize = -1; if (*designsize != -1) (void)sprintf(fontname, "%s%d", shortfontname, *designsize); else (void)strcpy(fontname, shortfontname); if (*p != '.') return FALSE; else p++; if (sscanf(p, "%d", magnification) != 1) return FALSE; while ('0' <= *p && *p <= '9') p++; if (!EQ(p, "pk")) return FALSE; return TRUE; } static FILE *PkFilePtr; /* File pointer used in resetpkfile() * and getnextcharinfo() */ static struct CharInfo CInfo; /* Structure returned to the user */ /* Resets the getnextcharinfo() function so it reopens the PK file */ void resetpkfile() { if (PkFilePtr) { (void)fclose(PkFilePtr), PkFilePtr = NULL; if (CInfo.bitmap) free(CInfo.bitmap), CInfo.bitmap = NULL; } } /* This function gets information on the next character in a font. If the * font file is not already opened, it will open the font file. The * downloadtype should be 'X' or 'Y'. The routine allocates and deallocates * memory for the character bitmap itself, so you needn't worry about it. * The filename parameter is only consulted when the routine is first called * to get the information on the first character. If you wish to read from a * new PK file, you must use the resetpkfile() function. The routine returns * 0 on success, 1 if there are no more characters, 2 if the PK file could not * be opened, 3 if the file is not a PK file and 4 if the PK version is * incorrect. This routine calls the cgetc() function so the error routine * should be set appropriately. */ int getnextcharinfo(filename, downloadtype, charinfo) char *filename; int downloadtype; struct CharInfo **charinfo; { unsigned long uinteger(); char *malloc(); int command, flag, dynf, packednum; long pl, li, lj, integer(); register Boolean black; register char *bitmap, *rowmap, *putbyte; register int i, j, k, currentw, currenth, mask; register long w, h; if (!PkFilePtr) { if (!(PkFilePtr = fopen(filename, "r"))) return 2; if (cgetc(PkFilePtr) != PKPRE) { (void)fclose(PkFilePtr), PkFilePtr = NULL; return 3; } if (cgetc(PkFilePtr) != PKID) { (void)fclose(PkFilePtr), PkFilePtr = NULL; return 4; } for (i = 0, j = cgetc(PkFilePtr); i < j; i++) /* Comment */ (void)cgetc(PkFilePtr); (void)integer(PkFilePtr, 4); /* Design size */ (void)integer(PkFilePtr, 4); /* Checksum */ (void)integer(PkFilePtr, 4); /* hppp */ (void)integer(PkFilePtr, 4); /* vppp */ } else if (CInfo.bitmap) free(CInfo.bitmap), CInfo.bitmap = NULL; tryagain: if ((command = cgetc(PkFilePtr)) == PKPOST) { /* No more chars */ (void)fseek(PkFilePtr, -1L, 1); /* Back up by 1 byte */ return 1; } if (0 <= command && command <= 239) { flag = command; dynf = (flag & DYNF) >> 4; if ((flag & 0x7) == 0x7) { /* Long format preamble */ CInfo.preambletype = plong; pl = integer(PkFilePtr, 4); CInfo.cc = integer(PkFilePtr, 4); CInfo.tfm = integer(PkFilePtr, 4); CInfo.dxordm = integer(PkFilePtr, 4); CInfo.dy = integer(PkFilePtr, 4); CInfo.w = integer(PkFilePtr, 4); CInfo.h = integer(PkFilePtr, 4); CInfo.hoff = integer(PkFilePtr, 4); CInfo.voff = integer(PkFilePtr, 4); if (CInfo.h == 0 || CInfo.w == 0) (void)fseek(PkFilePtr, pl - 28L, 1); } else if (0 <= (flag & 0x7) && (flag & 0x7) <= 3) { /* Short */ CInfo.preambletype = pshort; pl = cgetc(PkFilePtr) | (flag & 0x3) << 8; CInfo.cc = cgetc(PkFilePtr); CInfo.tfm = uinteger(PkFilePtr, 3); CInfo.dxordm = cgetc(PkFilePtr); CInfo.w = cgetc(PkFilePtr); CInfo.h = cgetc(PkFilePtr); CInfo.hoff = integer(PkFilePtr, 1); /* integer() returns */ CInfo.voff = integer(PkFilePtr, 1); /* signed values */ if (CInfo.h == 0 || CInfo.w == 0) (void)fseek(PkFilePtr, pl - 8L, 1); } else { /* Extended short form */ CInfo.preambletype = pexshort; pl = uinteger(PkFilePtr, 2) | (flag & 0x3) << 16; CInfo.cc = cgetc(PkFilePtr); CInfo.tfm = uinteger(PkFilePtr, 3); CInfo.dxordm = uinteger(PkFilePtr, 2); CInfo.w = uinteger(PkFilePtr, 2); CInfo.h = uinteger(PkFilePtr, 2); CInfo.hoff = integer(PkFilePtr, 2); CInfo.voff = integer(PkFilePtr, 2); if (CInfo.h == 0 || CInfo.w == 0) (void)fseek(PkFilePtr, pl - 13L, 1); } assert(pl > 0); assert(CInfo.h >= 0 && CInfo.w >= 0); assert(0 <= CInfo.cc && CInfo.cc <= 255); *charinfo = &CInfo; h = CInfo.h, w = CInfo.w; if (h == 0 || w == 0) return 0; if (downloadtype == 'X') { /* Shifting right 3 divides by 8 faster than division */ bitmap = CInfo.bitmap = malloc((unsigned)((w+7>>3)*h)); if (dynf == 14) { bzero(bitmap, (int)((w+7>>3)*h)); for (i = 0; i < h; i++) for (j = 0; j < w; j++) if (getbit(PkFilePtr)) *(bitmap + i*(w+7>>3) + (j>>3)) |= 1<<7-j%8; } else { /* Get normally packed raster */ black = flag & BRUNCOUNT; rowmap = malloc((unsigned)(w+7>>3)); bzero(rowmap,(int)(w+7>>3)); currenth = currentw = RepeatCount = 0; while (currenth < h && currentw < w) { packednum = pkpackednum(PkFilePtr, dynf); for (i = 0; i < packednum; i++) { if (black) *(rowmap + (currentw>>3)) |= 1<<7-currentw%8; if (++currentw == w) { for (j = 0; j < RepeatCount + 1; j++) bcopy(rowmap, bitmap + currenth++ * (w+7>>3), (w+7>>3)); bzero(rowmap, (int)(w+7>>3)); currentw = RepeatCount = 0; } } black = !black; } free(rowmap); } } else { /* Y orientation */ bitmap = CInfo.bitmap = malloc((unsigned)((h+7>>3)*w)); bzero(bitmap, (int)((h+7>>3)*w)); if (dynf == 14) { /* Get raster by bits */ for (i = 0; i < h; i++) for (j = 0; j < w; j++) if (getbit(PkFilePtr)) { putbyte = bitmap + j*(h+7>>3) + (h-(i+1)>>3); *putbyte |= 1<<7-((h+7)-i)%8; } } else { /* Get normally packed raster */ black = flag & BRUNCOUNT; rowmap = malloc((unsigned)(w+7>>3)); bzero(rowmap, (int)(w+7>>3)); currenth = currentw = RepeatCount = 0; while (currenth < h && currentw < w) { packednum = pkpackednum(PkFilePtr, dynf); for (i = 0; i < packednum; i++) { if (black) { *(rowmap + (currentw>>3)) |= 1<<7-currentw%8; putbyte = bitmap + currentw*(h+7>>3)+ (h-(currenth+1)>>3); *putbyte |= 1<<7-((h+7)-currenth)%8; } if (++currentw == w) { currenth++; for (j = 0; j < RepeatCount; j++) { for (k = 0; k < w; k++) { if (k % 8 == 0) mask = 0x80; else mask >>= 1; if (*(rowmap + (k>>3)) & mask) { putbyte = bitmap + k*(h+7>>3) + (h-(currenth+1)>>3); *putbyte |= 1<<7-((h+7)-currenth)%8; } } currenth++; } bzero(rowmap, (int)(w+7>>3)); currentw = RepeatCount = 0; } } black = !black; } free(rowmap); } /* End get normally packed raster */ } /* End Y orientation */ resetbitcount(); } else /* End of if (0 <= command && command <= 239) { */ switch (command) { case PKXXX1: case PKXXX2: case PKXXX3: case PKXXX4: for (li = 0, lj = uinteger(PkFilePtr, command - PKXXX1 + 1); li < lj; li++) (void)cgetc(PkFilePtr); goto tryagain; case PKYYY: (void)uinteger(PkFilePtr, 4); goto tryagain; default: #ifndef lint assert(FALSE); #else ; #endif } return 0; }