Back to index

tetex-bin  3.0
pkfont.c
Go to the documentation of this file.
00001 /*  $Header$
00002 
00003     This is dvipdfm, a DVI to PDF translator.
00004     Copyright (C) 1998, 1999 by Mark A. Wicks
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019     
00020     The author may be contacted via the e-mail address
00021 
00022        mwicks@kettering.edu
00023 */
00024 
00025 #include <stdio.h>
00026 #include "system.h"
00027 #include "mem.h"
00028 #include "error.h"
00029 #include "mfileio.h"
00030 #include "numbers.h"
00031 #include "pkfont.h"
00032 #include "pdfobj.h"
00033 #include "pdflimits.h"
00034 #include "tfm.h"
00035 #include "ctype.h"
00036 
00037 pdf_obj *pk_encoding_ref = NULL;
00038 static unsigned char verbose = 0, debug = 0;
00039 static unsigned font_dpi = 600;
00040 
00041 void pk_set_verbose(void)
00042 {
00043   if (verbose < 255) {
00044     verbose += 1;
00045   }
00046 }
00047 
00048 void pk_set_dpi (int dpi)
00049 {
00050   font_dpi = dpi;
00051 }
00052 
00053 
00054 static void make_pk_encoding_ref (void)
00055 {
00056   static char first = 1;
00057   if (first) {
00058     int i;
00059     pdf_obj *encoding, *differences;
00060     encoding = pdf_new_dict();
00061     pdf_add_dict (encoding, 
00062                 pdf_new_name ("Type"),
00063                 pdf_new_name ("Encoding"));
00064     differences = pdf_new_array();
00065     pdf_add_dict (encoding,
00066                 pdf_new_name ("Differences"),
00067                 differences);
00068     pdf_add_array (differences, pdf_new_number (0.0));
00069     for (i=0; i<256; i++) {
00070       sprintf (work_buffer, "x%x", i);
00071       pdf_add_array (differences, pdf_new_name(work_buffer));
00072     }
00073     pk_encoding_ref = pdf_ref_obj (encoding);
00074     pdf_release_obj (encoding);
00075   }
00076   first = 0;
00077   return;
00078 }
00079 
00080 static void release_pk_encoding_ref (void)
00081 {
00082   pdf_release_obj (pk_encoding_ref);
00083 }
00084 
00085 struct a_pk_font 
00086 {
00087   pdf_obj *direct, *indirect;
00088   char *tex_name, *pk_file_name;
00089   double ptsize;
00090   char *used_chars;
00091 } *pk_fonts;
00092 
00093 void init_pk_record (struct a_pk_font *p)
00094 {
00095   int i;
00096   p->used_chars = NEW (256, char);
00097   for (i=0; i<256; i++) {
00098     (p->used_chars)[i] = 0;
00099   }
00100   p->tex_name = NULL;
00101   p->pk_file_name = NULL;
00102 }
00103 
00104 
00105 int num_pk_fonts = 0, max_pk_fonts = 0;
00106 
00107 int pk_font (const char *tex_name, double ptsize, int tfm_font_id, char
00108             *res_name)
00109 {
00110   int i, j;
00111   int firstchar, lastchar;
00112   pdf_obj *tmp1;
00113   for (i=0; i<num_pk_fonts; i++) {
00114     if (!strcmp (tex_name, pk_fonts[i].tex_name) &&
00115        (ptsize == pk_fonts[i].ptsize))
00116       break;
00117   }
00118   if (i == num_pk_fonts) {
00119     char *pk_file_name;
00120     kpse_glyph_file_type kpse_file_info;
00121     if ((pk_file_name = kpse_find_glyph(tex_name,
00122                                    font_dpi*ptsize/tfm_get_design_size(tfm_font_id)+0.5,
00123                                    kpse_pk_format,
00124                                    &kpse_file_info))) {
00125       /* Make sure there is enough room in pk_fonts for this entry */
00126       if (num_pk_fonts >= max_pk_fonts) {
00127        max_pk_fonts += MAX_FONTS;
00128        pk_fonts = RENEW (pk_fonts, max_pk_fonts, struct a_pk_font);
00129       }
00130       init_pk_record (pk_fonts+i);
00131       pk_fonts[i].pk_file_name = NEW (strlen(pk_file_name)+1, char);
00132       strcpy (pk_fonts[i].pk_file_name, pk_file_name);
00133       pk_fonts[i].tex_name = NEW (strlen (tex_name)+1, char);
00134       strcpy (pk_fonts[i].tex_name, tex_name);
00135       pk_fonts[i].ptsize = ptsize;
00136       pk_fonts[i].direct = pdf_new_dict ();
00137       pdf_add_dict (pk_fonts[i].direct,
00138                   pdf_new_name ("Type"),
00139                   pdf_new_name ("Font"));
00140       pdf_add_dict (pk_fonts[i].direct,
00141                   pdf_new_name ("Name"),
00142                   pdf_new_name (res_name));
00143       pdf_add_dict (pk_fonts[i].direct,
00144                   pdf_new_name ("Subtype"),
00145                   pdf_new_name ("Type3"));
00146       firstchar = tfm_get_firstchar(tfm_font_id);
00147       pdf_add_dict (pk_fonts[i].direct,
00148                   pdf_new_name ("FirstChar"),
00149                   pdf_new_number (firstchar));
00150       lastchar = tfm_get_lastchar(tfm_font_id);
00151       pdf_add_dict (pk_fonts[i].direct,
00152                   pdf_new_name ("LastChar"),
00153                   pdf_new_number (lastchar));
00154       tmp1 = pdf_new_array ();
00155       for (j=firstchar; j<=lastchar; j++) {
00156        pdf_add_array (tmp1,
00157                      pdf_new_number(ROUND(tfm_get_width (tfm_font_id, j)*1000.0,0.01)));
00158       }
00159       pdf_add_dict (pk_fonts[i].direct,
00160                   pdf_new_name ("Widths"),
00161                   tmp1);
00162       {
00163        double max_height, max_depth, max_width;
00164        max_height = tfm_get_max_height (tfm_font_id)*1000.0;
00165        max_depth = tfm_get_max_depth (tfm_font_id)*1000.0;
00166        max_width = tfm_get_max_width (tfm_font_id)*1000.0;
00167        max_height = ROUND(max_height, 1.0);
00168        max_depth = ROUND(max_depth, 1.0);
00169        max_width = ROUND(max_width, 1.0);
00170        tmp1 = pdf_new_array();
00171        pdf_add_array (tmp1, pdf_new_number(-0.10*max_width));
00172        pdf_add_array (tmp1,
00173                      pdf_new_number(-max_depth-0.20*(max_height+max_depth)));
00174        pdf_add_array (tmp1, pdf_new_number(1.10*max_width));
00175        pdf_add_array (tmp1, pdf_new_number(max_height+0.10*(max_height+max_depth)));
00176        pdf_add_dict (pk_fonts[i].direct, 
00177                     pdf_new_name ("FontBBox"), tmp1);
00178       }
00179       {
00180        tmp1 = pdf_new_array();
00181        pdf_add_array (tmp1, pdf_new_number (0.001));
00182        pdf_add_array (tmp1, pdf_new_number (0.0));
00183        pdf_add_array (tmp1, pdf_new_number (0.0));
00184        pdf_add_array (tmp1, pdf_new_number (0.001));
00185        pdf_add_array (tmp1, pdf_new_number (0.0));
00186        pdf_add_array (tmp1, pdf_new_number (0.0));
00187        pdf_add_dict (pk_fonts[i].direct, pdf_new_name ("FontMatrix"),
00188                     tmp1);
00189       }
00190       if (pk_encoding_ref == NULL) {
00191        make_pk_encoding_ref();
00192       }
00193       pdf_add_dict (pk_fonts[i].direct,
00194                   pdf_new_name ("Encoding"),
00195                   pdf_link_obj (pk_encoding_ref));
00196       pk_fonts[i].indirect = pdf_ref_obj (pk_fonts[i].direct);
00197       num_pk_fonts += 1;
00198     } else { /* Found no glyph file */
00199       i = -1;
00200     }
00201   }
00202   return i;
00203 }
00204 
00205 pdf_obj *pk_font_resource (int pk_id)
00206 {
00207   if (pk_id <0 || pk_id >= num_pk_fonts) {
00208     fprintf (stderr, "pk_id = %d\n", pk_id);
00209     ERROR ("pk_font_resource:  Invalid pk_id\n");
00210   }
00211   return pdf_link_obj (pk_fonts[pk_id].indirect);
00212 }
00213 
00214 char *pk_font_used (int pk_id)
00215 {
00216   if (pk_id < 0 || pk_id >= num_pk_fonts) {
00217     fprintf (stderr, "pk_id = %d\n", pk_id);
00218     ERROR ("pk_font_used:  Invalid pk_id\n");
00219   }
00220   return pk_fonts[pk_id].used_chars;
00221 }
00222 
00223 
00224 static FILE *pk_file;
00225 
00226 static void do_skip(unsigned long length) 
00227 {
00228   unsigned long i;
00229   for (i=0; i<length; i++) 
00230     fgetc (pk_file);
00231   return;
00232 }
00233 
00234 static void add_raster_data (pdf_obj *glyph, long w, long h,
00235                           int dyn_f, int run_color,
00236                           unsigned char *pk_data,
00237                           unsigned char *eod)
00238 
00239      /* First define some macros to be used as "in-line" functions */
00240 #define advance_nybble() \
00241 { \
00242   if (partial_byte) { \
00243     partial_byte=0; \
00244     pk_data++; \
00245   } else \
00246     partial_byte=1; \
00247 }
00248 
00249 #define current_nybble() (partial_byte? (*pk_data%16): (*pk_data/16))
00250 #define set_bits(n) {\
00251   int i; \
00252   for (i=0; i<(n); i++) { \
00253     row[next_col] |= (128u>>next_bit++); \
00254     if (next_bit > 7) { \
00255       next_bit = 0; \
00256       next_col += 1; \
00257     } \
00258   } \
00259 }
00260 
00261 #define skip_bits(n) {\
00262   next_col += (n)/8; \
00263   next_bit += (n)%8; \
00264   if (next_bit > 7) { \
00265     next_bit -= 8; \
00266     next_col += 1; \
00267   } \
00268 }
00269 
00270 #define get_bit(p,n) (p[(n)/8] & (128u>>((n)%8)))
00271 
00272 {
00273   long i, w_bytes, repeat_count, run_count = 0;
00274   int partial_byte = 0;
00275   unsigned char *row;
00276   w_bytes = (w%8 == 0)? w/8: (w/8+1);
00277   row = NEW (w_bytes, unsigned char);
00278   /* Make sure we output "h" rows of data */
00279   if (dyn_f == 14) {
00280     for (i=0; i<h; i++) {
00281       int next_col = 0, next_bit = 0;
00282       int j;
00283       for (j=0; j<w_bytes; j++) row[j] = 0;
00284       for (j=0; j<w; j++) {
00285        if (get_bit(pk_data,i*w+j)) {
00286          skip_bits(1);
00287        }
00288        else {
00289          set_bits(1);
00290        }
00291       }
00292       pdf_add_stream (glyph, (char *) row, w_bytes);
00293     }
00294   } else
00295     for (i=0; i<h; i++) {
00296       int next_col = 0, next_bit = 0;
00297       int j, row_bits_left = w;
00298       /* Initialize row to all zeros */
00299       for (j=0; j<w_bytes; j++) row[j] = 0;
00300       repeat_count = 0;
00301       /* Fill any run left over from previous rows */
00302       if (run_count != 0) {
00303        int nbits;
00304        nbits = MIN (w, run_count);
00305        run_count -= nbits;
00306        switch (run_color) {
00307        case 1:  /* This is actually white ! */
00308          set_bits(nbits);
00309          break;
00310        case 0:
00311          skip_bits(nbits);
00312          break;
00313        }
00314        row_bits_left -= nbits;
00315       }
00316       /* Read nybbles until we have a full row */
00317       while (pk_data < eod && row_bits_left>0) {
00318        int com_nyb;
00319        long packed = 0;
00320        com_nyb = current_nybble();
00321        if (com_nyb == 15) {
00322          repeat_count = 1;
00323          advance_nybble();
00324          continue;
00325        }
00326        if (com_nyb == 14) {
00327          advance_nybble();
00328        }
00329        /* Get a packed number */
00330        {
00331          int nyb;
00332          nyb = current_nybble();
00333          /* Test for single nybble case */
00334          if (nyb > 0 && nyb <= dyn_f) {
00335            packed = nyb;
00336            advance_nybble();
00337          }
00338          if (nyb > dyn_f) {
00339            advance_nybble();
00340            packed = (nyb-dyn_f-1)*16+current_nybble()+dyn_f+1; 
00341            advance_nybble();
00342          }
00343          if (nyb == 0) {
00344            int nnybs = 1;
00345            while (current_nybble() == 0) {
00346              advance_nybble();
00347              nnybs += 1;
00348            }
00349            packed = 0;
00350            while (nnybs) {
00351              packed = packed*16 + current_nybble();
00352              advance_nybble();
00353              nnybs -= 1;
00354            }
00355            packed += (13-dyn_f)*16-15+dyn_f;
00356          }
00357        }
00358        if (com_nyb == 14) {
00359          repeat_count = packed;
00360          continue;
00361        }
00362        {
00363          int nbits;
00364          run_count = packed;    
00365          run_color = !run_color;
00366          nbits = MIN (row_bits_left, run_count);
00367          run_count -= nbits;
00368          row_bits_left -= nbits;
00369          switch (run_color) {
00370          case 1: 
00371            set_bits(nbits);
00372            break;
00373          case 0:
00374            skip_bits(nbits);
00375            break;
00376          }
00377        }
00378        continue;
00379       }
00380       pdf_add_stream (glyph, (char *) row, w_bytes);
00381       /* Duplicate the row "repeat_count" times */
00382       for (j=0; j<repeat_count; j++) {
00383        pdf_add_stream (glyph, (char *) row, w_bytes);
00384       }
00385       /* Skip repeat_count interations */
00386       i += repeat_count;
00387     }
00388   RELEASE (row);
00389 }
00390 
00391 static void do_preamble(void)
00392 {
00393   /* Check for id byte */
00394   if (fgetc (pk_file) == 89) {
00395     /* Skip comment */
00396     do_skip (get_unsigned_byte (pk_file));
00397     /* Skip other header info.  It's normally used for verifying this
00398        is the file wethink it is */
00399     do_skip (16);
00400   } else {
00401     ERROR ("embed_pk_font: PK ID byte is incorrect.  Are you sure this is a PK file?");
00402   }
00403   return;
00404 }
00405 
00406 #define SHORT_FORM 1
00407 #define MED_FORM 2
00408 #define LONG_FORM 3
00409 static void do_character (unsigned char flag, int pk_id, pdf_obj *char_procs)
00410 {
00411   int format;
00412   unsigned long packet_length = 0, code = 0;
00413   /* Last three bits of flag determine packet size in a complex way */
00414   if ((flag & 4) == 0) {
00415     format = SHORT_FORM;
00416   } else if ((flag & 7) == 7) {
00417     format = LONG_FORM;
00418   } else {
00419     format = MED_FORM;
00420   }
00421   if (debug) {
00422     fprintf (stderr, "packet format: %d\n", format);
00423   }
00424   switch (format) {
00425   case SHORT_FORM:
00426     packet_length = (flag & 3) * 256u + get_unsigned_byte (pk_file);
00427     code = get_unsigned_byte (pk_file);
00428     break;
00429   case MED_FORM:
00430     packet_length = (flag & 3) * 65536ul + get_unsigned_pair(pk_file);
00431     code = get_unsigned_byte (pk_file);
00432     break;
00433   case LONG_FORM:
00434     packet_length = get_unsigned_quad (pk_file);
00435     code = get_unsigned_quad (pk_file);
00436     if (code > 255)
00437       ERROR ("Unable to handle long characters in PK files");
00438     break;
00439   }
00440   if (debug) {
00441     fprintf (stderr, "\npk_do_character: code=%lu, packet_length=%lu\n",
00442             code, packet_length);
00443   }
00444   if ((pk_fonts[pk_id].used_chars)[code%256]) {
00445     int dyn_f;
00446     unsigned long tfm_width = 0;
00447     long dm=0, dx=0, dy=0, w=0, h=0;
00448     int hoff=0, voff=0;
00449     dyn_f = flag/16;
00450     switch (format) {
00451     case SHORT_FORM:
00452       tfm_width = get_unsigned_triple (pk_file);
00453       dm = get_unsigned_byte (pk_file);
00454       w = get_unsigned_byte (pk_file);
00455       h = get_unsigned_byte (pk_file);
00456       hoff = get_signed_byte (pk_file);
00457       voff = get_signed_byte (pk_file);
00458       packet_length -= 8;
00459       break;
00460     case MED_FORM:
00461       tfm_width = get_unsigned_triple (pk_file);
00462       dm = get_unsigned_pair (pk_file);
00463       w = get_unsigned_pair (pk_file);
00464       h = get_unsigned_pair (pk_file);
00465       hoff = get_signed_pair (pk_file);
00466       voff = get_signed_pair (pk_file);
00467       packet_length -= 13;
00468       break;
00469     case LONG_FORM:
00470       tfm_width = get_signed_quad (pk_file);
00471       dx = get_signed_quad (pk_file);
00472       dy = get_signed_quad (pk_file);
00473       w = get_signed_quad (pk_file);
00474       h = get_signed_quad (pk_file);
00475       hoff = get_signed_quad (pk_file);
00476       voff = get_signed_quad (pk_file);
00477       packet_length -= 28;
00478       break;
00479     }
00480     {
00481       pdf_obj *glyph;
00482       double char_width, llx, lly, urx, ury;
00483       double pix2charu;
00484       int len;
00485       pix2charu = 72000.0/((double) font_dpi)/pk_fonts[pk_id].ptsize;
00486       char_width = tfm_width / (double) (1<<20) * 1000.0;
00487       char_width = ROUND (char_width, 0.01);
00488       llx = -hoff*pix2charu;
00489       lly = (voff-h)*pix2charu;
00490       urx = (w-hoff)*pix2charu;
00491       ury = voff*pix2charu;
00492       glyph = pdf_new_stream(STREAM_COMPRESS);
00493       /* The following line is a "metric" for the PDF reader */
00494       sprintf (work_buffer, "%.2f %.2f %.2f %.2f %.2f %.2f d1",
00495               char_width, 0.0,
00496               llx, lly, urx, ury);
00497       len = strlen (work_buffer);
00498       pdf_add_stream (glyph, work_buffer, len);
00499       /* For now add an outline box for debugging purposes */
00500       /*      len = sprintf (work_buffer, " %.2f %.2f m %.2f %.2f l %.2f %.2f l %.2f %.2f l s",
00501                    llx, lly, urx, lly, urx, ury, llx, ury);
00502                    pdf_add_stream (glyph, work_buffer, len); */
00503       /* Scale and translate origin to lower left corner for raster data */
00504       sprintf (work_buffer, " q %.2f 0 0 %.2f %.2f %.2f cm",
00505               w*pix2charu, h*pix2charu, llx, lly);
00506       len = strlen (work_buffer);
00507       pdf_add_stream (glyph, work_buffer, len);
00508       if (w != 0 && h != 0 && packet_length != 0) {
00509        unsigned char *pk_data;
00510        long read_len;
00511        sprintf (work_buffer, "\nBI\n/W %ld\n/H %ld\n/IM true\n/BPC 1\n/I true\n", w, h);
00512         len = strlen (work_buffer);
00513        pdf_add_stream (glyph, work_buffer, len);
00514        sprintf (work_buffer, "ID ");
00515         len = strlen (work_buffer);
00516        pdf_add_stream (glyph, work_buffer, len);
00517        pk_data = NEW (packet_length, unsigned char);
00518        if ((read_len=fread (pk_data, 1, packet_length, pk_file))!=
00519            packet_length) {
00520          fprintf (stderr, "packet length should be %ld, but only %ld bytes were read\n",
00521                  packet_length, read_len);
00522          ERROR ("Error reading character packet from PK file\n");
00523        }
00524        add_raster_data (glyph, w, h, dyn_f, (flag&8)>>3, pk_data, pk_data+packet_length);
00525        RELEASE (pk_data);
00526        sprintf (work_buffer, "\nEI");
00527         len = strlen (work_buffer);
00528        pdf_add_stream (glyph, work_buffer, len);
00529       } /* Otherwise we embed an empty stream :-( */
00530       sprintf (work_buffer, "\nQ");
00531       len = strlen (work_buffer);
00532       pdf_add_stream (glyph, work_buffer, len);
00533       sprintf (work_buffer, "x%x", (int)code%256);
00534       pdf_add_dict (char_procs, pdf_new_name (work_buffer),
00535                   pdf_ref_obj (glyph));
00536       pdf_release_obj (glyph);
00537     }
00538   } else {
00539     if (debug)
00540       fprintf (stderr, "\npk_do_character: Skipping code=%ld, length=%ld\n", code,
00541               packet_length);
00542     do_skip (packet_length);
00543   }
00544 }
00545 
00546 #define PK_XXX1 240
00547 #define PK_XXX2 241
00548 #define PK_XXX3 242
00549 #define PK_XXX4 243
00550 #define PK_YYY 244
00551 #define PK_POST 245
00552 #define PK_NO_OP 246
00553 #define PK_PRE 247
00554 
00555 static void embed_pk_font (int pk_id)
00556 {
00557   if (verbose)
00558     fprintf (stderr, "(%s", pk_fonts[pk_id].pk_file_name);
00559   if ((pk_file = MFOPEN (pk_fonts[pk_id].pk_file_name,
00560                      FOPEN_RBIN_MODE))) {
00561     int pk_command_byte;
00562     pdf_obj *char_procs;
00563     char_procs = pdf_new_dict();
00564     while ((pk_command_byte = fgetc(pk_file)) >= 0 &&
00565           pk_command_byte != PK_POST) {
00566       if (pk_command_byte < 240) {
00567        do_character (pk_command_byte, pk_id, char_procs);
00568       } else  /* A command byte */
00569        switch (pk_command_byte) {
00570        case PK_NO_OP:
00571          break;
00572        case PK_XXX1:
00573          do_skip(get_unsigned_byte(pk_file));
00574          break;
00575        case PK_XXX2:
00576          do_skip(get_unsigned_pair(pk_file));
00577          break;
00578        case PK_XXX3:
00579          do_skip(get_unsigned_triple(pk_file));
00580          break;
00581        case PK_XXX4:
00582          do_skip(get_unsigned_quad(pk_file));
00583          break;
00584        case PK_YYY:
00585          do_skip(4);
00586          break;
00587        case PK_PRE:
00588          do_preamble();
00589          break;
00590        }
00591     }
00592     MFCLOSE (pk_file);
00593     if (verbose) 
00594       fprintf (stderr, ")\n");
00595     pdf_add_dict (pk_fonts[pk_id].direct,
00596                 pdf_new_name ("CharProcs"),
00597                 pdf_ref_obj(char_procs));
00598     pdf_release_obj (char_procs);
00599   } else {
00600     ERROR ("embed_pk_font: Failed to open PK file");
00601   }
00602 }
00603 
00604 void pk_close_all (void)
00605 {
00606   int i;
00607   if (pk_encoding_ref)
00608     pdf_release_obj (pk_encoding_ref);
00609   for (i=0; i<num_pk_fonts; i++) {
00610     embed_pk_font (i);
00611     pdf_release_obj (pk_fonts[i].direct);
00612     pdf_release_obj (pk_fonts[i].indirect);
00613     RELEASE (pk_fonts[i].tex_name);
00614     RELEASE (pk_fonts[i].pk_file_name);
00615     RELEASE (pk_fonts[i].used_chars);
00616   }
00617   if (pk_fonts)
00618     RELEASE (pk_fonts);
00619 }
00620