ref: 00b9b4736f1ee8bf19a143f3eb8f4811c60e78cd
dir: /otf.c/
/* OpenType and TrueType fonts */ #include <arpa/inet.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "mkfn.h" #define MAX(a, b) ((a) < (b) ? (b) : (a)) #define LEN(a) (sizeof(a) / sizeof((a)[0])) #define NGLYPHS (1 << 16) #define NLOOKUPS (1 << 12) #define GNLEN (64) #define NGRPS 2048 #define U32(buf, off) (htonl(*(u32 *) ((buf) + (off)))) #define U16(buf, off) (htons(*(u16 *) ((buf) + (off)))) #define U8(buf, off) (*(u8 *) ((buf) + (off))) #define S16(buf, off) ((s16) htons(*(u16 *) ((buf) + (off)))) #define S32(buf, off) ((s32) htonl(*(u32 *) ((buf) + (off)))) #define GCTXLEN 16 /* number of context backtrack coverage arrays */ typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef int s32; typedef short s16; static char glyph_name[NGLYPHS][GNLEN]; static int glyph_code[NGLYPHS]; static int glyph_bbox[NGLYPHS][4]; static int glyph_wid[NGLYPHS]; static int glyph_n; static int upm; /* units per em */ struct otf { void *otf; /* TTC header or offset table */ void *off; /* offset table */ char name[128]; /* font name */ }; static char *macset[]; static char *stdset[]; static int owid(int w) { return (w < 0 ? w * 1000 - upm / 2 : w * 1000 + upm / 2) / upm; } static int uwid(int w) { int d = 72000 / mkfn_res; return (w < 0 ? owid(w) - d / 20 : owid(w) + d / 20) * 10 / d; } /* whether the script is right-to-left */ static int otf_r2l(char *feat) { char *scrp = strchr(feat, ':') + 1; return !strcmp("arab", scrp) || !strcmp("hebr", scrp); } /* report unsupported otf tables */ static void otf_unsupported(char *sub, int type, int fmt) { if (mkfn_warn) { fprintf(stderr, "neatmkfn: unsupported %s lookup %d", sub, type); if (fmt > 0) fprintf(stderr, " format %d", fmt); fprintf(stderr, "\n"); } } /* find the otf table with the given name */ static void *otf_table(struct otf *otf, char *name) { int nrecs = U16(otf->off, 4); int i; for (i = 0; i < nrecs; i++) { void *rec = otf->off + 12 + i * 16; /* an otf table record */ if (!strncmp(rec, name, 4)) return otf->otf + U32(rec, 8); } return NULL; } /* obtain postscript font name from name table */ static void otf_name(struct otf *otf, void *tab) { char name[256]; void *str = tab + U16(tab, 4); /* storage area */ int n = U16(tab, 2); /* number of name records */ int i; for (i = 0; i < n; i++) { void *rec = tab + 6 + 12 * i; int pid = U16(rec, 0); /* platform id */ int eid = U16(rec, 2); /* encoding id */ int lid = U16(rec, 4); /* language id */ int nid = U16(rec, 6); /* name id */ int len = U16(rec, 8); /* string length */ int off = U16(rec, 10); /* string offset */ if (pid == 1 && eid == 0 && lid == 0 && nid == 6) { memcpy(name, str + off, len); name[len] = '\0'; snprintf(otf->name, sizeof(otf->name), "%s", name); } } } /* parse otf cmap format 4 subtable */ static void otf_cmap4(struct otf *otf, void *cmap4) { int nsegs; void *ends, *begs, *deltas, *offsets; int beg, end, delta, offset; int i, j; nsegs = U16(cmap4, 6) / 2; ends = cmap4 + 14; begs = ends + 2 * nsegs + 2; deltas = begs + 2 * nsegs; offsets = deltas + 2 * nsegs; for (i = 0; i < nsegs; i++) { beg = U16(begs, 2 * i); end = U16(ends, 2 * i); delta = U16(deltas, 2 * i); offset = U16(offsets, 2 * i); if (offset) { for (j = beg; j <= end; j++) glyph_code[(U16(offsets + 2 * i, offset + (j - beg) * 2) + delta) & 0xffff] = j; } else { for (j = beg; j <= end; j++) glyph_code[(j + delta) & 0xffff] = j; } } } /* parse otf cmap header */ static void otf_cmap(struct otf *otf, void *cmap) { int nrecs = U16(cmap, 2); int i; for (i = 0; i < nrecs; i++) { void *rec = cmap + 4 + i * 8; /* a cmap record */ int plat = U16(rec, 0); int enc = U16(rec, 2); void *tab = cmap + U32(rec, 4); /* a cmap subtable */ int fmt = U16(tab, 0); if (plat == 3 && enc == 1 && fmt == 4) otf_cmap4(otf, tab); } } static void otf_post(struct otf *otf, void *post) { void *post2; /* version 2.0 header */ void *index; /* glyph name indices */ void *names; /* glyph names */ int cname = 0; int i; if (U32(post, 0) != 0x20000) return; post2 = post + 32; glyph_n = U16(post2, 0); index = post2 + 2; names = index + 2 * glyph_n; for (i = 0; i < glyph_n; i++) { int idx = U16(index, 2 * i); if (idx < 258) { strcpy(glyph_name[i], macset[idx]); } else { memcpy(glyph_name[i], names + cname + 1, U8(names, cname)); glyph_name[i][U8(names, cname)] = '\0'; cname += U8(names, cname) + 1; } } } static void otf_glyf(struct otf *otf, void *glyf) { void *maxp = otf_table(otf, "maxp"); void *head = otf_table(otf, "head"); void *loca = otf_table(otf, "loca"); void *gdat; void *gdat_next; int n = U16(maxp, 4); int fmt = U16(head, 50); int i, j; if (!glyph_n) glyph_n = n; for (i = 0; i < n; i++) { if (fmt) { gdat = glyf + U32(loca, 4 * i); gdat_next = glyf + U32(loca, 4 * (i + 1)); } else { gdat = glyf + U16(loca, 2 * i) * 2; gdat_next = glyf + U16(loca, 2 * (i + 1)) * 2; } if (gdat < gdat_next) for (j = 0; j < 4; j++) glyph_bbox[i][j] = S16(gdat, 2 + 2 * j); } } static void otf_hmtx(struct otf *otf, void *hmtx) { void *hhea = otf_table(otf, "hhea"); int n; int i; n = U16(hhea, 34); for (i = 0; i < n; i++) glyph_wid[i] = U16(hmtx, i * 4); for (i = n; i < glyph_n; i++) glyph_wid[i] = glyph_wid[n - 1]; } static void otf_kern(struct otf *otf, void *kern) { int off = 4; int i, j; int n = U16(kern, 2); /* number of kern subtables */ for (i = 0; i < n; i++) { void *tab = kern + off; /* a kern subtable */ int cov = U16(tab, 4); off += U16(tab, 2); if ((cov >> 8) == 0 && (cov & 1)) { /* format 0 */ int npairs = U16(tab, 6); for (j = 0; j < npairs; j++) { int c1 = U16(tab, 14 + 6 * j); int c2 = U16(tab, 14 + 6 * j + 2); int val = S16(tab, 14 + 6 * j + 4); mkfn_kern(glyph_name[c1], glyph_name[c2], uwid(val)); } } } } static int *coverage(void *cov, int *ncov) { int fmt = U16(cov, 0); int n = U16(cov, 2); int beg, end; int i, j; int *out = malloc(glyph_n * sizeof(*out)); int cnt = 0; if (fmt == 1) { for (i = 0; i < n; i++) out[cnt++] = U16(cov, 4 + 2 * i); } if (fmt == 2) { for (i = 0; i < n; i++) { beg = U16(cov, 4 + 6 * i); end = U16(cov, 4 + 6 * i + 2); for (j = beg; j <= end; j++) out[cnt++] = j; } } if (ncov) *ncov = cnt; return out; } static int classdef(void *tab, int *gl, int *cls) { int fmt = U16(tab, 0); int ngl = 0; int i, j; if (fmt == 1) { int beg = U16(tab, 2); ngl = U16(tab, 4); for (i = 0; i < ngl; i++) { gl[i] = beg + i; cls[i] = U16(tab, 6 + 2 * i); } } if (fmt == 2) { int n = U16(tab, 2); for (i = 0; i < n; i++) { int beg = U16(tab, 4 + 6 * i); int end = U16(tab, 4 + 6 * i + 2); for (j = beg; j <= end; j++) { gl[ngl] = j; cls[ngl] = U16(tab, 4 + 6 * i + 4); ngl++; } } } return ngl; } static int intcmp(void *v1, void *v2) { return *(int *) v1 - *(int *) v2; } static int ggrp_make(int *src, int n); static int ggrp_class(int *src, int *cls, int nsrc, int id) { int *g = malloc(nsrc * sizeof(g[0])); int n = 0; int i; int grp; for (i = 0; i < nsrc; i++) if (cls[i] == id) g[n++] = src[i]; qsort(g, n, sizeof(g[0]), (void *) intcmp); grp = ggrp_make(g, n); free(g); return grp; } static int ggrp_coverage(int *g, int n) { qsort(g, n, sizeof(g[0]), (void *) intcmp); return ggrp_make(g, n); } static int valuerecord_len(int fmt) { int off = 0; int i; for (i = 0; i < 8; i++) if (fmt & (1 << i)) off += 2; return off; } static void valuerecord_print(int fmt, void *rec) { int vals[8] = {0}; int off = 0; int i; for (i = 0; i < 8; i++) { if (fmt & (1 << i)) { vals[i] = uwid(S16(rec, off)); off += 2; } } if (fmt) printf(":%+d%+d%+d%+d", vals[0], vals[1], vals[2], vals[3]); } static int valuerecord_small(int fmt, void *rec) { int off = 0; int i; for (i = 0; i < 8; i++) { if (fmt & (1 << i)) { if (abs(uwid(S16(rec, off))) >= MAX(1, mkfn_kmin)) return 0; off += 2; } } return 1; } /* single adjustment positioning */ static void otf_gpostype1(struct otf *otf, void *sub, char *feat) { int fmt = U16(sub, 0); int vfmt = U16(sub, 4); int *cov; int ncov, nvals; int vlen = valuerecord_len(vfmt); int i; cov = coverage(sub + U16(sub, 2), &ncov); if (fmt == 1) { for (i = 0; i < ncov; i++) { if (valuerecord_small(vfmt, sub + 6)) continue; printf("gpos %s 1 %s", feat, glyph_name[cov[i]]); valuerecord_print(vfmt, sub + 6); printf("\n"); } } if (fmt == 2) { nvals = U16(sub, 6); for (i = 0; i < nvals; i++) { if (valuerecord_small(vfmt, sub + 6)) continue; printf("gpos %s 1 %s", feat, glyph_name[cov[i]]); valuerecord_print(vfmt, sub + 8 + i * vlen); printf("\n"); } } free(cov); } /* pair adjustment positioning */ static void otf_gpostype2(struct otf *otf, void *sub, char *feat) { int fmt = U16(sub, 0); int vfmt1 = U16(sub, 4); /* valuerecord 1 */ int vfmt2 = U16(sub, 6); /* valuerecord 2 */ int fmtoff1, fmtoff2; int vrlen; /* the length of vfmt1 and vfmt2 */ int i, j; vrlen = valuerecord_len(vfmt1) + valuerecord_len(vfmt2); if (fmt == 1) { int nc1 = U16(sub, 8); int *cov = coverage(sub + U16(sub, 2), NULL); for (i = 0; i < nc1; i++) { void *c2 = sub + U16(sub, 10 + 2 * i); int nc2 = U16(c2, 0); for (j = 0; j < nc2; j++) { int second = U16(c2 + 2 + (2 + vrlen) * j, 0); fmtoff1 = 2 + (2 + vrlen) * j + 2; fmtoff2 = fmtoff1 + valuerecord_len(vfmt1); if (valuerecord_small(vfmt1, c2 + fmtoff1) && valuerecord_small(vfmt2, c2 + fmtoff2)) continue; printf("gpos %s 2", feat); printf(" %s", glyph_name[cov[i]]); valuerecord_print(vfmt1, c2 + fmtoff1); printf(" %s", glyph_name[second]); valuerecord_print(vfmt2, c2 + fmtoff2); printf("\n"); } } free(cov); } if (fmt == 2) { static int gl1[NGLYPHS], gl2[NGLYPHS]; static int cls1[NGLYPHS], cls2[NGLYPHS]; static int grp1[NGLYPHS], grp2[NGLYPHS]; int ngl1 = classdef(sub + U16(sub, 8), gl1, cls1); int ngl2 = classdef(sub + U16(sub, 10), gl2, cls2); int ncls1 = U16(sub, 12); int ncls2 = U16(sub, 14); for (i = 0; i < ncls1; i++) grp1[i] = ggrp_class(gl1, cls1, ngl1, i); for (i = 0; i < ncls2; i++) grp2[i] = ggrp_class(gl2, cls2, ngl2, i); for (i = 0; i < ncls1; i++) { for (j = 0; j < ncls2; j++) { fmtoff1 = 16 + (i * ncls2 + j) * vrlen; fmtoff2 = fmtoff1 + valuerecord_len(vfmt1); if (valuerecord_small(vfmt1, sub + fmtoff1) && valuerecord_small(vfmt2, sub + fmtoff2)) continue; printf("gpos %s %d", feat, 2); printf(" @%d", grp1[i]); valuerecord_print(vfmt1, sub + fmtoff1); printf(" @%d", grp2[j]); valuerecord_print(vfmt2, sub + fmtoff2); printf("\n"); } } } } /* cursive attachment positioning */ static void otf_gpostype3(struct otf *otf, void *sub, char *feat) { int fmt = U16(sub, 0); int *cov, *icov, *ocov; int i, n; int icnt = 0; int ocnt = 0; int igrp, ogrp; if (fmt != 1) return; cov = coverage(sub + U16(sub, 2), NULL); n = U16(sub, 4); icov = malloc(n * sizeof(icov[0])); ocov = malloc(n * sizeof(ocov[0])); for (i = 0; i < n; i++) if (U16(sub, 6 + 4 * i)) ocov[ocnt++] = cov[i]; for (i = 0; i < n; i++) if (U16(sub, 6 + 4 * i + 2)) icov[icnt++] = cov[i]; igrp = ggrp_coverage(icov, icnt); ogrp = ggrp_coverage(ocov, ocnt); free(icov); free(ocov); for (i = 0; i < n; i++) { int prev = U16(sub, 6 + 4 * i); int next = U16(sub, 6 + 4 * i + 2); if (prev) { int dx = -uwid(S16(sub, prev + 2)); int dy = -uwid(S16(sub, prev + 4)); if (otf_r2l(feat)) dx += uwid(glyph_wid[cov[i]]); printf("gpos %s 2 @%d %s:%+d%+d%+d%+d\n", feat, igrp, glyph_name[cov[i]], 0, 0, dx, dy); } if (next) { int dx = uwid(S16(sub, next + 2)) - uwid(glyph_wid[cov[i]]); int dy = uwid(S16(sub, next + 4)); if (otf_r2l(feat)) { dx += uwid(glyph_wid[cov[i]]); } printf("gpos %s 2 %s @%d:%+d%+d%+d%+d\n", feat, glyph_name[cov[i]], ogrp, 0, 0, dx, dy); } } free(cov); } /* mark-to-base attachment positioning */ static void otf_gpostype4(struct otf *otf, void *sub, char *feat) { int fmt = U16(sub, 0); int *mcov; /* mark coverage */ int *bcov; /* base coverage */ int cgrp[1024]; /* glyph groups assigned to classes */ int bgrp; /* the group assigned to base glyphs */ int mcnt; /* mark coverage size */ int bcnt; /* base coverage size */ int ccnt; /* class count */ void *marks; /* mark array table */ void *bases; /* base array table */ int i, j; if (fmt != 1) return; mcov = coverage(sub + U16(sub, 2), &mcnt); bcov = coverage(sub + U16(sub, 4), &bcnt); ccnt = U16(sub, 6); marks = sub + U16(sub, 8); bases = sub + U16(sub, 10); bgrp = ggrp_coverage(bcov, bcnt); for (i = 0; i < ccnt; i++) { int *grp = malloc(mcnt * sizeof(grp[0])); int cnt = 0; for (j = 0; j < mcnt; j++) if (U16(marks, 2 + 4 * j) == i) grp[cnt++] = mcov[j]; cgrp[i] = ggrp_coverage(grp, cnt); free(grp); } for (i = 0; i < mcnt; i++) { void *mark = marks + U16(marks, 2 + 4 * i + 2); /* mark anchor */ int dx = -uwid(S16(mark, 2)); int dy = -uwid(S16(mark, 4)); if (otf_r2l(feat)) { dx += uwid(glyph_wid[mcov[i]]); dy = -dy; } printf("gpos %s 2 @%d %s:%+d%+d%+d%+d\n", feat, bgrp, glyph_name[mcov[i]], dx, dy, 0, 0); } for (i = 0; i < bcnt; i++) { for (j = 0; j < ccnt; j++) { void *base = bases + U16(bases, 2 + ccnt * 2 * i + 2 * j); int dx = uwid(S16(base, 2)) - uwid(glyph_wid[bcov[i]]); int dy = uwid(S16(base, 4)); if (otf_r2l(feat)) { dx += uwid(glyph_wid[bcov[i]]); dy = -dy; } printf("gpos %s 2 %s @%d:%+d%+d%+d%+d\n", feat, glyph_name[bcov[i]], cgrp[j], dx, dy, 0, 0); } } free(mcov); free(bcov); } /* gsub context */ struct gctx { int bgrp[GCTXLEN]; /* backtrack coverage arrays */ int igrp[GCTXLEN]; /* input coverage arrays */ int lgrp[GCTXLEN]; /* lookahead coverage arrays*/ int bn, in, ln; /* size of b[], i[], l[] */ int seqidx; /* sequence index */ }; static int gctx_len(struct gctx *ctx, int patlen) { return ctx ? ctx->bn + ctx->in + ctx->ln - patlen : 0; } static void gctx_backtrack(struct gctx *ctx) { int i; if (!ctx) return; for (i = 0; i < ctx->bn; i++) printf(" =@%d", ctx->bgrp[i]); for (i = 0; i < ctx->seqidx; i++) printf(" =@%d", ctx->igrp[i]); } static void gctx_lookahead(struct gctx *ctx, int patlen) { int i; if (!ctx) return; for (i = ctx->seqidx + patlen; i < ctx->in; i++) printf(" =@%d", ctx->igrp[i]); for (i = 0; i < ctx->ln; i++) printf(" =@%d", ctx->lgrp[i]); } /* single substitution */ static void otf_gsubtype1(struct otf *otf, void *sub, char *feat, struct gctx *ctx) { int *cov; int fmt = U16(sub, 0); int ncov; int i; cov = coverage(sub + U16(sub, 2), &ncov); if (fmt == 1) { for (i = 0; i < ncov; i++) { int dst = cov[i] + S16(sub, 4); if (dst >= glyph_n || dst < 0) continue; printf("gsub %s %d", feat, 2 + gctx_len(ctx, 1)); gctx_backtrack(ctx); printf(" -%s +%s", glyph_name[cov[i]], glyph_name[dst]); gctx_lookahead(ctx, 1); printf("\n"); } } if (fmt == 2) { int n = U16(sub, 4); for (i = 0; i < n; i++) { printf("gsub %s %d", feat, 2 + gctx_len(ctx, 1)); gctx_backtrack(ctx); printf(" -%s +%s", glyph_name[cov[i]], glyph_name[U16(sub, 6 + 2 * i)]); gctx_lookahead(ctx, 1); printf("\n"); } } free(cov); } /* alternate substitution */ static void otf_gsubtype3(struct otf *otf, void *sub, char *feat, struct gctx *ctx) { int *cov; int fmt = U16(sub, 0); int n, i, j; if (fmt != 1) return; cov = coverage(sub + U16(sub, 2), NULL); n = U16(sub, 4); for (i = 0; i < n; i++) { void *alt = sub + U16(sub, 6 + 2 * i); int nalt = U16(alt, 0); for (j = 0; j < nalt; j++) { printf("gsub %s %d", feat, 2 + gctx_len(ctx, 1)); gctx_backtrack(ctx); printf(" -%s +%s", glyph_name[cov[i]], glyph_name[U16(alt, 2 + 2 * j)]); gctx_lookahead(ctx, 1); printf("\n"); } } free(cov); } /* ligature substitution */ static void otf_gsubtype4(struct otf *otf, void *sub, char *feat, struct gctx *ctx) { int fmt = U16(sub, 0); int *cov; int n, i, j, k; if (fmt != 1) return; cov = coverage(sub + U16(sub, 2), NULL); n = U16(sub, 4); for (i = 0; i < n; i++) { void *set = sub + U16(sub, 6 + 2 * i); int nset = U16(set, 0); for (j = 0; j < nset; j++) { void *lig = set + U16(set, 2 + 2 * j); int nlig = U16(lig, 2); printf("gsub %s %d", feat, nlig + 1 + gctx_len(ctx, nlig)); gctx_backtrack(ctx); printf(" -%s", glyph_name[cov[i]]); for (k = 0; k < nlig - 1; k++) printf(" -%s", glyph_name[U16(lig, 4 + 2 * k)]); printf(" +%s", glyph_name[U16(lig, 0)]); gctx_lookahead(ctx, nlig); printf("\n"); } } free(cov); } /* chaining contextual substitution */ static void otf_gsubtype6(struct otf *otf, void *sub, char *feat, void *gsub) { struct gctx ctx = {{0}}; void *lookups = gsub + U16(gsub, 8); int fmt = U16(sub, 0); int *cov; int i, j, nsub, ncov; int off = 2; if (fmt != 3) { otf_unsupported("GSUB", 6, fmt); return; } ctx.bn = U16(sub, off); for (i = 0; i < ctx.bn; i++) { cov = coverage(sub + U16(sub, off + 2 + 2 * i), &ncov); ctx.bgrp[i] = ggrp_coverage(cov, ncov); free(cov); } off += 2 + 2 * ctx.bn; ctx.in = U16(sub, off); for (i = 0; i < ctx.in; i++) { cov = coverage(sub + U16(sub, off + 2 + 2 * i), &ncov); ctx.igrp[i] = ggrp_coverage(cov, ncov); free(cov); } off += 2 + 2 * ctx.in; ctx.ln = U16(sub, off); for (i = 0; i < ctx.ln; i ++) { cov = coverage(sub + U16(sub, off + 2 + 2 * i), &ncov); ctx.lgrp[i] = ggrp_coverage(cov, ncov); free(cov); } off += 2 + 2 * ctx.ln; nsub = U16(sub, off); /* nsub > 1 is not supported */ for (i = 0; i < nsub && i < 1; i++) { int lidx = U16(sub, off + 2 + 4 * i + 2); void *lookup = lookups + U16(lookups, 2 + 2 * lidx); int ltype = U16(lookup, 0); int ntabs = U16(lookup, 4); ctx.seqidx = U16(sub, off + 2 + 4 * i); for (j = 0; j < ntabs; j++) { void *tab = lookup + U16(lookup, 6 + 2 * j); int type = ltype; if (type == 7) { /* extension substitution */ type = U16(tab, 2); tab = tab + U32(tab, 4); } if (type == 1) otf_gsubtype1(otf, tab, feat, &ctx); if (type == 3) otf_gsubtype3(otf, tab, feat, &ctx); if (type == 4) otf_gsubtype4(otf, tab, feat, &ctx); } } } /* an otf gsub/gpos lookup */ struct otflookup { char scrp[8]; /* script name */ char lang[8]; /* language name */ char feat[8]; /* feature name */ int lookup; /* index into the lookup table */ }; /* parse the given gsub/gpos feature table */ static int otf_featrec(struct otf *otf, void *gtab, void *featrec, char *stag, char *ltag, struct otflookup *lookups, int lookups_n) { void *feats = gtab + U16(gtab, 6); void *feat = feats + U16(featrec, 4); int n = U16(feat, 2); int i, j; for (i = 0; i < n; i++) { int lookup = U16(feat, 4 + 2 * i); /* lookup index */ /* do not store features common to all languages in a script */ for (j = 0; j < lookups_n; j++) if (lookups[j].lookup == lookup && !lookups[j].lang[0]) if (!strcmp(lookups[j].scrp, stag)) break; if (j == lookups_n) { memcpy(lookups[j].feat, featrec, 4); lookups[j].feat[4] = '\0'; strcpy(lookups[j].scrp, stag); strcpy(lookups[j].lang, ltag); lookups[j].lookup = U16(feat, 4 + 2 * i); lookups_n++; } } return lookups_n; } /* parse the given language table and its feature tables */ static int otf_lang(struct otf *otf, void *gtab, void *lang, char *stag, char *ltag, struct otflookup *lookups, int lookups_n) { void *feats = gtab + U16(gtab, 6); int featidx = U16(lang, 2); int nfeat = U16(lang, 4); int i; if (featidx != 0xffff) lookups_n = otf_featrec(otf, gtab, feats + 2 + 6 * featidx, stag, ltag, lookups, lookups_n); for (i = 0; i < nfeat; i++) lookups_n = otf_featrec(otf, gtab, feats + 2 + 6 * U16(lang, 6 + 2 * i), stag, ltag, lookups, lookups_n); return lookups_n; } /* return lookup table tag (i.e. liga:latn:ENG); returns a static buffer */ static char *lookuptag(struct otflookup *lu) { static char tag[16]; sprintf(tag, "%s:%s", lu->feat, lu->scrp[0] ? lu->scrp : "DFLT"); if (lu->lang[0]) sprintf(strchr(tag, '\0'), ":%s", lu->lang); return tag; } static int lookupcmp(void *v1, void *v2) { struct otflookup *l1 = v1; struct otflookup *l2 = v2; if (strcmp(l1->scrp, l2->scrp)) return strcmp(l1->scrp, l2->scrp); if (mkfn_featrank(l1->scrp, l1->feat) != mkfn_featrank(l1->scrp, l2->feat)) return mkfn_featrank(l1->scrp, l1->feat) - mkfn_featrank(l1->scrp, l2->feat); return l1->lookup - l2->lookup; } /* extract lookup tables for all features of the given gsub/gpos table */ static int otf_gtab(struct otf *otf, void *gpos, struct otflookup *lookups) { void *scripts = gpos + U16(gpos, 4); int nscripts, nlangs; void *script; char stag[8], ltag[8]; /* script and language tags */ int i, j; int n = 0; nscripts = U16(scripts, 0); for (i = 0; i < nscripts; i++) { void *grec = scripts + 2 + 6 * i; memcpy(stag, grec, 4); stag[4] = '\0'; if (!mkfn_script(stag, nscripts)) continue; script = scripts + U16(grec, 4); nlangs = U16(script, 2); if (U16(script, 0) && mkfn_lang(NULL, nlangs + (U16(script, 0) != 0))) n = otf_lang(otf, gpos, script + U16(script, 0), stag, "", lookups, n); for (j = 0; j < nlangs; j++) { void *lrec = script + 4 + 6 * j; memcpy(ltag, lrec, 4); ltag[4] = '\0'; if (mkfn_lang(ltag, nlangs + (U16(script, 0) != 0))) n = otf_lang(otf, gpos, script + U16(lrec, 4), stag, ltag, lookups, n); } } qsort(lookups, n, sizeof(lookups[0]), (void *) lookupcmp); return n; } static void otf_gpos(struct otf *otf, void *gpos) { struct otflookup lookups[NLOOKUPS]; void *lookuplist = gpos + U16(gpos, 8); int nlookups = otf_gtab(otf, gpos, lookups); int i, j; if (mkfn_dry) return; for (i = 0; i < nlookups; i++) { void *lookup = lookuplist + U16(lookuplist, 2 + 2 * lookups[i].lookup); int ltype = U16(lookup, 0); int ntabs = U16(lookup, 4); char *tag = lookuptag(&lookups[i]); printf("gsec %s\n", tag); for (j = 0; j < ntabs; j++) { void *tab = lookup + U16(lookup, 6 + 2 * j); int type = ltype; if (type == 9) { /* extension positioning */ type = U16(tab, 2); tab = tab + U32(tab, 4); } switch (type) { case 1: otf_gpostype1(otf, tab, tag); break; case 2: otf_gpostype2(otf, tab, tag); break; case 3: otf_gpostype3(otf, tab, tag); break; case 4: otf_gpostype4(otf, tab, tag); break; default: otf_unsupported("GPOS", type, 0); } } } } static void otf_gsub(struct otf *otf, void *gsub) { struct otflookup lookups[NLOOKUPS]; void *lookuplist = gsub + U16(gsub, 8); int nlookups = otf_gtab(otf, gsub, lookups); int i, j; if (mkfn_dry) return; for (i = 0; i < nlookups; i++) { void *lookup = lookuplist + U16(lookuplist, 2 + 2 * lookups[i].lookup); int ltype = U16(lookup, 0); int ntabs = U16(lookup, 4); char *tag = lookuptag(&lookups[i]); printf("gsec %s\n", tag); for (j = 0; j < ntabs; j++) { void *tab = lookup + U16(lookup, 6 + 2 * j); int type = ltype; if (type == 7) { /* extension substitution */ type = U16(tab, 2); tab = tab + U32(tab, 4); } switch (type) { case 1: otf_gsubtype1(otf, tab, tag, NULL); break; case 3: otf_gsubtype3(otf, tab, tag, NULL); break; case 4: otf_gsubtype4(otf, tab, tag, NULL); break; case 6: otf_gsubtype6(otf, tab, tag, gsub); break; default: otf_unsupported("GSUB", type, 0); } } } } /* 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)); } /* obtain the value of the given key from a cff dict */ static int cffdict_get(void *dict, int len, int key, int *args) { int off = 0; int op = 0; int val = 0; /* operators: keys (one or two bytes); operands: values */ while (off < len) { val = op; if (args) { memmove(args + 1, args + 0, 3 * sizeof(args[0])); args[0] = val; } 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); if (len >= GNLEN) len = GNLEN - 1; memcpy(dst, cffidx_get(stridx, id), len); dst[len] = '\0'; } static void otf_cff(struct otf *otf, void *cff) { void *nameidx; /* name index */ void *topidx; /* top dict index */ void *stridx; /* string idx */ void *chridx; /* charstrings index */ void *charset; /* charset offset */ int badcff; /* invalid CFF SIDs */ 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, NULL); charset = cff + cffdict_get(cffidx_get(topidx, 0), cffidx_len(topidx, 0), 15, NULL); glyph_n = cffidx_cnt(chridx); badcff = cffidx_cnt(chridx) - 391 > cffidx_cnt(stridx); strcpy(glyph_name[0], ".notdef"); /* read font name from name index */ if (cffidx_cnt(nameidx) > 0) { char name[256] = ""; memcpy(name, cffidx_get(nameidx, 0), cffidx_len(nameidx, 0)); if (name[0] && !otf->name[0]) snprintf(otf->name, sizeof(otf->name), "%s", name); } /* read charset: glyph to character name */ if (!badcff && U8(charset, 0) == 0) { for (i = 0; i < glyph_n; i++) cff_char(stridx, U16(charset, 1 + i * 2), glyph_name[i + 1]); } if (!badcff && (U8(charset, 0) == 1 || U8(charset, 0) == 2)) { int g = 1; int sz = U8(charset, 0) == 1 ? 3 : 4; for (i = 0; g < glyph_n; i++) { int sid = U16(charset, 1 + i * sz); int cnt = cff_int(charset, 1 + i * sz + 2, sz - 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(); char buf[1 << 12]; int nr = 0; while ((nr = read(fd, buf, sizeof(buf))) > 0) sbuf_mem(sb, buf, nr); return sbuf_done(sb); } static void otf_feat(struct otf *otf) { if (otf_table(otf, "GSUB")) otf_gsub(otf, otf_table(otf, "GSUB")); if (otf_table(otf, "GPOS")) otf_gpos(otf, otf_table(otf, "GPOS")); } int otf_offsettable(void *otf_otf, void *otf_off) { int i; unsigned tag = U32(otf_off, 0); struct otf otf_cur = {otf_otf, otf_off}; struct otf *otf = &otf_cur; if (tag != 0x00010000 && tag != 0x4F54544F) return 1; upm = U16(otf_table(otf, "head"), 18); otf_name(otf, otf_table(otf, "name")); otf_cmap(otf, otf_table(otf, "cmap")); otf_post(otf, otf_table(otf, "post")); if (otf_table(otf, "glyf")) otf_glyf(otf, otf_table(otf, "glyf")); if (otf_table(otf, "CFF ")) otf_cff(otf, otf_table(otf, "CFF ")); for (i = 0; i < glyph_n; i++) { if (!glyph_name[i][0]) { if (glyph_code[i]) sprintf(glyph_name[i], "uni%04X", glyph_code[i]); else sprintf(glyph_name[i], "gl%05X", i); } } otf_hmtx(otf, otf_table(otf, "hmtx")); if (!mkfn_font(otf->name)) return 0; for (i = 0; i < glyph_n; i++) { mkfn_char(glyph_name[i], -1, glyph_code[i] != 0xffff ? glyph_code[i] : 0, uwid(glyph_wid[i]), uwid(glyph_bbox[i][0]), uwid(glyph_bbox[i][1]), uwid(glyph_bbox[i][2]), uwid(glyph_bbox[i][3])); } mkfn_header(otf->name); if (otf_table(otf, "kern")) otf_kern(otf, otf_table(otf, "kern")); otf_feat(otf); return 0; } int otf_read(void) { char *otf_buf = otf_input(0); unsigned tag = U32(otf_buf, 0); int n, i; if (tag == 0x00010000 || tag == 0x4F54544F) return otf_offsettable(otf_buf, otf_buf); if (tag != 0x74746366) return 1; /* OpenType Collections */ n = U32(otf_buf, 8); for (i = 0; i < n; i++) { void *off = otf_buf + U32(otf_buf, 12 + i * 4); otf_offsettable(otf_buf, off); } return 0; } /* glyph groups */ static int *ggrp_g[NGRPS]; static int ggrp_len[NGRPS]; static int ggrp_n; static int ggrp_find(int *src, int n) { int i, j; for (i = 0; i < ggrp_n; i++) { if (ggrp_len[i] == n) { for (j = 0; j < n; j++) if (src[j] != ggrp_g[i][j]) break; if (j == n) return i; } } return -1; } static int ggrp_make(int *src, int n) { int id = ggrp_find(src, n); int i; if (id >= 0) return id; id = ggrp_n++; ggrp_g[id] = malloc(n * sizeof(ggrp_g[id][0])); ggrp_len[id] = n; for (i = 0; i < n; i++) ggrp_g[id][i] = src[i]; printf("ggrp %d %d", id, n); for (i = 0; i < n; i++) printf(" %s", glyph_name[src[i]]); printf("\n"); return id; } static char *macset[] = { ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "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", "grave", "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", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf", "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", };