ps: use a dictionary in selectfont_cached
[neatpost.git] / post.c
blobf70e6e301c49da2ce040d516834da5a228b40d2f
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 = 216; /* page width */
15 static int ps_pageheight = 279; /* page height */
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("BeginObject", cmd))
213 drawmbeg(arg);
214 if (!strcmp("EndObject", cmd))
215 drawmend(arg);
218 static char devpath[PATHLEN] = TROFFFDIR;
219 static char devname[PATHLEN] = "utf";
221 static void postx(void)
223 char cmd[128];
224 char font[128];
225 int pos;
226 nextword(cmd);
227 switch (cmd[0]) {
228 case 'f':
229 pos = nextnum();
230 nextword(font);
231 dev_mnt(pos, font, font);
232 outmnt(pos);
233 break;
234 case 'i':
235 if (dev_open(devpath, devname)) {
236 fprintf(stderr, "neatpost: cannot open device %s\n", devname);
237 exit(1);
239 ps_header(ps_pagewidth, ps_pageheight, ps_linewidth);
240 break;
241 case 'T':
242 nextword(devname);
243 break;
244 case 's':
245 break;
246 case 'X':
247 postps();
248 break;
250 nexteol();
253 static void postcmd(int c)
255 char cs[GNLEN];
256 if (isdigit(c)) {
257 outrel((c - '0') * 10 + next() - '0', 0);
258 nextutf8(cs);
259 outc(cs);
260 return;
262 switch (c) {
263 case 's':
264 outsize(nextnum());
265 break;
266 case 'f':
267 outfont(nextnum());
268 break;
269 case 'H':
270 outh(nextnum());
271 break;
272 case 'V':
273 outv(nextnum());
274 break;
275 case 'h':
276 outrel(nextnum(), 0);
277 break;
278 case 'v':
279 outrel(0, nextnum());
280 break;
281 case 'c':
282 nextutf8(cs);
283 outc(cs);
284 break;
285 case 'm':
286 nextword(cs);
287 outcolor(clr_get(cs));
288 break;
289 case 'N':
290 nextnum();
291 break;
292 case 'C':
293 nextword(cs);
294 outc(cs);
295 break;
296 case 'p':
297 if (o_pages)
298 ps_pageend(o_pages);
299 o_pages = nextnum();
300 ps_pagebeg(o_pages);
301 outpage();
302 break;
303 case 'w':
304 break;
305 case 'n':
306 nextnum();
307 nextnum();
308 break;
309 case 'D':
310 postdraw();
311 break;
312 case 'x':
313 postx();
314 break;
315 case '#':
316 nexteol();
317 break;
318 default:
319 fprintf(stderr, "neatpost: unknown command %c\n", c);
323 static void post(void)
325 int c;
326 while ((c = next()) >= 0)
327 if (!isspace(c))
328 postcmd(c);
329 if (o_pages)
330 ps_pageend(o_pages);
333 static void setpagesize(char *s)
335 int d1, d2, n;
336 /* predefined paper sizes */
337 if (!strcmp("letter", s)) {
338 ps_pagewidth = 216;
339 ps_pageheight = 279;
340 return;
342 /* custom paper size in mm, like 210x297 for a4 */
343 if (isdigit(s[0]) && strchr(s, 'x')) {
344 ps_pagewidth = atoi(s);
345 ps_pageheight = atoi(strchr(s, 'x') + 1);
346 return;
348 /* ISO paper sizes */
349 if (!isdigit(s[1]))
350 return;
351 n = s[1] - '0';
352 switch (tolower(s[0])) {
353 case 'a':
354 d1 = 841;
355 d2 = 1189;
356 break;
357 case 'b':
358 d1 = 1000;
359 d2 = 1414;
360 break;
361 case 'c':
362 d1 = 917;
363 d2 = 1297;
364 break;
365 default:
366 return;
368 ps_pagewidth = ((n & 1) ? d2 : d1) >> ((n + 1) >> 1);
369 ps_pageheight = ((n & 1) ? d1 : d2) >> (n >> 1);
372 static char *usage =
373 "Usage: neatpost [options] <input >output\n"
374 "Options:\n"
375 " -F dir \tset font directory (" TROFFFDIR ")\n"
376 " -p size \tset paper size (a4)\n"
377 " -w lwid \tdrawing line thickness in thousandths of an em\n";
379 int main(int argc, char *argv[])
381 int i;
382 for (i = 1; i < argc; i++) {
383 if (argv[i][0] == '-' && argv[i][1] == 'F') {
384 strcpy(devpath, argv[i][2] ? argv[i] + 2 : argv[++i]);
385 } else if (argv[i][0] == '-' && argv[i][1] == 'p') {
386 setpagesize(argv[i][2] ? argv[i] + 2 : argv[++i]);
387 } else if (argv[i][0] == '-' && argv[i][1] == 'w') {
388 ps_linewidth = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
389 } else {
390 printf("%s", usage);
391 return 0;
394 post();
395 ps_trailer(o_pages, o_fonts);
396 return 0;