/* pk.c */
/************************************************************************
Part of the dvipng distribution
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program. If not, see
.
Copyright (C) 2002-2009, 2019 Jan-Åke Larsson
************************************************************************/
#include "dvipng.h"
#define PK_POST 245
#define PK_PRE 247
#define PK_ID 89
unsigned char dyn_f;
int repeatcount;
int poshalf;
static unsigned char getnyb(unsigned char** pos)
{
if (poshalf == 0) {
poshalf=1;
return(**pos / 16);
} else {
poshalf=0;
return(*(*pos)++ & 15);
}
}
static uint32_t pk_packed_num(unsigned char** pos)
{
register int i;
uint32_t j;
i = (int)getnyb(pos);
if (i == 0) {
do {
j = (uint32_t)getnyb(pos);
i++;
} while (j == 0);
while (i > 0) {
j = j * 16 + (uint32_t)getnyb(pos);
i--;
};
return (j - 15 + (13 - dyn_f) * 16 + dyn_f);
} else if (i <= (int)dyn_f) {
return ((uint32_t)i);
} else if (i < 14) {
return ((i-(uint32_t)dyn_f - 1) * 16 + (uint32_t)getnyb(pos)
+ dyn_f + 1);
} else {
if (i == 14) {
repeatcount = (int)pk_packed_num(pos);
} else {
repeatcount = 1;
}
return (pk_packed_num(pos)); /* tail end recursion !! */
}
}
static unsigned char* skip_specials(unsigned char* pos, unsigned char* end)
{
uint32_t i;
while (pos < end && *pos >= 240 && *pos != PK_POST) {
i=0;
switch (*pos++) {
case 243:
i = *pos++;
case 242:
if (pos >= end) break;
i = 256 * i + *pos++;
case 241:
if (pos >= end) break;
i = 256 * i + *pos++;
case 240:
if (pos >= end) break;
i = 256 * i + *pos++;
DEBUG_PRINT(DEBUG_PK,("\n PK SPECIAL\t'%.*s' ",(int)i,pos));
pos += i;
break;
case 244:
#ifdef DEBUG
{
uint32_t c;
c=UNumRead(pos,4);
DEBUG_PRINT(DEBUG_PK,("\n PK SPECIAL\t%d",c));
}
#endif
pos += 4;
break;
case 245:
break;
case 246:
DEBUG_PRINT(DEBUG_PK,("\n PK\tNOP "));
break;
case 247: case 248: case 249: case 250:
case 251: case 252: case 253: case 254:
case 255:
Fatal("unexpected PK flagbyte %d", (int)*pos);
}
}
return(pos);
}
void LoadPK(int32_t c, register struct char_entry * ptr)
{
unsigned short shrunk_width,shrunk_height;
unsigned short width,height;
short xoffset,yoffset;
unsigned short i_offset,j_offset;
int i,j,k,n;
int count=0;
bool paint_switch;
unsigned char *pos,*buffer;
DEBUG_PRINT(DEBUG_PK,("\n LOAD PK CHAR\t%d",c));
pos=ptr->pkdata;
if ((ptr->flag_byte & 7) == 7) n=4;
else if ((ptr->flag_byte & 4) == 4) n=2;
else n=1;
dyn_f = ptr->flag_byte / 16;
paint_switch = ((ptr->flag_byte & 8) != 0);
/*
* Read character preamble
*/
if (n != 4) {
ptr->tfmw = UNumRead(pos, 3);
/* +n: vertical escapement not used */
pos+=3+n;
} else {
ptr->tfmw = UNumRead(pos, 4);
/* +4: horizontal escapement not used */
/* +n: vertical escapement not used */
pos+=8+n;
}
DEBUG_PRINT(DEBUG_PK,(" %d",ptr->tfmw));
ptr->tfmw = (dviunits)
((int64_t) ptr->tfmw * currentfont->s / 0x100000 );
DEBUG_PRINT(DEBUG_PK,(" (%d)",ptr->tfmw));
width = UNumRead(pos, n);
height = UNumRead(pos+=n, n);
DEBUG_PRINT(DEBUG_PK,(" %dx%d",width,height));
if (width > 0x7fff || height > 0x7fff)
Fatal("character %d too large in file %s", c, currentfont->name);
/*
* Hotspot issues: Shrinking to the topleft corner rather than the
hotspot will displace glyphs a fraction of a pixel. We deal with
this in as follows: The glyph is shrunk to its hotspot by
offsetting the bitmap somewhat to put the hotspot in the lower
left corner of a "shrink square". Shrinking to the topleft corner
will then act as shrinking to the hotspot. This may enlarge the
bitmap somewhat, of course. (Also remember that the below
calculation of i/j_offset is in integer arithmetics.)
There will still be a displacement from rounding the dvi
position, but vertically it will be equal for all glyphs on a
line, so we displace a whole line vertically by fractions of a
pixel. This is acceptible, IMHO. Sometime there will be support
for subpixel positioning, horizontally. Will do for now, I
suppose.
*/
xoffset = SNumRead(pos+=n, n);
i_offset = ( shrinkfactor - xoffset % shrinkfactor ) % shrinkfactor;
width += i_offset;
ptr->xOffset = xoffset+i_offset;
yoffset = SNumRead(pos+=n, n);
j_offset = ( shrinkfactor - (yoffset-(shrinkfactor-1)) % shrinkfactor )
% shrinkfactor;
height += j_offset;
ptr->yOffset = yoffset+j_offset;
DEBUG_PRINT(DEBUG_PK,(" (%dx%d)",width,height));
/*
Extra marginal so that we do not crop the image when shrinking.
*/
shrunk_width = (width + shrinkfactor - 1) / shrinkfactor;
shrunk_height = (height + shrinkfactor - 1) / shrinkfactor;
ptr->w = shrunk_width;
ptr->h = shrunk_height;
pos+=n;
if ((buffer = calloc(shrunk_width*shrunk_height*
shrinkfactor*shrinkfactor,sizeof(char)))==NULL)
Fatal("cannot allocate space for pk buffer");
DEBUG_PRINT(DEBUG_GLYPH,("\nDRAW GLYPH %d\n", (int)c));
/* Raster char */
if (dyn_f == 14) { /* get raster by bits */
int bitweight = 0;
for (j = j_offset; j < (int) height; j++) { /* get all rows */
for (i = i_offset; i < (int) width; i++) { /* get one row */
bitweight /= 2;
if (bitweight == 0) {
count = *pos++;
bitweight = 128;
}
if (count & bitweight) {
buffer[i+j*width]=1;
#ifdef DEBUG
DEBUG_PRINT(DEBUG_GLYPH,("+"));
} else {
DEBUG_PRINT(DEBUG_GLYPH,(" "));
#endif
}
}
DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
}
} else { /* get packed raster */
poshalf=0;
repeatcount = 0;
for(i=i_offset, j=j_offset; j 0) {
if (i+count < width) {
if (paint_switch)
for(k=0;k0; repeatcount--,j++) {
for (i = i_offset; i0) {
DEBUG_PRINT(DEBUG_GLYPH,("="));
} else {
DEBUG_PRINT(DEBUG_GLYPH,(" "));
}
#endif
}
DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
}
i=i_offset;
}
}
paint_switch = 1 - paint_switch;
}
if (i>i_offset)
Fatal("wrong number of bits stored: char. %c, font %s",
(char)c, currentfont->name);
if (j>height)
Fatal("bad PK file %s, too many bits", currentfont->name);
}
/*
Shrink raster while doing antialiasing. (See above. The
single-glyph output seems better than what xdvi at 300 dpi,
shrinkfactor 3 produces.)
*/
if ((ptr->data = calloc(shrunk_width*shrunk_height,sizeof(char))) == NULL)
Fatal("unable to malloc image space for char %c", (char)c);
for (j = 0; j < (int) height; j++) {
for (i = 0; i < (int) width; i++) {
/* if (((i % shrinkfactor) == 0) && ((j % shrinkfactor) == 0))
ptr->data[i/shrinkfactor+j/shrinkfactor*shrunk_width] =
buffer[i+j*width];
else */
ptr->data[i/shrinkfactor+j/shrinkfactor*shrunk_width] +=
buffer[i+j*width];
}
}
for (j = 0; j < shrunk_height; j++) {
for (i = 0; i < shrunk_width; i++) {
ptr->data[i+j*shrunk_width] = ptr->data[i+j*shrunk_width]
*255/shrinkfactor/shrinkfactor;
DEBUG_PRINT(DEBUG_GLYPH,("%3u ",ptr->data[i+j*shrunk_width]));
}
DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
}
free(buffer);
}
void InitPK(struct font_entry * tfontp)
{
unsigned char* position, *end;
struct char_entry *tcharptr; /* temporary char_entry pointer */
uint32_t hppp, vppp, packet_length;
uint32_t c;
DEBUG_PRINT((DEBUG_DVI|DEBUG_PK),("\n OPEN FONT:\t'%s'", tfontp->name));
Message(BE_VERBOSE,"<%s>", tfontp->name);
if (MmapFile(tfontp->name,&(tfontp->fmmap)))
Fatal("font file %s unusable", tfontp->name);
position=(unsigned char*)tfontp->fmmap.data;
if (tfontp->fmmap.size < 3 || tfontp->fmmap.size < 3+*(position+2)+16)
Fatal("PK file %s ends prematurely",tfontp->name);
if (*position++ != PK_PRE)
Fatal("unknown font format in file %s",tfontp->name);
if (*position++ != PK_ID)
Fatal( "wrong version %d of PK file %s (should be 89)",
(int)*(position-1),tfontp->name);
DEBUG_PRINT(DEBUG_PK,("\n PK_PRE:\t'%.*s'",(int)*position, position+1));
position += *position + 1;
tfontp->designsize = UNumRead(position, 4);
DEBUG_PRINT(DEBUG_PK,(" %d", tfontp->designsize));
tfontp->type = FONT_TYPE_PK;
c = UNumRead(position+4, 4);
DEBUG_PRINT(DEBUG_PK,(" %d", c));
CheckChecksum (tfontp->c, c, tfontp->name);
hppp = UNumRead(position+8, 4);
vppp = UNumRead(position+12, 4);
DEBUG_PRINT(DEBUG_PK,(" %d %d", hppp,vppp));
if (hppp != vppp)
Warning("aspect ratio is %d:%d (should be 1:1)", hppp, vppp);
tfontp->magnification = (uint32_t)((uint64_t)hppp * 7227 * 5 / 65536l + 50)/100;
position+=16;
/* Read char definitions */
end=(unsigned char *) tfontp->fmmap.data+tfontp->fmmap.size;
position = skip_specials(position,end);
while (position < end && *position != PK_POST) {
DEBUG_PRINT(DEBUG_PK,("\n @%ld PK CHAR:\t%d",
(long)((char *)position - tfontp->fmmap.data), *position));
if ((tcharptr = malloc(sizeof(struct char_entry))) == NULL)
Fatal("cannot allocate space for char_entry");
tcharptr->flag_byte = *position;
tcharptr->data = NULL;
tcharptr->tfmw = 0;
if ((*position & 7) == 7) {
if (position >= end - 9) Fatal("PK file %s ends prematurely",tfontp->name);
packet_length = UNumRead(position+1,4);
c = UNumRead(position+5, 4);
position += 9;
} else if (*position & 4) {
if (position >= end - 4) Fatal("PK file %s ends prematurely",tfontp->name);
packet_length = (*position & 3) * 65536l + UNumRead(position+1, 2);
c = UNumRead(position+3, 1);
position += 4;
} else {
if (position >= end - 3) Fatal("PK file %s ends prematurely",tfontp->name);
packet_length = (*position & 3) * 256 + UNumRead(position+1, 1);
c = UNumRead(position+2, 1);
position += 3;
}
DEBUG_PRINT(DEBUG_PK,(" %d %d",packet_length,c));
if (c > (LASTFNTCHAR))
Fatal("PK font %s exceeds char numbering limit",tfontp->name);
tcharptr->length = packet_length;
tcharptr->pkdata = position;
tfontp->chr[c]=tcharptr;
position += packet_length;
position = skip_specials(position, end);
}
if (position >= end) Fatal("PK file %s ends prematurely",tfontp->name);
}
static void UnLoadPK(struct char_entry *ptr)
{
if (ptr->data!=NULL)
free(ptr->data);
ptr->data=NULL;
}
void DonePK(struct font_entry *tfontp)
{
int c=FIRSTFNTCHAR;
UnMmapFile(&(tfontp->fmmap));
while(c<=LASTFNTCHAR) {
if (tfontp->chr[c]!=NULL) {
UnLoadPK((struct char_entry*)tfontp->chr[c]);
free(tfontp->chr[c]);
}
c++;
}
if (tfontp->name!=NULL)
free(tfontp->name);
tfontp->name=NULL;
}