Add an error check for correct formatting in Deflate uncompressed
[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 }
6c64aedd 74 tail->next = NULL;
3e2dd889 75}
76
77static 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;
6c64aedd 94 ret->next = NULL;
3e2dd889 95 return ret;
96}
c885c2ff 97
44407fea 98void read_pfa_file(input *in) {
3e2dd889 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
108void 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}
117static char *pf_read_token(pfstate *);
118
119/*
120 * Read a character from the initial plaintext part of a Type 1 font
121 */
122static 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
132static 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
138static void pf_rewind(pfstate *pf) {
139 pf->curblock = pf->data;
140 pf->offset = 0;
141}
142
143static 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
154static 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
165static void pf_identify(t1_font *tf) {
c885c2ff 166 rdstringc rsc = { 0, 0, NULL };
167 char *p;
44407fea 168 size_t len;
169 char *fontname;
170 font_info *fi;
c885c2ff 171 int c;
3e2dd889 172 pfstate pfs, *pf = &pfs;
44407fea 173
3e2dd889 174 pf->data = tf->data;
175 pf_rewind(pf);
c885c2ff 176 do {
3e2dd889 177 c = pf_getc(pf);
c885c2ff 178 if (c == EOF) {
179 sfree(rsc.text);
3e2dd889 180 error(err_pfeof, &tf->pos);
c885c2ff 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);
3e2dd889 188 error(err_pfhead, &tf->pos);
44407fea 189 return;
c885c2ff 190 }
191 p++;
44407fea 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;
c885c2ff 197 sfree(rsc.text);
198
44407fea 199 for (fi = all_fonts; fi; fi = fi->next) {
200 if (strcmp(fi->name, fontname) == 0) {
3e2dd889 201 fi->fontfile = tf;
babfe3e2 202 fi->filetype = TYPE1;
44407fea 203 sfree(fontname);
204 return;
205 }
206 }
3e2dd889 207 error(err_pfnoafm, &tf->pos, fontname);
44407fea 208 sfree(fontname);
209}
c885c2ff 210
211/*
212 * PostScript white space characters; PLRM3 table 3.1
213 */
214static 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 */
222static int pf_isspecial(int c) {
223 return c == '(' || c == ')' || c == '<' || c == '>' || c == '[' ||
224 c == ']' || c == '{' || c == '}' || c == '/' || c == '%';
225}
226
3e2dd889 227static size_t pf_findtoken(t1_font *tf, size_t off, char const *needle) {
c885c2ff 228 char *tok;
3e2dd889 229 pfstate pfs, *pf = &pfs;
c885c2ff 230
3e2dd889 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 }
c885c2ff 247 }
3e2dd889 248}
249
250static 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 error(err_pfeof, &tf->pos);
c885c2ff 256 return 0;
257 }
3e2dd889 258 return ret;
c885c2ff 259}
260
3e2dd889 261static 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 error(err_pfeof, &tf->pos);
269 return 0;
270 }
271 return ret - 12 - tf->length1; /* backspace over "cleartomark\n" */
272}
273
274static 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;
c885c2ff 283 }
3e2dd889 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
308void 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);
c885c2ff 315}
316
317static 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
3e2dd889 324static 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 */
366void 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
c885c2ff 374/*
375 * Return the middle, encrypted, part of a font.
376 */
377void pf_part2(font_info *fi, char **bufp, size_t *lenp) {
3e2dd889 378 t1_font *tf = fi->fontfile;
c885c2ff 379
3e2dd889 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;
c885c2ff 385}
386
3e2dd889 387static char *pf_read_litstring(pfstate *pf) {
c885c2ff 388 rdstringc rsc = { 0, 0, NULL };
389 int depth = 1;
390 int c;
391
392 rdaddc(&rsc, '(');
393 do {
3e2dd889 394 c = pf_getc(pf);
c885c2ff 395 switch (c) {
396 case '(':
397 depth++; break;
398 case ')':
399 depth--; break;
400 case '\\':
401 rdaddc(&rsc, '\\');
3e2dd889 402 c = pf_getc(pf);
c885c2ff 403 break;
404 }
405 if (c != EOF) rdaddc(&rsc, c);
406 } while (depth > 0 && c != EOF);
407 return rsc.text;
408}
409
3e2dd889 410static char *pf_read_hexstring(pfstate *pf) {
c885c2ff 411 rdstringc rsc = { 0, 0, NULL };
412 int c;
413
414 rdaddc(&rsc, '<');
415 do {
3e2dd889 416 c = pf_getc(pf);
c885c2ff 417 if (c != EOF) rdaddc(&rsc, c);
418 } while (c != '>' && c != EOF);
419 return rsc.text;
420}
421
3e2dd889 422static char *pf_read_word(pfstate *pf, int c) {
c885c2ff 423 rdstringc rsc = { 0, 0, NULL };
424
425 rdaddc(&rsc, c);
426 if (c == '{' || c == '}' || c == '[' || c == ']')
427 return rsc.text;
428 for (;;) {
3e2dd889 429 c = pf_getc(pf);
c885c2ff 430 if (pf_isspecial(c) || pf_isspace(c) || c == EOF) break;
431 rdaddc(&rsc, c);
432 }
3e2dd889 433 if (pf_isspecial(c)) pf_ungetc(c, pf);
c885c2ff 434 return rsc.text;
435}
436
3e2dd889 437static char *pf_read_token(pfstate *pf) {
c885c2ff 438 int c;
439
440 do {
3e2dd889 441 c = pf_getc(pf);
c885c2ff 442 } while (pf_isspace(c));
443 if (c == EOF) return NULL;
444 if (c == '%') {
445 do {
3e2dd889 446 c = pf_getc(pf);
c885c2ff 447 } while (c != 012 && c != 015);
3e2dd889 448 return pf_read_token(pf);
c885c2ff 449 }
3e2dd889 450 if (c == '(') return pf_read_litstring(pf);
451 if (c == '<') return pf_read_hexstring(pf);
452 return pf_read_word(pf, c);
c885c2ff 453}