[comp.lang.postscript] More Mac PS problems - ET++ documentation

esink@turia.dit.upm.es (06/05/91)

Our printers are LaserWriters.  I'm trying to print the documentation
for ET++.  As distributed, it is a MS Word document, but a postscript
version exists on apple.com.  It was apparently created using command-K
or something similar.

I've tried just printing the postscript file using lpr, which is how
I print most any other ps file.  Only 9 pages come out (of 34) and the
printer hangs.  In addition, footnotes appear in what I am guessing is
Geneva (ultra ugly, whatever it is).  Furthermore, although the
document starts out in Helvetica, on page 4 it suddenly decides to 
switch the whole document into that same ugly Geneva.

OK, I applied the techniques given by macps22 and replaced the
md dictionary with the one obtained from prepfix.  Exactly
the same results.

Why does this document make my printer barf ?  The ET++ paper looks
real good, at least the first 9 pages do anyway.  I'd love to see
the rest...

Thanks in advance,

Eric

Eric W. Sink                     | "If no one is criticizing |Opinions
Departamento de Telematica       | your work, it is possible |mine -
Universidad Politecnica de Madrid| that you are not doing    |all of
esink@turia.dit.upm.es           | anything." -George Verwer |them.

gillies@m.cs.uiuc.edu (Don Gillies) (06/08/91)

Our imagen printers used to crash all the time on long macintosh
postscript files (we had a very early version of the ultrascript
interpreter).  Eventually, I wrote a piece of software called "pp.c",
"Postscript Processor" to extract individual pages from a postscript
file.  It sweeps font information forward so that it extracted pages
print correctly.  This software should allow you to print the ET++
document if it was generated on a macintosh with the version 5.x or
6.0.1 or 6.0.2 laserwriter icons (but probably not the new TrueType
system 7.0 icons).  The "pp" command allows you to select any range of
pages, to convert line terminators NL <-> LF, and to change the number
of copies printed.  Here is the pp.c utility.

------------------------------------------------------

/* This UNIX filter modifies a postscript file uploaded from
 * a macintosh.  It will be tested with laserwriter 5.2 and 6.0.
 * The filter has 2 purposes:
 *  (a) To tell the postscript interpreter to print N copies of a file.
 *      These lines look like this:
 *	       ... ^Muserdict/#copies N put^M ...
 *	If only 1 copy is requested, this line does not appear in the file
 * 	and must be prepended.  If the line already appears, it is modified.
 *  (b) To select particular pages to print.  This is accomplished by 
 *	watching the page number comment lines:
 *		...^M%%Page: ? N^M ...
 *	and omitting data in unwanted pages, but KEEPING any font definitions
 *	which were loaded on previous pages (because these definitions may be
 *	used on later page).  The preamble is handled as page zero, and is
 *	always included in the job.
 *
 *	Most of the complexity in this program comes from parsing the
 *	page number specification, which has the same syntax as troff.
 */

#include <stdio.h>
#include <ctype.h>

#define D(x)			/* D(x)  = x  -> turns on debugging */

#define	LOWNUM	1		/* lowest page number selectable */
#define	HIGHNUM 300		/* highest page number selectable */
#define	TRUE	1
#define	FALSE	0
#define NULL	0
/* For the next two definitions:
 * Modify to '\n' if your postscript files are terminated by newlines.  
 * Modify to '\r' & '\n' to xform ^M -> ^J with this filter. */
char	EOLCHARIN = '\r';	/* the character to terminate input lines */
char	EOLCHAROUT = '\r';	/* the character to terminate output lines */
char inTable[]  = {'\r', '\r', '\n', '\n'};  /* for -Tx option */
char outTable[] = {'\r', '\n', '\n', '\r'};

char pageprint[HIGHNUM+1];      /* boolean map of pages to print */
				/* page zero is the header; always print it */

char line[100000];		/* a linotype font is approx. 50K long. */
				/* buffer to hold 1 line of postscript file */
int linelth;			/* length of line of postscript read */

int copies = 1;			/* number of copies to print (default: 1) */
int currentPage = 0;		/* page currently being read (0 = preamble) */
int ok = TRUE;			/* set to false when parse error occurs */
char *cp;			/* pointer to character in line being parsed */
int color = TRUE;		/* allow setrgbcolor commands? (default:yes) */

char *strfind();		/* finds substr1 in substr2, or returns NULL */

main(argc, argv)
int argc;
char *argv[];
{
	int only;		/* flag for storing errors */
	int i;			/* scratch */
	int mutate;		/* type of CR->LF transformation */
	
	only = FALSE;		/* assume no page specifier (-oXX) given */
	fill(LOWNUM-1, HIGHNUM, FALSE); /* print no pages yet */
	pageprint[0] = TRUE;	/* always print preamble */

	for(i=1; (i < argc) && ok;i++) {
		cp =  argv[i];
		if (*cp++ == '-' && *cp) {
			switch(*cp++) {
			case 'c':	/* number of Copies to print */
				ok = sscanf(cp, "%d", &copies);
				ok = ok && (copies <= 5);  /* limit 5 */
				break;
			case 'o':	/* print Only certain copies */
				only = TRUE;
				if (!numberspec()) 
					fprintf(stderr, "Bad Number Range\n");
				break;
			case 'X':	/* remove "setrgbcolor" lines */
				color = FALSE;
				break;
			case 'T':	/* mutate CR->CR, CR->LF or LF->LF */
				ok = sscanf(cp, "%d", &mutate);
				ok = (ok && mutate <= 3 && mutate >= 0);
				if (ok) {
					EOLCHARIN = inTable[mutate];
					EOLCHAROUT = outTable[mutate];
				}
				break;
			default:	/* parsing error */
				ok = FALSE;
			}
		}
	}
	
	if (!only) fill(LOWNUM, HIGHNUM);

	if (! ok) usage();

/* testing code
	fprintf(stderr, "pp -c%d -f%c %c %c\n -o",copies,flist[0],flist[1],flist[2]);
	for(i=0; i< HIGHNUM; i++) {
		if (pageprint[i]) putchar('.');
		else putchar(' ');
	}
	end of testing code */

	filter();		/* process postscript file from stdin */

}

/* This routine actually does the filtering -- it reads lines of input,
 * records the current page number, and outputs a line as long
 * as the page number was selected by the user.  It ALWAYS outputs
 * commands to load a font.  It modifies commands to select the
 * number of pages to print.  It installs a page number command at
 * the end of the preamble.  If "color" is false, it removes 
 * "setrgbcolor" commands from the input stream.
 */
filter () {
	int pushpage;
	char *setrgbcolor = " setrgbcolor\n";
	setrgbcolor[12] = EOLCHAROUT;

	pushpage = 0;
	while(!feof(stdin)) {
	        readline();
		if (!strncmp("{}mark ",line,7)) {  /* starting font spec */
			pushpage = currentPage; currentPage = 0;
		}
		if (strfind(" 0 rf",line,5)) {     /* ending font spec */
			writeline();
			currentPage = pushpage;
			continue;
		}
		else if (!color && strfind(setrgbcolor,line,13)) {
			continue;	/* remove the entire line */
		}
		else if (!strncmp(line, "userdict/#copies",16)) {
			printf("userdict/#copies %d put", copies);
			putchar(EOLCHAROUT);
			continue;
		}
		else if (!strncmp(line, "%%Page: ? ", 6)) {
			sscanf(line, "%%%%Page: ? %d", &currentPage);
		}
		if (pageprint[currentPage]) writeline();
		if (!strncmp(line, "%%EndDocumentSetup",18)) {
			printf("userdict/#copies %d put", copies);
			putchar(EOLCHAROUT);
		}
	}
}

/* Read a line of intput, terminated by EOLCHARIN, and store it in the
 * buffer variable "line".  Convert the line terminator, EOLCHARIN ->
 * EOLCHAROUT (this is useful for debugging).  Bugs: This routines does
 * not check for line buffer overflow */
readline() {
	register char *p;
	for (p = line; (*p = getchar()) != EOLCHARIN && *p != EOF; p++);
	*p++ = EOLCHAROUT;
	*p = NULL;
	linelth = (p - line);
}

writeline() {
	fputs(line,stdout);
}

/*=== Routines that parse the syntax of the -oXX page number specifiers=====*/

/* parser for number specification
 * BNF for number language:
 *
 * numberspec	= range , numberspec | range
 * range     	=  n  |  n-n  |  n-  |  -n
 * n		= (integer)
 * the semantics is OR (overlapping ranges never detract from the number
 * of pages to print).  The following recursive descent w/o backtrack
 * parser uses the global variable "cp" to read input, and the global
 * variable "ok" to signal an error.
 */

 /* Parses a sequence of ranges, i.e.  29,38,47,32-12
  * via "range()" function, fills lookup table at corresponding locations.
  */
int numberspec() {
   	D(fprintf(stderr,"numberspec: %s\n",cp));
	if (ok = range()) {
		if (*cp++ == ',') return(ok = numberspec());
		else if (*(cp-1) != NULL) ok = FALSE;
	}
	return(ok);
}

/* Parses a range, "N-", "-M", or "N-M", fills page printing table, returns
 * TRUE if successful 
 */
int range()
{
	int number1, number2, gotfirst, gotsecond, gotdash;
	gotfirst = gotsecond = gotdash = FALSE;
	number1 = number2 = 0;

    	D(fprintf(stderr,"range: %s\n",cp));
	if (isdigit(*cp)) {
		gotfirst = TRUE;
		if (!n(&number1)) return(ok=FALSE);
	}
	if (*cp == '-') {
		gotdash = TRUE; 
		cp++;
		if (isdigit(*cp)) {
			gotsecond = TRUE;
			if (!n(&number2)) return(ok=FALSE);
		}
	}

	if (gotfirst && gotdash && gotsecond) fill(number1,number2,TRUE);
	else if (gotfirst && gotdash && !gotsecond) fill(number1,HIGHNUM,TRUE);
	else if (!gotfirst && gotdash && gotsecond) fill(LOWNUM, number2,TRUE);
	else if (gotfirst&& !gotdash && !gotsecond) fill(number1,number1,TRUE);
	else ok = FALSE; 
	return(ok);
}

/* Parses an integer, stores it in *number, returns TRUE if successful */
int n(number)
int *number;
{	*number = 0;
	while (isdigit(*cp)) *number = *number * 10 + (*cp++) - '0';
	if (*number > HIGHNUM || *number < LOWNUM) ok = FALSE;
  	D(fprintf(stderr,"number returns: %d\n",*number));
	return(ok);
}

/* Fills [low..high] in the page-printing lookup table with a T/F value */
fill(low,high,val) 
int low,high,val;
{	
	int i;
	if (low > high) {int tmp; tmp = low; low = high; high = tmp;}
	for (i=low;i<=high;i++) pageprint[i] = val;
}

usage() {
char **pp;
fprintf(stderr,"Usage: pp -cN -oList -T# -f filename ...\n\n");
fprintf(stderr,"   -cN    - number of copies to print (maximum: 5)\n");
fprintf(stderr,"   -X     - edit to remove color instructions \n");
fprintf(stderr,"   -oList - list of copies to print, as in troff, i.e.\n");
fprintf(stderr,"              a-b    print pages a through b\n");
fprintf(stderr,"               -b    print from beginning to page b\n");
fprintf(stderr,"              a-     print from a to the end\n");
fprintf(stderr,"	    RANGE IS LIMITED TO [%d..%d]\n",LOWNUM,HIGHNUM);
fprintf(stderr,"   -T0-3   - translate (0) LF->LF {default} (1) LF->CR\n");
fprintf(stderr,"	      (2) CR->CR (3) CR->LF\n");
exit(1);
}

/* returns a pointer to first occurence of s1 in s2, or NULL if none found. */
char *strfind(s1,s2,n)
char *s1, *s2;
int n;
{
	while (*s2 != '\000') {
	  if (*s2 == *s1 && !strncmp(s1,s2,n)) break;
	  s2++;
	}
 	D(fprintf(stderr,"strfind: %s %s returning %d\n",s1,s2,*s2 == '\000' ? 0 : s2));
	return(*s2 == '\000' ? NULL : s2);
}


--