Back to index

tetex-bin  3.0
pkout.c
Go to the documentation of this file.
00001 /* FILE:    pkout.c
00002  *
00003  * PURPOSE: implementation of PK output functions
00004  *
00005  * COMMENT: functions are derived from `The GFtoPK processor' but
00006  *          presented in a modular fashion so they can be used in other
00007  *          programs. 
00008  *          (Porting `top-down' WEB code into modular readable C-code is
00009  *          not a trivial exercise.)
00010  *
00011  * VERSION: Febr. 1992
00012  *          Dec. 1993
00013  *          Sep. 1995
00014  *
00015  * AUTHOR:  Piet Tutelaers (rcpt@urc.tue.nl)
00016  */
00017 
00018 /* Some constants */
00019 #define PK_ID  89
00020 #define PK_SPC1      240           /* All we need, max length always < 255 */
00021 #define PK_NUMSPC 244
00022 #define PK_NOOP      246
00023 #define PK_PRE       247
00024 #define PK_POST      245
00025 
00026 #define DPI   72.27
00027 #define MIN(a,b) ( (a<b)? (a): (b))
00028 
00029 #include <stdio.h>
00030 #include <stdarg.h>  /* va_start(), va_end() */
00031 #include <stdlib.h>  /* malloc() */
00032 #include "basics.h"  /* definitions and fatal() */
00033 #include "pkout.h"
00034 
00035 #undef DEBUG
00036 static int pk_len = 0;
00037 static FILE *pkfile;
00038 
00039 void pk_open(char *pkname)
00040 {
00041    pkfile = fopen(pkname, WB);
00042    if (pkfile == NULL) fatal("Can not open %s\n", pkname);
00043 }
00044 
00045 void pk_close()
00046 {
00047    fclose(pkfile);
00048 }
00049 
00050 /* Portable Big Endian output functions */
00051 
00052 /* Nybble buffer */
00053 static int pk_output_byte, bitweight;
00054 static void pk1(int);
00055 
00056 static void pk_nybble(int x)
00057 {
00058    if (bitweight == 16) {
00059       bitweight = 1; pk_output_byte = x*16;
00060    }
00061    else {
00062       bitweight = 16; pk1(pk_output_byte + x);
00063    }
00064 }
00065 
00066 static void pk1(int x)
00067 {
00068    if (x < 0) x += 256; 
00069    putc(x & 0x00ff, pkfile); 
00070    pk_len++;
00071 }
00072 
00073 static void pk2(int x)
00074 {
00075    if (x < 0) x += 65536; 
00076    pk1((x & 0x00ff00) >> 8);
00077    pk1(x & 0x0000ff);
00078 }
00079 
00080 static void pk3(INT32 x)
00081 {
00082    pk1((x & 0x00ff0000) >> 16);
00083    pk1((x & 0x0000ff00) >> 8);
00084    pk1(x & 0x000000ff);
00085 }
00086 
00087 static void pk4(INT32 x)
00088 {
00089    if (x < 0) { /* next two lines from mackay@cs.washington.edu */
00090       x += 2<<29; x += 2<<29;
00091       pk1(((x & 0x7f000000) >> 24) + 128);
00092    }
00093    else pk1((x & 0x7f000000) >> 24); 
00094    pk1((x & 0x00ff0000) >> 16);
00095    pk1((x & 0x0000ff00) >> 8);
00096    pk1(x & 0x000000ff);
00097 }
00098 
00099 static int MAX_COUNTS; /* length of array to hold runlengths */
00100 
00101 void pk_preamble(char *comment,    /* source of the font */
00102                float pointsize,    /* pointsize in points */
00103                INT32 checksum,        /* should equal to tfm-value */
00104                unsigned int h_res,    /* horizontal resolution (dpi) */
00105                unsigned int v_res) /* vertical resolution (dpi) */
00106 { 
00107    int i, len;
00108 
00109    /* compute MAX_COUNTS based upon pointsize, h_res and v_res */
00110    MAX_COUNTS = pointsize / DPI * h_res * (pointsize / DPI * v_res + 1);
00111    
00112    pk1(PK_PRE);
00113    pk1(PK_ID);
00114    len = strlen(comment);
00115    len = len>255? 255: len;
00116    pk1(len);
00117    for (i=0; i<len; i++) pk1(*comment++);
00118 
00119    pk4(pointsize * (1<<20) + 0.5);
00120    pk4(checksum);
00121    pk4(h_res / DPI * (1<<16)); pk4(v_res / DPI * (1<<16));
00122 }
00123 
00124 /* From `The GFtoPK processor', pp. 231 */
00125 
00126 int optimal_size(int W, int H, int cnt, int count[], int *dyn_f)
00127 {  int comp_size = 0, b_comp_size, deriv[14], i, j, k;
00128 
00129    i = (count[0] == 0? 1: 0); /* skip first empty runlength */
00130    /* compute comp_size and deriv[1..13] */
00131    for (j=1; j<14; j++) deriv[j] = 0;
00132    while (i<cnt) {
00133       j = count[i++];
00134       if (j == -1) { comp_size++; continue; }
00135       if (j < 0) { j = -j; comp_size++; }
00136       if (j < 209) comp_size += 2;
00137       else {
00138         k = j - 193;
00139         while (k >= 16) { k=k/16; comp_size += 2; }
00140         comp_size++;
00141       }
00142       if (j < 14) deriv[j]--;
00143       else {
00144         if (j < 209) deriv[(223 - j) / 15]++;
00145         else {
00146            k = 16;
00147            while (k*16 < j+3) k *= 16;
00148            if (j-k <= 192) deriv[(207-j+k)/15] += 2;
00149         }
00150       }
00151    }
00152    /* find minimal value */
00153    b_comp_size = comp_size; *dyn_f = 0;
00154    for (i=1; i<14; i++) {
00155       comp_size += deriv[i];
00156       if (comp_size <= b_comp_size) {
00157         b_comp_size = comp_size; *dyn_f = i;
00158       }
00159    }
00160    comp_size = (b_comp_size + 1) / 2;
00161    if (H*W == 0 || (comp_size > (H*W + 7) / 8)) {
00162       comp_size = (H*W + 7) / 8; *dyn_f = 14;
00163    }
00164    return comp_size;
00165 }
00166 
00167 /* Next array contains the runlengths */
00168 static int *count = NULL;
00169 
00170 /* Global variables (interface between pk_runlengths() and pk_char() */
00171 static int dyn_f, comp_size, cnt;
00172 
00173 static void pk_runlengths(int W, int H, int (*next_pixel)())
00174 {  int current_value, pixel, i, j, max_counts, first_count;
00175    int total_pixels, rc, row, col, runlength;
00176    int color1, color2, total_pixels2, /* counting equal rows variables */
00177        pixels, i1, i2, newrow;
00178 
00179    first_count = H; max_counts = first_count + H * W;
00180    if (count == NULL) {
00181       count = malloc(MAX_COUNTS * sizeof(int));
00182       if (count == NULL) fatal("Out of memory\n");
00183    }
00184    
00185    if (max_counts > MAX_COUNTS) {
00186       free(count);
00187       count = malloc(max_counts * sizeof(int));
00188       if (count == NULL) fatal("Out of memory\n");
00189       MAX_COUNTS = max_counts;
00190    }
00191 
00192    /* 1: compute transitions BLACK<->WHITE */
00193 
00194    cnt = first_count;
00195    runlength = 0; current_value = BLACK;
00196    for (row=0; row < H; row++) {
00197       for (col=0; col<W; col++) {
00198         pixel = (*next_pixel)();
00199          if (pixel == current_value) runlength++;
00200          else if (pixel == OTHER(current_value)) {
00201             count[cnt++] = runlength;
00202             current_value = OTHER(current_value);
00203             runlength = 1;
00204          }
00205       }
00206    }
00207    if (runlength>0)
00208       count[cnt++] = runlength;
00209 
00210 #ifdef DEBUG
00211    current_value = BLACK;
00212    for (i=first_count; i<cnt; i++)
00213       if (count[i] < 0) printf("[%d]", -count[i]);
00214       else {
00215         if (current_value == BLACK) printf("%d", count[i]);
00216         else printf("(%d)", count[i]);
00217         current_value = OTHER(current_value);
00218       }
00219    putchar('\n');
00220 #endif
00221 
00222    /* 2: Now remove equal lines and add a repeat count at the first
00223          transition of a row */
00224 
00225    i = first_count; j = 0;
00226    total_pixels = 0; row = -1;
00227    current_value = BLACK;
00228    if (count[i] == 0) {
00229       count[j++] = count[i++];
00230       current_value = WHITE;
00231    }
00232    while (i < cnt) {
00233       if (j >= i) fatal("Program error: report to author!\n");
00234       count[j++] = count[i];
00235       total_pixels += count[i++];
00236       current_value = OTHER(current_value);
00237       newrow = total_pixels / W; col = total_pixels % W;
00238       /* if transition starts in column zero or in a previous row 
00239          then continue */
00240       if (newrow == row || row == H-1 || col == 0) continue;
00241       row = newrow;
00242 
00243       /* count equal rows */
00244 
00245          /* 1: goto first transition of next row */
00246       color2 = current_value;
00247       total_pixels2 = total_pixels; i2 = i;
00248       while (i2 < cnt && total_pixels2 / W == row) {
00249         total_pixels2 += count[i2++];
00250         color2 = OTHER(color2);
00251       }
00252 
00253          /* 2: do we need to compute a repeat count? */
00254       if (color2 != current_value 
00255           || total_pixels2 - total_pixels != W) continue;
00256 
00257         /* 3: count them */
00258       rc = 0; i1 = i;
00259       while (i2 < cnt && count[i1] == count[i2]) {
00260 #ifdef DEBUG
00261          printf("%d (%d)", count[i1], count[i2]);
00262 #endif
00263         total_pixels2 += count[i2++]; i1++; 
00264       }
00265       rc = total_pixels2 / W - row;  /* enclosed rows */
00266       if (MIN(count[i1], count[i2]) + total_pixels2 % W < W)
00267             rc--;
00268 #ifdef DEBUG
00269       printf(" row %d: rc = %d\n", row, rc);
00270 #endif
00271 
00272        /* 3: now remove the equal rows and finish last row */      
00273       if (rc > 0) {
00274         /* insert a repeat count */
00275          if (j >= i) fatal("Program error: report to author\n");
00276          count[j++] = -rc;
00277 
00278          /* finish runlengths of current row */
00279          while (total_pixels + count[i] < row*W+W) {
00280             if (j >= i) fatal("Program error: increase FIRST_COUNT!\n");
00281             count[j++] = count[i];
00282             total_pixels += count[i++];
00283             current_value = OTHER(current_value);
00284          }
00285 
00286         /* skip runlengths of equal rows */
00287          while (total_pixels + count[i] < (row+rc+1)*W) {
00288             total_pixels += count[i++];
00289             current_value = OTHER(current_value);
00290          }
00291          row += rc;
00292       }
00293    }
00294    cnt = j; /* that is what we have now */
00295 
00296    /* 3: compute optimal packing size */
00297 
00298    comp_size = optimal_size(H, W, cnt, count, &dyn_f);
00299 #ifdef DEBUG
00300    current_value = BLACK;
00301    for (i=0; i<cnt; i++)
00302       if (count[i] < 0) printf("[%d]", -count[i]);
00303       else {
00304         if (current_value == BLACK) printf("%d", count[i]);
00305         else printf("(%d)", count[i]);
00306         current_value = OTHER(current_value);
00307       }
00308    printf("\nOptimal packing with dyn_f = %d total size %d\n", 
00309    dyn_f, comp_size);
00310 #endif
00311 }
00312 
00313 #define MAX_TWOBYTE_NYBBLE (208 - 15*dyn_f)
00314 
00315 static void pk_number(int x)
00316 {  int k;
00317 
00318    if (x < 0) { /* repeat count */
00319       if (x == -1) pk_nybble(0xF);
00320       else { pk_nybble(0xE);
00321          pk_number(-x);
00322       }
00323    } 
00324    else if (x <= dyn_f) pk_nybble(x);
00325    else if (x <= MAX_TWOBYTE_NYBBLE) { /* two nybble values */
00326       x-= dyn_f+1;
00327       pk_nybble(dyn_f + 1 + x / 16);
00328       pk_nybble(x % 16);
00329    }
00330    else { /* huge counts */
00331       x = x - MAX_TWOBYTE_NYBBLE + 15; k = 16;
00332       while (k <= x) { k *= 16; pk_nybble(0x0); }
00333       while (k > 1) { k = k / 16; pk_nybble(x / k); x = x % k; }
00334    } 
00335 }
00336 
00337 /* in case runlength encoding is not optimal we send a compressed 
00338    bitmap in stead of the packed runlengths
00339    See GFtoPK processor, pp. 235 */
00340 static void pk_bitmap(int width, int cnt, int runlength[])
00341 {  int count,        /* number of bits in the current state to send */
00342        buff,         /* byte buffer */
00343        h_bit,        /* horizontal bit count for each runlength */
00344        p_bit,        /* what bit are we about to send out? */
00345        r_on, s_on,   /* state saving variables */
00346        r_count, s_count,/* dito */
00347        r_i, s_i,     /* dito */
00348        rc,           /* repeat count */
00349        state,        /* state variable (what state?) */
00350        on,           /* starting with black? */
00351        i;            /* points to next runlength */
00352    static int power[9] = {1, 2, 4, 8, 16, 32, 64, 128, 256};
00353 
00354    buff = 0; p_bit = 8; h_bit = width; 
00355    state = 0; rc = 0;
00356    on = 1; count = runlength[0]; i = 1;
00357    while (i < cnt || state || count>0) {
00358       if (state) { 
00359          count = r_count; i = r_i; on = r_on; rc--; 
00360       }
00361       else { 
00362          r_count = count; r_i = i; r_on = on; 
00363       }
00364 
00365       /* send one row of width bits */
00366       do {
00367         if (count == 0) {
00368            if (runlength[i] < 0) {
00369               if (!state) rc = - runlength[i];
00370               i++;
00371            }
00372            count = runlength[i]; i++; on = !on;
00373         }
00374         if (count >= p_bit && p_bit < h_bit) {
00375            /* we end a byte, we don't end a runlength */
00376            if (on) buff += power[p_bit] - 1;
00377            pk1(buff); 
00378            buff = 0; h_bit -= p_bit; 
00379            count -= p_bit; p_bit = 8;
00380         }
00381         else {
00382            if (count < p_bit && count < h_bit) {
00383               /* we neither end the row nor the byte */
00384               if (on) buff += power[p_bit] - power[p_bit-count];
00385               p_bit -= count; h_bit -= count; count = 0;
00386            }
00387            else { /* we end a row and maybe a byte */
00388               if (on) buff += power[p_bit] - power[p_bit-h_bit];
00389               count -= h_bit; p_bit -= h_bit; h_bit = width;
00390               if (p_bit == 0) {
00391                  pk1(buff); buff = 0; p_bit = 8;
00392               }
00393            }
00394         }
00395       } while (h_bit != width);
00396 
00397       if (state && rc == 0) { 
00398          count = s_count; i = s_i; on = s_on; state = 0;
00399       }
00400       else if (!state && rc >0) {
00401         s_count = count; s_i = i; s_on = on; state = 1;
00402       }
00403    }
00404    if (p_bit != 8) pk1(buff);
00405 }
00406 
00407 /* For packing a character */
00408 void pk_char(int char_code,        /* character code 0..255 */
00409         INT32 tfm_width,        /* TFM width of character */
00410         int h_escapement,       /* horizontal escapement in pixels */
00411        unsigned int width,  /* width of bounding box */
00412        unsigned int height,        /* height of bounding box */
00413        int h_offset,               /* horizontal offset to reference point */
00414        int v_offset,               /* vertical offset to reference point */
00415         int (*next_pixel)())       /* user's next pixel generator */
00416 {  int i, pl, current_value;
00417    short two_byte;   /* two_byte quantities? */
00418    unsigned short flag_byte;
00419 
00420    pk_runlengths(width, height, next_pixel);
00421 
00422    /* write a character preamble */
00423 
00424    flag_byte = dyn_f * 16;
00425    if (count[0] > 0) flag_byte += 8; /* starting in BLACK */
00426    if (tfm_width > 0XFFFFFF || width > 65535 || height > 65535
00427        || h_offset > 32767 || v_offset > 32767
00428        || h_offset < -32768 || v_offset < -32768
00429        || comp_size > 196594) 
00430    { /* write long format */
00431       fatal("Can't handle long format yet!\n");
00432    }
00433    else if (h_escapement > 255 || width > 255 || height > 255
00434             || h_offset > 127 || v_offset > 127
00435             || h_offset < -128 || v_offset < -128 
00436             || comp_size > 1015)
00437    { /* write two-byte short character preamble */
00438       comp_size += 13; flag_byte += comp_size / 65535 + 4; 
00439       pk1(flag_byte);
00440       pk2(comp_size % 65535);
00441       pk1(char_code);
00442       pk3(tfm_width);
00443       pk2(h_escapement); /* pixels ! */
00444       pk2(width);
00445       pk2(height);
00446       pk2(h_offset);
00447       pk2(v_offset);
00448    }
00449    else { /* write one-byte short character preamble */
00450       comp_size += 8;  flag_byte += comp_size / 256; 
00451       pk1(flag_byte);
00452       pk1(comp_size % 256);
00453       pk1(char_code);
00454       pk3(tfm_width);
00455       pk1(h_escapement); /* pixels ! */
00456       pk1(width);
00457       pk1(height);
00458       pk1(h_offset);
00459       pk1(v_offset);
00460    }
00461 
00462    /* and now the character itself */
00463    if (dyn_f != 14) { /* send compressed format */ 
00464       if (count[0] == 0) i = 1; else i = 0;
00465       bitweight = 16;
00466       for (i=i; i<cnt; i++) pk_number(count[i]);
00467       if (bitweight != 16) pk1(pk_output_byte);
00468    }
00469    else /* send bitmap */
00470       if (height > 0) pk_bitmap(width, cnt, count);
00471 
00472 }
00473 
00474 /*
00475  * Output small string special (<255 chars)
00476  */
00477 static void pkstring(char *fmt, ...) {
00478    char buf[256]; int i, len;
00479    va_list args;
00480 
00481    va_start(args, fmt);
00482 #ifdef CHARSPRINTF
00483    (void) vsprintf(buf, fmt, args);
00484    len = strlen(buf);
00485 #else
00486    len = vsprintf(buf, fmt, args);
00487 #endif
00488    if (len > 255 || len < 0) fatal("PK string exceeds 255 characters\n");
00489    va_end(args);
00490 
00491    pk1(PK_SPC1);
00492    pk1(len); i = 0;
00493    for (i = 0; i < len; i++) pk1(buf[i]);
00494 }
00495 
00496 /*
00497  * Compute METAFONT magnification string for <dpi>
00498  */
00499 
00500 int PSPKINT(float x) {
00501    return (int) x;
00502 }
00503 
00504 static char mag_str[64];
00505 
00506 char *magnification (int dpi, int BDPI) {
00507    double size, magstep;
00508 
00509    if (dpi == BDPI) {
00510       sprintf(mag_str, "magstep(0)");
00511       return mag_str;
00512    }
00513    size = BDPI; magstep = 0; 
00514    while (dpi < size) {
00515       size = size / 1.095445115;
00516       magstep -= 0.5;
00517       if (dpi == PSPKINT(size + 0.5)) {
00518          sprintf(mag_str, "magstep(%.1f)", magstep);
00519          return mag_str;
00520       }
00521       if (dpi > size) {
00522          sprintf(mag_str, "%d+%d/%d", PSPKINT(dpi/BDPI), dpi%BDPI, BDPI);
00523          return mag_str;
00524       }
00525    }
00526    while (dpi > size) {
00527       size = size * 1.095445115;
00528       magstep += 0.5;
00529       if (dpi == PSPKINT(size + 0.5)) {
00530          sprintf(mag_str, "magstep(%.1f)", magstep);
00531          return mag_str;
00532       }
00533       if (dpi < size) {
00534          sprintf(mag_str, "%d+%d/%d", PSPKINT(dpi/BDPI), dpi%BDPI, BDPI);
00535          return mag_str;
00536       }
00537    }
00538 }
00539 
00540 /*
00541  * Barebone postample
00542  */ 
00543 void pk_postamble()
00544 {      
00545    pk1(PK_POST);
00546    while (pk_len % 4 != 0) pk1(PK_NOOP);
00547 }
00548 
00549 /*
00550  * Write special PK strings in the postamble for identification
00551  * purposes as proposed by mackay@cs.washington.edu.
00552  */ 
00553 void ps2pk_postamble(char *fontname, /* The real FontName from the afm */
00554                 char *encname,     /* The actual name, not the filename */
00555                 int base_res,      /* basic resolution */
00556                 int h_res,       /* Match against base_res for mag */
00557                 int v_res,       /* Match against h_res for aspect_ratio */
00558                 float pointsize,   /* Used for fontfacebyte calculation */
00559                 char *args)        /* Essential ps2pk args */
00560 {      
00561    int i;
00562 
00563    pkstring("ps2pk options: %s", args);
00564    pkstring("fontid=%s", fontname);
00565    if (encname) pkstring("codingscheme=%s", encname);
00566    pkstring("fontfacebyte"); 
00567    pk1(PK_NUMSPC);
00568    i = (pointsize < 127.0) ?
00569      ((254 - (int)((2 * pointsize)+0.5)) * (1 << 16)) : 0;
00570    pk4(i);
00571    pkstring("pixels_per_inch=%d", base_res);
00572    pkstring("mag=%s", magnification(h_res, base_res));
00573    if (v_res != h_res)
00574       pkstring("aspect ratio=%d / %d", 
00575          (int)((1.0 * v_res / h_res * base_res) + 0.5), base_res);
00576 
00577    pk1(PK_POST);
00578    while (pk_len % 4 != 0) pk1(PK_NOOP);
00579 }