Continue ESM transform
This commit is contained in:
parent
b8205a63aa
commit
36835216f5
44 changed files with 1939 additions and 502 deletions
|
@ -1,11 +1,13 @@
|
|||
import { CreateEmptyFont } from "./empty-font.mjs";
|
||||
import { buildGlyphs } from "../glyphs/index.mjs";
|
||||
import { copyFontMetrics } from "../meta/aesthetics.mjs";
|
||||
import { assignFontNames } from "../meta/naming.mjs";
|
||||
import { buildOtl } from "../otl/index.mjs";
|
||||
|
||||
import * as Caching from "./caching/index.mjs";
|
||||
import { CreateEmptyFont } from "./empty-font.mjs";
|
||||
import { finalizeFont } from "./finalize/index.mjs";
|
||||
import { convertOtd } from "./otd-conv/index.mjs";
|
||||
import * as Caching from "./caching/index.mjs";
|
||||
import { buildOtl } from "../otl/index.mjs";
|
||||
import { assignFontNames } from "../meta/naming.mjs";
|
||||
import { copyFontMetrics } from "../meta/aesthetics.mjs";
|
||||
|
||||
("use strict");
|
||||
export async function buildFont(argv, para) {
|
||||
const gs = buildGlyphs(para);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import fs from "fs";
|
||||
import zlib from "zlib";
|
||||
|
||||
import { encode, decode } from "@msgpack/msgpack";
|
||||
|
||||
const Edition = 20;
|
||||
|
@ -75,8 +76,13 @@ class Cache {
|
|||
export const load = async function (path, version, freshAgeKey) {
|
||||
let cache = new Cache(freshAgeKey);
|
||||
if (path && fs.existsSync(path)) {
|
||||
const buf = zlib.gunzipSync(await fs.promises.readFile(path));
|
||||
cache.loadRep(version, decode(buf));
|
||||
try {
|
||||
const buf = zlib.gunzipSync(await fs.promises.readFile(path));
|
||||
cache.loadRep(version, decode(buf));
|
||||
} catch (e) {
|
||||
console.error("Error loading cache. Treat as empty.");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import * as TypoGeom from "typo-geom";
|
||||
|
||||
import * as CurveUtil from "../../support/geometry/curve-util.mjs";
|
||||
import * as Geom from "../../support/geometry/index.mjs";
|
||||
import { Point } from "../../support/geometry/point.mjs";
|
||||
import { Transform } from "../../support/geometry/transform.mjs";
|
||||
import * as CurveUtil from "../../support/geometry/curve-util.mjs";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
function regulateGlyphStore(cache, skew, glyphStore) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { finalizeGlyphs } from "./glyphs.mjs";
|
||||
import { gcFont } from "./gc.mjs";
|
||||
import { Nwid, Wwid } from "../../support/gr.mjs";
|
||||
|
||||
import { gcFont } from "./gc.mjs";
|
||||
import { finalizeGlyphs } from "./glyphs.mjs";
|
||||
|
||||
function assignGrAndCodeRank(glyphStore, ...flatteners) {
|
||||
for (const g of glyphStore.glyphs()) {
|
||||
g.codeRank = 0xffffffff;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Joining, AnyCv, TieMark, Nwid, Wwid } from "../../support/gr.mjs";
|
||||
|
||||
const ApplePostNames = new Map([
|
||||
/* spell-checker: disable */
|
||||
[0xd, "nonmarkingreturn"],
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { Ot } from "ot-builder";
|
||||
|
||||
import { Point } from "../../support/geometry/point.mjs";
|
||||
import * as Gr from "../../support/gr.mjs";
|
||||
|
||||
import { byCode, bySpacing, byGr, byBuildOrder } from "./glyph-name.mjs";
|
||||
|
||||
function byRank([gna, a], [gnb, b]) {
|
||||
return (
|
||||
b.glyphRank - a.glyphRank ||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { convertGlyphs } from "./glyphs.mjs";
|
||||
import { convertGsub, convertGpos, convertGdef } from "./layout.mjs";
|
||||
|
||||
export function convertOtd(baseFont, otl, gs) {
|
||||
const { glyphs, cmap } = convertGlyphs(gs);
|
||||
const gsub = convertGsub(otl.GSUB, glyphs);
|
||||
|
|
|
@ -1,339 +1,311 @@
|
|||
import { Ot } from "ot-builder";
|
||||
class LookupStore {
|
||||
constructor(handlers, glyphs) {
|
||||
this.glyphs = glyphs;
|
||||
this.m_handlers = handlers;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
declare(id, otdLookup) {
|
||||
if (this.m_mapping.has(id))
|
||||
return;
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!handler)
|
||||
return;
|
||||
this.m_mapping.set(id, handler.init());
|
||||
}
|
||||
fill(id, otdLookup) {
|
||||
const dst = this.query(id);
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!dst || !handler)
|
||||
return;
|
||||
if (otdLookup.subtables)
|
||||
throw new Error("Unreachable.");
|
||||
handler.fill(dst, otdLookup, this);
|
||||
}
|
||||
}
|
||||
const GsubSingleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Single();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = store.glyphs.queryByName(st[k]);
|
||||
if (from && to)
|
||||
dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubMultipleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Multiple();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = mapGlyphListAll(st[k], store);
|
||||
if (!from || !to)
|
||||
continue;
|
||||
dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubAlternateHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Alternate();
|
||||
},
|
||||
fill: GsubMultipleHandler.fill
|
||||
};
|
||||
const GsubLigatureHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Ligature();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const { from: _from, to: _to } of st) {
|
||||
const to = store.glyphs.queryByName(_to);
|
||||
const from = mapGlyphListAll(_from, store);
|
||||
if (!from || !to)
|
||||
continue;
|
||||
dst.mapping.push({ from, to });
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubChainingHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Chaining();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.rules) {
|
||||
const match = [];
|
||||
for (const m of st.match) {
|
||||
const m1 = mapGlyphListSome(m, store);
|
||||
if (!m1)
|
||||
continue out;
|
||||
match.push(new Set(m1));
|
||||
}
|
||||
const inputBegins = st.inputBegins;
|
||||
const inputEnds = st.inputEnds;
|
||||
const applications = [];
|
||||
for (const ap of st.apply) {
|
||||
const lookup = store.query(ap.lookup);
|
||||
if (!lookup)
|
||||
continue out;
|
||||
applications.push({ at: ap.at - inputBegins, apply: lookup });
|
||||
}
|
||||
dst.rules.push({ match, inputBegins, inputEnds, applications });
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubReverseHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.ReverseSub();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.rules) {
|
||||
const match = [];
|
||||
const doSubAt = st.inputIndex;
|
||||
const replacement = new Map();
|
||||
for (let j = 0; j < st.match.length; j++) {
|
||||
{
|
||||
const m1 = new Set();
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
if (gFrom)
|
||||
m1.add(gFrom);
|
||||
}
|
||||
if (!m1.size)
|
||||
continue out;
|
||||
match.push(m1);
|
||||
}
|
||||
if (j === doSubAt) {
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
const gTo = store.glyphs.queryByName(st.to[k]);
|
||||
if (!gFrom)
|
||||
continue;
|
||||
if (gTo) {
|
||||
replacement.set(gFrom, gTo);
|
||||
}
|
||||
else {
|
||||
replacement.set(gFrom, gFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.rules.push({ match, doSubAt, replacement });
|
||||
}
|
||||
}
|
||||
};
|
||||
function mapGlyphListAll(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg)
|
||||
return null;
|
||||
out.push(fg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function mapGlyphListSome(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg)
|
||||
continue;
|
||||
out.push(fg);
|
||||
}
|
||||
if (!out.length)
|
||||
return null;
|
||||
return out;
|
||||
}
|
||||
const GsubHandlers = {
|
||||
gsub_single: GsubSingleHandler,
|
||||
gsub_multiple: GsubMultipleHandler,
|
||||
gsub_alternate: GsubAlternateHandler,
|
||||
gsub_ligature: GsubLigatureHandler,
|
||||
gsub_chaining: GsubChainingHandler,
|
||||
gsub_reverse: GsubReverseHandler
|
||||
};
|
||||
const GposMarkToBaseHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToBase();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const mm = collectClassMap(src.marks);
|
||||
dst.marks = convertMarkRecords(src.marks, mm, store);
|
||||
dst.bases = convertBaseRecords(src.bases, mm, store);
|
||||
}
|
||||
};
|
||||
const GposMarkToMarkHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToMark();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const mm = collectClassMap(src.marks);
|
||||
dst.marks = convertMarkRecords(src.marks, mm, store);
|
||||
dst.baseMarks = convertBaseRecords(src.bases, mm, store);
|
||||
}
|
||||
};
|
||||
function collectClassMap(marks) {
|
||||
let n = 0;
|
||||
const m = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
if (!m.has(mark.class)) {
|
||||
m.set(mark.class, n);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
function convertMarkRecords(marks, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g)
|
||||
continue;
|
||||
let markAnchors = [];
|
||||
markAnchors[mm.get(mark.class)] = { x: mark.x, y: mark.y };
|
||||
out.set(g, { markAnchors: markAnchors });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function convertBaseRecords(bases, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in bases) {
|
||||
const baseObj = bases[gn];
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g)
|
||||
continue;
|
||||
const baseArray = [];
|
||||
for (const bkStr in baseObj) {
|
||||
baseArray[mm.get(bkStr)] = baseObj[bkStr];
|
||||
}
|
||||
out.set(g, { baseAnchors: baseArray });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const GposHandlers = {
|
||||
gpos_mark_to_base: GposMarkToBaseHandler,
|
||||
gpos_mark_to_mark: GposMarkToMarkHandler
|
||||
};
|
||||
class FeatureStore {
|
||||
constructor(lookups) {
|
||||
this.lookupStore = lookups;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
fill(id, data) {
|
||||
const tag = id.slice(0, 4);
|
||||
const lookups = [];
|
||||
for (const lid of data) {
|
||||
const lookup = this.lookupStore.query(lid);
|
||||
if (lookup)
|
||||
lookups.push(lookup);
|
||||
}
|
||||
this.m_mapping.set(id, { tag, lookups });
|
||||
}
|
||||
}
|
||||
class ScriptLanguageStore {
|
||||
constructor(features) {
|
||||
this.featureStore = features;
|
||||
this.m_scriptMapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return this.m_scriptMapping;
|
||||
}
|
||||
fill(id, data) {
|
||||
const scriptTag = id.slice(0, 4);
|
||||
const languageTag = id.slice(5, 9).padEnd(4);
|
||||
let sr = this.m_scriptMapping.get(scriptTag);
|
||||
if (!sr) {
|
||||
sr = { defaultLanguage: null, languages: new Map() };
|
||||
this.m_scriptMapping.set(scriptTag, sr);
|
||||
}
|
||||
const lr = this.createLanguageRecord(data);
|
||||
if (languageTag === "dflt" || languageTag === "DFLT")
|
||||
sr.defaultLanguage = lr;
|
||||
else
|
||||
sr.languages.set(languageTag, lr);
|
||||
}
|
||||
createLanguageRecord(data) {
|
||||
const features = [];
|
||||
for (const fid of data.features) {
|
||||
const feature = this.featureStore.query(fid);
|
||||
if (feature)
|
||||
features.push(feature);
|
||||
}
|
||||
return {
|
||||
requiredFeature: this.featureStore.query(data.requiredFeature) || null,
|
||||
features: features
|
||||
};
|
||||
}
|
||||
}
|
||||
function ConvertGsubGposT(handlers, T) {
|
||||
return function (table, glyphs) {
|
||||
if (!table)
|
||||
return null;
|
||||
const ls = new LookupStore(handlers, glyphs);
|
||||
if (table.lookups) {
|
||||
if (table.lookupOrder) {
|
||||
for (const l of table.lookupOrder)
|
||||
ls.declare(l, table.lookups[l]);
|
||||
}
|
||||
for (const l in table.lookups)
|
||||
ls.declare(l, table.lookups[l]);
|
||||
for (const l in table.lookups)
|
||||
ls.fill(l, table.lookups[l]);
|
||||
}
|
||||
const fs = new FeatureStore(ls);
|
||||
if (table.features) {
|
||||
for (const f in table.features)
|
||||
fs.fill(f, table.features[f]);
|
||||
}
|
||||
const ss = new ScriptLanguageStore(fs);
|
||||
if (table.languages) {
|
||||
for (const sl in table.languages)
|
||||
ss.fill(sl, table.languages[sl]);
|
||||
}
|
||||
return new T(ss.extract(), fs.extract(), ls.extract());
|
||||
};
|
||||
}
|
||||
function convertGdef(otdGdef, glyphs) {
|
||||
const gdef = new Ot.Gdef.Table();
|
||||
gdef.glyphClassDef = new Map();
|
||||
for (const gn in otdGdef.glyphClassDef) {
|
||||
const g = glyphs.queryByName(gn);
|
||||
if (g)
|
||||
gdef.glyphClassDef.set(g, otdGdef.glyphClassDef[gn]);
|
||||
}
|
||||
return gdef;
|
||||
}
|
||||
export const convertGsub = ConvertGsubGposT(GsubHandlers, Ot.Gsub.Table);
|
||||
export const convertGpos = ConvertGsubGposT(GposHandlers, Ot.Gpos.Table);
|
||||
export { convertGdef };
|
||||
import { Ot } from "ot-builder";
|
||||
|
||||
class LookupStore {
|
||||
constructor(handlers, glyphs) {
|
||||
this.glyphs = glyphs;
|
||||
this.m_handlers = handlers;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
declare(id, otdLookup) {
|
||||
if (this.m_mapping.has(id)) return;
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!handler) return;
|
||||
this.m_mapping.set(id, handler.init());
|
||||
}
|
||||
fill(id, otdLookup) {
|
||||
const dst = this.query(id);
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!dst || !handler) return;
|
||||
if (otdLookup.subtables) throw new Error("Unreachable.");
|
||||
handler.fill(dst, otdLookup, this);
|
||||
}
|
||||
}
|
||||
const GsubSingleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Single();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = store.glyphs.queryByName(st[k]);
|
||||
if (from && to) dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubMultipleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Multiple();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = mapGlyphListAll(st[k], store);
|
||||
if (!from || !to) continue;
|
||||
dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubAlternateHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Alternate();
|
||||
},
|
||||
fill: GsubMultipleHandler.fill
|
||||
};
|
||||
const GsubLigatureHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Ligature();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const { from: _from, to: _to } of st) {
|
||||
const to = store.glyphs.queryByName(_to);
|
||||
const from = mapGlyphListAll(_from, store);
|
||||
if (!from || !to) continue;
|
||||
dst.mapping.push({ from, to });
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubChainingHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Chaining();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.rules) {
|
||||
const match = [];
|
||||
for (const m of st.match) {
|
||||
const m1 = mapGlyphListSome(m, store);
|
||||
if (!m1) continue out;
|
||||
match.push(new Set(m1));
|
||||
}
|
||||
const inputBegins = st.inputBegins;
|
||||
const inputEnds = st.inputEnds;
|
||||
const applications = [];
|
||||
for (const ap of st.apply) {
|
||||
const lookup = store.query(ap.lookup);
|
||||
if (!lookup) continue out;
|
||||
applications.push({ at: ap.at - inputBegins, apply: lookup });
|
||||
}
|
||||
dst.rules.push({ match, inputBegins, inputEnds, applications });
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubReverseHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.ReverseSub();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.rules) {
|
||||
const match = [];
|
||||
const doSubAt = st.inputIndex;
|
||||
const replacement = new Map();
|
||||
for (let j = 0; j < st.match.length; j++) {
|
||||
{
|
||||
const m1 = new Set();
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
if (gFrom) m1.add(gFrom);
|
||||
}
|
||||
if (!m1.size) continue out;
|
||||
match.push(m1);
|
||||
}
|
||||
if (j === doSubAt) {
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
const gTo = store.glyphs.queryByName(st.to[k]);
|
||||
if (!gFrom) continue;
|
||||
if (gTo) {
|
||||
replacement.set(gFrom, gTo);
|
||||
} else {
|
||||
replacement.set(gFrom, gFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.rules.push({ match, doSubAt, replacement });
|
||||
}
|
||||
}
|
||||
};
|
||||
function mapGlyphListAll(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg) return null;
|
||||
out.push(fg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function mapGlyphListSome(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg) continue;
|
||||
out.push(fg);
|
||||
}
|
||||
if (!out.length) return null;
|
||||
return out;
|
||||
}
|
||||
const GsubHandlers = {
|
||||
gsub_single: GsubSingleHandler,
|
||||
gsub_multiple: GsubMultipleHandler,
|
||||
gsub_alternate: GsubAlternateHandler,
|
||||
gsub_ligature: GsubLigatureHandler,
|
||||
gsub_chaining: GsubChainingHandler,
|
||||
gsub_reverse: GsubReverseHandler
|
||||
};
|
||||
const GposMarkToBaseHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToBase();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const mm = collectClassMap(src.marks);
|
||||
dst.marks = convertMarkRecords(src.marks, mm, store);
|
||||
dst.bases = convertBaseRecords(src.bases, mm, store);
|
||||
}
|
||||
};
|
||||
const GposMarkToMarkHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToMark();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const mm = collectClassMap(src.marks);
|
||||
dst.marks = convertMarkRecords(src.marks, mm, store);
|
||||
dst.baseMarks = convertBaseRecords(src.bases, mm, store);
|
||||
}
|
||||
};
|
||||
function collectClassMap(marks) {
|
||||
let n = 0;
|
||||
const m = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
if (!m.has(mark.class)) {
|
||||
m.set(mark.class, n);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
function convertMarkRecords(marks, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g) continue;
|
||||
let markAnchors = [];
|
||||
markAnchors[mm.get(mark.class)] = { x: mark.x, y: mark.y };
|
||||
out.set(g, { markAnchors: markAnchors });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function convertBaseRecords(bases, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in bases) {
|
||||
const baseObj = bases[gn];
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g) continue;
|
||||
const baseArray = [];
|
||||
for (const bkStr in baseObj) {
|
||||
baseArray[mm.get(bkStr)] = baseObj[bkStr];
|
||||
}
|
||||
out.set(g, { baseAnchors: baseArray });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const GposHandlers = {
|
||||
gpos_mark_to_base: GposMarkToBaseHandler,
|
||||
gpos_mark_to_mark: GposMarkToMarkHandler
|
||||
};
|
||||
class FeatureStore {
|
||||
constructor(lookups) {
|
||||
this.lookupStore = lookups;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
fill(id, data) {
|
||||
const tag = id.slice(0, 4);
|
||||
const lookups = [];
|
||||
for (const lid of data) {
|
||||
const lookup = this.lookupStore.query(lid);
|
||||
if (lookup) lookups.push(lookup);
|
||||
}
|
||||
this.m_mapping.set(id, { tag, lookups });
|
||||
}
|
||||
}
|
||||
class ScriptLanguageStore {
|
||||
constructor(features) {
|
||||
this.featureStore = features;
|
||||
this.m_scriptMapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return this.m_scriptMapping;
|
||||
}
|
||||
fill(id, data) {
|
||||
const scriptTag = id.slice(0, 4);
|
||||
const languageTag = id.slice(5, 9).padEnd(4);
|
||||
let sr = this.m_scriptMapping.get(scriptTag);
|
||||
if (!sr) {
|
||||
sr = { defaultLanguage: null, languages: new Map() };
|
||||
this.m_scriptMapping.set(scriptTag, sr);
|
||||
}
|
||||
const lr = this.createLanguageRecord(data);
|
||||
if (languageTag === "dflt" || languageTag === "DFLT") sr.defaultLanguage = lr;
|
||||
else sr.languages.set(languageTag, lr);
|
||||
}
|
||||
createLanguageRecord(data) {
|
||||
const features = [];
|
||||
for (const fid of data.features) {
|
||||
const feature = this.featureStore.query(fid);
|
||||
if (feature) features.push(feature);
|
||||
}
|
||||
return {
|
||||
requiredFeature: this.featureStore.query(data.requiredFeature) || null,
|
||||
features: features
|
||||
};
|
||||
}
|
||||
}
|
||||
function ConvertGsubGposT(handlers, T) {
|
||||
return function (table, glyphs) {
|
||||
if (!table) return null;
|
||||
const ls = new LookupStore(handlers, glyphs);
|
||||
if (table.lookups) {
|
||||
if (table.lookupOrder) {
|
||||
for (const l of table.lookupOrder) ls.declare(l, table.lookups[l]);
|
||||
}
|
||||
for (const l in table.lookups) ls.declare(l, table.lookups[l]);
|
||||
for (const l in table.lookups) ls.fill(l, table.lookups[l]);
|
||||
}
|
||||
const fs = new FeatureStore(ls);
|
||||
if (table.features) {
|
||||
for (const f in table.features) fs.fill(f, table.features[f]);
|
||||
}
|
||||
const ss = new ScriptLanguageStore(fs);
|
||||
if (table.languages) {
|
||||
for (const sl in table.languages) ss.fill(sl, table.languages[sl]);
|
||||
}
|
||||
return new T(ss.extract(), fs.extract(), ls.extract());
|
||||
};
|
||||
}
|
||||
function convertGdef(otdGdef, glyphs) {
|
||||
const gdef = new Ot.Gdef.Table();
|
||||
gdef.glyphClassDef = new Map();
|
||||
for (const gn in otdGdef.glyphClassDef) {
|
||||
const g = glyphs.queryByName(gn);
|
||||
if (g) gdef.glyphClassDef.set(g, otdGdef.glyphClassDef[gn]);
|
||||
}
|
||||
return gdef;
|
||||
}
|
||||
export const convertGsub = ConvertGsubGposT(GsubHandlers, Ot.Gsub.Table);
|
||||
export const convertGpos = ConvertGsubGposT(GposHandlers, Ot.Gpos.Table);
|
||||
export { convertGdef };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue