530 lines
12 KiB
C
530 lines
12 KiB
C
/* 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.
|
|
**
|
|
** author: I. Lee Hetherington (ilh@lcs.mit.edu)
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] =
|
|
"@(#) t1asm.c 1.2 10:09:46 5/22/92";
|
|
static char copyright[] =
|
|
"@(#) Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.";
|
|
#endif
|
|
|
|
/* Note: this is ANSI C. */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef MSDOS
|
|
#define WB "wb"
|
|
#else
|
|
#define WB "w"
|
|
#endif
|
|
|
|
#define BANNER "This is t1asm 1.2.\n"
|
|
#define LINESIZE 256
|
|
|
|
#define MAXBLOCKLEN ((1<<17)-6)
|
|
#define MINBLOCKLEN ((1<<8)-6)
|
|
|
|
#define MARKER 128
|
|
#define ASCII 1
|
|
#define BINARY 2
|
|
#define DONE 3
|
|
|
|
typedef unsigned char byte;
|
|
|
|
static FILE *ifp = stdin;
|
|
static FILE *ofp = stdout;
|
|
|
|
/* flags */
|
|
static int pfb = 0;
|
|
static int active = 0;
|
|
static int start_charstring = 0;
|
|
static int in_eexec = 0;
|
|
|
|
static char line[LINESIZE + 1];
|
|
|
|
/* 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 int blocklen = MAXBLOCKLEN;
|
|
static int blockpos = -1;
|
|
static int blocktyp = ASCII;
|
|
|
|
/* decryption stuff */
|
|
static unsigned short er, cr;
|
|
static unsigned short 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 },
|
|
{ "escape", 12, -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 decryption functions because eexec and charstring decryption
|
|
must proceed in parallel. */
|
|
|
|
static byte eencrypt(byte plain)
|
|
{
|
|
byte cipher;
|
|
|
|
cipher = (plain ^ (er >> 8));
|
|
er = (cipher + er) * c1 + c2;
|
|
return cipher;
|
|
}
|
|
|
|
static byte cencrypt(byte plain)
|
|
{
|
|
byte cipher;
|
|
|
|
cipher = (plain ^ (cr >> 8));
|
|
cr = (cipher + cr) * c1 + c2;
|
|
return cipher;
|
|
}
|
|
|
|
/* This function flushes a buffered PFB block. */
|
|
|
|
static void output_block()
|
|
{
|
|
int i;
|
|
|
|
/* output four-byte block length */
|
|
fputc(blockpos & 0xff, ofp);
|
|
fputc((blockpos >> 8) & 0xff, ofp);
|
|
fputc((blockpos >> 16) & 0xff, ofp);
|
|
fputc((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 and writes ASCII trailer.
|
|
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 < 7; i++) {
|
|
for (j = 0; j < 64; j++)
|
|
eexec_byte('0');
|
|
eexec_byte('\n');
|
|
}
|
|
eexec_string("cleartomark\n");
|
|
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 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(v)
|
|
int v;
|
|
{
|
|
byte b = ((unsigned int)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 %s ", charstring_bp - charstring_buf, 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", cp->name);
|
|
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);
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *p, *q, *r;
|
|
int c;
|
|
|
|
extern char *optarg;
|
|
extern int optind;
|
|
extern int getopt(int argc, char **argv, char *optstring);
|
|
|
|
fprintf(stderr, "%s", 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], WB);
|
|
if (!ofp) {
|
|
fprintf(stderr, "error: cannot open %s for writing\n", argv[2]);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* 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)) {
|
|
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 (start_charstring) {
|
|
if (!cs_start[0]) {
|
|
fprintf(stderr, "error: couldn't find charstring start command\n");
|
|
exit(1);
|
|
}
|
|
parse_charstring();
|
|
}
|
|
}
|
|
eexec_end();
|
|
|
|
fclose(ifp);
|
|
fclose(ofp);
|
|
|
|
return 0;
|
|
}
|
|
|