Back to index

tetex-bin  3.0
pk.c
Go to the documentation of this file.
00001 /* pk.c */
00002 
00003 /************************************************************************
00004 
00005   Part of the dvipng distribution
00006 
00007   This program is free software; you can redistribute it and/or modify
00008   it under the terms of the GNU General Public License as published by
00009   the Free Software Foundation; either version 2 of the License, or
00010   (at your option) any later version.
00011 
00012   This program is distributed in the hope that it will be useful, but
00013   WITHOUT ANY WARRANTY; without even the implied warranty of
00014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015   General Public License for more details.
00016 
00017   You should have received a copy of the GNU General Public License
00018   along with this program; if not, write to the Free Software
00019   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00020   02111-1307, USA.
00021 
00022   Copyright (C) 2002-2005 Jan-Åke Larsson
00023 
00024 ************************************************************************/
00025 
00026 #include "dvipng.h"
00027 #if HAVE_ALLOCA_H
00028 # include <alloca.h>
00029 #endif
00030 
00031 #define PK_POST 245
00032 #define PK_PRE 247
00033 #define PK_ID 89
00034 
00035 unsigned char   dyn_f;
00036 int             repeatcount;
00037 int             poshalf;
00038 
00039 unsigned char getnyb(unsigned char** pos)
00040 {
00041   if (poshalf == 0) {
00042     poshalf=1;
00043     return(**pos / 16);
00044   } else {
00045     poshalf=0;
00046     return(*(*pos)++ & 15);
00047   }
00048 }
00049 
00050 uint32_t pk_packed_num(unsigned char** pos)
00051 {
00052   register int    i;
00053   uint32_t        j;
00054 
00055   i = (int)getnyb(pos);
00056   if (i == 0) {
00057     do {
00058       j = (uint32_t)getnyb(pos);
00059       i++;
00060     } while (j == 0);
00061     while (i > 0) {
00062       j = j * 16 + (uint32_t)getnyb(pos);
00063       i--;
00064     };
00065     return (j - 15 + (13 - dyn_f) * 16 + dyn_f);
00066   } else if (i <= (int)dyn_f) {
00067     return ((uint32_t)i);
00068   } else if (i < 14) {
00069     return ((i-(uint32_t)dyn_f - 1) * 16 + (uint32_t)getnyb(pos) 
00070            + dyn_f + 1);
00071   } else {
00072     if (i == 14) {
00073       repeatcount = (int)pk_packed_num(pos);
00074     } else {
00075       repeatcount = 1;
00076     }
00077     return (pk_packed_num(pos));    /* tail end recursion !! */
00078   }
00079 }
00080 
00081 unsigned char* skip_specials(unsigned char* pos)
00082 {
00083   uint32_t    i;
00084 
00085   while (*pos >= 240 && *pos != PK_POST) {
00086     i=0;
00087     switch (*pos++) {
00088     case 243:
00089       i = *pos++;
00090     case 242: 
00091       i = 256 * i + *pos++;
00092     case 241:
00093       i = 256 * i + *pos++;
00094     case 240:
00095       i = 256 * i + *pos++;
00096       DEBUG_PRINT(DEBUG_PK,("\n  PK SPECIAL\t'%.*s' ",(int)i,pos));
00097       pos += i;
00098       break;
00099     case 244: 
00100 #ifdef DEBUG
00101       { 
00102        uint32_t c;
00103        c=UNumRead(pos,4);
00104        DEBUG_PRINT(DEBUG_PK,("\n  PK SPECIAL\t%d",c));
00105       }
00106 #endif
00107       pos += 4;
00108       break;
00109     case 245: 
00110       break;
00111     case 246:
00112       DEBUG_PRINT(DEBUG_PK,("\n  PK\tNOP "));
00113       break;
00114     case 247: case 248: case 249: case 250:
00115     case 251: case 252: case 253: case 254:
00116     case 255: 
00117       Fatal("Unexpected flagbyte %d!\n", (int)*pos);
00118     }
00119   }
00120   return(pos);
00121 }
00122 
00123 
00124 void LoadPK(int32_t c, register struct char_entry * ptr)
00125 {
00126   unsigned short   shrunk_width,shrunk_height;
00127   unsigned short   width,height;
00128   short   xoffset,yoffset;
00129   unsigned short   i_offset,j_offset;
00130   int   i,j,k,n;
00131   int   count=0;
00132   bool  paint_switch;
00133   unsigned char *pos,*buffer;
00134 
00135   DEBUG_PRINT(DEBUG_PK,("\n  LOAD PK CHAR\t%d",c));
00136   pos=ptr->pkdata;
00137   if ((ptr->flag_byte & 7) == 7) n=4;
00138   else if ((ptr->flag_byte & 4) == 4) n=2;
00139   else n=1;
00140   dyn_f = ptr->flag_byte / 16;
00141   paint_switch = ((ptr->flag_byte & 8) != 0);
00142   /*
00143    *  Read character preamble
00144    */
00145   if (n != 4) {
00146     ptr->tfmw = UNumRead(pos, 3);
00147     /* +n:   vertical escapement not used */
00148     pos+=3+n;
00149   } else {
00150     ptr->tfmw = UNumRead(pos, 4);
00151     /* +4:  horizontal escapement not used */
00152     /* +n:   vertical escapement not used */
00153     pos+=8+n;
00154   }
00155   DEBUG_PRINT(DEBUG_PK,(" %d",ptr->tfmw));
00156   ptr->tfmw = (dviunits)
00157     ((int64_t) ptr->tfmw * currentfont->s / 0x100000 );
00158   DEBUG_PRINT(DEBUG_PK,(" (%d)",ptr->tfmw));
00159   
00160   width   = UNumRead(pos, n);
00161   height  = UNumRead(pos+=n, n);
00162   DEBUG_PRINT(DEBUG_PK,(" %dx%d",width,height));
00163 
00164   if (width > 0x7fff || height > 0x7fff)
00165     Fatal("Character %d too large in file %s", c, currentfont->name);
00166 
00167   /* 
00168    * Hotspot issues: Shrinking to the topleft corner rather than the
00169      hotspot will displace glyphs a fraction of a pixel. We deal with
00170      this in as follows: The glyph is shrunk to its hotspot by
00171      offsetting the bitmap somewhat to put the hotspot in the lower
00172      left corner of a "shrink square". Shrinking to the topleft corner
00173      will then act as shrinking to the hotspot. This may enlarge the
00174      bitmap somewhat, of course.  (Also remember that the below
00175      calculation of i/j_offset is in integer arithmetics.)
00176      
00177      There will still be a displacement from rounding the dvi
00178      position, but vertically it will be equal for all glyphs on a
00179      line, so we displace a whole line vertically by fractions of a
00180      pixel. This is acceptible, IMHO. Sometime there will be support
00181      for subpixel positioning, horizontally. Will do for now, I
00182      suppose.
00183    */
00184   xoffset = SNumRead(pos+=n, n);
00185   i_offset = ( shrinkfactor - xoffset % shrinkfactor ) % shrinkfactor;
00186   width += i_offset;
00187   ptr->xOffset = xoffset+i_offset;
00188 
00189   yoffset = SNumRead(pos+=n, n);
00190   j_offset = ( shrinkfactor - (yoffset-(shrinkfactor-1)) % shrinkfactor )
00191     % shrinkfactor;
00192   height += j_offset;
00193   ptr->yOffset = yoffset+j_offset;
00194 
00195   DEBUG_PRINT(DEBUG_PK,(" (%dx%d)",width,height));
00196   /* 
00197      Extra marginal so that we do not crop the image when shrinking.
00198   */
00199   shrunk_width = (width + shrinkfactor - 1) / shrinkfactor;
00200   shrunk_height = (height + shrinkfactor - 1) / shrinkfactor;
00201   ptr->w = shrunk_width;
00202   ptr->h = shrunk_height;
00203   pos+=n;
00204   buffer = alloca(shrunk_width*shrunk_height*
00205                 shrinkfactor*shrinkfactor*sizeof(char));
00206   (void)memset(buffer,0,shrunk_width*shrunk_height*
00207               shrinkfactor*shrinkfactor*sizeof(char));
00208   DEBUG_PRINT(DEBUG_GLYPH,("\nDRAW GLYPH %d\n", (int)c));
00209   /*
00210     Raster char
00211   */
00212   if (dyn_f == 14) { /* get raster by bits */
00213     int bitweight = 0;
00214     for (j = j_offset; j < (int) height; j++) {  /* get all rows */
00215       for (i = i_offset; i < (int) width; i++) {    /* get one row */
00216        bitweight /= 2;
00217        if (bitweight == 0) {
00218          count = *pos++;
00219          bitweight = 128;
00220        }
00221        if (count & bitweight) {
00222          buffer[i+j*width]=1;
00223 #ifdef DEBUG
00224          DEBUG_PRINT(DEBUG_GLYPH,("+"));
00225        } else {
00226          DEBUG_PRINT(DEBUG_GLYPH,(" "));
00227 #endif
00228        }
00229       }
00230       DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
00231     }
00232   } else {           /* get packed raster */
00233     poshalf=0;
00234     repeatcount = 0;
00235     for(i=i_offset, j=j_offset; j<height; ) {
00236       count = pk_packed_num(&pos);
00237       while (count > 0) {
00238        if (i+count < width) {
00239          if (paint_switch) 
00240            for(k=0;k<count;k++) {
00241              buffer[k+i+j*width]=1;
00242              DEBUG_PRINT(DEBUG_GLYPH,("*"));
00243            }
00244 #ifdef DEBUG
00245          else for(k=0;k<count;k++) 
00246            DEBUG_PRINT(DEBUG_GLYPH,(" "));
00247 #endif
00248          i += count;
00249          count = 0;
00250        } else {
00251          if (paint_switch) 
00252            for(k=i;k<width;k++) {
00253              buffer[k+j*width]=1;
00254              DEBUG_PRINT(DEBUG_GLYPH,("#"));
00255            }
00256 #ifdef DEBUG
00257          else for(k=i;k<width;k++) 
00258            DEBUG_PRINT(DEBUG_GLYPH,(" "));
00259 #endif
00260          DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
00261          j++;
00262          count -= width-i;
00263          /* Repeat row(s) */
00264          for (;repeatcount>0; repeatcount--,j++) {
00265            for (i = i_offset; i<width; i++) {
00266              buffer[i+j*width]=buffer[i+(j-1)*width];
00267 #ifdef DEBUG
00268              if (buffer[i+j*width]>0) {
00269               DEBUG_PRINT(DEBUG_GLYPH,("="));
00270              } else {
00271               DEBUG_PRINT(DEBUG_GLYPH,(" "));
00272              }
00273 #endif
00274            }
00275            DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
00276          }
00277          i=i_offset;
00278        }
00279       }
00280       paint_switch = 1 - paint_switch;
00281     }
00282     if (i>i_offset)
00283       Fatal("Wrong number of bits stored:  char. <%c>(%d), font %s, Dyn: %d", 
00284            (char)c, (int)c, currentfont->name,dyn_f);
00285     if (j>height)
00286       Fatal("Bad pk file (%s), too many bits", currentfont->name);
00287   }
00288   /*
00289     Shrink raster while doing antialiasing. (See above. The
00290     single-glyph output seems better than what xdvi at 300 dpi,
00291     shrinkfactor 3 produces.)
00292   */
00293   if ((ptr->data = calloc(shrunk_width*shrunk_height,sizeof(char))) == NULL)
00294     Fatal("Unable to allocate image space for char <%c>\n", (char)c);
00295   for (j = 0; j < (int) height; j++) {    
00296     for (i = 0; i < (int) width; i++) {    
00297       /* if (((i % shrinkfactor) == 0) && ((j % shrinkfactor) == 0))
00298         ptr->data[i/shrinkfactor+j/shrinkfactor*shrunk_width] =
00299         buffer[i+j*width];
00300         else */
00301       ptr->data[i/shrinkfactor+j/shrinkfactor*shrunk_width] +=
00302        buffer[i+j*width];
00303     }
00304   }    
00305   for (j = 0; j < shrunk_height; j++) {   
00306     for (i = 0; i < shrunk_width; i++) {    
00307       ptr->data[i+j*shrunk_width] = ptr->data[i+j*shrunk_width]
00308        *255/shrinkfactor/shrinkfactor;
00309       DEBUG_PRINT(DEBUG_GLYPH,("%3u ",ptr->data[i+j*shrunk_width]));
00310     }
00311     DEBUG_PRINT(DEBUG_GLYPH,("|\n"));
00312   }     
00313 }
00314 
00315 void InitPK(struct font_entry * tfontp)
00316 {
00317   unsigned char* position;
00318   struct char_entry *tcharptr; /* temporary char_entry pointer  */
00319   uint32_t    hppp, vppp, packet_length;
00320   uint32_t    c;
00321 
00322   DEBUG_PRINT((DEBUG_DVI|DEBUG_PK),("\n  OPEN FONT:\t'%s'", tfontp->name));
00323   Message(BE_VERBOSE,"<%s>", tfontp->name);
00324   if (MmapFile(tfontp->name,&(tfontp->fmmap)))
00325     Fatal("font file %s unusable", tfontp->name);
00326   position=(unsigned char*)tfontp->fmmap.mmap;
00327   if (tfontp->fmmap.size < 2 || tfontp->fmmap.size < 3+*(position+2)+16) 
00328     Fatal("PK file %s ends prematurely",tfontp->name);
00329   if (*position++ != PK_PRE) 
00330     Fatal("unknown font format in file <%s> !\n",currentfont->name);
00331   if (*position++ != PK_ID) 
00332     Fatal( "wrong version of pk file!  (%d should be 89)\n",
00333           (int)*(position-1));
00334   DEBUG_PRINT(DEBUG_PK,("\n  PK_PRE:\t'%.*s'",(int)*position, position+1));
00335   position += *position + 1;
00336 
00337   tfontp->designsize = UNumRead(position, 4);
00338   DEBUG_PRINT(DEBUG_PK,(" %d", tfontp->designsize));
00339   tfontp->type = FONT_TYPE_PK;
00340   
00341   c = UNumRead(position+4, 4);
00342   DEBUG_PRINT(DEBUG_PK,(" %d", c));
00343   CheckChecksum (tfontp->c, c, tfontp->name);
00344 
00345   hppp = UNumRead(position+8, 4);
00346   vppp = UNumRead(position+12, 4);
00347   DEBUG_PRINT(DEBUG_PK,(" %d %d", hppp,vppp));
00348   if (hppp != vppp)
00349     Warning("aspect ratio is %d:%d (should be 1:1)!", hppp, vppp);
00350   tfontp->magnification = (uint32_t)((uint64_t)hppp * 7227 * 5 / 65536l + 50)/100;
00351   position+=16;
00352   /* Read char definitions */
00353   position = skip_specials(position);
00354   while (*position != PK_POST) {
00355     DEBUG_PRINT(DEBUG_PK,("\n  @%ld PK CHAR:\t%d",
00356                        (long)position - (long)tfontp->fmmap.mmap, *position));
00357     if ((tcharptr = malloc(sizeof(struct char_entry))) == NULL)
00358       Fatal("can't malloc space for char_entry");
00359     tcharptr->flag_byte = *position;
00360     tcharptr->data = NULL;
00361     tcharptr->tfmw = 0;
00362     if ((*position & 7) == 7) {
00363       packet_length = UNumRead(position+1,4);
00364       c = UNumRead(position+5, 4);
00365       position += 9;
00366     } else if (*position & 4) {
00367       packet_length = (*position & 3) * 65536l +
00368        UNumRead(position+1, 2);
00369       c = UNumRead(position+3, 1);
00370       position += 4;
00371     } else {
00372       packet_length = (*position & 3) * 256 +
00373        UNumRead(position+1, 1);
00374       c = UNumRead(position+2, 1);
00375       position += 3;
00376     }
00377   DEBUG_PRINT(DEBUG_PK,(" %d %d",packet_length,c));
00378   if (c > (LASTFNTCHAR))
00379     Fatal("Bad character (%d) in PK-File\n",(int)c);
00380   tcharptr->length = packet_length;
00381   tcharptr->pkdata = position;
00382   tfontp->chr[c]=tcharptr;
00383   position += packet_length;
00384   position = skip_specials(position);
00385   }
00386 }
00387 
00388 void UnLoadPK(struct char_entry *ptr)
00389 {
00390   if (ptr->data!=NULL)
00391     free(ptr->data);
00392   ptr->data=NULL;
00393 }
00394 
00395 void DonePK(struct font_entry *tfontp)
00396 {
00397   int c=FIRSTFNTCHAR;
00398 
00399   UnMmapFile(&(tfontp->fmmap));
00400   while(c<=LASTFNTCHAR) {
00401     if (tfontp->chr[c]!=NULL) {
00402       UnLoadPK((struct char_entry*)tfontp->chr[c]);
00403       free(tfontp->chr[c]);
00404     }
00405     c++;
00406   }
00407   tfontp->name[0]='\0';
00408 }