Refactor: Create a separate class for glyph store
This commit is contained in:
parent
02e6d041be
commit
4d20f8e655
25 changed files with 482 additions and 360 deletions
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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]
|
||||
|
|
109
font-src/support/glyph-store.js
Normal file
109
font-src/support/glyph-store.js
Normal 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;
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue