]> git.cameronkatri.com Git - mandoc.git/blob - term_ps.c
Add in -Opaper=xxx support for -Tps postscript. This doesn't have any
[mandoc.git] / term_ps.c
1 /* $Id: term_ps.c,v 1.17 2010/06/29 14:53:14 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/param.h>
22
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "out.h"
32 #include "main.h"
33 #include "term.h"
34
35 struct glyph {
36 int wx; /* WX in AFM */
37 };
38
39 struct font {
40 const char *name; /* FontName in AFM */
41 #define MAXCHAR 95 /* total characters we can handle */
42 struct glyph gly[MAXCHAR]; /* glyph metrics */
43 };
44
45 /*
46 * We define, for the time being, three fonts: bold, oblique/italic, and
47 * normal (roman). The following table hard-codes the font metrics for
48 * ASCII, i.e., 32--127.
49 */
50
51 static const struct font fonts[TERMFONT__MAX] = {
52 { "Courier", {
53 { 600 },
54 { 600 },
55 { 600 },
56 { 600 },
57 { 600 },
58 { 600 },
59 { 600 },
60 { 600 },
61 { 600 },
62 { 600 },
63 { 600 },
64 { 600 },
65 { 600 },
66 { 600 },
67 { 600 },
68 { 600 },
69 { 600 },
70 { 600 },
71 { 600 },
72 { 600 },
73 { 600 },
74 { 600 },
75 { 600 },
76 { 600 },
77 { 600 },
78 { 600 },
79 { 600 },
80 { 600 },
81 { 600 },
82 { 600 },
83 { 600 },
84 { 600 },
85 { 600 },
86 { 600 },
87 { 600 },
88 { 600 },
89 { 600 },
90 { 600 },
91 { 600 },
92 { 600 },
93 { 600 },
94 { 600 },
95 { 600 },
96 { 600 },
97 { 600 },
98 { 600 },
99 { 600 },
100 { 600 },
101 { 600 },
102 { 600 },
103 { 600 },
104 { 600 },
105 { 600 },
106 { 600 },
107 { 600 },
108 { 600 },
109 { 600 },
110 { 600 },
111 { 600 },
112 { 600 },
113 { 600 },
114 { 600 },
115 { 600 },
116 { 600 },
117 { 600 },
118 { 600 },
119 { 600 },
120 { 600 },
121 { 600 },
122 { 600 },
123 { 600 },
124 { 600 },
125 { 600 },
126 { 600 },
127 { 600 },
128 { 600 },
129 { 600 },
130 { 600 },
131 { 600 },
132 { 600 },
133 { 600 },
134 { 600 },
135 { 600 },
136 { 600 },
137 { 600 },
138 { 600 },
139 { 600 },
140 { 600 },
141 { 600 },
142 { 600 },
143 { 600 },
144 { 600 },
145 { 600 },
146 { 600 },
147 { 600 },
148 } },
149 { "Courier-Bold", {
150 { 600 },
151 { 600 },
152 { 600 },
153 { 600 },
154 { 600 },
155 { 600 },
156 { 600 },
157 { 600 },
158 { 600 },
159 { 600 },
160 { 600 },
161 { 600 },
162 { 600 },
163 { 600 },
164 { 600 },
165 { 600 },
166 { 600 },
167 { 600 },
168 { 600 },
169 { 600 },
170 { 600 },
171 { 600 },
172 { 600 },
173 { 600 },
174 { 600 },
175 { 600 },
176 { 600 },
177 { 600 },
178 { 600 },
179 { 600 },
180 { 600 },
181 { 600 },
182 { 600 },
183 { 600 },
184 { 600 },
185 { 600 },
186 { 600 },
187 { 600 },
188 { 600 },
189 { 600 },
190 { 600 },
191 { 600 },
192 { 600 },
193 { 600 },
194 { 600 },
195 { 600 },
196 { 600 },
197 { 600 },
198 { 600 },
199 { 600 },
200 { 600 },
201 { 600 },
202 { 600 },
203 { 600 },
204 { 600 },
205 { 600 },
206 { 600 },
207 { 600 },
208 { 600 },
209 { 600 },
210 { 600 },
211 { 600 },
212 { 600 },
213 { 600 },
214 { 600 },
215 { 600 },
216 { 600 },
217 { 600 },
218 { 600 },
219 { 600 },
220 { 600 },
221 { 600 },
222 { 600 },
223 { 600 },
224 { 600 },
225 { 600 },
226 { 600 },
227 { 600 },
228 { 600 },
229 { 600 },
230 { 600 },
231 { 600 },
232 { 600 },
233 { 600 },
234 { 600 },
235 { 600 },
236 { 600 },
237 { 600 },
238 { 600 },
239 { 600 },
240 { 600 },
241 { 600 },
242 { 600 },
243 { 600 },
244 { 600 },
245 } },
246 { "Courier-Oblique", {
247 { 600 },
248 { 600 },
249 { 600 },
250 { 600 },
251 { 600 },
252 { 600 },
253 { 600 },
254 { 600 },
255 { 600 },
256 { 600 },
257 { 600 },
258 { 600 },
259 { 600 },
260 { 600 },
261 { 600 },
262 { 600 },
263 { 600 },
264 { 600 },
265 { 600 },
266 { 600 },
267 { 600 },
268 { 600 },
269 { 600 },
270 { 600 },
271 { 600 },
272 { 600 },
273 { 600 },
274 { 600 },
275 { 600 },
276 { 600 },
277 { 600 },
278 { 600 },
279 { 600 },
280 { 600 },
281 { 600 },
282 { 600 },
283 { 600 },
284 { 600 },
285 { 600 },
286 { 600 },
287 { 600 },
288 { 600 },
289 { 600 },
290 { 600 },
291 { 600 },
292 { 600 },
293 { 600 },
294 { 600 },
295 { 600 },
296 { 600 },
297 { 600 },
298 { 600 },
299 { 600 },
300 { 600 },
301 { 600 },
302 { 600 },
303 { 600 },
304 { 600 },
305 { 600 },
306 { 600 },
307 { 600 },
308 { 600 },
309 { 600 },
310 { 600 },
311 { 600 },
312 { 600 },
313 { 600 },
314 { 600 },
315 { 600 },
316 { 600 },
317 { 600 },
318 { 600 },
319 { 600 },
320 { 600 },
321 { 600 },
322 { 600 },
323 { 600 },
324 { 600 },
325 { 600 },
326 { 600 },
327 { 600 },
328 { 600 },
329 { 600 },
330 { 600 },
331 { 600 },
332 { 600 },
333 { 600 },
334 { 600 },
335 { 600 },
336 { 600 },
337 { 600 },
338 { 600 },
339 { 600 },
340 { 600 },
341 { 600 },
342 } },
343 };
344
345 /* These work the buffer used by the header and footer. */
346 #define PS_BUFSLOP 128
347 #define PS_GROWBUF(p, sz) \
348 do if ((p)->engine.ps.psmargcur + (sz) > \
349 (p)->engine.ps.psmargsz) { \
350 (p)->engine.ps.psmargsz += /* CONSTCOND */ \
351 MAX(PS_BUFSLOP, (sz)); \
352 (p)->engine.ps.psmarg = realloc \
353 ((p)->engine.ps.psmarg, \
354 (p)->engine.ps.psmargsz); \
355 if (NULL == (p)->engine.ps.psmarg) { \
356 perror(NULL); \
357 exit(EXIT_FAILURE); \
358 } \
359 } while (/* CONSTCOND */ 0)
360
361
362 static void ps_letter(struct termp *, char);
363 static void ps_begin(struct termp *);
364 static void ps_end(struct termp *);
365 static void ps_advance(struct termp *, size_t);
366 static void ps_endline(struct termp *);
367 static void ps_fclose(struct termp *);
368 static size_t ps_width(const struct termp *, char);
369 static void ps_pclose(struct termp *);
370 static void ps_pletter(struct termp *, int);
371 static void ps_printf(struct termp *, const char *, ...);
372 static void ps_putchar(struct termp *, char);
373 static void ps_setfont(struct termp *, enum termfont);
374
375
376 void *
377 ps_alloc(char *outopts)
378 {
379 struct termp *p;
380 size_t pagex, pagey, margin;
381 const char *toks[2];
382 char *v;
383
384 if (NULL == (p = term_alloc(TERMENC_ASCII)))
385 return(NULL);
386
387 /* Default is USA letter. */
388 pagex = 612;
389 pagey = 792;
390 margin = 72;
391
392 p->type = TERMTYPE_PS;
393 p->letter = ps_letter;
394 p->begin = ps_begin;
395 p->end = ps_end;
396 p->advance = ps_advance;
397 p->endline = ps_endline;
398 p->width = ps_width;
399
400 toks[0] = "paper";
401 toks[1] = NULL;
402
403 while (outopts && *outopts)
404 switch (getsubopt(&outopts, UNCONST(toks), &v)) {
405 case (0):
406 if (0 == strcasecmp(v, "a4")) {
407 pagex = 595;
408 pagey = 842;
409 } else if (0 == strcasecmp(v, "letter")) {
410 pagex = 612;
411 pagey = 792;
412 }
413 break;
414 default:
415 break;
416 }
417
418 assert(margin * 2 < pagex);
419 assert(margin * 2 < pagey);
420
421 p->engine.ps.width = pagex;
422 p->engine.ps.height = pagey;
423 p->engine.ps.header = pagey - (margin / 2);
424 p->engine.ps.top = pagey - margin;
425 p->engine.ps.footer = (margin / 2);
426 p->engine.ps.bottom = margin;
427 p->engine.ps.left = margin;
428 p->engine.ps.lineheight = 12;
429
430 p->defrmargin = pagex - (margin * 2);
431 return(p);
432 }
433
434
435 void
436 ps_free(void *arg)
437 {
438 struct termp *p;
439
440 p = (struct termp *)arg;
441
442 if (p->engine.ps.psmarg)
443 free(p->engine.ps.psmarg);
444
445 term_free(p);
446 }
447
448
449 static void
450 ps_printf(struct termp *p, const char *fmt, ...)
451 {
452 va_list ap;
453 int pos;
454
455 va_start(ap, fmt);
456
457 /*
458 * If we're running in regular mode, then pipe directly into
459 * vprintf(). If we're processing margins, then push the data
460 * into our growable margin buffer.
461 */
462
463 if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
464 vprintf(fmt, ap);
465 va_end(ap);
466 return;
467 }
468
469 /*
470 * XXX: I assume that the in-margin print won't exceed
471 * PS_BUFSLOP (128 bytes), which is reasonable but still an
472 * assumption that will cause pukeage if it's not the case.
473 */
474
475 PS_GROWBUF(p, PS_BUFSLOP);
476
477 pos = (int)p->engine.ps.psmargcur;
478 vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
479 p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
480
481 va_end(ap);
482 }
483
484
485 static void
486 ps_putchar(struct termp *p, char c)
487 {
488 int pos;
489
490 /* See ps_printf(). */
491
492 if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
493 putchar(c);
494 return;
495 }
496
497 PS_GROWBUF(p, 2);
498
499 pos = (int)p->engine.ps.psmargcur++;
500 p->engine.ps.psmarg[pos++] = c;
501 p->engine.ps.psmarg[pos] = '\0';
502 }
503
504
505 /* ARGSUSED */
506 static void
507 ps_end(struct termp *p)
508 {
509
510 /*
511 * At the end of the file, do one last showpage. This is the
512 * same behaviour as groff(1) and works for multiple pages as
513 * well as just one.
514 */
515
516 assert(0 == p->engine.ps.psstate);
517 assert('\0' == p->engine.ps.last);
518 assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
519 printf("%s", p->engine.ps.psmarg);
520 p->engine.ps.pages++;
521 printf("showpage\n");
522
523 printf("%%%%Trailer\n");
524 printf("%%%%Pages: %zu\n", p->engine.ps.pages);
525 printf("%%%%EOF\n");
526 }
527
528
529 static void
530 ps_begin(struct termp *p)
531 {
532 time_t t;
533 int i;
534
535 /*
536 * Print margins into margin buffer. Nothing gets output to the
537 * screen yet, so we don't need to initialise the primary state.
538 */
539
540 if (p->engine.ps.psmarg) {
541 assert(p->engine.ps.psmargsz);
542 p->engine.ps.psmarg[0] = '\0';
543 }
544
545 p->engine.ps.psmargcur = 0;
546 p->engine.ps.psstate = PS_MARGINS;
547 p->engine.ps.pscol = p->engine.ps.left;
548 p->engine.ps.psrow = p->engine.ps.header;
549
550 ps_setfont(p, TERMFONT_NONE);
551
552 (*p->headf)(p, p->argf);
553 (*p->endline)(p);
554
555 p->engine.ps.pscol = p->engine.ps.left;
556 p->engine.ps.psrow = p->engine.ps.footer;
557
558 (*p->footf)(p, p->argf);
559 (*p->endline)(p);
560
561 p->engine.ps.psstate &= ~PS_MARGINS;
562
563 assert(0 == p->engine.ps.psstate);
564 assert(p->engine.ps.psmarg);
565 assert('\0' != p->engine.ps.psmarg[0]);
566
567 /*
568 * Print header and initialise page state. Following this,
569 * stuff gets printed to the screen, so make sure we're sane.
570 */
571
572 t = time(NULL);
573
574 printf("%%!PS-Adobe-3.0\n");
575 printf("%%%%Creator: mandoc-%s\n", VERSION);
576 printf("%%%%CreationDate: %s", ctime(&t));
577 printf("%%%%DocumentData: Clean7Bit\n");
578 printf("%%%%Orientation: Portrait\n");
579 printf("%%%%Pages: (atend)\n");
580 printf("%%%%PageOrder: Ascend\n");
581 printf("%%%%Orientation: Portrait\n");
582 printf("%%%%DocumentMedia: Default %zu %zu 0 () ()\n",
583 p->engine.ps.width,
584 p->engine.ps.height);
585 printf("%%%%DocumentNeededResources: font");
586 for (i = 0; i < (int)TERMFONT__MAX; i++)
587 printf(" %s", fonts[i].name);
588 printf("\n%%%%EndComments\n");
589
590 printf("%%%%Page: %zu %zu\n",
591 p->engine.ps.pages + 1,
592 p->engine.ps.pages + 1);
593
594 ps_setfont(p, TERMFONT_NONE);
595 p->engine.ps.pscol = p->engine.ps.left;
596 p->engine.ps.psrow = p->engine.ps.top;
597 }
598
599
600 static void
601 ps_pletter(struct termp *p, int c)
602 {
603 int f;
604
605 /*
606 * If we're not in a PostScript "word" context, then open one
607 * now at the current cursor.
608 */
609
610 if ( ! (PS_INLINE & p->engine.ps.psstate)) {
611 ps_printf(p, "%zu %zu moveto\n(",
612 p->engine.ps.pscol,
613 p->engine.ps.psrow);
614 p->engine.ps.psstate |= PS_INLINE;
615 }
616
617 /*
618 * We need to escape these characters as per the PostScript
619 * specification. We would also escape non-graphable characters
620 * (like tabs), but none of them would get to this point and
621 * it's superfluous to abort() on them.
622 */
623
624 switch (c) {
625 case ('('):
626 /* FALLTHROUGH */
627 case (')'):
628 /* FALLTHROUGH */
629 case ('\\'):
630 ps_putchar(p, '\\');
631 break;
632 default:
633 break;
634 }
635
636 /* Write the character and adjust where we are on the page. */
637
638 f = (int)p->engine.ps.lastf;
639
640 if (c <= 32 || (c - 32 > MAXCHAR)) {
641 ps_putchar(p, ' ');
642 p->engine.ps.pscol += (fonts[f].gly[0].wx / 100);
643 return;
644 }
645
646 ps_putchar(p, c);
647 c -= 32;
648 p->engine.ps.pscol += (fonts[f].gly[c].wx / 100);
649 }
650
651
652 static void
653 ps_pclose(struct termp *p)
654 {
655
656 /*
657 * Spit out that we're exiting a word context (this is a
658 * "partial close" because we don't check the last-char buffer
659 * or anything).
660 */
661
662 if ( ! (PS_INLINE & p->engine.ps.psstate))
663 return;
664
665 ps_printf(p, ") show\n");
666 p->engine.ps.psstate &= ~PS_INLINE;
667 }
668
669
670 static void
671 ps_fclose(struct termp *p)
672 {
673
674 /*
675 * Strong closure: if we have a last-char, spit it out after
676 * checking that we're in the right font mode. This will of
677 * course open a new scope, if applicable.
678 *
679 * Following this, close out any scope that's open.
680 */
681
682 if ('\0' != p->engine.ps.last) {
683 if (p->engine.ps.lastf != TERMFONT_NONE) {
684 ps_pclose(p);
685 ps_setfont(p, TERMFONT_NONE);
686 }
687 ps_pletter(p, p->engine.ps.last);
688 p->engine.ps.last = '\0';
689 }
690
691 if ( ! (PS_INLINE & p->engine.ps.psstate))
692 return;
693
694 ps_pclose(p);
695 }
696
697
698 static void
699 ps_letter(struct termp *p, char c)
700 {
701 char cc;
702
703 /*
704 * State machine dictates whether to buffer the last character
705 * or not. Basically, encoded words are detected by checking if
706 * we're an "8" and switching on the buffer. Then we put "8" in
707 * our buffer, and on the next charater, flush both character
708 * and buffer. Thus, "regular" words are detected by having a
709 * regular character and a regular buffer character.
710 */
711
712 if ('\0' == p->engine.ps.last) {
713 assert(8 != c);
714 p->engine.ps.last = c;
715 return;
716 } else if (8 == p->engine.ps.last) {
717 assert(8 != c);
718 p->engine.ps.last = '\0';
719 } else if (8 == c) {
720 assert(8 != p->engine.ps.last);
721 if ('_' == p->engine.ps.last) {
722 if (p->engine.ps.lastf != TERMFONT_UNDER) {
723 ps_pclose(p);
724 ps_setfont(p, TERMFONT_UNDER);
725 }
726 } else if (p->engine.ps.lastf != TERMFONT_BOLD) {
727 ps_pclose(p);
728 ps_setfont(p, TERMFONT_BOLD);
729 }
730 p->engine.ps.last = c;
731 return;
732 } else {
733 if (p->engine.ps.lastf != TERMFONT_NONE) {
734 ps_pclose(p);
735 ps_setfont(p, TERMFONT_NONE);
736 }
737 cc = p->engine.ps.last;
738 p->engine.ps.last = c;
739 c = cc;
740 }
741
742 ps_pletter(p, c);
743 }
744
745
746 static void
747 ps_advance(struct termp *p, size_t len)
748 {
749
750 /*
751 * Advance some spaces. This can probably be made smarter,
752 * i.e., to have multiple space-separated words in the same
753 * scope, but this is easier: just close out the current scope
754 * and readjust our column settings.
755 */
756
757 ps_fclose(p);
758 p->engine.ps.pscol += len;
759 }
760
761
762 static void
763 ps_endline(struct termp *p)
764 {
765
766 /* Close out any scopes we have open: we're at eoln. */
767
768 ps_fclose(p);
769
770 /*
771 * If we're in the margin, don't try to recalculate our current
772 * row. XXX: if the column tries to be fancy with multiple
773 * lines, we'll do nasty stuff.
774 */
775
776 if (PS_MARGINS & p->engine.ps.psstate)
777 return;
778
779 /*
780 * Put us down a line. If we're at the page bottom, spit out a
781 * showpage and restart our row.
782 */
783
784 p->engine.ps.pscol = p->engine.ps.left;
785 if (p->engine.ps.psrow >= p->engine.ps.lineheight +
786 p->engine.ps.bottom) {
787 p->engine.ps.psrow -= p->engine.ps.lineheight;
788 return;
789 }
790
791 assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
792 printf("%s", p->engine.ps.psmarg);
793 printf("%%%%Page: %zu %zu\n",
794 p->engine.ps.pages + 1,
795 p->engine.ps.pages + 1);
796 printf("showpage\n");
797 p->engine.ps.pages++;
798 p->engine.ps.psrow = p->engine.ps.top;
799 }
800
801
802 static void
803 ps_setfont(struct termp *p, enum termfont f)
804 {
805
806 assert(f < TERMFONT__MAX);
807 ps_printf(p, "/%s 10 selectfont\n", fonts[(int)f].name);
808 p->engine.ps.lastf = f;
809 }
810
811
812 /* ARGSUSED */
813 static size_t
814 ps_width(const struct termp *p, char c)
815 {
816
817 if (c <= 32 || c - 32 >= MAXCHAR)
818 return(fonts[(int)TERMFONT_NONE].gly[0].wx / 100);
819
820 c -= 32;
821 return(fonts[(int)TERMFONT_NONE].gly[(int)c].wx / 100);
822 }