1 /* FILE:    pkout.c
2  *
3  * PURPOSE: implementation of PK output functions
4  *
5  * COMMENT: functions are derived from `The GFtoPK processor' but
6  *          presented in a modular fashion so they can be used in other
7  *          programs.
8  *          (Porting `top-down' WEB code into modular readable C-code is
9  *          not a trivial exercise.)
10  *
11  * VERSION: 1.7 (December 2014)
12  *
13  * AUTHOR:  Piet Tutelaers (rcpt@urc.tue.nl)
14  */
15 
16 /* Some constants */
17 #define PK_ID	 89
18 #define PK_SPC1	240		/* All we need, max length always < 255 */
19 #define PK_NUMSPC 244
20 #define PK_NOOP	246
21 #define PK_PRE	247
22 #define PK_POST	245
23 
24 #define DPI	72.27
25 #define MIN(a,b) ( (a<b)? (a): (b))
26 
27 #include "basics.h"	/* definitions and fatal() */
28 #include <stdio.h>
29 #include <stdarg.h>	/* va_start(), va_end() */
30 #include <stdlib.h>	/* malloc() */
31 #include "pkout.h"
32 
33 #undef DEBUG
34 static int pk_len = 0;
35 static FILE *pkfile;
36 
pk_open(char * pkname)37 void pk_open(char *pkname)
38 {
39    pkfile = fopen(pkname, WB);
40    if (pkfile == NULL) fatal("Can not open %s\n", pkname);
41 }
42 
pk_close(void)43 void pk_close(void)
44 {
45    fclose(pkfile);
46 }
47 
48 /* Portable Big Endian output functions */
49 
50 /* Nybble buffer */
51 static int pk_output_byte, bitweight;
52 static void pk1(int);
53 
pk_nybble(int x)54 static void pk_nybble(int x)
55 {
56    if (bitweight == 16) {
57       bitweight = 1; pk_output_byte = x*16;
58    }
59    else {
60       bitweight = 16; pk1(pk_output_byte + x);
61    }
62 }
63 
pk1(int x)64 static void pk1(int x)
65 {
66    if (x < 0) x += 256;
67    putc(x & 0x00ff, pkfile);
68    pk_len++;
69 }
70 
pk2(int x)71 static void pk2(int x)
72 {
73    if (x < 0) x += 65536;
74    pk1((x & 0x00ff00) >> 8);
75    pk1(x & 0x0000ff);
76 }
77 
pk3(int32_t x)78 static void pk3(int32_t x)
79 {
80    pk1((x & 0x00ff0000) >> 16);
81    pk1((x & 0x0000ff00) >> 8);
82    pk1(x & 0x000000ff);
83 }
84 
pk4(int32_t x)85 static void pk4(int32_t x)
86 {
87    if (x < 0) { /* next two lines from mackay@cs.washington.edu */
88       x += 2<<29; x += 2<<29;
89       pk1(((x & 0x7f000000) >> 24) + 128);
90    }
91    else pk1((x & 0x7f000000) >> 24);
92    pk1((x & 0x00ff0000) >> 16);
93    pk1((x & 0x0000ff00) >> 8);
94    pk1(x & 0x000000ff);
95 }
96 
97 static int MAX_COUNTS; /* length of array to hold runlengths */
98 
pk_preamble(char * comment,float pointsize,int32_t checksum,unsigned int h_res,unsigned int v_res)99 void pk_preamble(char *comment, 	/* source of the font */
100 		 float pointsize,	/* pointsize in points */
101 		 int32_t checksum,      /* should equal to tfm-value */
102 		 unsigned int h_res,    /* horizontal resolution (dpi) */
103 		 unsigned int v_res)	/* vertical resolution (dpi) */
104 {
105    int i, len;
106 
107    /* compute MAX_COUNTS based upon pointsize, h_res and v_res */
108    MAX_COUNTS = pointsize / DPI * h_res * (pointsize / DPI * v_res + 1);
109 
110    pk1(PK_PRE);
111    pk1(PK_ID);
112    len = strlen(comment);
113    len = len>255? 255: len;
114    pk1(len);
115    for (i=0; i<len; i++) pk1(*comment++);
116 
117    pk4((int32_t)(pointsize * (1<<20) + 0.5));
118    pk4(checksum);
119    pk4((int32_t)(h_res / DPI * (1<<16))); pk4((int32_t)(v_res / DPI * (1<<16)));
120 }
121 
122 /* From `The GFtoPK processor', pp. 231 */
123 
optimal_size(int W,int H,int cnt,int count[],int * dyn_f)124 static int optimal_size(int W, int H, int cnt, int count[], int *dyn_f)
125 {  int comp_size = 0, b_comp_size, deriv[14], i, j, k;
126 
127    i = (count[0] == 0? 1: 0); /* skip first empty runlength */
128    /* compute comp_size and deriv[1..13] */
129    for (j=1; j<14; j++) deriv[j] = 0;
130    while (i<cnt) {
131       j = count[i++];
132       if (j == -1) { comp_size++; continue; }
133       if (j < 0) { j = -j; comp_size++; }
134       if (j < 209) comp_size += 2;
135       else {
136       	 k = j - 193;
137       	 while (k >= 16) { k=k/16; comp_size += 2; }
138       	 comp_size++;
139       }
140       if (j < 14) deriv[j]--;
141       else {
142       	 if (j < 209) deriv[(223 - j) / 15]++;
143       	 else {
144       	    k = 16;
145       	    while (k*16 < j+3) k *= 16;
146       	    if (j-k <= 192) deriv[(207-j+k)/15] += 2;
147       	 }
148       }
149    }
150    /* find minimal value */
151    b_comp_size = comp_size; *dyn_f = 0;
152    for (i=1; i<14; i++) {
153       comp_size += deriv[i];
154       if (comp_size <= b_comp_size) {
155       	 b_comp_size = comp_size; *dyn_f = i;
156       }
157    }
158    comp_size = (b_comp_size + 1) / 2;
159    if (H*W == 0 || (comp_size > (H*W + 7) / 8)) {
160       comp_size = (H*W + 7) / 8; *dyn_f = 14;
161    }
162    return comp_size;
163 }
164 
165 /* Next array contains the runlengths */
166 static int *count = NULL;
167 
168 /* Global variables (interface between pk_runlengths() and pk_char() */
169 static int dyn_f, comp_size, cnt;
170 
pk_runlengths(int W,int H,int (* next_pixel)())171 static void pk_runlengths(int W, int H, int (*next_pixel)())
172 {  int current_value, pixel, i, j, max_counts, first_count;
173    int total_pixels, rc, row, col, runlength;
174    int color2, total_pixels2, /* counting equal rows variables */
175        i1, i2, newrow;
176 
177    first_count = H; max_counts = first_count + H * W;
178    if (count == NULL) {
179       count = malloc(MAX_COUNTS * sizeof(int));
180       if (count == NULL) fatal("Out of memory\n");
181    }
182 
183    if (max_counts > MAX_COUNTS) {
184       free(count);
185       count = malloc(max_counts * sizeof(int));
186       if (count == NULL) fatal("Out of memory\n");
187       MAX_COUNTS = max_counts;
188    }
189 
190    /* 1: compute transitions BLACK<->WHITE */
191 
192    cnt = first_count;
193    runlength = 0; current_value = BLACK;
194    for (row=0; row < H; row++) {
195       for (col=0; col<W; col++) {
196       	 pixel = (*next_pixel)();
197          if (pixel == current_value) runlength++;
198          else if (pixel == OTHER(current_value)) {
199             count[cnt++] = runlength;
200             current_value = OTHER(current_value);
201             runlength = 1;
202          }
203       }
204    }
205    if (runlength>0)
206       count[cnt++] = runlength;
207 
208    /* for an empty glyph */
209    if (cnt == first_count) count[cnt++] = 0;
210 
211 #ifdef DEBUG
212    current_value = BLACK;
213    for (i=first_count; i<cnt; i++)
214       if (count[i] < 0) printf("[%d]", -count[i]);
215       else {
216 	 if (current_value == BLACK) printf("%d", count[i]);
217 	 else printf("(%d)", count[i]);
218 	 current_value = OTHER(current_value);
219       }
220    putchar('\n');
221 #endif
222 
223    /* 2: Now remove equal lines and add a repeat count at the first
224          transition of a row */
225 
226    i = first_count; j = 0;
227    total_pixels = 0; row = -1;
228    current_value = BLACK;
229    if (count[i] == 0) {
230       count[j++] = count[i++];
231       current_value = WHITE;
232    }
233    while (i < cnt) {
234       if (j >= i) fatal("Program error: report to author!\n");
235       count[j++] = count[i];
236       total_pixels += count[i++];
237       current_value = OTHER(current_value);
238       newrow = total_pixels / W; col = total_pixels % W;
239       /* if transition starts in column zero or in a previous row
240          then continue */
241       if (newrow == row || row == H-1 || col == 0) continue;
242       row = newrow;
243 
244       /* count equal rows */
245 
246          /* 1: goto first transition of next row */
247       color2 = current_value;
248       total_pixels2 = total_pixels; i2 = i;
249       while (i2 < cnt && total_pixels2 / W == row) {
250 	 total_pixels2 += count[i2++];
251 	 color2 = OTHER(color2);
252       }
253 
254          /* 2: do we need to compute a repeat count? */
255       if (color2 != current_value
256           || total_pixels2 - total_pixels != W) continue;
257 
258 	 /* 3: count them */
259       rc = 0; i1 = i;
260       while (i2 < cnt && count[i1] == count[i2]) {
261 #ifdef DEBUG
262          printf("%d (%d)", count[i1], count[i2]);
263 #endif
264 	 total_pixels2 += count[i2++]; i1++;
265       }
266       rc = total_pixels2 / W - row;  /* enclosed rows */
267       if (MIN(count[i1], count[i2]) + total_pixels2 % W < W)
268             rc--;
269 #ifdef DEBUG
270       printf(" row %d: rc = %d\n", row, rc);
271 #endif
272 
273 	/* 3: now remove the equal rows and finish last row */
274       if (rc > 0) {
275       	 /* insert a repeat count */
276          if (j >= i) fatal("Program error: report to author\n");
277          count[j++] = -rc;
278 
279          /* finish runlengths of current row */
280          while (total_pixels + count[i] < row*W+W) {
281             if (j >= i) fatal("Program error: increase FIRST_COUNT!\n");
282             count[j++] = count[i];
283             total_pixels += count[i++];
284             current_value = OTHER(current_value);
285          }
286 
287         /* skip runlengths of equal rows */
288          while (total_pixels + count[i] < (row+rc+1)*W) {
289             total_pixels += count[i++];
290             current_value = OTHER(current_value);
291          }
292          row += rc;
293       }
294    }
295    cnt = j; /* that is what we have now */
296 
297    /* 3: compute optimal packing size */
298 
299    comp_size = optimal_size(H, W, cnt, count, &dyn_f);
300 #ifdef DEBUG
301    current_value = BLACK;
302    for (i=0; i<cnt; i++)
303       if (count[i] < 0) printf("[%d]", -count[i]);
304       else {
305 	 if (current_value == BLACK) printf("%d", count[i]);
306 	 else printf("(%d)", count[i]);
307 	 current_value = OTHER(current_value);
308       }
309    printf("\nOptimal packing with dyn_f = %d total size %d\n",
310    dyn_f, comp_size);
311 #endif
312 }
313 
314 #define MAX_TWOBYTE_NYBBLE (208 - 15*dyn_f)
315 
pk_number(int x)316 static void pk_number(int x)
317 {  int k;
318 
319    if (x < 0) { /* repeat count */
320       if (x == -1) pk_nybble(0xF);
321       else { pk_nybble(0xE);
322          pk_number(-x);
323       }
324    }
325    else if (x <= dyn_f) pk_nybble(x);
326    else if (x <= MAX_TWOBYTE_NYBBLE) { /* two nybble values */
327       x-= dyn_f+1;
328       pk_nybble(dyn_f + 1 + x / 16);
329       pk_nybble(x % 16);
330    }
331    else { /* huge counts */
332       x = x - MAX_TWOBYTE_NYBBLE + 15; k = 16;
333       while (k <= x) { k *= 16; pk_nybble(0x0); }
334       while (k > 1) { k = k / 16; pk_nybble(x / k); x = x % k; }
335    }
336 }
337 
338 /* in case runlength encoding is not optimal we send a compressed
339    bitmap in stead of the packed runlengths
340    See GFtoPK processor, pp. 235 */
pk_bitmap(int width,int cnt,int runlength[])341 static void pk_bitmap(int width, int cnt, int runlength[])
342 {  int count,		/* number of bits in the current state to send */
343        buff,		/* byte buffer */
344        h_bit,		/* horizontal bit count for each runlength */
345        p_bit,   	/* what bit are we about to send out? */
346        r_on, s_on,	/* state saving variables */
347        r_count, s_count,/* dito */
348        r_i, s_i,	/* dito */
349        rc,		/* repeat count */
350        state,		/* state variable (what state?) */
351        on,		/* starting with black? */
352        i;		/* points to next runlength */
353    static int power[9] = {1, 2, 4, 8, 16, 32, 64, 128, 256};
354 
355    buff = 0; p_bit = 8; h_bit = width;
356    state = 0; rc = 0;
357    r_on = s_on = r_count = s_count = r_i = s_i = 0; /* avoid warnings */
358    on = 1; count = runlength[0]; i = 1;
359    while (i < cnt || state || count>0) {
360       if (state) {
361          count = r_count; i = r_i; on = r_on; rc--;
362       }
363       else {
364          r_count = count; r_i = i; r_on = on;
365       }
366 
367       /* send one row of width bits */
368       do {
369       	 if (count == 0) {
370       	    if (runlength[i] < 0) {
371 	       if (!state) rc = - runlength[i];
372 	       i++;
373       	    }
374       	    count = runlength[i]; i++; on = !on;
375       	 }
376       	 if (count >= p_bit && p_bit < h_bit) {
377       	    /* we end a byte, we don't end a runlength */
378       	    if (on) buff += power[p_bit] - 1;
379       	    pk1(buff);
380       	    buff = 0; h_bit -= p_bit;
381       	    count -= p_bit; p_bit = 8;
382       	 }
383       	 else {
384       	    if (count < p_bit && count < h_bit) {
385 	       /* we neither end the row nor the byte */
386 	       if (on) buff += power[p_bit] - power[p_bit-count];
387 	       p_bit -= count; h_bit -= count; count = 0;
388       	    }
389       	    else { /* we end a row and maybe a byte */
390 	       if (on) buff += power[p_bit] - power[p_bit-h_bit];
391 	       count -= h_bit; p_bit -= h_bit; h_bit = width;
392 	       if (p_bit == 0) {
393 	          pk1(buff); buff = 0; p_bit = 8;
394 	       }
395       	    }
396       	 }
397       } while (h_bit != width);
398 
399       if (state && rc == 0) {
400          count = s_count; i = s_i; on = s_on; state = 0;
401       }
402       else if (!state && rc >0) {
403       	 s_count = count; s_i = i; s_on = on; state = 1;
404       }
405    }
406    if (p_bit != 8) pk1(buff);
407 }
408 
409 /* For packing a character */
pk_char(int char_code,int32_t tfm_width,int h_escapement,unsigned int width,unsigned int height,int h_offset,int v_offset,int (* next_pixel)())410 void pk_char(int char_code, 	/* character code 0..255 */
411         int32_t tfm_width,      /* TFM width of character */
412         int h_escapement,       /* horizontal escapement in pixels */
413    	unsigned int width, 	/* width of bounding box */
414    	unsigned int height, 	/* height of bounding box */
415    	int h_offset, 		/* horizontal offset to reference point */
416    	int v_offset, 		/* vertical offset to reference point */
417         int (*next_pixel)())	/* user's next pixel generator */
418 {  int i;
419    unsigned short flag_byte;
420 
421    pk_runlengths(width, height, next_pixel);
422 
423    /* write a character preamble */
424 
425    flag_byte = dyn_f * 16;
426    if (count[0] > 0) flag_byte += 8; /* starting in BLACK */
427    if (tfm_width > 0XFFFFFF || width > 65535 || height > 65535
428        || h_offset > 32767 || v_offset > 32767
429        || h_offset < -32768 || v_offset < -32768
430        || comp_size > 196594)
431    { /* write long format */
432       fatal("Can't handle long format yet!\n");
433    }
434    else if (h_escapement > 255 || width > 255 || height > 255
435             || h_offset > 127 || v_offset > 127
436             || h_offset < -128 || v_offset < -128
437             || comp_size > 1015)
438    { /* write two-byte short character preamble */
439       comp_size += 13; flag_byte += comp_size / 65535 + 4;
440       pk1(flag_byte);
441       pk2(comp_size % 65535);
442       pk1(char_code);
443       pk3(tfm_width);
444       pk2(h_escapement); /* pixels ! */
445       pk2(width);
446       pk2(height);
447       pk2(h_offset);
448       pk2(v_offset);
449    }
450    else { /* write one-byte short character preamble */
451       comp_size += 8;  flag_byte += comp_size / 256;
452       pk1(flag_byte);
453       pk1(comp_size % 256);
454       pk1(char_code);
455       pk3(tfm_width);
456       pk1(h_escapement); /* pixels ! */
457       pk1(width);
458       pk1(height);
459       pk1(h_offset);
460       pk1(v_offset);
461    }
462 
463    /* and now the character itself */
464    if (dyn_f != 14) { /* send compressed format */
465       if (count[0] == 0) i = 1; else i = 0;
466       bitweight = 16;
467       for (i=i; i<cnt; i++) pk_number(count[i]);
468       if (bitweight != 16) pk1(pk_output_byte);
469    }
470    else /* send bitmap */
471       if (height > 0) pk_bitmap(width, cnt, count);
472 
473 }
474 
475 /*
476  * Output small string special (<255 chars)
477  */
pkstring(const char * fmt,...)478 static void pkstring(const char *fmt, ...) {
479    char buf[256]; int i, len;
480    va_list args;
481 
482    va_start(args, fmt);
483 #ifdef CHARSPRINTF
484    (void) vsprintf(buf, fmt, args);
485    len = strlen(buf);
486 #else
487    len = vsprintf(buf, fmt, args);
488 #endif
489    if (len > 255 || len < 0) fatal("PK string exceeds 255 characters\n");
490    va_end(args);
491 
492    pk1(PK_SPC1);
493    pk1(len); i = 0;
494    for (i = 0; i < len; i++) pk1(buf[i]);
495 }
496 
497 /*
498  * Compute METAFONT magnification string for <dpi>
499  */
500 
PSPKINT(float x)501 static int PSPKINT(float x) {
502    return (int) x;
503 }
504 
505 static char mag_str[64];
506 
magnification(int dpi,int BDPI)507 static char *magnification (int dpi, int BDPI) {
508    double size, magstep;
509 
510    if (dpi == BDPI) {
511       sprintf(mag_str, "magstep(0)");
512       return mag_str;
513    }
514    size = BDPI; magstep = 0;
515    while (dpi < size) {
516       size = size / 1.095445115;
517       magstep -= 0.5;
518       if (dpi == PSPKINT(size + 0.5)) {
519          sprintf(mag_str, "magstep(%.1f)", magstep);
520          return mag_str;
521       }
522       if (dpi > size) {
523          sprintf(mag_str, "%d+%d/%d", PSPKINT(dpi/BDPI), dpi%BDPI, BDPI);
524          return mag_str;
525       }
526    }
527    while (dpi > size) {
528       size = size * 1.095445115;
529       magstep += 0.5;
530       if (dpi == PSPKINT(size + 0.5)) {
531          sprintf(mag_str, "magstep(%.1f)", magstep);
532          return mag_str;
533       }
534       if (dpi < size) {
535          sprintf(mag_str, "%d+%d/%d", PSPKINT(dpi/BDPI), dpi%BDPI, BDPI);
536          return mag_str;
537       }
538    }
539    fatal("PK could not determine magnification\n");
540 }
541 
542 /*
543  * Barebone postample
544  */
pk_postamble(void)545 void pk_postamble(void)
546 {
547    pk1(PK_POST);
548    while (pk_len % 4 != 0) pk1(PK_NOOP);
549 }
550 
551 /*
552  * Write special PK strings in the postamble for identification
553  * purposes as proposed by mackay@cs.washington.edu.
554  */
ps2pk_postamble(char * fontname,char * encname,int base_res,int h_res,int v_res,float pointsize,char * args)555 void ps2pk_postamble(char *fontname, /* The real FontName from the afm */
556 		  char *encname,     /* The actual name, not the filename */
557 		  int base_res,      /* basic resolution */
558 		  int h_res,	     /* Match against base_res for mag */
559 		  int v_res,	     /* Match against h_res for aspect_ratio */
560 		  float pointsize,   /* Used for fontfacebyte calculation */
561 		  char *args)        /* Essential ps2pk args */
562 {
563    int i;
564 
565    pkstring("ps2pk options: %s", args);
566    pkstring("fontid=%s", fontname);
567    if (encname) pkstring("codingscheme=%s", encname);
568    pkstring("fontfacebyte");
569    pk1(PK_NUMSPC);
570    i = (pointsize < 127.0) ?
571      ((254 - (int)((2 * pointsize)+0.5)) * (1 << 16)) : 0;
572    pk4(i);
573    pkstring("pixels_per_inch=%d", base_res);
574    pkstring("mag=%s", magnification(h_res, base_res));
575    if (v_res != h_res)
576       pkstring("aspect ratio=%d / %d",
577 	  (int)((1.0 * v_res / h_res * base_res) + 0.5), base_res);
578 
579    pk1(PK_POST);
580    while (pk_len % 4 != 0) pk1(PK_NOOP);
581 }
582