post: close the output device before exiting
[neatpost.git] / post.c
blob075b159a4652c62b12bd2f9bfc0d1d4725b87c9d
1 /*
2 * neatpost troff postscript postprocessor
4 * Copyright (C) 2013-2014 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "post.h"
14 static int ps_pagewidth = 2159; /* page width (tenths of a millimetre) */
15 static int ps_pageheight = 2794;/* page height (tenths of a millimetre) */
16 static int ps_linewidth = 40; /* drawing line thickness in thousandths of an em */
17 static int o_pages; /* output pages */
19 static int next(void)
21 return getc(stdin);
24 static void back(int c)
26 ungetc(c, stdin);
29 static int utf8len(int c)
31 if (c <= 0x7f)
32 return 1;
33 if (c >= 0xfc)
34 return 6;
35 if (c >= 0xf8)
36 return 5;
37 if (c >= 0xf0)
38 return 4;
39 if (c >= 0xe0)
40 return 3;
41 if (c >= 0xc0)
42 return 2;
43 return 1;
46 static int nextutf8(char *s)
48 int c = next();
49 int l = utf8len(c);
50 int i;
51 if (c < 0)
52 return 0;
53 s[0] = c;
54 for (i = 1; i < l; i++)
55 s[i] = next();
56 s[l] = '\0';
57 return l;
60 /* skip blanks */
61 static void nextskip(void)
63 int c;
64 do {
65 c = next();
66 } while (isspace(c));
67 back(c);
70 static int nextnum(void)
72 int c;
73 int n = 0;
74 int neg = 0;
75 nextskip();
76 while (1) {
77 c = next();
78 if (!n && c == '-') {
79 neg = 1;
80 continue;
82 if (!isdigit(c))
83 back(c);
84 if (c < 0 || !isdigit(c))
85 break;
86 n = n * 10 + c - '0';
88 return neg ? -n : n;
91 static int iseol(void)
93 int c;
94 do {
95 c = next();
96 } while (c == ' ');
97 back(c);
98 return c == '\n';
101 /* skip until the end of line */
102 static void nexteol(void)
104 int c;
105 do {
106 c = next();
107 } while (c >= 0 && c != '\n');
110 static void nextword(char *s)
112 int c;
113 nextskip();
114 c = next();
115 while (c >= 0 && !isspace(c)) {
116 *s++ = c;
117 c = next();
119 if (c >= 0)
120 back(c);
121 *s = '\0';
124 /* read until eol */
125 static void readln(char *s)
127 int c;
128 c = next();
129 while (c > 0 && c != '\n') {
130 *s++ = c;
131 c = next();
133 if (c == '\n')
134 back(c);
135 *s = '\0';
138 static void postline(void)
140 int h, v;
141 while (!iseol()) {
142 h = nextnum();
143 v = nextnum();
144 drawl(h, v);
148 static void postspline(void)
150 int h2, v2;
151 int h1 = nextnum();
152 int v1 = nextnum();
153 if (iseol()) {
154 drawl(h1, v1);
155 return;
157 while (!iseol()) {
158 h2 = nextnum();
159 v2 = nextnum();
160 draws(h1, v1, h2, v2);
161 h1 = h2;
162 v1 = v2;
164 draws(h1, v1, 0, 0);
167 static void postdraw(void)
169 int h1, h2, v1, v2;
170 int c = next();
171 drawbeg();
172 switch (tolower(c)) {
173 case 'l':
174 h1 = nextnum();
175 v1 = nextnum();
176 drawl(h1, v1);
177 break;
178 case 'c':
179 drawc(nextnum());
180 break;
181 case 'e':
182 h1 = nextnum();
183 v1 = nextnum();
184 drawe(h1, v1);
185 break;
186 case 'a':
187 h1 = nextnum();
188 v1 = nextnum();
189 h2 = nextnum();
190 v2 = nextnum();
191 drawa(h1, v1, h2, v2);
192 break;
193 case '~':
194 postspline();
195 break;
196 case 'p':
197 postline();
198 break;
200 drawend(c == 'p' || c == 'P', c == 'E' || c == 'C' || c == 'P');
201 nexteol();
204 static void postps(void)
206 char cmd[ILNLEN];
207 char arg[ILNLEN];
208 nextword(cmd);
209 readln(arg);
210 if (!strcmp("PS", cmd) || !strcmp("ps", cmd))
211 out("%s\n", arg);
212 if (!strcmp("rotate", cmd))
213 outrotate(atoi(arg));
214 if (!strcmp("eps", cmd))
215 outeps(arg);
216 if (!strcmp("BeginObject", cmd))
217 drawmbeg(arg);
218 if (!strcmp("EndObject", cmd))
219 drawmend(arg);
222 static char postdir[PATHLEN] = TROFFFDIR; /* output device directory */
223 static char postdev[PATHLEN] = "utf"; /* output device name */
225 static void postx(void)
227 char cmd[128];
228 char font[128];
229 int pos;
230 nextword(cmd);
231 switch (cmd[0]) {
232 case 'f':
233 pos = nextnum();
234 nextword(font);
235 dev_mnt(pos, font, font);
236 outmnt(pos);
237 break;
238 case 'i':
239 if (dev_open(postdir, postdev)) {
240 fprintf(stderr, "neatpost: cannot open device %s\n", postdev);
241 exit(1);
243 ps_header(ps_pagewidth, ps_pageheight, ps_linewidth);
244 break;
245 case 'T':
246 nextword(postdev);
247 break;
248 case 's':
249 break;
250 case 'X':
251 postps();
252 break;
254 nexteol();
257 static void postcmd(int c)
259 char cs[GNLEN];
260 if (isdigit(c)) {
261 outrel((c - '0') * 10 + next() - '0', 0);
262 nextutf8(cs);
263 outc(cs);
264 return;
266 switch (c) {
267 case 's':
268 outsize(nextnum());
269 break;
270 case 'f':
271 outfont(nextnum());
272 break;
273 case 'H':
274 outh(nextnum());
275 break;
276 case 'V':
277 outv(nextnum());
278 break;
279 case 'h':
280 outrel(nextnum(), 0);
281 break;
282 case 'v':
283 outrel(0, nextnum());
284 break;
285 case 'c':
286 nextutf8(cs);
287 outc(cs);
288 break;
289 case 'm':
290 nextword(cs);
291 outcolor(clr_get(cs));
292 break;
293 case 'N':
294 nextnum();
295 break;
296 case 'C':
297 nextword(cs);
298 outc(cs);
299 break;
300 case 'p':
301 if (o_pages)
302 ps_pageend(o_pages);
303 o_pages = nextnum();
304 ps_pagebeg(o_pages);
305 outpage();
306 break;
307 case 'w':
308 break;
309 case 'n':
310 nextnum();
311 nextnum();
312 break;
313 case 'D':
314 postdraw();
315 break;
316 case 'x':
317 postx();
318 break;
319 case '#':
320 nexteol();
321 break;
322 default:
323 fprintf(stderr, "neatpost: unknown command %c\n", c);
324 nexteol();
328 static void post(void)
330 int c;
331 while ((c = next()) >= 0)
332 if (!isspace(c))
333 postcmd(c);
334 if (o_pages)
335 ps_pageend(o_pages);
338 static struct paper {
339 char *name;
340 int w, h;
341 } papers[] = {
342 {"letter", 2159, 2794},
343 {"legal", 2159, 3556},
344 {"ledger", 4318, 2794},
345 {"tabloid", 2794, 4318},
348 static void setpagesize(char *s)
350 int d1, d2, n, i;
351 /* predefined paper sizes */
352 for (i = 0; i < LEN(papers); i++) {
353 if (!strcmp(papers[i].name, s)) {
354 ps_pagewidth = papers[i].w;
355 ps_pageheight = papers[i].h;
356 return;
359 /* custom paper size in mm, like 2100x2970 for a4 */
360 if (isdigit(s[0]) && strchr(s, 'x')) {
361 ps_pagewidth = atoi(s);
362 ps_pageheight = atoi(strchr(s, 'x') + 1);
363 return;
365 /* ISO paper sizes */
366 if (!strchr("abcABC", s[0]) || !isdigit(s[1]))
367 return;
368 if (tolower(s[0]) == 'a') {
369 d1 = 8410;
370 d2 = 11890;
372 if (tolower(s[0]) == 'b') {
373 d1 = 10000;
374 d2 = 14140;
376 if (tolower(s[0]) == 'c') {
377 d1 = 9170;
378 d2 = 12970;
380 n = s[1] - '0';
381 ps_pagewidth = ((n & 1) ? d2 : d1) >> ((n + 1) >> 1);
382 ps_pageheight = ((n & 1) ? d1 : d2) >> (n >> 1);
383 ps_pagewidth -= ps_pagewidth % 10;
384 ps_pageheight -= ps_pageheight % 10;
387 static void errdie(char *msg)
389 fprintf(stderr, msg);
390 exit(1);
393 void *mextend(void *old, long oldsz, long newsz, int memsz)
395 void *new = xmalloc(newsz * memsz);
396 memcpy(new, old, oldsz * memsz);
397 memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
398 free(old);
399 return new;
402 void *xmalloc(long len)
404 void *m = malloc(len);
405 if (!m)
406 errdie("neatroff: malloc() failed\n");
407 return m;
410 static char *usage =
411 "Usage: neatpost [options] <input >output\n"
412 "Options:\n"
413 " -F dir \tset font directory (" TROFFFDIR ")\n"
414 " -p size \tset paper size (letter); e.g., a4, 2100x2970\n"
415 " -w lwid \tdrawing line thickness in thousandths of an em (40)\n";
417 int main(int argc, char *argv[])
419 int i;
420 for (i = 1; i < argc; i++) {
421 if (argv[i][0] == '-' && argv[i][1] == 'F') {
422 strcpy(postdir, argv[i][2] ? argv[i] + 2 : argv[++i]);
423 } else if (argv[i][0] == '-' && argv[i][1] == 'p') {
424 setpagesize(argv[i][2] ? argv[i] + 2 : argv[++i]);
425 } else if (argv[i][0] == '-' && argv[i][1] == 'w') {
426 ps_linewidth = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
427 } else {
428 printf("%s", usage);
429 return 0;
432 post();
433 ps_trailer(o_pages, o_fonts);
434 dev_close();
435 return 0;