Refactor: Create a separate class for glyph store

This commit is contained in:
be5invis 2020-08-09 18:11:36 -07:00
parent 02e6d041be
commit 4d20f8e655
25 changed files with 482 additions and 360 deletions

View file

@ -15,7 +15,7 @@ module.exports = function (para) {
assignFontNames(para, gs.metrics, font);
setFontMetrics(para, gs.metrics, font);
const otl = buildOtl(para, gs.glyphs, gs.glyphList, gs.unicodeGlyphs);
const otl = buildOtl(para, gs.glyphStore);
font.GSUB = otl.GSUB;
font.GPOS = otl.GPOS;
font.GDEF = otl.GDEF;
@ -28,6 +28,6 @@ module.exports = function (para) {
}
}
finalizeFont(para, [...gs.glyphList], excludeChars, font);
return font;
finalizeFont(para, gs.glyphStore, excludeChars, font);
return { font, glyphStore: gs.glyphStore };
};

View file

@ -1,4 +1,5 @@
import '../support/glyph' as Glyph
import '../support/glyph-store' as GlyphStore
import '../support/point' as Point
import './kits/spiro-kit' as spirokit
import './kits/boole-kit' as BooleKit
@ -25,9 +26,7 @@ define [tagged tag component] : begin
export all : define [buildGlyphs para recursive recursiveCodes] : begin
define variantSelector para.variantSelector
local glyphList {}
local glyphMap {.}
local unicodeGlyphs {}
local glyphStore : new GlyphStore
define metrics : calculateMetrics para
define [object GlobalTransform UPM Middle CAP XH SB RightSB Contrast Stroke Superness Width TanSlope OverlayPos Descender SymbolMid ParenTop ParenBot OperTop OperBot PlusTop PlusBot TackTop TackBot adviceBlackness MVertStrokeD] metrics
@ -89,11 +88,11 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
warnAboutBrokenGlyph glyphObject ensuredGlyphName
if saveGlyphName : begin
glyphList.push glyphObject
if (saveGlyphName.0 != '.' && glyphMap.(saveGlyphName))
if (saveGlyphName.0 != '.' && [glyphStore.queryByName saveGlyphName])
throw : new Error "Glyph \(saveGlyphName) already exists"
glyphMap.(saveGlyphName) = glyphObject
glyphStore.addGlyph saveGlyphName glyphObject
if unicode : $assignUnicodeImpl$ glyphObject unicode
set dependencyProfile.(saveGlyphName) : getDependencyProfile glyphObject
dec nPending
@ -113,8 +112,10 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
console.log 'Family' para.naming.family para.naming.weight para.naming.width para.naming.slope
define [$assignUnicodeImpl$ g unicode] : begin
g.assignUnicode unicode
set unicodeGlyphs.(g.unicode.((g.unicode.length - 1))) g
local u unicode
if ([typeof unicode] === "string") : begin
set u [unicode.codePointAt 0]
glyphStore.encodeGlyph u g
### Spiro constructions
# Basic knots
@ -122,7 +123,7 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
define booleFns : BooleKit.SetupBuilders : object GlobalTransform Glyph
# IDKY, but wrapping "metrics" prevents Node.js on Arch modifying it.
define $$Capture$$ : object [metrics : Object.create metrics] $NamedParameterPair$ $donothing$ para recursive recursiveCodes variantSelector glyphMap glyphList unicodeGlyphs $createAndSaveGlyphImpl$ spirofns booleFns MarkSet AS_BASE ALSO_METRICS pickHash dependencyProfile getDependencyProfile buildGlyphs newtemp tagged DivFrame fontMetrics $assignUnicodeImpl$
define $$Capture$$ : object [metrics : Object.create metrics] $NamedParameterPair$ $donothing$ para recursive recursiveCodes variantSelector glyphStore $createAndSaveGlyphImpl$ spirofns booleFns MarkSet AS_BASE ALSO_METRICS pickHash dependencyProfile getDependencyProfile buildGlyphs newtemp tagged DivFrame fontMetrics $assignUnicodeImpl$
### HERE WE GO
run-glyph-module '../glyphs/common-shapes.js'
@ -153,5 +154,5 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
run-glyph-module '../glyphs/autobuild-composite.js'
run-glyph-module '../glyphs/autobuild-transformed.js'
return : object metrics [glyphs glyphMap] glyphList unicodeGlyphs
return : object metrics glyphStore

View file

@ -1,11 +1,40 @@
"use strict";
const Point = require("../../support/point");
const { AnyCv } = require("../../support/gr");
function delta(a, b) {
return Math.round((a - b) * 32);
function autoref(glyphStore) {
suppressNaN(glyphStore);
hashContours(glyphStore);
const glyphEntryList = getGlyphEntryList(glyphStore);
linkRefl(glyphEntryList);
linkComponent(glyphEntryList);
unlinkHybrid(glyphStore);
}
function suppressNaN(glyphStore) {
for (const g of glyphStore.glyphs()) {
if (!g.contours) continue;
for (let k = 0; k < g.contours.length; k++) {
let contour = g.contours[k];
for (let z of contour) {
if (!isFinite(z.x)) z.x = 0;
if (!isFinite(z.y)) z.y = 0;
}
}
}
}
function hashContours(glyphStore) {
for (const g of glyphStore.glyphs()) {
if (!g.contours) continue;
for (let k = 0; k < g.contours.length; k++) {
const contour = g.contours[k];
contour.hash = contourHash(contour);
}
}
}
function contourHash(c) {
if (!c || c.length < 2) return ".";
let lx = c[0].x,
@ -18,8 +47,68 @@ function contourHash(c) {
}
return buf;
}
function delta(a, b) {
return Math.round((a - b) * 32);
}
function match(g1, g2, _n) {
function getGlyphEntryList(glyphStore) {
const excludeUnicode = new Set();
excludeUnicode.add(0x80);
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
for (const [j, gn, g] of glyphStore.indexedNamedEntries()) {
if (AnyCv.query(g).length) g.autoRefPriority = -1;
const us = glyphStore.queryUnicodeOf(g);
if (us) {
for (const u of us) if (excludeUnicode.has(u)) g.avoidBeingComposite = true;
}
}
return Array.from(glyphStore.indexedNamedEntries()).sort(byGlyphPriority);
}
function byGlyphPriority([ja, gna, a], [jb, gnb, b]) {
const pri1 = a.autoRefPriority || 0;
const pri2 = b.autoRefPriority || 0;
if (pri1 > pri2) return -1;
if (pri1 < pri2) return 1;
if (a.contours && b.contours && a.contours.length < b.contours.length) return 1;
if (a.contours && b.contours && a.contours.length > b.contours.length) return -1;
return 0;
}
function linkRefl(glyphEntryList) {
for (let j = 0; j < glyphEntryList.length; j++) {
const [, gnj, gj] = glyphEntryList[j];
if (!gj.contours.length || (gj.references && gj.references.length)) continue;
for (let k = j + 1; k < glyphEntryList.length; k++) {
const [, gnk, gk] = glyphEntryList[k];
if (gj.contours.length === gk.contours.length) {
match(gnj, gj, gnk, gk);
}
}
}
}
function linkComponent(glyphEntryList) {
for (let j = 0; j < glyphEntryList.length; j++) {
const [, gnj, gj] = glyphEntryList[j];
if (gj.autoRefPriority < 0) continue;
if (!gj.contours.length) continue;
if (gj.references && gj.references.length) continue;
for (let k = glyphEntryList.length - 1; k >= 0; k--) {
const [, gnk, gk] = glyphEntryList[k];
if (gj.contours.length > gk.contours.length) continue;
if (
gj.contours.length === gk.contours.length &&
!(gk.references && gk.references.length)
) {
continue;
}
while (match(gnj, gj, gnk, gk)) "pass";
}
}
}
function match(gn1, g1, gn2, g2) {
for (let j = 0; j + g1.contours.length <= g2.contours.length; j++) {
let found = true;
for (let k = j; k < g2.contours.length && k - j < g1.contours.length; k++) {
@ -44,13 +133,7 @@ function match(g1, g2, _n) {
continue;
}
if (!g2.references) g2.references = [];
g2.references.push({
glyph: g1.name,
_n: _n,
x: refX,
y: refY,
roundToGrid: false
});
g2.references.push({ glyph: gn1, x: refX, y: refY, roundToGrid: false });
g2.contours.splice(j, g1.contours.length);
return true;
}
@ -58,76 +141,25 @@ function match(g1, g2, _n) {
return false;
}
function unlinkRef(g, dx, dy, glyf) {
function unlinkHybrid(glyphStore) {
for (const g of glyphStore.glyphs()) {
if (!g.references || g.references.length === 0) continue;
if (!g.avoidBeingComposite && g.contours.length === 0) continue;
g.contours = unlinkRef(g, 0, 0, glyphStore);
g.references = [];
}
}
function unlinkRef(g, dx, dy, glyphStore) {
let contours = g.contours.map(c => c.map(z => new Point(z.x + dx, z.y + dy, z.on, z.cubic)));
if (g.references)
if (g.references) {
for (let r of g.references) {
contours = contours.concat(unlinkRef(glyf[r._n], r.x + dx, r.y + dy, glyf));
contours = contours.concat(
unlinkRef(glyphStore.queryByName(r.glyph), r.x + dx, r.y + dy, glyphStore)
);
}
}
return contours;
}
function autoref(gs, excludeUnicodeSet) {
suppressNaN(gs);
for (let j = 0; j < gs.length; j++) {
const g = gs[j];
if (g.contours) {
for (let k = 0; k < g.contours.length; k++) {
const contour = g.contours[k];
contour.hash = contourHash(contour);
}
}
}
// Refl-referencify, forward.
for (let j = 0; j < gs.length; j++) {
if (!gs[j].contours.length || (gs[j].references && gs[j].references.length)) continue;
for (let k = j + 1; k < gs.length; k++) {
if (gs[j].contours.length === gs[k].contours.length) {
match(gs[j], gs[k], j);
}
}
}
// referencify, backward
for (let j = 0; j < gs.length; j++) {
if (gs[j].autoRefPriority < 0) continue;
if (!gs[j].contours.length) continue;
if (gs[j].references && gs[j].references.length) continue;
for (let k = gs.length - 1; k >= 0; k--) {
if (gs[j].contours.length > gs[k].contours.length) continue;
if (
gs[j].contours.length === gs[k].contours.length &&
!(gs[k].references && gs[k].references.length)
) {
continue;
}
while (match(gs[j], gs[k], j)) "pass";
}
}
// unlink composite
for (let j = 0; j < gs.length; j++) {
if (!gs[j].references || gs[j].references.length === 0) continue;
if (!gs[j].avoidBeingComposite && gs[j].contours.length === 0) continue;
gs[j].contours = unlinkRef(gs[j], 0, 0, gs);
gs[j].references = [];
}
}
function suppressNaN(glyf) {
for (let j = 0; j < glyf.length; j++) {
let g = glyf[j];
if (!g.contours) continue;
for (let k = 0; k < g.contours.length; k++) {
let contour = g.contours[k];
for (let z of contour) {
if (!isFinite(z.x)) z.x = 0;
if (!isFinite(z.y)) z.y = 0;
}
}
}
}
module.exports = autoref;

View file

@ -2,11 +2,11 @@
const { Radical } = require("../../support/gr");
module.exports = function gcFont(gs, excludedChars, restFont, cfg) {
module.exports = function gcFont(glyphStore, excludedChars, restFont, cfg) {
markSweepOtl(restFont.GSUB);
markSweepOtl(restFont.GPOS);
const sink = mark(gs, excludedChars, restFont, cfg);
sweep(gs, restFont, sink);
const sink = mark(glyphStore, excludedChars, restFont, cfg);
return sweep(glyphStore, restFont, sink);
};
function markSweepOtl(table) {
@ -57,21 +57,22 @@ function markLookups(table, sink) {
} while (loop < 0xff && lookupSetChanged);
}
function mark(gs, excludedChars, restFont, cfg) {
const sink = markInitial(gs, excludedChars);
function mark(glyphStore, excludedChars, restFont, cfg) {
const sink = markInitial(glyphStore, excludedChars);
while (markStep(sink, restFont, cfg));
return sink;
}
function markInitial(gs, excludedChars) {
function markInitial(glyphStore, excludedChars) {
let sink = new Set();
for (const g of gs) {
for (const [gName, g] of glyphStore.namedEntries()) {
if (!g) continue;
if (g.glyphRank > 0) sink.add(g.name);
if (Radical.get(g)) sink.add(g.name);
if (g.unicode) {
for (const u of g.unicode) {
if (!excludedChars.has(u)) sink.add(g.name);
if (g.glyphRank > 0) sink.add(gName);
if (Radical.get(g)) sink.add(gName);
const unicodeSet = glyphStore.queryUnicodeOf(g);
if (unicodeSet) {
for (const u of unicodeSet) {
if (!excludedChars.has(u)) sink.add(gName);
}
}
}
@ -126,52 +127,52 @@ function markSubtable(sink, type, st, cfg) {
}
}
function sweep(gs, restFont, sink) {
filterInPlace(gs, g => sink.has(g.name));
sweepOtl(restFont.GSUB, sink);
sweepOtl(restFont.GPOS, sink);
function sweep(glyphStore, restFont, gnSet) {
sweepOtl(restFont.GSUB, gnSet);
sweepOtl(restFont.GPOS, gnSet);
return glyphStore.filterByName(gnSet);
}
function sweepOtl(table, sink) {
function sweepOtl(table, gnSet) {
if (!table || !table.lookups) return;
for (const lid in table.lookups) {
const lookup = table.lookups[lid];
if (!lookup.subtables) continue;
const newSubtables = [];
for (const st of lookup.subtables) {
const keep = sweepSubtable(st, lookup.type, sink);
const keep = sweepSubtable(st, lookup.type, gnSet);
if (keep) newSubtables.push(st);
}
lookup.subtables = newSubtables;
}
}
function sweepSubtable(st, type, gs) {
function sweepSubtable(st, type, gnSet) {
switch (type) {
case "gsub_single":
return sweep_GsubSingle(st, gs);
return sweep_GsubSingle(st, gnSet);
case "gsub_multiple":
case "gsub_alternate":
return sweep_GsubMultiple(st, gs);
return sweep_GsubMultiple(st, gnSet);
case "gsub_ligature":
return sweep_GsubLigature(st, gs);
return sweep_GsubLigature(st, gnSet);
case "gsub_chaining":
return sweep_GsubChaining(st, gs);
return sweep_GsubChaining(st, gnSet);
case "gsub_reverse":
return sweep_gsubReverse(st, gs);
return sweep_gsubReverse(st, gnSet);
case "gpos_mark_to_base":
case "gpos_mark_to_mark":
return sweep_gposMark(st, gs);
return sweep_gposMark(st, gnSet);
default:
return true;
}
}
function sweep_GsubSingle(st, gs) {
function sweep_GsubSingle(st, gnSet) {
let nonEmpty = false;
let from = Object.keys(st);
for (const gidFrom of from) {
if (!gs.has(gidFrom) || !gs.has(st[gidFrom])) {
if (!gnSet.has(gidFrom) || !gnSet.has(st[gidFrom])) {
delete st[gidFrom];
} else {
nonEmpty = true;
@ -180,14 +181,14 @@ function sweep_GsubSingle(st, gs) {
return nonEmpty;
}
function sweep_GsubMultiple(st, gs) {
function sweep_GsubMultiple(st, gnSet) {
let nonEmpty = false;
let from = Object.keys(st);
for (const gidFrom of from) {
let include = gs.has(gidFrom);
let include = gnSet.has(gidFrom);
if (st[gidFrom]) {
for (const gidTo of st[gidFrom]) {
include = include && gs.has(gidTo);
include = include && gnSet.has(gidTo);
}
} else {
include = false;
@ -201,26 +202,26 @@ function sweep_GsubMultiple(st, gs) {
return nonEmpty;
}
function sweep_GsubLigature(st, gs) {
function sweep_GsubLigature(st, gnSet) {
if (!st.substitutions) return false;
let newSubst = [];
for (const rule of st.substitutions) {
let include = true;
if (!gs.has(rule.to)) include = false;
for (const from of rule.from) if (!gs.has(from)) include = false;
if (!gnSet.has(rule.to)) include = false;
for (const from of rule.from) if (!gnSet.has(from)) include = false;
if (include) newSubst.push(rule);
}
st.substitutions = newSubst;
return true;
}
function sweep_GsubChaining(st, gs) {
function sweep_GsubChaining(st, gnSet) {
const newMatch = [];
for (let j = 0; j < st.match.length; j++) {
newMatch[j] = [];
for (let k = 0; k < st.match[j].length; k++) {
const gidFrom = st.match[j][k];
if (gs.has(gidFrom)) {
if (gnSet.has(gidFrom)) {
newMatch[j].push(gidFrom);
}
}
@ -230,16 +231,16 @@ function sweep_GsubChaining(st, gs) {
return true;
}
function sweep_gsubReverse(st, gs) {
function sweep_gsubReverse(st, gnSet) {
const newMatch = [],
newTo = [];
for (let j = 0; j < st.match.length; j++) {
newMatch[j] = [];
for (let k = 0; k < st.match[j].length; k++) {
const gidFrom = st.match[j][k];
let include = gs.has(gidFrom);
let include = gnSet.has(gidFrom);
if (j === st.inputIndex) {
include = include && gs.has(st.to[k]);
include = include && gnSet.has(st.to[k]);
if (include) {
newMatch[j].push(gidFrom);
newTo.push(st.to[k]);
@ -255,7 +256,7 @@ function sweep_gsubReverse(st, gs) {
return true;
}
function sweep_gposMark(st, gs) {
function sweep_gposMark(st, gnSet) {
let marks = st.marks || {},
newMarks = {},
hasMarks = false;
@ -264,13 +265,13 @@ function sweep_gposMark(st, gs) {
hasBases = true;
for (const gid in marks) {
if (gs.has(gid) && marks[gid]) {
if (gnSet.has(gid) && marks[gid]) {
newMarks[gid] = marks[gid];
hasMarks = true;
}
}
for (const gid in bases) {
if (gs.has(gid) && bases[gid]) {
if (gnSet.has(gid) && bases[gid]) {
newBases[gid] = bases[gid];
hasBases = true;
}
@ -279,17 +280,3 @@ function sweep_gposMark(st, gs) {
st.bases = newBases;
return hasMarks && hasBases;
}
function filterInPlace(a, condition) {
let i = 0,
j = 0;
while (i < a.length) {
const val = a[i];
if (condition(val, i, a)) a[j++] = val;
i++;
}
a.length = j;
return a;
}

View file

@ -5,82 +5,52 @@ const TypoGeom = require("typo-geom");
const Point = require("../../support/point");
const CurveUtil = require("../../support/curve-util");
const { AnyCv } = require("../../support/gr");
const gcFont = require("./gc");
module.exports = function finalizeFont(para, glyphList, excludedCodePoints, font) {
forceMonospaceIfNeeded(para, glyphList);
gcFont(glyphList, excludedCodePoints, font, {});
extractGlyfCmap(regulateGlyphList(para, glyphList), font);
module.exports = function finalizeFont(para, glyphStore, excludedCodePoints, font) {
glyphStore = forceMonospaceIfNeeded(para, glyphStore);
glyphStore = gcFont(glyphStore, excludedCodePoints, font, {});
extractGlyfCmap(regulateGlyphStore(para, glyphStore), font);
};
function forceMonospaceIfNeeded(para, glyphList) {
if (!para.forceMonospace || para.spacing > 0) return;
function forceMonospaceIfNeeded(para, glyphStore) {
const unitWidth = Math.round(para.width);
let i = 0,
j = 0;
for (; i < glyphList.length; i++) {
const g = glyphList[i];
g.advanceWidth = Math.round(g.advanceWidth || 0);
if (g.advanceWidth === 0 || g.advanceWidth === unitWidth) glyphList[j++] = g;
}
glyphList.length = j;
if (!para.forceMonospace || para.spacing > 0) return glyphStore;
return glyphStore.filterByGlyph({
has: g => {
const gw = Math.round(g.advanceWidth || 0);
return gw === 0 || gw === unitWidth;
}
});
}
function extractGlyfCmap(glyphList, font) {
function extractGlyfCmap(glyphStore, font) {
const glyf = {};
const cmap = {};
for (let g of glyphList) {
glyf[g.name] = g;
if (!g.unicode) continue;
for (let u of g.unicode) {
if (isFinite(u - 0)) cmap[u] = g.name;
const sortedEntries = Array.from(glyphStore.indexedNamedEntries()).sort(byRank);
for (const [origIndex, name, g] of sortedEntries) {
glyf[name] = g;
const us = glyphStore.queryUnicodeOf(g);
if (us) {
for (const u of us) if (isFinite(u - 0) && u) cmap[u] = name;
}
}
font.glyf = glyf;
font.cmap = cmap;
}
function regulateGlyphList(para, gs) {
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
const excludeUnicode = new Set();
excludeUnicode.add(0x80);
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
// autoref
for (let j = 0; j < gs.length; j++) {
gs[j].glyphOrder = j;
if (AnyCv.query(gs[j]).length) gs[j].autoRefPriority = -1;
if (gs[j].unicode) {
for (const u of gs[j].unicode) {
if (excludeUnicode.has(u)) gs[j].avoidBeingComposite = true;
}
}
}
gs.sort(byGlyphPriority);
autoRef(gs, excludeUnicode);
function regulateGlyphStore(para, glyphStore) {
autoRef(glyphStore);
// regulate
for (let g of gs) regulateGlyph(g, skew);
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
for (let g of glyphStore.glyphs()) regulateGlyph(g, skew);
// reorder
return gs.sort(byRank);
return glyphStore;
}
function byGlyphPriority(a, b) {
const pri1 = a.autoRefPriority || 0;
const pri2 = b.autoRefPriority || 0;
if (pri1 > pri2) return -1;
if (pri1 < pri2) return 1;
if (a.contours && b.contours && a.contours.length < b.contours.length) return 1;
if (a.contours && b.contours && a.contours.length > b.contours.length) return -1;
return 0;
}
function byRank(a, b) {
return (b.glyphRank || 0) - (a.glyphRank || 0) || (a.glyphOrder || 0) - (b.glyphOrder || 0);
function byRank([ja, gna, a], [jb, gnb, b]) {
return (b.glyphRank || 0) - (a.glyphRank || 0) || (ja || 0) - (jb || 0);
}
function regulateGlyph(g, skew) {

View file

@ -67,57 +67,66 @@ glyph-block AutoBuild-Accents : begin
return s
local foundDecompositions {.}
define [decideName namingParts parts code] : begin
local baseName namingParts.0.name
define [decideName namingParts code] : begin
local baseName : glyphStore.queryNameOf namingParts.0
local glyphName baseName
foreach [part : namingParts.slice 1] : if part : glyphName = glyphName + [fallback part.shortName part.name]
foreach [part : namingParts.slice 1] : if part : begin
glyphName = glyphName + [fallback part.shortName : glyphStore.queryNameOf part]
if foundDecompositions.(glyphName) : begin
local j 2
while foundDecompositions.(glyphName + j) [inc j]
set glyphName (glyphName + j)
if (glyphName.length > 27) : set glyphName ('uni' + [pad [[code.toString 16].toUpperCase] 4])
return glyphName
local [buildForCode code] : if [not unicodeGlyphs.(code)] : begin
local [buildForCode code] : if [not : glyphStore.queryByUnicode code] : begin
local str : String.fromCharCode code
local nfd : fallback customDecompositions.(str) : unorm.nfd str
if (nfd.length > 1) : begin
local parts {}
local parts { }
local allFound true
foreach j [range 0 nfd.length] : begin
local part unicodeGlyphs.([nfd.charCodeAt j])
if [not part] : then : set allFound false
: else : set parts.(j) unicodeGlyphs.([nfd.charCodeAt j])
local part : glyphStore.queryByUnicode [nfd.charCodeAt j]
if [not part] : then
set allFound false
: else
set parts.(j) part
if allFound : begin
local namingParts : parts.slice 0
local glyphName : decideName parts code
set parts : subParts parts
local glyphName : decideName namingParts parts code
set foundDecompositions.(glyphName) {glyphName code parts}
set foundDecompositions.(glyphName) { glyphName code parts }
if recursiveCodes : recursiveCodes.forEach buildForCode
: else : foreach code [range 0x0000 0xFFFF] : buildForCode code
local s_parts nothing
local s_parts nothing
define construction : glyph-proc
include s_parts.0 AS_BASE ALSO_METRICS
foreach part [items-of : s_parts.slice 1] : if part : begin
include part
if (part.name === 'rtailBR') : eject-contour 'serifRB'
if (part.markAnchors && part.markAnchors.bottomright) : begin
eject-contour 'serifRB'
define [RootGlyphProc goalName code parts] : begin
set s_parts parts
create-glyph goalName code construction
foreach [_id : items-of : Object.keys foundDecompositions] : begin
local {glyphName code parts} foundDecompositions.(_id)
RootGlyphProc glyphName code parts
local { glyphName code parts } foundDecompositions.(_id)
RootGlyphProc glyphName code parts
define part0Name : glyphStore.queryNameOf parts.0
if(parts.0 != [query-glyph part0Name]) : throw : new Error "Unreachable"
if(parts.0 != [query-glyph parts.0.name]) : throw : new Error "Unreachable"
local dstTree {}
local targetNameMap {.}
set targetNameMap.(parts.0.name) glyphName
local tree : getGrTree parts.0.name { DotlessOrNot AnyDerivingCv } query-glyph
set targetNameMap.(part0Name) glyphName
local tree : getGrTree part0Name { DotlessOrNot AnyDerivingCv } query-glyph
foreach [{gr origBase relBase} : items-of tree] : begin
local origGN targetNameMap.(origBase)
if [not origGN] : throw : new Error 'Unreachable'

View file

@ -95,7 +95,7 @@ glyph-block AutoBuild-Enclosure : begin
define gniPart : '.ci.' + gidPart + '@' + [{ prefix finalPlacement dscale xCompress shift }.join '/']
if [not : query-glyph gniPart] : create-glyph gniPart : glyph-proc
set-width 0
include miniatureFont.(gidPart)
include : miniatureFont.queryByName gidPart
include : Upright
include : Scale (dscale * xCompress) dscale
include : Translate 0 (dscale * (-CAP / 2 + shift))
@ -113,7 +113,7 @@ glyph-block AutoBuild-Enclosure : begin
local firstDerivedGyph null
local shift 0
foreach [gidPart : items-of parts] : do
local derivedGlyph miniatureFont.(gidPart)
local derivedGlyph : miniatureFont.queryByName gidPart
if [not firstDerivedGyph] : set firstDerivedGyph derivedGlyph
set totalWidth : totalWidth + derivedGlyph.advanceWidth
local xCompress [Math.min 1 (mockInnerWidth / totalWidth)]
@ -128,7 +128,7 @@ glyph-block AutoBuild-Enclosure : begin
define gidPart parts.(partIndex)
define finalPlacement : accumulatedAdvanceSoFar - width / 2 - totalWidth * dscale / 2
finalParts.push : EnsureComponentGlyphT gidPart : EnsureInnerSubGlyphImpl miniatureFont prefix finalPlacement dscale xCompress shift
set accumulatedAdvanceSoFar : accumulatedAdvanceSoFar + miniatureFont.(gidPart).advanceWidth * dscale * xCompress
set accumulatedAdvanceSoFar : accumulatedAdvanceSoFar + [miniatureFont.queryByName gidPart].advanceWidth * dscale * xCompress
return finalParts
@ -375,7 +375,7 @@ glyph-block AutoBuild-Enclosure : begin
if [query-glyph gniPart] : return gniPart
create-glyph gniPart : glyph-proc
set-width 0
include miniatureFont.(gidPart)
include : miniatureFont.queryByName gidPart
include : Upright
include : Translate offset 0
include : Scale xCompress 1
@ -394,7 +394,7 @@ glyph-block AutoBuild-Enclosure : begin
local gidPart partsWithDot.(j)
if j : set totalWidth : totalWidth - SB
set offsets.(j) totalWidth
set totalWidth : totalWidth + miniatureFont.(gidPart).advanceWidth
set totalWidth : totalWidth + [miniatureFont.queryByName gidPart].advanceWidth
set totalWidth : totalWidth - SB
local xCompress : if (totalWidth > width) (width / totalWidth) 1
local xTranslate : [if (totalWidth > width) 0 (width / 2 - totalWidth / 2)] - width
@ -423,7 +423,7 @@ glyph-block AutoBuild-Enclosure : begin
# Circled & Braced
define [digitGlyphNames j] : begin
return : [(j+'').split ''].map: c => unicodeGlyphs.(['0'.charCodeAt 0] + (c - 0)).name
return : [(j+'').split ''].map: c => [glyphStore.queryNameOfUnicode (['0'.charCodeAt 0] + (c - 0))]
if [not recursive] : do "Single-digit circled"
local compositions : list
@ -436,8 +436,8 @@ glyph-block AutoBuild-Enclosure : begin
0x2460 + j - 1
digitGlyphNames j
begin WideWidth1
foreach [j : range 0 26] : compositions.push {(0x24B6 + j) {unicodeGlyphs.(['A'.charCodeAt 0] + j).name} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x24D0 + j) {unicodeGlyphs.(['a'.charCodeAt 0] + j).name} WideWidth1 0.5 (XH/2)}
foreach [j : range 0 26] : compositions.push {(0x24B6 + j) {[glyphStore.queryNameOfUnicode (['A'.charCodeAt 0] + j)]} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x24D0 + j) {[glyphStore.queryNameOfUnicode (['a'.charCodeAt 0] + j)]} WideWidth1 0.5 (XH/2)}
createCircledGlyphs 1 compositions
if [not recursive] : do "Double-digit circled"
@ -467,7 +467,7 @@ glyph-block AutoBuild-Enclosure : begin
0x2776 + j - 1
digitGlyphNames j
begin WideWidth1
foreach [j : range 0 26] : compositions.push {(0x1F150 + j) {unicodeGlyphs.(['A'.charCodeAt 0] + j).name} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x1F150 + j) {[glyphStore.queryNameOfUnicode (['A'.charCodeAt 0] + j)]} WideWidth1}
createInsetCircledGlyphs 1 compositions
if [not recursive] : do "Double-digit inset circled"
@ -485,7 +485,7 @@ glyph-block AutoBuild-Enclosure : begin
if [not recursive] : do "boxed"
local compositions {}
compositions.push { null {'markBaseSpace'} WideWidth1 }
foreach [j : range 0 26] : compositions.push {(0x1F130 + j) {unicodeGlyphs.(['A'.charCodeAt 0] + j).name} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x1F130 + j) {[glyphStore.queryNameOfUnicode (['A'.charCodeAt 0] + j)]} WideWidth1}
createBoxedGlyphs 1 compositions
if [not recursive] : do "double-digit boxed"
@ -521,7 +521,7 @@ glyph-block AutoBuild-Enclosure : begin
if [not recursive] : do "inset boxed"
local compositions {}
foreach [j : range 0 26] : compositions.push {(0x1F170 + j) {unicodeGlyphs.(['A'.charCodeAt 0] + j).name} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x1F170 + j) {[glyphStore.queryNameOfUnicode (['A'.charCodeAt 0] + j)]} WideWidth1}
createInsetBoxedGlyphs 1 compositions
if [not recursive] : do "double-digit inset boxed"
@ -535,9 +535,9 @@ glyph-block AutoBuild-Enclosure : begin
if [not recursive] : do "inset mosaic"
local compositions {}
compositions.push { 0x1FBB1 { [unicodeGlyphs.(0x2714).name.replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
compositions.push { 0x1FBB4 { [unicodeGlyphs.(0x21B2).name.replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
compositions.push { 0x1FBC4 { [unicodeGlyphs.(0x003F).name.replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
compositions.push { 0x1FBB1 { [[glyphStore.queryNameOfUnicode (0x2714)].replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
compositions.push { 0x1FBB4 { [[glyphStore.queryNameOfUnicode (0x21B2)].replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
compositions.push { 0x1FBC4 { [[glyphStore.queryNameOfUnicode (0x003F)].replace [regex '.WWID$'] ".NWID"] } WideWidth2 }
createInsetMosaicGlyphs 1 compositions
if [not recursive] : do "Single-digit double circled"
@ -564,8 +564,8 @@ glyph-block AutoBuild-Enclosure : begin
0x2474 + j - 1
digitGlyphNames j
begin WideWidth1
foreach [j : range 0 26] : compositions.push {(0x249C + j) {unicodeGlyphs.(['a'.charCodeAt 0] + j).name} WideWidth1 0.5 (XH/2)}
foreach [j : range 0 26] : compositions.push {(0x1F110 + j) {unicodeGlyphs.(['A'.charCodeAt 0] + j).name} WideWidth1}
foreach [j : range 0 26] : compositions.push {(0x249C + j) {[glyphStore.queryNameOfUnicode (['a'.charCodeAt 0] + j)]} WideWidth1 0.5 (XH/2)}
foreach [j : range 0 26] : compositions.push {(0x1F110 + j) {[glyphStore.queryNameOfUnicode (['A'.charCodeAt 0] + j)]} WideWidth1}
createBracedGlyphs 1 compositions
if [not recursive] : do "Double-digit braced"
@ -621,11 +621,12 @@ glyph-block Autobuild-Fractions : begin
define [numeratorImpl numid] : begin
local gnn ".frac-num-\(prefix){\(numid)}"
if [not : query-glyph gnn] : create-glyph gnn : glyph-proc
define mfNumGlyph : miniatureFont.queryByName numid
set-width 0
if [not miniatureFont.(numid)] : console.log numid
include miniatureFont.(numid)
include mfNumGlyph
include : Upright
include : Translate (- miniatureFont.(numid).advanceWidth / 2) 0
include : Translate (- mfNumGlyph.advanceWidth / 2) 0
include : Scale scaleFactor
include : Translate Middle (SymbolMid + dist / 2)
include : Italify
@ -635,10 +636,12 @@ glyph-block Autobuild-Fractions : begin
define [denumeratorImpl denid] : begin
local gnd ".frac-den-\(prefix){\(denid)}"
if [not : query-glyph gnd] : create-glyph gnd : glyph-proc
define mfDenGlyph : miniatureFont.queryByName denid
set-width 0
include miniatureFont.(denid)
include mfDenGlyph
include : Upright
include : Translate (- miniatureFont.(denid).advanceWidth / 2) 0
include : Translate (- mfDenGlyph.advanceWidth / 2) 0
include : Scale scaleFactor
include : Translate Middle (SymbolMid - CAP * scaleFactor - dist / 2)
include : Italify
@ -721,7 +724,7 @@ glyph-block AutoBuild-Accented-Equal : begin
if [query-glyph gni] : return gni
create-glyph gni : glyph-proc
set-width 0
include dFont.(gidPart)
include : dFont.queryByName gidPart
include : Upright
include : Translate (-totalWidth / 2 + offset) 0
include : Scale scale
@ -736,7 +739,7 @@ glyph-block AutoBuild-Accented-Equal : begin
foreach [j : range 0 parts.length] : begin
local gidPart parts.(j)
set offsets.(j) totalWidth
set totalWidth : totalWidth + dFont.(gidPart).advanceWidth
set totalWidth : totalWidth + [dFont.queryByName gidPart].advanceWidth
if [not : query-glyph gn] : create-glyph gn unicode : glyph-proc
set-width Width
@ -792,7 +795,7 @@ glyph-block Autobuild-Ligatures : begin
if [query-glyph gni] : return gni
create-glyph gni : glyph-proc
set-width aw
include df.(gidPart)
include : df.queryByName gidPart
include : Upright
include : Translate offset1 0
include : Scale compress 1
@ -804,8 +807,8 @@ glyph-block Autobuild-Ligatures : begin
local { gn unicode { c1 c2 } desiredWidth } job
local ps {}
local dfg1 df1.(c1)
local dfg2 df2.(c2)
local dfg1 : df1.queryByName c1
local dfg2 : df2.queryByName c2
if FMosaicWide : begin
local aw : dfg1.advanceWidth + dfg2.advanceWidth
@ -908,21 +911,25 @@ glyph-block Autobuild-Pnonetic-Ligatures : begin
local s 0
local step (-OX)
local dist (Stroke * 2)
define dfg1 : df1.queryByName c1
define dfg2 : df2.queryByName c2
while (s < dist) : begin
include df2.(c2)
include dfg2
include : Translate step 0
set s : s + step
include : Translate (df1.(c1).advanceWidth * wadj1 - kern) 0
include : Translate (dfg1.advanceWidth * wadj1 - kern) 0
create-glyph glyphName unicode : glyph-proc
local sumChildrenWidth : df1.(c1).advanceWidth * wadj1 + df2.(c2).advanceWidth * wadj2
define dfg1 : df1.queryByName c1
define dfg2 : df2.queryByName c2
local sumChildrenWidth : dfg1.advanceWidth * wadj1 + dfg2.advanceWidth * wadj2
local refW : sumChildrenWidth - kern
include df2.(c2)
include : Translate (df1.(c1).advanceWidth * wadj1 - kern) 0
include dfg2
include : Translate (dfg1.advanceWidth * wadj1 - kern) 0
include : difference
intersection
Rect (CAP * 2) (Descender * 2) (-Width) (df1.(c1).advanceWidth * wadj1 - kern + df2.(c2).advanceWidth * wadj2 / 2)
glyph-proc : include df1.(c1)
Rect (CAP * 2) (Descender * 2) (-Width) (dfg1.advanceWidth * wadj1 - kern + dfg2.advanceWidth * wadj2 / 2)
glyph-proc : include dfg1
maskOut
include : Upright
include : Translate (-refW / 2) 0

View file

@ -9,7 +9,7 @@ glyph-module
glyph-block Autobuild-Transformed : begin
glyph-block-import CommonShapes
glyph-block-import Common-Derivatives
glyph-block-import Recursive-Build : Fork Miniature Widen
glyph-block-import Recursive-Build : Fork Miniature
glyph-block-import Overmarks
define [suggestName _name] : begin
@ -66,9 +66,10 @@ glyph-block Autobuild-Transformed : begin
foreach {unicode glyphid pri} [items-of records]
if [not : query-glyph targetNameMap.(glyphid)]
create-glyph (targetNameMap.(glyphid)) unicode : glyph-proc
if [not miniatureFont.(glyphid)] : throw : new Error "Cannot find glyph \(glyphid)"
local middle : miniatureFont.(glyphid).advanceWidth / 2
include miniatureFont.(glyphid) AS_BASE ALSO_METRICS
if [not : miniatureFont.queryByName glyphid] : begin
throw : new Error "Cannot find glyph \(glyphid)"
local middle : [miniatureFont.queryByName glyphid].advanceWidth / 2
include [miniatureFont.queryByName glyphid] AS_BASE ALSO_METRICS
include [Upright] true
include [Translate (-middle) (-CAP)] true
include [Scale 0.7] true
@ -89,8 +90,8 @@ glyph-block Autobuild-Transformed : begin
foreach {unicode glyphid pri} [items-of records]
if [not : query-glyph targetNameMap.(glyphid)]
create-glyph (targetNameMap.(glyphid)) unicode : glyph-proc
local middle : miniatureFont.(glyphid).advanceWidth / 2
include miniatureFont.(glyphid) AS_BASE ALSO_METRICS
local middle : [miniatureFont.queryByName glyphid].advanceWidth / 2
include [miniatureFont.queryByName glyphid] AS_BASE ALSO_METRICS
include [Upright] true
include [Translate (-middle) 0] true
include [Scale 0.7] true
@ -111,11 +112,10 @@ glyph-block Autobuild-Transformed : begin
set forkedParams.diversityI 1
set forkedParams.diversityII 1
local sf : Fork pendingGlyphs forkedParams
foreach {unicode glyphid} [items-of records]
if [not : query-glyph targetNameMap.(glyphid)]
foreach {unicode glyphid} [items-of records] : begin
if [not : query-glyph targetNameMap.(glyphid)] : begin
create-glyph targetNameMap.(glyphid) unicode : glyph-proc
include sf.(glyphid) AS_BASE
set-width sf.(glyphid).advanceWidth
include [sf.queryByName glyphid] AS_BASE ALSO_METRICS
link-relations relSets
define [createMedievalCombs defaultLow defaultHigh _records] : begin
@ -131,7 +131,7 @@ glyph-block Autobuild-Transformed : begin
foreach {unicode glyphid} [items-of records] : if [not : query-glyph targetNameMap.(glyphid)]
create-glyph targetNameMap.(glyphid) unicode : glyph-proc
set-width 0
local derived miniatureFont.(glyphid)
local derived [miniatureFont.queryByName glyphid]
local low defaultLow
local high defaultHigh
if (derived && derived.baseAnchors.above && derived.baseAnchors.below) : begin
@ -163,8 +163,8 @@ glyph-block Autobuild-Transformed : begin
foreach {unicode glyphid} [items-of records] : if [not : query-glyph targetNameMap.(glyphid)]
create-glyph targetNameMap.(glyphid) unicode : glyph-proc
set-width 0
local middle : miniatureFont.(glyphid).advanceWidth / 2
include miniatureFont.(glyphid)
local middle : [miniatureFont.queryByName glyphid].advanceWidth / 2
include [miniatureFont.queryByName glyphid]
include : Upright
include : Translate (-middle) (-XH)
include : Scale 0.4
@ -305,7 +305,7 @@ glyph-block Autobuild-Transformed : begin
if [not recursive] : let [df : Miniature {'a' 'o'} 4 0.7] : begin
create-glyph 'ordfeminine' 0xAA : glyph-proc
include df.a
include : df.queryByName 'a'
include : HBarBottom SB RightSB Descender
include : Upright
include : Translate (-Middle) (-XH)
@ -314,7 +314,7 @@ glyph-block Autobuild-Transformed : begin
include : Italify
create-glyph 'ordmasculine' 0xBA : glyph-proc
include df.o
include : df.queryByName 'o'
include : HBarBottom SB RightSB Descender
include : Upright
include : Translate (-Middle) (-XH)
@ -477,10 +477,10 @@ glyph-block Autobuild-Rhotic : begin
if [not recursive] : let [thinfont : Widen {'schwa' 'revLatinEpsilon'} 0.85 1] : begin
create-glyph 'er' 0x25A : glyph-proc # er
include MarkSet.e
include thinfont.schwa
include : thinfont.queryByName 'schwa'
include : ErTail (Width * 0.85 - SB - markFine * HVContrast * 1.25)
create-glyph 'revlatinepsiloner' 0x25D : glyph-proc # revlatinepsiloner
include MarkSet.e
include thinfont.revLatinEpsilon
include : thinfont.queryByName 'revLatinEpsilon'
include : ErTail (Width * 0.85 - SB - markFine * HVContrast * 1.25)

View file

@ -40,14 +40,15 @@ glyph-block Common-Derivatives : begin
define [glyph-is-needed name] : [not pickHash] || pickHash.(name)
define [query-glyph id] : return glyphMap.(id)
define [query-glyph id] : return : glyphStore.queryByName id
define [refer-glyph id] : lambda [copyAnchors copyWidth] : begin
if [not glyphMap.(id)] : throw : new Error "Cannot find glyph '\(id)'"
this.includeGlyph glyphMap.(id) copyAnchors copyWidth
local goal : query-glyph id
if [not goal] : throw : new Error "Cannot find glyph '\(id)'"
this.includeGlyph goal copyAnchors copyWidth
define [with-related-glyphs sourceGid dstGid unicode Fn] : if [glyph-is-needed sourceGid] : begin
local glyphSrc glyphMap.(sourceGid)
local glyphSrc : glyphStore.queryByName sourceGid
local glyphDst : create-glyph dstGid unicode : glyph-proc
include : Fn sourceGid null
@ -120,14 +121,14 @@ glyph-block Recursive-Build : begin
local shouldBuildList : Object.keys sbh :.filter ([x] => [not [not x]])
#console.log shouldBuildList
local shouldBuildUnicodes : shouldBuildList.map ([x] => [if (glyphMap.(x) && glyphMap.(x).unicode) glyphMap.(x).unicode.0 nothing])
local shouldBuildUnicodes : shouldBuildList.map ([x] => [if ([glyphStore.queryByName x] && [glyphStore.queryUnicodeOfName x]) [glyphStore.queryUnicodeArrayOfName x].0 nothing])
:.filter ([x] => [not [not x]])
local p {.}
foreach [{k v} : pairs-of all ps] : set p.(k) v
local gs : buildGlyphs p shouldBuildList shouldBuildUnicodes
return gs.glyphs
return gs.glyphStore
define [Miniature] : params [glyphs crowd scale [slopeAngle para.slopeAngle] [sbscale (Width / UPM)] [mono false]] : begin
local forkedPara : Object.create para

View file

@ -2386,7 +2386,7 @@ glyph-block Letter-Latin-Lower-D : begin
include : refer-glyph "commaAbove"
include : Translate (Width + (RightSB - SB) / 2 + markExtend / 2) 0
local f : Widen {src} 0.95 1
include f.(src)
include : f.queryByName src
include MarkSet.b
create-glyph 'dcurlytail' 0x221 : glyph-proc

View file

@ -1365,7 +1365,7 @@ glyph-block Overmarks : begin
define AnchorMap : list
list 'above' 'tieAbove' 'aboveBrace'
list 'below' 'tieBelow' 'belowBrace'
foreach { gn g } [pairs-of glyphMap] : begin
foreach { gn g } [glyphStore.namedEntries] : begin
local handled false
foreach { akFrom akTo akBrace } [items-of AnchorMap] : begin
if (!handled && g.markAnchors && g.markAnchors.(akFrom)) : begin

View file

@ -90,7 +90,7 @@ glyph-block Symbol-Math-Letter-Like : begin
create-glyph [MangleName 'infty'] [MangleUnicode 0x221E] : glyph-proc
set-width MosaicWidth
include df.'eight.lnum'
include : df.queryByName 'eight.lnum'
include : Translate (-(Width / 2)) (-CAP / 2)
include : Rotate (Math.PI / 2)
include : Scale s
@ -99,7 +99,7 @@ glyph-block Symbol-Math-Letter-Like : begin
create-glyph [MangleName 'propto'] [MangleUnicode 0x221D] : glyph-proc
set-width MosaicWidth
include df.rotetedpropto
include : df.queryByName 'rotetedpropto'
include : Translate (-(Width / 2)) (-CAP / 2)
include : Rotate (Math.PI / 2)
include : Scale s

View file

@ -12,8 +12,8 @@ const Toml = require("@iarna/toml");
module.exports = async function main(argv) {
const para = await getParameters(argv);
const font = BuildFont(para);
if (argv.oCharMap) await saveCharMap(argv, font);
const { font, glyphStore } = BuildFont(para);
if (argv.oCharMap) await saveCharMap(argv, glyphStore);
if (argv.o) await saveOtd(argv, font);
};
@ -96,12 +96,14 @@ function objHashNonEmpty(obj) {
return false;
}
async function saveCharMap(argv, font) {
async function saveCharMap(argv, glyphStore) {
let charMap = [];
for (const gid in font.glyf) {
const glyph = font.glyf[gid];
if (!glyph) continue;
charMap.push([glyph.name, glyph.unicode, ...createGrDisplaySheet(font, gid)]);
for (const [gn] of glyphStore.namedEntries()) {
charMap.push([
gn,
Array.from(glyphStore.queryUnicodeOfName(gn) || []),
...createGrDisplaySheet(glyphStore, gn)
]);
}
await fs.writeFile(argv.oCharMap, JSON.stringify(charMap), "utf8");
}

View file

@ -211,7 +211,7 @@ define-macro glyph-block : syntax-rules
set externEnv.$glyphBlockVariableUsage$ variableSet
define captureImports `[metrics $NamedParameterPair$ $donothing$ para recursive
recursiveCodes variantSelector glyphMap glyphList unicodeGlyphs $createAndSaveGlyphImpl$
recursiveCodes variantSelector glyphStore $createAndSaveGlyphImpl$
spirofns booleFns MarkSet AS_BASE ALSO_METRICS pickHash dependencyProfile
getDependencyProfile buildGlyphs newtemp tagged DivFrame fontMetrics $assignUnicodeImpl$]
define metricImports `[UPM HalfUPM Width SB CAP XH Descender Contrast SymbolMid ParenTop

View file

@ -55,7 +55,7 @@ define [interpretLookupAt gs j lut] : match lut.type
if subtable.(gs.(j)) : begin
set gs.(j) subtable.(gs.(j))
export : define [BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF config] : begin
export : define [BuildCompatLigatures glyphStore GSUB GDEF config] : begin
foreach [cldef : items-of config] : do
if [not cldef.unicode] : break nothing
if [not cldef.featureTag] : break nothing
@ -69,23 +69,23 @@ export : define [BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF c
local gnames {}
for [local j 0] [j < cldef.sequence.length] [inc j] : begin
if [not unicodeGlyphs.[cldef.sequence.charCodeAt j]] : break nothing
gnames.push unicodeGlyphs.[cldef.sequence.charCodeAt j].name
if [not : glyphStore.queryByUnicode : cldef.sequence.charCodeAt j] : break nothing
gnames.push : glyphStore.queryNameOfUnicode : cldef.sequence.charCodeAt j
interpretLookups gnames feature GSUB.lookups
local g1 : new Glyph ('$clig.' + cldef.unicode)
define g1Name : '$clig.' + cldef.unicode
local g1 : new Glyph g1Name
set g1.advanceWidth 0
set g1.autoRefPriority 1
set g1.unicode {cldef.unicode}
foreach [gn : items-of gnames] : begin
local g glyphs.(gn)
local g : glyphStore.queryByName gn
g1.applyTransform : new Transform 1 0 0 1 (-g1.advanceWidth) 0
g1.includeGlyph g
g1.applyTransform : new Transform 1 0 0 1 (g1.advanceWidth) 0
set g1.advanceWidth : g1.advanceWidth + g.advanceWidth
set glyphs.(g1.name) g1
set unicodeGlyphs.(cldef.unicode) g1
glyphList.push g1
set GDEF.glyphClassDef.(g1.name) GDEF_LIGATURE
glyphStore.addGlyph g1Name g1
glyphStore.encodeGlyph cldef.unicode g1
set GDEF.glyphClassDef.(g1Name) GDEF_LIGATURE

View file

@ -6,7 +6,7 @@ define MarkClasses {
'trailing' 'lf' 'tieAbove' 'tieBelow' 'aboveBrace' 'belowBrace'
}
export : define [buildMarkMkmk sink glyphList] : begin
export : define [buildMarkMkmk sink glyphStore] : begin
define mark : add-feature sink 'mark'
define mkmk : add-feature sink 'mkmk'
add-common-feature sink mark
@ -16,7 +16,7 @@ export : define [buildMarkMkmk sink glyphList] : begin
local mkmkLookupNames {}
foreach markCls [items-of MarkClasses] : begin
local [object markSubtable mkmkSubtable] : createMTSubtables glyphList { markCls }
local [object markSubtable mkmkSubtable] : createMTSubtables glyphStore { markCls }
if ([objectIsNotEmpty markSubtable.marks] && [objectIsNotEmpty markSubtable.bases]) : begin
local markLookup : add-lookup sink {.type 'gpos_mark_to_base' .subtables { markSubtable }}
mark.lookups.push markLookup
@ -30,33 +30,33 @@ export : define [buildMarkMkmk sink glyphList] : begin
foreach markLookup [items-of markLookupNames] : foreach mkmkLookup [items-of mkmkLookupNames]
sink.lookupDep.push { markLookup mkmkLookup }
define [createMTSubtables glyphList markClasses] : begin
define [createMTSubtables glyphStore markClasses] : begin
local markSubtable {.marks {.} .bases {.}}
local mkmkSubtable {.marks {.} .bases {.}}
local allowMarkClsSet : new Set markClasses
foreach glyph [items-of glyphList] : begin
createMarkInfo markSubtable.marks glyph allowMarkClsSet
createMarkInfo mkmkSubtable.marks glyph allowMarkClsSet
foreach { gn glyph } [glyphStore.namedEntries] : begin
createMarkInfo markSubtable.marks gn glyph allowMarkClsSet
createMarkInfo mkmkSubtable.marks gn glyph allowMarkClsSet
local isMark : objectIsNotEmpty glyph.markAnchors
if isMark
createBaseInfo mkmkSubtable.bases glyph allowMarkClsSet
createBaseInfo markSubtable.bases glyph allowMarkClsSet
createBaseInfo mkmkSubtable.bases gn glyph allowMarkClsSet
createBaseInfo markSubtable.bases gn glyph allowMarkClsSet
return : object markSubtable mkmkSubtable
define [createBaseInfo sink glyph allowMarkClsSet] : begin
define [createBaseInfo sink gn glyph allowMarkClsSet] : begin
local res {.}
local pushed false
foreach { markCls anchor } [pairs-of glyph.baseAnchors] : if [allowMarkClsSet.has markCls] : begin
set pushed true
set res.(markCls) {.x anchor.x .y anchor.y}
if pushed : set sink.(glyph.name) res
if pushed : set sink.(gn) res
return pushed
define [createMarkInfo sink glyph allowMarkClsSet] : begin
define [createMarkInfo sink gn glyph allowMarkClsSet] : begin
local m null
foreach { markCls anchor } [pairs-of glyph.markAnchors] : if [allowMarkClsSet.has markCls] : begin
set m {.class markCls .x anchor.x .y anchor.y}
if m : set sink.(glyph.name) m
if m : set sink.(gn) m
return m
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -5,7 +5,7 @@ extern Set
define-operator "~>" 880 'right' : syntax-rules
`(@l ~> @r) `{.left @l .right @r}
export : define [buildCCMP sink glyphs markGlyphs] : begin
export : define [buildCCMP sink glyphStore markGlyphs] : begin
local rec : BeginLookupBlock sink
define ccmp : add-feature sink 'ccmp'
@ -18,7 +18,7 @@ export : define [buildCCMP sink glyphs markGlyphs] : begin
define TieMarkFrom {}
define TieMarkTo {}
define TieGlyphs {}
foreach [{gid g} : pairs-of glyphs] : if (gid.(0) !== ".") : begin
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
if g.baseAnchors.trailing : groupTR.push gid
if g.baseAnchors.lf : groupLF.push gid
if [Dotless.get g] : begin
@ -214,7 +214,7 @@ export : define [buildCCMP sink glyphs markGlyphs] : begin
# CCMP decomposition
define decompositions {.}
foreach {gid g} [pairs-of glyphs] : begin
foreach { gid g } [glyphStore.namedEntries] : begin
local parts : CcmpDecompose.get g
if (parts && parts.length) : set decompositions.(gid) parts
@ -227,4 +227,4 @@ export : define [buildCCMP sink glyphs markGlyphs] : begin
add-common-feature sink ccmp
EndLookupBlock rec sink
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -5,7 +5,7 @@ extern Set
define [FeatureName tag] : tag + '_cvss'
define [LookupName tag] : 'lookup_cvss_' + tag
export : define [buildCVSS sink para glyphs glyphList] : begin
export : define [buildCVSS sink para glyphStore] : begin
if [not para.enableCvSs] : return nothing
local rec : BeginLookupBlock sink
@ -13,7 +13,7 @@ export : define [buildCVSS sink para glyphs glyphList] : begin
# Decomposition of enclosures
define decompositions {.}
foreach {gid g} [pairs-of glyphs] : begin
foreach { gid g } [glyphStore.namedEntries] : begin
local parts : CvDecompose.get g
if (parts && parts.length) : set decompositions.(gid) parts
@ -22,7 +22,7 @@ export : define [buildCVSS sink para glyphs glyphList] : begin
.subtables : list decompositions
# cvxx
foreach [glyph : items-of glyphList]
foreach {gn glyph} [glyphStore.namedEntries]
foreach [gr : items-of : AnyCv.query glyph] : if gr.tag : begin
local lookupName : LookupName gr.tag
if [not : cvLookupNameSet.has lookupName] : begin
@ -36,7 +36,7 @@ export : define [buildCVSS sink para glyphs glyphList] : begin
sink.lookupDep.push { lookupCvDecompose lookupName }
cvLookupNameSet.add lookupName
set [pick-lookup sink lookupName].subtables.0.(glyph.name) [gr.get glyph]
set [pick-lookup sink lookupName].subtables.0.(gn) [gr.get glyph]
# ssxx
foreach [{name composition} : pairs-of para.variants] : begin

View file

@ -11,13 +11,14 @@ define look-around null
define advance : lambda [t] null
define ident : lambda [t] : t.map : lambda [x] x
export : define [buildLigations sink para plm glyphs] : begin
export : define [buildLigations sink para plm] : begin
local rec : BeginLookupBlock sink
local rankedLookups {}
foreach [ {featureName mappedFeature} : pairs-of plm] : buildLigationsImpl sink para glyphs featureName mappedFeature rankedLookups
foreach [ {featureName mappedFeature} : pairs-of plm] : begin
buildLigationsImpl sink para featureName mappedFeature rankedLookups
EndLookupBlock rec sink
define [buildLigationsImpl sink para glyphs featureName mappedFeature rankedLookups] : begin
define [buildLigationsImpl sink para featureName mappedFeature rankedLookups] : begin
define {chain-rule reverse-rule} : ChainRuleBuilder sink
define arrowStick {'hyphen' 'equal'}

View file

@ -1,22 +1,22 @@
import [add-common-feature add-feature add-lookup BeginLookupBlock EndLookupBlock] from "./table-util"
# Name-driven feature pairs
export : define [buildPairFeature sink tag1 tag2 glyphs glyphList codedOnly] : begin
export : define [buildPairFeature sink tag1 tag2 glyphStore codedOnly] : begin
local rec : BeginLookupBlock sink
local mapTag2 {.}
local mapTag1 {.}
define reHidden : regex "^\\."
define reTag1 : new RegExp ("\\." + tag1 + "$")
foreach [glyph : items-of glyphList] : begin
if ([reTag1.test glyph.name] && ![reHidden.test glyph.name]) : do
local gnTag2 : glyph.name.replace reTag1 ('.' + tag2)
local glyphTag2 glyphs.(gnTag2)
foreach { glyphName glyph } [glyphStore.namedEntries] : begin
if ([reTag1.test glyphName] && ![reHidden.test glyphName]) : do
local gnTag2 : glyphName.replace reTag1 ('.' + tag2)
local glyphTag2 : glyphStore.queryByName gnTag2
if (glyphTag2) : begin
if(!codedOnly || glyph.unicode && glyph.unicode.length > 0)
set mapTag2.(glyph.name) gnTag2
if(!codedOnly || glyphTag2.unicode && glyphTag2.unicode.length > 0)
set mapTag1.(gnTag2) glyph.name
if(!codedOnly || [glyphStore.queryUnicodeOf glyph])
set mapTag2.(glyphName) gnTag2
if(!codedOnly || [glyphStore.queryUnicodeOf glyphTag2])
set mapTag1.(gnTag2) glyphName
if [objectIsNotEmpty mapTag1] : begin
define lookup1 : add-lookup sink {.type 'gsub_single' .subtables {mapTag1}}
@ -32,4 +32,4 @@ export : define [buildPairFeature sink tag1 tag2 glyphs glyphList codedOnly] : b
EndLookupBlock rec sink
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -3,7 +3,7 @@ import [add-common-feature add-feature add-lookup ChainRuleBuilder query-related
define-operator "~>" 880 'right' : syntax-rules
`(@l ~> @r) `{.left @l .right @r}
export : define [buildGsubThousands sink para glyphs] : begin
export : define [buildGsubThousands sink para] : begin
local rec : BeginLookupBlock sink
define Thousand : add-feature sink 'THND'

View file

@ -19,31 +19,31 @@ define GDEF_LIGATURE 2
define GDEF_MARK 3
# GSUB
define [buildGSUB para glyphs glyphList markGlyphs] : begin
define [buildGSUB para glyphStore markGlyphs] : begin
define gsub : CreateEmptyTable
# lnum / onum
buildPairFeature gsub 'lnum' 'onum' glyphs glyphList true
buildPairFeature gsub 'lnum' 'onum' glyphStore true
# NWID / WWID
if (!para.forceMonospace || para.spacing > 0) : begin
buildPairFeature gsub 'NWID' 'WWID' glyphs glyphList true
buildPairFeature gsub 'NWID' 'WWID' glyphStore true
# ccmp
buildCCMP gsub glyphs markGlyphs
buildCCMP gsub glyphStore markGlyphs
# Ligation
if para.enableLigation : do
define plm : objectAssign {.} para.defaultBuildup
if (para.ligation.caltBuildup && para.ligation.caltBuildup.length) : begin
set plm.calt para.ligation.caltBuildup
buildLigations gsub para plm glyphs
buildLigations gsub para plm
# THND
buildGsubThousands gsub para glyphs
buildGsubThousands gsub para glyphStore
# cv##, ss##
buildCVSS gsub para glyphs glyphList
buildCVSS gsub para glyphStore
# locl
# Builds last, but the lookups are added into the beginning of the lookup list
@ -54,33 +54,33 @@ define [buildGSUB para glyphs glyphList markGlyphs] : begin
return gsub
# GPOS
define [buildGPOS para glyphs glyphList markGlyphs] : begin
define [buildGPOS para glyphStore markGlyphs] : begin
define gpos : CreateEmptyTable
buildMarkMkmk gpos glyphList
buildMarkMkmk gpos glyphStore
finalizeTable gpos
return gpos
# GDEF
define [buildGDEF para glyphs glyphList markGlyphs] : begin
define [buildGDEF para glyphStore markGlyphs] : begin
local GDEF {.glyphClassDef {.}}
foreach glyph [items-of glyphList] : begin
set GDEF.glyphClassDef.(glyph.name) : if [[regex '_'].test glyph.name] GDEF_LIGATURE GDEF_SIMPLE
foreach { gn glyph } [glyphStore.namedEntries] : begin
set GDEF.glyphClassDef.(gn) : if [[regex '_'].test gn] GDEF_LIGATURE GDEF_SIMPLE
if (glyph.markAnchors && [begin [local anchorKeys : Object.keys glyph.markAnchors] anchorKeys.length]) : begin
foreach key [items-of anchorKeys] : begin
if [not markGlyphs.(key)] : set markGlyphs.(key) {}
markGlyphs.(key).push glyph.name
markGlyphs.all.push glyph.name
set GDEF.glyphClassDef.(glyph.name) GDEF_MARK
markGlyphs.(key).push gn
markGlyphs.all.push gn
set GDEF.glyphClassDef.(gn) GDEF_MARK
return GDEF
export : define [buildOtl para glyphs glyphList unicodeGlyphs] : begin
export : define [buildOtl para glyphStore] : begin
local markGlyphs {.all {} }
local GPOS : buildGPOS para glyphs glyphList markGlyphs
local GDEF : buildGDEF para glyphs glyphList markGlyphs
local GSUB : buildGSUB para glyphs glyphList markGlyphs
local GPOS : buildGPOS para glyphStore markGlyphs
local GDEF : buildGDEF para glyphStore markGlyphs
local GSUB : buildGSUB para glyphStore markGlyphs
# Build compatibility ligatures
if (para.spacing > 0 && para.compLig) : begin
BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF para.compLig
BuildCompatLigatures glyphStore GSUB GDEF para.compLig
return [object GSUB GPOS GDEF]

View file

@ -0,0 +1,109 @@
"use strict";
class GlyphStore {
constructor() {
this.nameForward = new Map();
this.nameBackward = new Map();
this.encodingForward = new Map();
this.encodingBackward = new Map();
}
glyphs() {
return this.nameForward.values();
}
namedEntries() {
return this.nameForward.entries();
}
*indexedNamedEntries() {
let i = 0;
for (const [name, g] of this.nameForward.entries()) {
yield [i, name, g];
i++;
}
}
addGlyph(name, g) {
this.nameForward.set(name, g);
this.nameBackward.set(g, name);
}
queryByName(name) {
return this.nameForward.get(name);
}
queryNameOf(g) {
return this.nameBackward.get(g);
}
deleteGlyph(g) {
const name = this.nameBackward.get(g);
this.nameBackward.delete(g);
if (name) this.nameForward.delete(g);
this.deleteUnicodeAssignmentsOf(g);
}
deleteGlyphByName(name) {
const g = this.nameForward.get(name);
this.nameForward.delete(g);
if (g) {
this.nameBackward.delete(g);
this.deleteUnicodeAssignmentsOf(g);
}
}
encodeGlyph(u, g) {
this.encodingForward.set(u, g);
let s = this.encodingBackward.get(g);
if (!s) {
s = new Set();
this.encodingBackward.set(g, s);
}
s.add(u);
}
queryByUnicode(u) {
return this.encodingForward.get(u);
}
queryNameOfUnicode(u) {
const g = this.queryByUnicode(u);
if (!g) return undefined;
return this.queryNameOf(g);
}
queryUnicodeOf(g) {
const s = this.encodingBackward.get(g);
if (!s || !s.size) return null;
return s;
}
queryUnicodeOfName(name) {
const g = this.queryByName(name);
if (!g) return undefined;
return this.queryUnicodeOf(g);
}
queryUnicodeArrayOfName(name) {
return [...this.queryUnicodeOfName(name)];
}
deleteUnicodeAssignmentsOf(g) {
const s = this.nameBackward.get(g);
if (s) for (const u of s) this.encodingForward.delete(u);
this.encodingBackward.delete(g);
}
filterByName(nameSet) {
const gs1 = new GlyphStore();
for (const [name, g] of this.nameForward) {
if (!nameSet.has(name)) continue;
gs1.addGlyph(name, g);
const us = this.encodingBackward.get(g);
if (us) for (const u of us) gs1.encodeGlyph(u, g);
}
return gs1;
}
filterByGlyph(glyphSet) {
const gs1 = new GlyphStore();
for (const [name, g] of this.nameForward) {
if (!glyphSet.has(g)) continue;
gs1.addGlyph(name, g);
const us = this.encodingBackward.get(g);
if (us) for (const u of us) gs1.encodeGlyph(u, g);
}
return gs1;
}
}
module.exports = GlyphStore;

View file

@ -6,8 +6,7 @@ const Anchor = require("./anchor");
module.exports = class Glyph {
constructor(name) {
Object.defineProperty(this, "name", { value: name, writable: false });
this.unicode = [];
this._m_dependentName = name;
this.contours = [];
this.advanceWidth = 500;
this.autoRefPriority = 0;
@ -17,6 +16,15 @@ module.exports = class Glyph {
this.dependencies = [];
this.defaultTag = null;
}
get name() {
throw new TypeError("Glyph::name has been deprecated");
}
get unicode() {
throw new TypeError("Glyph::unicode has been deprecated");
}
set unicode(x) {
throw new TypeError("Glyph::unicode has been deprecated");
}
// PTL pattern matching
static unapply(obj, arity) {
if (obj instanceof Glyph) return [obj];
@ -26,14 +34,9 @@ module.exports = class Glyph {
setWidth(w) {
this.advanceWidth = w;
}
// Encoding
assignUnicode(u) {
if (typeof u === "string") this.unicode.push(u.codePointAt(0));
else this.unicode.push(u);
}
// Dependency
dependsOn(glyph) {
if (glyph.name) this.dependencies.push(glyph.name);
if (glyph._m_dependentName) this.dependencies.push(glyph._m_dependentName);
if (glyph.dependencies) for (const dep of glyph.dependencies) this.dependencies.push(dep);
}
// Contour Tagging

View file

@ -235,8 +235,8 @@ function getGrMesh(gidList, grq, fnGidToGlyph) {
return ret;
}
function createGrDisplaySheet(font, gid) {
const glyph = font.glyf[gid];
function createGrDisplaySheet(glyphStore, gid) {
const glyph = glyphStore.queryByName(gid);
if (!glyph) return [];
// Query selected typographic features -- mostly NWID and WWID
@ -249,7 +249,7 @@ function createGrDisplaySheet(font, gid) {
if (decomposition) {
const variantFeatureSet = new Set();
for (const componentGn of decomposition) {
const component = font.glyf[componentGn];
const component = glyphStore.queryByName(componentGn);
if (!component) continue;
const cvRow = queryCvFeatureTagsOf(componentGn, component, variantFeatureSet);
if (cvRow.length) charVariantFeatures.push(cvRow);