/* 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 #include #include #endif #include #include #include #include #include #ifdef WINDOWS # ifdef STANDALONE # define WINDOWS_FUNCTIONS # include "winport.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 __cdecl 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; }