mkfn: support OpenType font collections
[neatmkfn.git] / trfn.c
blobd289039dd11b306508b0c727739783a5462fea79
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "mkfn.h"
6 #include "trfn_agl.h"
7 #include "trfn_ch.h"
9 #define LEN(a) ((sizeof(a) / sizeof((a)[0])))
10 #define HEXDIGS "0123456789ABCDEF"
11 #define NCHAR 8 /* number of characters per glyph */
12 #define GNLEN 64 /* glyph name length */
14 /* for buffering the output */
15 static struct sbuf *sbuf_char; /* character definitions */
16 /* handling ligatures */
17 static char mkfn_ligs[8192]; /* font ligatures */
18 static char mkfn_ligs2[8192]; /* font ligatures, whose length is two */
19 /* character type */
20 static int mkfn_asc; /* minimum height of glyphs with ascender */
21 static int mkfn_desc; /* minimum depth of glyphs with descender */
22 /* lookup tables */
23 static struct tab *tab_agl; /* adobe glyph list table */
24 static struct tab *tab_alts; /* character aliases table */
26 static int utf8len(int c)
28 if (c > 0 && c <= 0x7f)
29 return 1;
30 if (c >= 0xfc)
31 return 6;
32 if (c >= 0xf8)
33 return 5;
34 if (c >= 0xf0)
35 return 4;
36 if (c >= 0xe0)
37 return 3;
38 if (c >= 0xc0)
39 return 2;
40 return c != 0;
43 static int utf8get(char **src)
45 int result;
46 int l = 1;
47 char *s = *src;
48 if (~((unsigned char) **src) & 0xc0)
49 return (unsigned char) *(*src)++;
50 while (l < 6 && (unsigned char) *s & (0x40 >> l))
51 l++;
52 result = (0x3f >> l) & (unsigned char) *s++;
53 while (l--)
54 result = (result << 6) | ((unsigned char) *s++ & 0x3f);
55 *src = s;
56 return result;
59 static void utf8put(char **d, int c)
61 int l = 0;
62 if (c > 0xffff) {
63 *(*d)++ = 0xf0 | (c >> 18);
64 l = 3;
65 } else if (c > 0x7ff) {
66 *(*d)++ = 0xe0 | (c >> 12);
67 l = 2;
68 } else if (c > 0x7f) {
69 *(*d)++ = 0xc0 | (c >> 6);
70 l = 1;
71 } else {
72 *(*d)++ = c > 0 ? c : ' ';
74 while (l--)
75 *(*d)++ = 0x80 | ((c >> (l * 6)) & 0x3f);
76 **d = '\0';
79 static int hexval(char *s, int len)
81 char *digs = HEXDIGS;
82 int n = 0;
83 int i;
84 for (i = 0; i < len; i++) {
85 if (s[i] && strchr(digs, s[i]))
86 n = n * 16 + (strchr(digs, s[i]) - digs);
87 else
88 break;
90 return i < 4 ? -1 : n;
93 static int agl_map(char *d, char *s)
95 char *u = tab_get(tab_agl, s); /* unicode code point like "FB8E" */
96 if (!u)
97 return 1;
98 while (u && *u) {
99 while (*u == ' ')
100 u++;
101 utf8put(&d, hexval(u, 6));
102 u = strchr(u, ' ');
104 *d = '\0';
105 return 0;
108 static int achar_map(char *name)
110 int i;
111 for (i = 0; i < LEN(achars); i++) {
112 struct achar *a = &achars[i];
113 if (!strncmp(a->name, name, strlen(a->name))) {
114 char *postfix = name + strlen(a->name);
115 if (!*postfix)
116 return a->c;
117 if (!strcmp("isolated", postfix))
118 return a->s ? a->s : a->c;
119 if (!strcmp("initial", postfix))
120 return a->i ? a->i : a->c;
121 if (!strcmp("medial", postfix))
122 return a->m ? a->m : a->c;
123 if (!strcmp("final", postfix))
124 return a->f ? a->f : a->c;
127 return 0;
130 static int achar_shape(int c, int pjoin, int njoin)
132 int i;
133 for (i = 0; i < LEN(achars); i++) {
134 struct achar *a = &achars[i];
135 if (a->c == c) {
136 if (!pjoin && !njoin)
137 return a->c;
138 if (!pjoin && njoin)
139 return a->i ? a->i : a->c;
140 if (pjoin && njoin)
141 return a->m ? a->m : a->c;
142 if (pjoin && !njoin)
143 return a->f ? a->f : a->c;
146 return c;
149 static void ashape(char *str, char *ext)
151 int s[NCHAR];
152 char *src = str;
153 int i, l;
154 int bjoin = !strcmp(".medi", ext) || !strcmp(".fina", ext);
155 int ejoin = !strcmp(".medi", ext) || !strcmp(".init", ext);
156 for (l = 0; l < NCHAR && *src; l++)
157 s[l] = utf8get(&src);
158 for (i = 0; i < l; i++)
159 s[i] = achar_shape(s[i], i > 0 || bjoin, i < l - 1 || ejoin);
160 for (i = 0; i < l; i++)
161 utf8put(&str, s[i]);
164 /* find the utf-8 name of src with the given unicode codepoint */
165 static int trfn_name(char *dst, char *src, int codepoint)
167 char ch[GNLEN];
168 char *d = dst;
169 char *s;
170 int i;
171 if (codepoint) {
172 utf8put(&dst, codepoint);
173 return 0;
175 if (!src || src[0] == '.')
176 return 1;
177 while (*src && *src != '.') {
178 s = ch;
179 if (src[0] == '_')
180 src++;
181 while (*src && *src != '_' && *src != '.')
182 *s++ = *src++;
183 *s = '\0';
184 if (!agl_map(d, ch)) {
185 d = strchr(d, '\0');
186 } else if (ch[0] == 'u' && ch[1] == 'n' &&
187 ch[2] == 'i' && hexval(ch + 3, 4) > 0) {
188 for (i = 0; strlen(ch + 3 + 4 * i) >= 4; i++)
189 utf8put(&d, hexval(ch + 3 + 4 * i, 4));
190 } else if (ch[0] == 'u' && hexval(ch + 1, 4) > 0) {
191 utf8put(&d, hexval(ch + 1, 6));
192 } else if (achar_map(ch)) {
193 utf8put(&d, achar_map(ch));
194 } else {
195 return 1;
198 ashape(dst, src);
199 return *src && strcmp(src, ".medi") && strcmp(src, ".fina") &&
200 strcmp(src, ".init") && strcmp(src, ".isol");
203 static void trfn_aglexceptions(char *dst)
205 int i;
206 for (i = 0; i < LEN(agl_exceptions); i++)
207 if (!strcmp(agl_exceptions[i][0], dst))
208 strcpy(dst, agl_exceptions[i][1]);
211 static void trfn_ligput(char *c)
213 char *dst = strlen(c) == 2 ? mkfn_ligs2 : mkfn_ligs;
214 sprintf(strchr(dst, '\0'), "%s ", c);
217 static void trfn_lig(char *c)
219 int i;
220 for (i = 0; i < LEN(agl_exceptions); i++)
221 if (!strcmp(agl_exceptions[i][1], c))
222 return;
223 if (c[0] && c[1] && strlen(c) > utf8len((unsigned char) c[0])) {
224 trfn_ligput(c);
225 } else {
226 for (i = 0; i < LEN(ligs_utf8); i++)
227 if (!strcmp(ligs_utf8[i][0], c))
228 trfn_ligput(ligs_utf8[i][1]);
232 static int trfn_type(char *s, int lly, int ury)
234 int typ = 0;
235 int c = !s[0] || s[1] ? 0 : (unsigned char) *s;
236 if (c == 't' && !mkfn_asc)
237 mkfn_asc = ury;
238 if ((c == 'g' || c == 'j' || c == 'p' || c == 'q' || c == 'y') &&
239 (!mkfn_desc || mkfn_desc < lly))
240 mkfn_desc = lly;
241 if (!mkfn_desc || !mkfn_asc) {
242 if (c > 0 && c < 128)
243 return ctype_ascii[c];
244 return 3;
246 if (!mkfn_desc || lly <= mkfn_desc)
247 typ |= 1;
248 if (!mkfn_asc || ury >= mkfn_asc)
249 typ |= 2;
250 return typ;
253 /* n is the position and u is the unicode codepoint */
254 void mkfn_char(char *psname, int n, int u, int wid,
255 int llx, int lly, int urx, int ury)
257 char uc[GNLEN]; /* mapping unicode character */
258 char **a_tr; /* troff character names */
259 char pos[GNLEN] = ""; /* postscript character position/name */
260 int typ; /* character type */
261 /* initializing character attributes */
262 if (trfn_name(uc, psname, u))
263 strcpy(uc, "---");
264 trfn_aglexceptions(uc);
265 if (mkfn_pos && n >= 0 && n < 256)
266 sprintf(pos, "%d", n);
267 if (mkfn_pos && n < 0 && !uc[1] && uc[0] >= 32 && uc[0] <= 125)
268 if (!strchr(psname, '.'))
269 sprintf(pos, "%d", uc[0]);
270 typ = trfn_type(!strchr(psname, '.') ? uc : "", lly, ury);
271 if (!mkfn_swid && (!strcmp(" ", uc) || !strcmp(" ", uc)))
272 mkfn_swid = wid;
273 /* printing troff charset */
274 if (isspace((unsigned char) uc[0]) || strchr(uc, ' '))
275 strcpy(uc, "---"); /* space not allowed in char names */
276 if (strcmp("---", uc))
277 trfn_lig(uc);
278 sbuf_printf(sbuf_char, "char %s\t%d", uc, wid);
279 if (mkfn_bbox && (llx || lly || urx || ury))
280 sbuf_printf(sbuf_char, ",%d,%d,%d,%d", llx, lly, urx, ury);
281 sbuf_printf(sbuf_char, "\t%d\t%s\t%s\n", typ, psname, pos);
282 a_tr = tab_get(tab_alts, uc);
283 while (a_tr && *a_tr)
284 sbuf_printf(sbuf_char, "char %s\t\"\n", *a_tr++);
287 void mkfn_kern(char *c1, char *c2, int x)
289 if (x && abs(x) >= mkfn_kmin)
290 if (!mkfn_dry)
291 printf("kern %s\t%s\t%d\n", c1, c2, x);
294 /* print spacewidth and ligature lines */
295 void trfn_header(void)
297 printf("spacewidth %d\n", mkfn_swid);
298 if (!mkfn_noligs)
299 printf("ligatures %s%s0\n", mkfn_ligs, mkfn_ligs2);
302 /* print character definitions */
303 void trfn_cdefs(void)
305 fputs(sbuf_buf(sbuf_char), stdout);
308 void trfn_init(void)
310 int i;
311 sbuf_char = sbuf_make();
312 tab_agl = tab_alloc(LEN(agl));
313 for (i = 0; i < LEN(agl); i++)
314 tab_put(tab_agl, agl[i][0], agl[i][1]);
315 tab_alts = tab_alloc(LEN(alts));
316 for (i = 0; i < LEN(alts); i++)
317 tab_put(tab_alts, alts[i][0], alts[i] + 1);
320 void trfn_done(void)
322 sbuf_free(sbuf_char);
323 tab_free(tab_alts);
324 if (tab_agl)
325 tab_free(tab_agl);