ps: remove the parantheses around PS header title
[neatpost.git] / post.c
blob2f261d04da2a53b4d691e95fad2c00467b29c831
1 /*
2 * NEATPOST: NEATROFF'S POSTSCRIPT POSTPROCESSOR
4 * Copyright (C) 2013-2017 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "post.h"
24 static char *ps_title; /* document title */
25 static int ps_pagewidth = 2159; /* page width (tenths of a millimetre) */
26 static int ps_pageheight = 2794;/* page height (tenths of a millimetre) */
27 static int ps_linewidth = 40; /* drawing line thickness in thousandths of an em */
28 static int o_pages; /* output pages */
30 static int next(void)
32 return getc(stdin);
35 static void back(int c)
37 ungetc(c, stdin);
40 static int utf8len(int c)
42 if (c <= 0x7f)
43 return 1;
44 if (c >= 0xfc)
45 return 6;
46 if (c >= 0xf8)
47 return 5;
48 if (c >= 0xf0)
49 return 4;
50 if (c >= 0xe0)
51 return 3;
52 if (c >= 0xc0)
53 return 2;
54 return 1;
57 static int nextutf8(char *s)
59 int c = next();
60 int l = utf8len(c);
61 int i;
62 if (c < 0)
63 return 0;
64 s[0] = c;
65 for (i = 1; i < l; i++)
66 s[i] = next();
67 s[l] = '\0';
68 return l;
71 /* skip blanks */
72 static void nextskip(void)
74 int c;
75 do {
76 c = next();
77 } while (isspace(c));
78 back(c);
81 static int nextnum(void)
83 int c;
84 int n = 0;
85 int neg = 0;
86 nextskip();
87 while (1) {
88 c = next();
89 if (!n && (c == '-' || c == '+')) {
90 neg = c == '-';
91 continue;
93 if (!isdigit(c))
94 back(c);
95 if (c < 0 || !isdigit(c))
96 break;
97 n = n * 10 + c - '0';
99 return neg ? -n : n;
102 static int readnum(int *n)
104 int c;
105 do {
106 c = next();
107 } while (c == ' ');
108 back(c);
109 if (c == '-' || c == '+' || (c >= '0' && c <= '9')) {
110 *n = nextnum();
111 return 0;
113 return 1;
116 static int iseol(void)
118 int c;
119 do {
120 c = next();
121 } while (c == ' ');
122 back(c);
123 return c == '\n';
126 /* skip until the end of line */
127 static void nexteol(void)
129 int c;
130 do {
131 c = next();
132 } while (c >= 0 && c != '\n');
135 static void nextword(char *s)
137 int c;
138 nextskip();
139 c = next();
140 while (c >= 0 && !isspace(c)) {
141 *s++ = c;
142 c = next();
144 if (c >= 0)
145 back(c);
146 *s = '\0';
149 /* read until eol */
150 static void readln(char *s)
152 int c;
153 c = next();
154 while (c > 0 && c != '\n') {
155 *s++ = c;
156 c = next();
158 if (c == '\n')
159 back(c);
160 *s = '\0';
163 static void postline(void)
165 int h, v;
166 while (!readnum(&h) && !readnum(&v))
167 drawl(h, v);
170 static void postarc(void)
172 int h1, v1, h2, v2;
173 if (!readnum(&h1) && !readnum(&v1) && !readnum(&h2) && !readnum(&v2))
174 drawa(h1, v1, h2, v2);
177 static void postspline(void)
179 int h2, v2;
180 int h1 = nextnum();
181 int v1 = nextnum();
182 if (iseol()) {
183 drawl(h1, v1);
184 return;
186 while (!readnum(&h2) && !readnum(&v2)) {
187 draws(h1, v1, h2, v2);
188 h1 = h2;
189 v1 = v2;
191 draws(h1, v1, 0, 0);
194 static void postpoly(void)
196 int l = 'l';
197 int c;
198 while (!iseol() && (l == 'l' || l == '~' || l == 'a')) {
199 do {
200 c = next();
201 } while (c == ' ');
202 back(c);
203 if (c != '-' && c != '+' && (c < '0' || c > '9')) {
204 l = c;
205 while (c >= 0 && !isspace(c))
206 c = next();
207 continue;
209 if (l == 'l')
210 postline();
211 if (l == '~')
212 postspline();
213 if (l == 'a')
214 postarc();
218 static void postdraw(void)
220 int h1, v1;
221 int c = next();
222 drawbeg();
223 switch (tolower(c)) {
224 case 'l':
225 h1 = nextnum();
226 v1 = nextnum();
227 drawl(h1, v1);
228 break;
229 case 'c':
230 drawc(nextnum());
231 break;
232 case 'e':
233 h1 = nextnum();
234 v1 = nextnum();
235 drawe(h1, v1);
236 break;
237 case 'a':
238 postarc();
239 break;
240 case '~':
241 postspline();
242 break;
243 case 'p':
244 postpoly();
245 break;
247 drawend(c == 'p' || c == 'P', c == 'E' || c == 'C' || c == 'P');
248 nexteol();
251 static void postps(void)
253 char cmd[ILNLEN];
254 char arg[ILNLEN];
255 nextword(cmd);
256 readln(arg);
257 if (!strcmp("PS", cmd) || !strcmp("ps", cmd))
258 out("%s\n", arg);
259 if (!strcmp("rotate", cmd))
260 outrotate(atoi(arg));
261 if (!strcmp("eps", cmd))
262 outeps(arg);
263 if (!strcmp("link", cmd))
264 outlink(arg);
265 if (!strcmp("BeginObject", cmd))
266 drawmbeg(arg);
267 if (!strcmp("EndObject", cmd))
268 drawmend(arg);
271 static char postdir[PATHLEN] = TROFFFDIR; /* output device directory */
272 static char postdev[PATHLEN] = "utf"; /* output device name */
274 static void postx(void)
276 char cmd[128];
277 char font[128];
278 int pos;
279 nextword(cmd);
280 switch (cmd[0]) {
281 case 'f':
282 pos = nextnum();
283 nextword(font);
284 dev_mnt(pos, font, font);
285 outmnt(pos);
286 break;
287 case 'i':
288 if (dev_open(postdir, postdev)) {
289 fprintf(stderr, "neatpost: cannot open device %s\n", postdev);
290 exit(1);
292 ps_header(ps_title, ps_pagewidth, ps_pageheight, ps_linewidth);
293 break;
294 case 'T':
295 nextword(postdev);
296 break;
297 case 's':
298 break;
299 case 'X':
300 postps();
301 break;
303 nexteol();
306 static void postcmd(int c)
308 char cs[GNLEN];
309 if (isdigit(c)) {
310 outrel((c - '0') * 10 + next() - '0', 0);
311 nextutf8(cs);
312 outc(cs);
313 return;
315 switch (c) {
316 case 's':
317 outsize(nextnum());
318 break;
319 case 'f':
320 outfont(nextnum());
321 break;
322 case 'H':
323 outh(nextnum());
324 break;
325 case 'V':
326 outv(nextnum());
327 break;
328 case 'h':
329 outrel(nextnum(), 0);
330 break;
331 case 'v':
332 outrel(0, nextnum());
333 break;
334 case 'c':
335 nextutf8(cs);
336 outc(cs);
337 break;
338 case 'm':
339 nextword(cs);
340 outcolor(clr_get(cs));
341 break;
342 case 'N':
343 nextnum();
344 break;
345 case 'C':
346 nextword(cs);
347 outc(cs);
348 break;
349 case 'p':
350 if (o_pages)
351 ps_pageend(o_pages);
352 o_pages = nextnum();
353 ps_pagebeg(o_pages);
354 outpage();
355 break;
356 case 'w':
357 break;
358 case 'n':
359 nextnum();
360 nextnum();
361 break;
362 case 'D':
363 postdraw();
364 break;
365 case 'x':
366 postx();
367 break;
368 case '#':
369 nexteol();
370 break;
371 default:
372 fprintf(stderr, "neatpost: unknown command %c\n", c);
373 nexteol();
377 static void post(void)
379 int c;
380 while ((c = next()) >= 0)
381 if (!isspace(c))
382 postcmd(c);
383 if (o_pages)
384 ps_pageend(o_pages);
387 static struct paper {
388 char *name;
389 int w, h;
390 } papers[] = {
391 {"letter", 2159, 2794},
392 {"legal", 2159, 3556},
393 {"ledger", 4318, 2794},
394 {"tabloid", 2794, 4318},
397 static void setpagesize(char *s)
399 int d1, d2, n, i;
400 /* predefined paper sizes */
401 for (i = 0; i < LEN(papers); i++) {
402 if (!strcmp(papers[i].name, s)) {
403 ps_pagewidth = papers[i].w;
404 ps_pageheight = papers[i].h;
405 return;
408 /* custom paper size in mm, like 2100x2970 for a4 */
409 if (isdigit(s[0]) && strchr(s, 'x')) {
410 ps_pagewidth = atoi(s);
411 ps_pageheight = atoi(strchr(s, 'x') + 1);
412 return;
414 /* ISO paper sizes */
415 if (!strchr("abcABC", s[0]) || !isdigit(s[1]))
416 return;
417 if (tolower(s[0]) == 'a') {
418 d1 = 8410;
419 d2 = 11890;
421 if (tolower(s[0]) == 'b') {
422 d1 = 10000;
423 d2 = 14140;
425 if (tolower(s[0]) == 'c') {
426 d1 = 9170;
427 d2 = 12970;
429 n = s[1] - '0';
430 ps_pagewidth = ((n & 1) ? d2 : d1) >> ((n + 1) >> 1);
431 ps_pageheight = ((n & 1) ? d1 : d2) >> (n >> 1);
432 ps_pagewidth -= ps_pagewidth % 10;
433 ps_pageheight -= ps_pageheight % 10;
436 static void errdie(char *msg)
438 fprintf(stderr, msg);
439 exit(1);
442 void *mextend(void *old, long oldsz, long newsz, int memsz)
444 void *new = xmalloc(newsz * memsz);
445 memcpy(new, old, oldsz * memsz);
446 memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
447 free(old);
448 return new;
451 void *xmalloc(long len)
453 void *m = malloc(len);
454 if (!m)
455 errdie("neatroff: malloc() failed\n");
456 return m;
459 static char *usage =
460 "Usage: neatpost [options] <input >output\n"
461 "Options:\n"
462 " -F dir \tset font directory (" TROFFFDIR ")\n"
463 " -p size \tset paper size (letter); e.g., a4, 2100x2970\n"
464 " -t title\tspecify document title\n"
465 " -w lwid \tdrawing line thickness in thousandths of an em (40)\n"
466 " -n \talways draw glyphs by name (ps glyphshow)\n";
468 int main(int argc, char *argv[])
470 int i;
471 for (i = 1; i < argc; i++) {
472 if (argv[i][0] == '-' && argv[i][1] == 'F') {
473 strcpy(postdir, argv[i][2] ? argv[i] + 2 : argv[++i]);
474 } else if (argv[i][0] == '-' && argv[i][1] == 'p') {
475 setpagesize(argv[i][2] ? argv[i] + 2 : argv[++i]);
476 } else if (argv[i][0] == '-' && argv[i][1] == 'w') {
477 ps_linewidth = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
478 } else if (argv[i][0] == '-' && argv[i][1] == 'n') {
479 outgname(1);
480 } else if (argv[i][0] == '-' && argv[i][1] == 't') {
481 ps_title = argv[i][2] ? argv[i] + 2 : argv[++i];
482 } else {
483 printf("%s", usage);
484 return 0;
487 post();
488 ps_trailer(o_pages, o_fonts);
489 dev_close();
490 return 0;