+
+/*
+ * PostScript white space characters; PLRM3 table 3.1
+ */
+static int pf_isspace(int c) {
+ return c == 000 || c == 011 || c == 012 || c == 014 || c == 015 ||
+ c == ' ';
+}
+
+/*
+ * PostScript special characters; PLRM3 page 27
+ */
+static int pf_isspecial(int c) {
+ return c == '(' || c == ')' || c == '<' || c == '>' || c == '[' ||
+ c == ']' || c == '{' || c == '}' || c == '/' || c == '%';
+}
+
+static size_t pf_findtoken(t1_font *tf, size_t off, char const *needle) {
+ char *tok;
+ pfstate pfs, *pf = &pfs;
+
+ pf->data = tf->data;
+ pf_seek(pf, off);
+ for (;;) {
+ tok = pf_read_token(pf);
+ if (tok == NULL) {
+ if (pf->offset == 0 && pf->curblock->type == PFB_BINARY)
+ pf->curblock = pf->curblock->next;
+ else
+ return (size_t)-1;
+ } else {
+ if (strcmp(tok, needle) == 0) {
+ sfree(tok);
+ return pf_tell(pf);
+ }
+ sfree(tok);
+ }
+ }
+}
+
+static size_t pf_length1(t1_font *tf) {
+ size_t ret;
+
+ ret = pf_findtoken(tf, 0, "eexec");
+ if (ret == (size_t)-1) {
+ error(err_pfeof, &tf->pos);
+ return 0;
+ }
+ return ret;
+}
+
+static size_t pf_length2(t1_font *tf) {
+ size_t ret;
+
+ if (tf->length1 == 0)
+ tf->length1 = pf_length1(tf);
+ ret = pf_findtoken(tf, tf->length1, "cleartomark");
+ if (ret == (size_t)-1) {
+ error(err_pfeof, &tf->pos);
+ return 0;
+ }
+ return ret - 12 - tf->length1; /* backspace over "cleartomark\n" */
+}
+
+static void pf_getascii(t1_font *tf, size_t off, size_t len,
+ char **bufp, size_t *lenp) {
+ t1_data *td = tf->data;
+ size_t blk, i;
+ char *p;
+
+ while (td && off >= td->length) {
+ off -= td->length;
+ td = td->next;
+ }
+ *bufp = NULL;
+ *lenp = 0;
+ while (td && len) {
+ blk = len < td->length ? len : td->length;
+ if (td->type == PFB_ASCII) {
+ *bufp = sresize(*bufp, *lenp + blk, char);
+ memcpy(*bufp + *lenp, td->data + off, blk);
+ *lenp += blk;
+ } else {
+ *bufp = sresize(*bufp, *lenp + blk * 2 + blk / 39 + 3, char);
+ p = *bufp + *lenp;
+ for (i = 0; i < blk; i++) {
+ if (i % 39 == 0) p += sprintf(p, "\n");
+ p += sprintf(p, "%02x", td->data[off + i]);
+ }
+ p += sprintf(p, "\n");
+ *lenp = p - *bufp;
+ }
+ len -= blk;
+ td = td->next;
+ off = 0;
+ }
+}
+
+void pf_writeps(font_info const *fi, FILE *ofp) {
+ char *buf;
+ size_t len;
+
+ pf_getascii(fi->fontfile, 0, INT_MAX, &buf, &len);
+ fwrite(buf, 1, len, ofp);
+ sfree(buf);
+}
+
+static int hexval(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
+ if (c >= 'a' && c <= 'f') return c - 'a' + 0xa;
+ return 0;
+}
+
+static void pf_getbinary(t1_font *tf, size_t off, size_t len,
+ char **bufp, size_t *lenp) {
+ t1_data *td = tf->data;
+ size_t blk, i;
+ int havenybble = 0;
+ char *p, nybble;
+
+ while (td && off >= td->length) {
+ off -= td->length;
+ td = td->next;
+ }
+ *bufp = NULL;
+ *lenp = 0;
+ while (td && len) {
+ blk = len < td->length ? len : td->length;
+ if (td->type == PFB_BINARY) {
+ *bufp = sresize(*bufp, *lenp + blk, char);
+ memcpy(*bufp + *lenp, td->data + off, blk);
+ *lenp += blk;
+ } else {
+ *bufp = sresize(*bufp, *lenp + blk / 2 + 1, char);
+ p = *bufp + *lenp;
+ for (i = 0; i < blk; i++) {
+ if (pf_isspace(td->data[off + i])) continue;
+ if (!havenybble)
+ nybble = hexval(td->data[off+i]);
+ else
+ *p++ = (nybble << 4) | hexval(td->data[off+i]);
+ havenybble = !havenybble;
+ }
+ *lenp = p - *bufp;
+ }
+ len -= blk;
+ td = td->next;
+ off = 0;
+ }
+}
+
+
+/*
+ * Return the initial, unencrypted, part of a font.
+ */
+void pf_part1(font_info *fi, char **bufp, size_t *lenp) {
+ t1_font *tf = fi->fontfile;
+
+ if (tf->length1 == 0)
+ tf->length1 = pf_length1(tf);
+ pf_getascii(tf, 0, tf->length1, bufp, lenp);
+}
+
+/*
+ * Return the middle, encrypted, part of a font.
+ */
+void pf_part2(font_info *fi, char **bufp, size_t *lenp) {
+ t1_font *tf = fi->fontfile;
+
+ if (tf->length2 == 0)
+ tf->length2 = pf_length2(tf);
+ pf_getbinary(tf, tf->length1, tf->length2, bufp, lenp);
+ if (*lenp >= 256)
+ *lenp -= 256;
+}
+
+static char *pf_read_litstring(pfstate *pf) {
+ rdstringc rsc = { 0, 0, NULL };
+ int depth = 1;
+ int c;
+
+ rdaddc(&rsc, '(');
+ do {
+ c = pf_getc(pf);
+ switch (c) {
+ case '(':
+ depth++; break;
+ case ')':
+ depth--; break;
+ case '\\':
+ rdaddc(&rsc, '\\');
+ c = pf_getc(pf);
+ break;
+ }
+ if (c != EOF) rdaddc(&rsc, c);
+ } while (depth > 0 && c != EOF);
+ return rsc.text;
+}
+
+static char *pf_read_hexstring(pfstate *pf) {
+ rdstringc rsc = { 0, 0, NULL };
+ int c;
+
+ rdaddc(&rsc, '<');
+ do {
+ c = pf_getc(pf);
+ if (c != EOF) rdaddc(&rsc, c);
+ } while (c != '>' && c != EOF);
+ return rsc.text;
+}
+
+static char *pf_read_word(pfstate *pf, int c) {
+ rdstringc rsc = { 0, 0, NULL };
+
+ rdaddc(&rsc, c);
+ if (c == '{' || c == '}' || c == '[' || c == ']')
+ return rsc.text;
+ for (;;) {
+ c = pf_getc(pf);
+ if (pf_isspecial(c) || pf_isspace(c) || c == EOF) break;
+ rdaddc(&rsc, c);
+ }
+ if (pf_isspecial(c)) pf_ungetc(c, pf);
+ return rsc.text;
+}
+
+static char *pf_read_token(pfstate *pf) {
+ int c;
+
+ do {
+ c = pf_getc(pf);
+ } while (pf_isspace(c));
+ if (c == EOF) return NULL;
+ if (c == '%') {
+ do {
+ c = pf_getc(pf);
+ } while (c != 012 && c != 015);
+ return pf_read_token(pf);
+ }
+ if (c == '(') return pf_read_litstring(pf);
+ if (c == '<') return pf_read_hexstring(pf);
+ return pf_read_word(pf, c);
+}