pdf: basic support for \X'pdf pic.pdf'
[neatpost.git] / post.c
blobd995ff493a5e5afd172e2f33f24ae4889f747a80
1 /*
2 * NEATPOST: NEATROFF'S POSTSCRIPT/PDF POSTPROCESSOR
4 * Copyright (C) 2013-2018 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("pdf", cmd))
264 outpdf(arg);
265 if (!strcmp("link", cmd))
266 outlink(arg);
267 if (!strcmp("BeginObject", cmd))
268 drawmbeg(arg);
269 if (!strcmp("EndObject", cmd))
270 drawmend(arg);
273 static char postdir[PATHLEN] = TROFFFDIR; /* output device directory */
274 static char postdev[PATHLEN] = "utf"; /* output device name */
276 static void postx(void)
278 char cmd[128];
279 char font[128];
280 int pos;
281 nextword(cmd);
282 switch (cmd[0]) {
283 case 'f':
284 pos = nextnum();
285 nextword(font);
286 dev_mnt(pos, font, font);
287 outmnt(pos);
288 break;
289 case 'i':
290 if (dev_open(postdir, postdev)) {
291 fprintf(stderr, "neatpost: cannot open device %s\n", postdev);
292 exit(1);
294 ps_header(ps_title, ps_pagewidth, ps_pageheight, ps_linewidth);
295 break;
296 case 'T':
297 nextword(postdev);
298 break;
299 case 's':
300 break;
301 case 'X':
302 postps();
303 break;
305 nexteol();
308 static void postcmd(int c)
310 char cs[GNLEN];
311 if (isdigit(c)) {
312 outrel((c - '0') * 10 + next() - '0', 0);
313 nextutf8(cs);
314 outc(cs);
315 return;
317 switch (c) {
318 case 's':
319 outsize(nextnum());
320 break;
321 case 'f':
322 outfont(nextnum());
323 break;
324 case 'H':
325 outh(nextnum());
326 break;
327 case 'V':
328 outv(nextnum());
329 break;
330 case 'h':
331 outrel(nextnum(), 0);
332 break;
333 case 'v':
334 outrel(0, nextnum());
335 break;
336 case 'c':
337 nextutf8(cs);
338 outc(cs);
339 break;
340 case 'm':
341 nextword(cs);
342 outcolor(clr_get(cs));
343 break;
344 case 'N':
345 nextnum();
346 break;
347 case 'C':
348 nextword(cs);
349 outc(cs);
350 break;
351 case 'p':
352 if (o_pages)
353 ps_pageend(o_pages);
354 o_pages = nextnum();
355 ps_pagebeg(o_pages);
356 outpage();
357 break;
358 case 'w':
359 break;
360 case 'n':
361 nextnum();
362 nextnum();
363 break;
364 case 'D':
365 postdraw();
366 break;
367 case 'x':
368 postx();
369 break;
370 case '#':
371 nexteol();
372 break;
373 default:
374 fprintf(stderr, "neatpost: unknown command %c\n", c);
375 nexteol();
379 static void post(void)
381 int c;
382 while ((c = next()) >= 0)
383 if (!isspace(c))
384 postcmd(c);
385 if (o_pages)
386 ps_pageend(o_pages);
389 static struct paper {
390 char *name;
391 int w, h;
392 } papers[] = {
393 {"letter", 2159, 2794},
394 {"legal", 2159, 3556},
395 {"ledger", 4318, 2794},
396 {"tabloid", 2794, 4318},
399 static void setpagesize(char *s)
401 int d1, d2, n, i;
402 /* predefined paper sizes */
403 for (i = 0; i < LEN(papers); i++) {
404 if (!strcmp(papers[i].name, s)) {
405 ps_pagewidth = papers[i].w;
406 ps_pageheight = papers[i].h;
407 return;
410 /* custom paper size in mm, like 2100x2970 for a4 */
411 if (isdigit(s[0]) && strchr(s, 'x')) {
412 ps_pagewidth = atoi(s);
413 ps_pageheight = atoi(strchr(s, 'x') + 1);
414 return;
416 /* ISO paper sizes */
417 if (!strchr("abcABC", s[0]) || !isdigit(s[1]))
418 return;
419 if (tolower(s[0]) == 'a') {
420 d1 = 8410;
421 d2 = 11890;
423 if (tolower(s[0]) == 'b') {
424 d1 = 10000;
425 d2 = 14140;
427 if (tolower(s[0]) == 'c') {
428 d1 = 9170;
429 d2 = 12970;
431 n = s[1] - '0';
432 ps_pagewidth = ((n & 1) ? d2 : d1) >> ((n + 1) >> 1);
433 ps_pageheight = ((n & 1) ? d1 : d2) >> (n >> 1);
434 ps_pagewidth -= ps_pagewidth % 10;
435 ps_pageheight -= ps_pageheight % 10;
438 void *mextend(void *old, long oldsz, long newsz, int memsz)
440 void *new = malloc(newsz * memsz);
441 memcpy(new, old, oldsz * memsz);
442 memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
443 free(old);
444 return new;
447 static char *usage =
448 "Usage: neatpost [options] <input >output\n"
449 "Options:\n"
450 " -F dir \tset font directory (" TROFFFDIR ")\n"
451 " -p size \tset paper size (letter); e.g., a4, 2100x2970\n"
452 " -t title\tspecify document title\n"
453 " -w lwid \tdrawing line thickness in thousandths of an em (40)\n"
454 " -n \talways draw glyphs by name (ps glyphshow)\n";
456 int main(int argc, char *argv[])
458 int i;
459 for (i = 1; i < argc; i++) {
460 if (argv[i][0] == '-' && argv[i][1] == 'F') {
461 strcpy(postdir, argv[i][2] ? argv[i] + 2 : argv[++i]);
462 } else if (argv[i][0] == '-' && argv[i][1] == 'p') {
463 setpagesize(argv[i][2] ? argv[i] + 2 : argv[++i]);
464 } else if (argv[i][0] == '-' && argv[i][1] == 'w') {
465 ps_linewidth = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
466 } else if (argv[i][0] == '-' && argv[i][1] == 'n') {
467 outgname(1);
468 } else if (argv[i][0] == '-' && argv[i][1] == 't') {
469 ps_title = argv[i][2] ? argv[i] + 2 : argv[++i];
470 } else {
471 printf("%s", usage);
472 return 0;
475 post();
476 ps_trailer(o_pages);
477 dev_close();
478 return 0;