]> git.cameronkatri.com Git - mandoc.git/blob - term_ps.c
When support for bold italic font was added to the parsers and to the
[mandoc.git] / term_ps.c
1 /* $Id: term_ps.c,v 1.65 2014/08/24 23:43:13 schwarze Exp $ */
2 /*
3 * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and 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.
9 *
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.
17 */
18 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include "mandoc.h"
32 #include "mandoc_aux.h"
33 #include "out.h"
34 #include "main.h"
35 #include "term.h"
36
37 /* These work the buffer used by the header and footer. */
38 #define PS_BUFSLOP 128
39
40 /* Convert PostScript point "x" to an AFM unit. */
41 #define PNT2AFM(p, x) \
42 (size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
43
44 /* Convert an AFM unit "x" to a PostScript points */
45 #define AFM2PNT(p, x) \
46 ((double)(x) / (1000.0 / (double)(p)->ps->scale))
47
48 struct glyph {
49 unsigned short wx; /* WX in AFM */
50 };
51
52 struct font {
53 const char *name; /* FontName in AFM */
54 #define MAXCHAR 95 /* total characters we can handle */
55 struct glyph gly[MAXCHAR]; /* glyph metrics */
56 };
57
58 struct termp_ps {
59 int flags;
60 #define PS_INLINE (1 << 0) /* we're in a word */
61 #define PS_MARGINS (1 << 1) /* we're in the margins */
62 #define PS_NEWPAGE (1 << 2) /* new page, no words yet */
63 size_t pscol; /* visible column (AFM units) */
64 size_t psrow; /* visible row (AFM units) */
65 char *psmarg; /* margin buf */
66 size_t psmargsz; /* margin buf size */
67 size_t psmargcur; /* cur index in margin buf */
68 char last; /* character buffer */
69 enum termfont lastf; /* last set font */
70 enum termfont nextf; /* building next font here */
71 size_t scale; /* font scaling factor */
72 size_t pages; /* number of pages shown */
73 size_t lineheight; /* line height (AFM units) */
74 size_t top; /* body top (AFM units) */
75 size_t bottom; /* body bottom (AFM units) */
76 size_t height; /* page height (AFM units */
77 size_t width; /* page width (AFM units) */
78 size_t lastwidth; /* page width before last ll */
79 size_t left; /* body left (AFM units) */
80 size_t header; /* header pos (AFM units) */
81 size_t footer; /* footer pos (AFM units) */
82 size_t pdfbytes; /* current output byte */
83 size_t pdflastpg; /* byte of last page mark */
84 size_t pdfbody; /* start of body object */
85 size_t *pdfobjs; /* table of object offsets */
86 size_t pdfobjsz; /* size of pdfobjs */
87 };
88
89 static double ps_hspan(const struct termp *,
90 const struct roffsu *);
91 static size_t ps_width(const struct termp *, int);
92 static void ps_advance(struct termp *, size_t);
93 static void ps_begin(struct termp *);
94 static void ps_closepage(struct termp *);
95 static void ps_end(struct termp *);
96 static void ps_endline(struct termp *);
97 static void ps_fclose(struct termp *);
98 static void ps_growbuf(struct termp *, size_t);
99 static void ps_letter(struct termp *, int);
100 static void ps_pclose(struct termp *);
101 static void ps_pletter(struct termp *, int);
102 #if __GNUC__ - 0 >= 4
103 __attribute__((__format__ (__printf__, 2, 3)))
104 #endif
105 static void ps_printf(struct termp *, const char *, ...);
106 static void ps_putchar(struct termp *, char);
107 static void ps_setfont(struct termp *, enum termfont);
108 static void ps_setwidth(struct termp *, int, size_t);
109 static struct termp *pspdf_alloc(char *);
110 static void pdf_obj(struct termp *, size_t);
111
112 /*
113 * We define, for the time being, three fonts: bold, oblique/italic, and
114 * normal (roman). The following table hard-codes the font metrics for
115 * ASCII, i.e., 32--127.
116 */
117
118 static const struct font fonts[TERMFONT__MAX] = {
119 { "Times-Roman", {
120 { 250 },
121 { 333 },
122 { 408 },
123 { 500 },
124 { 500 },
125 { 833 },
126 { 778 },
127 { 333 },
128 { 333 },
129 { 333 },
130 { 500 },
131 { 564 },
132 { 250 },
133 { 333 },
134 { 250 },
135 { 278 },
136 { 500 },
137 { 500 },
138 { 500 },
139 { 500 },
140 { 500 },
141 { 500 },
142 { 500 },
143 { 500 },
144 { 500 },
145 { 500 },
146 { 278 },
147 { 278 },
148 { 564 },
149 { 564 },
150 { 564 },
151 { 444 },
152 { 921 },
153 { 722 },
154 { 667 },
155 { 667 },
156 { 722 },
157 { 611 },
158 { 556 },
159 { 722 },
160 { 722 },
161 { 333 },
162 { 389 },
163 { 722 },
164 { 611 },
165 { 889 },
166 { 722 },
167 { 722 },
168 { 556 },
169 { 722 },
170 { 667 },
171 { 556 },
172 { 611 },
173 { 722 },
174 { 722 },
175 { 944 },
176 { 722 },
177 { 722 },
178 { 611 },
179 { 333 },
180 { 278 },
181 { 333 },
182 { 469 },
183 { 500 },
184 { 333 },
185 { 444 },
186 { 500 },
187 { 444 },
188 { 500},
189 { 444},
190 { 333},
191 { 500},
192 { 500},
193 { 278},
194 { 278},
195 { 500},
196 { 278},
197 { 778},
198 { 500},
199 { 500},
200 { 500},
201 { 500},
202 { 333},
203 { 389},
204 { 278},
205 { 500},
206 { 500},
207 { 722},
208 { 500},
209 { 500},
210 { 444},
211 { 480},
212 { 200},
213 { 480},
214 { 541},
215 } },
216 { "Times-Bold", {
217 { 250 },
218 { 333 },
219 { 555 },
220 { 500 },
221 { 500 },
222 { 1000 },
223 { 833 },
224 { 333 },
225 { 333 },
226 { 333 },
227 { 500 },
228 { 570 },
229 { 250 },
230 { 333 },
231 { 250 },
232 { 278 },
233 { 500 },
234 { 500 },
235 { 500 },
236 { 500 },
237 { 500 },
238 { 500 },
239 { 500 },
240 { 500 },
241 { 500 },
242 { 500 },
243 { 333 },
244 { 333 },
245 { 570 },
246 { 570 },
247 { 570 },
248 { 500 },
249 { 930 },
250 { 722 },
251 { 667 },
252 { 722 },
253 { 722 },
254 { 667 },
255 { 611 },
256 { 778 },
257 { 778 },
258 { 389 },
259 { 500 },
260 { 778 },
261 { 667 },
262 { 944 },
263 { 722 },
264 { 778 },
265 { 611 },
266 { 778 },
267 { 722 },
268 { 556 },
269 { 667 },
270 { 722 },
271 { 722 },
272 { 1000 },
273 { 722 },
274 { 722 },
275 { 667 },
276 { 333 },
277 { 278 },
278 { 333 },
279 { 581 },
280 { 500 },
281 { 333 },
282 { 500 },
283 { 556 },
284 { 444 },
285 { 556 },
286 { 444 },
287 { 333 },
288 { 500 },
289 { 556 },
290 { 278 },
291 { 333 },
292 { 556 },
293 { 278 },
294 { 833 },
295 { 556 },
296 { 500 },
297 { 556 },
298 { 556 },
299 { 444 },
300 { 389 },
301 { 333 },
302 { 556 },
303 { 500 },
304 { 722 },
305 { 500 },
306 { 500 },
307 { 444 },
308 { 394 },
309 { 220 },
310 { 394 },
311 { 520 },
312 } },
313 { "Times-Italic", {
314 { 250 },
315 { 333 },
316 { 420 },
317 { 500 },
318 { 500 },
319 { 833 },
320 { 778 },
321 { 333 },
322 { 333 },
323 { 333 },
324 { 500 },
325 { 675 },
326 { 250 },
327 { 333 },
328 { 250 },
329 { 278 },
330 { 500 },
331 { 500 },
332 { 500 },
333 { 500 },
334 { 500 },
335 { 500 },
336 { 500 },
337 { 500 },
338 { 500 },
339 { 500 },
340 { 333 },
341 { 333 },
342 { 675 },
343 { 675 },
344 { 675 },
345 { 500 },
346 { 920 },
347 { 611 },
348 { 611 },
349 { 667 },
350 { 722 },
351 { 611 },
352 { 611 },
353 { 722 },
354 { 722 },
355 { 333 },
356 { 444 },
357 { 667 },
358 { 556 },
359 { 833 },
360 { 667 },
361 { 722 },
362 { 611 },
363 { 722 },
364 { 611 },
365 { 500 },
366 { 556 },
367 { 722 },
368 { 611 },
369 { 833 },
370 { 611 },
371 { 556 },
372 { 556 },
373 { 389 },
374 { 278 },
375 { 389 },
376 { 422 },
377 { 500 },
378 { 333 },
379 { 500 },
380 { 500 },
381 { 444 },
382 { 500 },
383 { 444 },
384 { 278 },
385 { 500 },
386 { 500 },
387 { 278 },
388 { 278 },
389 { 444 },
390 { 278 },
391 { 722 },
392 { 500 },
393 { 500 },
394 { 500 },
395 { 500 },
396 { 389 },
397 { 389 },
398 { 278 },
399 { 500 },
400 { 444 },
401 { 667 },
402 { 444 },
403 { 444 },
404 { 389 },
405 { 400 },
406 { 275 },
407 { 400 },
408 { 541 },
409 } },
410 { "Times-BoldItalic", {
411 { 250 },
412 { 389 },
413 { 555 },
414 { 500 },
415 { 500 },
416 { 833 },
417 { 778 },
418 { 333 },
419 { 333 },
420 { 333 },
421 { 500 },
422 { 570 },
423 { 250 },
424 { 333 },
425 { 250 },
426 { 278 },
427 { 500 },
428 { 500 },
429 { 500 },
430 { 500 },
431 { 500 },
432 { 500 },
433 { 500 },
434 { 500 },
435 { 500 },
436 { 500 },
437 { 333 },
438 { 333 },
439 { 570 },
440 { 570 },
441 { 570 },
442 { 500 },
443 { 832 },
444 { 667 },
445 { 667 },
446 { 667 },
447 { 722 },
448 { 667 },
449 { 667 },
450 { 722 },
451 { 778 },
452 { 389 },
453 { 500 },
454 { 667 },
455 { 611 },
456 { 889 },
457 { 722 },
458 { 722 },
459 { 611 },
460 { 722 },
461 { 667 },
462 { 556 },
463 { 611 },
464 { 722 },
465 { 667 },
466 { 889 },
467 { 667 },
468 { 611 },
469 { 611 },
470 { 333 },
471 { 278 },
472 { 333 },
473 { 570 },
474 { 500 },
475 { 333 },
476 { 500 },
477 { 500 },
478 { 444 },
479 { 500 },
480 { 444 },
481 { 333 },
482 { 500 },
483 { 556 },
484 { 278 },
485 { 278 },
486 { 500 },
487 { 278 },
488 { 778 },
489 { 556 },
490 { 500 },
491 { 500 },
492 { 500 },
493 { 389 },
494 { 389 },
495 { 278 },
496 { 556 },
497 { 444 },
498 { 667 },
499 { 500 },
500 { 444 },
501 { 389 },
502 { 348 },
503 { 220 },
504 { 348 },
505 { 570 },
506 } },
507 };
508
509 void *
510 pdf_alloc(char *outopts)
511 {
512 struct termp *p;
513
514 if (NULL != (p = pspdf_alloc(outopts)))
515 p->type = TERMTYPE_PDF;
516
517 return(p);
518 }
519
520 void *
521 ps_alloc(char *outopts)
522 {
523 struct termp *p;
524
525 if (NULL != (p = pspdf_alloc(outopts)))
526 p->type = TERMTYPE_PS;
527
528 return(p);
529 }
530
531 static struct termp *
532 pspdf_alloc(char *outopts)
533 {
534 struct termp *p;
535 unsigned int pagex, pagey;
536 size_t marginx, marginy, lineheight;
537 const char *toks[2];
538 const char *pp;
539 char *v;
540
541 p = mandoc_calloc(1, sizeof(struct termp));
542 p->enc = TERMENC_ASCII;
543 p->ps = mandoc_calloc(1, sizeof(struct termp_ps));
544
545 p->advance = ps_advance;
546 p->begin = ps_begin;
547 p->end = ps_end;
548 p->endline = ps_endline;
549 p->hspan = ps_hspan;
550 p->letter = ps_letter;
551 p->setwidth = ps_setwidth;
552 p->width = ps_width;
553
554 toks[0] = "paper";
555 toks[1] = NULL;
556
557 pp = NULL;
558
559 while (outopts && *outopts)
560 switch (getsubopt(&outopts, UNCONST(toks), &v)) {
561 case 0:
562 pp = v;
563 break;
564 default:
565 break;
566 }
567
568 /* Default to US letter (millimetres). */
569
570 pagex = 216;
571 pagey = 279;
572
573 /*
574 * The ISO-269 paper sizes can be calculated automatically, but
575 * it would require bringing in -lm for pow() and I'd rather not
576 * do that. So just do it the easy way for now. Since this
577 * only happens once, I'm not terribly concerned.
578 */
579
580 if (pp && strcasecmp(pp, "letter")) {
581 if (0 == strcasecmp(pp, "a3")) {
582 pagex = 297;
583 pagey = 420;
584 } else if (0 == strcasecmp(pp, "a4")) {
585 pagex = 210;
586 pagey = 297;
587 } else if (0 == strcasecmp(pp, "a5")) {
588 pagex = 148;
589 pagey = 210;
590 } else if (0 == strcasecmp(pp, "legal")) {
591 pagex = 216;
592 pagey = 356;
593 } else if (2 != sscanf(pp, "%ux%u", &pagex, &pagey))
594 fprintf(stderr, "%s: Unknown paper\n", pp);
595 }
596
597 /*
598 * This MUST be defined before any PNT2AFM or AFM2PNT
599 * calculations occur.
600 */
601
602 p->ps->scale = 11;
603
604 /* Remember millimetres -> AFM units. */
605
606 pagex = PNT2AFM(p, ((double)pagex * 2.834));
607 pagey = PNT2AFM(p, ((double)pagey * 2.834));
608
609 /* Margins are 1/9 the page x and y. */
610
611 marginx = (size_t)((double)pagex / 9.0);
612 marginy = (size_t)((double)pagey / 9.0);
613
614 /* Line-height is 1.4em. */
615
616 lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
617
618 p->ps->width = p->ps->lastwidth = (size_t)pagex;
619 p->ps->height = (size_t)pagey;
620 p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
621 p->ps->top = pagey - marginy;
622 p->ps->footer = (marginy / 2) - (lineheight / 2);
623 p->ps->bottom = marginy;
624 p->ps->left = marginx;
625 p->ps->lineheight = lineheight;
626
627 p->defrmargin = pagex - (marginx * 2);
628 return(p);
629 }
630
631 static void
632 ps_setwidth(struct termp *p, int iop, size_t width)
633 {
634 size_t lastwidth;
635
636 lastwidth = p->ps->width;
637 if (0 < iop)
638 p->ps->width += width;
639 else if (0 > iop)
640 p->ps->width -= width;
641 else
642 p->ps->width = width ? width : p->ps->lastwidth;
643 p->ps->lastwidth = lastwidth;
644 }
645
646 void
647 pspdf_free(void *arg)
648 {
649 struct termp *p;
650
651 p = (struct termp *)arg;
652
653 if (p->ps->psmarg)
654 free(p->ps->psmarg);
655 if (p->ps->pdfobjs)
656 free(p->ps->pdfobjs);
657
658 free(p->ps);
659 term_free(p);
660 }
661
662 static void
663 ps_printf(struct termp *p, const char *fmt, ...)
664 {
665 va_list ap;
666 int pos, len;
667
668 va_start(ap, fmt);
669
670 /*
671 * If we're running in regular mode, then pipe directly into
672 * vprintf(). If we're processing margins, then push the data
673 * into our growable margin buffer.
674 */
675
676 if ( ! (PS_MARGINS & p->ps->flags)) {
677 len = vprintf(fmt, ap);
678 va_end(ap);
679 p->ps->pdfbytes += len < 0 ? 0 : (size_t)len;
680 return;
681 }
682
683 /*
684 * XXX: I assume that the in-margin print won't exceed
685 * PS_BUFSLOP (128 bytes), which is reasonable but still an
686 * assumption that will cause pukeage if it's not the case.
687 */
688
689 ps_growbuf(p, PS_BUFSLOP);
690
691 pos = (int)p->ps->psmargcur;
692 vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
693
694 va_end(ap);
695
696 p->ps->psmargcur = strlen(p->ps->psmarg);
697 }
698
699 static void
700 ps_putchar(struct termp *p, char c)
701 {
702 int pos;
703
704 /* See ps_printf(). */
705
706 if ( ! (PS_MARGINS & p->ps->flags)) {
707 putchar(c);
708 p->ps->pdfbytes++;
709 return;
710 }
711
712 ps_growbuf(p, 2);
713
714 pos = (int)p->ps->psmargcur++;
715 p->ps->psmarg[pos++] = c;
716 p->ps->psmarg[pos] = '\0';
717 }
718
719 static void
720 pdf_obj(struct termp *p, size_t obj)
721 {
722
723 assert(obj > 0);
724
725 if ((obj - 1) >= p->ps->pdfobjsz) {
726 p->ps->pdfobjsz = obj + 128;
727 p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs,
728 p->ps->pdfobjsz, sizeof(size_t));
729 }
730
731 p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
732 ps_printf(p, "%zu 0 obj\n", obj);
733 }
734
735 static void
736 ps_closepage(struct termp *p)
737 {
738 int i;
739 size_t len, base;
740
741 /*
742 * Close out a page that we've already flushed to output. In
743 * PostScript, we simply note that the page must be showed. In
744 * PDF, we must now create the Length, Resource, and Page node
745 * for the page contents.
746 */
747
748 assert(p->ps->psmarg && p->ps->psmarg[0]);
749 ps_printf(p, "%s", p->ps->psmarg);
750
751 if (TERMTYPE_PS != p->type) {
752 ps_printf(p, "ET\n");
753
754 len = p->ps->pdfbytes - p->ps->pdflastpg;
755 base = p->ps->pages * 4 + p->ps->pdfbody;
756
757 ps_printf(p, "endstream\nendobj\n");
758
759 /* Length of content. */
760 pdf_obj(p, base + 1);
761 ps_printf(p, "%zu\nendobj\n", len);
762
763 /* Resource for content. */
764 pdf_obj(p, base + 2);
765 ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
766 ps_printf(p, "/Font <<\n");
767 for (i = 0; i < (int)TERMFONT__MAX; i++)
768 ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
769 ps_printf(p, ">>\n>>\n");
770
771 /* Page node. */
772 pdf_obj(p, base + 3);
773 ps_printf(p, "<<\n");
774 ps_printf(p, "/Type /Page\n");
775 ps_printf(p, "/Parent 2 0 R\n");
776 ps_printf(p, "/Resources %zu 0 R\n", base + 2);
777 ps_printf(p, "/Contents %zu 0 R\n", base);
778 ps_printf(p, ">>\nendobj\n");
779 } else
780 ps_printf(p, "showpage\n");
781
782 p->ps->pages++;
783 p->ps->psrow = p->ps->top;
784 assert( ! (PS_NEWPAGE & p->ps->flags));
785 p->ps->flags |= PS_NEWPAGE;
786 }
787
788 static void
789 ps_end(struct termp *p)
790 {
791 size_t i, xref, base;
792
793 /*
794 * At the end of the file, do one last showpage. This is the
795 * same behaviour as groff(1) and works for multiple pages as
796 * well as just one.
797 */
798
799 if ( ! (PS_NEWPAGE & p->ps->flags)) {
800 assert(0 == p->ps->flags);
801 assert('\0' == p->ps->last);
802 ps_closepage(p);
803 }
804
805 if (TERMTYPE_PS == p->type) {
806 ps_printf(p, "%%%%Trailer\n");
807 ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
808 ps_printf(p, "%%%%EOF\n");
809 return;
810 }
811
812 pdf_obj(p, 2);
813 ps_printf(p, "<<\n/Type /Pages\n");
814 ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
815 (size_t)AFM2PNT(p, p->ps->width),
816 (size_t)AFM2PNT(p, p->ps->height));
817
818 ps_printf(p, "/Count %zu\n", p->ps->pages);
819 ps_printf(p, "/Kids [");
820
821 for (i = 0; i < p->ps->pages; i++)
822 ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3);
823
824 base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4;
825
826 ps_printf(p, "]\n>>\nendobj\n");
827 pdf_obj(p, base);
828 ps_printf(p, "<<\n");
829 ps_printf(p, "/Type /Catalog\n");
830 ps_printf(p, "/Pages 2 0 R\n");
831 ps_printf(p, ">>\n");
832 xref = p->ps->pdfbytes;
833 ps_printf(p, "xref\n");
834 ps_printf(p, "0 %zu\n", base + 1);
835 ps_printf(p, "0000000000 65535 f \n");
836
837 for (i = 0; i < base; i++)
838 ps_printf(p, "%.10zu 00000 n \n",
839 p->ps->pdfobjs[(int)i]);
840
841 ps_printf(p, "trailer\n");
842 ps_printf(p, "<<\n");
843 ps_printf(p, "/Size %zu\n", base + 1);
844 ps_printf(p, "/Root %zu 0 R\n", base);
845 ps_printf(p, "/Info 1 0 R\n");
846 ps_printf(p, ">>\n");
847 ps_printf(p, "startxref\n");
848 ps_printf(p, "%zu\n", xref);
849 ps_printf(p, "%%%%EOF\n");
850 }
851
852 static void
853 ps_begin(struct termp *p)
854 {
855 time_t t;
856 int i;
857
858 /*
859 * Print margins into margin buffer. Nothing gets output to the
860 * screen yet, so we don't need to initialise the primary state.
861 */
862
863 if (p->ps->psmarg) {
864 assert(p->ps->psmargsz);
865 p->ps->psmarg[0] = '\0';
866 }
867
868 /*p->ps->pdfbytes = 0;*/
869 p->ps->psmargcur = 0;
870 p->ps->flags = PS_MARGINS;
871 p->ps->pscol = p->ps->left;
872 p->ps->psrow = p->ps->header;
873
874 ps_setfont(p, TERMFONT_NONE);
875
876 (*p->headf)(p, p->argf);
877 (*p->endline)(p);
878
879 p->ps->pscol = p->ps->left;
880 p->ps->psrow = p->ps->footer;
881
882 (*p->footf)(p, p->argf);
883 (*p->endline)(p);
884
885 p->ps->flags &= ~PS_MARGINS;
886
887 assert(0 == p->ps->flags);
888 assert(p->ps->psmarg);
889 assert('\0' != p->ps->psmarg[0]);
890
891 /*
892 * Print header and initialise page state. Following this,
893 * stuff gets printed to the screen, so make sure we're sane.
894 */
895
896 t = time(NULL);
897
898 if (TERMTYPE_PS == p->type) {
899 ps_printf(p, "%%!PS-Adobe-3.0\n");
900 ps_printf(p, "%%%%CreationDate: %s", ctime(&t));
901 ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
902 ps_printf(p, "%%%%Orientation: Portrait\n");
903 ps_printf(p, "%%%%Pages: (atend)\n");
904 ps_printf(p, "%%%%PageOrder: Ascend\n");
905 ps_printf(p, "%%%%DocumentMedia: "
906 "Default %zu %zu 0 () ()\n",
907 (size_t)AFM2PNT(p, p->ps->width),
908 (size_t)AFM2PNT(p, p->ps->height));
909 ps_printf(p, "%%%%DocumentNeededResources: font");
910
911 for (i = 0; i < (int)TERMFONT__MAX; i++)
912 ps_printf(p, " %s", fonts[i].name);
913
914 ps_printf(p, "\n%%%%EndComments\n");
915 } else {
916 ps_printf(p, "%%PDF-1.1\n");
917 pdf_obj(p, 1);
918 ps_printf(p, "<<\n");
919 ps_printf(p, ">>\n");
920 ps_printf(p, "endobj\n");
921
922 for (i = 0; i < (int)TERMFONT__MAX; i++) {
923 pdf_obj(p, (size_t)i + 3);
924 ps_printf(p, "<<\n");
925 ps_printf(p, "/Type /Font\n");
926 ps_printf(p, "/Subtype /Type1\n");
927 ps_printf(p, "/Name /F%d\n", i);
928 ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
929 ps_printf(p, ">>\n");
930 }
931 }
932
933 p->ps->pdfbody = (size_t)TERMFONT__MAX + 3;
934 p->ps->pscol = p->ps->left;
935 p->ps->psrow = p->ps->top;
936 p->ps->flags |= PS_NEWPAGE;
937 ps_setfont(p, TERMFONT_NONE);
938 }
939
940 static void
941 ps_pletter(struct termp *p, int c)
942 {
943 int f;
944
945 /*
946 * If we haven't opened a page context, then output that we're
947 * in a new page and make sure the font is correctly set.
948 */
949
950 if (PS_NEWPAGE & p->ps->flags) {
951 if (TERMTYPE_PS == p->type) {
952 ps_printf(p, "%%%%Page: %zu %zu\n",
953 p->ps->pages + 1, p->ps->pages + 1);
954 ps_printf(p, "/%s %zu selectfont\n",
955 fonts[(int)p->ps->lastf].name,
956 p->ps->scale);
957 } else {
958 pdf_obj(p, p->ps->pdfbody +
959 p->ps->pages * 4);
960 ps_printf(p, "<<\n");
961 ps_printf(p, "/Length %zu 0 R\n",
962 p->ps->pdfbody + 1 + p->ps->pages * 4);
963 ps_printf(p, ">>\nstream\n");
964 }
965 p->ps->pdflastpg = p->ps->pdfbytes;
966 p->ps->flags &= ~PS_NEWPAGE;
967 }
968
969 /*
970 * If we're not in a PostScript "word" context, then open one
971 * now at the current cursor.
972 */
973
974 if ( ! (PS_INLINE & p->ps->flags)) {
975 if (TERMTYPE_PS != p->type) {
976 ps_printf(p, "BT\n/F%d %zu Tf\n",
977 (int)p->ps->lastf, p->ps->scale);
978 ps_printf(p, "%.3f %.3f Td\n(",
979 AFM2PNT(p, p->ps->pscol),
980 AFM2PNT(p, p->ps->psrow));
981 } else
982 ps_printf(p, "%.3f %.3f moveto\n(",
983 AFM2PNT(p, p->ps->pscol),
984 AFM2PNT(p, p->ps->psrow));
985 p->ps->flags |= PS_INLINE;
986 }
987
988 assert( ! (PS_NEWPAGE & p->ps->flags));
989
990 /*
991 * We need to escape these characters as per the PostScript
992 * specification. We would also escape non-graphable characters
993 * (like tabs), but none of them would get to this point and
994 * it's superfluous to abort() on them.
995 */
996
997 switch (c) {
998 case '(':
999 /* FALLTHROUGH */
1000 case ')':
1001 /* FALLTHROUGH */
1002 case '\\':
1003 ps_putchar(p, '\\');
1004 break;
1005 default:
1006 break;
1007 }
1008
1009 /* Write the character and adjust where we are on the page. */
1010
1011 f = (int)p->ps->lastf;
1012
1013 if (c <= 32 || c - 32 >= MAXCHAR)
1014 c = 32;
1015
1016 ps_putchar(p, (char)c);
1017 c -= 32;
1018 p->ps->pscol += (size_t)fonts[f].gly[c].wx;
1019 }
1020
1021 static void
1022 ps_pclose(struct termp *p)
1023 {
1024
1025 /*
1026 * Spit out that we're exiting a word context (this is a
1027 * "partial close" because we don't check the last-char buffer
1028 * or anything).
1029 */
1030
1031 if ( ! (PS_INLINE & p->ps->flags))
1032 return;
1033
1034 if (TERMTYPE_PS != p->type) {
1035 ps_printf(p, ") Tj\nET\n");
1036 } else
1037 ps_printf(p, ") show\n");
1038
1039 p->ps->flags &= ~PS_INLINE;
1040 }
1041
1042 static void
1043 ps_fclose(struct termp *p)
1044 {
1045
1046 /*
1047 * Strong closure: if we have a last-char, spit it out after
1048 * checking that we're in the right font mode. This will of
1049 * course open a new scope, if applicable.
1050 *
1051 * Following this, close out any scope that's open.
1052 */
1053
1054 if (p->ps->last != '\0') {
1055 assert(p->ps->last != 8);
1056 if (p->ps->nextf != p->ps->lastf) {
1057 ps_pclose(p);
1058 ps_setfont(p, p->ps->nextf);
1059 }
1060 p->ps->nextf = TERMFONT_NONE;
1061 ps_pletter(p, p->ps->last);
1062 p->ps->last = '\0';
1063 }
1064
1065 if ( ! (PS_INLINE & p->ps->flags))
1066 return;
1067
1068 ps_pclose(p);
1069 }
1070
1071 static void
1072 ps_letter(struct termp *p, int arg)
1073 {
1074 char c;
1075
1076 c = arg >= 128 || arg <= 0 ? '?' : arg;
1077
1078 /*
1079 * When receiving an initial character, merely buffer it,
1080 * because a backspace might follow to specify formatting.
1081 * When receiving a backspace, use the buffered character
1082 * to build the font instruction and clear the buffer.
1083 * Only when there are two non-backspace characters in a row,
1084 * activate the font built so far and print the first of them;
1085 * the second, again, merely gets buffered.
1086 * The final character will get printed from ps_fclose().
1087 */
1088
1089 if (c == 8) {
1090 assert(p->ps->last != '\0');
1091 assert(p->ps->last != 8);
1092 if ('_' == p->ps->last) {
1093 switch (p->ps->nextf) {
1094 case TERMFONT_BI:
1095 break;
1096 case TERMFONT_BOLD:
1097 p->ps->nextf = TERMFONT_BI;
1098 break;
1099 default:
1100 p->ps->nextf = TERMFONT_UNDER;
1101 }
1102 } else {
1103 switch (p->ps->nextf) {
1104 case TERMFONT_BI:
1105 break;
1106 case TERMFONT_UNDER:
1107 p->ps->nextf = TERMFONT_BI;
1108 break;
1109 default:
1110 p->ps->nextf = TERMFONT_BOLD;
1111 }
1112 }
1113 } else if (p->ps->last != '\0' && p->ps->last != 8) {
1114 if (p->ps->nextf != p->ps->lastf) {
1115 ps_pclose(p);
1116 ps_setfont(p, p->ps->nextf);
1117 }
1118 p->ps->nextf = TERMFONT_NONE;
1119 ps_pletter(p, p->ps->last);
1120 }
1121 p->ps->last = c;
1122 }
1123
1124 static void
1125 ps_advance(struct termp *p, size_t len)
1126 {
1127
1128 /*
1129 * Advance some spaces. This can probably be made smarter,
1130 * i.e., to have multiple space-separated words in the same
1131 * scope, but this is easier: just close out the current scope
1132 * and readjust our column settings.
1133 */
1134
1135 ps_fclose(p);
1136 p->ps->pscol += len;
1137 }
1138
1139 static void
1140 ps_endline(struct termp *p)
1141 {
1142
1143 /* Close out any scopes we have open: we're at eoln. */
1144
1145 ps_fclose(p);
1146
1147 /*
1148 * If we're in the margin, don't try to recalculate our current
1149 * row. XXX: if the column tries to be fancy with multiple
1150 * lines, we'll do nasty stuff.
1151 */
1152
1153 if (PS_MARGINS & p->ps->flags)
1154 return;
1155
1156 /* Left-justify. */
1157
1158 p->ps->pscol = p->ps->left;
1159
1160 /* If we haven't printed anything, return. */
1161
1162 if (PS_NEWPAGE & p->ps->flags)
1163 return;
1164
1165 /*
1166 * Put us down a line. If we're at the page bottom, spit out a
1167 * showpage and restart our row.
1168 */
1169
1170 if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) {
1171 p->ps->psrow -= p->ps->lineheight;
1172 return;
1173 }
1174
1175 ps_closepage(p);
1176 }
1177
1178 static void
1179 ps_setfont(struct termp *p, enum termfont f)
1180 {
1181
1182 assert(f < TERMFONT__MAX);
1183 p->ps->lastf = f;
1184
1185 /*
1186 * If we're still at the top of the page, let the font-setting
1187 * be delayed until we actually have stuff to print.
1188 */
1189
1190 if (PS_NEWPAGE & p->ps->flags)
1191 return;
1192
1193 if (TERMTYPE_PS == p->type)
1194 ps_printf(p, "/%s %zu selectfont\n",
1195 fonts[(int)f].name, p->ps->scale);
1196 else
1197 ps_printf(p, "/F%d %zu Tf\n",
1198 (int)f, p->ps->scale);
1199 }
1200
1201 static size_t
1202 ps_width(const struct termp *p, int c)
1203 {
1204
1205 if (c <= 32 || c - 32 >= MAXCHAR)
1206 c = 0;
1207 else
1208 c -= 32;
1209
1210 return((size_t)fonts[(int)TERMFONT_NONE].gly[c].wx);
1211 }
1212
1213 static double
1214 ps_hspan(const struct termp *p, const struct roffsu *su)
1215 {
1216 double r;
1217
1218 /*
1219 * All of these measurements are derived by converting from the
1220 * native measurement to AFM units.
1221 */
1222 switch (su->unit) {
1223 case SCALE_BU:
1224 /*
1225 * Traditionally, the default unit is fixed to the
1226 * output media. So this would refer to the point. In
1227 * mandoc(1), however, we stick to the default terminal
1228 * scaling unit so that output is the same regardless
1229 * the media.
1230 */
1231 r = PNT2AFM(p, su->scale * 72.0 / 240.0);
1232 break;
1233 case SCALE_CM:
1234 r = PNT2AFM(p, su->scale * 72.0 / 2.54);
1235 break;
1236 case SCALE_EM:
1237 r = su->scale *
1238 fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
1239 break;
1240 case SCALE_EN:
1241 r = su->scale *
1242 fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
1243 break;
1244 case SCALE_IN:
1245 r = PNT2AFM(p, su->scale * 72.0);
1246 break;
1247 case SCALE_MM:
1248 r = su->scale *
1249 fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0;
1250 break;
1251 case SCALE_PC:
1252 r = PNT2AFM(p, su->scale * 12.0);
1253 break;
1254 case SCALE_PT:
1255 r = PNT2AFM(p, su->scale * 1.0);
1256 break;
1257 case SCALE_VS:
1258 r = su->scale * p->ps->lineheight;
1259 break;
1260 default:
1261 r = su->scale;
1262 break;
1263 }
1264
1265 return(r);
1266 }
1267
1268 static void
1269 ps_growbuf(struct termp *p, size_t sz)
1270 {
1271 if (p->ps->psmargcur + sz <= p->ps->psmargsz)
1272 return;
1273
1274 if (sz < PS_BUFSLOP)
1275 sz = PS_BUFSLOP;
1276
1277 p->ps->psmargsz += sz;
1278 p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz);
1279 }