ref: 10f935774e53b1b3a6b27e6961952b69d1325f22
dir: /pdf.c/
/* PDF post-processor functions */
#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_i, p_i; /* output and pdf fonts (indices into pfont[]) */
static int p_f, p_s, p_m; /* output font */
static int o_queued; /* queued character type */
static char o_iset[1024]; /* fonts accesssed in this page */
static int *xobj; /* page xobject object ids */
static int xobj_sz, xobj_n; /* number of xobjects */
/* loaded PDF fonts */
struct pfont {
char name[128]; /* font PostScript name */
char path[1024]; /* font path */
char desc[1024]; /* font descriptor path */
int gbeg; /* the first glyph */
int gend; /* the last glyph */
int sub; /* subfont number */
int obj; /* the font object */
int des; /* font descriptor */
int cid; /* CID-indexed */
};
static struct pfont *pfonts;
static int pfonts_n, pfonts_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 pdfmem(char *s, int len)
{
fwrite(s, len, 1, stdout);
pdf_pos += len;
}
/* 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';
}
/* write the object corresponding to the given font */
static void pfont_write(struct pfont *ps)
{
int i;
int enc_obj;
struct font *fn = dev_fontopen(ps->desc);
/* the encoding object */
enc_obj = obj_beg(0);
pdfout("<<\n");
pdfout(" /Type /Encoding\n");
pdfout(" /Differences [ %d", ps->gbeg % 256);
for (i = ps->gbeg; i <= ps->gend; i++)
pdfout(" /%s", font_glget(fn, i)->id);
pdfout(" ]\n");
pdfout(">>\n");
obj_end();
/* the font object */
obj_beg(ps->obj);
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 %d\n", ps->gbeg % 256);
pdfout(" /LastChar %d\n", ps->gend % 256);
pdfout(" /Widths [");
for (i = ps->gbeg; i <= ps->gend; i++)
pdfout(" %d", (long) font_glget(fn, i)->wid * 100 * 72 / dev_res);
pdfout(" ]\n");
pdfout(" /FontDescriptor %d 0 R\n", ps->des);
pdfout(" /Encoding %d 0 R\n", enc_obj);
pdfout(">>\n");
obj_end();
font_close(fn);
}
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 % 40 == 39 && i + 1 < n)
sbuf_chr(d, '\n');
}
sbuf_str(d, ">\n");
}
/* write the object corresponding to this CID font */
static void pfont_writecid(struct pfont *ps)
{
int cid_obj;
struct font *fn = dev_fontopen(ps->desc);
int gcnt = 0;
int i;
/* CIDFont */
cid_obj = obj_beg(0);
pdfout("<<\n");
pdfout(" /Type /Font\n");
pdfout(" /Subtype /CIDFontType2\n");
pdfout(" /BaseFont /%s\n", ps->name);
pdfout(" /CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>\n");
pdfout(" /FontDescriptor %d 0 R\n", ps->des);
pdfout(" /DW 1000\n");
while (font_glget(fn, gcnt))
gcnt++;
pdfout(" /W [ %d [", ps->gbeg);
for (i = ps->gbeg; i <= ps->gend; i++)
pdfout(" %d", (long) font_glget(fn, i)->wid * 100 * 72 / dev_res);
pdfout(" ] ]\n");
pdfout(">>\n");
obj_end();
/* the font object */
obj_beg(ps->obj);
pdfout("<<\n");
pdfout(" /Type /Font\n");
pdfout(" /Subtype /Type0\n");
pdfout(" /BaseFont /%s\n", ps->name);
pdfout(" /Encoding /Identity-H\n");
pdfout(" /DescendantFonts [%d 0 R]\n", cid_obj);
pdfout(">>\n");
obj_end();
font_close(fn);
}
/* write font descriptor; returns its object ID */
static int writedesc(struct font *fn)
{
int str_obj = -1;
int des_obj;
char buf[1 << 10];
int fntype = fonttype(font_path(fn));
if (fntype == '1' || fntype == 't') {
int fd = open(font_path(fn), 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 (fntype == '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 (fntype == '1')
pdfout(" /Length2 %d\n", l2);
if (fntype == '1')
pdfout(" /Length3 %d\n", l3);
pdfout(">>\n");
pdfout("stream\n");
pdfmem(sbuf_buf(sb), sbuf_len(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", font_name(fn));
pdfout(" /Flags 32\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",
fntype == 't' ? "2" : "", str_obj);
pdfout(">>\n");
obj_end();
return des_obj;
}
static int pfont_find(struct glyph *g)
{
struct font *fn = g->font;
char *name = font_name(fn);
struct pfont *ps = NULL;
int fntype = fonttype(font_path(fn));
int sub = fntype == '1' ? font_glnum(fn, g) / 256 : 0;
int i;
for (i = 0; i < pfonts_n; i++)
if (!strcmp(name, pfonts[i].name) && pfonts[i].sub == sub)
return i;
if (pfonts_n == pfonts_sz) {
pfonts_sz += 16;
pfonts = mextend(pfonts, pfonts_n,
pfonts_sz, sizeof(pfonts[0]));
}
ps = &pfonts[pfonts_n];
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));
ps->cid = fntype == 't';
ps->obj = obj_map();
ps->sub = sub;
ps->gbeg = 1 << 20;
for (i = 0; i < pfonts_n; i++)
if (!strcmp(pfonts[i].name, ps->name))
break;
if (i < pfonts_n)
ps->des = pfonts[i].des;
else
ps->des = writedesc(fn);
return pfonts_n++;
}
static void pfont_done(void)
{
int i;
for (i = 0; i < pfonts_n; i++) {
if (pfonts[i].cid)
pfont_writecid(&pfonts[i]);
else
pfont_write(&pfonts[i]);
}
free(pfonts);
}
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 = pfont_find(g);
o_iset[fn] = 1;
return fn;
}
/* 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)
{
int gid;
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));
/* printing glyph identifier */
gid = font_glnum(g->font, g);
if (pfonts[o_i].cid)
sbuf_printf(pg, "%04x", gid);
else
sbuf_printf(pg, "%02x", gid % 256);
/* updating gbeg and gend */
if (gid < pfonts[o_i].gbeg)
pfonts[o_i].gbeg = gid;
if (gid > pfonts[o_i].gend)
pfonts[o_i].gend = gid;
/* advancing */
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_i >= 0 && (o_i != p_i || o_s != p_s)) {
struct pfont *ps = &pfonts[o_i];
o_flush();
if (ps->cid)
sbuf_printf(pg, "/%s %d Tf\n", ps->name, o_s);
else
sbuf_printf(pg, "/%s.%d %d Tf\n", ps->name, ps->sub, o_s);
p_i = o_i;
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_i = 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)
{
}
static char *strcut(char *dst, char *src)
{
while (*src == ' ' || *src == '\n')
src++;
if (src[0] == '"') {
src++;
while (*src && (src[0] != '"' || src[1] == '"')) {
if (*src == '"')
src++;
*dst++ = *src++;
}
if (*src == '"')
src++;
} else {
while (*src && *src != ' ' && *src != '\n')
*dst++ = *src++;
}
*dst = '\0';
return src;
}
/* return a copy of a PDF object; returns a static buffer */
static char *pdf_copy(char *pdf, int len, int pos)
{
static char buf[1 << 12];
int datlen;
pos += pdf_ws(pdf, len, pos);
datlen = pdf_len(pdf, len, pos);
if (datlen > sizeof(buf) - 1)
datlen = sizeof(buf) - 1;
memcpy(buf, pdf + pos, datlen);
buf[datlen] = '\0';
return buf;
}
static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb);
/* write stream to sb */
static int pdf_strcopy(char *pdf, int len, int pos, struct sbuf *sb)
{
int slen, val;
int beg;
if ((val = pdf_dval_val(pdf, len, pos, "/Length")) < 0)
return -1;
slen = atoi(pdf + val);
pos = pos + pdf_len(pdf, len, pos);
pos += pdf_ws(pdf, len, pos);
if (pos + slen + 15 > len)
return -1;
beg = pos;
pos += strlen("stream");
if (pdf[pos] == '\r')
pos++;
pos += 1 + slen;
if (pdf[pos] == '\r' || pdf[pos] == ' ')
pos++;
if (pdf[pos] == '\n')
pos++;
pos += strlen("endstream") + 1;
sbuf_mem(sb, pdf + beg, pos - beg);
return 0;
}
/* copy a PDF object and return its new identifier */
static int pdf_objcopy(char *pdf, int len, int pos)
{
int id;
if ((pos = pdf_ref(pdf, len, pos)) < 0)
return -1;
if (pdf_type(pdf, len, pos) == 'd') {
struct sbuf *sb = sbuf_make();
pdf_dictcopy(pdf, len, pos, sb);
sbuf_chr(sb, '\n');
if (pdf_dval(pdf, len, pos, "/Length") >= 0)
pdf_strcopy(pdf, len, pos, sb);
id = obj_beg(0);
pdfmem(sbuf_buf(sb), sbuf_len(sb));
obj_end();
sbuf_free(sb);
} else {
id = obj_beg(0);
pdfmem(pdf + pos, pdf_len(pdf, len, pos));
pdfout("\n");
obj_end();
}
return id;
}
/* copy a PDF dictionary recursively */
static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb)
{
int i;
int key, val, id;
sbuf_printf(sb, "<<");
for (i = 0; ; i++) {
if ((key = pdf_dkey(pdf, len, pos, i)) < 0)
break;
sbuf_printf(sb, " %s", pdf_copy(pdf, len, key));
val = pdf_dval(pdf, len, pos, pdf_copy(pdf, len, key));
if (pdf_type(pdf, len, val) == 'r') {
if ((id = pdf_objcopy(pdf, len, val)) >= 0)
sbuf_printf(sb, " %d 0 R", id);
} else {
sbuf_printf(sb, " %s", pdf_copy(pdf, len, val));
}
}
sbuf_printf(sb, " >>");
}
/* copy resources dictionary */
static void pdf_rescopy(char *pdf, int len, int pos, struct sbuf *sb)
{
char *res_fields[] = {"/ProcSet", "/ExtGState", "/ColorSpace",
"/Pattern", "/Shading", "/Properties", "/Font", "/XObject"};
int res, i;
sbuf_printf(sb, " /Resources <<\n");
for (i = 0; i < LEN(res_fields); i++) {
if ((res = pdf_dval_val(pdf, len, pos, res_fields[i])) >= 0) {
if (pdf_type(pdf, len, res) == 'd') {
sbuf_printf(sb, " %s ", res_fields[i]);
pdf_dictcopy(pdf, len, res, sb);
sbuf_printf(sb, "\n");
} else {
sbuf_printf(sb, " %s %s\n", res_fields[i],
pdf_copy(pdf, len, res));
}
}
}
sbuf_printf(sb, " >>\n");
}
static int pdfbbox(char *pdf, int len, int pos, int dim[4])
{
int val;
int i;
for (i = 0; i < 4; i++) {
if ((val = pdf_lval(pdf, len, pos, i)) < 0)
return -1;
dim[i] = atoi(pdf + val);
}
return 0;
}
static int pdfext(char *pdf, int len, int hwid, int vwid)
{
char *cont_fields[] = {"/Filter", "/DecodeParms"};
int trailer, root, cont, pages, page1, res;
int kids_val, page1_val, val, bbox;
int xobj_id, length;
int dim[4];
int hzoom = 100, vzoom = 100;
struct sbuf *sb;
int i;
if ((trailer = pdf_trailer(pdf, len)) < 0)
return -1;
if ((root = pdf_dval_obj(pdf, len, trailer, "/Root")) < 0)
return -1;
if ((pages = pdf_dval_obj(pdf, len, root, "/Pages")) < 0)
return -1;
if ((kids_val = pdf_dval_val(pdf, len, pages, "/Kids")) < 0)
return -1;
if ((page1_val = pdf_lval(pdf, len, kids_val, 0)) < 0)
return -1;
if ((page1 = pdf_ref(pdf, len, page1_val)) < 0)
return -1;
if ((cont = pdf_dval_obj(pdf, len, page1, "/Contents")) < 0)
return -1;
if ((val = pdf_dval_val(pdf, len, cont, "/Length")) < 0)
return -1;
res = pdf_dval_val(pdf, len, page1, "/Resources");
length = atoi(pdf + val);
bbox = pdf_dval_val(pdf, len, page1, "/MediaBox");
if (bbox < 0)
bbox = pdf_dval_val(pdf, len, pages, "/MediaBox");
if (bbox >= 0 && !pdfbbox(pdf, len, bbox, dim)) {
if (hwid > 0)
hzoom = hwid / (dim[2] - dim[0]);
if (vwid > 0)
vzoom = vwid / (dim[3] - dim[1]);
if (vwid <= 0)
vzoom = hzoom;
if (hwid <= 0)
hzoom = vzoom;
}
sb = sbuf_make();
sbuf_printf(sb, "<<\n");
sbuf_printf(sb, " /Type /XObject\n");
sbuf_printf(sb, " /Subtype /Form\n");
sbuf_printf(sb, " /FormType 1\n");
if (bbox >= 0)
sbuf_printf(sb, " /BBox %s\n", pdf_copy(pdf, len, bbox));
sbuf_printf(sb, " /Matrix [%d.%02d 0 0 %d.%02d %s]\n",
hzoom / 100, hzoom % 100, vzoom / 100, vzoom % 100,
pdfpos(o_h, o_v));
if (res >= 0)
pdf_rescopy(pdf, len, res, sb);
sbuf_printf(sb, " /Length %d\n", length);
for (i = 0; i < LEN(cont_fields); i++)
if ((val = pdf_dval_val(pdf, len, cont, cont_fields[i])) >= 0)
sbuf_printf(sb, " %s %s\n", cont_fields[i],
pdf_copy(pdf, len, val));
sbuf_printf(sb, ">>\n");
pdf_strcopy(pdf, len, cont, sb);
xobj_id = obj_beg(0);
pdfmem(sbuf_buf(sb), sbuf_len(sb));
obj_end();
sbuf_free(sb);
if (xobj_n == xobj_sz) {
xobj_sz += 8;
xobj = mextend(xobj, xobj_n, xobj_sz, sizeof(xobj[0]));
}
xobj[xobj_n++] = xobj_id;
return xobj_n - 1;
}
void outpdf(char *spec)
{
char pdf[1 << 12];
char buf[1 << 12];
struct sbuf *sb;
int hwid, vwid, nspec;
int xobj_id;
int fd, nr;
spec = strcut(pdf, spec);
if (!pdf[0])
return;
/* requested image dimensions */
nspec = sscanf(spec, "%d %d", &hwid, &vwid);
if (nspec < 1)
hwid = 0;
if (nspec < 2)
vwid = 0;
/* reading the pdf file */
sb = sbuf_make();
fd = open(pdf, O_RDONLY);
while ((nr = read(fd, buf, sizeof(buf))) > 0)
sbuf_mem(sb, buf, nr);
close(fd);
/* the XObject */
xobj_id = pdfext(sbuf_buf(sb), sbuf_len(sb), hwid, vwid);
sbuf_free(sb);
o_flush();
out_fontup();
sbuf_printf(pg, "ET /FO%d Do BT\n", xobj_id);
p_h = -1;
p_v = -1;
}
void outlink(char *spec)
{
}
void outpage(void)
{
o_v = 0;
o_h = 0;
p_i = 0;
p_v = 0;
p_h = 0;
p_s = 0;
p_f = 0;
p_m = 0;
o_i = -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\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 */
pfont_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) - 1);
pdfout(">>\n");
pdfout("stream\n");
pdfmem(sbuf_buf(pg), sbuf_len(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 < pfonts_n; i++) {
if (o_iset[i]) {
struct pfont *ps = &pfonts[i];
if (ps->cid)
pdfout(" /%s %d 0 R", ps->name, ps->obj);
else
pdfout(" /%s.%d %d 0 R", ps->name, ps->sub, ps->obj);
}
}
pdfout(" >>\n");
if (xobj_n) { /* XObjects */
pdfout(" /XObject <<");
for (i = 0; i < xobj_n; i++)
pdfout(" /FO%d %d 0 R", i, xobj[i]);
pdfout(" >>\n");
}
pdfout(" >>\n");
pdfout(" /Contents %d 0 R\n", cont_id);
pdfout(">>\n");
obj_end();
sbuf_free(pg);
memset(o_iset, 0, pfonts_n * sizeof(o_iset[0]));
free(xobj);
xobj = NULL;
xobj_n = 0;
xobj_sz = 0;
}