ref: e02a200628b9f7e0b2a98d6d051e4a7610ecc739
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 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");
}
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';
}
/* include font descriptor; returns object id */
static int psfont_writedesc(struct psfont *ps)
{
int str_obj = -1;
int des_obj;
char buf[1 << 10];
int i;
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 */
for (i = 0; i < sbuf_len(ffsb); i++) {
sbuf_printf(sb, "%02x", (unsigned char) sbuf_buf(ffsb)[i]);
if (i % 40 == 39 && i + 1 < sbuf_len(ffsb))
sbuf_chr(sb, '\n');
}
sbuf_str(sb, ">\n");
/* 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");
pdfout("%s", 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");
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++) {
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;
}