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