shithub: neatroff

Download patch

ref: 3b1dedaa9cfe9e5b78166054f829e4c36bb8e56e
parent: e6a27240e5bcb4d04ca08426f311c6fd7758989d
author: Ali Gholami Rudi <ali@rudi.ir>
date: Sun Jun 23 12:45:35 EDT 2013

wb: pairwise kerning support

The ".kn 1" enables pairwise kerning.  Now the kernpairs section of
font description file describes kerning pairs as "c1 c2 value", as
groff does.

--- a/dev.c
+++ b/dev.c
@@ -154,7 +154,8 @@
 	return fn_font[n_f]->spacewid;
 }
 
-int dev_font(char *id)
+/* return the mounted position of font */
+int dev_pos(char *id)
 {
 	int i;
 	if (isdigit(id[0])) {
@@ -173,19 +174,14 @@
 	return dev_mnt(0, id, id);
 }
 
-int charwid(int wid, int sz)
+/* return the font struct at pos */
+struct font *dev_font(int pos)
 {
-	/* the original troff rounds the widths up */
-	return (wid * sz + dev_uwid / 2) / dev_uwid;
+	return pos >= 0 && pos < fn_n ? fn_font[pos] : NULL;
 }
 
-/* return 1 if lig is a ligature in the font mounted at f */
-int dev_lig(int f, char *lig)
+int charwid(int wid, int sz)
 {
-	struct font *fn = fn_font[f];
-	int i;
-	for (i = 0; i < fn->nlig; i++)
-		if (!strcmp(lig, fn->lig[i]))
-			return font_find(fn, lig) != NULL;
-	return 0;
+	/* the original troff rounds the widths up */
+	return (wid * sz + dev_uwid / 2) / dev_uwid;
 }
--- a/font.c
+++ b/font.c
@@ -29,6 +29,8 @@
 	return NULL;
 }
 
+static int font_section(struct font *fn, FILE *fin, char *name);
+
 static void font_charset(struct font *fn, FILE *fin)
 {
 	char tok[ILNLEN];
@@ -40,6 +42,8 @@
 	while (fn->n < NGLYPHS) {
 		if (fscanf(fin, "%s", name) != 1)
 			break;
+		if (!font_section(fn, fin, name))
+			break;
 		fscanf(fin, "%s", tok);
 		glyph = prev;
 		if (strcmp("\"", tok)) {
@@ -60,6 +64,57 @@
 	}
 }
 
+static void font_kernpairs(struct font *fn, FILE *fin)
+{
+	char c1[ILNLEN], c2[ILNLEN];
+	int val;
+	while (fn->n < NGLYPHS) {
+		if (fscanf(fin, "%s", c1) != 1)
+			break;
+		if (!font_section(fn, fin, c1))
+			break;
+		if (fscanf(fin, "%s %d", c2, &val) != 2)
+			break;
+		strcpy(fn->kern_c1[fn->nkern], c1);
+		strcpy(fn->kern_c2[fn->nkern], c2);
+		fn->kern[fn->nkern] = val;
+		fn->nkern++;
+	}
+}
+
+static int font_section(struct font *fn, FILE *fin, char *name)
+{
+	if (!strcmp("charset", name)) {
+		font_charset(fn, fin);
+		return 0;
+	}
+	if (!strcmp("kernpairs", name)) {
+		font_kernpairs(fn, fin);
+		return 0;
+	}
+	return 1;
+}
+
+/* return 1 if lig is a ligature */
+int font_lig(struct font *fn, char *lig)
+{
+	int i;
+	for (i = 0; i < fn->nlig; i++)
+		if (!strcmp(lig, fn->lig[i]))
+			return font_find(fn, lig) != NULL;
+	return 0;
+}
+
+/* return pairwise kerning value between c1 and c2 */
+int font_kern(struct font *fn, char *c1, char *c2)
+{
+	int i;
+	for (i = 0; i < fn->nkern; i++)
+		if (!strcmp(fn->kern_c1[i], c1) && !strcmp(fn->kern_c2[i], c2))
+			return fn->kern[i];
+	return 0;
+}
+
 struct font *font_open(char *path)
 {
 	struct font *fn = malloc(sizeof(*fn));
@@ -102,10 +157,8 @@
 			skipline(fin);
 			continue;
 		}
-		if (!strcmp("charset", tok)) {
-			font_charset(fn, fin);
+		if (!font_section(fn, fin, tok))
 			break;
-		}
 	}
 	fclose(fin);
 	return fn;
--- a/out.c
+++ b/out.c
@@ -215,7 +215,7 @@
 			out_draw(c);
 			break;
 		case 'f':
-			out_ft(dev_font(c));
+			out_ft(dev_pos(c));
 			break;
 		case 'h':
 			outnn("h%d", eval(c, 'm'));
--- a/ren.c
+++ b/ren.c
@@ -433,7 +433,7 @@
 
 static void ren_ft(char *s)
 {
-	int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_font(s);
+	int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
 	if (fn >= 0) {
 		n_f0 = n_f;
 		n_f = fn;
@@ -670,8 +670,11 @@
 	}
 	if (c[0] == c_ni)
 		nextchar(c + 1, next);
-	if (!n_lg || wb_lig(wb, c))
+	if (!n_lg || wb_lig(wb, c)) {
+		if (n_kn)
+			wb_kern(wb, c);
 		wb_put(wb, c);
+	}
 }
 
 /* read the argument of \w and push its width */
--- a/roff.c
+++ b/roff.c
@@ -15,6 +15,7 @@
 	n_o = SC_IN;
 	n_p = SC_IN * 11;
 	n_lg = 1;
+	n_kn = 0;
 }
 
 static void compile(void)
--- a/roff.h
+++ b/roff.h
@@ -10,6 +10,7 @@
 #define NFILES		16	/* number of input files */
 #define NFONTS		32	/* number of fonts */
 #define NLIGS		32	/* number of font ligatures */
+#define NKERNS		128	/* number of font pairwise kerning pairs */
 #define FNLEN		32	/* font name length */
 #define NGLYPHS		512	/* glyphs in fonts */
 #define GNLEN		32	/* glyph name length */
@@ -94,7 +95,11 @@
 	struct glyph *g[NGLYPHS];	/* character glyphs in charset */
 	int n;				/* number of characters in charset */
 	char lig[NLIGS][GNLEN * 4];	/* font ligatures */
-	int nlig;			/* number of font ligatures */
+	int nlig;
+	int kern[NKERNS];		/* font pairwise kerning */
+	char kern_c1[NKERNS][GNLEN];
+	char kern_c2[NKERNS][GNLEN];
+	int nkern;
 };
 
 /* output device functions */
@@ -101,9 +106,9 @@
 int dev_open(char *path);
 void dev_close(void);
 int dev_mnt(int pos, char *id, char *name);
-int dev_font(char *id);
+int dev_pos(char *id);
+struct font *dev_font(int pos);
 int charwid(int wid, int sz);
-int dev_lig(int f, char *c);
 
 /* font-related functions */
 struct font *font_open(char *path);
@@ -110,6 +115,8 @@
 void font_close(struct font *fn);
 struct glyph *font_glyph(struct font *fn, char *id);
 struct glyph *font_find(struct font *fn, char *name);
+int font_lig(struct font *fn, char *c);
+int font_kern(struct font *fn, char *c1, char *c2);
 
 /* glyph handling functions */
 struct glyph *dev_glyph(char *c, int fn);
@@ -191,6 +198,7 @@
 int wb_empty(struct wb *wb);
 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb);
 int wb_lig(struct wb *wb, char *c);
+int wb_kern(struct wb *wb, char *c);
 
 /* hyphenation flags */
 #define HY_MASK		0x0f	/* enable hyphenation */
@@ -314,6 +322,7 @@
 #define n_lg		(*nreg(REG(0, 'g')))	/* .lg mode */
 #define n_hy		(*nreg(REG(0, 'h')))	/* .hy mode */
 #define n_i0		(*nreg(REG(0, 'i')))	/* last .i */
+#define n_kn		(*nreg(REG(0, 'k')))	/* .kn mode */
 #define n_l0		(*nreg(REG(0, 'l')))	/* last .l */
 #define n_L0		(*nreg(REG(0, 'L')))	/* last .L */
 #define n_mk		(*nreg(REG(0, 'm')))	/* .mk internal register */
--- a/tr.c
+++ b/tr.c
@@ -417,6 +417,12 @@
 		n_lg = atoi(args[1]);
 }
 
+static void tr_kn(char **args)
+{
+	if (args[1])
+		n_kn = atoi(args[1]);
+}
+
 static char *arg_regname(char *s, int len)
 {
 	char *e = s + 2;
@@ -618,6 +624,7 @@
 	{"if", tr_if, mkargs_null},
 	{"ig", tr_ig},
 	{"in", tr_in},
+	{"kn", tr_kn},
 	{"lg", tr_lg},
 	{"ll", tr_ll},
 	{"ls", tr_ls},
--- a/wb.c
+++ b/wb.c
@@ -97,6 +97,7 @@
 			sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
 	}
 	if (strcmp(c_hc, c)) {
+		wb->prev_h = wb->h;
 		wb->h += charwid(g ? g->wid : SC_DW, R_S(wb));
 		wb->ct |= g ? g->type : 0;
 		wb_stsb(wb);
@@ -113,7 +114,7 @@
 	if (p[0] == c_ec && p[1] == '(')
 		p += 2;
 	sprintf(lig, "%s%s", p, c);
-	if (dev_lig(R_F(wb), lig)) {
+	if (font_lig(dev_font(R_F(wb)), lig)) {
 		wb->h = wb->prev_h;
 		sbuf_pop(&wb->sbuf);
 		wb_put(wb, lig);
@@ -122,6 +123,17 @@
 	return 1;
 }
 
+/* return 0 if pairwise kerning was done */
+int wb_kern(struct wb *wb, char *c)
+{
+	char *p = sbuf_last(&wb->sbuf);
+	int val;
+	val = p ? font_kern(dev_font(R_F(wb)), p, c) : 0;
+	if (val)
+		wb_hmov(wb, charwid(val, R_S(wb)));
+	return !val;
+}
+
 int wb_part(struct wb *wb)
 {
 	return wb->part;
@@ -274,11 +286,11 @@
 	char *r = NULL;
 	int c;
 	skipreqs(&s, w1);
-	while ((c = out_readc(&s, d)) == 0) {
+	while ((c = out_readc(&s, d)) >= 0) {
 		wb_putc(w1, c, d);
 		if (wb_wid(w1) > w && (!any || r))
-			break;
-		if (!strcmp("-", d) || (!strcmp("em", d) || !strcmp("hy", d)))
+			continue;
+		if (!c && (!strcmp("-", d) || (!strcmp("em", d) || !strcmp("hy", d))))
 			r = s;
 	}
 	return r;
@@ -296,11 +308,11 @@
 	char *r = NULL;
 	int c;
 	skipreqs(&s, w1);
-	while ((c = out_readc(&s, d)) == 0) {
+	while ((c = out_readc(&s, d)) >= 0) {
 		wb_putc(w1, c, d);
 		if (wb_wid(w1) + wb_dashwid(w1) > w && (!(flg & HY_ANY) || r))
-			break;
-		if (!strcmp(c_hc, d))
+			continue;
+		if (!c && !strcmp(c_hc, d))
 			r = s;
 	}
 	return r;
@@ -308,24 +320,25 @@
 
 static char *hyphpos(char *s, int w, struct wb *w1, int flg)
 {
+	char *map[ILNLEN] = {NULL};	/* mapping from word to s */
+	int fits[ILNLEN] = {0};		/* fits[i] if word[0..i]- fits w */
 	char word[ILNLEN];
-	char *map[ILNLEN];	/* mapping from word to s */
 	char hyph[ILNLEN];
 	char d[ILNLEN];
 	char *prev_s = s;
 	char *r = NULL;
-	int fit = 0;
 	char *wp = word, *we = word + sizeof(word);
 	int beg, end;
 	int i, c;
 	skipreqs(&s, w1);
-	while ((c = out_readc(&s, d)) == 0 && wp + strlen(d) + 1 < we) {
+	while ((c = out_readc(&s, d)) >= 0 && wp + strlen(d) + 1 < we) {
 		wb_putc(w1, c, d);
-		strcpy(wp, d);
-		while (*wp)
-			map[wp++ - word] = prev_s;
-		if (wb_wid(w1) + wb_dashwid(w1) <= w)
-			fit = wp - word;
+		if (c == 0) {
+			strcpy(wp, d);
+			map[wp - word] = prev_s;
+			wp = strchr(wp, '\0');
+			fits[wp - word] = wb_wid(w1) + wb_dashwid(w1) <= w;
+		}
 		prev_s = s;
 	}
 	if (strlen(word) < 4)
@@ -334,7 +347,7 @@
 	beg = flg & HY_FIRSTTWO ? 3 : 2;
 	end = strlen(word) - (flg & HY_FINAL ? 2 : 1);
 	for (i = beg; i < end; i++)
-		if (hyph[i] && (i <= fit || ((flg & HY_ANY) && !r)))
+		if (map[i] && hyph[i] && (fits[i] || ((flg & HY_ANY) && !r)))
 			r = map[i];
 	return r;
 }