shithub: neatpost

ref: 60fd4c1038c15de25849494c7fd3df6e12b5bb67
dir: /ps.c/

View raw version
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <stdio.h>
#include "post.h"

static char ps_title[256];	/* document title */
static char ps_author[256];	/* document author */
static int ps_height;		/* document height in basic units */
static int o_f, o_s, o_m;	/* font and size */
static int o_h, o_v;		/* current user position */
static int p_f, p_s, p_m;	/* output postscript font */
static int o_qtype;		/* queued character type */
static int o_qv, o_qh, o_qend;	/* queued character position */
static int o_rh, o_rv, o_rdeg;	/* previous rotation position and degree */
static int o_gname;		/* use glyphshow for all glyphs */

static char o_fonts[FNLEN * NFONTS] = " ";

static void outvf(char *s, va_list ap)
{
	vfprintf(stdout, s, ap);
}

static void outf(char *s, ...)
{
	va_list ap;
	va_start(ap, s);
	outvf(s, ap);
	va_end(ap);
}

static void o_flush(void)
{
	if (o_qtype == 1)
		outf(") %d %d w\n", o_qh, o_qv);
	if (o_qtype == 2)
		outf("] %d %d g\n", o_qh, o_qv);
	o_qtype = 0;
}

void outgname(int g)
{
	o_gname = g;
}

void outpage(void)
{
	o_flush();
	o_v = 0;
	o_h = 0;
	p_s = 0;
	p_f = 0;
	p_m = 0;
	o_rdeg = 0;
}

static void o_queue(struct glyph *g)
{
	int type = 1 + (g->pos <= 0 || o_gname);
	if (o_qtype != type || o_qend != o_h || o_qv != o_v) {
		o_flush();
		o_qh = o_h;
		o_qv = o_v;
		o_qtype = type;
		outf(type == 1 ? "(" : "[");
	}
	if (o_qtype == 1) {
		if (g->pos >= ' ' && g->pos <= '~')
			outf("%s%c", strchr("()\\", g->pos) ? "\\" : "", g->pos);
		else
			outf("\\%d%d%d", (g->pos >> 6) & 7,
					(g->pos >> 3) & 7, g->pos & 7);
	} else {
		outf("/%s", g->id);
	}
	o_qend = o_h + font_wid(g->font, o_s, g->wid);
}

/* calls o_flush() if necessary */
void out(char *s, ...)
{
	va_list ap;
	o_flush();
	va_start(ap, s);
	outvf(s, ap);
	va_end(ap);
}

static void out_fontup(int fid)
{
	char fnname[FNLEN];
	struct font *fn;
	if (o_m != p_m) {
		out("%d %d %d rgb\n", CLR_R(o_m), CLR_G(o_m), CLR_B(o_m));
		p_m = o_m;
	}
	if (fid != p_f || o_s != p_s) {
		fn = dev_font(fid);
		out("%d /%s f\n", o_s, font_name(fn));
		p_f = fid;
		p_s = o_s;
		sprintf(fnname, " %s ", font_name(fn));
		if (!strstr(o_fonts, fnname))
			sprintf(strchr(o_fonts, '\0'), "%s ", font_name(fn));
	}
}

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;
	}
	out_fontup(dev_fontid(fn));
	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;
}

/* a font was mounted at pos f */
void outmnt(int f)
{
	if (p_f == f)
		p_f = -1;
}

void outsize(int s)
{
	if (s > 0)
		o_s = s;
}

void outcolor(int c)
{
	o_m = c;
}

void outrotate(int deg)
{
	o_flush();
	out_fontup(o_f);
	if (o_rdeg)
		outf("%d %d %d rot\n", -o_rdeg, o_rh, o_rv);
	o_rdeg = deg;
	o_rh = o_h;
	o_rv = o_v;
	outf("%d %d %d rot\n", deg, o_h, o_v);
}

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)
		outf("%d %d m ", o_h, o_v);
	draw_point = 1;
}

/* start a multi-segment path */
void drawmbeg(char *s)
{
	o_flush();
	out_fontup(o_f);
	draw_path = 1;
	outf("gsave newpath %s\n", s);
}

/* end a multi-segment path */
void drawmend(char *s)
{
	draw_path = 0;
	draw_point = 0;
	outf("%s grestore\n", s);
}

void drawbeg(void)
{
	o_flush();
	out_fontup(o_f);
	if (draw_path)
		return;
	outf("newpath ");
}

void drawend(int close, int fill)
{
	if (draw_path)
		return;
	draw_point = 0;
	if (close)
		outf("closepath ");
	if (fill)
		outf("fill\n");
	else
		outf("stroke\n");
}

void drawl(int h, int v)
{
	drawmv();
	outrel(h, v);
	outf("%d %d drawl ", o_h, o_v);
}

void drawc(int c)
{
	drawmv();
	outrel(c, 0);
	outf("%d %d drawe ", c, c);
}

void drawe(int h, int v)
{
	drawmv();
	outrel(h, 0);
	outf("%d %d drawe ", h, v);
}

void drawa(int h1, int v1, int h2, int v2)
{
	drawmv();
	outf("%d %d %d %d drawa ", h1, v1, h2, v2);
	outrel(h1 + h2, v1 + v2);
}

void draws(int h1, int v1, int h2, int v2)
{
	drawmv();
	outf("%d %d %d %d %d %d draws ", o_h, o_v, o_h + h1, o_v + v1,
		o_h + h1 + h2, o_v + v1 + v2);
	outrel(h1, v1);
}

void outeps(char *eps, int hwid, int vwid)
{
	char buf[1 << 12];
	int llx, lly, urx, ury;
	FILE *filp;
	int nbb, ver;
	if (!(filp = fopen(eps, "r")))
		return;
	if (!fgets(buf, sizeof(buf), filp)) {
		fprintf(stderr, "warning: file %s is empty\n", eps);
		fclose(filp);
		return;
	}
	if (sscanf(buf, "%%!PS-Adobe-%d.%d EPSF-%d.%d", &ver, &ver, &ver, &ver) != 4) {
		fprintf(stderr, "warning: unsupported EPSF header in %s\n", eps);
		fclose(filp);
		return;
	}
	nbb = 0;
	while (fgets(buf, sizeof(buf), filp))
		if (!strncmp(buf, "%%BoundingBox: ", 15))
			if ((nbb = sscanf(buf + 15, "%d %d %d %d",
					&llx, &lly, &urx, &ury)) == 4)
				break;
	fclose(filp);
	if (nbb < 4)		/* no BoundingBox comment */
		return;
	if (hwid <= 0 && vwid <= 0)
		hwid = (urx - llx) * dev_res / 72;
	if (vwid <= 0)
		vwid = (ury - lly) * hwid / (urx - llx);
	if (hwid <= 0)
		hwid = (urx - llx) * vwid / (ury - lly);
	/* output the EPS file */
	o_flush();
	out_fontup(o_f);
	outf("%d %d %d %d %d %d %d %d EPSFBEG\n",
		llx, lly, hwid, urx - llx, vwid, ury - lly, o_h, o_v);
	outf("%%%%BeginDocument: %s\n", eps);
	filp = fopen(eps, "r");
	while (fgets(buf, sizeof(buf), filp))
		out("%s", buf);
	fclose(filp);
	outf("%%%%EndDocument\n");
	outf("EPSFEND\n");
}

void outpdf(char *pdf, int hwid, int vwid)
{
}

void outlink(char *lnk, int hwid, int vwid)
{
	o_flush();
	if (lnk[0] == '#' || isdigit((unsigned char) lnk[0])) {
		outf("[ /Rect [ %d %d t %d %d t ] %s%s "
			"/Subtype /Link /LNK pdfmark\n",
			o_h, o_v, o_h + hwid, o_v + vwid,
			lnk[0] == '#' ? "/Dest /" : "/Page ",
			lnk[0] == '#' ? lnk + 1 : lnk);
	} else {
		outf("[ /Rect [ %d %d t %d %d t ] "
			"/Action << /Subtype /URI /URI %s >> /Open true "
			"/Subtype /Link /LNK pdfmark\n",
			o_h, o_v, o_h + hwid, o_v + vwid, pdftext_static(lnk));
	}
}

void outname(int n, char (*desc)[64], int *page, int *off)
{
	int i;
	o_flush();
	for (i = 0; i < n; i++) {
		outf("[ /Dest /%s", desc[i]);
		outf(" /Page %d", page[i]);
		if (off[i] > 0)
			outf(" /View [/XYZ null %d null]",
				(ps_height - off[i]) * 72 / dev_res);
		outf(" /DEST pdfmark\n");
	}
}

void outmark(int n, char (*desc)[256], int *page, int *off, int *level)
{
	int i, j;
	o_flush();
	for (i = 0; i < n; i++) {
		int cnt = 0;
		for (j = i + 1; j < n && level[j] > level[i]; j++)
			if (level[j] == level[i] + 1)
				cnt++;
		outf("[ /Title %s", pdftext_static(desc[i]));
		if (page[i] > 0)
			outf(" /Page %d", page[i]);
		if (cnt > 0)
			outf(" /Count %d", cnt);
		if (off[i] > 0)
			outf(" /View [/XYZ null %d null]",
				(ps_height - off[i]) * 72 / dev_res);
		outf(" /OUT pdfmark\n");
	}
}

void outinfo(char *kwd, char *val)
{
	if (!strcmp("Author", kwd))
		snprintf(ps_author, sizeof(ps_author), "%s", val);
	if (!strcmp("Title", kwd))
		snprintf(ps_title, sizeof(ps_title), "%s", val);
}

void outset(char *var, char *val)
{
}

void docpagebeg(int n)
{
	out("%%%%Page: %d %d\n", n, n);
	out("/saveobj save def\n");
	out("mark\n");
	out("%d pagesetup\n", n);
}

void docpageend(int n)
{
	out("cleartomark\n");
	out("showpage\n");
	out("saveobj restore\n");
}

void doctrailer(int pages)
{
	out("[");
	if (ps_title[0])
		out(" /Title %s", pdftext_static(ps_title));
	if (ps_author[0])
		out(" /Author %s", pdftext_static(ps_author));
	out(" /Creator (Neatroff) /DOCINFO pdfmark\n");
	out("%%%%Trailer\n");
	out("done\n");
	out("%%%%DocumentFonts: %s\n", o_fonts);
	out("%%%%Pages: %d\n", pages);
	out("%%%%EOF\n");
}

static char *prolog =
	"/setup {\n"
	"	counttomark 2 idiv {def} repeat pop\n"
	"	/scaling 72 resolution div def\n"
	"	linewidth setlinewidth\n"
	"	1 setlinecap\n"
	"	0 pagesize 1 get translate\n"
	"	scaling scaling scale\n"
	"	0 0 moveto\n"
	"} def\n"
	"\n"
	"/pagesetup {\n"
	"	/page exch def\n"
	"	currentdict /pagedict known currentdict page known and {\n"
	"		page load pagedict exch get cvx exec\n"
	"	} if\n"
	"} def\n"
	"\n"
	"/pdfmark where\n"
	"	{ pop globaldict /?pdfmark /exec load put }\n"
	"	{ globaldict begin\n"
	"		/?pdfmark /pop load def\n"
	"		/pdfmark /cleartomark load def\n"
	"		end }\n"
	"	ifelse\n"
	"\n"
	"/t {neg} bind def\n"
	"/w {neg moveto show} bind def\n"
	"/m {neg moveto} bind def\n"
	"/g {neg moveto {glyphshow} forall} bind def\n"
	"/rgb {255 div 3 1 roll 255 div 3 1 roll 255 div 3 1 roll setrgbcolor} bind def\n"
	"/rot {/y exch def /x exch def x y neg translate rotate x neg y translate} bind def\n"
	"/done {/lastpage where {pop lastpage} if} def\n"
	"\n"
	"% caching fonts, as selectfont is supposed to be doing\n"
	"/fncache 16 dict def\n"
	"/selectfont_append { fncache exch dup findfont put } bind def\n"
	"/selectfont_cached {\n"
	"	exch dup fncache exch known not { dup selectfont_append } if\n"
	"	fncache exch get exch scalefont setfont\n"
	"} bind def\n"
	"/f {\n"
	"	exch dup 3 1 roll scaling div selectfont_cached\n"
	"	linewidth mul scaling 10 mul div setlinewidth\n"
	"} bind def\n"
	"\n"
	"/savedmatrix matrix def\n"
	"/drawl {\n"
	"	neg lineto\n"
	"} bind def\n"
	"/drawe {\n"
	"	savedmatrix currentmatrix pop scale\n"
	"	.5 0 rmoveto currentpoint .5 0 rmoveto .5 0 360 arc\n"
	"	savedmatrix setmatrix\n"
	"} bind def\n"
	"/drawa {\n"
	"	/dy2 exch def\n"
	"	/dx2 exch def\n"
	"	/dy1 exch def\n"
	"	/dx1 exch def\n"
	"	currentpoint dy1 neg add exch dx1 add exch\n"
	"	dx1 dx1 mul dy1 dy1 mul add sqrt\n"
	"	dy1 dx1 neg atan\n"
	"	dy2 neg dx2 atan\n"
	"	arc\n"
	"} bind def\n"
	"/draws {\n"
	"	/y2 exch def\n"
	"	/x2 exch def\n"
	"	/y1 exch def\n"
	"	/x1 exch def\n"
	"	/y0 exch def\n"
	"	/x0 exch def\n"
	"	x0 5 x1 mul add 6 div\n"
	"	y0 5 y1 mul add -6 div\n"
	"	x2 5 x1 mul add 6 div\n"
	"	y2 5 y1 mul add -6 div\n"
	"	x1 x2 add 2 div\n"
	"	y1 y2 add -2 div\n"
	"	curveto\n"
	"} bind def\n"
	"% including EPS files\n"
	"/EPSFBEG {\n"
	"	/epsf_state save def\n"
	"	neg translate\n"
	"	div 3 1 roll div exch scale\n"
	"	neg exch neg exch translate\n"
	"	/dict_count countdictstack def\n"
	"	/op_count count 1 sub def\n"
	"	userdict begin\n"
	"	/showpage { } def\n"
	"	0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n"
	"	10 setmiterlimit [ ] 0 setdash newpath\n"
	"} bind def\n"
	"/EPSFEND {\n"
	"	count op_count sub {pop} repeat\n"
	"	countdictstack dict_count sub {end} repeat\n"
	"	epsf_state restore\n"
	"} bind def\n";

/* pagewidth and pageheight are in tenths of a millimetre */
void docheader(char *title, int pagewidth, int pageheight, int linewidth)
{
	ps_height = pageheight * dev_res / 254;
	out("%%!PS-Adobe-2.0\n");
	out("%%%%Version: 1.0\n");
	if (title)
		out("%%%%Title: %s\n", title);
	out("%%%%Creator: Neatroff\n");
	out("%%%%DocumentFonts: (atend)\n");
	out("%%%%Pages: (atend)\n");
	out("%%%%EndComments\n");

	out("%%%%BeginProlog\n");
	out("/resolution %d def\n", dev_res);
	out("/pagesize [%d %d] def\n", (pagewidth * 72 + 127) / 254,
					(pageheight * 72 + 127) / 254);
	out("/linewidth %d.%02d def\n\n", linewidth / 100, linewidth % 100);
	out("%s", prolog);
	out("%%%%EndProlog\n");
	out("%%%%BeginSetup\n");
	out("<< /PageSize pagesize /ImagingBBox null >> setpagedevice\n");
	out("mark\n");
	out("setup\n");
	out("%%%%EndSetup\n");
}