shithub: neatpost

Download patch

ref: be125d2592791b5b168ab0e828ba561725500fae
author: Ali Gholami Rudi <ali@rudi.ir>
date: Sun Jun 9 18:57:37 EDT 2013

neatpost started

Based on troff's dpost postscript output.

--- /dev/null
+++ b/Makefile
@@ -1,0 +1,11 @@
+CC = cc
+CFLAGS = -Wall -O2 -DTROFFROOT=\"/root/troff/home\"
+LDFLAGS =
+
+all: post
+%.o: %.c post.h
+	$(CC) -c $(CFLAGS) $<
+post: post.o out.o ps.o font.o dev.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+clean:
+	rm -f *.o post
--- /dev/null
+++ b/dev.c
@@ -1,0 +1,142 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+char dev_dir[PATHLEN];	/* device directory */
+int dev_res;		/* device resolution */
+int dev_uwid;		/* device unitwidth */
+int dev_hor;		/* minimum horizontal movement */
+int dev_ver;		/* minimum vertical movement */
+
+/* mounted fonts */
+static char fn_name[NFONTS][FNLEN];	/* font names */
+static struct font *fn_font[NFONTS];	/* font structs */
+static int fn_n;			/* number of mounted fonts */
+
+static void skipline(FILE* filp)
+{
+	int c;
+	do {
+		c = getc(filp);
+	} while (c != '\n' && c != EOF);
+}
+
+int dev_mnt(int pos, char *id, char *name)
+{
+	char path[PATHLEN];
+	struct font *fn;
+	sprintf(path, "%s/%s", dev_dir, name);
+	fn = font_open(path);
+	if (!fn)
+		return -1;
+	if (fn_font[pos])
+		font_close(fn_font[pos]);
+	if (fn_name[pos] != name)	/* ignore if fn_name[pos] is passed */
+		strcpy(fn_name[pos], id);
+	fn_font[pos] = fn;
+	return pos;
+}
+
+int dev_open(char *dir)
+{
+	char path[PATHLEN];
+	char tok[ILNLEN];
+	FILE *desc;
+	int i;
+	strcpy(dev_dir, dir);
+	sprintf(path, "%s/DESC", dir);
+	desc = fopen(path, "r");
+	while (fscanf(desc, "%s", tok) == 1) {
+		if (tok[0] == '#') {
+			skipline(desc);
+			continue;
+		}
+		if (!strcmp("fonts", tok)) {
+			fscanf(desc, "%d", &fn_n);
+			for (i = 0; i < fn_n; i++)
+				fscanf(desc, "%s", fn_name[i + 1]);
+			fn_n++;
+			continue;
+		}
+		if (!strcmp("sizes", tok)) {
+			while (fscanf(desc, "%s", tok) == 1)
+				if (!strcmp("0", tok))
+					break;
+			continue;
+		}
+		if (!strcmp("res", tok)) {
+			fscanf(desc, "%d", &dev_res);
+			continue;
+		}
+		if (!strcmp("unitwidth", tok)) {
+			fscanf(desc, "%d", &dev_uwid);
+			continue;
+		}
+		if (!strcmp("hor", tok)) {
+			fscanf(desc, "%d", &dev_hor);
+			continue;
+		}
+		if (!strcmp("ver", tok)) {
+			fscanf(desc, "%d", &dev_ver);
+			continue;
+		}
+		if (!strcmp("charset", tok)) {
+			break;
+		}
+	}
+	fclose(desc);
+	return 0;
+}
+
+void dev_close(void)
+{
+	int i;
+	for (i = 0; i < fn_n; i++) {
+		if (fn_font[i])
+			font_close(fn_font[i]);
+		fn_font[i] = NULL;
+	}
+}
+
+struct glyph *dev_glyph(char *c, int fn)
+{
+	struct glyph *g;
+	int i;
+	g = font_find(fn_font[fn], c);
+	if (g)
+		return g;
+	for (i = 0; i < fn_n; i++)
+		if (fn_font[i] && fn_font[i]->special)
+			if ((g = font_find(fn_font[i], c)))
+				return g;
+	return NULL;
+}
+
+struct glyph *dev_glyph_byid(char *id, int fn)
+{
+	return font_glyph(fn_font[fn], id);
+}
+
+int charwid(int wid, int sz)
+{
+	/* the original troff rounds the widths up */
+	return (wid * sz + dev_uwid / 2) / dev_uwid;
+}
+
+struct font *dev_font(int fn)
+{
+	return fn < fn_n ? fn_font[fn] : NULL;
+}
+
+int dev_fontid(struct font *fn, int cur)
+{
+	int i;
+	if (fn_font[cur] == fn)
+		return cur;
+	for (i = 0; i < fn_n; i++)
+		if (fn_font[i] == fn)
+			return i;
+	return 0;
+}
--- /dev/null
+++ b/font.c
@@ -1,0 +1,114 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+static void skipline(FILE* filp)
+{
+	int c;
+	do {
+		c = getc(filp);
+	} while (c != '\n' && c != EOF);
+}
+
+struct glyph *font_find(struct font *fn, char *name)
+{
+	int i;
+	for (i = 0; i < fn->n; i++)
+		if (!strcmp(name, fn->c[i]))
+			return fn->g[i];
+	return NULL;
+}
+
+struct glyph *font_glyph(struct font *fn, char *id)
+{
+	int i;
+	for (i = 0; i < fn->nglyphs; i++)
+		if (!strcmp(fn->glyphs[i].id, id))
+			return &fn->glyphs[i];
+	return NULL;
+}
+
+static void font_charset(struct font *fn, FILE *fin)
+{
+	char tok[ILNLEN];
+	char name[ILNLEN];
+	char id[ILNLEN];
+	struct glyph *glyph = NULL;
+	struct glyph *prev = NULL;
+	int wid, type;
+	while (fn->n < NGLYPHS) {
+		if (fscanf(fin, "%s", name) != 1)
+			break;
+		fscanf(fin, "%s", tok);
+		glyph = prev;
+		if (strcmp("\"", tok)) {
+			wid = atoi(tok);
+			fscanf(fin, "%d %s", &type, id);
+			skipline(fin);
+			glyph = &fn->glyphs[fn->nglyphs++];
+			strcpy(glyph->id, id);
+			strcpy(glyph->name, name);
+			glyph->wid = wid;
+			glyph->type = type;
+			glyph->font = fn;
+		}
+		prev = glyph;
+		strcpy(fn->c[fn->n], name);
+		fn->g[fn->n] = glyph;
+		fn->n++;
+	}
+}
+
+struct font *font_open(char *path)
+{
+	struct font *fn = malloc(sizeof(*fn));
+	char tok[ILNLEN];
+	FILE *fin;
+	fin = fopen(path, "r");
+	memset(fn, 0, sizeof(*fn));
+	while (fscanf(fin, "%s", tok) == 1) {
+		if (tok[0] == '#') {
+			skipline(fin);
+			continue;
+		}
+		if (!strcmp("spacewidth", tok)) {
+			fscanf(fin, "%d", &fn->spacewid);
+			continue;
+		}
+		if (!strcmp("special", tok)) {
+			fn->special = 1;
+			continue;
+		}
+		if (!strcmp("name", tok)) {
+			fscanf(fin, "%s", fn->name);
+			continue;
+		}
+		if (!strcmp("fontname", tok)) {
+			fscanf(fin, "%s", fn->psname);
+			continue;
+		}
+		if (!strcmp("named", tok)) {
+			skipline(fin);
+			continue;
+		}
+		if (!strcmp("ligatures", tok)) {
+			while (fscanf(fin, "%s", tok) == 1)
+				if (!strcmp("0", tok))
+					break;
+			skipline(fin);
+			continue;
+		}
+		if (!strcmp("charset", tok)) {
+			font_charset(fn, fin);
+			break;
+		}
+	}
+	fclose(fin);
+	return fn;
+}
+
+void font_close(struct font *fn)
+{
+	free(fn);
+}
--- /dev/null
+++ b/out.c
@@ -1,0 +1,163 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+static int o_f, o_s;		/* font and size */
+static int o_h, o_v;		/* current user position */
+static int p_f, p_s;		/* output postscript font */
+static int o_qtype;		/* queued character type */
+static int o_qv, o_qh, o_qend;	/* queued character position */
+char o_fonts[FNLEN * NFONTS] = " ";
+
+static void outf(char *s, va_list ap)
+{
+	vfprintf(stdout, s, ap);
+}
+
+void outvf(char *s, ...)
+{
+	va_list ap;
+	va_start(ap, s);
+	outf(s, ap);
+	va_end(ap);
+}
+
+static void o_flush(void)
+{
+	if (o_qtype == 1)
+		outvf(") %d %d w\n", o_qh, o_qv);
+	if (o_qtype == 2)
+		outvf("] %d %d g\n", o_qh, o_qv);
+	o_qtype = 0;
+}
+
+void outpage(void)
+{
+	o_flush();
+	o_v = 0;
+	o_h = 0;
+	p_s = 0;
+	p_f = 0;
+}
+
+static void o_queue(struct glyph *g)
+{
+	int type = 1 + !isdigit((g)->id[0]);
+	int num = atoi(g->id);
+	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;
+		outvf(type == 1 ? "(" : "[");
+	}
+	if (o_qtype == 1) {
+		if (num >= ' ' && num <= '~')
+			outvf("%s%c", strchr("()\\", num) ? "\\" : "", num);
+		else
+			outvf("\\%d%d%d",
+				(num >> 6) & 7, (num >> 3) & 7, num & 7);
+	} else {
+		outvf("/%s", g->id);
+	}
+	o_qend = o_h + charwid(g->wid, o_s);
+}
+
+/* calls o_flush() if necessary */
+void out(char *s, ...)
+{
+	va_list ap;
+	o_flush();
+	va_start(ap, s);
+	outf(s, ap);
+	va_end(ap);
+}
+
+/* glyph placement adjustments */
+static struct fixlist {
+	char *name;
+	int dh, dv;
+} fixlist[] = {
+	{"br", -5, 15},
+	{"lc", 20, 0},
+	{"lf", 20, 0},
+	{"rc", -11, 0},
+	{"rf", -11, 0},
+	{"rn", -50, 0},
+};
+
+static void fixpos(struct glyph *g, int *dh, int *dv)
+{
+	struct font *fn = g->font;
+	int i;
+	*dh = 0;
+	*dv = 0;
+	if (!strcmp("S", fn->name)) {
+		for (i = 0; i < LEN(fixlist); i++) {
+			if (!strcmp(fixlist[i].name, g->name)) {
+				*dh = charwid(fixlist[i].dh, o_s);
+				*dv = charwid(fixlist[i].dv, o_s);
+				return;
+			}
+		}
+	}
+}
+
+void outc(char *c)
+{
+	struct glyph *g;
+	struct font *fn;
+	char fnname[FNLEN];
+	int fontid;
+	int dh, dv;
+	g = dev_glyph(c, o_f);
+	fn = g ? g->font : dev_font(o_f);
+	if (!g) {
+		outrel(*c == ' ' && fn ? charwid(fn->spacewid, o_s) : 1, 0);
+		return;
+	}
+	fontid = dev_fontid(fn, o_f);
+	if (fontid != p_f || o_s != p_s) {
+		out("%d /%s f\n", o_s, fn->psname);
+		p_f = fontid;
+		p_s = o_s;
+		sprintf(fnname, " %s ", fn->psname);
+		if (!strstr(o_fonts, fnname))
+			sprintf(strchr(o_fonts, '\0'), "%s ", fn->psname);
+	}
+	fixpos(g, &dh, &dv);
+	o_h += dh;
+	o_v += dv;
+	o_queue(g);
+	o_h -= dh;
+	o_v -= dv;
+}
+
+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)
+{
+	o_f = f;
+}
+
+void outsize(int s)
+{
+	o_s = s;
+}
--- /dev/null
+++ b/post.c
@@ -1,0 +1,271 @@
+/*
+ * neatpost troff postscript postprocessor
+ *
+ * Copyright (C) 2013 Ali Gholami Rudi <ali at rudi dot ir>
+ *
+ * This program is released under the Modified BSD license.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include "post.h"
+
+static int o_pg;
+
+static int next(void)
+{
+	return getc(stdin);
+}
+
+static void back(int c)
+{
+	ungetc(c, stdin);
+}
+
+static int utf8len(int c)
+{
+	if (c <= 0x7f)
+		return 1;
+	if (c >= 0xfc)
+		return 6;
+	if (c >= 0xf8)
+		return 5;
+	if (c >= 0xf0)
+		return 4;
+	if (c >= 0xe0)
+		return 3;
+	if (c >= 0xc0)
+		return 2;
+	return 1;
+}
+
+static int nextutf8(char *s)
+{
+	int c = next();
+	int l = utf8len(c);
+	int i;
+	if (c < 0)
+		return 0;
+	s[0] = c;
+	for (i = 1; i < l; i++)
+		s[i] = next();
+	s[l] = '\0';
+	return l;
+}
+
+/* skip blanks */
+static void nextskip(void)
+{
+	int c;
+	do {
+		c = next();
+	} while (isspace(c));
+	back(c);
+}
+
+static int nextnum(void)
+{
+	int c;
+	int n = 0;
+	int neg = 0;
+	nextskip();
+	while (1) {
+		c = next();
+		if (!n && c == '-') {
+			neg = 1;
+			continue;
+		}
+		if (!isdigit(c))
+			back(c);
+		if (c < 0 || !isdigit(c))
+			break;
+		n = n * 10 + c - '0';
+	}
+	return neg ? -n : n;
+}
+
+static int iseol(void)
+{
+	int c;
+	do {
+		c = next();
+	} while (c != ' ');
+	if (c != '\n')
+		back(c);
+	return c == '\n';
+}
+
+/* skip until the end of line */
+static void nexteol(void)
+{
+	int c;
+	do {
+		c = next();
+	} while (c >= 0 && c != '\n');
+}
+
+static void nextword(char *s)
+{
+	int c;
+	nextskip();
+	c = next();
+	while (c >= 0 && !isspace(c)) {
+		*s++ = c;
+		c = next();
+	}
+	if (c >= 0)
+		back(c);
+	*s = '\0';
+}
+
+static void draw(void)
+{
+	int h1, h2, v1, v2;
+	int c = next();
+	switch (c) {
+	case 'l':
+		h1 = nextnum();
+		v1 = nextnum();
+		outrel(h1, v1);
+		break;
+	case 'c':
+		h1 = nextnum();
+		outrel(h1, 0);
+		break;
+	case 'e':
+		h1 = nextnum();
+		v1 = nextnum();
+		outrel(h1, 0);
+		break;
+	case 'a':
+		h1 = nextnum();
+		v1 = nextnum();
+		h2 = nextnum();
+		v2 = nextnum();
+		outrel(h1 + h2, v1 + v2);
+		break;
+	default:
+		h1 = nextnum();
+		v1 = nextnum();
+		outrel(h1, v1);
+		while (!iseol()) {
+			h2 = nextnum();
+			v2 = nextnum();
+			outrel(h2, v2);
+		}
+		break;
+	}
+	nexteol();
+}
+
+static char devpath[PATHLEN] = "devutf";
+
+static void postx(void)
+{
+	char cmd[128];
+	char dev[128];
+	int pos;
+	nextword(cmd);
+	switch (cmd[0]) {
+	case 'f':
+		pos = nextnum();
+		nextword(dev);
+		dev_mnt(pos, dev, dev);
+		break;
+	case 'i':
+		dev_open(devpath);
+		break;
+	case 'T':
+		nextword(dev);
+		sprintf(devpath, "%s/font/dev%s", TROFFROOT, dev);
+		break;
+	case 's':
+		break;
+	case 'X':
+		break;
+	}
+	nexteol();
+}
+
+static void postcmd(int c)
+{
+	char cs[GNLEN];
+	if (isdigit(c)) {
+		outrel((c - '0') * 10 + next() - '0', 0);
+		nextutf8(cs);
+		outc(cs);
+		return;
+	}
+	switch (c) {
+	case 's':
+		outsize(nextnum());
+		break;
+	case 'f':
+		outfont(nextnum());
+		break;
+	case 'H':
+		outh(nextnum());
+		break;
+	case 'V':
+		outv(nextnum());
+		break;
+	case 'h':
+		outrel(nextnum(), 0);
+		break;
+	case 'v':
+		outrel(0, nextnum());
+		break;
+	case 'c':
+		nextutf8(cs);
+		outc(cs);
+		break;
+	case 'N':
+		nextnum();
+		break;
+	case 'C':
+		nextword(cs);
+		outc(cs);
+		break;
+	case 'p':
+		if (o_pg)
+			ps_pageend(o_pg);
+		o_pg = nextnum();
+		ps_pagebeg(o_pg);
+		outpage();
+		break;
+	case 'w':
+		break;
+	case 'n':
+		nextnum();
+		nextnum();
+		break;
+	case 'D':
+		draw();
+		break;
+	case 'x':
+		postx();
+		break;
+	case '#':
+		nexteol();
+		break;
+	default:
+		fprintf(stderr, "unknown command: %c\n", c);
+	}
+}
+
+static void post(void)
+{
+	int c;
+	while ((c = next()) >= 0)
+		if (!isspace(c))
+			postcmd(c);
+	if (o_pg)
+		ps_pageend(o_pg);
+}
+
+int main(void)
+{
+	ps_header();
+	post();
+	ps_trailer(o_pg, o_fonts);
+	return 0;
+}
--- /dev/null
+++ b/post.h
@@ -1,0 +1,71 @@
+/* predefined array limits */
+#define PATHLEN		1024	/* path length */
+#define NFONTS		32	/* number of fonts */
+#define FNLEN		32	/* font name length */
+#define NGLYPHS		512	/* glyphs in fonts */
+#define GNLEN		32	/* glyph name length */
+#define ILNLEN		256	/* line limit of input files */
+#define LNLEN		4000	/* line buffer length (ren.c/out.c) */
+
+#define MIN(a, b)	((a) < (b) ? (a) : (b))
+#define MAX(a, b)	((a) < (b) ? (b) : (a))
+#define LEN(a)		(sizeof(a) / sizeof((a)[0]))
+
+/* device related variables */
+extern int dev_res;
+extern int dev_uwid;
+extern int dev_hor;
+extern int dev_ver;
+
+struct glyph {
+	char name[FNLEN];	/* name of the glyph */
+	char id[FNLEN];		/* device-dependent glyph identifier */
+	struct font *font;	/* glyph font */
+	int wid;		/* character width */
+	int type;		/* character type; ascender/descender */
+};
+
+struct font {
+	char name[FNLEN];
+	char psname[FNLEN];
+	struct glyph glyphs[NGLYPHS];
+	int nglyphs;
+	int spacewid;
+	int special;
+	char c[NGLYPHS][FNLEN];		/* character names in charset */
+	struct glyph *g[NGLYPHS];	/* character glyphs in charset */
+	int n;				/* number of characters in charset */
+};
+
+/* output device functions */
+int dev_open(char *path);
+void dev_close(void);
+int dev_mnt(int pos, char *id, char *name);
+struct font *dev_font(int fn);
+int dev_fontid(struct font *fn, int cur);
+int charwid(int wid, int sz);
+struct glyph *dev_glyph(char *c, int fn);
+struct glyph *dev_glyph_byid(char *id, int fn);
+
+/* font-related functions */
+struct font *font_open(char *path);
+void font_close(struct font *fn);
+struct glyph *font_glyph(struct font *fn, char *id);
+struct glyph *font_find(struct font *fn, char *name);
+
+/* output functions */
+void out(char *s, ...);
+void outc(char *s);
+void outh(int h);
+void outv(int v);
+void outrel(int h, int v);
+void outfont(int f);
+void outsize(int s);
+void outpage(void);
+extern char o_fonts[];
+
+/* postscript functions */
+void ps_header(void);
+void ps_trailer(int pages, char *fonts);
+void ps_pagebeg(int n);
+void ps_pageend(int n);
--- /dev/null
+++ b/ps.c
@@ -1,0 +1,91 @@
+#include "post.h"
+
+void ps_pagebeg(int n)
+{
+	out("%%%%Page: %d %d\n", n, n);
+	out("/saveobj save def\n");
+	out("mark\n");
+	out("%d pagesetup\n", n);
+}
+
+void ps_pageend(int n)
+{
+	out("cleartomark\n");
+	out("showpage\n");
+	out("saveobj restore\n");
+	out("%%%%EndPage: %d %d\n", n, n);
+}
+
+void ps_trailer(int pages, char *fonts)
+{
+	out("%%%%Trailer\n");
+	out("done\n");
+	out("%%%%DocumentFonts: %s\n", fonts);
+	out("%%%%Pages: %d\n", pages);
+}
+
+static char *prolog =
+	"/linewidth .4 def\n"
+	"/resolution 720 def\n"
+	"/pagebbox [0 0 612 792] def\n"
+	"/inch {72 mul} bind def\n"
+	"\n"
+	"/setup {\n"
+	"	counttomark 2 idiv {def} repeat pop\n"
+	"\n"
+	"	/scaling 72 resolution div def\n"
+	"	linewidth setlinewidth\n"
+	"	1 setlinecap\n"
+	"\n"
+	"	pagedimensions\n"
+	"	xcenter ycenter translate\n"
+	"	width 2 div neg height 2 div translate\n"
+	"	scaling scaling scale\n"
+	"\n"
+	"	0 0 moveto\n"
+	"} def\n"
+	"\n"
+	"/pagedimensions {\n"
+	"	pagebbox aload pop\n"
+	"	4 -1 roll exch 4 1 roll 4 copy\n"
+	"	sub /width exch def\n"
+	"	sub /height exch def\n"
+	"	add 2 div /xcenter exch def\n"
+	"	add 2 div /ycenter exch def\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"
+	"/w {neg moveto show} bind def\n"
+	"/m {neg dup /y exch def moveto} bind def\n"
+	"/g {neg moveto {glyphshow} forall} bind def\n"
+	"/done {/lastpage where {pop lastpage} if} def\n"
+	"\n"
+	"/f {\n"
+	"	dup /font exch def findfont exch\n"
+	"	dup /ptsize exch def scaling div dup /size exch def scalefont setfont\n"
+	"	linewidth ptsize mul scaling 10 mul div setlinewidth\n"
+	"} bind def\n";
+
+void ps_header(void)
+{
+	out("%%!PS-Adobe-2.0\n");
+	out("%%%%Version: 1.0\n");
+	out("%%%%Creator: neatroff - http://litcave.rudi.ir/\n");
+	out("%%%%DocumentFonts: (atend)\n");
+	out("%%%%Pages: (atend)\n");
+	out("%%%%EndComments\n");
+
+	out("%%%%BeginProlog\n");
+	out("%s", prolog);
+	out("%%%%EndProlog\n");
+	out("%%%%BeginSetup\n");
+	out("mark\n");
+	out("setup\n");
+	out("%%%%EndSetup\n");
+}