Add support for using TrueType fonts, including embedding in PostScript but
[sgt/halibut] / in_pf.c
CommitLineData
3e2dd889 1/*
2 * PostScript Type 1 font file support for Halibut
3 */
4/*
5 * Type 1 font file formats are specified by Adobe Technical Note
6 * #5040: "Supporting Downloadable PostScript Language Fonts".
7 * Halibut supports hexadecimal format (section 3.1) and IBM PC format
8 * (section 3.3), commonly called PFA and PFB respectively.
9 */
10
11#include <assert.h>
12#include <limits.h>
44407fea 13#include <stdio.h>
3e2dd889 14#include <stdlib.h>
44407fea 15#include "halibut.h"
16#include "paper.h"
17
3e2dd889 18#define PFB_ASCII 1
19#define PFB_BINARY 2
20#define PFB_EOF 3
21
22typedef struct t1_font_Tag t1_font;
23typedef struct t1_data_Tag t1_data;
24
25struct t1_font_Tag {
26 t1_data *data;
27 size_t length1;
28 size_t length2;
29 filepos pos;
30};
31
32struct t1_data_Tag {
33 char type;
34 size_t length;
35 unsigned char *data;
36 t1_data *next;
37};
38
39typedef struct pfstate_Tag {
40 t1_data *data;
41 t1_data *curblock;
42 size_t offset;
43} pfstate;
44
45static void pf_identify(t1_font *tf);
46
47static t1_data *load_pfb_file(FILE *fp, filepos *pos) {
48 t1_data *head = NULL, *tail = NULL;
49 int c, i;
50 char type;
51
52 pos->line = 0;
53 for (;;) {
54 if (fgetc(fp) != 128) abort();
55 type = fgetc(fp);
56 if (type == PFB_EOF) return head;
57 if (tail) {
58 tail->next = snew(t1_data);
59 tail = tail->next;
60 } else {
61 head = snew(t1_data);
62 tail = head;
63 }
64 tail->type = type;
65 tail->length = 0;
66 for (i = 0; i < 4; i++) {
67 c = fgetc(fp);
68 if (c == EOF) abort();
69 tail->length |= c << (8 * i);
70 }
71 tail->data = snewn(tail->length, unsigned char);
72 if (fread(tail->data, 1, tail->length, fp) != tail->length) abort();
73 }
74}
75
76static t1_data *load_pfa_file(FILE *fp, filepos *pos) {
77 t1_data *ret = snew(t1_data);
78 size_t off = 0, len, got;
79
80 pos->line = 0;
81 ret->type = PFB_ASCII;
82 len = 32768;
83 ret->data = snewn(len, unsigned char);
84 for (;;) {
85 got = fread(ret->data + off, 1, len - off, fp);
86 off += got;
87 if (off != len) break;
88 len *= 2;
89 ret->data = sresize(ret->data, len, unsigned char);
90 }
91 ret->data = sresize(ret->data, off, unsigned char);
92 ret->length = off;
93 return ret;
94}
c885c2ff 95
44407fea 96void read_pfa_file(input *in) {
3e2dd889 97 t1_font *tf = snew(t1_font);
98
99 tf->data = load_pfa_file(in->currfp, &in->pos);
100 tf->pos = in->pos;
101 tf->length1 = tf->length2 = 0;
102 fclose(in->currfp);
103 pf_identify(tf);
104}
105
106void read_pfb_file(input *in) {
107 t1_font *tf = snew(t1_font);
108
109 tf->data = load_pfb_file(in->currfp, &in->pos);
110 tf->pos = in->pos;
111 tf->length1 = tf->length2 = 0;
112 fclose(in->currfp);
113 pf_identify(tf);
114}
115static char *pf_read_token(pfstate *);
116
117/*
118 * Read a character from the initial plaintext part of a Type 1 font
119 */
120static int pf_getc(pfstate *pf) {
121 if (pf->offset == pf->curblock->length) {
122 if (pf->curblock->next == NULL) return EOF;
123 pf->curblock = pf->curblock->next;
124 pf->offset = 0;
125 }
126 if (pf->curblock->type != PFB_ASCII) return EOF;
127 return pf->curblock->data[pf->offset++];
128}
129
130static void pf_ungetc(int c, pfstate *pf) {
131 assert(pf->offset > 0);
132 pf->offset--;
133 assert(c == pf->curblock->data[pf->offset]);
134}
135
136static void pf_rewind(pfstate *pf) {
137 pf->curblock = pf->data;
138 pf->offset = 0;
139}
140
141static void pf_seek(pfstate *pf, size_t off) {
142 t1_data *td = pf->data;
143
144 while (td->length < off) {
145 off -= td->length;
146 td = td->next;
147 }
148 pf->curblock = td;
149 pf->offset = off;
150}
151
152static size_t pf_tell(pfstate *pf) {
153 t1_data *td = pf->data;
154 size_t o = 0;
155
156 while (td != pf->curblock) {
157 o += td->length;
158 td = td->next;
159 }
160 return o + pf->offset;
161}
162
163static void pf_identify(t1_font *tf) {
c885c2ff 164 rdstringc rsc = { 0, 0, NULL };
165 char *p;
44407fea 166 size_t len;
167 char *fontname;
168 font_info *fi;
c885c2ff 169 int c;
3e2dd889 170 pfstate pfs, *pf = &pfs;
44407fea 171
3e2dd889 172 pf->data = tf->data;
173 pf_rewind(pf);
c885c2ff 174 do {
3e2dd889 175 c = pf_getc(pf);
c885c2ff 176 if (c == EOF) {
177 sfree(rsc.text);
3e2dd889 178 error(err_pfeof, &tf->pos);
c885c2ff 179 return;
180 }
181 rdaddc(&rsc, c);
182 } while (c != 012 && c != 015);
183 p = rsc.text;
184 if ((p = strchr(p, ':')) == NULL) {
185 sfree(rsc.text);
3e2dd889 186 error(err_pfhead, &tf->pos);
44407fea 187 return;
c885c2ff 188 }
189 p++;
44407fea 190 p += strspn(p, " \t");
191 len = strcspn(p, " \t");
192 fontname = snewn(len + 1, char);
193 memcpy(fontname, p, len);
194 fontname[len] = 0;
c885c2ff 195 sfree(rsc.text);
196
44407fea 197 for (fi = all_fonts; fi; fi = fi->next) {
198 if (strcmp(fi->name, fontname) == 0) {
3e2dd889 199 fi->fontfile = tf;
babfe3e2 200 fi->filetype = TYPE1;
44407fea 201 sfree(fontname);
202 return;
203 }
204 }
3e2dd889 205 error(err_pfnoafm, &tf->pos, fontname);
44407fea 206 sfree(fontname);
207}
c885c2ff 208
209/*
210 * PostScript white space characters; PLRM3 table 3.1
211 */
212static int pf_isspace(int c) {
213 return c == 000 || c == 011 || c == 012 || c == 014 || c == 015 ||
214 c == ' ';
215}
216
217/*
218 * PostScript special characters; PLRM3 page 27
219 */
220static int pf_isspecial(int c) {
221 return c == '(' || c == ')' || c == '<' || c == '>' || c == '[' ||
222 c == ']' || c == '{' || c == '}' || c == '/' || c == '%';
223}
224
3e2dd889 225static size_t pf_findtoken(t1_font *tf, size_t off, char const *needle) {
c885c2ff 226 char *tok;
3e2dd889 227 pfstate pfs, *pf = &pfs;
c885c2ff 228
3e2dd889 229 pf->data = tf->data;
230 pf_seek(pf, off);
231 for (;;) {
232 tok = pf_read_token(pf);
233 if (tok == NULL) {
234 if (pf->offset == 0 && pf->curblock->type == PFB_BINARY)
235 pf->curblock = pf->curblock->next;
236 else
237 return (size_t)-1;
238 } else {
239 if (strcmp(tok, needle) == 0) {
240 sfree(tok);
241 return pf_tell(pf);
242 }
243 sfree(tok);
244 }
c885c2ff 245 }
3e2dd889 246}
247
248static size_t pf_length1(t1_font *tf) {
249 size_t ret;
250
251 ret = pf_findtoken(tf, 0, "eexec");
252 if (ret == (size_t)-1) {
253 error(err_pfeof, &tf->pos);
c885c2ff 254 return 0;
255 }
3e2dd889 256 return ret;
c885c2ff 257}
258
3e2dd889 259static size_t pf_length2(t1_font *tf) {
260 size_t ret;
261
262 if (tf->length1 == 0)
263 tf->length1 = pf_length1(tf);
264 ret = pf_findtoken(tf, tf->length1, "cleartomark");
265 if (ret == (size_t)-1) {
266 error(err_pfeof, &tf->pos);
267 return 0;
268 }
269 return ret - 12 - tf->length1; /* backspace over "cleartomark\n" */
270}
271
272static void pf_getascii(t1_font *tf, size_t off, size_t len,
273 char **bufp, size_t *lenp) {
274 t1_data *td = tf->data;
275 size_t blk, i;
276 char *p;
277
278 while (td && off >= td->length) {
279 off -= td->length;
280 td = td->next;
c885c2ff 281 }
3e2dd889 282 *bufp = NULL;
283 *lenp = 0;
284 while (td && len) {
285 blk = len < td->length ? len : td->length;
286 if (td->type == PFB_ASCII) {
287 *bufp = sresize(*bufp, *lenp + blk, char);
288 memcpy(*bufp + *lenp, td->data + off, blk);
289 *lenp += blk;
290 } else {
291 *bufp = sresize(*bufp, *lenp + blk * 2 + blk / 39 + 3, char);
292 p = *bufp + *lenp;
293 for (i = 0; i < blk; i++) {
294 if (i % 39 == 0) p += sprintf(p, "\n");
295 p += sprintf(p, "%02x", td->data[off + i]);
296 }
297 p += sprintf(p, "\n");
298 *lenp = p - *bufp;
299 }
300 len -= blk;
301 td = td->next;
302 off = 0;
303 }
304}
305
306void pf_writeps(font_info const *fi, FILE *ofp) {
307 char *buf;
308 size_t len;
309
310 pf_getascii(fi->fontfile, 0, INT_MAX, &buf, &len);
311 fwrite(buf, 1, len, ofp);
312 sfree(buf);
c885c2ff 313}
314
315static int hexval(char c) {
316 if (c >= '0' && c <= '9') return c - '0';
317 if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
318 if (c >= 'a' && c <= 'f') return c - 'a' + 0xa;
319 return 0;
320}
321
3e2dd889 322static void pf_getbinary(t1_font *tf, size_t off, size_t len,
323 char **bufp, size_t *lenp) {
324 t1_data *td = tf->data;
325 size_t blk, i;
326 int havenybble = 0;
327 char *p, nybble;
328
329 while (td && off >= td->length) {
330 off -= td->length;
331 td = td->next;
332 }
333 *bufp = NULL;
334 *lenp = 0;
335 while (td && len) {
336 blk = len < td->length ? len : td->length;
337 if (td->type == PFB_BINARY) {
338 *bufp = sresize(*bufp, *lenp + blk, char);
339 memcpy(*bufp + *lenp, td->data + off, blk);
340 *lenp += blk;
341 } else {
342 *bufp = sresize(*bufp, *lenp + blk / 2 + 1, char);
343 p = *bufp + *lenp;
344 for (i = 0; i < blk; i++) {
345 if (pf_isspace(td->data[off + i])) continue;
346 if (!havenybble)
347 nybble = hexval(td->data[off+i]);
348 else
349 *p++ = (nybble << 4) | hexval(td->data[off+i]);
350 havenybble = !havenybble;
351 }
352 *lenp = p - *bufp;
353 }
354 len -= blk;
355 td = td->next;
356 off = 0;
357 }
358}
359
360
361/*
362 * Return the initial, unencrypted, part of a font.
363 */
364void pf_part1(font_info *fi, char **bufp, size_t *lenp) {
365 t1_font *tf = fi->fontfile;
366
367 if (tf->length1 == 0)
368 tf->length1 = pf_length1(tf);
369 pf_getascii(tf, 0, tf->length1, bufp, lenp);
370}
371
c885c2ff 372/*
373 * Return the middle, encrypted, part of a font.
374 */
375void pf_part2(font_info *fi, char **bufp, size_t *lenp) {
3e2dd889 376 t1_font *tf = fi->fontfile;
c885c2ff 377
3e2dd889 378 if (tf->length2 == 0)
379 tf->length2 = pf_length2(tf);
380 pf_getbinary(tf, tf->length1, tf->length2, bufp, lenp);
381 if (*lenp >= 256)
382 *lenp -= 256;
c885c2ff 383}
384
3e2dd889 385static char *pf_read_litstring(pfstate *pf) {
c885c2ff 386 rdstringc rsc = { 0, 0, NULL };
387 int depth = 1;
388 int c;
389
390 rdaddc(&rsc, '(');
391 do {
3e2dd889 392 c = pf_getc(pf);
c885c2ff 393 switch (c) {
394 case '(':
395 depth++; break;
396 case ')':
397 depth--; break;
398 case '\\':
399 rdaddc(&rsc, '\\');
3e2dd889 400 c = pf_getc(pf);
c885c2ff 401 break;
402 }
403 if (c != EOF) rdaddc(&rsc, c);
404 } while (depth > 0 && c != EOF);
405 return rsc.text;
406}
407
3e2dd889 408static char *pf_read_hexstring(pfstate *pf) {
c885c2ff 409 rdstringc rsc = { 0, 0, NULL };
410 int c;
411
412 rdaddc(&rsc, '<');
413 do {
3e2dd889 414 c = pf_getc(pf);
c885c2ff 415 if (c != EOF) rdaddc(&rsc, c);
416 } while (c != '>' && c != EOF);
417 return rsc.text;
418}
419
3e2dd889 420static char *pf_read_word(pfstate *pf, int c) {
c885c2ff 421 rdstringc rsc = { 0, 0, NULL };
422
423 rdaddc(&rsc, c);
424 if (c == '{' || c == '}' || c == '[' || c == ']')
425 return rsc.text;
426 for (;;) {
3e2dd889 427 c = pf_getc(pf);
c885c2ff 428 if (pf_isspecial(c) || pf_isspace(c) || c == EOF) break;
429 rdaddc(&rsc, c);
430 }
3e2dd889 431 if (pf_isspecial(c)) pf_ungetc(c, pf);
c885c2ff 432 return rsc.text;
433}
434
3e2dd889 435static char *pf_read_token(pfstate *pf) {
c885c2ff 436 int c;
437
438 do {
3e2dd889 439 c = pf_getc(pf);
c885c2ff 440 } while (pf_isspace(c));
441 if (c == EOF) return NULL;
442 if (c == '%') {
443 do {
3e2dd889 444 c = pf_getc(pf);
c885c2ff 445 } while (c != 012 && c != 015);
3e2dd889 446 return pf_read_token(pf);
c885c2ff 447 }
3e2dd889 448 if (c == '(') return pf_read_litstring(pf);
449 if (c == '<') return pf_read_hexstring(pf);
450 return pf_read_word(pf, c);
c885c2ff 451}