ref: c1d1442713a74a60c81bcaab17377916ef5161e2
dir: /pdf.c/
#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.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 char **font_ps; /* document font names */ static int *font_id; /* font object */ static int *font_ct; /* font content stream object */ static int *font_ix; /* font index */ static int font_sz, font_n; /* number of fonts */ 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 */ /* print pdf output */ static void pdfout(char *s, ...) { va_list ap; va_start(ap, s); pdf_pos += vprintf(s, ap); va_end(ap); } /* 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"); } /* embed font; return stream object identifier */ static int font_outdat(char *path, char *name, int ix) { struct sbuf *sb; FILE *fp; int c, i, id; for (i = 0; i < font_n; i++) if (!strcmp(name, font_ps[i]) && font_ct[i] >= 0) return font_ct[i]; fp = fopen(path, "r"); if (!fp) return -1; sb = sbuf_make(); c = fgetc(fp); for (i = 0; c != EOF; i++) { sbuf_printf(sb, "%02x", c); c = fgetc(fp); if (i % 40 == 39 && c != EOF) sbuf_chr(sb, '\n'); } sbuf_str(sb, ">\n"); fclose(fp); id = obj_beg(0); pdfout("<<\n"); pdfout(" /Filter /ASCIIHexDecode\n"); pdfout(" /Length %d\n", sbuf_len(sb)); pdfout(" /Length1 %d\n", i); pdfout(">>\n"); pdfout("stream\n"); pdfout("%s", sbuf_buf(sb)); pdfout("endstream\n"); obj_end(); sbuf_free(sb); return id; } /* write the object corresponding to font font_id[f] */ static void font_out(struct font *fn, int f) { int i; int enc_obj, des_obj; char *path = font_path(fn); char *ext = path ? strrchr(path, '.') : NULL; /* the encoding object */ enc_obj = obj_beg(0); pdfout("<<\n"); pdfout(" /Type /Encoding\n"); pdfout(" /Differences [ 0"); for (i = 0; i < 256; i++) { struct glyph *g = font_glget(fn, font_ix[f] * 256 + i); pdfout(" /%s", g ? g->id : ".notdef"); } pdfout(" ]\n"); pdfout(">>\n"); obj_end(); /* embedding the font */ if (ext && !strcmp(".ttf", ext)) font_ct[f] = font_outdat(path, font_ps[f], font_ix[f]); /* the font descriptor */ des_obj = obj_beg(0); pdfout("<<\n"); pdfout(" /Type /FontDescriptor\n"); pdfout(" /FontName /%s\n", font_ps[f]); 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 (font_ct[f] >= 0) pdfout(" /FontFile2 %d 0 R\n", font_ct[f]); pdfout(">>\n"); obj_end(); /* the font object */ obj_beg(font_id[f]); pdfout("<<\n"); pdfout(" /Type /Font\n"); pdfout(" /Subtype /%s\n", ext && !strcmp(".ttf", ext) ? "TrueType" : "Type1"); pdfout(" /BaseFont /%s\n", font_ps[f]); pdfout(" /FirstChar 0\n"); pdfout(" /LastChar 255\n"); pdfout(" /Widths ["); for (i = 0; i < 256; i++) { struct glyph *g = font_glget(fn, font_ix[f] * 256 + i); pdfout(" %d", (g ? g->wid : 0) * dev_res / 72); } pdfout(" ]\n"); pdfout(" /FontDescriptor %d 0 R\n", des_obj); pdfout(" /Encoding %d 0 R\n", enc_obj); pdfout(">>\n"); obj_end(); } static int font_put(struct font *fn, int ix) { int i; char *name = font_name(fn); for (i = 0; i < font_n; i++) if (!strcmp(font_ps[i], font_name(fn)) && font_ix[i] == ix) return i; if (font_n == font_sz) { font_sz += 128; font_id = mextend(font_id, font_n, font_sz, sizeof(font_id[0])); font_ps = mextend(font_ps, font_n, font_sz, sizeof(font_ps[0])); font_ix = mextend(font_ix, font_n, font_sz, sizeof(font_ix[0])); font_ct = mextend(font_ct, font_n, font_sz, sizeof(font_ct[0])); } font_id[font_n] = obj_map(); font_ix[font_n] = ix; font_ps[font_n] = malloc(strlen(name) + 1); font_ct[font_n] = -1; strcpy(font_ps[font_n], name); font_n++; font_out(fn, font_n - 1); return font_n - 1; } void out(char *s, ...) { } static void o_flush(void) { if (o_queued == 1) sbuf_printf(pg, ") Tj\n"); o_queued = 0; } static int o_loadfont(struct glyph *g) { struct font *fn = g ? g->font : dev_font(o_f); int ix = font_glnum(fn, g) / 256; char *name = font_name(fn); int i; int id; for (i = 0; i < o_fsn; i++) if (!strcmp(name, font_ps[o_fs[i]]) && font_ix[o_fs[i]] == ix) return i; id = font_put(fn, ix); 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++] = id; return o_fsn - 1; } #define PREC 1000 #define PRECN "3" /* convert troff position to pdf position; returns a static buffer */ static char *pdfpos(int uh, int uv) { static char buf[64]; int h = uh * PREC * 72 / dev_res; int v = pdf_height * PREC - (uv * PREC * 72 / dev_res); sprintf(buf, "%d.%0" PRECN "d %d.%0" PRECN "d", h / PREC, h % PREC, v / PREC, v % PREC); 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) { int pos; if (o_h != p_h || 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; pos = font_glnum(g->font, g) % 256; sbuf_printf(pg, "\\%d%d%d", (pos >> 6) & 7, (pos >> 3) & 7, pos & 7); p_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 != p_pf || o_s != p_s) { int f = o_fs[o_pf]; o_flush(); sbuf_printf(pg, "/%s.%d %d Tf\n", font_ps[f], font_ix[f], 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_v = 0; p_h = 0; p_s = 0; p_f = 0; p_m = 0; } 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 */ static void drawmv(void) { if (!draw_point) sbuf_printf(pg, "%d %d m ", o_h, o_v); draw_point = 1; } 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) { o_flush(); outrel(h, v); sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v)); } void drawc(int c) { outrel(c, 0); } void drawe(int h, int v) { outrel(h, 0); } void drawa(int h1, int v1, int h2, int v2) { outrel(h1 + h2, v1 + v2); } void draws(int h1, int v1, int h2, int v2) { outrel(h1, v1); } 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(); /* 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); for (i = 0; i < font_n; i++) free(font_ps[i]); free(font_ps); free(font_ct); free(font_id); free(font_ix); } 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"); pdfout("%s", 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++) pdfout(" /%s.%d %d 0 R", font_ps[o_fs[i]], font_ix[o_fs[i]], font_id[o_fs[i]]); 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; }