/* * This is the font part of textool, dealing with PK format files. * The low level routines to deal with the PK format * were transliterated from pktopx * See the pktopx documentation for details. * For V2.3, 29 Mar 88 - * Fixed stupid errors in skip_specials() that caused specials to be missed */ #include "defs.h" #include #include /* for access() stuff */ #include /* for LoadAChar() */ char *index(); int strlen(); char *strcpy(); char *sprintf(); /* procedures in sunstuff */ extern void Fatal(); extern void Warning(); /* procedures in dvistuff */ extern float ActualFactor(); extern FILE *OpenFontFile(); /* procedures here */ void ReadCharDefs(); void LoadAChar(); /* a few global variables and data structures */ #include "globals.h" #define PK_XXX1 240 #define PK_XXX2 241 #define PK_XXX3 242 #define PK_XXX4 243 #define PK_YYY 244 #define PK_POST 245 #define PK_NOP 246 #define PK_PRE 247 #define PK_ID 89 #define ROWSIZE 64 /* maximum width of raster in 16 bit words */ static FILE *pkfp; static long pk_loc; /* points to next byte to be read in file */ static long pk_char_start; static int repeat_count; static int dyn_f; static int word_width; static int char_height; static int char_width; static Boolean got_pk_post; static Boolean turn_on; static int bit_weight; static int flag_byte; static int packet_length; static int char_code; static int tfm_width; static int hor_esc; static int vert_esc; static int h_off; static int v_off; static int nybindex; static short row[ROWSIZE]; static short *rowptr; static short *imageptr; static int power[16] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000}; static int gpower[17] = {0, 1, 3, 7, 15, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff}; #define get_pk_byte() (pk_loc++, getc(pkfp)) int get_pk_word() { int myword; myword = get_pk_byte() << 8; return (myword + get_pk_byte()); } int get_pk_double() { register int mydouble; mydouble = get_pk_byte() << 8; mydouble += get_pk_byte(); mydouble = (mydouble << 8) + get_pk_byte(); return ((mydouble << 8) + get_pk_byte()); } /* * This returns the next nybble from the pk file * The variable nybindex controls which nybble is * returned. This is initialised to 0 in normal_raster() * as a previous normal_raster() may have read an odd * number of nybbles. */ int get_pk_nybble() { static int mynybble; if (nybindex) { nybindex = 0; return (mynybble & 0xf); } else { mynybble = get_pk_byte(); nybindex = 1; return (mynybble >> 4); } } /* * This returns a non-zero value if the next pk file * bit is a one. It is only used in an if (..) so * the actual value is not important. * The variable bit_weight is used to count the bits * between calls to get_pk_byte(). It is initialised * to 0 in bitmap_raster() so a byte is fetched the * first time. * The function is given for illustration. The macro * following is used for speed. */ /* *int *get_pk_bit() *{ * static int byte_for_bits; * * if (bit_weight <= 1) { * bit_weight = 256; * byte_for_bits = get_pk_byte(); * } * bit_weight = bit_weight >> 1; * return (byte_for_bits & bit_weight); *} */ static int byte_for_bits; #define get_pk_bit() (((bit_weight<=1) ?\ (bit_weight = 128, byte_for_bits=get_pk_byte()) : (bit_weight>>=1)),\ byte_for_bits&bit_weight) void send_row() { register short *dp; register int i; register int repcount; dp = imageptr; repcount = repeat_count + 1; while (repcount--) { rowptr = row; i = word_width; while(i--) *dp++ = *rowptr++; } imageptr=dp; /* save for next call */ } void skip_specials() { register int i; do { i = 0; flag_byte = get_pk_byte(); if (flag_byte >= PK_XXX1) switch (flag_byte) { case PK_XXX4: i = get_pk_byte(); case PK_XXX3: i = (i<<8) + get_pk_byte(); case PK_XXX2: i = (i<<8) + get_pk_byte(); case PK_XXX1: i = (i<<8) + get_pk_byte(); while (i--) (void)get_pk_byte(); break; case PK_YYY: (void)get_pk_double(); break; case PK_POST: #ifdef DEBUG if (ExtraDebug) fprintf(stderr, "Got pk_post in skip_specials()\n"); #endif got_pk_post = TRUE; return; case PK_NOP: break; default: Fatal("Bad pk command byte (%d) in skip_specials()", flag_byte); return; } } while (flag_byte >= PK_XXX1); } /* * This routine returns the next run count from the pk * character description. It will set the global repeat_count * to any repeat count value encountered. */ int pk_packed_num() { register int i, j; i = get_pk_nybble(); if (i == 0) { do { j = get_pk_nybble(); i++; } while (j == 0); while (i--) j = (j<<4) + get_pk_nybble(); return (j - 15 + ((13 - dyn_f)<<4) + dyn_f); } else if (i <= dyn_f) return (i); else if (i < 14) return (((i - dyn_f - 1)<<4) + get_pk_nybble() + dyn_f + 1); else { if (i == 14) repeat_count = pk_packed_num(); else /* i must be 15 */ repeat_count = 1; return (pk_packed_num()); } } /* * This deals with run count encoded rasters. * It uses pk_packed_num() which in turn uses get_pk_nybble(). */ void normal_raster(dp) short *dp; { register int word = 0; register int count; register int h_bit = char_width; register int word_weight = 16; register int rows_left = char_height; repeat_count = 0; nybindex = 0; imageptr = dp; rowptr = row; while (rows_left > 0) { count = pk_packed_num(); while (count > 0) { if ((count < word_weight) && (count < h_bit)) { if (turn_on) word += gpower[word_weight] - gpower[word_weight - count]; h_bit -= count; word_weight -= count; count = 0; } else if ((count >= h_bit) && (h_bit <= word_weight)) { if (turn_on) word += gpower[word_weight] - gpower[word_weight - h_bit]; *rowptr++ = word; send_row(); rowptr = row; rows_left -= repeat_count + 1; repeat_count = 0; word = 0; word_weight = 16; count -= h_bit; h_bit = char_width; } else { if (turn_on) word += gpower[word_weight]; *rowptr++ = word; word = 0; count -= word_weight; h_bit -= word_weight; word_weight = 16; } } turn_on ^= 1; } if ((rows_left != 0) || (h_bit != char_width)) { Fatal("Bad pk file - more bits than required\n"); return; } } /* * This deals with bit mapped raster (dyn_f = 14). * It uses get_pk_bit(). */ void bitmap_raster(dp) short *dp; { register int i; register int j; register int word; register int word_weight; bit_weight = 0; for (i = 0; i < char_height; i++) { word = 0; word_weight = 15; for (j = 0; j < char_width; j++) { if (get_pk_bit()) word += power[word_weight]; if (word_weight-- == 0) { *dp++ = word; word = 0; word_weight = 15; } } if (word_weight < 15) *dp++ = word; } } /* * This reads the pk file preamble and rejects the file * if it does not start correctly. */ void get_pk_preamble() { register int comment_length; got_pk_post = FALSE; fseek(pkfp, 0L, 0); pk_loc = 0; if (get_pk_byte() != PK_PRE) { Fatal("Bad pk file - doesn't start with pk_pre\n"); return; } if (get_pk_byte() != PK_ID) { Fatal("Bad pk file - wrong pk_id byte\n"); return; } comment_length = get_pk_byte(); while (comment_length--) get_pk_byte(); (void)get_pk_double(); (void)get_pk_double(); (void)get_pk_double(); (void)get_pk_double(); } /* * This reads the preamble for one character up to the tfm width. * The tfm width is read as when it is stored in the char_entry * struct it needs to be multiplied by the font "s" parameter. * ReadCharDefs() is passed a pointer to the font_entry struct * so it can do this whereas LoadAChar() can't. * The rest of the char will either be skipped or read with * get_pk_char_dimen(). * flag_byte will have been set by a previous skip_specials(). */ void get_pk_char_code() { dyn_f = flag_byte >> 4; turn_on = (flag_byte & 0x8) >> 3; flag_byte &= 0x7; pk_char_start = pk_loc - 1; /* - 1 as flag byte has been read */ if (flag_byte < 4) { /* short preamble */ packet_length = (flag_byte << 8) + get_pk_byte(); char_code = get_pk_byte(); tfm_width = get_pk_byte() << 16; tfm_width += get_pk_word(); packet_length -= 3; /* correct for reading tfm_width */ } else if (flag_byte < 7) { /* extended short preamble */ packet_length = ((flag_byte - 4) << 16) + get_pk_word(); char_code = get_pk_byte(); tfm_width = get_pk_byte() << 16; tfm_width += get_pk_word(); packet_length -= 3; } else { /* long preamble */ packet_length = get_pk_double(); char_code = get_pk_double(); tfm_width = get_pk_double(); packet_length -= 4; } } /* * This reads the char preamble starting after the tfm width, * carrying on where get_pk_char_code() left off. */ void get_pk_char_dimen() { if (flag_byte < 4) { /* short preamble */ hor_esc = get_pk_byte(); char_width = get_pk_byte(); word_width = (char_width + 15)>>4; char_height = get_pk_byte(); h_off = (char)get_pk_byte(); v_off = (char)get_pk_byte(); } else if (flag_byte < 7) { /* extended short preamble */ hor_esc = get_pk_word(); char_width = get_pk_word(); word_width = (char_width + 15)>>4; char_height = get_pk_word(); h_off = (short)get_pk_word(); v_off = (short)get_pk_word(); } else { /* long preamble */ hor_esc = get_pk_double(); vert_esc = get_pk_double(); char_width = get_pk_double(); word_width = (char_width + 15)>>4; char_height = get_pk_double(); h_off = get_pk_double(); v_off = get_pk_double(); } } /* * This reads the preamble for one character. */ void get_pk_char_preamble() { flag_byte = get_pk_byte(); get_pk_char_code(); get_pk_char_dimen(); } /* * Now here are the routines called by the rest of textool */ /*-->FindFontFile*/ void FindFontFile(tfontptr, mag) struct font_entry *tfontptr; unsigned int mag; { char *direct, *tcp, *tcp1; int found; char curarea[STRSIZE]; tfontptr->font_mag = (int)( ActualFactor((int)(((float)tfontptr->s/(float)tfontptr->d)*1000.0+0.5)) #ifdef USEGLOBALMAG * ActualFactor(mag) #endif * (float)RESOLUTION + 0.5); #ifdef DEBUG if (ExtraDebug) { fprintf(stderr, "FindFontFile():\n"); fprintf(stderr, " mag = %d\n", mag); fprintf(stderr, " s = %d\n", tfontptr->s); fprintf(stderr, " d = %d\n", tfontptr->d); fprintf(stderr, " font_mag = %d\n", tfontptr->font_mag); } #endif if (tfontptr->a != 0) { sprintf(tfontptr->name, "%s.%dpk", tfontptr->n, tfontptr->font_mag); } else { direct = PKpath; found = FALSE; do { tcp = index(direct, ':'); if (tcp == NULL) tcp = strlen(direct) + direct; strncpy(curarea, direct, tcp-direct); tcp1 = curarea + (tcp - direct); *tcp1++ = '/'; *tcp1++ = '\0'; sprintf(tfontptr->name, "%s%s.%dpk", curarea, tfontptr->n, tfontptr->font_mag); found = (access(tfontptr->name, R_OK) == 0); if (*tcp) direct = tcp + 1; else direct = tcp; } while ( !found && *direct != '\0'); } #ifdef DEBUG if (ExtraDebug) fprintf(stderr, "FindFontFile() returns with name %s\n", tfontptr->name); #endif } /*-->LoadAChar*/ void LoadAChar(ptr) register struct char_entry *ptr; { register Pixrect *pr; register short *dp; pkfp = OpenFontFile(); if (ptr->where.address.fileOffset == 0) { ptr->where.address.pixrectptr = NULL; return; } fseek(pkfp, ptr->where.address.fileOffset, 0); get_pk_char_preamble(); ptr->width = char_width; ptr->height = char_height; ptr->xOffset= h_off; ptr->yOffset = v_off; if ((pr = mem_create(ptr->width, ptr->height, 1)) == NULL) { Fatal("Couldn't allocate pixrect"); return; } dp = ((struct mpr_data *)pr->pr_data)->md_image; if (dyn_f == 14) bitmap_raster(dp); else normal_raster(dp); ptr->where.address.pixrectptr = pr; ptr->where.isloaded = TRUE; } /*-->ReadCharDefs*/ void ReadCharDefs(tfontptr, fp) struct font_entry *tfontptr; FILE *fp; { register struct char_entry *tcharptr; /* temporary char_entry pointer */ register Pixrect *pr; register int junk_count; short *dp; #ifdef DEBUG if (ExtraDebug) fprintf(stderr, "ReadCharDefs() for font %s\n", tfontptr->name); #endif pkfp = fp; get_pk_preamble(); skip_specials(); while (!got_pk_post) { get_pk_char_code(); tcharptr = &(tfontptr->ch[char_code]); tcharptr->tfmw = tfm_width * (float)tfontptr->s / (float)(1<<20); if (BigPreLoad) { /* * Load the rest of the character info. */ get_pk_char_dimen(); tcharptr->width = char_width; tcharptr->height = char_height; tcharptr->xOffset= h_off; tcharptr->yOffset = v_off; if ((pr = mem_create(char_width, char_height, 1)) == NULL) { Fatal("Couldn't allocate pixrect"); return; } dp = ((struct mpr_data *)pr->pr_data)->md_image; if (dyn_f == 14) bitmap_raster(dp); else normal_raster(dp); tcharptr->where.address.pixrectptr = pr; tcharptr->where.isloaded = TRUE; } else { tcharptr->where.isloaded = FALSE; tcharptr->where.address.fileOffset = pk_char_start; /* * Otherwise skip the rest of the char. * The bytes are read rather than using fseek as the pk files * are small and the whole thing is likely to fit in one stdio * buffer so this will not involve any function calls. */ junk_count = packet_length; while (junk_count--) getc(pkfp); pk_loc += packet_length; } skip_specials(); } }