pdf: add splines
[neatpost.git] / ps.c
blob33336f8acd834ed9ceab21d570d96638bbcb5d5f
1 #include <ctype.h>
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "post.h"
8 static char ps_title[256]; /* document title */
9 static char ps_author[256]; /* document author */
10 static int ps_height; /* document height in basic units */
11 static int o_f, o_s, o_m; /* font and size */
12 static int o_h, o_v; /* current user position */
13 static int p_f, p_s, p_m; /* output postscript font */
14 static int o_qtype; /* queued character type */
15 static int o_qv, o_qh, o_qend; /* queued character position */
16 static int o_rh, o_rv, o_rdeg; /* previous rotation position and degree */
17 static int o_gname; /* use glyphshow for all glyphs */
19 static char o_fonts[FNLEN * NFONTS] = " ";
21 static void outvf(char *s, va_list ap)
23 vfprintf(stdout, s, ap);
26 static void outf(char *s, ...)
28 va_list ap;
29 va_start(ap, s);
30 outvf(s, ap);
31 va_end(ap);
34 static void o_flush(void)
36 if (o_qtype == 1)
37 outf(") %d %d w\n", o_qh, o_qv);
38 if (o_qtype == 2)
39 outf("] %d %d g\n", o_qh, o_qv);
40 o_qtype = 0;
43 void outgname(int g)
45 o_gname = g;
48 void outpage(void)
50 o_flush();
51 o_v = 0;
52 o_h = 0;
53 p_s = 0;
54 p_f = 0;
55 p_m = 0;
56 o_rdeg = 0;
59 static void o_queue(struct glyph *g)
61 int type = 1 + (g->pos <= 0 || o_gname);
62 if (o_qtype != type || o_qend != o_h || o_qv != o_v) {
63 o_flush();
64 o_qh = o_h;
65 o_qv = o_v;
66 o_qtype = type;
67 outf(type == 1 ? "(" : "[");
69 if (o_qtype == 1) {
70 if (g->pos >= ' ' && g->pos <= '~')
71 outf("%s%c", strchr("()\\", g->pos) ? "\\" : "", g->pos);
72 else
73 outf("\\%d%d%d", (g->pos >> 6) & 7,
74 (g->pos >> 3) & 7, g->pos & 7);
75 } else {
76 outf("/%s", g->id);
78 o_qend = o_h + font_wid(g->font, o_s, g->wid);
81 /* calls o_flush() if necessary */
82 void out(char *s, ...)
84 va_list ap;
85 o_flush();
86 va_start(ap, s);
87 outvf(s, ap);
88 va_end(ap);
91 static void out_fontup(int fid)
93 char fnname[FNLEN];
94 struct font *fn;
95 if (o_m != p_m) {
96 out("%d %d %d rgb\n", CLR_R(o_m), CLR_G(o_m), CLR_B(o_m));
97 p_m = o_m;
99 if (fid != p_f || o_s != p_s) {
100 fn = dev_font(fid);
101 out("%d /%s f\n", o_s, font_name(fn));
102 p_f = fid;
103 p_s = o_s;
104 sprintf(fnname, " %s ", font_name(fn));
105 if (!strstr(o_fonts, fnname))
106 sprintf(strchr(o_fonts, '\0'), "%s ", font_name(fn));
110 void outc(char *c)
112 struct glyph *g;
113 struct font *fn;
114 g = dev_glyph(c, o_f);
115 fn = g ? g->font : dev_font(o_f);
116 if (!g) {
117 outrel(*c == ' ' && fn ? font_swid(fn, o_s) : 1, 0);
118 return;
120 out_fontup(dev_fontid(fn));
121 o_queue(g);
124 void outh(int h)
126 o_h = h;
129 void outv(int v)
131 o_v = v;
134 void outrel(int h, int v)
136 o_h += h;
137 o_v += v;
140 void outfont(int f)
142 if (dev_font(f))
143 o_f = f;
146 /* a font was mounted at pos f */
147 void outmnt(int f)
149 if (p_f == f)
150 p_f = -1;
153 void outsize(int s)
155 if (s > 0)
156 o_s = s;
159 void outcolor(int c)
161 o_m = c;
164 void outrotate(int deg)
166 o_flush();
167 out_fontup(o_f);
168 if (o_rdeg)
169 outf("%d %d %d rot\n", -o_rdeg, o_rh, o_rv);
170 o_rdeg = deg;
171 o_rh = o_h;
172 o_rv = o_v;
173 outf("%d %d %d rot\n", deg, o_h, o_v);
176 static int draw_path; /* number of path segments */
177 static int draw_point; /* point was set for postscript newpath */
179 static void drawmv(void)
181 if (!draw_point)
182 outf("%d %d m ", o_h, o_v);
183 draw_point = 1;
186 /* start a multi-segment path */
187 void drawmbeg(char *s)
189 o_flush();
190 out_fontup(o_f);
191 draw_path = 1;
192 outf("gsave newpath %s\n", s);
195 /* end a multi-segment path */
196 void drawmend(char *s)
198 draw_path = 0;
199 draw_point = 0;
200 outf("%s grestore\n", s);
203 void drawbeg(void)
205 o_flush();
206 out_fontup(o_f);
207 if (draw_path)
208 return;
209 outf("newpath ");
212 void drawend(int close, int fill)
214 if (draw_path)
215 return;
216 draw_point = 0;
217 if (close)
218 outf("closepath ");
219 if (fill)
220 outf("fill\n");
221 else
222 outf("stroke\n");
225 void drawl(int h, int v)
227 drawmv();
228 outrel(h, v);
229 outf("%d %d drawl ", o_h, o_v);
232 void drawc(int c)
234 drawmv();
235 outrel(c, 0);
236 outf("%d %d drawe ", c, c);
239 void drawe(int h, int v)
241 drawmv();
242 outrel(h, 0);
243 outf("%d %d drawe ", h, v);
246 void drawa(int h1, int v1, int h2, int v2)
248 drawmv();
249 outf("%d %d %d %d drawa ", h1, v1, h2, v2);
250 outrel(h1 + h2, v1 + v2);
253 void draws(int h1, int v1, int h2, int v2)
255 drawmv();
256 outf("%d %d %d %d %d %d draws ", o_h, o_v, o_h + h1, o_v + v1,
257 o_h + h1 + h2, o_v + v1 + v2);
258 outrel(h1, v1);
261 void outeps(char *eps, int hwid, int vwid)
263 char buf[1 << 12];
264 int llx, lly, urx, ury;
265 FILE *filp;
266 int nbb, ver;
267 if (!(filp = fopen(eps, "r")))
268 return;
269 if (!fgets(buf, sizeof(buf), filp)) {
270 fprintf(stderr, "warning: file %s is empty\n", eps);
271 fclose(filp);
272 return;
274 if (sscanf(buf, "%%!PS-Adobe-%d.%d EPSF-%d.%d", &ver, &ver, &ver, &ver) != 4) {
275 fprintf(stderr, "warning: unsupported EPSF header in %s\n", eps);
276 fclose(filp);
277 return;
279 nbb = 0;
280 while (fgets(buf, sizeof(buf), filp))
281 if (!strncmp(buf, "%%BoundingBox: ", 15))
282 if ((nbb = sscanf(buf + 15, "%d %d %d %d",
283 &llx, &lly, &urx, &ury)) == 4)
284 break;
285 fclose(filp);
286 if (nbb < 4) /* no BoundingBox comment */
287 return;
288 if (hwid <= 0 && vwid <= 0)
289 hwid = (urx - llx) * dev_res / 72;
290 if (vwid <= 0)
291 vwid = (ury - lly) * hwid / (urx - llx);
292 if (hwid <= 0)
293 hwid = (urx - llx) * vwid / (ury - lly);
294 /* output the EPS file */
295 o_flush();
296 out_fontup(o_f);
297 outf("%d %d %d %d %d %d %d %d EPSFBEG\n",
298 llx, lly, hwid, urx - llx, vwid, ury - lly, o_h, o_v);
299 outf("%%%%BeginDocument: %s\n", eps);
300 filp = fopen(eps, "r");
301 while (fgets(buf, sizeof(buf), filp))
302 out("%s", buf);
303 fclose(filp);
304 outf("%%%%EndDocument\n");
305 outf("EPSFEND\n");
308 void outpdf(char *pdf, int hwid, int vwid)
312 void outlink(char *lnk, int hwid, int vwid)
314 o_flush();
315 if (lnk[0] == '#' || isdigit((unsigned char) lnk[0])) {
316 outf("[ /Rect [ %d %d t %d %d t ] %s%s "
317 "/Subtype /Link /LNK pdfmark\n",
318 o_h, o_v, o_h + hwid, o_v + vwid,
319 lnk[0] == '#' ? "/Dest /" : "/Page ",
320 lnk[0] == '#' ? lnk + 1 : lnk);
321 } else {
322 outf("[ /Rect [ %d %d t %d %d t ] "
323 "/Action << /Subtype /URI /URI %s >> /Open true "
324 "/Subtype /Link /LNK pdfmark\n",
325 o_h, o_v, o_h + hwid, o_v + vwid, pdftext_static(lnk));
329 void outname(int n, char (*desc)[64], int *page, int *off)
331 int i;
332 o_flush();
333 for (i = 0; i < n; i++) {
334 outf("[ /Dest /%s", desc[i]);
335 outf(" /Page %d", page[i]);
336 if (off[i] > 0)
337 outf(" /View [/XYZ null %d null]",
338 (ps_height - off[i]) * 72 / dev_res);
339 outf(" /DEST pdfmark\n");
343 void outmark(int n, char (*desc)[256], int *page, int *off, int *level)
345 int i, j;
346 o_flush();
347 for (i = 0; i < n; i++) {
348 int cnt = 0;
349 for (j = i + 1; j < n && level[j] > level[i]; j++)
350 if (level[j] == level[i] + 1)
351 cnt++;
352 outf("[ /Title %s", pdftext_static(desc[i]));
353 if (page[i] > 0)
354 outf(" /Page %d", page[i]);
355 if (cnt > 0)
356 outf(" /Count %d", cnt);
357 if (off[i] > 0)
358 outf(" /View [/XYZ null %d null]",
359 (ps_height - off[i]) * 72 / dev_res);
360 outf(" /OUT pdfmark\n");
364 void outinfo(char *kwd, char *val)
366 if (!strcmp("Author", kwd))
367 snprintf(ps_author, sizeof(ps_author), "%s", val);
368 if (!strcmp("Title", kwd))
369 snprintf(ps_title, sizeof(ps_title), "%s", val);
372 void outset(char *var, char *val)
376 void docpagebeg(int n)
378 out("%%%%Page: %d %d\n", n, n);
379 out("/saveobj save def\n");
380 out("mark\n");
381 out("%d pagesetup\n", n);
384 void docpageend(int n)
386 out("cleartomark\n");
387 out("showpage\n");
388 out("saveobj restore\n");
391 void doctrailer(int pages)
393 out("[");
394 if (ps_title[0])
395 out(" /Title %s", pdftext_static(ps_title));
396 if (ps_author[0])
397 out(" /Author %s", pdftext_static(ps_author));
398 out(" /Creator (Neatroff) /DOCINFO pdfmark\n");
399 out("%%%%Trailer\n");
400 out("done\n");
401 out("%%%%DocumentFonts: %s\n", o_fonts);
402 out("%%%%Pages: %d\n", pages);
403 out("%%%%EOF\n");
406 static char *prolog =
407 "/setup {\n"
408 " counttomark 2 idiv {def} repeat pop\n"
409 " /scaling 72 resolution div def\n"
410 " linewidth setlinewidth\n"
411 " 1 setlinecap\n"
412 " 0 pagesize 1 get translate\n"
413 " scaling scaling scale\n"
414 " 0 0 moveto\n"
415 "} def\n"
416 "\n"
417 "/pagesetup {\n"
418 " /page exch def\n"
419 " currentdict /pagedict known currentdict page known and {\n"
420 " page load pagedict exch get cvx exec\n"
421 " } if\n"
422 "} def\n"
423 "\n"
424 "/pdfmark where\n"
425 " { pop globaldict /?pdfmark /exec load put }\n"
426 " { globaldict begin\n"
427 " /?pdfmark /pop load def\n"
428 " /pdfmark /cleartomark load def\n"
429 " end }\n"
430 " ifelse\n"
431 "\n"
432 "/t {neg} bind def\n"
433 "/w {neg moveto show} bind def\n"
434 "/m {neg moveto} bind def\n"
435 "/g {neg moveto {glyphshow} forall} bind def\n"
436 "/rgb {255 div 3 1 roll 255 div 3 1 roll 255 div 3 1 roll setrgbcolor} bind def\n"
437 "/rot {/y exch def /x exch def x y neg translate rotate x neg y translate} bind def\n"
438 "/done {/lastpage where {pop lastpage} if} def\n"
439 "\n"
440 "% caching fonts, as selectfont is supposed to be doing\n"
441 "/fncache 16 dict def\n"
442 "/selectfont_append { fncache exch dup findfont put } bind def\n"
443 "/selectfont_cached {\n"
444 " exch dup fncache exch known not { dup selectfont_append } if\n"
445 " fncache exch get exch scalefont setfont\n"
446 "} bind def\n"
447 "/f {\n"
448 " exch dup 3 1 roll scaling div selectfont_cached\n"
449 " linewidth mul scaling 10 mul div setlinewidth\n"
450 "} bind def\n"
451 "\n"
452 "/savedmatrix matrix def\n"
453 "/drawl {\n"
454 " neg lineto\n"
455 "} bind def\n"
456 "/drawe {\n"
457 " savedmatrix currentmatrix pop scale\n"
458 " .5 0 rmoveto currentpoint .5 0 rmoveto .5 0 360 arc\n"
459 " savedmatrix setmatrix\n"
460 "} bind def\n"
461 "/drawa {\n"
462 " /dy2 exch def\n"
463 " /dx2 exch def\n"
464 " /dy1 exch def\n"
465 " /dx1 exch def\n"
466 " currentpoint dy1 neg add exch dx1 add exch\n"
467 " dx1 dx1 mul dy1 dy1 mul add sqrt\n"
468 " dy1 dx1 neg atan\n"
469 " dy2 neg dx2 atan\n"
470 " arc\n"
471 "} bind def\n"
472 "/draws {\n"
473 " /y2 exch def\n"
474 " /x2 exch def\n"
475 " /y1 exch def\n"
476 " /x1 exch def\n"
477 " /y0 exch def\n"
478 " /x0 exch def\n"
479 " x0 5 x1 mul add 6 div\n"
480 " y0 5 y1 mul add -6 div\n"
481 " x2 5 x1 mul add 6 div\n"
482 " y2 5 y1 mul add -6 div\n"
483 " x1 x2 add 2 div\n"
484 " y1 y2 add -2 div\n"
485 " curveto\n"
486 "} bind def\n"
487 "% including EPS files\n"
488 "/EPSFBEG {\n"
489 " /epsf_state save def\n"
490 " neg translate\n"
491 " div 3 1 roll div exch scale\n"
492 " neg exch neg exch translate\n"
493 " /dict_count countdictstack def\n"
494 " /op_count count 1 sub def\n"
495 " userdict begin\n"
496 " /showpage { } def\n"
497 " 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n"
498 " 10 setmiterlimit [ ] 0 setdash newpath\n"
499 "} bind def\n"
500 "/EPSFEND {\n"
501 " count op_count sub {pop} repeat\n"
502 " countdictstack dict_count sub {end} repeat\n"
503 " epsf_state restore\n"
504 "} bind def\n";
506 /* pagewidth and pageheight are in tenths of a millimetre */
507 void docheader(char *title, int pagewidth, int pageheight, int linewidth)
509 ps_height = pageheight * dev_res / 254;
510 out("%%!PS-Adobe-2.0\n");
511 out("%%%%Version: 1.0\n");
512 if (title)
513 out("%%%%Title: %s\n", title);
514 out("%%%%Creator: Neatroff\n");
515 out("%%%%DocumentFonts: (atend)\n");
516 out("%%%%Pages: (atend)\n");
517 out("%%%%EndComments\n");
519 out("%%%%BeginProlog\n");
520 out("/resolution %d def\n", dev_res);
521 out("/pagesize [%d %d] def\n", (pagewidth * 72 + 127) / 254,
522 (pageheight * 72 + 127) / 254);
523 out("/linewidth %d.%02d def\n\n", linewidth / 100, linewidth % 100);
524 out("%s", prolog);
525 out("%%%%EndProlog\n");
526 out("%%%%BeginSetup\n");
527 out("<< /PageSize pagesize /ImagingBBox null >> setpagedevice\n");
528 out("mark\n");
529 out("setup\n");
530 out("%%%%EndSetup\n");