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");
+}