shithub: neatmkfn

Download patch

ref: 0ad2c280032bca38fde14b4c3742d4443261bb3e
parent: b4638e8564e25250934efc0ade4cc45b58508cf4
author: Ali Gholami Rudi <ali@rudi.ir>
date: Wed Aug 16 14:10:38 EDT 2017

otf: otf file support

Now neatmkfn parses the CFF table.
Glyph bounding boxes are not computed though.

--- a/otf.c
+++ b/otf.c
@@ -8,6 +8,7 @@
 #include "trfn.h"
 
 #define MAX(a, b)	((a) < (b) ? (b) : (a))
+#define LEN(a)		(sizeof(a) / sizeof((a)[0]))
 
 #define NGLYPHS		(1 << 14)
 #define NLOOKUPS	(1 << 12)
@@ -39,6 +40,7 @@
 static int warn;		/* report unsupported tables */
 
 static char *macset[];
+static char *stdset[];
 
 static int owid(int w)
 {
@@ -156,15 +158,15 @@
 	void *names;			/* glyph names */
 	int cname = 0;
 	int i;
-	if (U32(post, 0) != 0x00020000)
-		return;
 	post2 = post + 32;
 	glyph_n = U16(post2, 0);
+	if (U32(post, 0) != 0x20000)
+		return;
 	index = post2 + 2;
 	names = index + 2 * glyph_n;
 	for (i = 0; i < glyph_n; i++) {
 		int idx = U16(index, 2 * i);
-		if (idx <= 257) {
+		if (idx < 258) {
 			strcpy(glyph_name[i], macset[idx]);
 		} else {
 			memcpy(glyph_name[i], names + cname + 1,
@@ -884,6 +886,156 @@
 	}
 }
 
+/* read a cff offset, which has sz bytes */
+static int cff_int(void *tab, int off, int sz)
+{
+	int i;
+	int n = 0;
+	for (i = 0; i < sz; i++)
+		n = n * 256 + U8(tab, off + i);
+	return n;
+}
+
+/* cff dict operand/operator */
+static int cff_op(void *tab, int off, int *val)
+{
+	int b0 = U8(tab, off);
+	int i;
+	if (b0 >= 32 && b0 <= 246) {
+		*val = b0 - 139;
+		return 1;
+	}
+	if (b0 >= 247 && b0 <= 250) {
+		*val = (b0 - 247) * 256 + U8(tab, off + 1) + 108;
+		return 2;
+	}
+	if (b0 >= 251 && b0 <= 254) {
+		*val = -(b0 - 251) * 256 - U8(tab, off + 1) - 108;
+		return 2;
+	}
+	if (b0 == 28) {
+		*val = (U8(tab, off + 1) << 8) | U8(tab, off + 2);
+		return 3;
+	}
+	if (b0 == 29) {
+		*val = (U8(tab, off + 1) << 24) | (U8(tab, off + 2) << 16) |
+			(U8(tab, off + 3) << 8) | U8(tab, off + 4);
+		return 5;
+	}
+	if (b0 == 30) {
+		for (i = 1; i < 32; i++) {
+			int nib = U8(tab, off + i);
+			if ((nib & 0x0f) == 0x0f || (nib & 0xf0) == 0xf0)
+				break;
+		}
+		*val = 0;
+		return i + 1;
+	}
+	*val = b0;
+	return 1;
+}
+
+static int cffidx_cnt(void *idx)
+{
+	return U16(idx, 0);
+}
+
+static void *cffidx_get(void *idx, int i)
+{
+	int cnt = U16(idx, 0);
+	int sz = U8(idx, 2);
+	return idx + 3 + (cnt + 1) * sz - 1 + cff_int(idx, 3 + i * sz, sz);
+}
+
+static int cffidx_len(void *idx, int i)
+{
+	return cffidx_get(idx, i + 1) - cffidx_get(idx, i);
+}
+
+static void *cffidx_end(void *idx)
+{
+	return cffidx_get(idx, cffidx_cnt(idx));
+}
+
+static int cffdict_get(void *dict, int len, int key)
+{
+	int off = 0;
+	int op = 0;
+	int val = 0;
+	/* operators: keys (one or two bytes); operands: values */
+	while (off < len) {
+		val = op;
+		off += cff_op(dict, off, &op);
+		if (op == 12) {			/* two-byte operator */
+			off += cff_op(dict, off, &op);
+			op += 1200;
+		}
+		if (op == key)
+			return val;
+	}
+	return 0;
+}
+
+static void cff_char(void *stridx, int id, char *dst)
+{
+	int len;
+	if (id < 391) {
+		strcpy(dst, stdset[id]);
+		return;
+	}
+	id -= 391;
+	len = cffidx_len(stridx, id);
+	memcpy(dst, cffidx_get(stridx, id), len);
+	dst[len] = '\0';
+}
+
+static void otf_cff(void *otf, void *cff)
+{
+	void *nameidx;		/* name index */
+	void *topidx;		/* top dict index */
+	void *stridx;		/* string idx */
+	void *chridx;		/* charstrings index */
+	void *charset;		/* charset offset of top dict table */
+	int i, j;
+	if (U8(cff, 0) != 1)
+		return;
+	nameidx = cff + U8(cff, 2);
+	topidx = cffidx_end(nameidx);
+	if (cffidx_cnt(nameidx) < 1)
+		return;
+	stridx = cffidx_end(topidx);
+	chridx = cff + cffdict_get(cffidx_get(topidx, 0), cffidx_len(topidx, 0), 17);
+	charset = cff + cffdict_get(cffidx_get(topidx, 0), cffidx_len(topidx, 0), 15);
+	glyph_n = cffidx_cnt(chridx);
+	strcpy(glyph_name[0], ".notdef");
+	if (U8(charset, 0) == 0) {
+		for (i = 0; i < glyph_n; i++)
+			cff_char(stridx, i, glyph_name[i]);
+	}
+	if (U8(charset, 0) == 1) {
+		int g = 1;
+		for (i = 0; g < glyph_n; i++) {
+			int sid = U16(charset, 1 + i * 3);
+			int cnt = U8(charset, 1 + i * 3 + 2);
+			for (j = 0; j <= cnt && g < glyph_n; j++) {
+				cff_char(stridx, sid + j, glyph_name[g]);
+				g++;
+			}
+		}
+	}
+	if (U8(charset, 0) == 2) {
+		int g = 1;
+		for (i = 0; g < glyph_n; i++) {
+			int sid = U16(charset, 1 + i * 4);
+			int cnt = U16(charset, 1 + i * 4 + 2);
+			for (j = 0; j <= cnt && g < glyph_n; j++) {
+				cff_char(stridx, sid + j, glyph_name[g]);
+				g++;
+			}
+		}
+	}
+}
+
 static void *otf_input(int fd)
 {
 	struct sbuf *sb = sbuf_make();
@@ -906,6 +1058,8 @@
 	otf_post(otf_buf, otf_table(otf_buf, "post"));
 	if (otf_table(otf_buf, "glyf"))
 		otf_glyf(otf_buf, otf_table(otf_buf, "glyf"));
+	if (otf_table(otf_buf, "CFF "))
+		otf_cff(otf_buf, otf_table(otf_buf, "CFF "));
 	otf_hmtx(otf_buf, otf_table(otf_buf, "hmtx"));
 	for (i = 0; i < glyph_n; i++) {
 		trfn_char(glyph_name[i], -1,
@@ -1021,4 +1175,86 @@
 	"onequarter", "threequarters", "franc", "Gbreve", "gbreve",
 	"Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute",
 	"Ccaron", "ccaron", "dcroat",
+};
+
+static char *stdset[] = {
+	".notdef", "space", "exclam", "quotedbl", "numbersign",
+	"dollar", "percent", "ampersand", "quoteright", "parenleft",
+	"parenright", "asterisk", "plus", "comma", "hyphen",
+	"period", "slash", "zero", "one", "two",
+	"three", "four", "five", "six", "seven",
+	"eight", "nine", "colon", "semicolon", "less",
+	"equal", "greater", "question", "at", "A",
+	"B", "C", "D", "E", "F",
+	"G", "H", "I", "J", "K",
+	"L", "M", "N", "O", "P",
+	"Q", "R", "S", "T", "U",
+	"V", "W", "X", "Y", "Z",
+	"bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
+	"quoteleft", "a", "b", "c", "d",
+	"e", "f", "g", "h", "i",
+	"j", "k", "l", "m", "n",
+	"o", "p", "q", "r", "s",
+	"t", "u", "v", "w", "x",
+	"y", "z", "braceleft", "bar", "braceright",
+	"asciitilde", "exclamdown", "cent", "sterling", "fraction",
+	"yen", "florin", "section", "currency", "quotesingle",
+	"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
+	"fl", "endash", "dagger", "daggerdbl", "periodcentered",
+	"paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
+	"guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
+	"acute", "circumflex", "tilde", "macron", "breve",
+	"dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
+	"ogonek", "caron", "emdash", "AE", "ordfeminine",
+	"Lslash", "Oslash", "OE", "ordmasculine", "ae",
+	"dotlessi", "lslash", "oslash", "oe", "germandbls",
+	"onesuperior", "logicalnot", "mu", "trademark", "Eth",
+	"onehalf", "plusminus", "Thorn", "onequarter", "divide",
+	"brokenbar", "degree", "thorn", "threequarters", "twosuperior",
+	"registered", "minus", "eth", "multiply", "threesuperior",
+	"copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
+	"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
+	"Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
+	"Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
+	"Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
+	"Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
+	"aacute", "acircumflex", "adieresis", "agrave", "aring",
+	"atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
+	"egrave", "iacute", "icircumflex", "idieresis", "igrave",
+	"ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
+	"otilde", "scaron", "uacute", "ucircumflex", "udieresis",
+	"ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
+	"Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
+	"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "zerooldstyle",
+	"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
+	"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior",
+	"threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior",
+	"centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior",
+	"msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
+	"tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
+	"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall",
+	"Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
+	"Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
+	"Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
+	"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
+	"Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
+	"colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
+	"centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall",
+	"Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
+	"hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall",
+	"oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
+	"twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior",
+	"sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior",
+	"twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior",
+	"seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior",
+	"periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall",
+	"Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
+	"Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
+	"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
+	"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall",
+	"OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall",
+	"Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000",
+	"001.001", "001.002", "001.003", "Black", "Bold",
+	"Book", "Light", "Medium", "Regular", "Roman",
+	"Semibold",
 };