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