home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume20
/
hp2pk
/
part03
/
writepk.c
< prev
Wrap
C/C++ Source or Header
|
1991-06-02
|
19KB
|
707 lines
/* WRITEPK.C - Write data to a TeX PK font file
***************************************************************************
*
* void init_pk_file(fp, comment, dsize, density, pkp)
* FILE *fp;
* char *comment;
* fixnum dsize;
* int density;
* struct pk_preamble *pkp;
*
* void write_pk_character(fp, pkc, bmp)
* FILE *fp;
* struct pk_chardata *pkc;
* struct character_bitmap *bmp;
*
* void end_pk_file(fp)
* FILE *fp;
*
***************************************************************************
*
* Source of information for TeX PK font files:
*
* GFtoPK.WEB by Tomas Rokicki
*
***************************************************************************
* EDIT HISTORY
* 19-Oct-90 SRMc - split out from HP2PK.C to improve modularity
* 14-Jan-91 SRMc - add TURBO C patches from Thomas B. Ridgeway
* (ridgeway@blackbox.hacc.washington.edu)
***************************************************************************
* Copyright 1990, 1991 by the Summer Institute of Linguistics, Inc.
* All rights reserved.
*/
/*#define TURBO_C*/ /* uncomment if using MSDOS TURBO C */
#include <stdio.h>
#ifdef BSD
extern int memcmp();
extern char *memset(), *memcpy();
#include <strings.h>
#else
#ifdef TURBO_C
#include <mem.h>
#define MSDOS
#else
#ifndef THINK_C /* THINK C includes memxxx() functions in <string.h> */
#include <memory.h>
#endif
#endif
#include <string.h>
#endif
#include "pk.h"
#include "bitmap.h"
#define END_OF_BITMAP 2 /* used in write_pk_character() */
/************************************************************************/
/* EXTERN DECLARATIONS */
/************************************************************************/
extern void exit();
/* from MYALLOC.C */
extern char *myalloc(), *myrealloc();
extern void myfree();
/* from BIGENDIO.C */
extern void put_halfword(), put_3_bytes(), put_fullword();
/************************************************************************/
/* STATIC GLOBAL VARIABLES */
/************************************************************************/
/*
* powers of two (used in various computations)
*/
static unsigned long power[32] = {
0x00000001L, 0x00000002L, 0x00000004L, 0x00000008L,
0x00000010L, 0x00000020L, 0x00000040L, 0x00000080L,
0x00000100L, 0x00000200L, 0x00000400L, 0x00000800L,
0x00001000L, 0x00002000L, 0x00004000L, 0x00008000L,
0x00010000L, 0x00020000L, 0x00040000L, 0x00080000L,
0x00100000L, 0x00200000L, 0x00400000L, 0x00800000L,
0x01000000L, 0x02000000L, 0x04000000L, 0x08000000L,
0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L
};
/*
* globals for pk_nyb() output
*/
static short bit_weight; /* either 1 or 16 */
static unsigned char output_byte; /* current stored value */
/****************************************************************************
* NAME
* init_pk_file
* ARGUMENTS
* fp - output PK FILE pointer
* comment - comment string for PK file
* dsize - font design size (fixed-point, 20-bit fraction)
* density - dots per inch of the font bitmap
* pkp - pointer to PK file preamble structure
* DESCRIPTION
* Fill in and write the header information for a .PK file.
* RETURN VALUE
* none
*/
void init_pk_file(fp, comment, dsize, density, pkp)
FILE *fp;
char *comment;
fixnum dsize;
int density;
struct pk_preamble *pkp;
{
int i;
double d;
/*
* fill in the preamble
*/
pkp->preamble = PK_PRE;
pkp->ident = PK_ID;
pkp->comment_size = strlen(comment);
memcpy( pkp->comment, comment, pkp->comment_size );
pkp->designsize = dsize;
pkp->checksum = 0L;
d = (density / 72.27) * 65536.0;
pkp->hppp = d;
pkp->vppp = pkp->hppp; /* assumes square pixels */
/*
* write the preamble
*/
putc(pkp->preamble, fp);
putc(pkp->ident, fp);
putc(pkp->comment_size, fp);
for ( i = 0 ; i < pkp->comment_size ; ++i ) /* one byte at a time */
putc( pkp->comment[i], fp); /* ensures proper byte order */
put_fullword(pkp->designsize, fp);
put_fullword(pkp->checksum, fp);
put_fullword(pkp->hppp, fp);
put_fullword(pkp->vppp, fp);
}
/****************************************************************************
* MACRO NAME
* ADD_BIT_COUNT
* ARGUMENTS
* x - value to store in the bit_counts array
* DESCRIPTION
* Add a value to the bit_counts array, increasing the array size if
* necessary.
*/
#define ADD_BIT_COUNT(x) \
if (current_bit_count == last_bit_count)\
{ \
bit_counts = (int *)myrealloc((char *)bit_counts,\
(unsigned)((max_bit_counts_size+(pkc->pixel_height)) * sizeof(int)));\
current_bit_count = &bit_counts[max_bit_counts_size];\
max_bit_counts_size += (pkc->pixel_height);\
last_bit_count = &bit_counts[max_bit_counts_size];\
} \
*current_bit_count++ = (x)
/****************************************************************************
* NAME
* static pk_nyb
* ARGUMENTS
* a - 4-bit "nybble" for output
* fp - output PK FILE pointer
* DESCRIPTION
* Write a 4-bit "nybble" to the PK file, storing intermediate values in
* output_byte and using bit_weight as a flag.
* RETURN VALUE
* none
*/
static void pk_nyb(a,fp)
unsigned char a;
FILE *fp;
{
if (bit_weight == 16)
{
output_byte = (a << 4) & 0xF0;
bit_weight = 1;
}
else
{
putc( (output_byte + (a & 0xF)) & 0xFF, fp);;
bit_weight = 16;
}
}
/****************************************************************************
* NAME
* write_pk_character
* ARGUMENTS
* fp - output PK FILE pointer
* pkc - pointer to PK character data structure
* bmp - pointer to character bitmap data
* DESCRIPTION
* Write one character's info to the PK file.
* This function converts a raw bitmap into the dense format used by PK
* files. (algorithms courtesy of PXtoPK and GFtoPK)
* RETURN VALUE
* none
*/
void write_pk_character(fp, pkc, bmp)
FILE *fp;
struct pk_chardata *pkc;
struct character_bitmap *bmp;
{
/* (this is largely translated borrowed code from GFtoPK, which is why
* there aren't too many comments at times */
long best_packet_length;
int i, j, k;
int count;
int bit, oldbit;
long curr_word = 0L;
int bit_ptr;
int bit_mod_32 = 0;
long *prast;
long *end_raster;
int dyn_f;
long deriv[13+1]; /* array[1..13] of integer */
int first_on;
int state;
int on;
int h_bit;
int p_bit;
int r_on, s_on;
int r_count, s_count;
int max_2;
long pred_pk_loc;
int buff;
static long *zero_row = NULL;
static long *ones_row = NULL;
static int max_row_size = 0;
static int *row_repeats = NULL;
static int max_column_size = 0;
int *cur_repeat;
int repeat_flag;
static int *bit_counts = NULL;
static int max_bit_counts_size = 0;
int *current_bit_count, *first_bit_count, *last_bit_count;
int *r_bit_count, *s_bit_count;
/*
* set up an all-zeros row and an all-ones row
*/
if (bmp->raster_width > max_row_size)
{
max_row_size = bmp->raster_width;
if (zero_row)
myfree(zero_row);
zero_row = (long *)myalloc( (unsigned)(4 * max_row_size) );
memset( zero_row, 0x00, 4 * max_row_size );
if (ones_row)
myfree(ones_row);
ones_row = (long *)myalloc( (unsigned)(4 * max_row_size) );
}
memset( ones_row, 0xFF, 4 * bmp->raster_width );
i = pkc->pixel_width % 32;
if (i == 1)
ones_row[bmp->raster_width - 1] = power[31]; /* MAXLONGINT */
else if (i > 1)
ones_row[bmp->raster_width - 1] = -power[32-i];
/*
* set markers for runs of identical rows of bits
*/
if (((pkc->pixel_height)+1) > max_column_size)
{
if (row_repeats)
myfree(row_repeats);
max_column_size = (pkc->pixel_height) + 1;
row_repeats = (int *)myalloc( (unsigned)(max_column_size * sizeof(int)) );
}
for ( prast = bmp->raster, i = 0 ;
i < (pkc->pixel_height)-1 ;
++i, prast += bmp->raster_width )
{
if (memcmp( prast, zero_row, bmp->raster_width * 4) == 0)
row_repeats[i] = 0; /* skip all-zero rows */
else if (memcmp( prast, ones_row, bmp->raster_width * 4) == 0)
row_repeats[i] = 0; /* skip all-one rows */
else if (memcmp(prast, prast+bmp->raster_width, bmp->raster_width * 4)==0)
row_repeats[i] = 1; /* mark this equal to next row */
else
row_repeats[i] = 0; /* not equal to next row... */
}
row_repeats[(pkc->pixel_height)-1] = 0; /* last row can't equal "next" */
/*
* convert boolean markers of repeated rows into repeat counts
*/
for ( i = 0 ; i < (pkc->pixel_height) ; i = k + 1 )
{
k = i;
while ((row_repeats[k] == 1) && (k < (pkc->pixel_height)))
++k;
row_repeats[i] = k - i;
}
row_repeats[(pkc->pixel_height)] = 0; /* to simplify bookkeepping */
/*
* create the bit counts for each row
*/
if (((pkc->pixel_height) * 5) > max_bit_counts_size)
{
if (bit_counts)
myfree(bit_counts);
max_bit_counts_size = (pkc->pixel_height) * 5;
bit_counts = (int *)myalloc((unsigned)(max_bit_counts_size * sizeof(int)));
}
last_bit_count = &bit_counts[max_bit_counts_size];
prast = bmp->raster;
cur_repeat = row_repeats;
current_bit_count = bit_counts;
repeat_flag = 0;
end_raster = &(bmp->raster[(pkc->pixel_height) * bmp->raster_width]);
count = 0;
for ( bit_ptr = pkc->pixel_width, oldbit = 0 ; oldbit != END_OF_BITMAP ; ++bit_ptr )
{
if (bit_ptr == pkc->pixel_width)
{
/*
* time to try the next row of this bitmap
*/
if (*cur_repeat > 0)
{
/*
* skip over rows that are repeated
*/
repeat_flag = *cur_repeat;
cur_repeat += repeat_flag;
prast += bmp->raster_width * repeat_flag;
}
++cur_repeat;
bit_mod_32 = 0;
bit_ptr = 0;
}
--bit_mod_32;
if (bit_mod_32 == -1)
{
/*
* time to get the next longword of this row
*/
bit_mod_32 = 31;
curr_word = *prast++;
}
if (prast > end_raster)
bit = END_OF_BITMAP; /* at end of character bitmap */
else if (curr_word & power[bit_mod_32])
{
bit = 1;
curr_word ^= power[bit_mod_32];
}
else
bit = 0;
if (bit == oldbit)
++count;
else
{
ADD_BIT_COUNT( count );
count = 1;
oldbit = bit;
if (repeat_flag > 0)
{
ADD_BIT_COUNT( -repeat_flag );
repeat_flag = 0;
}
}
}
ADD_BIT_COUNT( 0 );
ADD_BIT_COUNT( 0 );
/*
* Quoting from GFTOPK.WEB, from which the following code was adapted:
*
* Here is another piece of rather intricate code. Here we determine the
* smallest size in which we can pack the data, calculating |dyn_f| in
* the process. To do this, we calculate the size required if |dyn_f| is
* 0, and put this in |pkc->packet_length|. Then, we calculate the changes in the
* size for each increment of |dyn_f|, and stick these values in the
* |deriv| array. Finally, we scan through this array, and find the
* final minimum value, which we then use to send the character data.
*/
for ( i = 0 ; i <= 13 ; ++i )
deriv[i] = 0;
first_on = (bit_counts[0] == 0);
if (first_on)
first_bit_count = &bit_counts[1];
else
first_bit_count = &bit_counts[0];
pkc->packet_length = 0L;
for ( current_bit_count = first_bit_count ; *current_bit_count != 0 ; )
{
/* Quoting again from GFTOPK.WEB, except for changes in variable names:
*
* When we enter this module, we have a count, at
* |*current_bit_count|. First, we add to the |pkc->packet_length| the
* number of nybbles that this count would require, assuming
* |dyn_f| to be zero. Since when |dyn_f| is zero, there are no
* one nybble counts, we simply check the two-nybble counts, and
* then the extensible counts.
*
* Next, we take the count value and determine the value of
* |dyn_f| (if any) that would cause this count to take either
* more or less nybbles. If a valid value for |dyn_f| exists in
* this range, we accumulate this change in the |deriv| array.
*
* We know that a repeat count of one will not change the length
* of the raster representation, no matter what |dyn_f| is,
* because it is always represented by the nybble 15, so we do
* that as a special case.
*/
j = *current_bit_count++;
if (j == -1)
++pkc->packet_length; /* we have a row repeat count of 1 */
else
{
if (j < 0)
{
++pkc->packet_length;
j = -j; /* get positive row count */
}
if (j < 209) /* 208 = 13 * 16 (13 is max dyn_f) */
pkc->packet_length += 2;
else
{
k = j - 193; /* 192 = 12 * 16 */
while (k >= 16)
{
k = k / 16;
pkc->packet_length += 2;
}
++pkc->packet_length;
}
if (j < 14)
--deriv[j];
else if (j < 209)
++deriv[(223-j)/15];
else
{
k = 16;
while ((k * 16) < (j + 3))
k = k * 16;
if ((j - k) <= 192)
deriv[(207-j+k)/15] += 2;
}
}
}
/*
* set best_packet_length to the best size for the given value of dyn_f
*/
best_packet_length = pkc->packet_length;
dyn_f = 0;
for ( i = 1 ; i <= 13 ; ++i )
{
pkc->packet_length += deriv[i];
if (pkc->packet_length <= best_packet_length)
{
best_packet_length = pkc->packet_length;
dyn_f = i;
}
}
pkc->packet_length = (best_packet_length + 1) / 2; /* convert from nybble to byte size */
if ( (pkc->packet_length > (((pkc->pixel_height) * pkc->pixel_width + 7) / 8)) ||
(((pkc->pixel_height) * pkc->pixel_width) == 0) )
{
/*
* raw bitmap is the best we can do -- no compression possible
*/
pkc->packet_length = ((pkc->pixel_height) * pkc->pixel_width + 7) / 8;
dyn_f = 14;
}
/*
* write character preamble
*/
pkc->flag_byte = dyn_f << 4;
if (first_on)
pkc->flag_byte |= 0x08;
if ( (pkc->tfm_width > 0xFFFFFFL) || (pkc->tfm_width < 0L) ||
(pkc->dx < 0L) || (pkc->packet_length > 196579L) ||
(pkc->pixel_width > 0xFFFFL) || (pkc->pixel_height > 0xFFFFL) ||
(pkc->hoff > 32767L) || (pkc->hoff < -32768L) ||
(pkc->voff > 32767L) || (pkc->voff < -32768L) )
{
pkc->packet_length += 28L;
pkc->flag_byte |= 0x07;
/*
* write long character preamble
*/
putc(pkc->flag_byte & 0xFF, fp);
put_fullword(pkc->packet_length, fp);
put_fullword(pkc->char_code, fp);
pred_pk_loc = ftell(fp) + pkc->packet_length;
put_fullword(pkc->tfm_width, fp);
put_fullword(pkc->dx, fp);
put_fullword(pkc->dy, fp);
put_fullword((long)pkc->pixel_width, fp);
put_fullword((long)(pkc->pixel_height), fp);
put_fullword(pkc->hoff, fp);
put_fullword(pkc->voff, fp);
}
else if ((pkc->dx > 0xFF0000L) || (pkc->packet_length > 1016) ||
(pkc->pixel_width > 255) || (pkc->pixel_height > 255) ||
(pkc->hoff > 127) || (pkc->hoff < -128) ||
(pkc->voff > 127) || (pkc->voff < -128) )
{
pkc->packet_length += 13L;
pkc->flag_byte |= (pkc->packet_length >> 16) | 4;
/*
* write two-byte short character preamble
*/
putc(pkc->flag_byte & 0xFF, fp);
put_halfword((unsigned short)(pkc->packet_length & 0xFFFF), fp);
putc((unsigned short)pkc->char_code & 0xFF, fp);
pred_pk_loc = ftell(fp) + pkc->packet_length;
put_3_bytes(pkc->tfm_width, fp);
put_halfword((unsigned short)(pkc->dx >> 16), fp);
put_halfword((unsigned short)pkc->pixel_width, fp);
put_halfword((unsigned short)(pkc->pixel_height), fp);
put_halfword((unsigned short)pkc->hoff, fp);
put_halfword((unsigned short)pkc->voff, fp);
}
else
{
pkc->packet_length += 8L;
pkc->flag_byte |= pkc->packet_length >> 8;
/*
* write one-byte short character preamble
*/
putc(pkc->flag_byte & 0xFF, fp);
putc((unsigned char)(pkc->packet_length & 0xFF), fp);
putc((unsigned char)pkc->char_code & 0xFF, fp);
pred_pk_loc = ftell(fp) + pkc->packet_length;
put_3_bytes(pkc->tfm_width, fp);
putc((unsigned char)(pkc->dx >> 16) & 0xFF, fp);
putc((unsigned char)pkc->pixel_width & 0xFF, fp);
putc((unsigned char)(pkc->pixel_height) & 0xFF, fp);
putc((unsigned char)pkc->hoff & 0xFF, fp);
putc((unsigned char)pkc->voff & 0xFF, fp);
}
if (dyn_f != 14)
{
/*
* send compressed format
*/
bit_weight = 16;
max_2 = 208 - 15 * dyn_f; /* set max value for two nybbles */
current_bit_count = first_bit_count;
while (*current_bit_count != 0)
{
j = *current_bit_count++;
if (j == -1)
pk_nyb(15,fp); /* send special row repeat count = 1 */
else
{
if (j < 0)
{
pk_nyb(14,fp); /* send row repeat flag */
j = -j; /* convert to positive count */
}
if (j <= dyn_f)
pk_nyb(j,fp); /* small, one-nybble value */
else if (j <= max_2)
{ /* two-nybble value */
j = j - dyn_f - 1;
pk_nyb(j / 16 + dyn_f + 1, fp);
pk_nyb(j % 16, fp);
}
else
{ /* multi-nybble value */
j = j - max_2 + 15;
k = 16;
while (k <= j)
{
k = k * 16;
pk_nyb(0,fp);
}
while (k > 1)
{
k = k / 16;
pk_nyb(j / k, fp);
j = j % k;
}
}
}
}
if (bit_weight != 16)
putc(output_byte & 0xFF, fp); /* pad to byte boundary */
}
else
{
/*
* send raw bit map
*/
buff = 0;
p_bit = 8;
current_bit_count = first_bit_count;
r_bit_count = s_bit_count = bit_counts;
h_bit = pkc->pixel_width;
on = !first_on;
state = r_on = s_on = 0;
count = r_count = s_count = 0;
repeat_flag = 0;
while ((*current_bit_count != 0) || state || (count > 0))
{
if (state)
{
count = r_count;
current_bit_count = r_bit_count;
on = r_on;
--repeat_flag;
}
else
{
r_count = count;
r_bit_count = current_bit_count;
r_on = on;
}
/*
* send one row by bits
*/
do {
if (count == 0)
{
if (*current_bit_count < 0)
{
if (!state)
repeat_flag = -(*current_bit_count);
++current_bit_count;
}
count = *current_bit_count++;
on = !on;
}
if ((count >= p_bit) && (p_bit < h_bit))
{
if (on)
buff = buff + power[p_bit] - 1;
putc(buff & 0xFF, fp);
buff = 0;
h_bit = h_bit - p_bit;
count = count - p_bit;
p_bit = 8;
}
else if ((count < p_bit) && (count < h_bit))
{
if (on)
buff = buff + power[p_bit] - power[p_bit-count];
p_bit = p_bit - count;
h_bit = h_bit - count;
count = 0;
}
else
{
if (on)
buff = buff + power[p_bit] - power[p_bit-h_bit];
count = count - h_bit;
p_bit = p_bit - h_bit;
h_bit = pkc->pixel_width;
if (p_bit == 0)
{
putc(buff & 0xFF, fp);
buff = 0;
p_bit = 8;
}
}
} while (h_bit != pkc->pixel_width);
if (state && (repeat_flag == 0))
{
count = s_count;
current_bit_count = s_bit_count;
on = s_on;
state = 0;
}
else if (!state && (repeat_flag > 0))
{
s_count = count;
s_bit_count = current_bit_count;
s_on = on;
state = 1;
}
}
if (p_bit != 8)
putc(buff & 0xFF, fp);
}
if (pred_pk_loc != ftell(fp))
{
fflush(stdout);
fprintf(stderr, "\nERROR: Bad predicted character length.\n");
exit(1);
}
}
/****************************************************************************
* NAME
* end_pk_file
* ARGUMENTS
* fp - output PK FILE pointer
* DESCRIPTION
* Write the postamble for the .PK file.
* RETURN VALUE
* none
*/
void end_pk_file(fp)
FILE *fp;
{
putc(PK_POST & 0xFF, fp);
while (ftell(fp) % 4 != 0)
putc(PK_NO_OP & 0xFF, fp);
}