pdf: recursively import resources from an external PDF
[neatpost.git] / pdf.c
blob5c9e519afb26200d7528ebad1e134af0aac5ff6f
1 /* PDF post-processor functions */
2 #include <fcntl.h>
3 #include <stdarg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include "post.h"
10 static char *pdf_title; /* document title */
11 static int pdf_width; /* page width */
12 static int pdf_height; /* page height */
13 static int pdf_pages; /* pages object id */
14 static int pdf_root; /* root object id */
15 static int pdf_pos; /* current pdf file offset */
16 static int *obj_off; /* object offsets */
17 static int obj_sz, obj_n; /* number of pdf objects */
18 static int *page_id; /* page object ids */
19 static int page_sz, page_n; /* number of pages */
21 static struct sbuf *pg; /* current page contents */
22 static int o_f, o_s, o_m; /* font and size */
23 static int o_h, o_v; /* current user position */
24 static int p_h, p_v; /* current output position */
25 static int o_i, p_i; /* output and pdf fonts (indices into pfont[]) */
26 static int p_f, p_s, p_m; /* output font */
27 static int o_queued; /* queued character type */
28 static char o_iset[1024]; /* fonts accesssed in this page */
29 static int *xobj; /* page xobject object ids */
30 static int xobj_sz, xobj_n; /* number of xobjects */
32 /* loaded PDF fonts */
33 struct pfont {
34 char name[128]; /* font PostScript name */
35 char path[1024]; /* font path */
36 char desc[1024]; /* font descriptor path */
37 int gbeg; /* the first glyph */
38 int gend; /* the last glyph */
39 int sub; /* subfont number */
40 int obj; /* the font object */
41 int des; /* font descriptor */
42 int cid; /* CID-indexed */
45 static struct pfont *pfonts;
46 static int pfonts_n, pfonts_sz;
48 /* print formatted pdf output */
49 static void pdfout(char *s, ...)
51 va_list ap;
52 va_start(ap, s);
53 pdf_pos += vprintf(s, ap);
54 va_end(ap);
57 /* print pdf output */
58 static void pdfouts(char *s)
60 fputs(s, stdout);
61 pdf_pos += strlen(s);
64 /* print pdf output */
65 static void pdfmem(char *s, int len)
67 fwrite(s, len, 1, stdout);
68 pdf_pos += len;
71 /* allocate an object number */
72 static int obj_map(void)
74 if (obj_n == obj_sz) {
75 obj_sz += 1024;
76 obj_off = mextend(obj_off, obj_n, obj_sz, sizeof(obj_off[0]));
78 return obj_n++;
81 /* start the definition of an object */
82 static int obj_beg(int id)
84 if (id <= 0)
85 id = obj_map();
86 obj_off[id] = pdf_pos;
87 pdfout("%d 0 obj\n", id);
88 return id;
91 /* end an object definition */
92 static void obj_end(void)
94 pdfout("endobj\n\n");
97 void out(char *s, ...)
101 /* the length of the clear-text, encrypted, and fixed-content portions */
102 static int type1lengths(char *t1, int l, int *l1, int *l2, int *l3)
104 int i;
105 char *cleartext = t1;
106 char *encrypted = NULL;
107 char *fixedcont = NULL;
108 for (i = 0; i < l - 5 && !encrypted; i++)
109 if (t1[i] == 'e' && !memcmp("eexec", t1 + i, 5))
110 encrypted = t1 + i;
111 if (!encrypted)
112 return 1;
113 for (; i < l - 512 && !fixedcont; i++)
114 if (t1[i] == '0' && !memcmp("00000", t1 + i, 5))
115 fixedcont = t1 + i;
116 *l1 = encrypted - cleartext;
117 *l2 = fixedcont ? fixedcont - cleartext : 0;
118 return 0;
121 /* return font type: 't': TrueType, '1': Type 1, 'o': OpenType */
122 static int fonttype(char *path)
124 char *ext = strrchr(path, '.');
125 if (ext && !strcmp(".ttf", ext))
126 return 't';
127 if (ext && !strcmp(".otf", ext))
128 return 'o';
129 return '1';
132 /* write the object corresponding to the given font */
133 static void pfont_write(struct pfont *ps)
135 int i;
136 int enc_obj;
137 struct font *fn = dev_fontopen(ps->desc);
138 /* the encoding object */
139 enc_obj = obj_beg(0);
140 pdfout("<<\n");
141 pdfout(" /Type /Encoding\n");
142 pdfout(" /Differences [ %d", ps->gbeg % 256);
143 for (i = ps->gbeg; i <= ps->gend; i++)
144 pdfout(" /%s", font_glget(fn, i)->id);
145 pdfout(" ]\n");
146 pdfout(">>\n");
147 obj_end();
148 /* the font object */
149 obj_beg(ps->obj);
150 pdfout("<<\n");
151 pdfout(" /Type /Font\n");
152 if (fonttype(ps->path) == 't')
153 pdfout(" /Subtype /TrueType\n");
154 else
155 pdfout(" /Subtype /Type1\n");
156 pdfout(" /BaseFont /%s\n", ps->name);
157 pdfout(" /FirstChar %d\n", ps->gbeg % 256);
158 pdfout(" /LastChar %d\n", ps->gend % 256);
159 pdfout(" /Widths [");
160 for (i = ps->gbeg; i <= ps->gend; i++)
161 pdfout(" %d", (long) font_glget(fn, i)->wid * 100 * 72 / dev_res);
162 pdfout(" ]\n");
163 pdfout(" /FontDescriptor %d 0 R\n", ps->des);
164 pdfout(" /Encoding %d 0 R\n", enc_obj);
165 pdfout(">>\n");
166 obj_end();
167 font_close(fn);
170 static void encodehex(struct sbuf *d, char *s, int n)
172 static char hex[] = "0123456789ABCDEF";
173 int i;
174 for (i = 0; i < n; i++) {
175 sbuf_chr(d, hex[((unsigned char) s[i]) >> 4]);
176 sbuf_chr(d, hex[((unsigned char) s[i]) & 0x0f]);
177 if (i % 40 == 39 && i + 1 < n)
178 sbuf_chr(d, '\n');
180 sbuf_str(d, ">\n");
183 /* write the object corresponding to this CID font */
184 static void pfont_writecid(struct pfont *ps)
186 int cid_obj;
187 struct font *fn = dev_fontopen(ps->desc);
188 int gcnt = 0;
189 int i;
190 /* CIDFont */
191 cid_obj = obj_beg(0);
192 pdfout("<<\n");
193 pdfout(" /Type /Font\n");
194 pdfout(" /Subtype /CIDFontType2\n");
195 pdfout(" /BaseFont /%s\n", ps->name);
196 pdfout(" /CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>\n");
197 pdfout(" /FontDescriptor %d 0 R\n", ps->des);
198 pdfout(" /DW 1000\n");
199 while (font_glget(fn, gcnt))
200 gcnt++;
201 pdfout(" /W [ %d [", ps->gbeg);
202 for (i = ps->gbeg; i <= ps->gend; i++)
203 pdfout(" %d", (long) font_glget(fn, i)->wid * 100 * 72 / dev_res);
204 pdfout(" ] ]\n");
205 pdfout(">>\n");
206 obj_end();
207 /* the font object */
208 obj_beg(ps->obj);
209 pdfout("<<\n");
210 pdfout(" /Type /Font\n");
211 pdfout(" /Subtype /Type0\n");
212 pdfout(" /BaseFont /%s\n", ps->name);
213 pdfout(" /Encoding /Identity-H\n");
214 pdfout(" /DescendantFonts [%d 0 R]\n", cid_obj);
215 pdfout(">>\n");
216 obj_end();
217 font_close(fn);
220 /* write font descriptor; returns its object ID */
221 static int writedesc(struct font *fn)
223 int str_obj = -1;
224 int des_obj;
225 char buf[1 << 10];
226 int fntype = fonttype(font_path(fn));
227 if (fntype == '1' || fntype == 't') {
228 int fd = open(font_path(fn), O_RDONLY);
229 struct sbuf *ffsb = sbuf_make();
230 struct sbuf *sb = sbuf_make();
231 int l1 = 0, l2 = 0, l3 = 0;
232 int nr;
233 /* reading the font file */
234 while ((nr = read(fd, buf, sizeof(buf))) > 0)
235 sbuf_mem(ffsb, buf, nr);
236 close(fd);
237 l1 = sbuf_len(ffsb);
238 /* initialize Type 1 lengths */
239 if (fntype == '1') {
240 if (type1lengths(sbuf_buf(ffsb), sbuf_len(ffsb),
241 &l1, &l2, &l3))
242 l1 = 0;
243 /* remove the fixed-content portion of the font */
244 if (l3)
245 sbuf_cut(ffsb, l1 + l2);
246 l1 -= l3;
248 /* encoding file contents */
249 encodehex(sb, sbuf_buf(ffsb), sbuf_len(ffsb));
250 /* write font data if it has nonzero length */
251 if (l1) {
252 str_obj = obj_beg(0);
253 pdfout("<<\n");
254 pdfout(" /Filter /ASCIIHexDecode\n");
255 pdfout(" /Length %d\n", sbuf_len(sb));
256 pdfout(" /Length1 %d\n", l1);
257 if (fntype == '1')
258 pdfout(" /Length2 %d\n", l2);
259 if (fntype == '1')
260 pdfout(" /Length3 %d\n", l3);
261 pdfout(">>\n");
262 pdfout("stream\n");
263 pdfouts(sbuf_buf(sb));
264 pdfout("endstream\n");
265 obj_end();
267 sbuf_free(ffsb);
268 sbuf_free(sb);
270 /* the font descriptor */
271 des_obj = obj_beg(0);
272 pdfout("<<\n");
273 pdfout(" /Type /FontDescriptor\n");
274 pdfout(" /FontName /%s\n", font_name(fn));
275 pdfout(" /Flags 32\n");
276 pdfout(" /FontBBox [-1000 -1000 1000 1000]\n");
277 pdfout(" /MissingWidth 1000\n");
278 pdfout(" /StemV 100\n");
279 pdfout(" /ItalicAngle 0\n");
280 pdfout(" /CapHeight 100\n");
281 pdfout(" /Ascent 100\n");
282 pdfout(" /Descent 100\n");
283 if (str_obj >= 0)
284 pdfout(" /FontFile%s %d 0 R\n",
285 fntype == 't' ? "2" : "", str_obj);
286 pdfout(">>\n");
287 obj_end();
288 return des_obj;
291 static int pfont_find(struct glyph *g)
293 struct font *fn = g->font;
294 char *name = font_name(fn);
295 struct pfont *ps = NULL;
296 int fntype = fonttype(font_path(fn));
297 int sub = fntype == '1' ? font_glnum(fn, g) / 256 : 0;
298 int i;
299 for (i = 0; i < pfonts_n; i++)
300 if (!strcmp(name, pfonts[i].name) && pfonts[i].sub == sub)
301 return i;
302 if (pfonts_n == pfonts_sz) {
303 pfonts_sz += 16;
304 pfonts = mextend(pfonts, pfonts_n,
305 pfonts_sz, sizeof(pfonts[0]));
307 ps = &pfonts[pfonts_n];
308 snprintf(ps->name, sizeof(ps->name), "%s", name);
309 snprintf(ps->path, sizeof(ps->path), "%s", font_path(fn));
310 snprintf(ps->desc, sizeof(ps->desc), "%s", font_desc(fn));
311 ps->cid = fntype == 't';
312 ps->obj = obj_map();
313 ps->sub = sub;
314 ps->gbeg = 1 << 20;
315 for (i = 0; i < pfonts_n; i++)
316 if (!strcmp(pfonts[i].name, ps->name))
317 break;
318 if (i < pfonts_n)
319 ps->des = pfonts[i].des;
320 else
321 ps->des = writedesc(fn);
322 return pfonts_n++;
325 static void pfont_done(void)
327 int i;
328 for (i = 0; i < pfonts_n; i++) {
329 if (pfonts[i].cid)
330 pfont_writecid(&pfonts[i]);
331 else
332 pfont_write(&pfonts[i]);
334 free(pfonts);
337 static void o_flush(void)
339 if (o_queued == 1)
340 sbuf_printf(pg, ">] TJ\n");
341 o_queued = 0;
344 static int o_loadfont(struct glyph *g)
346 int fn = pfont_find(g);
347 o_iset[fn] = 1;
348 return fn;
351 /* like pdfpos() but assume that uh and uv are multiplied by 100 */
352 static char *pdfpos00(int uh, int uv)
354 static char buf[64];
355 int h = (long) uh * 72 / dev_res;
356 int v = (long) pdf_height * 100 - (long) uv * 72 / dev_res;
357 sprintf(buf, "%s%d.%02d %s%d.%02d",
358 h < 0 ? "-" : "", abs(h) / 100, abs(h) % 100,
359 v < 0 ? "-" : "", abs(v) / 100, abs(v) % 100);
360 return buf;
363 /* convert troff position to pdf position; returns a static buffer */
364 static char *pdfpos(int uh, int uv)
366 return pdfpos00(uh * 100, uv * 100);
369 /* troff length to thousands of a unit of text space; returns a static buffer */
370 static char *pdfunit(int uh, int sz)
372 static char buf[64];
373 int h = (long) uh * 1000 * 72 / sz / dev_res;
374 sprintf(buf, "%s%d", h < 0 ? "-" : "", abs(h));
375 return buf;
378 /* convert troff color to pdf color; returns a static buffer */
379 static char *pdfcolor(int m)
381 static char buf[64];
382 int r = CLR_R(m) * 1000 / 255;
383 int g = CLR_G(m) * 1000 / 255;
384 int b = CLR_B(m) * 1000 / 255;
385 sbuf_printf(pg, "%d.%03d %d.%03d %d.%03d",
386 r / 1000, r % 1000, g / 1000, g % 1000, b / 1000, b % 1000);
387 return buf;
390 static void o_queue(struct glyph *g)
392 int gid;
393 if (o_v != p_v) {
394 o_flush();
395 sbuf_printf(pg, "1 0 0 1 %s Tm\n", pdfpos(o_h, o_v));
396 p_h = o_h;
397 p_v = o_v;
399 if (!o_queued)
400 sbuf_printf(pg, "[<");
401 o_queued = 1;
402 if (o_h != p_h)
403 sbuf_printf(pg, "> %s <", pdfunit(p_h - o_h, o_s));
404 /* printing glyph identifier */
405 gid = font_glnum(g->font, g);
406 if (pfonts[o_i].cid)
407 sbuf_printf(pg, "%04x", gid);
408 else
409 sbuf_printf(pg, "%02x", gid % 256);
410 /* updating gbeg and gend */
411 if (gid < pfonts[o_i].gbeg)
412 pfonts[o_i].gbeg = gid;
413 if (gid > pfonts[o_i].gend)
414 pfonts[o_i].gend = gid;
415 /* advancing */
416 p_h = o_h + font_wid(g->font, o_s, g->wid);
419 static void out_fontup(void)
421 if (o_m != p_m) {
422 o_flush();
423 sbuf_printf(pg, "%s rg\n", pdfcolor(o_m));
424 p_m = o_m;
426 if (o_i >= 0 && (o_i != p_i || o_s != p_s)) {
427 struct pfont *ps = &pfonts[o_i];
428 o_flush();
429 if (ps->cid)
430 sbuf_printf(pg, "/%s %d Tf\n", ps->name, o_s);
431 else
432 sbuf_printf(pg, "/%s.%d %d Tf\n", ps->name, ps->sub, o_s);
433 p_i = o_i;
434 p_s = o_s;
438 void outc(char *c)
440 struct glyph *g;
441 struct font *fn;
442 g = dev_glyph(c, o_f);
443 fn = g ? g->font : dev_font(o_f);
444 if (!g) {
445 outrel(*c == ' ' && fn ? font_swid(fn, o_s) : 1, 0);
446 return;
448 o_i = o_loadfont(g);
449 out_fontup();
450 o_queue(g);
453 void outh(int h)
455 o_h = h;
458 void outv(int v)
460 o_v = v;
463 void outrel(int h, int v)
465 o_h += h;
466 o_v += v;
469 void outfont(int f)
471 if (dev_font(f))
472 o_f = f;
475 void outsize(int s)
477 if (s > 0)
478 o_s = s;
481 void outcolor(int c)
483 o_m = c;
486 void outrotate(int deg)
490 void outeps(char *eps)
494 static char *strcut(char *dst, char *src)
496 while (*src == ' ' || *src == '\n')
497 src++;
498 if (src[0] == '"') {
499 src++;
500 while (*src && (src[0] != '"' || src[1] == '"')) {
501 if (*src == '"')
502 src++;
503 *dst++ = *src++;
505 if (*src == '"')
506 src++;
507 } else {
508 while (*src && *src != ' ' && *src != '\n')
509 *dst++ = *src++;
511 *dst = '\0';
512 return src;
515 /* return a copy of a PDF object; returns a static buffer */
516 static char *pdf_copy(char *pdf, int len, int pos)
518 static char buf[1 << 12];
519 int datlen;
520 pos += pdf_ws(pdf, len, pos);
521 datlen = pdf_len(pdf, len, pos);
522 if (datlen > sizeof(buf) - 1)
523 datlen = sizeof(buf) - 1;
524 memcpy(buf, pdf + pos, datlen);
525 buf[datlen] = '\0';
526 return buf;
529 static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb);
531 /* write stream to sb */
532 static int pdf_strcopy(char *pdf, int len, int pos, struct sbuf *sb)
534 int slen, val;
535 int beg;
536 if ((val = pdf_dval_val(pdf, len, pos, "/Length")) < 0)
537 return -1;
538 slen = atoi(pdf + val);
539 pos = pos + pdf_len(pdf, len, pos);
540 pos += pdf_ws(pdf, len, pos);
541 if (pos + slen + 15 > len)
542 return -1;
543 beg = pos;
544 pos += strlen("stream");
545 if (pdf[pos] == '\r')
546 pos++;
547 pos += 1 + slen;
548 if (pdf[pos] == '\r' || pdf[pos] == ' ')
549 pos++;
550 if (pdf[pos] == '\n')
551 pos++;
552 pos += strlen("endstream") + 1;
553 sbuf_mem(sb, pdf + beg, pos - beg);
554 return 0;
557 /* copy a PDF object and return its new identifier */
558 static int pdf_objcopy(char *pdf, int len, int pos)
560 int id;
561 if ((pos = pdf_ref(pdf, len, pos)) < 0)
562 return -1;
563 if (pdf_type(pdf, len, pos) == 'd') {
564 struct sbuf *sb = sbuf_make();
565 pdf_dictcopy(pdf, len, pos, sb);
566 sbuf_chr(sb, '\n');
567 if (pdf_dval(pdf, len, pos, "/Length") >= 0)
568 pdf_strcopy(pdf, len, pos, sb);
569 id = obj_beg(0);
570 pdfmem(sbuf_buf(sb), sbuf_len(sb));
571 obj_end();
572 sbuf_free(sb);
573 } else {
574 id = obj_beg(0);
575 pdfmem(pdf + pos, pdf_len(pdf, len, pos));
576 pdfout("\n");
577 obj_end();
579 return id;
582 /* copy a PDF dictionary recursively */
583 static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb)
585 int i;
586 int key, val, id;
587 sbuf_printf(sb, "<<");
588 for (i = 0; ; i++) {
589 if ((key = pdf_dkey(pdf, len, pos, i)) < 0)
590 break;
591 sbuf_printf(sb, " %s", pdf_copy(pdf, len, key));
592 val = pdf_dval(pdf, len, pos, pdf_copy(pdf, len, key));
593 if (pdf_type(pdf, len, val) == 'r') {
594 if ((id = pdf_objcopy(pdf, len, val)) >= 0)
595 sbuf_printf(sb, " %d 0 R", id);
596 } else {
597 sbuf_printf(sb, " %s", pdf_copy(pdf, len, val));
600 sbuf_printf(sb, " >>");
603 /* copy resources dictionary */
604 static void pdf_rescopy(char *pdf, int len, int pos, struct sbuf *sb)
606 char *res_fields[] = {"/ProcSet", "/ExtGState", "/ColorSpace",
607 "/Pattern", "/Shading", "/Properties", "/Font", "/XObject"};
608 int res, i;
609 sbuf_printf(sb, " /Resources <<\n");
610 for (i = 0; i < LEN(res_fields); i++) {
611 if ((res = pdf_dval_val(pdf, len, pos, res_fields[i])) >= 0) {
612 if (pdf_type(pdf, len, res) == 'd') {
613 sbuf_printf(sb, " %s ", res_fields[i]);
614 pdf_dictcopy(pdf, len, res, sb);
615 sbuf_printf(sb, "\n");
616 } else {
617 sbuf_printf(sb, " %s %s\n", res_fields[i],
618 pdf_copy(pdf, len, res));
622 sbuf_printf(sb, " >>\n");
625 static int pdfext(char *pdf, int len)
627 char *cont_fields[] = {"/Filter", "/DecodeParms"};
628 int trailer, root, cont, pages, page1, res;
629 int kids_val, page1_val, val;
630 int xobj_id, length;
631 int bbox;
632 struct sbuf *sb;
633 int i;
634 if ((trailer = pdf_trailer(pdf, len)) < 0)
635 return -1;
636 if ((root = pdf_dval_obj(pdf, len, trailer, "/Root")) < 0)
637 return -1;
638 if ((pages = pdf_dval_obj(pdf, len, root, "/Pages")) < 0)
639 return -1;
640 if ((kids_val = pdf_dval_val(pdf, len, pages, "/Kids")) < 0)
641 return -1;
642 if ((page1_val = pdf_lval(pdf, len, kids_val, 0)) < 0)
643 return -1;
644 if ((page1 = pdf_ref(pdf, len, page1_val)) < 0)
645 return -1;
646 if ((cont = pdf_dval_obj(pdf, len, page1, "/Contents")) < 0)
647 return -1;
648 if ((val = pdf_dval_val(pdf, len, cont, "/Length")) < 0)
649 return -1;
650 res = pdf_dval_val(pdf, len, page1, "/Resources");
651 length = atoi(pdf + val);
652 bbox = pdf_dval_val(pdf, len, page1, "/MediaBox");
653 if (bbox < 0)
654 bbox = pdf_dval_val(pdf, len, pages, "/MediaBox");
655 sb = sbuf_make();
656 sbuf_printf(sb, "<<\n");
657 sbuf_printf(sb, " /Type /XObject\n");
658 sbuf_printf(sb, " /Subtype /Form\n");
659 sbuf_printf(sb, " /FormType 1\n");
660 if (bbox >= 0)
661 sbuf_printf(sb, " /BBox %s\n", pdf_copy(pdf, len, bbox));
662 sbuf_printf(sb, " /Matrix [1 0 0 1 %s]\n", pdfpos(o_h, o_v));
663 if (res >= 0)
664 pdf_rescopy(pdf, len, res, sb);
665 sbuf_printf(sb, " /Length %d\n", length);
666 for (i = 0; i < LEN(cont_fields); i++)
667 if ((val = pdf_dval_val(pdf, len, cont, cont_fields[i])) >= 0)
668 sbuf_printf(sb, " %s %s\n", cont_fields[i],
669 pdf_copy(pdf, len, val));
670 sbuf_printf(sb, ">>\n");
671 pdf_strcopy(pdf, len, cont, sb);
672 xobj_id = obj_beg(0);
673 pdfmem(sbuf_buf(sb), sbuf_len(sb));
674 obj_end();
675 sbuf_free(sb);
676 if (xobj_n == xobj_sz) {
677 xobj_sz += 8;
678 xobj = mextend(xobj, xobj_n, xobj_sz, sizeof(xobj[0]));
680 xobj[xobj_n++] = xobj_id;
681 return xobj_n - 1;
684 void outpdf(char *spec)
686 char pdf[1 << 12];
687 char buf[1 << 12];
688 struct sbuf *sb;
689 int xobj_id;
690 int fd, nr;
691 spec = strcut(pdf, spec);
692 if (!pdf[0])
693 return;
694 /* reading the pdf file */
695 sb = sbuf_make();
696 fd = open(pdf, O_RDONLY);
697 while ((nr = read(fd, buf, sizeof(buf))) > 0)
698 sbuf_mem(sb, buf, nr);
699 close(fd);
700 /* the XObject */
701 xobj_id = pdfext(sbuf_buf(sb), sbuf_len(sb));
702 sbuf_free(sb);
703 o_flush();
704 out_fontup();
705 sbuf_printf(pg, "ET /FO%d Do BT\n", xobj_id);
706 p_h = -1;
707 p_v = -1;
710 void outlink(char *spec)
714 void outpage(void)
716 o_v = 0;
717 o_h = 0;
718 p_i = 0;
719 p_v = 0;
720 p_h = 0;
721 p_s = 0;
722 p_f = 0;
723 p_m = 0;
724 o_i = -1;
727 void outmnt(int f)
729 if (p_f == f)
730 p_f = -1;
733 void outgname(int g)
737 static int draw_path; /* number of path segments */
738 static int draw_point; /* point was set for postscript newpath */
740 void drawbeg(void)
742 o_flush();
743 out_fontup();
744 if (draw_path)
745 return;
746 sbuf_printf(pg, "%s m\n", pdfpos(o_h, o_v));
749 void drawend(int close, int fill)
751 if (draw_path)
752 return;
753 draw_point = 0;
754 if (!fill) /* stroking color */
755 sbuf_printf(pg, "%s RG\n", pdfcolor(o_m));
756 if (fill)
757 sbuf_printf(pg, "f\n");
758 else
759 sbuf_printf(pg, close ? "s\n" : "S\n");
762 void drawmbeg(char *s)
766 void drawmend(char *s)
770 void drawl(int h, int v)
772 outrel(h, v);
773 sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v));
776 /* draw circle/ellipse quadrant */
777 static void drawquad(int ch, int cv)
779 long b = 551915;
780 long x0 = o_h * 1000;
781 long y0 = o_v * 1000;
782 long x3 = x0 + ch * 1000 / 2;
783 long y3 = y0 + cv * 1000 / 2;
784 long x1 = x0;
785 long y1 = y0 + cv * b / 1000 / 2;
786 long x2 = x0 + ch * b / 1000 / 2;
787 long y2 = y3;
788 if (ch * cv < 0) {
789 x1 = x3 - ch * b / 1000 / 2;
790 y1 = y0;
791 x2 = x3;
792 y2 = y3 - cv * b / 1000 / 2;
794 sbuf_printf(pg, "%s ", pdfpos00(x1 / 10, y1 / 10));
795 sbuf_printf(pg, "%s ", pdfpos00(x2 / 10, y2 / 10));
796 sbuf_printf(pg, "%s c\n", pdfpos00(x3 / 10, y3 / 10));
797 outrel(ch / 2, cv / 2);
800 /* draw a circle */
801 void drawc(int c)
803 drawquad(+c, +c);
804 drawquad(+c, -c);
805 drawquad(-c, -c);
806 drawquad(-c, +c);
807 outrel(c, 0);
810 /* draw an ellipse */
811 void drawe(int h, int v)
813 drawquad(+h, +v);
814 drawquad(+h, -v);
815 drawquad(-h, -v);
816 drawquad(-h, +v);
817 outrel(h, 0);
820 /* draw an arc */
821 void drawa(int h1, int v1, int h2, int v2)
823 drawl(h1 + h2, v1 + v2);
826 /* draw an spline */
827 void draws(int h1, int v1, int h2, int v2)
829 outrel(h1, v1);
830 sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v));
833 void ps_header(char *title, int pagewidth, int pageheight, int linewidth)
835 pdf_title = title;
836 obj_map();
837 pdf_root = obj_map();
838 pdf_pages = obj_map();
839 pdf_title = title;
840 pdfout("%%PDF-1.6\n");
841 pdf_width = (pagewidth * 72 + 127) / 254;
842 pdf_height = (pageheight * 72 + 127) / 254;
845 void ps_trailer(int pages)
847 int i;
848 int xref_off;
849 int info_id;
850 /* pdf pages object */
851 obj_beg(pdf_pages);
852 pdfout("<<\n");
853 pdfout(" /Type /Pages\n");
854 pdfout(" /MediaBox [ 0 0 %d %d ]\n", pdf_width, pdf_height);
855 pdfout(" /Count %d\n", page_n);
856 pdfout(" /Kids [");
857 for (i = 0; i < page_n; i++)
858 pdfout(" %d 0 R", page_id[i]);
859 pdfout(" ]\n");
860 pdfout(">>\n");
861 obj_end();
862 /* pdf root object */
863 obj_beg(pdf_root);
864 pdfout("<<\n");
865 pdfout(" /Type /Catalog\n");
866 pdfout(" /Pages %d 0 R\n", pdf_pages);
867 pdfout(">>\n");
868 obj_end();
869 /* fonts */
870 pfont_done();
871 /* info object */
872 info_id = obj_beg(0);
873 pdfout("<<\n");
874 if (pdf_title)
875 pdfout(" /Title (%s)\n", pdf_title);
876 pdfout(" /Creator (Neatroff)\n");
877 pdfout(" /Producer (Neatpost)\n");
878 pdfout(">>\n");
879 obj_end();
880 /* the xref */
881 xref_off = pdf_pos;
882 pdfout("xref\n");
883 pdfout("0 %d\n", obj_n);
884 pdfout("0000000000 65535 f \n");
885 for (i = 1; i < obj_n; i++)
886 pdfout("%010d 00000 n \n", obj_off[i]);
887 /* the trailer */
888 pdfout("trailer\n");
889 pdfout("<<\n");
890 pdfout(" /Size %d\n", obj_n);
891 pdfout(" /Root %d 0 R\n", pdf_root);
892 pdfout(" /Info %d 0 R\n", info_id);
893 pdfout(">>\n");
894 pdfout("startxref\n");
895 pdfout("%d\n", xref_off);
896 pdfout("%%%%EOF\n");
897 free(page_id);
898 free(obj_off);
901 void ps_pagebeg(int n)
903 pg = sbuf_make();
904 sbuf_printf(pg, "BT\n");
907 void ps_pageend(int n)
909 int cont_id;
910 int i;
911 o_flush();
912 sbuf_printf(pg, "ET\n");
913 /* page contents */
914 cont_id = obj_beg(0);
915 pdfout("<<\n");
916 pdfout(" /Length %d\n", sbuf_len(pg) - 1);
917 pdfout(">>\n");
918 pdfout("stream\n");
919 pdfmem(sbuf_buf(pg), sbuf_len(pg));
920 pdfout("endstream\n");
921 obj_end();
922 /* the page object */
923 if (page_n == page_sz) {
924 page_sz += 1024;
925 page_id = mextend(page_id, page_n, page_sz, sizeof(page_id[0]));
927 page_id[page_n++] = obj_beg(0);
928 pdfout("<<\n");
929 pdfout(" /Type /Page\n");
930 pdfout(" /Parent %d 0 R\n", pdf_pages);
931 pdfout(" /Resources <<\n");
932 pdfout(" /Font <<");
933 for (i = 0; i < pfonts_n; i++) {
934 if (o_iset[i]) {
935 struct pfont *ps = &pfonts[i];
936 if (ps->cid)
937 pdfout(" /%s %d 0 R", ps->name, ps->obj);
938 else
939 pdfout(" /%s.%d %d 0 R", ps->name, ps->sub, ps->obj);
942 pdfout(" >>\n");
943 if (xobj_n) { /* XObjects */
944 pdfout(" /XObject <<");
945 for (i = 0; i < xobj_n; i++)
946 pdfout(" /FO%d %d 0 R", i, xobj[i]);
947 pdfout(" >>\n");
949 pdfout(" >>\n");
950 pdfout(" /Contents %d 0 R\n", cont_id);
951 pdfout(">>\n");
952 obj_end();
953 sbuf_free(pg);
954 memset(o_iset, 0, pfonts_n * sizeof(o_iset[0]));
955 free(xobj);
956 xobj = NULL;
957 xobj_n = 0;
958 xobj_sz = 0;