shithub: neatroff

Download patch

ref: 297fc906899f4c1f9b37edca27a7e610ac8bf004
parent: 8c778d1a462baf17fb0094c3f9e1018443dcb85a
author: Ali Gholami Rudi <ali@rudi.ir>
date: Sun Aug 3 19:52:41 EDT 2014

font: support gsub/gpos context glyphs

For instance, the line

  gsub feat 5 =a -x -y -z +xyz

tells neatroff to replace the glyphs x y z with glyph xyz only if they
follow an a and font feature feat is enabled.  This patch also
supports matching one of the many glyphs in the patterns.  Also the
rule

  gsub feat 5 -a |b |c -y +axy

matches any of the strings ay, by and cy; neatroff replaces any
of them with the glyph axy if feat is enabled.

--- a/font.c
+++ b/font.c
@@ -6,10 +6,15 @@
 
 #define GHASH(g1, g2)		((((g2) + 1) << 16) | ((g1) + 1))
 
+#define GF_PAT		1	/* gsub/gpos pattern glyph */
+#define GF_REP		2	/* gsub replacement glyph */
+#define GF_CON		4	/* context glyph */
+#define GF_ALT		8	/* pattern followed by alternative patterns */
+
 /* glyph pattern for gsub and gpos tables; each grule has some gpats */
 struct gpat {
 	short g;		/* glyph index */
-	short rep;		/* ignore this glyph; gsub replacement */
+	short flg;		/* pattern flags; GF_* */
 	short x, y, xadv, yadv;	/* gpos data */
 };
 
@@ -118,13 +123,13 @@
 {
 	int g1 = -1, g2 = -1;
 	int i = 0;
-	while (i < rule->len && rule->pats[i].rep)
+	while (i < rule->len && rule->pats[i].flg == GF_REP)
 		i++;
-	g1 = i < rule->len ? rule->pats[i++].g : -1;
-	while (i < rule->len && rule->pats[i].rep)
+	g1 = i < rule->len && rule->pats[i].flg == GF_PAT ? rule->pats[i++].g : -1;
+	while (i < rule->len && rule->pats[i].flg == GF_REP)
 		i++;
-	g2 = i < rule->len ? rule->pats[i++].g : -1;
-	return GHASH(g1, g2);
+	g2 = i < rule->len && rule->pats[i].flg == GF_PAT ? rule->pats[i++].g : -1;
+	return GHASH(g1, g1 < 0 ? -1 : g2);
 }
 
 static int grule_find(struct grule *rules, int n, int hash)
@@ -141,29 +146,60 @@
 	return rules[l].hash == hash ? l : -1;
 }
 
-static int font_rulematches(struct font *fn, struct grule *rule, int *src, int len)
+static int font_rulematches(struct font *fn, struct grule *rule,
+			int *src, int slen, int *dst, int dlen)
 {
-	int j, sidx = 0;
+	int sidx = 0;		/* the index of matched glyphs in src */
+	int didx = 0;		/* the index of matched glyphs in dst */
+	int ncon = 0;		/* number of initial context glyphs */
+	struct gpat *pats = rule->pats;
+	int j;
 	if (!fn->feat_set[rule->feat])
 		return 0;
-	for (j = 0; j < rule->len; j++)
-		if (!rule->pats[j].rep)
-			if (sidx >= len || rule->pats[j].g != src[sidx++])
-				return 0;
-	return j == rule->len;
+	/* the number of initial context glyphs */
+	for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
+		if (pats[j].flg == GF_CON)
+			ncon++;
+	if (dlen < ncon)
+		return 0;
+	/* matching the base pattern */
+	for (; j < rule->len; j++) {
+		if (pats[j].flg & GF_REP)
+			continue;
+		if (sidx < slen && pats[j].g == src[sidx]) {
+			sidx++;
+			while (pats[j].flg & GF_ALT)
+				j++;
+		} else if (!(pats[j].flg & GF_ALT)) {
+			return 0;
+		}
+	}
+	if (j != rule->len)
+		return 0;
+	/* matching the initial context */
+	for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++) {
+		if (pats[j].g == dst[didx - ncon]) {
+			didx++;
+			while (pats[j].flg & GF_ALT)
+				j++;
+		} else if (!(pats[j].flg & GF_ALT)) {
+			return 0;
+		}
+	}
+	return 1;
 }
 
-static struct grule *font_findrule(struct font *fn, struct grule *rules,
-			int n, int *src, int len)
+static struct grule *font_findrule(struct font *fn, struct grule *rules, int n,
+		int *src, int slen, int *dst, int dlen)
 {
+	int hash[] = {GHASH(-1, -1), GHASH(src[0], -1), GHASH(src[0], src[1])};
 	int i, j;
-	for (j = 0; j < 2 && j < len; j++) {
-		int hash = GHASH(src[0], j == 0 ? -1 : src[1]);
-		int idx = grule_find(rules, n, hash);
+	for (j = 0; j < LEN(hash) && j <= slen; j++) {
+		int idx = grule_find(rules, n, hash[j]);
 		if (idx < 0)
 			continue;
-		for (i = idx; i < n && rules[i].hash == hash; i++)
-			if (font_rulematches(fn, rules + i, src, len))
+		for (i = idx; i < n && rules[i].hash == hash[j]; i++)
+			if (font_rulematches(fn, rules + i, src, slen, dst, dlen))
 				return rules + i;
 	}
 	return NULL;
@@ -179,14 +215,14 @@
 	for (i = 0; i < nsrc; i++)
 		src[i] = font_idx(fn, gsrc[i]);
 	for (i = 0; i < nsrc; i++) {
-		struct grule *rule = font_findrule(fn, fn->gsub,
-					fn->gsub_n, src + i, nsrc - i);
+		struct grule *rule = font_findrule(fn, fn->gsub, fn->gsub_n,
+				src + i, nsrc - i, dst + ndst, ndst);
 		dmap[ndst] = i;
 		if (rule) {
 			for (j = 0; j < rule->len; j++) {
-				if (rule->pats[j].rep)
+				if (rule->pats[j].flg == GF_REP)
 					dst[ndst++] = rule->pats[j].g;
-				else
+				if (rule->pats[j].flg == GF_PAT)
 					i++;
 			}
 			i--;
@@ -201,8 +237,8 @@
 	for (i = 0; i < ndst; i++)
 		gdst[i] = fn->glyphs + dst[i];
 	for (i = 0; i < ndst; i++) {
-		struct grule *rule = font_findrule(fn, fn->gpos,
-					fn->gpos_n, dst + i, ndst - i);
+		struct grule *rule = font_findrule(fn, fn->gpos, fn->gpos_n,
+				dst + i, ndst - i, dst + i, i);
 		if (!rule)
 			continue;
 		for (j = 0; j < rule->len; j++) {
@@ -319,7 +355,16 @@
 		if (!tok[0] || !(g = font_glyph(fn, tok + 1)))
 			return 0;
 		rule->pats[i].g = font_idx(fn, g);
-		rule->pats[i].rep = tok[0] == '+';
+		if (tok[0] == '-')
+			rule->pats[i].flg = GF_PAT;
+		if (tok[0] == '=')
+			rule->pats[i].flg = GF_CON;
+		if (tok[0] == '+')
+			rule->pats[i].flg = GF_REP;
+		if (i > 0 && tok[0] == '|') {
+			rule->pats[i].flg = rule->pats[i - 1].flg;
+			rule->pats[i - 1].flg |= GF_ALT;
+		}
 	}
 	return 0;
 }
@@ -364,6 +409,8 @@
 	rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
 	rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
 	rule->pats[0].xadv = val;
+	rule->pats[0].flg = GF_PAT;
+	rule->pats[1].flg = GF_PAT;
 	return 0;
 }
 
@@ -378,10 +425,12 @@
 		g[n++] = font_idx(fn, font_find(fn, c));
 	if (!(rule = font_gsub(fn, "liga", n + 1)))
 		return;
-	for (j = 0; j < n; j++)
+	for (j = 0; j < n; j++) {
 		rule->pats[j].g = g[j];
-	rule->pats[n].g = font_idx(fn, font_glyph(fn, lig));
-	rule->pats[n].rep = 1;
+		rule->pats[j].flg = GF_PAT;
+	}
+	rule->pats[n].g = font_idx(fn, font_find(fn, lig));
+	rule->pats[n].flg = GF_REP;
 }
 
 static void skipline(FILE* filp)