shithub: neatpost

Download patch

ref: cbbd74a9276ca113a3de4edadcc9fd6e83e8c90a
parent: 7d942f01f10c6b44bf5012f73c36a83af35e3b4b
author: Ali Gholami Rudi <ali@rudi.ir>
date: Thu Nov 27 20:43:11 EST 2014

font: do not limit the number of glyphs

--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
 CC = cc
 CFLAGS = -Wall -O2 "-DTROFFFDIR=\"$(FDIR)\""
 LDFLAGS =
-OBJS = post.o out.o ps.o font.o dev.o clr.o
+OBJS = post.o out.o ps.o font.o dev.o clr.o dict.o iset.o
 
 all: post
 %.o: %.c post.h
--- /dev/null
+++ b/dict.c
@@ -1,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+#define DHASH(d, s)	((unsigned char) (s)[0])
+#define CNTMIN		(1 << 10)
+
+struct dict {
+	struct iset *map;
+	char **key;
+	int *val;
+	int size;
+	int n;
+	int notfound;		/* the value returned for missing keys */
+	int dupkeys;		/* duplicate keys if set */
+};
+
+static void dict_extend(struct dict *d, int size)
+{
+	d->key = mextend(d->key, d->size, size, sizeof(d->key[0]));
+	d->val = mextend(d->val, d->size, size, sizeof(d->val[0]));
+	d->size = size;
+}
+
+struct dict *dict_make(int notfound, int dupkeys)
+{
+	struct dict *d = xmalloc(sizeof(*d));
+	memset(d, 0, sizeof(*d));
+	d->n = 1;
+	d->dupkeys = dupkeys;
+	d->notfound = notfound;
+	d->map = iset_make();
+	dict_extend(d, CNTMIN);
+	return d;
+}
+
+void dict_free(struct dict *d)
+{
+	int i;
+	if (d->dupkeys)
+		for (i = 0; i < d->size; i++)
+			free(d->key[i]);
+	free(d->val);
+	free(d->key);
+	iset_free(d->map);
+	free(d);
+}
+
+void dict_put(struct dict *d, char *key, int val)
+{
+	int idx;
+	if (d->n >= d->size)
+		dict_extend(d, d->n + CNTMIN);
+	if (d->dupkeys) {
+		int len = strlen(key) + 1;
+		char *dup = xmalloc(len);
+		memcpy(dup, key, len);
+		key = dup;
+	}
+	idx = d->n++;
+	d->key[idx] = key;
+	d->val[idx] = val;
+	iset_put(d->map, DHASH(d, key), idx);
+}
+
+/* return the index of key in d */
+int dict_idx(struct dict *d, char *key)
+{
+	int *r = iset_get(d->map, DHASH(d, key));
+	while (r && *r >= 0) {
+		if (!strcmp(d->key[*r], key))
+			return *r;
+		r++;
+	}
+	return -1;
+}
+
+char *dict_key(struct dict *d, int idx)
+{
+	return d->key[idx];
+}
+
+int dict_val(struct dict *d, int idx)
+{
+	return d->val[idx];
+}
+
+int dict_get(struct dict *d, char *key)
+{
+	int idx = dict_idx(d, key);
+	return idx >= 0 ? d->val[idx] : d->notfound;
+}
+
+/* match a prefix of key; in the first call, *idx should be -1 */
+int dict_prefix(struct dict *d, char *key, int *pos)
+{
+	int *r = iset_get(d->map, DHASH(d, key));
+	while (r && r[++*pos] >= 0) {
+		int idx = r[*pos];
+		int plen = strlen(d->key[idx]);
+		if (!strncmp(d->key[idx], key, plen))
+			return d->val[idx];
+	}
+	return d->notfound;
+}
--- a/font.c
+++ b/font.c
@@ -1,3 +1,4 @@
+/* font handling */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -7,59 +8,43 @@
 	char name[FNLEN];
 	char fontname[FNLEN];
 	int spacewid;
-	struct glyph gl[NGLYPHS];	/* font glyphs */
-	int gl_n;			/* number of glyphs in gl[] */
-	/* charset mapping; ch[i] is mapped to glyph ch_g[i] */
-	char ch[NGLYPHS][GNLEN];
-	int ch_g[NGLYPHS];
-	int ch_n;			/* number of characters in ch[] */
-	/* glyph table; lists per glyph identifier starting character */
-	int ghead[256];			/* glyph list head */
-	int gnext[NGLYPHS];		/* next item in glyph list */
-	/* charset table; lists per mapping starting character */
-	int chead[256];			/* charset list head */
-	int cnext[NGLYPHS];		/* next item in glyph list */
+	struct glyph *gl;		/* glyphs present in the font */
+	int gl_n, gl_sz;		/* number of glyphs in the font */
+	struct dict *gl_dict;		/* mapping from gl[i].id to i */
+	struct dict *ch_dict;		/* charset mapping */
 };
 
+/* find a glyph by its name */
 struct glyph *font_find(struct font *fn, char *name)
 {
-	int i = fn->chead[(unsigned char) name[0]];
-	while (i >= 0) {
-		if (!strcmp(name, fn->ch[i]))
-			return fn->gl + fn->ch_g[i];
-		i = fn->cnext[i];
-	}
-	return NULL;
+	int i = dict_get(fn->ch_dict, name);
+	return i >= 0 ? fn->gl + i : NULL;
 }
 
+/* find a glyph by its device-dependent identifier */
 struct glyph *font_glyph(struct font *fn, char *id)
 {
-	int i = fn->ghead[(unsigned char) id[0]];
-	while (i >= 0) {
-		if (!strcmp(fn->gl[i].id, id))
-			return &fn->gl[i];
-		i = fn->gnext[i];
-	}
-	return NULL;
+	int i = dict_get(fn->gl_dict, id);
+	return i >= 0 ? &fn->gl[i] : NULL;
 }
 
-static struct glyph *font_glyphput(struct font *fn, char *id,
-				char *name, int wid, int type)
+static int font_glyphput(struct font *fn, char *id, char *name, int type)
 {
-	int i = fn->gl_n++;
 	struct glyph *g;
-	g = &fn->gl[i];
+	if (fn->gl_n == fn->gl_sz) {
+		fn->gl_sz = fn->gl_sz + 1024;
+		fn->gl = mextend(fn->gl, fn->gl_n, fn->gl_sz, sizeof(fn->gl[0]));
+	}
+	g = &fn->gl[fn->gl_n];
 	strcpy(g->id, id);
 	strcpy(g->name, name);
-	g->wid = wid;
 	g->type = type;
 	g->font = fn;
-	fn->gnext[i] = fn->ghead[(unsigned char) id[0]];
-	fn->ghead[(unsigned char) id[0]] = i;
-	return g;
+	dict_put(fn->gl_dict, g->id, fn->gl_n);
+	return fn->gl_n++;
 }
 
-static void tilloel(FILE *fin, char *s)
+static void tilleol(FILE *fin, char *s)
 {
 	int c = fgetc(fin);
 	while (c != EOF && c != '\n') {
@@ -71,38 +56,32 @@
 		ungetc(c, fin);
 }
 
-static int font_readchar(struct font *fn, FILE *fin)
+static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid)
 {
+	struct glyph *g;
 	char tok[ILNLEN];
 	char name[ILNLEN];
 	char id[ILNLEN];
-	struct glyph *glyph = NULL;
-	int wid, type;
-	if (fn->ch_n >= NGLYPHS)
-		return 1;
+	int type;
 	if (fscanf(fin, "%s %s", name, tok) != 2)
 		return 1;
 	if (!strcmp("---", name))
-		sprintf(name, "c%04d", fn->ch_n);
+		sprintf(name, "c%04d", *n);
 	if (strcmp("\"", tok)) {
-		wid = atoi(tok);
 		if (fscanf(fin, "%d %s", &type, id) != 2)
 			return 1;
-		glyph = font_glyph(fn, id);
-		if (!glyph) {
-			glyph = font_glyphput(fn, id, name, wid, type);
-			tilloel(fin, tok);
-			if (sscanf(tok, "%d", &glyph->pos) < 1)
-				glyph->pos = 0;
+		*gid = dict_get(fn->gl_dict, id);
+		if (*gid < 0) {
+			*gid = font_glyphput(fn, id, name, type);
+			g = &fn->gl[*gid];
+			sscanf(tok, "%d", &g->wid);
+			tilleol(fin, tok);
+			if (sscanf(tok, "%d", &g->pos) != 1)
+				g->pos = 0;
 		}
-	} else {
-		glyph = fn->gl + fn->ch_g[fn->ch_n - 1];
 	}
-	strcpy(fn->ch[fn->ch_n], name);
-	fn->ch_g[fn->ch_n] = glyph - fn->gl;
-	fn->cnext[fn->ch_n] = fn->chead[(unsigned char) name[0]];
-	fn->chead[(unsigned char) name[0]] = fn->ch_n;
-	fn->ch_n++;
+	dict_put(fn->ch_dict, name, *gid);
+	(*n)++;
 	return 0;
 }
 
@@ -117,25 +96,24 @@
 struct font *font_open(char *path)
 {
 	struct font *fn;
+	int ch_g = -1;		/* last glyph in the charset */
+	int ch_n = 0;			/* number of glyphs in the charset */
 	char tok[ILNLEN];
 	FILE *fin;
-	int i;
 	fin = fopen(path, "r");
 	if (!fin)
 		return NULL;
-	fn = malloc(sizeof(*fn));
+	fn = xmalloc(sizeof(*fn));
 	if (!fn) {
 		fclose(fin);
 		return NULL;
 	}
 	memset(fn, 0, sizeof(*fn));
-	for (i = 0; i < LEN(fn->ghead); i++)
-		fn->ghead[i] = -1;
-	for (i = 0; i < LEN(fn->chead); i++)
-		fn->chead[i] = -1;
+	fn->gl_dict = dict_make(-1, 1);
+	fn->ch_dict = dict_make(-1, 1);
 	while (fscanf(fin, "%s", tok) == 1) {
 		if (!strcmp("char", tok)) {
-			font_readchar(fn, fin);
+			font_readchar(fn, fin, &ch_n, &ch_g);
 		} else if (!strcmp("spacewidth", tok)) {
 			fscanf(fin, "%d", &fn->spacewid);
 		} else if (!strcmp("name", tok)) {
@@ -147,7 +125,7 @@
 				if (!strcmp("0", tok))
 					break;
 		} else if (!strcmp("charset", tok)) {
-			while (!font_readchar(fn, fin))
+			while (!font_readchar(fn, fin, &ch_n, &ch_g))
 				;
 			break;
 		}
@@ -159,14 +137,19 @@
 
 void font_close(struct font *fn)
 {
+	dict_free(fn->gl_dict);
+	dict_free(fn->ch_dict);
+	free(fn->gl);
 	free(fn);
 }
 
+/* return width w for the given font and size */
 int font_wid(struct font *fn, int sz, int w)
 {
 	return (w * sz + dev_uwid / 2) / dev_uwid;
 }
 
+/* space width for the give word space or sentence space */
 int font_swid(struct font *fn, int sz)
 {
 	return font_wid(fn, sz, fn->spacewid);
--- /dev/null
+++ b/iset.c
@@ -1,0 +1,74 @@
+#include <stdlib.h>
+#include <string.h>
+#include "post.h"
+
+#define ALIGN(n, a)	(((n) + (a) - 1) & ~((a) - 1))
+#define CNTMIN		(1 << 10)
+#define CNTMAX		(1 << 20)
+
+/* iset structure to map integers to sets */
+struct iset {
+	int **set;
+	int *sz;
+	int *len;
+	int cnt;
+};
+
+static void iset_extend(struct iset *iset, int cnt)
+{
+	iset->set = mextend(iset->set, iset->cnt, cnt, sizeof(iset->set[0]));
+	iset->sz = mextend(iset->sz, iset->cnt, cnt, sizeof(iset->sz[0]));
+	iset->len = mextend(iset->len, iset->cnt, cnt, sizeof(iset->len[0]));
+	iset->cnt = cnt;
+}
+
+struct iset *iset_make(void)
+{
+	struct iset *iset = xmalloc(sizeof(*iset));
+	memset(iset, 0, sizeof(*iset));
+	iset_extend(iset, CNTMIN);
+	return iset;
+}
+
+void iset_free(struct iset *iset)
+{
+	int i;
+	for (i = 0; i < iset->cnt; i++)
+		free(iset->set[i]);
+	free(iset->set);
+	free(iset->len);
+	free(iset->sz);
+	free(iset);
+}
+
+int *iset_get(struct iset *iset, int key)
+{
+	return key >= 0 && key < iset->cnt ? iset->set[key] : NULL;
+}
+
+int iset_len(struct iset *iset, int key)
+{
+	return key >= 0 && key < iset->cnt ? iset->len[key] : 0;
+}
+
+void iset_put(struct iset *iset, int key, int ent)
+{
+	if (key < 0 || key >= CNTMAX)
+		return;
+	if (key >= iset->cnt)
+		iset_extend(iset, ALIGN(key + 1, CNTMIN));
+	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]));
+		if (iset->set[key]) {
+			memcpy(nset, iset->set[key],
+				olen * sizeof(iset->set[key][0]));
+			free(iset->set[key]);
+		}
+		iset->sz[key] = nlen;
+		iset->set[key] = nset;
+	}
+	iset->set[key][iset->len[key]++] = ent;
+	iset->set[key][iset->len[key]] = -1;
+}
--- a/post.c
+++ b/post.c
@@ -384,6 +384,29 @@
 	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);
+	memcpy(new, old, oldsz * memsz);
+	memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
+	free(old);
+	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"
--- a/post.h
+++ b/post.h
@@ -1,7 +1,6 @@
 /* predefined array limits */
 #define PATHLEN		1024	/* path length */
 #define NFONTS		32	/* number of fonts */
-#define NGLYPHS		1024	/* glyphs in fonts */
 #define FNLEN		64	/* font name length */
 #define GNLEN		32	/* glyph name length */
 #define ILNLEN		1000	/* line limit of input files */
@@ -82,3 +81,24 @@
 
 char *clr_str(int c);
 int clr_get(char *s);
+
+/* mapping integers to sets */
+struct iset *iset_make(void);
+void iset_free(struct iset *iset);
+int *iset_get(struct iset *iset, int key);
+void iset_put(struct iset *iset, int key, int ent);
+int iset_len(struct iset *iset, int key);
+
+/* mapping strings to longs */
+struct dict *dict_make(int notfound, int dupkeys);
+void dict_free(struct dict *d);
+void dict_put(struct dict *d, char *key, int val);
+int dict_get(struct dict *d, char *key);
+int dict_idx(struct dict *d, char *key);
+char *dict_key(struct dict *d, int idx);
+int dict_val(struct dict *d, int idx);
+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);