Iosevka/font-src/gen/otd-conv/glyphs.js
2021-07-28 20:13:04 -07:00

154 lines
3.9 KiB
JavaScript

const { Ot } = require("ot-builder");
const { Point } = require("../../support/geometry/point");
const Gr = require("../../support/gr");
const { byCode, bySpacing, byGr, byBuildOrder } = require("./glyph-name");
exports.convertGlyphs = function convertGlyphs(gsOrig) {
const sortedEntries = Array.from(gsOrig.namedEntries(Gr.Nwid, Gr.Wwid)).sort(byRank);
const gs = new MappedGlyphStore();
const cmap = new Ot.Cmap.Table();
for (const [name, gSrc] of sortedEntries) {
gs.declare(name, gSrc);
const us = gsOrig.queryUnicodeOf(gSrc);
if (us) {
for (const u of us) {
if (!(isFinite(u - 0) && u)) continue;
cmap.unicode.set(u, gs.queryBySourceGlyph(gSrc));
gs.setPrimaryUnicode(gSrc, u);
}
}
}
for (const [name, gSrc] of sortedEntries) gs.fill(name, gSrc);
gs.fillOtGlyphNames();
return { glyphs: gs, cmap };
};
function byRank([gna, a], [gnb, b]) {
return (
b.glyphRank - a.glyphRank ||
a.grRank - b.grRank ||
a.codeRank - b.codeRank ||
a.subRank - b.subRank
);
}
class MappedGlyphStore {
constructor() {
this.m_nameMapping = new Map();
this.m_mapping = new Map();
this.m_primaryUnicodeMapping = new Map();
}
declare(name, source) {
const g = new Ot.Glyph();
this.m_nameMapping.set(name, g);
this.m_mapping.set(source, g);
}
setPrimaryUnicode(source, u) {
this.m_primaryUnicodeMapping.set(u, source);
}
queryBySourceGlyph(source) {
return this.m_mapping.get(source);
}
queryByName(name) {
return this.m_nameMapping.get(name);
}
decideOrder() {
const gs = Ot.ListGlyphStoreFactory.createStoreFromList([...this.m_mapping.values()]);
return gs.decideOrder();
}
fill(name, source) {
const g = this.queryBySourceGlyph(source);
if (!g) throw new Error("Unreachable");
// Fill metrics
g.horizontal = { start: 0, end: source.advanceWidth };
// Fill Geometry
if (source.geometry.isEmpty()) return;
const rs = source.geometry.asReferences();
if (rs) {
this.fillReferences(g, rs);
} else {
this.fillContours(g, source.geometry.asContours());
}
}
fillOtGlyphNames() {
let gid = 0;
let conflictSet = new Set();
let rev = new Map();
for (const [u, g] of this.m_primaryUnicodeMapping) rev.set(g, u);
const glyphsInBuildOrder = Array.from(this.m_mapping).sort(
([a], [b]) => a.subRank - b.subRank
);
for (const [gSrc, gOt] of glyphsInBuildOrder) gOt.name = undefined;
// Name by Unicode
for (const [gSrc, gOt] of glyphsInBuildOrder) {
gOt.name = byCode(gSrc, rev.get(gSrc), conflictSet);
}
// Name by NWID/WWID
let nNewNames = 0;
do {
nNewNames = 0;
for (const [gSrcBase, gOtBase] of glyphsInBuildOrder) {
nNewNames += bySpacing(gSrcBase, gOtBase, this.m_nameMapping, conflictSet);
}
} while (nNewNames > 0);
// Name by Gr
do {
nNewNames = 0;
for (const [gSrcBase, gOtBase] of glyphsInBuildOrder) {
nNewNames += byGr(gSrcBase, gOtBase, this.m_nameMapping, conflictSet);
}
} while (nNewNames > 0);
// Name rest
for (const [gSrc, gOt] of glyphsInBuildOrder) {
gOt.name = byBuildOrder(gSrc.subRank, gSrc, gOt.name);
}
// validate
{
let gnSet = new Set();
for (const [gSrc, gOt] of this.m_mapping) {
if (gnSet.has(gOt.name)) throw new Error("Unreachable! duplicate name " + gOt.name);
gnSet.add(gOt.name);
}
}
}
fillReferences(g, rs) {
const gl = new Ot.Glyph.GeometryList();
for (const ref of rs) {
const target = this.queryBySourceGlyph(ref.glyph);
if (!target) throw new Error("Unreachable");
const tfm = Ot.Glyph.Transform2X3.Translate(ref.x, ref.y);
gl.items.push(new Ot.Glyph.TtReference(target, tfm));
}
g.geometry = gl;
}
fillContours(g, contours) {
const cs = new Ot.Glyph.ContourSet();
for (const c of contours) {
const c1 = [];
for (const z of c) {
c1.push(
Ot.Glyph.Point.create(
z.x,
z.y,
z.type === Point.Type.Quadratic
? Ot.Glyph.PointType.Quad
: Ot.Glyph.PointType.Corner
)
);
}
cs.contours.push(c1);
}
g.geometry = cs;
}
}