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