ref: fa29204bd5df54b40e2f0f81cca94e55dcfb801c
parent: 20d081c279e125781a24e2ddc97662d10915ad60
author: Ali Gholami Rudi <ali@rudi.ir>
date: Tue Mar 27 13:57:54 EDT 2018
pdf: a basic pdf post-processor
--- a/Makefile
+++ b/Makefile
@@ -4,12 +4,15 @@
CC = cc
CFLAGS = -Wall -O2 "-DTROFFFDIR=\"$(FDIR)\""
LDFLAGS =
-OBJS = post.o out.o ps.o font.o dev.o clr.o dict.o iset.o
+OBJS = post.o ps.o font.o dev.o clr.o dict.o iset.o
+OBJSPDF = post.o pdf.o font.o dev.o clr.o dict.o iset.o sbuf.o
-all: post
+all: post pdf
%.o: %.c post.h
$(CC) -c $(CFLAGS) $<
post: $(OBJS)
$(CC) -o $@ $(OBJS) $(LDFLAGS)
+pdf: $(OBJSPDF)
+ $(CC) -o $@ $(OBJSPDF) $(LDFLAGS)
clean:
- rm -f *.o post
+ rm -f *.o post pdf
--- a/dict.c
+++ b/dict.c
@@ -25,7 +25,7 @@
struct dict *dict_make(int notfound, int dupkeys)
{
- struct dict *d = xmalloc(sizeof(*d));
+ struct dict *d = malloc(sizeof(*d));
memset(d, 0, sizeof(*d));
d->n = 1;
d->dupkeys = dupkeys;
@@ -54,7 +54,7 @@
dict_extend(d, d->n + CNTMIN);
if (d->dupkeys) {
int len = strlen(key) + 1;
- char *dup = xmalloc(len);
+ char *dup = malloc(len);
memcpy(dup, key, len);
key = dup;
}
--- a/font.c
+++ b/font.c
@@ -7,6 +7,7 @@
struct font {
char name[FNLEN];
char fontname[FNLEN];
+ char fontpath[1024];
int spacewid;
struct glyph *gl; /* glyphs present in the font */
int gl_n, gl_sz; /* number of glyphs in the font */
@@ -108,7 +109,7 @@
fin = fopen(path, "r");
if (!fin)
return NULL;
- fn = xmalloc(sizeof(*fn));
+ fn = malloc(sizeof(*fn));
if (!fn) {
fclose(fin);
return NULL;
@@ -126,6 +127,12 @@
fscanf(fin, "%s", fn->name);
} else if (!strcmp("fontname", tok)) {
fscanf(fin, "%s", fn->fontname);
+ } else if (!strcmp("fontpath", tok)) {
+ int c = fgetc(fin);
+ while (c == ' ')
+ c = fgetc(fin);
+ ungetc(c, fin);
+ tilleol(fin, fn->fontpath);
} else if (!strcmp("ligatures", tok)) {
while (fscanf(fin, "%s", tok) == 1)
if (!strcmp("0", tok))
@@ -165,4 +172,19 @@
char *font_name(struct font *fn)
{
return fn->fontname;
+}
+
+char *font_path(struct font *fn)
+{
+ return fn->fontpath;
+}
+
+int font_glnum(struct font *fn, struct glyph *g)
+{
+ return g - fn->gl;
+}
+
+struct glyph *font_glget(struct font *fn, int id)
+{
+ return id >= 0 && id < fn->gl_n ? &fn->gl[id] : NULL;
}
--- a/iset.c
+++ b/iset.c
@@ -24,7 +24,7 @@
struct iset *iset_make(void)
{
- struct iset *iset = xmalloc(sizeof(*iset));
+ struct iset *iset = malloc(sizeof(*iset));
memset(iset, 0, sizeof(*iset));
iset_extend(iset, CNTMIN);
return iset;
@@ -60,7 +60,7 @@
if (key >= 0 && key < iset->cnt && iset->len[key] + 1 >= iset->sz[key]) {
int olen = iset->sz[key];
int nlen = iset->sz[key] * 2 + 8;
- void *nset = xmalloc(nlen * sizeof(iset->set[key][0]));
+ void *nset = malloc(nlen * sizeof(iset->set[key][0]));
if (iset->set[key]) {
memcpy(nset, iset->set[key],
olen * sizeof(iset->set[key][0]));
--- a/out.c
+++ /dev/null
@@ -1,354 +1,0 @@
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "post.h"
-
-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 */
-
-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);
-}
-
-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;
-}
-
-void outeps(char *spec)
-{
- char eps[1 << 12];
- char buf[1 << 12];
- int llx, lly, urx, ury;
- int hwid, vwid;
- FILE *filp;
- int nspec, nbb;
- spec = strcut(eps, spec);
- if (!eps[0])
- return;
- nspec = sscanf(spec, "%d %d", &hwid, &vwid);
- if (nspec < 1)
- hwid = 0;
- if (nspec < 2)
- vwid = 0;
- if (!(filp = fopen(eps, "r")))
- return;
- if (!fgets(buf, sizeof(buf), filp) ||
- (strcmp(buf, "%!PS-Adobe-2.0 EPSF-1.2\n") &&
- strcmp(buf, "%!PS-Adobe-2.0 EPSF-2.0\n") &&
- strcmp(buf, "%!PS-Adobe-3.0 EPSF-3.0\n"))) {
- 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 outlink(char *spec)
-{
- char lnk[1 << 12];
- int hwid, vwid;
- int nspec;
- spec = strcut(lnk, spec);
- if (!lnk[0] || (nspec = sscanf(spec, "%d %d", &hwid, &vwid)) != 2)
- return;
- 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, lnk);
- }
-}
--- /dev/null
+++ b/pdf.c
@@ -1,0 +1,489 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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 char **font_ps; /* document font names */
+static int *font_id; /* font object */
+static int *font_ct; /* font content stream object */
+static int *font_ix; /* font index */
+static int font_sz, font_n; /* number of fonts */
+
+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 */
+
+/* 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");
+}
+
+/* embed font; return stream object identifier */
+static int font_outdat(char *path, char *name, int ix)
+{
+ struct sbuf *sb;
+ FILE *fp;
+ int c, i, id;
+ for (i = 0; i < font_n; i++)
+ if (!strcmp(name, font_ps[i]) && font_ct[i] >= 0)
+ return font_ct[i];
+ fp = fopen(path, "r");
+ if (!fp)
+ return -1;
+ sb = sbuf_make();
+ c = fgetc(fp);
+ for (i = 0; c != EOF; i++) {
+ sbuf_printf(sb, "%02x", c);
+ c = fgetc(fp);
+ if (i % 40 == 39 && c != EOF)
+ sbuf_chr(sb, '\n');
+ }
+ sbuf_str(sb, ">\n");
+ fclose(fp);
+ id = obj_beg(0);
+ pdfout("<<\n");
+ pdfout(" /Filter /ASCIIHexDecode\n");
+ pdfout(" /Length %d\n", sbuf_len(sb));
+ pdfout(" /Length1 %d\n", i);
+ pdfout(">>\n");
+ pdfout("stream\n");
+ pdfout("%s", sbuf_buf(sb));
+ pdfout("endstream\n");
+ obj_end();
+ sbuf_free(sb);
+ return id;
+}
+
+/* write the object corresponding to font font_id[f] */
+static void font_out(struct font *fn, int f)
+{
+ int i;
+ int enc_obj, des_obj;
+ char *path = font_path(fn);
+ char *ext = path ? strrchr(path, '.') : NULL;
+ /* the encoding object */
+ enc_obj = obj_beg(0);
+ pdfout("<<\n");
+ pdfout(" /Type /Encoding\n");
+ pdfout(" /Differences [ 0");
+ for (i = 0; i < 256; i++) {
+ struct glyph *g = font_glget(fn, font_ix[f] * 256 + i);
+ pdfout(" /%s", g ? g->id : ".notdef");
+ }
+ pdfout(" ]\n");
+ pdfout(">>\n");
+ obj_end();
+ /* embedding the font */
+ if (ext && !strcmp(".ttf", ext))
+ font_ct[f] = font_outdat(path, font_ps[f], font_ix[f]);
+ /* the font descriptor */
+ des_obj = obj_beg(0);
+ pdfout("<<\n");
+ pdfout(" /Type /FontDescriptor\n");
+ pdfout(" /FontName /%s\n", font_ps[f]);
+ pdfout(" /Flags 0\n");
+ pdfout(" /MissingWidth 255\n");
+ pdfout(" /StemV 100\n");
+ pdfout(" /StemH 100\n");
+ pdfout(" /CapHeight 100\n");
+ pdfout(" /Ascent 100\n");
+ pdfout(" /Descent 100\n");
+ if (font_ct[f] >= 0)
+ pdfout(" /FontFile2 %d 0 R\n", font_ct[f]);
+ pdfout(">>\n");
+ obj_end();
+ /* the font object */
+ obj_beg(font_id[f]);
+ pdfout("<<\n");
+ pdfout(" /Type /Font\n");
+ pdfout(" /Subtype /%s\n",
+ ext && !strcmp(".ttf", ext) ? "TrueType" : "Type1");
+ pdfout(" /BaseFont /%s\n", font_ps[f]);
+ pdfout(" /FirstChar 0\n");
+ pdfout(" /LastChar 255\n");
+ pdfout(" /Widths [");
+ for (i = 0; i < 256; i++) {
+ struct glyph *g = font_glget(fn, font_ix[f] * 256 + i);
+ pdfout(" %d", (g ? g->wid : 0) * dev_res / 72);
+ }
+ pdfout(" ]\n");
+ pdfout(" /FontDescriptor %d 0 R\n", des_obj);
+ pdfout(" /Encoding %d 0 R\n", enc_obj);
+ pdfout(">>\n");
+ obj_end();
+}
+
+static int font_put(struct font *fn, int ix)
+{
+ int i;
+ char *name = font_name(fn);
+ for (i = 0; i < font_n; i++)
+ if (!strcmp(font_ps[i], font_name(fn)) && font_ix[i] == ix)
+ return i;
+ if (font_n == font_sz) {
+ font_sz += 128;
+ font_id = mextend(font_id, font_n, font_sz, sizeof(font_id[0]));
+ font_ps = mextend(font_ps, font_n, font_sz, sizeof(font_ps[0]));
+ font_ix = mextend(font_ix, font_n, font_sz, sizeof(font_ix[0]));
+ font_ct = mextend(font_ct, font_n, font_sz, sizeof(font_ct[0]));
+ }
+ font_id[font_n] = obj_map();
+ font_ix[font_n] = ix;
+ font_ps[font_n] = malloc(strlen(name) + 1);
+ font_ct[font_n] = -1;
+ strcpy(font_ps[font_n], name);
+ font_n++;
+ font_out(fn, font_n - 1);
+ return font_n - 1;
+}
+
+void out(char *s, ...)
+{
+}
+
+static void o_flush(void)
+{
+ if (o_queued == 1)
+ sbuf_printf(pg, ") Tj\n");
+ o_queued = 0;
+}
+
+static int o_loadfont(struct glyph *g)
+{
+ struct font *fn = g ? g->font : dev_font(o_f);
+ int ix = font_glnum(fn, g) / 256;
+ char *name = font_name(fn);
+ int i;
+ int id;
+ for (i = 0; i < o_fsn; i++)
+ if (!strcmp(name, font_ps[o_fs[i]]) && font_ix[o_fs[i]] == ix)
+ return i;
+ id = font_put(fn, ix);
+ 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++] = id;
+ return o_fsn - 1;
+}
+
+#define PREC 1000
+#define PRECN "3"
+
+static void o_queue(struct glyph *g)
+{
+ int pos;
+ if (o_h != p_h || o_v != p_v) {
+ long h = o_h * PREC * 72 / dev_res;
+ long v = pdf_height * PREC - (o_v * PREC * 72 / dev_res);
+ o_flush();
+ sbuf_printf(pg, "1 0 0 1 %d.%0" PRECN "d %d.%0" PRECN "d Tm\n",
+ h / PREC, h % PREC, v / PREC, v % PREC);
+ p_h = o_h;
+ p_v = o_v;
+ }
+ if (!o_queued)
+ sbuf_printf(pg, "(");
+ o_queued = 1;
+ pos = font_glnum(g->font, g) % 256;
+ sbuf_printf(pg, "\\%d%d%d", (pos >> 6) & 7, (pos >> 3) & 7, pos & 7);
+ p_h += font_wid(g->font, o_s, g->wid);
+}
+
+static void out_fontup(void)
+{
+ if (o_m != p_m) {
+ o_flush();
+ p_m = o_m;
+ }
+ if (o_pf != p_pf || o_s != p_s) {
+ int f = o_fs[o_pf];
+ o_flush();
+ sbuf_printf(pg, "/%s.%d %d Tf\n", font_ps[f], font_ix[f], 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_v = 0;
+ p_h = 0;
+ p_s = 0;
+ p_f = 0;
+ p_m = 0;
+}
+
+void outmnt(int f)
+{
+ if (p_f == f)
+ p_f = -1;
+}
+
+void outgname(int g)
+{
+}
+
+void drawbeg(void)
+{
+}
+
+void drawend(int close, int fill)
+{
+}
+
+void drawmbeg(char *s)
+{
+}
+
+void drawmend(char *s)
+{
+}
+
+void drawl(int h, int v)
+{
+}
+
+void drawc(int c)
+{
+}
+
+void drawe(int h, int v)
+{
+}
+
+void drawa(int h1, int v1, int h2, int v2)
+{
+}
+
+void draws(int h1, int v1, int h2, int v2)
+{
+}
+
+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();
+ /* 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);
+ for (i = 0; i < font_n; i++)
+ free(font_ps[i]);
+ free(font_ps);
+ free(font_ct);
+ free(font_id);
+ free(font_ix);
+}
+
+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++)
+ pdfout(" /%s.%d %d 0 R",
+ font_ps[o_fs[i]], font_ix[o_fs[i]], font_id[o_fs[i]]);
+ 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;
+}
--- a/post.c
+++ b/post.c
@@ -1,7 +1,7 @@
/*
- * NEATPOST: NEATROFF'S POSTSCRIPT POSTPROCESSOR
+ * NEATPOST: NEATROFF'S POSTSCRIPT/PDF POSTPROCESSOR
*
- * Copyright (C) 2013-2017 Ali Gholami Rudi <ali at rudi dot ir>
+ * Copyright (C) 2013-2018 Ali Gholami Rudi <ali at rudi dot ir>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -433,15 +433,9 @@
ps_pageheight -= ps_pageheight % 10;
}
-static void errdie(char *msg)
-{
- fprintf(stderr, msg);
- exit(1);
-}
-
void *mextend(void *old, long oldsz, long newsz, int memsz)
{
- void *new = xmalloc(newsz * memsz);
+ void *new = malloc(newsz * memsz);
memcpy(new, old, oldsz * memsz);
memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
free(old);
@@ -448,14 +442,6 @@
return new;
}
-void *xmalloc(long len)
-{
- void *m = malloc(len);
- if (!m)
- errdie("neatroff: malloc() failed\n");
- return m;
-}
-
static char *usage =
"Usage: neatpost [options] <input >output\n"
"Options:\n"
@@ -485,7 +471,7 @@
}
}
post();
- ps_trailer(o_pages, o_fonts);
+ ps_trailer(o_pages);
dev_close();
return 0;
}
--- a/post.h
+++ b/post.h
@@ -1,6 +1,6 @@
/* predefined array limits */
#define PATHLEN 1024 /* path length */
-#define NFONTS 32 /* number of fonts */
+#define NFONTS 1024 /* number of fonts */
#define FNLEN 64 /* font name length */
#define GNLEN 32 /* glyph name length */
#define ILNLEN 1000 /* line limit of input files */
@@ -41,6 +41,9 @@
int font_wid(struct font *fn, int sz, int w);
int font_swid(struct font *fn, int sz);
char *font_name(struct font *fn);
+char *font_path(struct font *fn);
+int font_glnum(struct font *fn, struct glyph *g);
+struct glyph *font_glget(struct font *fn, int id);
/* output functions */
void out(char *s, ...);
@@ -57,7 +60,6 @@
void outpage(void);
void outmnt(int f);
void outgname(int g);
-extern char o_fonts[];
void drawbeg(void);
void drawend(int close, int fill);
@@ -71,7 +73,7 @@
/* postscript functions */
void ps_header(char *title, int pagewidth, int pageheight, int linewidth);
-void ps_trailer(int pages, char *fonts);
+void ps_trailer(int pages);
void ps_pagebeg(int n);
void ps_pageend(int n);
@@ -102,5 +104,14 @@
int dict_prefix(struct dict *d, char *key, int *idx);
/* memory allocation */
-void *xmalloc(long len);
void *mextend(void *old, long oldsz, long newsz, int memsz);
+
+/* string buffers */
+struct sbuf *sbuf_make(void);
+char *sbuf_buf(struct sbuf *sb);
+char *sbuf_done(struct sbuf *sb);
+void sbuf_free(struct sbuf *sb);
+int sbuf_len(struct sbuf *sbuf);
+void sbuf_str(struct sbuf *sbuf, char *s);
+void sbuf_printf(struct sbuf *sbuf, char *s, ...);
+void sbuf_chr(struct sbuf *sbuf, int c);
--- a/ps.c
+++ b/ps.c
@@ -1,5 +1,358 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "post.h"
+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);
+}
+
+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;
+}
+
+void outeps(char *spec)
+{
+ char eps[1 << 12];
+ char buf[1 << 12];
+ int llx, lly, urx, ury;
+ int hwid, vwid;
+ FILE *filp;
+ int nspec, nbb;
+ spec = strcut(eps, spec);
+ if (!eps[0])
+ return;
+ nspec = sscanf(spec, "%d %d", &hwid, &vwid);
+ if (nspec < 1)
+ hwid = 0;
+ if (nspec < 2)
+ vwid = 0;
+ if (!(filp = fopen(eps, "r")))
+ return;
+ if (!fgets(buf, sizeof(buf), filp) ||
+ (strcmp(buf, "%!PS-Adobe-2.0 EPSF-1.2\n") &&
+ strcmp(buf, "%!PS-Adobe-2.0 EPSF-2.0\n") &&
+ strcmp(buf, "%!PS-Adobe-3.0 EPSF-3.0\n"))) {
+ 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 outlink(char *spec)
+{
+ char lnk[1 << 12];
+ int hwid, vwid;
+ int nspec;
+ spec = strcut(lnk, spec);
+ if (!lnk[0] || (nspec = sscanf(spec, "%d %d", &hwid, &vwid)) != 2)
+ return;
+ 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, lnk);
+ }
+}
+
void ps_pagebeg(int n)
{
out("%%%%Page: %d %d\n", n, n);
@@ -15,11 +368,11 @@
out("saveobj restore\n");
}
-void ps_trailer(int pages, char *fonts)
+void ps_trailer(int pages)
{
out("%%%%Trailer\n");
out("done\n");
- out("%%%%DocumentFonts: %s\n", fonts);
+ out("%%%%DocumentFonts: %s\n", o_fonts);
out("%%%%Pages: %d\n", pages);
out("%%%%EOF\n");
}
--- /dev/null
+++ b/sbuf.c
@@ -1,0 +1,95 @@
+/* variable length string buffer */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+#define SBUFSZ 128
+#define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1))
+#define NEXTSZ(o, r) ALIGN(MAX((o) * 2, (o) + (r)), SBUFSZ)
+
+struct sbuf {
+ char *s; /* allocated buffer */
+ int s_n; /* length of the string stored in s[] */
+ int s_sz; /* size of memory allocated for s[] */
+};
+
+static void sbuf_extend(struct sbuf *sbuf, int newsz)
+{
+ char *s = sbuf->s;
+ sbuf->s_sz = newsz;
+ sbuf->s = malloc(sbuf->s_sz);
+ if (sbuf->s_n)
+ memcpy(sbuf->s, s, sbuf->s_n);
+ free(s);
+}
+
+struct sbuf *sbuf_make(void)
+{
+ struct sbuf *sb = malloc(sizeof(*sb));
+ memset(sb, 0, sizeof(*sb));
+ return sb;
+}
+
+char *sbuf_buf(struct sbuf *sb)
+{
+ if (!sb->s)
+ sbuf_extend(sb, 1);
+ sb->s[sb->s_n] = '\0';
+ return sb->s;
+}
+
+char *sbuf_done(struct sbuf *sb)
+{
+ char *s = sbuf_buf(sb);
+ free(sb);
+ return s;
+}
+
+void sbuf_free(struct sbuf *sb)
+{
+ free(sb->s);
+ free(sb);
+}
+
+void sbuf_chr(struct sbuf *sbuf, int c)
+{
+ if (sbuf->s_n + 2 >= sbuf->s_sz)
+ sbuf_extend(sbuf, NEXTSZ(sbuf->s_sz, 1));
+ sbuf->s[sbuf->s_n++] = c;
+}
+
+void sbuf_mem(struct sbuf *sbuf, char *s, int len)
+{
+ if (sbuf->s_n + len + 1 >= sbuf->s_sz)
+ sbuf_extend(sbuf, NEXTSZ(sbuf->s_sz, len + 1));
+ memcpy(sbuf->s + sbuf->s_n, s, len);
+ sbuf->s_n += len;
+}
+
+void sbuf_str(struct sbuf *sbuf, char *s)
+{
+ sbuf_mem(sbuf, s, strlen(s));
+}
+
+int sbuf_len(struct sbuf *sbuf)
+{
+ return sbuf->s_n;
+}
+
+void sbuf_cut(struct sbuf *sb, int len)
+{
+ if (sb->s_n > len)
+ sb->s_n = len;
+}
+
+void sbuf_printf(struct sbuf *sbuf, char *s, ...)
+{
+ char buf[256];
+ va_list ap;
+ va_start(ap, s);
+ vsnprintf(buf, sizeof(buf), s, ap);
+ va_end(ap);
+ sbuf_str(sbuf, buf);
+}