135 lines
3.6 KiB
JavaScript
135 lines
3.6 KiB
JavaScript
const { Ot } = require("ot-builder");
|
|
const Point = require("../../support/point");
|
|
const { nameSingleGlyph1, nameSingleGlyph2, nameSingleGlyph3 } = require("./glyph-name");
|
|
|
|
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);
|
|
for (const [gSrc, gOt] of this.m_mapping) gOt.name = undefined;
|
|
|
|
// Name by Unicode
|
|
gid = 0;
|
|
for (const [gSrc, gOt] of this.m_mapping) {
|
|
gOt.name = nameSingleGlyph1(gid, gSrc, rev.get(gSrc), conflictSet);
|
|
gid++;
|
|
}
|
|
// Name by CV
|
|
for (const [gSrcBase, gOtBase] of this.m_mapping) {
|
|
nameSingleGlyph2(gSrcBase, gOtBase.name, this.m_nameMapping, conflictSet);
|
|
}
|
|
// Name rest
|
|
gid = 0;
|
|
for (const [gSrc, gOt] of this.m_mapping) {
|
|
gOt.name = nameSingleGlyph3(gid, gSrc, gOt.name);
|
|
gid++;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
module.exports = convertGlyphs;
|
|
function convertGlyphs(gsOrig) {
|
|
const sortedEntries = Array.from(gsOrig.indexedNamedEntries())
|
|
.map(([j, gn, g]) => [j, gn, queryOrderingUnicode(gsOrig, g), g])
|
|
.sort(byRank);
|
|
|
|
const gs = new MappedGlyphStore();
|
|
const cmap = new Ot.Cmap.Table();
|
|
|
|
for (const [origIndex, name, uOrd, 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 [origIndex, name, uOrd, gSrc] of sortedEntries) gs.fill(name, gSrc);
|
|
gs.fillOtGlyphNames();
|
|
return { glyphs: gs, cmap };
|
|
}
|
|
function queryOrderingUnicode(gs, g) {
|
|
const us = gs.queryUnicodeOf(g);
|
|
if (us && us.size) return Array.from(us).sort((a, b) => a - b)[0];
|
|
else return 0xffffff;
|
|
}
|
|
function byRank([ja, gna, ua, a], [jb, gnb, ub, b]) {
|
|
return (
|
|
(b.glyphRank || 0) - (a.glyphRank || 0) || (ua || 0) - (ub || 0) || (ja || 0) - (jb || 0)
|
|
);
|
|
}
|