ref: 1d4c0e6324ea5e0f8baf9ee591582d41ab24e587
dir: /pdf.c/
#include <fcntl.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "post.h" static char *pdf_title; /* document title */ static int pdf_width; /* page width */ static int pdf_height; /* page height */ static int pdf_pages; /* pages object id */ static int pdf_root; /* root object id */ static int pdf_pos; /* current pdf file offset */ static int *obj_off; /* object offsets */ static int obj_sz, obj_n; /* number of pdf objects */ static int *page_id; /* page object ids */ static int page_sz, page_n; /* number of pages */ static struct sbuf *pg; /* current page contents */ static int o_f, o_s, o_m; /* font and size */ static int o_h, o_v; /* current user position */ static int p_h, p_v; /* current output position */ static int o_pf, p_pf; /* output and pdf fonts; indices into o_fs[] */ static int p_f, p_s, p_m; /* output font */ static int o_queued; /* queued character type */ static int *o_fs; /* page fonts */ static int o_fsn, o_fssz; /* page fonts */ #define PSFN_MK(fn, ix) (((fn) << 16) | (ix)) #define PSFN_FN(fi) ((fi) >> 16) #define PSFN_IX(fi) ((fi) & 0xffff) struct psfont { char name[128]; /* font PostScript name */ char path[1024]; /* font path */ char desc[1024]; /* font descriptor path */ int *gmap; /* the sub-font assigned to each glyph */ int *gpos; /* the location of the glyph in its sub-font */ int gcnt; /* glyph count */ int lastfn; /* the last sub-font */ int lastgl; /* the number of glyphs in the last subfont */ int obj[64]; /* sub-font object ids */ int objdes; /* font descriptor object id */ }; static struct psfont *psfonts; static int psfonts_n, psfonts_sz; /* print formatted pdf output */ static void pdfout(char *s, ...) { va_list ap; va_start(ap, s); pdf_pos += vprintf(s, ap); va_end(ap); } /* print pdf output */ static void pdfouts(char *s) { fputs(s, stdout); pdf_pos += strlen(s); } /* allocate an object number */ static int obj_map(void) { if (obj_n == obj_sz) { obj_sz += 1024; obj_off = mextend(obj_off, obj_n, obj_sz, sizeof(obj_off[0])); } return obj_n++; } /* start the definition of an object */ static int obj_beg(int id) { if (id <= 0) id = obj_map(); obj_off[id] = pdf_pos; pdfout("%d 0 obj\n", id); return id; } /* end an object definition */ static void obj_end(void) { pdfout("endobj\n\n"); } void out(char *s, ...) { } /* the length of the clear-text, encrypted, and fixed-content portions */ static int type1lengths(char *t1, int l, int *l1, int *l2, int *l3) { int i; char *cleartext = t1; char *encrypted = NULL; char *fixedcont = NULL; for (i = 0; i < l - 5 && !encrypted; i++) if (t1[i] == 'e' && !memcmp("eexec", t1 + i, 5)) encrypted = t1 + i; if (!encrypted) return 1; for (; i < l - 512 && !fixedcont; i++) if (t1[i] == '0' && !memcmp("00000", t1 + i, 5)) fixedcont = t1 + i; *l1 = encrypted - cleartext; *l2 = fixedcont ? fixedcont - cleartext : 0; return 0; } /* return font type: 't': TrueType, '1': Type 1, 'o': OpenType */ static int fonttype(char *path) { char *ext = strrchr(path, '.'); if (ext && !strcmp(".ttf", ext)) return 't'; if (ext && !strcmp(".otf", ext)) return 'o'; return '1'; } static void encodehex(struct sbuf *d, char *s, int n) { static char hex[] = "0123456789ABCDEF"; int i; for (i = 0; i < n; i++) { sbuf_chr(d, hex[((unsigned char) s[i]) >> 4]); sbuf_chr(d, hex[((unsigned char) s[i]) & 0x0f]); if (i % 80 == 79 && i + 1 < n) sbuf_chr(d, '\n'); } sbuf_str(d, ">\n"); } /* include font descriptor; returns object id */ static int psfont_writedesc(struct psfont *ps) { int str_obj = -1; int des_obj; char buf[1 << 10]; if (fonttype(ps->path) == '1' || fonttype(ps->path) == 't') { int fd = open(ps->path, O_RDONLY); struct sbuf *ffsb = sbuf_make(); struct sbuf *sb = sbuf_make(); int l1 = 0, l2 = 0, l3 = 0; int nr; /* reading the font file */ while ((nr = read(fd, buf, sizeof(buf))) > 0) sbuf_mem(ffsb, buf, nr); close(fd); l1 = sbuf_len(ffsb); /* initialize Type 1 lengths */ if (fonttype(ps->path) == '1') { if (type1lengths(sbuf_buf(ffsb), sbuf_len(ffsb), &l1, &l2, &l3)) l1 = 0; /* remove the fixed-content portion of the font */ if (l3) sbuf_cut(ffsb, l1 + l2); l1 -= l3; } /* encoding file contents */ encodehex(sb, sbuf_buf(ffsb), sbuf_len(ffsb)); /* write font data if it has nonzero length */ if (l1) { str_obj = obj_beg(0); pdfout("<<\n"); pdfout(" /Filter /ASCIIHexDecode\n"); pdfout(" /Length %d\n", sbuf_len(sb)); pdfout(" /Length1 %d\n", l1); if (fonttype(ps->path) == '1') pdfout(" /Length2 %d\n", l2); if (fonttype(ps->path) == '1') pdfout(" /Length3 %d\n", l3); pdfout(">>\n"); pdfout("stream\n"); pdfouts(sbuf_buf(sb)); pdfout("endstream\n"); obj_end(); } sbuf_free(ffsb); sbuf_free(sb); } /* the font descriptor */ des_obj = obj_beg(0); pdfout("<<\n"); pdfout(" /Type /FontDescriptor\n"); pdfout(" /FontName /%s\n", ps->name); pdfout(" /Flags 4\n"); pdfout(" /FontBBox [-1000 -1000 1000 1000]\n"); pdfout(" /MissingWidth 1000\n"); pdfout(" /StemV 100\n"); pdfout(" /ItalicAngle 0\n"); pdfout(" /CapHeight 100\n"); pdfout(" /Ascent 100\n"); pdfout(" /Descent 100\n"); if (str_obj >= 0) pdfout(" /FontFile%s %d 0 R\n", fonttype(ps->path) == 't' ? "2" : "", str_obj); pdfout(">>\n"); obj_end(); return des_obj; } /* write the object corresponding to font font_id[f] */ static void psfont_write(struct psfont *ps, int ix) { int i; int enc_obj; struct font *fn = dev_fontopen(ps->desc); int map[256]; int gcnt = ix < ps->lastfn ? 256 : ps->lastgl; /* finding out the mapping */ for (i = 0; i < 256; i++) map[i] = -1; for (i = 0; i < ps->gcnt; i++) if (ps->gmap[i] == ix) map[ps->gpos[i]] = i; /* the encoding object */ enc_obj = obj_beg(0); pdfout("<<\n"); pdfout(" /Type /Encoding\n"); pdfout(" /Differences [ 0"); for (i = 0; i < gcnt; i++) pdfout(" /%s", map[i] >= 0 ? font_glget(fn, map[i])->id : ".notdef"); pdfout(" ]\n"); pdfout(">>\n"); obj_end(); /* the font object */ obj_beg(ps->obj[ix]); pdfout("<<\n"); pdfout(" /Type /Font\n"); if (fonttype(ps->path) == 't') pdfout(" /Subtype /TrueType\n"); else pdfout(" /Subtype /Type1\n"); pdfout(" /BaseFont /%s\n", ps->name); pdfout(" /FirstChar 0\n"); pdfout(" /LastChar %d\n", gcnt - 1); pdfout(" /Widths ["); for (i = 0; i < gcnt; i++) pdfout(" %d", (long) (map[i] >= 0 ? font_glget(fn, map[i])->wid : 0) * 100 * 72 / dev_res); pdfout(" ]\n"); pdfout(" /FontDescriptor %d 0 R\n", ps->objdes); pdfout(" /Encoding %d 0 R\n", enc_obj); pdfout(">>\n"); obj_end(); font_close(fn); } static int psfont_find(struct glyph *g) { struct font *fn = g->font; char *name = font_name(fn); struct psfont *ps = NULL; int gidx; int i; for (i = 0; i < psfonts_n; i++) if (!strcmp(name, psfonts[i].name)) break; if (i == psfonts_n) { if (psfonts_n == psfonts_sz) { psfonts_sz += 16; psfonts = mextend(psfonts, psfonts_n, psfonts_sz, sizeof(psfonts[0])); } psfonts_n++; ps = &psfonts[i]; snprintf(ps->name, sizeof(ps->name), "%s", name); snprintf(ps->path, sizeof(ps->path), "%s", font_path(fn)); snprintf(ps->desc, sizeof(ps->desc), "%s", font_desc(fn)); while (font_glget(fn, ps->gcnt)) ps->gcnt++; ps->gmap = calloc(ps->gcnt, sizeof(ps->gmap)); ps->gpos = calloc(ps->gcnt, sizeof(ps->gpos)); ps->lastfn = 0; ps->lastgl = 256; } ps = &psfonts[i]; gidx = font_glnum(fn, g); if (!ps->gmap[gidx]) { if (ps->lastgl == 256) { ps->lastgl = 0; ps->lastfn++; ps->obj[ps->lastfn] = obj_map(); } ps->gmap[gidx] = ps->lastfn; ps->gpos[gidx] = ps->lastgl++; } return PSFN_MK(i, ps->gmap[gidx]); } static int psfont_gpos(struct glyph *g) { int fn = psfont_find(g); return psfonts[PSFN_FN(fn)].gpos[font_glnum(g->font, g)]; } static void psfont_done(void) { int i, j; for (i = 0; i < psfonts_n; i++) { struct psfont *ps = &psfonts[i]; ps->objdes = psfont_writedesc(ps); for (j = 1; j <= ps->lastfn; j++) psfont_write(ps, j); } for (i = 0; i < psfonts_n; i++) { free(psfonts[i].gmap); free(psfonts[i].gpos); } free(psfonts); } static void o_flush(void) { if (o_queued == 1) sbuf_printf(pg, ">] TJ\n"); o_queued = 0; } static int o_loadfont(struct glyph *g) { int fn = psfont_find(g); int i; for (i = 0; i < o_fsn; i++) if (o_fs[i] == fn) return i; if (o_fsn == o_fssz) { o_fssz += 128; o_fs = mextend(o_fs, o_fsn, o_fssz, sizeof(o_fs[0])); } o_fs[o_fsn++] = fn; return o_fsn - 1; } /* like pdfpos() but assume that uh and uv are multiplied by 100 */ static char *pdfpos00(int uh, int uv) { static char buf[64]; int h = (long) uh * 72 / dev_res; int v = (long) pdf_height * 100 - (long) uv * 72 / dev_res; sprintf(buf, "%s%d.%02d %s%d.%02d", h < 0 ? "-" : "", abs(h) / 100, abs(h) % 100, v < 0 ? "-" : "", abs(v) / 100, abs(v) % 100); return buf; } /* convert troff position to pdf position; returns a static buffer */ static char *pdfpos(int uh, int uv) { return pdfpos00(uh * 100, uv * 100); } /* troff length to thousands of a unit of text space; returns a static buffer */ static char *pdfunit(int uh, int sz) { static char buf[64]; int h = (long) uh * 1000 * 72 / sz / dev_res; sprintf(buf, "%s%d", h < 0 ? "-" : "", abs(h)); return buf; } /* convert troff color to pdf color; returns a static buffer */ static char *pdfcolor(int m) { static char buf[64]; int r = CLR_R(m) * 1000 / 255; int g = CLR_G(m) * 1000 / 255; int b = CLR_B(m) * 1000 / 255; sbuf_printf(pg, "%d.%03d %d.%03d %d.%03d", r / 1000, r % 1000, g / 1000, g % 1000, b / 1000, b % 1000); return buf; } static void o_queue(struct glyph *g) { if (o_v != p_v) { o_flush(); sbuf_printf(pg, "1 0 0 1 %s Tm\n", pdfpos(o_h, o_v)); p_h = o_h; p_v = o_v; } if (!o_queued) sbuf_printf(pg, "[<"); o_queued = 1; if (o_h != p_h) sbuf_printf(pg, "> %s <", pdfunit(p_h - o_h, o_s)); sbuf_printf(pg, "%02x", psfont_gpos(g)); p_h = o_h + font_wid(g->font, o_s, g->wid); } static void out_fontup(void) { if (o_m != p_m) { o_flush(); sbuf_printf(pg, "%s rg\n", pdfcolor(o_m)); p_m = o_m; } if (o_pf >= 0 && (o_pf != p_pf || o_s != p_s)) { int fn = PSFN_FN(o_fs[o_pf]); int ix = PSFN_IX(o_fs[o_pf]); o_flush(); sbuf_printf(pg, "/%s.%d %d Tf\n", psfonts[fn].name, ix, o_s); p_pf = o_pf; p_s = o_s; } } void outc(char *c) { struct glyph *g; struct font *fn; g = dev_glyph(c, o_f); fn = g ? g->font : dev_font(o_f); if (!g) { outrel(*c == ' ' && fn ? font_swid(fn, o_s) : 1, 0); return; } o_pf = o_loadfont(g); out_fontup(); o_queue(g); } void outh(int h) { o_h = h; } void outv(int v) { o_v = v; } void outrel(int h, int v) { o_h += h; o_v += v; } void outfont(int f) { if (dev_font(f)) o_f = f; } void outsize(int s) { if (s > 0) o_s = s; } void outcolor(int c) { o_m = c; } void outrotate(int deg) { } void outeps(char *eps) { } void outlink(char *spec) { } void outpage(void) { o_v = 0; o_h = 0; p_pf = 0; p_v = 0; p_h = 0; p_s = 0; p_f = 0; p_m = 0; o_pf = -1; } void outmnt(int f) { if (p_f == f) p_f = -1; } void outgname(int g) { } static int draw_path; /* number of path segments */ static int draw_point; /* point was set for postscript newpath */ void drawbeg(void) { o_flush(); out_fontup(); if (draw_path) return; sbuf_printf(pg, "%s m\n", pdfpos(o_h, o_v)); } void drawend(int close, int fill) { if (draw_path) return; draw_point = 0; if (!fill) /* stroking color */ sbuf_printf(pg, "%s RG\n", pdfcolor(o_m)); if (fill) sbuf_printf(pg, "f\n"); else sbuf_printf(pg, close ? "s\n" : "S\n"); } void drawmbeg(char *s) { } void drawmend(char *s) { } void drawl(int h, int v) { outrel(h, v); sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v)); } /* draw circle/ellipse quadrant */ static void drawquad(int ch, int cv) { long b = 551915; long x0 = o_h * 1000; long y0 = o_v * 1000; long x3 = x0 + ch * 1000 / 2; long y3 = y0 + cv * 1000 / 2; long x1 = x0; long y1 = y0 + cv * b / 1000 / 2; long x2 = x0 + ch * b / 1000 / 2; long y2 = y3; if (ch * cv < 0) { x1 = x3 - ch * b / 1000 / 2; y1 = y0; x2 = x3; y2 = y3 - cv * b / 1000 / 2; } sbuf_printf(pg, "%s ", pdfpos00(x1 / 10, y1 / 10)); sbuf_printf(pg, "%s ", pdfpos00(x2 / 10, y2 / 10)); sbuf_printf(pg, "%s c\n", pdfpos00(x3 / 10, y3 / 10)); outrel(ch / 2, cv / 2); } /* draw a circle */ void drawc(int c) { drawquad(+c, +c); drawquad(+c, -c); drawquad(-c, -c); drawquad(-c, +c); outrel(c, 0); } /* draw an ellipse */ void drawe(int h, int v) { drawquad(+h, +v); drawquad(+h, -v); drawquad(-h, -v); drawquad(-h, +v); outrel(h, 0); } /* draw an arc */ void drawa(int h1, int v1, int h2, int v2) { drawl(h1 + h2, v1 + v2); } /* draw an spline */ void draws(int h1, int v1, int h2, int v2) { outrel(h1, v1); sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v)); } void ps_header(char *title, int pagewidth, int pageheight, int linewidth) { pdf_title = title; obj_map(); pdf_root = obj_map(); pdf_pages = obj_map(); pdf_title = title; pdfout("%%PDF-1.6\n"); pdf_width = (pagewidth * 72 + 127) / 254; pdf_height = (pageheight * 72 + 127) / 254; } void ps_trailer(int pages) { int i; int xref_off; int info_id; /* pdf pages object */ obj_beg(pdf_pages); pdfout("<<\n"); pdfout(" /Type /Pages\n"); pdfout(" /MediaBox [ 0 0 %d %d ]\n", pdf_width, pdf_height); pdfout(" /Count %d\n", page_n); pdfout(" /Kids ["); for (i = 0; i < page_n; i++) pdfout(" %d 0 R", page_id[i]); pdfout(" ]\n"); pdfout(">>\n"); obj_end(); /* pdf root object */ obj_beg(pdf_root); pdfout("<<\n"); pdfout(" /Type /Catalog\n"); pdfout(" /Pages %d 0 R\n", pdf_pages); pdfout(">>\n"); obj_end(); /* fonts */ psfont_done(); /* info object */ info_id = obj_beg(0); pdfout("<<\n"); if (pdf_title) pdfout(" /Title (%s)\n", pdf_title); pdfout(" /Creator (Neatroff)\n"); pdfout(" /Producer (Neatpost)\n"); pdfout(">>\n"); obj_end(); /* the xref */ xref_off = pdf_pos; pdfout("xref\n"); pdfout("0 %d\n", obj_n); pdfout("0000000000 65535 f \n"); for (i = 1; i < obj_n; i++) pdfout("%010d 00000 n \n", obj_off[i]); /* the trailer */ pdfout("trailer\n"); pdfout("<<\n"); pdfout(" /Size %d\n", obj_n); pdfout(" /Root %d 0 R\n", pdf_root); pdfout(" /Info %d 0 R\n", info_id); pdfout(">>\n"); pdfout("startxref\n"); pdfout("%d\n", xref_off); pdfout("%%%%EOF\n"); free(page_id); free(obj_off); } void ps_pagebeg(int n) { pg = sbuf_make(); sbuf_printf(pg, "BT\n"); } void ps_pageend(int n) { int cont_id; int i; o_flush(); sbuf_printf(pg, "ET\n"); /* page contents */ cont_id = obj_beg(0); pdfout("<<\n"); pdfout(" /Length %d\n", sbuf_len(pg)); pdfout(">>\n"); pdfout("stream\n"); pdfouts(sbuf_buf(pg)); pdfout("endstream\n"); obj_end(); /* the page object */ if (page_n == page_sz) { page_sz += 1024; page_id = mextend(page_id, page_n, page_sz, sizeof(page_id[0])); } page_id[page_n++] = obj_beg(0); pdfout("<<\n"); pdfout(" /Type /Page\n"); pdfout(" /Parent %d 0 R\n", pdf_pages); pdfout(" /Resources <<\n"); pdfout(" /Font <<"); for (i = 0; i < o_fsn; i++) { int fn = PSFN_FN(o_fs[i]); int ix = PSFN_IX(o_fs[i]); pdfout(" /%s.%d %d 0 R", psfonts[fn].name, ix, psfonts[fn].obj[ix]); } pdfout(" >>\n"); pdfout(" >>\n"); pdfout(" /Contents %d 0 R\n", cont_id); pdfout(">>\n"); obj_end(); sbuf_free(pg); free(o_fs); o_fs = NULL; o_fsn = 0; o_fssz = 0; }