/* Original code was fmfix [Frame Fix] -- a filter for Frame PostScript output. (c) Copyright 1993 by Hal Jespersen. hal@posix.com Name changed to add_line_ps -- filter for adding line numbers to PostScript output. additions (c) Copyright 1994 by Michael Hannah. mjhanna@sandia.gov Both authors have indicated that you should feel free to use this code as you like, as long as you cite some acknowledgment. Revisions ------- July 1, 1993 -c flag added by Michael Hannah, Sandia Labs July 2, 1993 -d flag added by Michael Hannah, Sandia Labs Dec. 6, 1994 -i -t -b -l -r flags added by Michael Hannah, Sandia Labs old -b flag changed to -2 name changed to add_line_ps This version adds line numbers to each printed line for review purposes (you can suppress them with the -n flag) and allows a selection of page ranges for printing. The page-range is specified the same as in troff, with "-o". (It was called 'fix' for historical reasons. It comes from a filter Hal used for POSIX standards, in which a lot more fixing than line numbers goes on.) It uses only stdin and stdout for its normal functions--a pure filter. "usage: %s [-g modulo] [-2] [-c] [-d] [-i] [-n] [-o pagelist] \n \t [-t topmargin] [-b bottommargin] [-l leftmargin] [-r rightmargin] \n", argv[0]); -g modulo print only every modulo page in page list -2 print two sets of line numbers (both sides of page) -c make line numbers continuous across pages -d make line numbers duplex outside edge (right side for odd) -i invert location of line numbers for duplex use inside edge (left side for odd) for single sided, use right edge -n suppress printing line numbers -o pagelist Print only pages whose page numbers appear in the comma-separated list of numbers and ranges. A range N-M means pages N through M; an initial -N means from the beginning to page N; and a final N- means from N to the end. Note: for following, bottom,left of page is 0,0. point = 1/72 inch -t topmargin ignore text above topmargin points for line numbers -b bottommargin ignore text below bottommargin points for line numbers -l leftmargin print left side numbers at leftmargin points -r rightmargin print right side numbers at rightmargin points The source comments refer to multiple columns. These are guesses; this filter has not been tried by either author on such documents. */ #include #include #include #include extern int optind; extern char *optarg; extern double fabs(); #define MAXPAGE 9999/* Maximum default ending page number */ #define MAXRANGE 511/* Maximum number of pages in -o list */ #define BUFSIZE 4096/* I/O buffer size (real big to be conservative) */ #define YMAX 200/* max number of lines on page (consider multiple columns) */ char buf[BUFSIZE];/* main buffer for I/O */ char oper[64];/* buffer for PostScript operators */ double num1, num2;/* return values from get_oper() */ double prevy;/* previous y-coord collected */ double ynum[YMAX];/* array for collected y-coords */ int yindex;/* index to that array */ int lineno;/* line # counter */ int pageno;/* current page number */ int epsf;/* switch to bypass embedded figures */ int inrange = 1; int endpage;/* highest page number to output */ int pages[MAXRANGE+1];/* array of page numbers to print */ int lastpgindex;/* index past last entry in pages[] */ int graze;/* graze value */ int numbering = 1;/* line-numbering switch */ int duplex = 0;/* duplex printing switch */ int invert = 0;/* invert left/right printing switch */ int print_both = 0;/* switch to print numbers both sides */ int reset_lineno = 1;/* continuous line numbers switch */ int font;/* keep track of current font */ /* The following should be adjusted to fit your document. */ int topmargin = 740;/* default highest point on new page (expressed in 1/72 inch units) */ int botmargin = 50;/* default lowest point on page */ int lineno_xpos;/* x-coord to place line numbers */ int left_lineno_xpos = 30;/* default locations for line numbers */ int right_lineno_xpos = 585; int lineno_point = 8;/* point size to use for line numbers */ int lineno_font = 0;/* font number to use for line numbers */ int lineno_vert = 10;/* minimum spacing required between line #s */ main(argc, argv) int argc; char *argv[]; { int i; char *olist = NULL; while ((i = getopt(argc, argv, "g:n2cdio:t:b:l:r:")) != EOF) { switch (i) { case 'o': if (olist != NULL) { fprintf(stderr, "multiple -o lists not allowed\n"); exit(1); } olist = optarg; break; case 'g': graze = atoi(optarg); break; case 'n': if (reset_lineno == 0) { fprintf(stderr, "-c and -n options confict\n"); exit(1); } if (duplex == 1) { fprintf(stderr, "-d and -n options confict\n"); exit(1); } if (invert == 1) { fprintf(stderr, "-i and -n options confict\n"); exit(1); } numbering = 0; break; case '2': if (numbering == 0) { fprintf(stderr, "-2 and -n options confict\n"); exit(1); } if (duplex == 1) { fprintf(stderr, "-2 and -d options confict\n"); exit(1); } if (invert == 1) { fprintf(stderr, "-2 and -i options confict\n"); exit(1); } print_both = 1; break; case 'c': if (numbering == 0) { fprintf(stderr, "-c and -n options confict\n"); exit(1); } reset_lineno = 0; break; case 'i': if (numbering == 0) { fprintf(stderr, "-i and -n options confict\n"); exit(1); } if (print_both == 1) { fprintf(stderr, "-i and -2 options confict\n"); exit(1); } invert = 1; break; case 'd': if (numbering == 0) { fprintf(stderr, "-d and -n options confict\n"); exit(1); } if (print_both == 1) { fprintf(stderr, "-2 and -d options confict\n"); exit(1); } duplex = 1; break; case 't': topmargin = atoi(optarg); break; case 'b': botmargin = atoi(optarg); break; case 'l': left_lineno_xpos = atoi(optarg); break; case 'r': right_lineno_xpos = atoi(optarg); break; default: fprintf(stderr, "usage: %s [-g modulo] [-2] [-c] [-d] [-i] [-n] [-o pagelist] \n\ \t [-t topmargin] [-b bottommargin] [-l leftmargin] [-r rightmargin]\n\ \t defaults = -t %d -b %d -l %d -r %d \ \n", argv[0], topmargin, botmargin, left_lineno_xpos, right_lineno_xpos); exit(1); } } if (left_lineno_xpos >= right_lineno_xpos) { fprintf(stderr, "-l %d -r %d options confict\n", left_lineno_xpos, right_lineno_xpos ); exit(1); } if ((right_lineno_xpos - left_lineno_xpos) <= 100) { fprintf(stderr, "-l %d -r %d options insufficient spread\n", left_lineno_xpos, right_lineno_xpos ); exit(1); } if (botmargin >= topmargin) { fprintf(stderr, "-b %d -t %d options confict\n", botmargin, topmargin ); exit(1); } if ((topmargin - botmargin) <= 100) { fprintf(stderr, "-b %d -t %d options insufficient spread\n", botmargin, topmargin ); exit(1); } argc -= optind; argv += optind; if (olist != NULL) pagelist(olist); else { /* no pagelist specified; get them all */ pages[0] = -1; pages[1] = endpage = MAXPAGE; lastpgindex = 1; } pass_hdr(); while (get_line(buf)) { if (buf[0] == '%') { if (strncmp(buf, "%%BeginDoc", 10) == 0 || strncmp(buf, "%%BeginBin", 10) == 0) { /* disable scanning during figures */ epsf = 1; } else if (strncmp(buf, "%%EndDoc", 8) == 0 || strncmp(buf, "%%EndBin", 8) == 0) { epsf = 0; } else if (strncmp(buf, "%%Page:", 7) == 0) { pageno = atoi(buf + 9); if (pageno > endpage) /* bail out if past last wanted page */ exit(0); else inrange = inpglist(pageno); } } else if (strcmp(buf, "FMENDPAGE") == 0) { /* at end of page, print the line #s */ if (numbering) print_linenos(); } else if (!epsf) { get_oper(); if (strlen(oper) == 1) { /* in my docs, all the interesting opers are single chars */ switch(*oper) { case 'B': case 'P': case 'S': case 'T': if (fabs(prevy - num1) < 1 || num1 > topmargin || num1 < botmargin) break; prevy = num1; /* Save the y-coord of this line as a possible lineno location */ ynum[yindex++] = num1; if (yindex == YMAX) { fprintf(stderr, "Too many Y values.\n"); exit(1); } break; default: break; } } else { if (strcmp(oper, "FMBEGINPAGE") == 0) { prevy = yindex = 0; /* reset indexes for new page */ } } } if (inrange) puts(buf); } exit(0); } /* Read a line of input into 'buf'. The lines can end with either \r or \n, The line in buf will omit this final char. */ get_line() { char *p; int c; for (p = buf; p < buf + BUFSIZE; p++) { if ((c = getchar()) == EOF) return 0; if (c == '\r') c = '\n'; *p = c; if (c == '\n') break; } if (ferror(stdin)) { fprintf(stderr, "Read error on input file\n"); exit(1); } *p++ = 0; return 1; } /* Return the 'oper' represented by the PostScript operator at the end of the line. */ get_oper() { char *p; char b[BUFSIZE]; *oper = 0; num1 = num2 = 0; if (*buf == '%') return; strcpy(b, " "); strcat(b, buf); if ((p = strrchr(b, ' ')) == NULL) return; strcpy(oper, p+1); *p = 0; if ((p = strrchr(b, ' ')) == NULL) return; num1 = atof(p+1); *p = 0; if ((p = strrchr(b, ' ')) == NULL) return; num2 = atof(p+1); return; } /* Pass the PostScript prologue through without modification. */ pass_hdr() { while (gets(buf) != NULL) { puts(buf); if (strcmp(buf, "%%EndSetup") == 0) break; } if (ferror(stdin)) { fprintf(stderr, "Read error on input file\n"); exit(1); } } compare_double(a, b) double *a, *b; { return ((int) *b - *a); } /* Print the line numbers from the saved array. This is done at the end of the page because Frame has a habit of laying down lines out of sequence, such as with table lines or multiple columns. */ print_linenos() { int i; double prev; /* sort the array of y-coords */ qsort(ynum, yindex, sizeof(double), compare_double); /* set up the font and point size wanted */ if (invert) { if (duplex) { if (pageno % 2 == 0) { lineno_xpos=right_lineno_xpos; } else { lineno_xpos=left_lineno_xpos; } } else { lineno_xpos=right_lineno_xpos; } } else { if (duplex) { if (pageno % 2 == 0) { lineno_xpos=left_lineno_xpos; } else { lineno_xpos=right_lineno_xpos; } } else { lineno_xpos=left_lineno_xpos; } } if (inrange) printf("0 X\n%d %d Q\n", lineno_font, lineno_point); for (i = prev = 0; i MAXRANGE-2) { fprintf(stderr, "too many pages in list\n"); exit(1); } pages[lastpgindex++] = n; if (n > endpage) /* save highest page number */ endpage = n; } /* Return 1 if the arg is a page number in the list to be printed. */ inpglist(n) int n; { int i; if (pages[0] == -1 && n <= pages[1]) goto checkgraze; if (pages[lastpgindex] == MAXPAGE && n >= pages[lastpgindex-1]) goto checkgraze; for (i = 0; i <= lastpgindex; i++) { if (n == pages[i]) goto checkgraze; } return 0; checkgraze: if (!graze) return 1; if (n % graze == 0) return 1; return 0; }