+/* t1asm
+ *
+ * This program `assembles' Adobe Type-1 font programs in pseudo-PostScript
+ * form into either PFB or PFA format. The human readable/editable input is
+ * charstring- and eexec-encrypted as specified in the `Adobe Type 1 Font
+ * Format' version 1.1 (the `black book'). There is a companion program,
+ * t1disasm, which `disassembles' PFB and PFA files into a pseudo-PostScript
+ * file.
+ *
+ * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.
+ *
+ * Permission is hereby granted to use, modify, and distribute this program
+ * for any purpose provided this copyright notice and the one below remain
+ * intact.
+ *
+ * I. Lee Hetherington (ilh@lcs.mit.edu)
+ *
+ * Revision 1.2 92/05/22 11:54:45 ilh
+ * Fixed bug where integers larger than 32000 could not be encoded in
+ * charstrings. Now integer range is correct for four-byte
+ * twos-complement integers: -(1<<31) <= i <= (1<<31)-1. Bug detected by
+ * Piet Tutelaers (rcpt@urc.tue.nl).
+ *
+ * Revision 1.1 92/05/22 11:48:46 ilh
+ * initial version
+ *
+ * Ported to Microsoft C/C++ Compiler and MS-DOS operating system by
+ * Kai-Uwe Herbing (herbing@netmbx.netmbx.de) on June 12, 1992. Code
+ * specific to the MS-DOS version is encapsulated with #ifdef _MSDOS
+ * ... #endif, where _MSDOS is an identifier, which is automatically
+ * defined, if you compile with the Microsoft C/C++ Compiler.
+ *
+ */
+
+#ifndef lint
+static char copyright[] =
+ "@(#) Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.";
+#ifdef _MSDOS
+static char portnotice[] =
+ "@(#) Ported to MS-DOS by Kai-Uwe Herbing (herbing@netmbx.netmbx.de).";
+#endif
+#endif
+
+/* Note: this is ANSI C. */
+
+#ifdef _MSDOS
+ #include <fcntl.h>
+ #include <getopt.h>
+ #include <io.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#ifdef WINDOWS
+# ifdef STANDALONE
+# define WINDOWS_FUNCTIONS
+# include "windows.h"
+# endif
+#endif
+
+/* int32 must be at least 32-bit and uint16 must be at least 16-bit */
+#if INT_MAX >= 0x7FFFFFFFUL
+typedef int int32;
+#else
+typedef long int32;
+#endif
+#if USHRT_MAX >= 0xFFFFUL
+typedef unsigned short uint16;
+#else
+typedef unsigned int uint16;
+#endif
+
+#define LINESIZE 256
+
+#define MAXBLOCKLEN ((1L<<17)-6)
+#define MINBLOCKLEN ((1L<<8)-6)
+
+#define MARKER 128
+#define ASCII 1
+#define BINARY 2
+#define DONE 3
+
+typedef unsigned char byte;
+
+/* must be visible from outside */
+FILE *ifp;
+FILE *ofp;
+
+/* flags */
+static int pfb = 0;
+static int active = 0;
+static int start_charstring = 0;
+static int in_eexec = 0;
+
+static char line[LINESIZE];
+
+/* lenIV and charstring start command */
+static int lenIV = 4;
+static char cs_start[10];
+
+/* for charstring buffering */
+static byte charstring_buf[65535];
+static byte *charstring_bp;
+
+/* for PFB block buffering */
+static byte blockbuf[MAXBLOCKLEN];
+static int32 blocklen = MAXBLOCKLEN;
+static int32 blockpos = -1;
+static int blocktyp = ASCII;
+
+/* decryption stuff */
+static uint16 er, cr;
+static uint16 c1 = 52845, c2 = 22719;
+
+/* table of charstring commands */
+static struct command {
+ char *name;
+ int one, two;
+} command_table[] = {
+ { "callothersubr", 12, 16 },
+ { "callsubr", 10, -1 },
+ { "closepath", 9, -1 },
+ { "div", 12, 12 },
+ { "dotsection", 12, 0 },
+ { "endchar", 14, -1 },
+ { "hlineto", 6, -1 },
+ { "hmoveto", 22, -1 },
+ { "hsbw", 13, -1 },
+ { "hstem", 1, -1 },
+ { "hstem3", 12, 2 },
+ { "hvcurveto", 31, -1 },
+ { "pop", 12, 17 },
+ { "return", 11, -1 },
+ { "rlineto", 5, -1 },
+ { "rmoveto", 21, -1 },
+ { "rrcurveto", 8, -1 },
+ { "sbw", 12, 7 },
+ { "seac", 12, 6 },
+ { "setcurrentpoint", 12, 33 },
+ { "vhcurveto", 30, -1 },
+ { "vlineto", 7, -1 },
+ { "vmoveto", 4, -1 },
+ { "vstem", 3, -1 },
+ { "vstem3", 12, 1 },
+}; /* alphabetical */
+
+/* Two separate encryption functions because eexec and charstring encryption
+ must proceed in parallel. */
+
+static byte eencrypt(byte plain)
+{
+ byte cipher;
+
+ cipher = (byte) (plain ^ (er >> 8));
+ er = (uint16) ((cipher + er) * c1 + c2);
+ return cipher;
+}
+
+static byte cencrypt(byte plain)
+{
+ byte cipher;
+
+ cipher = (byte) (plain ^ (cr >> 8));
+ cr = (uint16) ((cipher + cr) * c1 + c2);
+ return cipher;
+}
+
+/* This function flushes a buffered PFB block. */
+
+static void output_block()
+{
+ int32 i;
+
+ /* output four-byte block length */
+ fputc((int) (blockpos & 0xff), ofp);
+ fputc((int) ((blockpos >> 8) & 0xff), ofp);
+ fputc((int) ((blockpos >> 16) & 0xff), ofp);
+ fputc((int) ((blockpos >> 24) & 0xff), ofp);
+
+ /* output block data */
+ for (i = 0; i < blockpos; i++)
+ fputc(blockbuf[i], ofp);
+
+ /* mark block buffer empty and uninitialized */
+ blockpos = -1;
+}
+
+/* This function outputs a single byte. If output is in PFB format then output
+ is buffered through blockbuf[]. If output is in PFA format, then output
+ will be hexadecimal if in_eexec is set, ASCII otherwise. */
+
+static void output_byte(byte b)
+{
+ static char *hexchar = "0123456789ABCDEF";
+ static int hexcol = 0;
+
+ if (pfb) {
+ /* PFB */
+ if (blockpos < 0) {
+ fputc(MARKER, ofp);
+ fputc(blocktyp, ofp);
+ blockpos = 0;
+ }
+ blockbuf[blockpos++] = b;
+ if (blockpos == blocklen)
+ output_block();
+ } else {
+ /* PFA */
+ if (in_eexec) {
+ /* trim hexadecimal lines to 64 columns */
+ if (hexcol >= 64) {
+ fputc('\n', ofp);
+ hexcol = 0;
+ }
+ fputc(hexchar[(b >> 4) & 0xf], ofp);
+ fputc(hexchar[b & 0xf], ofp);
+ hexcol += 2;
+ } else {
+ fputc(b, ofp);
+ }
+ }
+}
+
+/* This function outputs a byte through possible eexec encryption. */
+
+static void eexec_byte(byte b)
+{
+ if (in_eexec)
+ output_byte(eencrypt(b));
+ else
+ output_byte(b);
+}
+
+/* This function outputs a null-terminated string through possible eexec
+ encryption. */
+
+static void eexec_string(char *string)
+{
+ while (*string)
+ eexec_byte((byte) *string++);
+}
+
+/* This function gets ready for the eexec-encrypted data. If output is in
+ PFB format then flush current ASCII block and get ready for binary block.
+ We start encryption with four random (zero) bytes. */
+
+static void eexec_start()
+{
+ eexec_string(line);
+ if (pfb) {
+ output_block();
+ blocktyp = BINARY;
+ }
+
+ in_eexec = 1;
+ er = 55665;
+ eexec_byte(0);
+ eexec_byte(0);
+ eexec_byte(0);
+ eexec_byte(0);
+}
+
+/* This function wraps-up the eexec-encrypted data.
+ If output is in PFB format then this entails flushing binary block and
+ starting an ASCII block. */
+
+static void eexec_end()
+{
+ int i, j;
+
+ if (pfb) {
+ output_block();
+ blocktyp = ASCII;
+ } else {
+ fputc('\n', ofp);
+ }
+ in_eexec = 0;
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 64; j++)
+ eexec_byte('0');
+ eexec_byte('\n');
+ }
+#if 0
+ eexec_string("cleartomark\n");
+#endif
+}
+
+/* This function writes ASCII trailer.
+ If output is in PFB format then this entails flushing binary block and
+ starting an ASCII block. */
+
+static void file_end()
+{
+ if (pfb) {
+ output_block();
+ fputc(MARKER, ofp);
+ fputc(DONE, ofp);
+ }
+}
+/* This function returns an input line of characters. A line is terminated by
+ length (including terminating null) greater than LINESIZE, a newline \n, or
+ when active (looking for charstrings) by '{'. When terminated by a newline
+ the newline is put into line[]. When terminated by '{', the '{' is not put
+ into line[], and the flag start_charstring is set to 1. */
+
+static void t1asm_getline()
+{
+ int c;
+ char *p = line;
+ int comment = 0;
+
+ start_charstring = 0;
+ while (p < line + LINESIZE) {
+ c = fgetc(ifp);
+ if (c == EOF)
+ break;
+ if (c == '%')
+ comment = 1;
+ if (active && !comment && c == '{') {
+ start_charstring = 1;
+ break;
+ }
+ *p++ = (char) c;
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+}
+
+/* This function is used by the binary search, bsearch(), for command names in
+ the command table. */
+
+static int command_compare(const void *key, const void *item)
+{
+ return strcmp((char *) key, ((struct command *) item)->name);
+}
+
+/* This function returns 1 if the string is an integer and 0 otherwise. */
+
+static int is_integer(char *string)
+{
+ if (isdigit(string[0]) || string[0] == '-' || string[0] == '+') {
+ while (*++string && isdigit(*string))
+ ; /* deliberately empty */
+ if (!*string)
+ return 1;
+ }
+ return 0;
+}
+
+/* This function initializes charstring encryption. Note that this is called
+ at the beginning of every charstring. */
+
+static void charstring_start()
+{
+ int i;
+
+ charstring_bp = charstring_buf;
+ cr = 4330;
+ for (i = 0; i < lenIV; i++)
+ *charstring_bp++ = cencrypt((byte) 0);
+}
+
+/* This function encrypts and buffers a single byte of charstring data. */
+
+static void charstring_byte(int v)
+{
+ byte b = (byte) (v & 0xff);
+
+ if (charstring_bp - charstring_buf > sizeof(charstring_buf)) {
+ fprintf(stderr, "error: charstring_buf full (%d bytes)\n",
+ sizeof(charstring_buf));
+ exit(1);
+ }
+ *charstring_bp++ = cencrypt(b);
+}
+
+/* This function outputs buffered, encrypted charstring data through possible
+ eexec encryption. */
+
+static void charstring_end()
+{
+ byte *bp;
+
+ sprintf(line, "%d ", charstring_bp - charstring_buf);
+ eexec_string(line);
+ sprintf(line, "%s ", cs_start);
+ eexec_string(line);
+ for (bp = charstring_buf; bp < charstring_bp; bp++)
+ eexec_byte(*bp);
+}
+
+/* This function generates the charstring representation of an integer. */
+
+static void charstring_int(int num)
+{
+ int x;
+
+ if (num >= -107 && num <= 107) {
+ charstring_byte(num + 139);
+ } else if (num >= 108 && num <= 1131) {
+ x = num - 108;
+ charstring_byte(x / 256 + 247);
+ charstring_byte(x % 256);
+ } else if (num >= -1131 && num <= -108) {
+ x = abs(num) - 108;
+ charstring_byte(x / 256 + 251);
+ charstring_byte(x % 256);
+ } else if (num >= (-2147483647-1) && num <= 2147483647) {
+ charstring_byte(255);
+ charstring_byte(num >> 24);
+ charstring_byte(num >> 16);
+ charstring_byte(num >> 8);
+ charstring_byte(num);
+ } else {
+ fprintf(stderr,
+ "error: cannot format the integer %d, too large\n", num);
+ exit(1);
+ }
+}
+
+/* This function parses an entire charstring into integers and commands,
+ outputting bytes through the charstring buffer. */
+
+static void parse_charstring()
+{
+ struct command *cp;
+
+ charstring_start();
+ while (fscanf(ifp, "%s", line) == 1) {
+ if (line[0] == '%') {
+ /* eat comment to end of line */
+ while (fgetc(ifp) != '\n' && !feof(ifp))
+ ; /* deliberately empty */
+ continue;
+ }
+ if (line[0] == '}')
+ break;
+ if (is_integer(line)) {
+ charstring_int(atoi(line));
+ } else {
+ cp = (struct command *)
+ bsearch((void *) line, (void *) command_table,
+ sizeof(command_table) / sizeof(struct command),
+ sizeof(struct command),
+ command_compare);
+ if (cp) {
+ charstring_byte(cp->one);
+ if (cp->two >= 0)
+ charstring_byte(cp->two);
+ } else {
+ fprintf(stderr, "error: cannot use `%s' in charstring\n",line);
+ exit(1);
+ }
+ }
+ }
+ charstring_end();
+}
+
+static void usage()
+{
+ fprintf(stderr,
+ "usage: t1asm [-b] [-l block-length] [input [output]]\n");
+ fprintf(stderr,
+ "\n-b means output in PFB format, otherwise PFA format.\n");
+ fprintf(stderr,
+ "The block length applies to the length of blocks in the\n");
+ fprintf(stderr,
+ "PFB output file; the default is to use the largest possible.\n");
+ exit(1);
+}
+
+static void print_banner()
+{
+ static char rcs_revision[] = ""; /* removed RCS */
+ static char revision[20];
+
+ if (sscanf(rcs_revision, "$Revision: %19s", revision) != 1)
+ revision[0] = '\0';
+ fprintf(stderr, "This is t1asm %s.\n", revision);
+}
+
+#ifdef STANDALONE
+int main(int argc, char **argv)
+{
+ char *p, *q, *r;
+ int c;
+
+ extern char *optarg;
+ extern int optind;
+
+ ifp = stdin;
+ ofp = stdout;
+
+ print_banner();
+
+ /* interpret command line arguments using getopt */
+ while ((c = getopt(argc, argv, "bl:")) != -1)
+ switch (c) {
+ case 'b':
+ pfb = 1;
+ break;
+ case 'l':
+ blocklen = atoi(optarg);
+ if (blocklen < MINBLOCKLEN) {
+ blocklen = MINBLOCKLEN;
+ fprintf(stderr,
+ "warning: using minimum block length of %d\n",
+ blocklen);
+ } else if (blocklen > MAXBLOCKLEN) {
+ blocklen = MAXBLOCKLEN;
+ fprintf(stderr,
+ "warning: using maximum block length of %d\n",
+ blocklen);
+ }
+ break;
+ default:
+ usage();
+ break;
+ }
+ if (argc - optind > 2)
+ usage();
+
+ /* possibly open input & output files */
+ if (argc - optind >= 1) {
+ ifp = fopen(argv[optind], "r");
+ if (!ifp) {
+ fprintf(stderr, "error: cannot open %s for reading\n", argv[1]);
+ exit(1);
+ }
+ }
+ if (argc - optind >= 2) {
+ ofp = fopen(argv[optind + 1], "w");
+ if (!ofp) {
+ fprintf(stderr, "error: cannot open %s for writing\n", argv[2]);
+ exit(1);
+ }
+ }
+
+#else
+int runt1asm(int pfbflag)
+{
+ char *p, *q, *r;
+
+ pfb = pfbflag;
+#endif
+
+ #ifdef _MSDOS
+ /* If we are processing a PFB (binary) output */
+ /* file, we must set its file mode to binary. */
+ if (pfb)
+ _setmode(_fileno(ofp), _O_BINARY);
+ #endif
+
+ /* Finally, we loop until no more input. Some special things to look for
+ are the `currentfile eexec' line, the beginning of the `/Subrs'
+ definition, the definition of `/lenIV', and the definition of the
+ charstring start command which has `...string currentfile...' in it. */
+
+ while (!feof(ifp) && !ferror(ifp)) {
+ t1asm_getline();
+ if (strcmp(line, "currentfile eexec\n") == 0) {
+ eexec_start();
+ continue;
+ } else if (strstr(line, "/Subrs") && isspace(line[6])) {
+ active = 1;
+ } else if ((p = strstr(line, "/lenIV"))) {
+ sscanf(p, "%*s %d", &lenIV);
+ } else if ((p = strstr(line, "string currentfile"))) {
+ /* locate the name of the charstring start command */
+ *p = '\0'; /* damage line[] */
+ q = strrchr(line, '/');
+ if (q) {
+ r = cs_start;
+ ++q;
+ while (!isspace(*q) && *q != '{')
+ *r++ = *q++;
+ *r = '\0';
+ }
+ *p = 's'; /* repair line[] */
+ }
+ /* output line data */
+ eexec_string(line);
+ if ((p = strstr(line, "currentfile closefile"))) {
+ eexec_end();
+ }
+ if (start_charstring) {
+ if (!cs_start[0]) {
+ fprintf(stderr, "error: couldn't find charstring start command\n");
+ exit(1);
+ }
+ parse_charstring();
+ }
+ }
+ file_end();
+
+ fclose(ifp);
+ fclose(ofp);
+
+ return 0;
+}