* Replace autoRef with semantic inclusion for more stable results.
* Move files around to make repository organized better.
This commit is contained in:
parent
400d8f3f38
commit
7c78329244
37 changed files with 1098 additions and 761 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -43,8 +43,7 @@ release-archives/
|
|||
testdrive/iosevka*
|
||||
|
||||
# Generated scripts
|
||||
font-src/gen/build-glyphs.js
|
||||
font-src/gen/kits/**/*.js
|
||||
font-src/kits/**/*.js
|
||||
font-src/meta/**/*.js
|
||||
font-src/otl/**/*.js
|
||||
font-src/glyphs/**/*.js
|
||||
|
|
|
@ -57,7 +57,7 @@ Iosevka supports Language-Specific Ligations, which is the ligation set enabled
|
|||
|
||||
To build Iosevka you should:
|
||||
|
||||
1. Ensure that [`nodejs`](http://nodejs.org) (≥ 12.16.0), [`ttfautohint`](http://www.freetype.org/ttfautohint/), [`otfcc`](https://github.com/caryll/otfcc) (≥ 0.10.3-alpha) and [`otf2otc`](https://github.com/adobe-type-tools/afdko) are present.
|
||||
1. Ensure that [`nodejs`](http://nodejs.org) (≥ 12.16.0) and [`ttfautohint`](http://www.freetype.org/ttfautohint/) are present.
|
||||
2. Install necessary libs by `npm install`. If you’ve installed them, upgrade to the latest.
|
||||
3. `npm run build -- contents::iosevka`.
|
||||
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
"use strict";
|
||||
|
||||
const EmptyFont = require("./empty-font.js");
|
||||
const buildGlyphs = require("./build-glyphs.js");
|
||||
const EmptyFont = require("./empty-font");
|
||||
const buildGlyphs = require("../glyphs/index");
|
||||
const finalizeFont = require("./finalize/index");
|
||||
const convertOtd = require("./otd-conv/index");
|
||||
|
||||
const { buildOtl } = require("../otl/index");
|
||||
const { assignFontNames } = require("../meta/naming");
|
||||
const { setFontMetrics } = require("../meta/aesthetics");
|
||||
|
||||
module.exports = function (para) {
|
||||
const font = EmptyFont();
|
||||
const otd = EmptyFont();
|
||||
const gs = buildGlyphs(para);
|
||||
|
||||
assignFontNames(para, gs.metrics, font);
|
||||
setFontMetrics(para, gs.metrics, font);
|
||||
assignFontNames(para, gs.metrics, otd);
|
||||
setFontMetrics(para, gs.metrics, otd);
|
||||
|
||||
const otl = buildOtl(para, gs.glyphStore);
|
||||
font.GSUB = otl.GSUB;
|
||||
font.GPOS = otl.GPOS;
|
||||
font.GDEF = otl.GDEF;
|
||||
otd.GSUB = otl.GSUB;
|
||||
otd.GPOS = otl.GPOS;
|
||||
otd.GDEF = otl.GDEF;
|
||||
|
||||
// Regulate
|
||||
const excludeChars = new Set();
|
||||
|
@ -28,6 +29,7 @@ module.exports = function (para) {
|
|||
}
|
||||
}
|
||||
|
||||
const finalGs = finalizeFont(para, gs.glyphStore, excludeChars, font);
|
||||
const finalGs = finalizeFont(para, gs.glyphStore, excludeChars, otd);
|
||||
const font = convertOtd(otd, finalGs);
|
||||
return { font, glyphStore: finalGs };
|
||||
};
|
||||
|
|
|
@ -76,20 +76,8 @@ module.exports = function () {
|
|||
ySuperscriptXSize: 665,
|
||||
ySuperscriptYOffset: 491,
|
||||
ySuperscriptYSize: 716,
|
||||
ulCodePageRange1: {
|
||||
latin1: true,
|
||||
latin2: true,
|
||||
greek: true,
|
||||
cyrillic: true,
|
||||
turkish: true,
|
||||
vietnamese: true,
|
||||
macRoman: true
|
||||
},
|
||||
ulCodePageRange2: {
|
||||
cp852: true,
|
||||
cp850: true,
|
||||
ascii: true
|
||||
}
|
||||
ulCodePageRange1: 0x2000011f,
|
||||
ulCodePageRange2: 0xc4000000
|
||||
},
|
||||
post: {
|
||||
version: 2,
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const Point = require("../../support/point");
|
||||
const { AnyCv } = require("../../support/gr");
|
||||
|
||||
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,
|
||||
ly = c[0].y;
|
||||
let buf = "";
|
||||
for (let j = 1; j < c.length; j++) {
|
||||
const z = c[j];
|
||||
buf += `${z.on ? "o" : "f"}${z.cubic ? "c" : "q"}${delta(z.x, lx)},${delta(z.y, ly)};`;
|
||||
(lx = z.x), (ly = z.y);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
function delta(a, b) {
|
||||
return Math.round((a - b) * 32);
|
||||
}
|
||||
|
||||
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++) {
|
||||
if (
|
||||
g1.contours[k - j].hash !== g2.contours[k].hash ||
|
||||
!(
|
||||
k <= j ||
|
||||
(delta(g1.contours[k - j][0].x, g1.contours[k - j - 1][0].x) ===
|
||||
delta(g2.contours[k][0].x, g2.contours[k - 1][0].x) &&
|
||||
delta(g1.contours[k - j][0].y, g1.contours[k - j - 1][0].y) ===
|
||||
delta(g2.contours[k][0].y, g2.contours[k - 1][0].y))
|
||||
)
|
||||
) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
const refX = g2.contours[j][0].x - g1.contours[0][0].x || 0;
|
||||
const refY = g2.contours[j][0].y - g1.contours[0][0].y || 0;
|
||||
if (Math.abs(refY) > 1 && g1.advanceWidth > 1) {
|
||||
continue;
|
||||
}
|
||||
if (!g2.references) g2.references = [];
|
||||
g2.references.push({ glyph: gn1, x: refX, y: refY, roundToGrid: false });
|
||||
g2.contours.splice(j, g1.contours.length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
for (let r of g.references) {
|
||||
contours = contours.concat(
|
||||
unlinkRef(glyphStore.queryByName(r.glyph), r.x + dx, r.y + dy, glyphStore)
|
||||
);
|
||||
}
|
||||
}
|
||||
return contours;
|
||||
}
|
||||
|
||||
module.exports = autoref;
|
|
@ -6,7 +6,7 @@ module.exports = function gcFont(glyphStore, excludedChars, restFont, cfg) {
|
|||
markSweepOtl(restFont.GSUB);
|
||||
markSweepOtl(restFont.GPOS);
|
||||
const sink = mark(glyphStore, excludedChars, restFont, cfg);
|
||||
return sweep(glyphStore, restFont, sink);
|
||||
return sweep(glyphStore, sink);
|
||||
};
|
||||
|
||||
function markSweepOtl(table) {
|
||||
|
@ -30,6 +30,44 @@ function markSweepOtl(table) {
|
|||
}
|
||||
table.features = features1;
|
||||
}
|
||||
|
||||
function mark(glyphStore, excludedChars, restFont, cfg) {
|
||||
const sink = markInitial(glyphStore, excludedChars);
|
||||
while (markStep(glyphStore, sink, restFont, cfg));
|
||||
return sink;
|
||||
}
|
||||
|
||||
function markInitial(glyphStore, excludedChars) {
|
||||
let sink = new Set();
|
||||
for (const [gName, g] of glyphStore.namedEntries()) {
|
||||
if (!g) continue;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sink;
|
||||
}
|
||||
|
||||
function markStep(glyphStore, sink, restFont, cfg) {
|
||||
const glyphCount = sink.size;
|
||||
if (restFont.GSUB) {
|
||||
for (const l in restFont.GSUB.lookups) {
|
||||
const lookup = restFont.GSUB.lookups[l];
|
||||
if (!lookup || !lookup.subtables) continue;
|
||||
for (let st of lookup.subtables) {
|
||||
markSubtable(sink, lookup.type, st, cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
const glyphCount1 = sink.size;
|
||||
return glyphCount1 > glyphCount;
|
||||
}
|
||||
|
||||
function markLookups(table, sink) {
|
||||
if (!table || !table.features) return;
|
||||
for (let f in table.features) {
|
||||
|
@ -57,43 +95,6 @@ function markLookups(table, sink) {
|
|||
} while (loop < 0xff && lookupSetChanged);
|
||||
}
|
||||
|
||||
function mark(glyphStore, excludedChars, restFont, cfg) {
|
||||
const sink = markInitial(glyphStore, excludedChars);
|
||||
while (markStep(sink, restFont, cfg));
|
||||
return sink;
|
||||
}
|
||||
|
||||
function markInitial(glyphStore, excludedChars) {
|
||||
let sink = new Set();
|
||||
for (const [gName, g] of glyphStore.namedEntries()) {
|
||||
if (!g) continue;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sink;
|
||||
}
|
||||
|
||||
function markStep(sink, restFont, cfg) {
|
||||
const glyphCount = sink.size;
|
||||
if (restFont.GSUB) {
|
||||
for (const l in restFont.GSUB.lookups) {
|
||||
const lookup = restFont.GSUB.lookups[l];
|
||||
if (!lookup || !lookup.subtables) continue;
|
||||
for (let st of lookup.subtables) {
|
||||
markSubtable(sink, lookup.type, st, cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
const glyphCount1 = sink.size;
|
||||
return glyphCount1 > glyphCount;
|
||||
}
|
||||
|
||||
function markSubtable(sink, type, st, cfg) {
|
||||
switch (type) {
|
||||
case "gsub_single":
|
||||
|
@ -127,156 +128,6 @@ function markSubtable(sink, type, st, cfg) {
|
|||
}
|
||||
}
|
||||
|
||||
function sweep(glyphStore, restFont, gnSet) {
|
||||
sweepOtl(restFont.GSUB, gnSet);
|
||||
sweepOtl(restFont.GPOS, gnSet);
|
||||
function sweep(glyphStore, gnSet) {
|
||||
return glyphStore.filterByName(gnSet);
|
||||
}
|
||||
|
||||
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, gnSet);
|
||||
if (keep) newSubtables.push(st);
|
||||
}
|
||||
lookup.subtables = newSubtables;
|
||||
}
|
||||
}
|
||||
|
||||
function sweepSubtable(st, type, gnSet) {
|
||||
switch (type) {
|
||||
case "gsub_single":
|
||||
return sweep_GsubSingle(st, gnSet);
|
||||
case "gsub_multiple":
|
||||
case "gsub_alternate":
|
||||
return sweep_GsubMultiple(st, gnSet);
|
||||
case "gsub_ligature":
|
||||
return sweep_GsubLigature(st, gnSet);
|
||||
case "gsub_chaining":
|
||||
return sweep_GsubChaining(st, gnSet);
|
||||
case "gsub_reverse":
|
||||
return sweep_gsubReverse(st, gnSet);
|
||||
case "gpos_mark_to_base":
|
||||
case "gpos_mark_to_mark":
|
||||
return sweep_gposMark(st, gnSet);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function sweep_GsubSingle(st, gnSet) {
|
||||
let nonEmpty = false;
|
||||
let from = Object.keys(st);
|
||||
for (const gidFrom of from) {
|
||||
if (!gnSet.has(gidFrom) || !gnSet.has(st[gidFrom])) {
|
||||
delete st[gidFrom];
|
||||
} else {
|
||||
nonEmpty = true;
|
||||
}
|
||||
}
|
||||
return nonEmpty;
|
||||
}
|
||||
|
||||
function sweep_GsubMultiple(st, gnSet) {
|
||||
let nonEmpty = false;
|
||||
let from = Object.keys(st);
|
||||
for (const gidFrom of from) {
|
||||
let include = gnSet.has(gidFrom);
|
||||
if (st[gidFrom]) {
|
||||
for (const gidTo of st[gidFrom]) {
|
||||
include = include && gnSet.has(gidTo);
|
||||
}
|
||||
} else {
|
||||
include = false;
|
||||
}
|
||||
if (!include) {
|
||||
delete st[gidFrom];
|
||||
} else {
|
||||
nonEmpty = true;
|
||||
}
|
||||
}
|
||||
return nonEmpty;
|
||||
}
|
||||
|
||||
function sweep_GsubLigature(st, gnSet) {
|
||||
if (!st.substitutions) return false;
|
||||
let newSubst = [];
|
||||
for (const rule of st.substitutions) {
|
||||
let include = true;
|
||||
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, 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 (gnSet.has(gidFrom)) {
|
||||
newMatch[j].push(gidFrom);
|
||||
}
|
||||
}
|
||||
if (!newMatch[j].length) return false;
|
||||
}
|
||||
st.match = newMatch;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = gnSet.has(gidFrom);
|
||||
if (j === st.inputIndex) {
|
||||
include = include && gnSet.has(st.to[k]);
|
||||
if (include) {
|
||||
newMatch[j].push(gidFrom);
|
||||
newTo.push(st.to[k]);
|
||||
}
|
||||
} else {
|
||||
if (include) newMatch[j].push(gidFrom);
|
||||
}
|
||||
}
|
||||
if (!newMatch[j].length) return false;
|
||||
}
|
||||
st.match = newMatch;
|
||||
st.to = newTo;
|
||||
return true;
|
||||
}
|
||||
|
||||
function sweep_gposMark(st, gnSet) {
|
||||
let marks = st.marks || {},
|
||||
newMarks = {},
|
||||
hasMarks = false;
|
||||
let bases = st.bases || {},
|
||||
newBases = {},
|
||||
hasBases = true;
|
||||
|
||||
for (const gid in marks) {
|
||||
if (gnSet.has(gid) && marks[gid]) {
|
||||
newMarks[gid] = marks[gid];
|
||||
hasMarks = true;
|
||||
}
|
||||
}
|
||||
for (const gid in bases) {
|
||||
if (gnSet.has(gid) && bases[gid]) {
|
||||
newBases[gid] = bases[gid];
|
||||
hasBases = true;
|
||||
}
|
||||
}
|
||||
st.marks = newMarks;
|
||||
st.bases = newBases;
|
||||
return hasMarks && hasBases;
|
||||
}
|
||||
|
|
144
font-src/gen/finalize/glyphs.js
Normal file
144
font-src/gen/finalize/glyphs.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
"use strict";
|
||||
|
||||
const TypoGeom = require("typo-geom");
|
||||
const Point = require("../../support/point");
|
||||
const CurveUtil = require("../../support/curve-util");
|
||||
|
||||
module.exports = finalizeGlyphs;
|
||||
function finalizeGlyphs(para, glyphStore) {
|
||||
suppressNaN(glyphStore);
|
||||
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
|
||||
regulateGlyphStore(skew, glyphStore);
|
||||
return 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 regulateGlyphStore(skew, glyphStore) {
|
||||
for (const g of glyphStore.glyphs()) {
|
||||
if (!g.semanticInclusions || !g.contours) continue;
|
||||
if (g.isPureComposite()) regulateCompositeGlyph(glyphStore, g);
|
||||
}
|
||||
for (const g of glyphStore.glyphs()) regulateSimpleGlyph(g, skew);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function regulateCompositeGlyph(glyphStore, g) {
|
||||
const references = [];
|
||||
for (const sr of g.semanticInclusions) {
|
||||
const gn = glyphStore.queryNameOf(sr.glyph);
|
||||
if (!gn || sr.glyph.autoRefPriority < 0) return;
|
||||
references.push({ glyph: gn, x: sr.x, y: sr.y });
|
||||
}
|
||||
|
||||
g.semanticInclusions = [];
|
||||
g.contours = [];
|
||||
g.references = references;
|
||||
}
|
||||
|
||||
function regulateSimpleGlyph(g, skew) {
|
||||
if (!g.contours || !g.contours.length) return;
|
||||
for (const contour of g.contours) for (const z of contour) z.x -= z.y * skew;
|
||||
g.contours = simplifyContours(g.contours);
|
||||
for (const contour of g.contours) for (const z of contour) z.x += z.y * skew;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function simplifyContours(source) {
|
||||
const sink = new FairizedShapeSink();
|
||||
|
||||
TypoGeom.ShapeConv.transferGenericShape(
|
||||
TypoGeom.Fairize.fairizeBezierShape(
|
||||
TypoGeom.Boolean.removeOverlap(
|
||||
CurveUtil.convertShapeToArcs(source),
|
||||
TypoGeom.Boolean.PolyFillType.pftNonZero,
|
||||
CurveUtil.BOOLE_RESOLUTION
|
||||
)
|
||||
),
|
||||
sink,
|
||||
CurveUtil.GEOMETRY_PRECISION
|
||||
);
|
||||
|
||||
return sink.contours;
|
||||
}
|
||||
|
||||
class FairizedShapeSink {
|
||||
constructor() {
|
||||
this.contours = [];
|
||||
this.lastContour = [];
|
||||
}
|
||||
beginShape() {}
|
||||
endShape() {
|
||||
if (this.lastContour.length > 2) {
|
||||
const zFirst = this.lastContour[0],
|
||||
zLast = this.lastContour[this.lastContour.length - 1];
|
||||
if (zFirst.on && zLast.on && zFirst.x === zLast.x && zFirst.y === zLast.y) {
|
||||
this.lastContour.pop();
|
||||
}
|
||||
this.contours.push(this.lastContour);
|
||||
}
|
||||
this.lastContour = [];
|
||||
}
|
||||
moveTo(x, y) {
|
||||
this.endShape();
|
||||
this.lineTo(x, y);
|
||||
}
|
||||
lineTo(x, y) {
|
||||
const z = Point.cornerFromXY(x, y).round(CurveUtil.RECIP_GEOMETRY_PRECISION);
|
||||
while (this.lastContour.length >= 2) {
|
||||
const a = this.lastContour[this.lastContour.length - 2],
|
||||
b = this.lastContour[this.lastContour.length - 1];
|
||||
if (isLineExtend(a, b, z)) {
|
||||
this.lastContour.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.lastContour.push(z);
|
||||
}
|
||||
arcTo(arc, x, y) {
|
||||
const offPoints = TypoGeom.Quadify.auto(arc, 1, 16);
|
||||
if (offPoints) {
|
||||
for (const z of offPoints)
|
||||
this.lastContour.push(Point.offFrom(z).round(CurveUtil.RECIP_GEOMETRY_PRECISION));
|
||||
}
|
||||
this.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
function isLineExtend(a, b, c) {
|
||||
return (
|
||||
a.on &&
|
||||
c.on &&
|
||||
((aligned(a.x, b.x, c.x) && between(a.y, b.y, c.y)) ||
|
||||
(aligned(a.y, b.y, c.y) && between(a.x, b.x, c.x)))
|
||||
);
|
||||
}
|
||||
function geometryPrecisionEqual(a, b) {
|
||||
return (
|
||||
Math.round(a * CurveUtil.RECIP_GEOMETRY_PRECISION) ===
|
||||
Math.round(b * CurveUtil.RECIP_GEOMETRY_PRECISION)
|
||||
);
|
||||
}
|
||||
function aligned(a, b, c) {
|
||||
return geometryPrecisionEqual(a, b) && geometryPrecisionEqual(b, c);
|
||||
}
|
||||
function between(a, b, c) {
|
||||
return (a <= b && b <= c) || (a >= b && b >= c);
|
||||
}
|
|
@ -1,17 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
const autoRef = require("./autoref");
|
||||
const TypoGeom = require("typo-geom");
|
||||
const Point = require("../../support/point");
|
||||
|
||||
const CurveUtil = require("../../support/curve-util");
|
||||
const finalizeGlyphs = require("./glyphs");
|
||||
const gcFont = require("./gc");
|
||||
|
||||
module.exports = function finalizeFont(para, glyphStore, excludedCodePoints, font) {
|
||||
module.exports = function finalizeFont(para, glyphStore, excludedCodePoints, restFont) {
|
||||
glyphStore = forceMonospaceIfNeeded(para, glyphStore);
|
||||
glyphStore = gcFont(glyphStore, excludedCodePoints, font, {});
|
||||
glyphStore = regulateGlyphStore(para, glyphStore);
|
||||
extractGlyfCmap(glyphStore, font);
|
||||
glyphStore = gcFont(glyphStore, excludedCodePoints, restFont, {});
|
||||
glyphStore = finalizeGlyphs(para, glyphStore);
|
||||
return glyphStore;
|
||||
};
|
||||
|
||||
|
@ -25,121 +20,3 @@ function forceMonospaceIfNeeded(para, glyphStore) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function extractGlyfCmap(glyphStore, font) {
|
||||
const glyf = {};
|
||||
const cmap = {};
|
||||
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 regulateGlyphStore(para, glyphStore) {
|
||||
autoRef(glyphStore);
|
||||
|
||||
// regulate
|
||||
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
|
||||
for (let g of glyphStore.glyphs()) regulateGlyph(g, skew);
|
||||
|
||||
return glyphStore;
|
||||
}
|
||||
|
||||
function byRank([ja, gna, a], [jb, gnb, b]) {
|
||||
return (b.glyphRank || 0) - (a.glyphRank || 0) || (ja || 0) - (jb || 0);
|
||||
}
|
||||
|
||||
function regulateGlyph(g, skew) {
|
||||
if (!g.contours || !g.contours.length) return;
|
||||
for (const contour of g.contours) for (const z of contour) z.x -= z.y * skew;
|
||||
g.contours = simplifyContours(g.contours);
|
||||
for (const contour of g.contours) for (const z of contour) z.x += z.y * skew;
|
||||
}
|
||||
|
||||
function simplifyContours(source) {
|
||||
const sink = new FairizedShapeSink();
|
||||
|
||||
TypoGeom.ShapeConv.transferGenericShape(
|
||||
TypoGeom.Fairize.fairizeBezierShape(
|
||||
TypoGeom.Boolean.removeOverlap(
|
||||
CurveUtil.convertShapeToArcs(source),
|
||||
TypoGeom.Boolean.PolyFillType.pftNonZero,
|
||||
CurveUtil.BOOLE_RESOLUTION
|
||||
)
|
||||
),
|
||||
sink,
|
||||
CurveUtil.GEOMETRY_PRECISION
|
||||
);
|
||||
|
||||
return sink.contours;
|
||||
}
|
||||
|
||||
class FairizedShapeSink {
|
||||
constructor() {
|
||||
this.contours = [];
|
||||
this.lastContour = [];
|
||||
}
|
||||
beginShape() {}
|
||||
endShape() {
|
||||
if (this.lastContour.length > 2) {
|
||||
const zFirst = this.lastContour[0],
|
||||
zLast = this.lastContour[this.lastContour.length - 1];
|
||||
if (zFirst.on && zLast.on && zFirst.x === zLast.x && zFirst.y === zLast.y) {
|
||||
this.lastContour.pop();
|
||||
}
|
||||
this.contours.push(this.lastContour);
|
||||
}
|
||||
this.lastContour = [];
|
||||
}
|
||||
moveTo(x, y) {
|
||||
this.endShape();
|
||||
this.lineTo(x, y);
|
||||
}
|
||||
lineTo(x, y) {
|
||||
const z = Point.cornerFromXY(x, y).round(CurveUtil.RECIP_GEOMETRY_PRECISION);
|
||||
while (this.lastContour.length >= 2) {
|
||||
const a = this.lastContour[this.lastContour.length - 2],
|
||||
b = this.lastContour[this.lastContour.length - 1];
|
||||
if (isLineExtend(a, b, z)) {
|
||||
this.lastContour.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.lastContour.push(z);
|
||||
}
|
||||
arcTo(arc, x, y) {
|
||||
const offPoints = TypoGeom.Quadify.auto(arc, 1, 16);
|
||||
if (offPoints) {
|
||||
for (const z of offPoints)
|
||||
this.lastContour.push(Point.offFrom(z).round(CurveUtil.RECIP_GEOMETRY_PRECISION));
|
||||
}
|
||||
this.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
function isLineExtend(a, b, c) {
|
||||
return (
|
||||
a.on &&
|
||||
c.on &&
|
||||
((aligned(a.x, b.x, c.x) && between(a.y, b.y, c.y)) ||
|
||||
(aligned(a.y, b.y, c.y) && between(a.x, b.x, c.x)))
|
||||
);
|
||||
}
|
||||
function geometryPrecisionEqual(a, b) {
|
||||
return (
|
||||
Math.round(a * CurveUtil.RECIP_GEOMETRY_PRECISION) ===
|
||||
Math.round(b * CurveUtil.RECIP_GEOMETRY_PRECISION)
|
||||
);
|
||||
}
|
||||
function aligned(a, b, c) {
|
||||
return geometryPrecisionEqual(a, b) && geometryPrecisionEqual(b, c);
|
||||
}
|
||||
function between(a, b, c) {
|
||||
return (a <= b && b <= c) || (a >= b && b >= c);
|
||||
}
|
||||
|
|
94
font-src/gen/otd-conv/glyphs.js
Normal file
94
font-src/gen/otd-conv/glyphs.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
const { Ot } = require("ot-builder");
|
||||
|
||||
class NamedGlyphStore {
|
||||
constructor() {
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
declare(name) {
|
||||
const g = new Ot.Glyph();
|
||||
g.name = name;
|
||||
this.m_mapping.set(name, g);
|
||||
}
|
||||
query(name) {
|
||||
return this.m_mapping.get(name);
|
||||
}
|
||||
|
||||
decideOrder() {
|
||||
const gs = Ot.ListGlyphStoreFactory.createStoreFromList([...this.m_mapping.values()]);
|
||||
return gs.decideOrder();
|
||||
}
|
||||
fill(name, data) {
|
||||
const g = this.query(name);
|
||||
if (!g) return;
|
||||
|
||||
g.horizontal = { start: 0, end: data.advanceWidth };
|
||||
if (data.references && data.references.length) {
|
||||
this.fillReferences(g, data);
|
||||
} else if (data.contours && data.contours.length) {
|
||||
this.fillContours(g, data);
|
||||
}
|
||||
}
|
||||
|
||||
fillReferences(g, data) {
|
||||
const gl = new Ot.Glyph.GeometryList();
|
||||
for (const ref of data.references) {
|
||||
const target = this.query(ref.glyph);
|
||||
if (!target) continue;
|
||||
const tfm = Ot.Glyph.Transform2X3.Translate(ref.x, ref.y);
|
||||
gl.items.push(new Ot.Glyph.TtReference(target, tfm));
|
||||
}
|
||||
g.geometry = gl;
|
||||
}
|
||||
fillContours(g, data) {
|
||||
const cs = new Ot.Glyph.ContourSet();
|
||||
for (const c of data.contours) {
|
||||
const c1 = [];
|
||||
for (const z of c) {
|
||||
c1.push(
|
||||
Ot.Glyph.Point.create(
|
||||
z.x,
|
||||
z.y,
|
||||
z.on ? Ot.Glyph.PointType.Corner : Ot.Glyph.PointType.Quad
|
||||
)
|
||||
);
|
||||
}
|
||||
cs.contours.push(c1);
|
||||
}
|
||||
g.geometry = cs;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = convertGlyphs;
|
||||
function convertGlyphs(gsOrig) {
|
||||
const sortedEntries = Array.from(gsOrig.indexedNamedEntries())
|
||||
.map(([j, gn, g]) => [j, gn, queryOrderingUnicode(gsOrig, g), g])
|
||||
.sort(byRank);
|
||||
|
||||
const gs = new NamedGlyphStore();
|
||||
const cmap = new Ot.Cmap.Table();
|
||||
|
||||
for (const [origIndex, name, uOrd, g] of sortedEntries) {
|
||||
gs.declare(name);
|
||||
const us = gsOrig.queryUnicodeOf(g);
|
||||
if (us) {
|
||||
for (const u of us) {
|
||||
if (isFinite(u - 0) && u) {
|
||||
cmap.unicode.set(u, gs.query(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [origIndex, name, uOrd, g] of sortedEntries) gs.fill(name, g);
|
||||
|
||||
return { glyphs: gs, cmap };
|
||||
}
|
||||
function queryOrderingUnicode(gs, g) {
|
||||
const us = gs.queryUnicodeOf(g);
|
||||
if (us && us.size) return Array.from(us).sort((a, b) => a - b)[0];
|
||||
else return 0xffffff;
|
||||
}
|
||||
function byRank([ja, gna, ua, a], [jb, gnb, ub, b]) {
|
||||
return (
|
||||
(b.glyphRank || 0) - (a.glyphRank || 0) || (ua || 0) - (ub || 0) || (ja || 0) - (jb || 0)
|
||||
);
|
||||
}
|
22
font-src/gen/otd-conv/index.js
Normal file
22
font-src/gen/otd-conv/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const Metadata = require("./metadata");
|
||||
const convertGlyphs = require("./glyphs");
|
||||
const convertName = require("./name");
|
||||
const { convertGsub, convertGpos, convertGdef } = require("./layout");
|
||||
|
||||
module.exports = function (otdRestFont, gs) {
|
||||
const head = Metadata.convertHead(otdRestFont.head);
|
||||
const hhea = Metadata.convertHhea(otdRestFont.hhea);
|
||||
const post = Metadata.convertPost(otdRestFont.post);
|
||||
const maxp = Metadata.convertMaxp(otdRestFont.maxp);
|
||||
const os2 = Metadata.convertOs2(otdRestFont.OS_2);
|
||||
|
||||
const name = convertName(otdRestFont.name);
|
||||
|
||||
const { glyphs, cmap } = convertGlyphs(gs);
|
||||
|
||||
const gsub = convertGsub(otdRestFont.GSUB, glyphs);
|
||||
const gpos = convertGpos(otdRestFont.GPOS, glyphs);
|
||||
const gdef = convertGdef(otdRestFont.GDEF, glyphs);
|
||||
|
||||
return { glyphs, head, hhea, post, maxp, os2, name, cmap, gsub, gpos, gdef };
|
||||
};
|
331
font-src/gen/otd-conv/layout.js
Normal file
331
font-src/gen/otd-conv/layout.js
Normal file
|
@ -0,0 +1,331 @@
|
|||
const { Ot } = require("ot-builder");
|
||||
|
||||
class LookupStore {
|
||||
constructor(handlers, glyphs) {
|
||||
this.glyphs = glyphs;
|
||||
this.m_handlers = handlers;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
|
||||
declare(id, otdLookup) {
|
||||
if (this.m_mapping.has(id)) return;
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!handler) return;
|
||||
this.m_mapping.set(id, handler.init());
|
||||
}
|
||||
fill(id, otdLookup) {
|
||||
const dst = this.query(id);
|
||||
const handler = this.m_handlers[otdLookup.type];
|
||||
if (!dst || !handler) return;
|
||||
|
||||
handler.fill(dst, otdLookup, this);
|
||||
}
|
||||
}
|
||||
|
||||
const GsubSingleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Single();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
for (const st of src.subtables) {
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.query(k);
|
||||
const to = store.glyphs.query(st[k]);
|
||||
if (from && to) dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubMultipleHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Multiple();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
for (const st of src.subtables) {
|
||||
out: for (const k in st) {
|
||||
const from = store.glyphs.query(k);
|
||||
const to = mapGlyphListAll(st[k], store);
|
||||
if (!from || !to) continue out;
|
||||
dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const GsubAlternateHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Alternate();
|
||||
},
|
||||
fill: GsubMultipleHandler.fill
|
||||
};
|
||||
const GsubLigatureHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Ligature();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
for (const st of src.subtables) {
|
||||
for (const { from: _from, to: _to } of st.substitutions) {
|
||||
const to = store.glyphs.query(_to);
|
||||
const from = mapGlyphListAll(_from, store);
|
||||
if (!from || !to) continue;
|
||||
dst.mapping.push({ from, to });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const GsubChainingHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.Chaining();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.subtables) {
|
||||
const match = [];
|
||||
for (const m of st.match) {
|
||||
const m1 = mapGlyphListSome(m, store);
|
||||
if (!m1) continue out;
|
||||
match.push(new Set(m1));
|
||||
}
|
||||
const inputBegins = st.inputBegins;
|
||||
const inputEnds = st.inputEnds;
|
||||
const applications = [];
|
||||
for (const ap of st.apply) {
|
||||
const lookup = store.query(ap.lookup);
|
||||
if (!lookup) continue out;
|
||||
applications.push({ at: ap.at - inputBegins, apply: lookup });
|
||||
}
|
||||
dst.rules.push({ match, inputBegins, inputEnds, applications });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const GsubReverseHandler = {
|
||||
init() {
|
||||
return new Ot.Gsub.ReverseSub();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
out: for (const st of src.subtables) {
|
||||
const match = [];
|
||||
const doSubAt = st.inputIndex;
|
||||
const replacement = new Map();
|
||||
for (let j = 0; j < st.match.length; j++) {
|
||||
{
|
||||
const m1 = new Set();
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.query(st.match[j][k]);
|
||||
if (gFrom) m1.add(gFrom);
|
||||
}
|
||||
if (!m1.size) continue out;
|
||||
match.push(m1);
|
||||
}
|
||||
|
||||
if (j === doSubAt) {
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.query(st.match[j][k]);
|
||||
const gTo = store.glyphs.query(st.to[k]);
|
||||
if (!gFrom) continue;
|
||||
if (gTo) {
|
||||
replacement.set(gFrom, gTo);
|
||||
} else {
|
||||
replacement.set(gFrom, gFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.rules.push({ match, doSubAt, replacement });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function mapGlyphListAll(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.query(item);
|
||||
if (!fg) return null;
|
||||
out.push(fg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function mapGlyphListSome(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.query(item);
|
||||
if (!fg) continue;
|
||||
out.push(fg);
|
||||
}
|
||||
if (!out.length) return null;
|
||||
return out;
|
||||
}
|
||||
|
||||
const GsubHandlers = {
|
||||
gsub_single: GsubSingleHandler,
|
||||
gsub_multiple: GsubMultipleHandler,
|
||||
gsub_alternate: GsubAlternateHandler,
|
||||
gsub_ligature: GsubLigatureHandler,
|
||||
gsub_chaining: GsubChainingHandler,
|
||||
gsub_reverse: GsubReverseHandler
|
||||
};
|
||||
|
||||
const GposMarkToBaseHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToBase();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.subtables[0];
|
||||
const mm = collectClassMap(st.marks);
|
||||
dst.marks = convertMarkRecords(st.marks, mm, store);
|
||||
dst.bases = convertBaseRecords(st.bases, mm, store);
|
||||
}
|
||||
};
|
||||
const GposMarkToMarkHandler = {
|
||||
init() {
|
||||
return new Ot.Gpos.MarkToMark();
|
||||
},
|
||||
fill(dst, src, store) {
|
||||
const st = src.subtables[0];
|
||||
const mm = collectClassMap(st.marks);
|
||||
dst.marks = convertMarkRecords(st.marks, mm, store);
|
||||
dst.baseMarks = convertBaseRecords(st.bases, mm, store);
|
||||
}
|
||||
};
|
||||
function collectClassMap(marks) {
|
||||
let n = 0;
|
||||
const m = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
if (!m.has(mark.class)) {
|
||||
m.set(mark.class, n);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
function convertMarkRecords(marks, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
const g = store.glyphs.query(gn);
|
||||
if (!g) continue;
|
||||
let markAnchors = [];
|
||||
markAnchors[mm.get(mark.class)] = { x: mark.x, y: mark.y };
|
||||
out.set(g, { markAnchors: markAnchors });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function convertBaseRecords(bases, mm, store) {
|
||||
const out = new Map();
|
||||
for (const gn in bases) {
|
||||
const baseObj = bases[gn];
|
||||
const g = store.glyphs.query(gn);
|
||||
if (!g) continue;
|
||||
const baseArray = [];
|
||||
for (const bkStr in baseObj) {
|
||||
baseArray[mm.get(bkStr)] = baseObj[bkStr];
|
||||
}
|
||||
out.set(g, { baseAnchors: baseArray });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const GposHandlers = {
|
||||
gpos_mark_to_base: GposMarkToBaseHandler,
|
||||
gpos_mark_to_mark: GposMarkToMarkHandler
|
||||
};
|
||||
|
||||
class FeatureStore {
|
||||
constructor(lookups) {
|
||||
this.lookupStore = lookups;
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return Array.from(this.m_mapping.values());
|
||||
}
|
||||
query(id) {
|
||||
return this.m_mapping.get(id);
|
||||
}
|
||||
fill(id, data) {
|
||||
const tag = id.slice(0, 4);
|
||||
const lookups = [];
|
||||
for (const lid of data) {
|
||||
const lookup = this.lookupStore.query(lid);
|
||||
if (lookup) lookups.push(lookup);
|
||||
}
|
||||
this.m_mapping.set(id, { tag, lookups });
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptLanguageStore {
|
||||
constructor(features) {
|
||||
this.featureStore = features;
|
||||
this.m_scriptMapping = new Map();
|
||||
}
|
||||
extract() {
|
||||
return this.m_scriptMapping;
|
||||
}
|
||||
fill(id, data) {
|
||||
const scriptTag = id.slice(0, 4);
|
||||
const languageTag = id.slice(5, 9).padEnd(4);
|
||||
let sr = this.m_scriptMapping.get(scriptTag);
|
||||
if (!sr) {
|
||||
sr = { defaultLanguage: null, languages: new Map() };
|
||||
this.m_scriptMapping.set(scriptTag, sr);
|
||||
}
|
||||
|
||||
const lr = this.createLanguageRecord(data);
|
||||
if (languageTag === "dflt" || languageTag === "DFLT") sr.defaultLanguage = lr;
|
||||
else sr.languages.set(languageTag, lr);
|
||||
}
|
||||
createLanguageRecord(data) {
|
||||
const features = [];
|
||||
for (const fid of data.features) {
|
||||
const feature = this.featureStore.query(fid);
|
||||
if (feature) features.push(feature);
|
||||
}
|
||||
return {
|
||||
requiredFeature: this.featureStore.query(data.requiredFeature) || null,
|
||||
features: features
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
exports.convertGsub = ConvertGsubGposT(GsubHandlers, Ot.Gsub.Table);
|
||||
exports.convertGpos = ConvertGsubGposT(GposHandlers, Ot.Gpos.Table);
|
||||
|
||||
function ConvertGsubGposT(handlers, T) {
|
||||
return function (table, glyphs) {
|
||||
if (!table) return null;
|
||||
const ls = new LookupStore(handlers, glyphs);
|
||||
if (table.lookups) {
|
||||
if (table.lookupOrder) {
|
||||
for (const l of table.lookupOrder) ls.declare(l, table.lookups[l]);
|
||||
}
|
||||
for (const l in table.lookups) ls.declare(l, table.lookups[l]);
|
||||
for (const l in table.lookups) ls.fill(l, table.lookups[l]);
|
||||
}
|
||||
const fs = new FeatureStore(ls);
|
||||
if (table.features) {
|
||||
for (const f in table.features) fs.fill(f, table.features[f]);
|
||||
}
|
||||
const ss = new ScriptLanguageStore(fs);
|
||||
if (table.languages) {
|
||||
for (const sl in table.languages) ss.fill(sl, table.languages[sl]);
|
||||
}
|
||||
|
||||
return new T(ss.extract(), fs.extract(), ls.extract());
|
||||
};
|
||||
}
|
||||
|
||||
exports.convertGdef = convertGdef;
|
||||
function convertGdef(otdGdef, glyphs) {
|
||||
const gdef = new Ot.Gdef.Table();
|
||||
gdef.glyphClassDef = new Map();
|
||||
for (const gn in otdGdef.glyphClassDef) {
|
||||
const g = glyphs.query(gn);
|
||||
if (g) gdef.glyphClassDef.set(g, otdGdef.glyphClassDef[gn]);
|
||||
}
|
||||
return gdef;
|
||||
}
|
98
font-src/gen/otd-conv/metadata.js
Normal file
98
font-src/gen/otd-conv/metadata.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
const { Ot } = require("ot-builder");
|
||||
|
||||
exports.convertHead = convertHead;
|
||||
function convertHead(otdHead) {
|
||||
const head = new Ot.Head.Table();
|
||||
head.fontRevision = otdHead.fontRevision;
|
||||
head.flags = otdHead.flags;
|
||||
head.fontDirectionHint = otdHead.fontDirectionHint;
|
||||
head.unitsPerEm = otdHead.unitsPerEm;
|
||||
head.macStyle = macStyleObjToEnum(otdHead.macStyle);
|
||||
head.lowestRecPPEM = otdHead.lowestRecPPEM;
|
||||
head.glyphDataFormat = otdHead.glyphDataFormat;
|
||||
return head;
|
||||
}
|
||||
|
||||
exports.convertHhea = convertHhea;
|
||||
function convertHhea(otdHhea) {
|
||||
const hhea = new Ot.MetricHead.Hhea();
|
||||
hhea.ascender = otdHhea.ascender;
|
||||
hhea.descender = otdHhea.descender;
|
||||
hhea.lineGap = otdHhea.lineGap;
|
||||
hhea.caretSlopeRise = otdHhea.caretSlopeRise;
|
||||
hhea.caretSlopeRun = otdHhea.caretSlopeRun;
|
||||
hhea.caretOffset = otdHhea.caretOffset;
|
||||
return hhea;
|
||||
}
|
||||
|
||||
exports.convertPost = convertPost;
|
||||
function convertPost(otdPost) {
|
||||
const post = new Ot.Post.Table(3, 0);
|
||||
post.italicAngle = otdPost.italicAngle;
|
||||
post.underlinePosition = otdPost.underlinePosition;
|
||||
post.underlineThickness = otdPost.underlineThickness;
|
||||
post.isFixedPitch = otdPost.isFixedPitch;
|
||||
return post;
|
||||
}
|
||||
|
||||
exports.convertMaxp = convertMaxp;
|
||||
function convertMaxp(otdMaxp) {
|
||||
const maxp = Ot.Maxp.Table.TrueType();
|
||||
return maxp;
|
||||
}
|
||||
|
||||
exports.convertOs2 = convertOs2;
|
||||
function convertOs2(otdOs2) {
|
||||
const os2 = new Ot.Os2.Table(4);
|
||||
os2.achVendID = otdOs2.achVendID;
|
||||
os2.panose = otdOs2.panose;
|
||||
os2.fsSelection = fsSelectionObjToEnum(otdOs2.fsSelection);
|
||||
os2.fsType = otdOs2.fsType;
|
||||
os2.sCapHeight = otdOs2.sCapHeight;
|
||||
os2.sFamilyClass = otdOs2.sFamilyClass;
|
||||
os2.sTypoAscender = otdOs2.sTypoAscender;
|
||||
os2.sTypoDescender = otdOs2.sTypoDescender;
|
||||
os2.sTypoLineGap = otdOs2.sTypoLineGap;
|
||||
os2.sxHeight = otdOs2.sxHeight;
|
||||
os2.usBreakChar = otdOs2.usBreakChar;
|
||||
os2.usDefaultChar = otdOs2.usDefaultChar;
|
||||
os2.usFirstCharIndex = otdOs2.usFirstCharIndex;
|
||||
os2.usLastCharIndex = otdOs2.usLastCharIndex;
|
||||
os2.usMaxContext = otdOs2.usMaxContext;
|
||||
os2.usWeightClass = otdOs2.usWeightClass;
|
||||
os2.usWidthClass = otdOs2.usWidthClass;
|
||||
os2.usWinAscent = otdOs2.usWinAscent;
|
||||
os2.usWinDescent = otdOs2.usWinDescent;
|
||||
os2.xAvgCharWidth = otdOs2.xAvgCharWidth;
|
||||
os2.yStrikeoutPosition = otdOs2.yStrikeoutPosition;
|
||||
os2.yStrikeoutSize = otdOs2.yStrikeoutSize;
|
||||
os2.ySubscriptXOffset = otdOs2.ySubscriptXOffset;
|
||||
os2.ySubscriptXSize = otdOs2.ySubscriptXSize;
|
||||
os2.ySubscriptYOffset = otdOs2.ySubscriptYOffset;
|
||||
os2.ySubscriptYSize = otdOs2.ySubscriptYSize;
|
||||
os2.ySuperscriptXOffset = otdOs2.ySuperscriptXOffset;
|
||||
os2.ySuperscriptXSize = otdOs2.ySuperscriptXSize;
|
||||
os2.ySuperscriptYOffset = otdOs2.ySuperscriptYOffset;
|
||||
os2.ySuperscriptYSize = otdOs2.ySuperscriptYSize;
|
||||
os2.ulCodePageRange1 = otdOs2.ulCodePageRange1;
|
||||
os2.ulCodePageRange2 = otdOs2.ulCodePageRange2;
|
||||
return os2;
|
||||
}
|
||||
|
||||
function macStyleObjToEnum(o) {
|
||||
return (
|
||||
(o.bold ? Ot.Head.MacStyle.Bold : 0) |
|
||||
(o.italic ? Ot.Head.MacStyle.Italic : 0) |
|
||||
(o.condensed ? Ot.Head.MacStyle.Condensed : 0) |
|
||||
(o.extended ? Ot.Head.MacStyle.Extended : 0)
|
||||
);
|
||||
}
|
||||
function fsSelectionObjToEnum(o) {
|
||||
return (
|
||||
(o.oblique ? Ot.Os2.FsSelection.OBLIQUE : 0) |
|
||||
(o.bold ? Ot.Os2.FsSelection.BOLD : 0) |
|
||||
(o.italic ? Ot.Os2.FsSelection.ITALIC : 0) |
|
||||
(o.regular ? Ot.Os2.FsSelection.REGULAR : 0) |
|
||||
(o.useTypoMetrics ? Ot.Os2.FsSelection.USE_TYPO_METRICS : 0)
|
||||
);
|
||||
}
|
20
font-src/gen/otd-conv/name.js
Normal file
20
font-src/gen/otd-conv/name.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const { Ot } = require("ot-builder");
|
||||
|
||||
module.exports = convertName;
|
||||
function convertName(otdName) {
|
||||
const name = new Ot.Name.Table();
|
||||
for (const entry of otdName) {
|
||||
name.records.push({
|
||||
platformID: entry.platformID,
|
||||
encodingID: entry.encodingID,
|
||||
languageID: entry.languageID,
|
||||
nameID: entry.nameID,
|
||||
value:
|
||||
entry.platformID === 3 && entry.encodingID === 1
|
||||
? entry.nameString
|
||||
: Buffer.from(entry.nameString, "utf-8")
|
||||
});
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
|
@ -114,14 +114,15 @@ glyph-block AutoBuild-Accents : begin
|
|||
|
||||
define construction : glyph-proc
|
||||
include s_parts.0 AS_BASE ALSO_METRICS
|
||||
local nonTrivial false
|
||||
foreach part [items-of : s_parts.slice 1] : if part : begin
|
||||
include part
|
||||
if (part.markAnchors && part.markAnchors.bottomright) : begin
|
||||
eject-contour 'serifRB'
|
||||
if [AnyDerivingCv.query s_parts.0] : begin
|
||||
set nonTrivial true
|
||||
if ([not nonTrivial] && [AnyDerivingCv.query s_parts.0]) : begin
|
||||
local decomposeParts { }
|
||||
foreach part [items-of s_parts] : begin
|
||||
recursivelyDecompose part decomposeParts
|
||||
foreach part [items-of s_parts] : recursivelyDecompose part decomposeParts
|
||||
CvDecompose.set currentGlyph decomposeParts
|
||||
|
||||
define [RootGlyphProc goalName code parts] : begin
|
||||
|
@ -195,12 +196,8 @@ define customDecompositions : object
|
|||
."\u0247" "e\u0337"
|
||||
|
||||
."\u0290" "z\u0322"
|
||||
."\u0256" "d\u0322"
|
||||
."\u0273" "n\u0322"
|
||||
."\u01AE" "T\u0322"
|
||||
."\u1D8F" "a\u0322"
|
||||
."\u1D90" "\u0251\u0322"
|
||||
."\u1D91" "\u0257\u0322"
|
||||
|
||||
."\u1E10" "D\u0326"
|
||||
."\u1E11" "d\u0326"
|
||||
|
|
|
@ -3,6 +3,8 @@ $$include '../../meta/macros.ptl'
|
|||
|
||||
import [mix linreg clamp fallback] from '../../support/utils'
|
||||
import [getGrMesh AnyDerivingCv CvDecompose] from "../../support/gr"
|
||||
extern Map
|
||||
extern Set
|
||||
|
||||
glyph-module
|
||||
|
||||
|
@ -27,12 +29,16 @@ glyph-block Autobuild-Enclosure-Shared : begin
|
|||
|
||||
glyph-block-export EnsureComponentGlyphT
|
||||
define [EnsureComponentGlyphT gidPart fnBuildup] : begin
|
||||
local rs : new Set
|
||||
local gniPart : fnBuildup gidPart
|
||||
local grs : AnyDerivingCv.query [query-glyph gidPart]
|
||||
if grs : foreach gr [items-of grs] : begin
|
||||
local relatedGidPart : gr.get [query-glyph gidPart]
|
||||
local gniRelated : fnBuildup relatedGidPart
|
||||
if [query-glyph gniPart] : gr.set [query-glyph gniPart] gniRelated
|
||||
if [query-glyph gniPart] : begin
|
||||
gr.set [query-glyph gniPart] gniRelated
|
||||
if [query-glyph gniRelated] : begin
|
||||
[query-glyph gniPart].tryBecomeMirrorOf [query-glyph gniRelated] rs
|
||||
return gniPart
|
||||
|
||||
glyph-block-export CollectJobs
|
||||
|
@ -78,8 +84,11 @@ glyph-block Autobuild-Enclosure-Shared : begin
|
|||
|
||||
glyph-block-export applyRelations
|
||||
define [applyRelations relApplications] : begin
|
||||
local rs : new Set
|
||||
foreach {gr f t} [items-of relApplications] : begin
|
||||
if [query-glyph f] : gr.set [query-glyph f] t
|
||||
if [query-glyph f] : begin
|
||||
gr.set [query-glyph f] t
|
||||
if [query-glyph t] : [query-glyph t].tryBecomeMirrorOf [query-glyph f] rs
|
||||
|
||||
glyph-block AutoBuild-Enclosure : begin
|
||||
glyph-block-import CommonShapes
|
||||
|
@ -91,9 +100,18 @@ glyph-block AutoBuild-Enclosure : begin
|
|||
define [AdjustDigitCount digits width] : Math.max 1 (digits * Width / width)
|
||||
define [EnclosureStrokeScale dscale digits width] : dscale / [Math.pow [AdjustDigitCount digits width] 0.66]
|
||||
|
||||
define aliasableInnerSubGlyphMap : new Map
|
||||
define [EnsureInnerSubGlyphImpl miniatureFont prefix finalPlacement dscale xCompress shift] : lambda [gidPart] : begin
|
||||
define gniPartNoPlc : '.ci.' + gidPart + '@' + [{ prefix dscale xCompress shift }.join '/']
|
||||
define gniPart : '.ci.' + gidPart + '@' + [{ prefix finalPlacement dscale xCompress shift }.join '/']
|
||||
if [not : query-glyph gniPart] : create-glyph gniPart : glyph-proc
|
||||
define aliasGoal : aliasableInnerSubGlyphMap.get gniPartNoPlc
|
||||
if [not : query-glyph gniPart] : begin
|
||||
if aliasGoal : begin
|
||||
create-glyph gniPart : composite-proc
|
||||
refer-glyph aliasGoal.gn
|
||||
Translate (finalPlacement - aliasGoal.placement) 0
|
||||
: else : begin
|
||||
create-glyph gniPart : glyph-proc
|
||||
set-width 0
|
||||
include : miniatureFont.queryByNameEnsured gidPart
|
||||
include : Upright
|
||||
|
@ -103,6 +121,9 @@ glyph-block AutoBuild-Enclosure : begin
|
|||
include : Translate 0 (SymbolMid - CAP * dscale / 2)
|
||||
include : Italify
|
||||
include : Translate finalPlacement 0
|
||||
aliasableInnerSubGlyphMap.set gniPartNoPlc : object
|
||||
gn gniPart
|
||||
placement finalPlacement
|
||||
return gniPart
|
||||
|
||||
define [EnsureInnerSubGlyphSeq miniatureFont prefix job dimens] : begin
|
||||
|
@ -136,12 +157,13 @@ glyph-block AutoBuild-Enclosure : begin
|
|||
define { gn unicode parts w bal baly } job
|
||||
define [object width mockInnerWidth dscale] dimens
|
||||
local finalParts : EnsureInnerSubGlyphSeq miniatureFont prefix job dimens
|
||||
local inner : new-glyph : glyph-proc
|
||||
if gnEnclosure : return : glyph-proc
|
||||
foreach [gidPart : items-of finalParts] : include [refer-glyph gidPart]
|
||||
include : Translate width 0
|
||||
CvDecompose.set currentGlyph [{gnEnclosure}.concat finalParts]
|
||||
: else : return : new-glyph : glyph-proc
|
||||
foreach [gidPart : items-of finalParts] : include [refer-glyph gidPart]
|
||||
include : Translate width 0
|
||||
return : glyph-proc
|
||||
include inner
|
||||
if (gnEnclosure) : CvDecompose.set currentGlyph [{gnEnclosure}.concat finalParts]
|
||||
|
||||
define [CircCrowd digits width] : 2 + 2 * [Math.pow [AdjustDigitCount digits width] 0.5] * [Math.max 1 (HalfUPM / Width)]
|
||||
define [CircScale digits width] : 0.65 / [Math.pow [AdjustDigitCount digits width] 0.5]
|
||||
|
@ -350,8 +372,8 @@ glyph-block AutoBuild-Enclosure : begin
|
|||
define dimens : bracedDottdeDimens digits ww
|
||||
define [object width mockInnerWidth dscale] dimens
|
||||
set-width width
|
||||
include : refer-glyph gnb
|
||||
include : EnclosureInner [if jobDecomposable gnb null] miniatureFont prefix job dimens
|
||||
include : refer-glyph gnb
|
||||
|
||||
foreach job [items-of jobs.decomposableJobs] : CreateGlyphImpl true job
|
||||
foreach job [items-of jobs.nonDecomposable] : CreateGlyphImpl false job
|
||||
|
|
|
@ -3,6 +3,7 @@ $$include '../../meta/macros.ptl'
|
|||
|
||||
import [linreg clamp mix fallback] from '../../support/utils'
|
||||
import [AnyDerivingCv DotlessOrNot getGrTree getGrMesh CvDecompose] from "../../support/gr"
|
||||
extern Set
|
||||
|
||||
glyph-module
|
||||
|
||||
|
@ -50,9 +51,13 @@ glyph-block Autobuild-Transformed : begin
|
|||
|
||||
return { [records.concat relatedRecords] relSets targetNameMap }
|
||||
|
||||
define [link-relations relSets] : foreach [{gr origDstName relDstName} : items-of relSets]
|
||||
if [query-glyph origDstName] : gr.set [query-glyph origDstName] relDstName
|
||||
|
||||
define [link-relations relSets] : begin
|
||||
local rs : new Set
|
||||
foreach [{gr origDstName relDstName} : items-of relSets]
|
||||
if [query-glyph origDstName] : begin
|
||||
gr.set [query-glyph origDstName] relDstName
|
||||
if [query-glyph relDstName] : begin
|
||||
[query-glyph origDstName].tryBecomeMirrorOf [query-glyph relDstName] rs
|
||||
|
||||
define [createSuperscripts _records] : begin
|
||||
local {records relSets targetNameMap} : extendRelatedGlyphs _records 'sup'
|
||||
|
@ -254,7 +259,7 @@ glyph-block Autobuild-Transformed : begin
|
|||
list 0x1DAC 'mltail'
|
||||
list 0x1DAD 'turnmleg'
|
||||
list 0x1DAE 'nltail'
|
||||
list 0x1DAF 'nrtailBR'
|
||||
list 0x1DAF 'nHookBottom'
|
||||
list 0x1DB0 'smcpN'
|
||||
list 0x1DB1 'obar'
|
||||
list 0x1DB2 'latinphi'
|
||||
|
|
|
@ -90,27 +90,19 @@ glyph-block Common-Derivatives : begin
|
|||
if [not newid] : throw : new Error "Target ID not specified"
|
||||
return : HCombine newid unicode id id spacing
|
||||
|
||||
define [vcombine newid unicode id1 id2 spacing] : begin
|
||||
define [VCombine newid unicode id1 id2 spacing] : begin
|
||||
if [not newid] : throw : new Error "Target ID not specified"
|
||||
create-glyph newid unicode : glyph-proc
|
||||
set-width [query-glyph id1].advanceWidth
|
||||
currentGlyph.dependsOn [query-glyph id1]
|
||||
currentGlyph.dependsOn [query-glyph id2]
|
||||
include : new-glyph : glyph-proc
|
||||
include [refer-glyph id2]
|
||||
include : Upright
|
||||
include : Translate 0 (-spacing)
|
||||
include : new-glyph : glyph-proc
|
||||
include [refer-glyph id2] AS_BASE ALSO_METRICS
|
||||
include : Translate (-spacing * TanSlope) (-spacing)
|
||||
include [refer-glyph id1]
|
||||
include : Upright
|
||||
include : Translate 0 (spacing / 2)
|
||||
include : Italify
|
||||
include : Translate (spacing * TanSlope / 2) (spacing / 2)
|
||||
define [VDual newid unicode id spacing] : begin
|
||||
if [not newid] : throw : new Error "Target ID not specified"
|
||||
return : vcombine newid unicode id id spacing
|
||||
return : VCombine newid unicode id id spacing
|
||||
|
||||
glyph-block-export select-variant italic-variant refer-glyph query-glyph with-related-glyphs
|
||||
glyph-block-export alias turned HDual HCombine VDual vcombine glyph-is-needed
|
||||
glyph-block-export alias turned HDual HCombine VDual VCombine glyph-is-needed
|
||||
|
||||
glyph-block Recursive-Build : begin
|
||||
define [Fork gs ps] : begin
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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
|
||||
import '../kits/spiro-kit' as spirokit
|
||||
import '../kits/boole-kit' as BooleKit
|
||||
import '../support/anchor' as Anchor
|
||||
import [ designParameters ] from "../meta/aesthetics"
|
||||
|
||||
|
@ -115,35 +115,35 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
|
|||
define $$Capture$$ : object [metrics : Object.create metrics] $NamedParameterPair$ $donothing$ para recursive recursiveCodes variantSelector glyphStore $createAndSaveGlyphImpl$ spirofns booleFns MarkSet AS_BASE ALSO_METRICS pickHash buildGlyphs tagged DivFrame fontMetrics $assignUnicodeImpl$
|
||||
|
||||
### HERE WE GO
|
||||
run-glyph-module '../glyphs/common/shapes.js'
|
||||
run-glyph-module '../glyphs/common/derivatives.js'
|
||||
run-glyph-module '../glyphs/space/index.js'
|
||||
run-glyph-module '../glyphs/marks/index.js'
|
||||
run-glyph-module './common/shapes.js'
|
||||
run-glyph-module './common/derivatives.js'
|
||||
run-glyph-module './space/index.js'
|
||||
run-glyph-module './marks/index.js'
|
||||
|
||||
# Unified letters
|
||||
run-glyph-module '../glyphs/letter/latin.js'
|
||||
run-glyph-module '../glyphs/letter/greek.js'
|
||||
run-glyph-module '../glyphs/letter/cyrillic.js'
|
||||
run-glyph-module './letter/latin.js'
|
||||
run-glyph-module './letter/greek.js'
|
||||
run-glyph-module './letter/cyrillic.js'
|
||||
|
||||
# Numbers
|
||||
run-glyph-module '../glyphs/number/index.js'
|
||||
run-glyph-module './number/index.js'
|
||||
|
||||
# Symbols
|
||||
run-glyph-module '../glyphs/symbol/punctuation.js'
|
||||
run-glyph-module '../glyphs/symbol/arrow.js'
|
||||
run-glyph-module '../glyphs/symbol/geometric.js'
|
||||
run-glyph-module '../glyphs/symbol/enclosure.js'
|
||||
run-glyph-module '../glyphs/symbol/math.js'
|
||||
run-glyph-module '../glyphs/symbol/letter.js'
|
||||
run-glyph-module '../glyphs/symbol/braille.js'
|
||||
run-glyph-module '../glyphs/symbol/mosaic.js'
|
||||
run-glyph-module '../glyphs/symbol/pictograph.js'
|
||||
run-glyph-module '../glyphs/symbol/ligation.js'
|
||||
run-glyph-module './symbol/punctuation.js'
|
||||
run-glyph-module './symbol/arrow.js'
|
||||
run-glyph-module './symbol/geometric.js'
|
||||
run-glyph-module './symbol/enclosure.js'
|
||||
run-glyph-module './symbol/math.js'
|
||||
run-glyph-module './symbol/letter.js'
|
||||
run-glyph-module './symbol/braille.js'
|
||||
run-glyph-module './symbol/mosaic.js'
|
||||
run-glyph-module './symbol/pictograph.js'
|
||||
run-glyph-module './symbol/ligation.js'
|
||||
|
||||
# Autobuilds
|
||||
run-glyph-module '../glyphs/auto-build/accents.js'
|
||||
run-glyph-module '../glyphs/auto-build/composite.js'
|
||||
run-glyph-module '../glyphs/auto-build/transformed.js'
|
||||
run-glyph-module './auto-build/accents.js'
|
||||
run-glyph-module './auto-build/composite.js'
|
||||
run-glyph-module './auto-build/transformed.js'
|
||||
|
||||
return : object metrics glyphStore
|
||||
|
|
@ -1160,7 +1160,6 @@ glyph-block Marks : begin
|
|||
curl (Middle + dx - Width) (XH / 2 + dy)
|
||||
|
||||
create-glyph 'arrVStrokeOver' : glyph-proc
|
||||
include : ForceUpright
|
||||
set-width 0
|
||||
local fine : 0.375 * OverlayStroke
|
||||
local dx : Width * 0
|
||||
|
@ -1171,7 +1170,6 @@ glyph-block Marks : begin
|
|||
curl (Middle + dx - Width) (XH / 2 + dy)
|
||||
|
||||
create-glyph 'arrDblVStrokeOver' : glyph-proc
|
||||
include : ForceUpright
|
||||
set-width 0
|
||||
local fine : 0.3 * OverlayStroke
|
||||
local dx : Width * 0
|
||||
|
@ -1186,7 +1184,6 @@ glyph-block Marks : begin
|
|||
curl (Middle + dx - Width + gap) (XH / 2 + dy)
|
||||
|
||||
create-glyph 'arrHStrokeOver' : glyph-proc
|
||||
include : ForceUpright
|
||||
set-width 0
|
||||
local fine : 0.375 * OverlayStroke
|
||||
local dx : Width * 0.3
|
||||
|
@ -1196,7 +1193,6 @@ glyph-block Marks : begin
|
|||
curl (Middle - Width + dx) (XH / 2)
|
||||
|
||||
create-glyph 'arrDblHStrokeOver' : glyph-proc
|
||||
include : ForceUpright
|
||||
set-width 0
|
||||
local fine : 0.3 * OverlayStroke
|
||||
local dx : Width * 0.3
|
||||
|
|
|
@ -13,16 +13,11 @@ glyph-block Symbol-Mosaic-NotDef : begin
|
|||
set currentGlyph.autoRefPriority (-9998)
|
||||
set currentGlyph.glyphRank (9998)
|
||||
|
||||
create-glyph 'nonmarkingreturn' 0x000D : glyph-proc
|
||||
set-width 0
|
||||
set currentGlyph.autoRefPriority (-1)
|
||||
|
||||
create-glyph 'space' ' ' : glyph-proc
|
||||
set currentGlyph.autoRefPriority (-100)
|
||||
local df : DivFrame para.diversityI
|
||||
set-width df.width
|
||||
|
||||
|
||||
create-glyph 'markDemoBaseSpace' 0xE00E : glyph-proc
|
||||
local df : DivFrame 1
|
||||
set-width df.width
|
||||
|
@ -41,7 +36,8 @@ glyph-block Symbol-Mosaic-NotDef : begin
|
|||
set-width 0
|
||||
set currentGlyph.autoRefPriority (-9999)
|
||||
|
||||
alias 'cgj' 0x034F '.null'
|
||||
alias 'nonmarkingreturn' 0x000D 'zwsp'
|
||||
alias 'cgj' 0x034F 'zwsp'
|
||||
alias 'nbsp' 0xA0 'space'
|
||||
alias 'threePerEmsp' 0x2004 'space'
|
||||
alias 'fourPerEmsp' 0x2005 'space'
|
||||
|
|
|
@ -443,17 +443,17 @@ glyph-block Symbol-Arrow : begin
|
|||
|
||||
do
|
||||
local hcDist : Math.max (halfstroke * 5) (arrowWidth / 2)
|
||||
vcombine [MangleName 'uni21C4'] [MangleUnicode 0x21C4] [MangleName 'arrowright'] [MangleName 'arrowleft'] (arrowSize * 1.75)
|
||||
VCombine [MangleName 'uni21C4'] [MangleUnicode 0x21C4] [MangleName 'arrowright'] [MangleName 'arrowleft'] (arrowSize * 1.75)
|
||||
HCombine [MangleName 'uni21C5'] [MangleUnicode 0x21C5] [MangleName 'arrowup'] [MangleName 'arrowdown'] hcDist
|
||||
vcombine [MangleName 'uni21C6'] [MangleUnicode 0x21C6] [MangleName 'arrowleft'] [MangleName 'arrowright'] (arrowSize * 1.75)
|
||||
vcombine [MangleName 'uni21C7'] [MangleUnicode 0x21C7] [MangleName 'arrowleft'] [MangleName 'arrowleft'] (arrowSize * 1.75)
|
||||
VCombine [MangleName 'uni21C6'] [MangleUnicode 0x21C6] [MangleName 'arrowleft'] [MangleName 'arrowright'] (arrowSize * 1.75)
|
||||
VCombine [MangleName 'uni21C7'] [MangleUnicode 0x21C7] [MangleName 'arrowleft'] [MangleName 'arrowleft'] (arrowSize * 1.75)
|
||||
HCombine [MangleName 'uni21C8'] [MangleUnicode 0x21C8] [MangleName 'arrowup'] [MangleName 'arrowup'] hcDist
|
||||
vcombine [MangleName 'uni21C9'] [MangleUnicode 0x21C9] [MangleName 'arrowright'] [MangleName 'arrowright'] (arrowSize * 1.75)
|
||||
VCombine [MangleName 'uni21C9'] [MangleUnicode 0x21C9] [MangleName 'arrowright'] [MangleName 'arrowright'] (arrowSize * 1.75)
|
||||
HCombine [MangleName 'uni21CA'] [MangleUnicode 0x21CA] [MangleName 'arrowdown'] [MangleName 'arrowdown'] hcDist
|
||||
HCombine [MangleName 'uni21F5'] [MangleUnicode 0x21F5] [MangleName 'arrowdown'] [MangleName 'arrowup'] hcDist
|
||||
vcombine [MangleName 'uni21CB'] [MangleUnicode 0x21CB] [MangleName 'arrowleftHR'] [MangleName 'arrowrightHR'] (arrowSize)
|
||||
vcombine [MangleName 'uni21CC'] [MangleUnicode 0x21CC] [MangleName 'arrowrightHL'] [MangleName 'arrowleftHL'] (arrowSize)
|
||||
vcombine [MangleName 'uni21B9'] [MangleUnicode 0x21B9] [MangleName 'arrowbarleft'] [MangleName 'arrowbarright'] (arrowSize * 1.75)
|
||||
VCombine [MangleName 'uni21CB'] [MangleUnicode 0x21CB] [MangleName 'arrowleftHR'] [MangleName 'arrowrightHR'] (arrowSize)
|
||||
VCombine [MangleName 'uni21CC'] [MangleUnicode 0x21CC] [MangleName 'arrowrightHL'] [MangleName 'arrowleftHL'] (arrowSize)
|
||||
VCombine [MangleName 'uni21B9'] [MangleUnicode 0x21B9] [MangleName 'arrowbarleft'] [MangleName 'arrowbarright'] (arrowSize * 1.75)
|
||||
|
||||
do "Legacy computing arrows"
|
||||
define bodyWidth : Math.min arrowWidth (arrowHeight * 0.6)
|
||||
|
|
|
@ -3,9 +3,7 @@ $$include '../../meta/macros.ptl'
|
|||
|
||||
import [mix linreg clamp fallback] from '../../support/utils'
|
||||
import [designParameters] from '../../meta/aesthetics'
|
||||
|
||||
# Needed for bitwise operators
|
||||
import '../../support/mask-bit' as maskBit
|
||||
import [maskBit maskBits popCountByte] from '../../support/mask-bit'
|
||||
|
||||
glyph-module
|
||||
|
||||
|
@ -21,6 +19,39 @@ glyph-block Symbol-Braille : begin
|
|||
local stress : 0.5 * [adviceBlackness 3.375]
|
||||
local dotRadius : PeriodRadius * [Math.min 1 (stress / HalfStroke)]
|
||||
|
||||
define [BrailleYCoord y] (SymbolMid + y * offsetY)
|
||||
define [BrailleOutlineShape byte] : glyph-proc
|
||||
foreach [bit : range 0 8] : if [maskBit byte bit] : begin
|
||||
include : match bit
|
||||
0 : DotAt left [BrailleYCoord (-3)] dotRadius
|
||||
1 : DotAt left [BrailleYCoord (-1)] dotRadius
|
||||
2 : DotAt left [BrailleYCoord 1 ] dotRadius
|
||||
3 : DotAt right [BrailleYCoord (-3)] dotRadius
|
||||
4 : DotAt right [BrailleYCoord (-1)] dotRadius
|
||||
5 : DotAt right [BrailleYCoord 1 ] dotRadius
|
||||
6 : DotAt left [BrailleYCoord 3 ] dotRadius
|
||||
7 : DotAt right [BrailleYCoord 3 ] dotRadius
|
||||
|
||||
define [BrailleReferenceShape byte] : glyph-proc
|
||||
foreach [bit : range 0 8] : if [maskBit byte bit]
|
||||
include : refer-glyph ['braille' + (bit + 1)]
|
||||
|
||||
define [BrailleHalvesShape byte] : glyph-proc
|
||||
local leftHalf : maskBits byte 0x47
|
||||
local rightHalf : maskBits byte 0xB8
|
||||
include : refer-glyph : brailleGlyphName leftHalf
|
||||
include : refer-glyph : brailleGlyphName rightHalf
|
||||
|
||||
define [brailleBuildStage byte] : begin
|
||||
if (1 == [popCountByte byte]) : return 1
|
||||
if (![maskBits byte 0x47] || ![maskBits byte 0xB8]) : return 2
|
||||
return 3
|
||||
|
||||
define [brailleGlyphName byte] : begin
|
||||
local dots {}
|
||||
foreach [bit : range 0 8] : if [maskBit byte bit] : dots.push (bit + 1)
|
||||
return ("braille" + [dots.join ''])
|
||||
|
||||
### Computed Braille Patterns
|
||||
###
|
||||
### Bit to Dot mapping:
|
||||
|
@ -28,52 +59,12 @@ glyph-block Symbol-Braille : begin
|
|||
### 1 4
|
||||
### 2 5
|
||||
### 6 7
|
||||
###
|
||||
foreach [byte : range 1 256] : begin
|
||||
local dots {}
|
||||
foreach [bit : range 0 8] : if [maskBit byte bit] : dots.push (bit + 1)
|
||||
create-glyph ("braille" + [dots.join '']) (0x2800 + byte) : glyph-proc
|
||||
set currentGlyph.autoRefPriority (-101)
|
||||
local [y-coord y] (SymbolMid + y * offsetY)
|
||||
# Is there a way to directly use (byte & (1 << bit)) here?
|
||||
foreach [bit : range 0 8] : if [maskBit byte bit] : begin
|
||||
include : match bit
|
||||
0 : DotAt left [y-coord (-3)] dotRadius
|
||||
1 : DotAt left [y-coord (-1)] dotRadius
|
||||
2 : DotAt left [y-coord 1 ] dotRadius
|
||||
3 : DotAt right [y-coord (-3)] dotRadius
|
||||
4 : DotAt right [y-coord (-1)] dotRadius
|
||||
5 : DotAt right [y-coord 1 ] dotRadius
|
||||
6 : DotAt left [y-coord 3 ] dotRadius
|
||||
7 : DotAt right [y-coord 3 ] dotRadius
|
||||
|
||||
if [query-glyph 'braille7'] : set [query-glyph 'braille7'].autoRefPriority 101
|
||||
if [query-glyph 'braille37'] : set [query-glyph 'braille37'].autoRefPriority 102
|
||||
if [query-glyph 'braille3'] : set [query-glyph 'braille3'].autoRefPriority 101
|
||||
if [query-glyph 'braille237'] : set [query-glyph 'braille237'].autoRefPriority 103
|
||||
if [query-glyph 'braille27'] : set [query-glyph 'braille27'].autoRefPriority 102
|
||||
if [query-glyph 'braille23'] : set [query-glyph 'braille23'].autoRefPriority 102
|
||||
if [query-glyph 'braille2'] : set [query-glyph 'braille2'].autoRefPriority 101
|
||||
if [query-glyph 'braille1237'] : set [query-glyph 'braille1237'].autoRefPriority 104
|
||||
if [query-glyph 'braille123'] : set [query-glyph 'braille123'].autoRefPriority 103
|
||||
if [query-glyph 'braille127'] : set [query-glyph 'braille127'].autoRefPriority 103
|
||||
if [query-glyph 'braille137'] : set [query-glyph 'braille137'].autoRefPriority 103
|
||||
if [query-glyph 'braille13'] : set [query-glyph 'braille13'].autoRefPriority 102
|
||||
if [query-glyph 'braille12'] : set [query-glyph 'braille12'].autoRefPriority 102
|
||||
if [query-glyph 'braille17'] : set [query-glyph 'braille17'].autoRefPriority 102
|
||||
if [query-glyph 'braille1'] : set [query-glyph 'braille1'].autoRefPriority 101
|
||||
if [query-glyph 'braille7'] : set [query-glyph 'braille7'].avoidBeingComposite true
|
||||
if [query-glyph 'braille37'] : set [query-glyph 'braille37'].avoidBeingComposite true
|
||||
if [query-glyph 'braille3'] : set [query-glyph 'braille3'].avoidBeingComposite true
|
||||
if [query-glyph 'braille237'] : set [query-glyph 'braille237'].avoidBeingComposite true
|
||||
if [query-glyph 'braille27'] : set [query-glyph 'braille27'].avoidBeingComposite true
|
||||
if [query-glyph 'braille23'] : set [query-glyph 'braille23'].avoidBeingComposite true
|
||||
if [query-glyph 'braille2'] : set [query-glyph 'braille2'].avoidBeingComposite true
|
||||
if [query-glyph 'braille1237'] : set [query-glyph 'braille1237'].avoidBeingComposite true
|
||||
if [query-glyph 'braille123'] : set [query-glyph 'braille123'].avoidBeingComposite true
|
||||
if [query-glyph 'braille127'] : set [query-glyph 'braille127'].avoidBeingComposite true
|
||||
if [query-glyph 'braille137'] : set [query-glyph 'braille137'].avoidBeingComposite true
|
||||
if [query-glyph 'braille13'] : set [query-glyph 'braille13'].avoidBeingComposite true
|
||||
if [query-glyph 'braille12'] : set [query-glyph 'braille12'].avoidBeingComposite true
|
||||
if [query-glyph 'braille17'] : set [query-glyph 'braille17'].avoidBeingComposite true
|
||||
if [query-glyph 'braille1'] : set [query-glyph 'braille1'].avoidBeingComposite true
|
||||
define stageConfig : list
|
||||
list 1 BrailleOutlineShape
|
||||
list 2 BrailleReferenceShape
|
||||
list 3 BrailleHalvesShape
|
||||
|
||||
foreach { stage F } [items-of stageConfig] : begin
|
||||
foreach [byte : range 1 256] : if (stage == [brailleBuildStage byte])
|
||||
create-glyph [brailleGlyphName byte] (0x2800 + byte) : F byte
|
||||
|
|
|
@ -305,7 +305,7 @@ glyph-block Symbol-Geometric : for-width-kinds WideWidth1
|
|||
corner (cx - size * triangleOvershoot) (cy - size)
|
||||
|
||||
define [TriangularVeeFill cx cy size] : intersection
|
||||
TriangleUpFill cx cy size
|
||||
TriangleDownFill cx cy size
|
||||
union
|
||||
dispiro
|
||||
widths.lhs GeometryStroke
|
||||
|
|
|
@ -4,7 +4,7 @@ $$include '../../meta/macros.ptl'
|
|||
import [mix linreg clamp fallback] from '../../support/utils'
|
||||
import [designParameters] from '../../meta/aesthetics'
|
||||
import '../../support/transform' as Transform
|
||||
import '../../support/mask-bit' as maskBit
|
||||
import [maskBit] from '../../support/mask-bit'
|
||||
|
||||
glyph-module
|
||||
|
||||
|
|
|
@ -1135,27 +1135,17 @@ glyph-block Symbol-Punctuation-Quotes-And-Primes : begin
|
|||
include : FlipAround Middle [mix [mix PeriodSize commaLow 0.5] yCurlyQuotes 0.5]
|
||||
include : FlipAround Middle yCurlyQuotes
|
||||
|
||||
create-glyph 'lowDoubleQuote' 0x201E : glyph-proc
|
||||
define [DoubleQuoteProc gn] : glyph-proc
|
||||
local dist : Width * 0.225
|
||||
include : refer-glyph "lowSingleQuote"
|
||||
include : refer-glyph gn
|
||||
include : Translate (-dist * 2) 0
|
||||
include : refer-glyph "lowSingleQuote"
|
||||
include : refer-glyph gn
|
||||
include : Translate dist 0
|
||||
|
||||
create-glyph 'openDoubleQuote' 0x201C : glyph-proc
|
||||
include : refer-glyph "lowDoubleQuote"
|
||||
include : FlipAround Middle [mix [mix PeriodSize commaLow 0.5] yCurlyQuotes 0.5]
|
||||
|
||||
create-glyph 'closeDoubleQuote' 0x201D : glyph-proc
|
||||
include : refer-glyph "openDoubleQuote"
|
||||
include : FlipAround Middle yCurlyQuotes
|
||||
|
||||
create-glyph 'revertDoubleQuote' 0x201F : glyph-proc
|
||||
local dist : Width * 0.225
|
||||
include : refer-glyph "revertSingleQuote"
|
||||
include : Translate (-dist * 2) 0
|
||||
include : refer-glyph "revertSingleQuote"
|
||||
include : Translate dist 0
|
||||
create-glyph 'lowDoubleQuote' 0x201E : DoubleQuoteProc 'lowSingleQuote'
|
||||
create-glyph 'openDoubleQuote' 0x201C : DoubleQuoteProc 'openSingleQuote'
|
||||
create-glyph 'closeDoubleQuote' 0x201D : DoubleQuoteProc 'closeSingleQuote'
|
||||
create-glyph 'revertDoubleQuote' 0x201F : DoubleQuoteProc 'revertSingleQuote'
|
||||
|
||||
create-glyph 'prime' 0x2032 : glyph-proc
|
||||
local wide : Width * 0.1
|
||||
|
@ -1592,15 +1582,18 @@ glyph-block Symbol-Punctuation-Ligation-Variants : begin
|
|||
* "question"
|
||||
* "exclam"
|
||||
|
||||
foreach [baseName : items-of dotLikePunctuations] : if [query-glyph baseName]
|
||||
create-glyph : glyph-proc
|
||||
include [refer-glyph baseName] AS_BASE ALSO_METRICS
|
||||
define [LigD baseName suffix pDelta] : if [query-glyph baseName] : begin
|
||||
local delta : Math.max 0 : Width / 2 - (XH - PeriodSize) / 2
|
||||
create-derived (baseName + '.lig.dRight') : Translate (delta * 1.0 ) 0
|
||||
create-derived (baseName + '.lig.dRightHalf') : Translate (delta * 0.5 ) 0
|
||||
create-derived (baseName + ".lig.dMid") : Translate 0 0
|
||||
create-derived (baseName + '.lig.dLeftHalf') : Translate (delta * (-0.5)) 0
|
||||
create-derived (baseName + '.lig.dLeft') : Translate (delta * (-1.0)) 0
|
||||
create-glyph (baseName + '.lig.' + suffix) : composite-proc
|
||||
refer-glyph baseName
|
||||
Translate (delta * pDelta ) 0
|
||||
|
||||
foreach [baseName : items-of dotLikePunctuations] : begin
|
||||
LigD baseName 'dRight' 1.0
|
||||
LigD baseName 'dRightHalf' 0.5
|
||||
LigD baseName "dMid" 0
|
||||
LigD baseName 'dLeftHalf' (-0.5)
|
||||
LigD baseName 'dLeft' (-1.0)
|
||||
|
||||
glyph-block Symbol-Other-Phonetic : begin
|
||||
glyph-block-import CommonShapes
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const { FontIo } = require("ot-builder");
|
||||
|
||||
const Toml = require("@iarna/toml");
|
||||
|
||||
|
@ -15,7 +16,7 @@ module.exports = async function main(argv) {
|
|||
const para = await getParameters(argv);
|
||||
const { font, glyphStore } = BuildFont(para);
|
||||
if (argv.oCharMap) await saveCharMap(argv, glyphStore);
|
||||
if (argv.o) await saveOTD(argv, font);
|
||||
if (argv.o) await saveTTF(argv, font);
|
||||
};
|
||||
|
||||
// Parameter preparation
|
||||
|
@ -81,9 +82,14 @@ async function tryParseToml(str) {
|
|||
}
|
||||
}
|
||||
|
||||
// Save OTD
|
||||
async function saveOTD(argv, font) {
|
||||
await fs.writeJSON(argv.o, font);
|
||||
// Save TTF
|
||||
async function saveTTF(argv, font) {
|
||||
const sfnt = FontIo.writeFont(font, {
|
||||
glyphStore: { statOs2XAvgCharWidth: false },
|
||||
generateDummyDigitalSignature: true
|
||||
});
|
||||
const buf = FontIo.writeSfntOtf(sfnt);
|
||||
await fs.writeFile(argv.o, buf);
|
||||
}
|
||||
|
||||
// Save character map file
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'typo-geom' as TypoGeom
|
||||
import '../../support/curve-util' as CurveUtil
|
||||
import '../support/curve-util' as CurveUtil
|
||||
|
||||
export : define [SetupBuilders args] : begin
|
||||
define [object Glyph GlobalTransform] args
|
|
@ -1,8 +1,8 @@
|
|||
import 'spiro' as SpiroJs
|
||||
import '../../support/spiro-expand' as SpiroExpansionContext
|
||||
import '../../support/curve-util' as CurveUtil
|
||||
import '../../support/transform' as Transform
|
||||
import '../../support/utils' as [object fallback mix bez2 bez3]
|
||||
import '../support/spiro-expand' as SpiroExpansionContext
|
||||
import '../support/curve-util' as CurveUtil
|
||||
import '../support/transform' as Transform
|
||||
import '../support/utils' as [object fallback mix bez2 bez3]
|
||||
|
||||
export : define [SetupBuilders args] : begin
|
||||
define [object para Glyph Contrast GlobalTransform Stroke] args
|
|
@ -107,7 +107,7 @@ define-macro glyph-block-import : syntax-rules
|
|||
|
||||
define allExports : object
|
||||
Common-Derivatives `[select-variant italic-variant refer-glyph query-glyph alias turned
|
||||
HDual HCombine VDual vcombine with-related-glyphs glyph-is-needed]
|
||||
HDual HCombine VDual VCombine with-related-glyphs glyph-is-needed]
|
||||
|
||||
CommonShapes `[Rect SquareAt Ring RingAt DotAt RingStroke RingStrokeAt DotStrokeAt
|
||||
CircleRing CircleRingAt CircleDotAt OShape OShapeOutline OBarLeftShape OBarRightShape
|
||||
|
|
|
@ -140,9 +140,6 @@ export : define [assignFontNames para metrics font] : begin
|
|||
nameFont font WWS_PREFERRED_FAMILY family # WWS Preferred Family
|
||||
nameFont font WWS_PREFERRED_STYLE style # WWS Preferred Style
|
||||
|
||||
set font.name.preferredFamily family
|
||||
set font.name.preferredSubFamily style
|
||||
|
||||
local {compatStyle compatFamilySuffix shortCompatFamilySuffix} : getStyleLinkedStyles para.naming.weight para.naming.width para.naming.slope
|
||||
local compatFamily family
|
||||
if (compatFamilySuffix != "Regular") : set compatFamily : family + ' ' + compatFamilySuffix
|
||||
|
|
|
@ -18,17 +18,17 @@ export : define [buildMarkMkmk sink glyphStore] : begin
|
|||
foreach markCls [items-of MarkClasses] : begin
|
||||
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
|
||||
markLookupNames.push markLookup
|
||||
local lidMark : add-lookup sink {.type 'gpos_mark_to_base' .subtables { markSubtable }}
|
||||
mark.lookups.push lidMark
|
||||
markLookupNames.push lidMark
|
||||
|
||||
if ([objectIsNotEmpty mkmkSubtable.marks] && [objectIsNotEmpty mkmkSubtable.bases]) : begin
|
||||
local mkmkLookup : add-lookup sink {.type 'gpos_mark_to_mark' .subtables { mkmkSubtable }}
|
||||
mkmk.lookups.push mkmkLookup
|
||||
mkmkLookupNames.push mkmkLookup
|
||||
local lidMkmk : add-lookup sink {.type 'gpos_mark_to_mark' .subtables { mkmkSubtable }}
|
||||
mkmk.lookups.push lidMkmk
|
||||
mkmkLookupNames.push lidMkmk
|
||||
|
||||
foreach markLookup [items-of markLookupNames] : foreach mkmkLookup [items-of mkmkLookupNames]
|
||||
sink.lookupDep.push { markLookup mkmkLookup }
|
||||
foreach lidMark [items-of markLookupNames] : foreach lidMkmk [items-of mkmkLookupNames]
|
||||
sink.lookupDep.push { lidMark lidMkmk }
|
||||
|
||||
define [createMTSubtables glyphStore markClasses] : begin
|
||||
local markSubtable {.marks {.} .bases {.}}
|
||||
|
|
|
@ -13,6 +13,7 @@ module.exports = class Glyph {
|
|||
this.markAnchors = {};
|
||||
this.baseAnchors = {};
|
||||
this.gizmo = Transform.Id();
|
||||
this.semanticInclusions = [];
|
||||
this.dependencies = [];
|
||||
this.defaultTag = null;
|
||||
}
|
||||
|
@ -51,6 +52,7 @@ module.exports = class Glyph {
|
|||
this.contours[j++] = this.contours[i];
|
||||
}
|
||||
this.contours.length = j;
|
||||
this.semanticInclusions = [];
|
||||
}
|
||||
// Inclusion
|
||||
include(component, copyAnchors, copyWidth) {
|
||||
|
@ -83,10 +85,18 @@ module.exports = class Glyph {
|
|||
}
|
||||
}
|
||||
|
||||
this.includeGeometry(g, shift.x, shift.y);
|
||||
const newContours = this.includeGeometry(g, shift.x, shift.y);
|
||||
if (copyAnchors || g.isMarkSet) this.copyAnchors(g);
|
||||
if (copyWidth && g.advanceWidth >= 0) this.advanceWidth = g.advanceWidth;
|
||||
this.dependsOn(g);
|
||||
if (g._m_identifier && newContours && newContours.length) {
|
||||
this.semanticInclusions.push({
|
||||
glyph: g,
|
||||
x: shift.x,
|
||||
y: shift.y,
|
||||
contours: newContours
|
||||
});
|
||||
}
|
||||
}
|
||||
cloneFromGlyph(g) {
|
||||
this.includeGlyph(g, true, true);
|
||||
|
@ -102,15 +112,56 @@ module.exports = class Glyph {
|
|||
this.glyphRank = g.glyphRank;
|
||||
this.avoidBeingComposite = g.avoidBeingComposite;
|
||||
}
|
||||
|
||||
includeGeometry(geom, shiftX, shiftY) {
|
||||
if (!geom || !geom.contours) return;
|
||||
if (!geom || !geom.contours || !geom.contours.length) return null;
|
||||
if (this.includeGeometryAsTransparentReferences(geom, shiftX, shiftY)) return null;
|
||||
return this.includeGeometryImpl(geom, shiftX, shiftY);
|
||||
}
|
||||
|
||||
isPureComposite() {
|
||||
if (!this.semanticInclusions || !this.semanticInclusions.length) return false;
|
||||
const origContourSet = new Set(this.contours);
|
||||
let handledContours = new Set();
|
||||
for (const sr of this.semanticInclusions) {
|
||||
for (const c of sr.contours) {
|
||||
if (!origContourSet.has(c) || handledContours.has(c)) return false;
|
||||
handledContours.add(c);
|
||||
}
|
||||
}
|
||||
for (const c of this.contours) if (!handledContours.has(c)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
includeGeometryAsTransparentReferences(geom, shiftX, shiftY) {
|
||||
if (!(geom instanceof Glyph && !geom._m_identifier)) return false;
|
||||
if (!geom.isPureComposite()) return false;
|
||||
|
||||
for (const sr of geom.semanticInclusions) {
|
||||
const cs = this.includeGeometryImpl(sr.glyph, sr.x + shiftX, sr.y + shiftY);
|
||||
if (cs) {
|
||||
this.semanticInclusions.push({
|
||||
glyph: sr.glyph,
|
||||
x: sr.x + shiftX,
|
||||
y: sr.y + shiftY,
|
||||
contours: cs
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
includeGeometryImpl(geom, shiftX, shiftY) {
|
||||
let newContours = [];
|
||||
for (const contour of geom.contours) {
|
||||
let c = [];
|
||||
c.tag = contour.tag || geom.tag || this.defaultTag;
|
||||
for (const z of contour) c.push(Point.translated(z, shiftX, shiftY));
|
||||
this.contours.push(c);
|
||||
newContours.push(c);
|
||||
}
|
||||
return newContours;
|
||||
}
|
||||
|
||||
combineAnchor(shift, baseThis, markThat, basesThat) {
|
||||
if (!baseThis || !markThat) return;
|
||||
shift.x = baseThis.x - markThat.x;
|
||||
|
@ -129,9 +180,19 @@ module.exports = class Glyph {
|
|||
if (g.baseAnchors) for (const k in g.baseAnchors) this.baseAnchors[k] = g.baseAnchors[k];
|
||||
}
|
||||
applyTransform(tfm, alsoAnchors) {
|
||||
for (const c of this.contours)
|
||||
for (let j = 0; j < c.length; j++) {
|
||||
c[j] = Point.transformed(tfm, c[j]);
|
||||
for (const c of this.contours) {
|
||||
for (let k = 0; k < c.length; k++) {
|
||||
c[k] = Point.transformed(tfm, c[k]);
|
||||
}
|
||||
}
|
||||
if (Transform.isTranslate(tfm)) {
|
||||
for (const sr of this.semanticInclusions) {
|
||||
sr.x += tfm.x;
|
||||
sr.y += tfm.y;
|
||||
}
|
||||
} else {
|
||||
// Applying a non-trivial inclusion will unlink all the SIs
|
||||
this.semanticInclusions = [];
|
||||
}
|
||||
if (alsoAnchors) {
|
||||
for (const k in this.baseAnchors)
|
||||
|
@ -140,6 +201,35 @@ module.exports = class Glyph {
|
|||
this.markAnchors[k] = Anchor.transform(tfm, this.markAnchors[k]);
|
||||
}
|
||||
}
|
||||
|
||||
tryBecomeMirrorOf(dst, rankSet) {
|
||||
if (rankSet.has(this) || rankSet.has(dst)) return;
|
||||
if (this.contours.length !== dst.contours.length) return;
|
||||
for (let j = 0; j < this.contours.length; j++) {
|
||||
const c1 = this.contours[j],
|
||||
c2 = dst.contours[j];
|
||||
if (c1.length !== c2.length) return;
|
||||
}
|
||||
for (let j = 0; j < this.contours.length; j++) {
|
||||
const c1 = this.contours[j],
|
||||
c2 = dst.contours[j];
|
||||
for (let k = 0; k < c1.length; k++) {
|
||||
const z1 = c1[k],
|
||||
z2 = c2[k];
|
||||
if (z1.x !== z2.x || z1.y !== z2.y || z1.on !== z2.on) return;
|
||||
}
|
||||
}
|
||||
this.semanticInclusions = [
|
||||
{
|
||||
glyph: dst,
|
||||
x: 0,
|
||||
y: 0,
|
||||
contours: [...this.contours]
|
||||
}
|
||||
];
|
||||
rankSet.add(this);
|
||||
}
|
||||
|
||||
// Anchors
|
||||
setBaseAnchor(id, x, y) {
|
||||
this.baseAnchors[id] = new Anchor(x, y).transform(this.gizmo);
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = function maskBit(x, y) {
|
||||
exports.maskBit = function maskBit(x, y) {
|
||||
return x & (1 << y);
|
||||
};
|
||||
|
||||
exports.maskBits = function maskBits(x, y) {
|
||||
return x & y;
|
||||
};
|
||||
|
||||
const pcNibbleLookup = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
exports.popCountByte = function (x) {
|
||||
return pcNibbleLookup[x & 0x0f] + pcNibbleLookup[(x >>> 4) & 0x0f];
|
||||
};
|
||||
|
|
|
@ -46,4 +46,8 @@ module.exports = class Transform {
|
|||
-(-this.x * this.xy + this.y * this.xx) / denom
|
||||
);
|
||||
}
|
||||
|
||||
static isTranslate(tfm) {
|
||||
return tfm.xx === 1 && tfm.yy === 1 && tfm.xy === 0 && tfm.yx === 0;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"ejs": "^3.1.3",
|
||||
"fs-extra": "^9.0.1",
|
||||
"object-assign": "^4.1.1",
|
||||
"otfcc-ttcize": "^0.10.2",
|
||||
"ot-builder": "^0.10.28",
|
||||
"otb-ttc-bundle": "^0.10.28",
|
||||
"patel": "^0.33.1",
|
||||
"semver": "^7.3.2",
|
||||
"spiro": "^2.0.0",
|
||||
|
|
|
@ -7,11 +7,7 @@ const FgYellow = "\x1b[33m";
|
|||
const Reset = "\x1b[0m";
|
||||
|
||||
console.log("Checking External Dependencies");
|
||||
check("otfccdump");
|
||||
check("otfccbuild");
|
||||
check("ttfautohint");
|
||||
check("otf2otc");
|
||||
checkOptional("ttx");
|
||||
|
||||
function check(util) {
|
||||
try {
|
||||
|
@ -21,11 +17,3 @@ function check(util) {
|
|||
console.error(FgRed + ` * External dependency <${util}> is not found.` + Reset);
|
||||
}
|
||||
}
|
||||
function checkOptional(util) {
|
||||
try {
|
||||
which.sync(util);
|
||||
console.error(FgGreen + ` * Optional external dependency <${util}> is present.` + Reset);
|
||||
} catch (e) {
|
||||
console.error(FgYellow + ` * Optional external dependency <${util}> is not found.` + Reset);
|
||||
}
|
||||
}
|
||||
|
|
89
verdafile.js
89
verdafile.js
|
@ -12,7 +12,7 @@ module.exports = build;
|
|||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
const path = require("path");
|
||||
const Path = require("path");
|
||||
const toml = require("@iarna/toml");
|
||||
|
||||
const BUILD = ".build";
|
||||
|
@ -21,11 +21,13 @@ const SNAPSHOT_TMP = ".build/snapshot";
|
|||
const DIST_SUPER_TTC = "dist/.super-ttc";
|
||||
const ARCHIVE_DIR = "release-archives";
|
||||
|
||||
const OTF2OTC = "otf2otc";
|
||||
const OTFCC_BUILD = "otfccbuild";
|
||||
const TTX = "ttx";
|
||||
const PATEL_C = ["node", "./node_modules/patel/bin/patel-c"];
|
||||
const TTCIZE = ["node", "./node_modules/otfcc-ttcize/bin/_startup"];
|
||||
const TTCIZE = [
|
||||
"node",
|
||||
"--max-old-space-size=8192",
|
||||
"node_modules/otb-ttc-bundle/bin/otb-ttc-bundle"
|
||||
];
|
||||
const webfontFormats = [
|
||||
["woff2", "woff2"],
|
||||
["woff", "woff"],
|
||||
|
@ -41,11 +43,8 @@ const WEIGHT_NORMAL = "regular";
|
|||
const SLOPE_NORMAL = "upright";
|
||||
const DEFAULT_SUBFAMILY = "regular";
|
||||
|
||||
const BUILD_PLANS = path.relative(__dirname, path.resolve(__dirname, "./build-plans.toml"));
|
||||
const PRIVATE_BUILD_PLANS = path.relative(
|
||||
__dirname,
|
||||
path.resolve(__dirname, "./private-build-plans.toml")
|
||||
);
|
||||
const BUILD_PLANS = "build-plans.toml";
|
||||
const PRIVATE_BUILD_PLANS = "private-build-plans.toml";
|
||||
|
||||
// Save journal to build/
|
||||
build.setJournal(`${BUILD}/.verda-build-journal`);
|
||||
|
@ -56,8 +55,9 @@ build.setSelfTracking();
|
|||
////// Oracles //////
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
const Version = oracle(`oracle:version`, async () => {
|
||||
const package_json = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json")));
|
||||
const Version = oracle(`oracle:version`, async target => {
|
||||
const [pj] = await target.need(sfu`package.json`);
|
||||
const package_json = JSON.parse(await fs.promises.readFile(pj.full, "utf-8"));
|
||||
return package_json.version;
|
||||
});
|
||||
|
||||
|
@ -422,15 +422,9 @@ const BuildRawTtf = file.make(
|
|||
const [fi] = await target.need(FontInfoOf(fn), Version);
|
||||
const charmap = output.dir + "/" + fn + ".charmap";
|
||||
await target.need(Scripts, Parameters, de`${output.dir}`);
|
||||
const otdPath = `${output.dir}/${output.name}.otd`;
|
||||
await node("font-src/index", { o: otdPath, oCharMap: charmap, ...fi });
|
||||
await optimizedOtfcc(otdPath, output.full);
|
||||
await rm(otdPath);
|
||||
await node("font-src/index", { o: output.full, oCharMap: charmap, ...fi });
|
||||
}
|
||||
);
|
||||
function optimizedOtfcc(from, to) {
|
||||
return run(OTFCC_BUILD, from, ["-o", `${to}`], ["-O3", "--keep-average-char-width", "-q"]);
|
||||
}
|
||||
|
||||
const BuildTTF = file.make(
|
||||
(gr, fn) => `${BUILD}/ttf/${gr}/${fn}.ttf`,
|
||||
|
@ -557,25 +551,17 @@ const glyfTtc = file.make(
|
|||
);
|
||||
|
||||
async function buildGlyfTtc(target, parts, out) {
|
||||
const [useFilter, useTtx] = await target.need(
|
||||
OptimizeWithFilter,
|
||||
OptimizeWithTtx,
|
||||
de`${out.dir}`
|
||||
);
|
||||
await target.need(de`${out.dir}`);
|
||||
const [ttfInputs] = await target.need(parts.map(part => BuildTTF(part.dir, part.file)));
|
||||
const tmpTtc = `${out.dir}/${out.name}.unhinted.ttc`;
|
||||
const ttfInputPaths = ttfInputs.map(p => p.full);
|
||||
const optimization = useFilter ? ["--filter-loop", useFilter] : useTtx ? ["--ttx-loop"] : [];
|
||||
await run(TTCIZE, optimization, ["-o", tmpTtc], ttfInputPaths);
|
||||
await run(TTCIZE, "-u", ["-o", tmpTtc], ttfInputPaths);
|
||||
await run("ttfautohint", tmpTtc, out.full);
|
||||
await rm(tmpTtc);
|
||||
}
|
||||
async function buildCompositeTtc(out, inputs) {
|
||||
await run(
|
||||
OTF2OTC,
|
||||
["-o", out.full],
|
||||
inputs.map(f => f.full)
|
||||
);
|
||||
const inputPaths = inputs.map(f => f.full);
|
||||
await run(TTCIZE, ["-o", out.full], inputPaths);
|
||||
}
|
||||
|
||||
const ExportSuperTtc = file.make(
|
||||
|
@ -664,7 +650,7 @@ const GroupArchive = task.group(`archive`, async (target, gid) => {
|
|||
///////////////////////////////////////////////////////////
|
||||
|
||||
const PagesDir = oracle(`pages-dir-path`, async target => {
|
||||
const pagesDir = path.resolve(__dirname, "../Iosevka-Pages");
|
||||
const pagesDir = Path.resolve(__dirname, "../Iosevka-Pages");
|
||||
if (!fs.existsSync(pagesDir)) {
|
||||
return "";
|
||||
} else {
|
||||
|
@ -686,7 +672,7 @@ const PagesDataExport = task(`pages:data-export`, async target => {
|
|||
cm.full,
|
||||
cmi.full,
|
||||
cmo.full,
|
||||
path.resolve(pagesDir, "shared/data-import/iosevka.json")
|
||||
Path.resolve(pagesDir, "shared/data-import/iosevka.json")
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -701,7 +687,7 @@ const PagesFontExport = task(`pages:font-export`, async target => {
|
|||
GroupContents`iosevka-sparkle`
|
||||
);
|
||||
for (const dir of dirs) {
|
||||
await cp(`${DIST}/${dir}`, path.resolve(pagesDir, "shared/font-import", dir));
|
||||
await cp(`${DIST}/${dir}`, Path.resolve(pagesDir, "shared/font-import", dir));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -710,7 +696,7 @@ const PagesFastFontExport = task(`pages:fast-font-export`, async target => {
|
|||
if (!pagesDir) return;
|
||||
const dirs = await target.need(GroupContents`iosevka`);
|
||||
for (const dir of dirs) {
|
||||
await cp(`${DIST}/${dir}`, path.resolve(pagesDir, "shared/font-import", dir));
|
||||
await cp(`${DIST}/${dir}`, Path.resolve(pagesDir, "shared/font-import", dir));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -889,28 +875,31 @@ const ScriptFiles = computed.group("script-files", async (target, ext) => {
|
|||
});
|
||||
const JavaScriptFromPtl = computed("scripts-js-from-ptl", async target => {
|
||||
const [ptl] = await target.need(ScriptFiles("ptl"));
|
||||
return ptl.map(x => x.replace(/\.ptl$/g, ".js"));
|
||||
return ptl.map(x => replaceExt(".js", x));
|
||||
});
|
||||
function replaceExt(extNew, file) {
|
||||
return Path.join(Path.dirname(file), Path.basename(file, Path.extname(file)) + extNew);
|
||||
}
|
||||
|
||||
const ScriptJS = file.glob(`font-src/**/*.js`, async (target, path) => {
|
||||
const [jsFromPtl] = await target.need(JavaScriptFromPtl);
|
||||
if (jsFromPtl.indexOf(path.full) >= 0) {
|
||||
const ptl = path.full.replace(/\.js$/g, ".ptl");
|
||||
if (/\/glyphs\//.test(path.full)) {
|
||||
await target.need(MARCOS);
|
||||
const CompiledJs = file.make(
|
||||
p => p,
|
||||
async (target, out) => {
|
||||
const ptl = replaceExt(".ptl", out.full);
|
||||
if (/\/glyphs\//.test(out.full)) await target.need(MARCOS);
|
||||
await target.need(sfu(ptl));
|
||||
await run(PATEL_C, "--strict", ptl, "-o", out.full);
|
||||
}
|
||||
await target.need(fu`${ptl}`);
|
||||
await run(PATEL_C, "--strict", ptl, "-o", path.full);
|
||||
} else {
|
||||
await target.need(fu`${path.full}`);
|
||||
}
|
||||
});
|
||||
);
|
||||
const Scripts = task("scripts", async target => {
|
||||
await target.need(Parameters);
|
||||
const [jsFromPtl] = await target.need(JavaScriptFromPtl);
|
||||
await target.need(jsFromPtl);
|
||||
const [_jsFromPtl] = await target.need(JavaScriptFromPtl);
|
||||
const [js] = await target.need(ScriptFiles("js"));
|
||||
await target.need(js.map(ScriptJS));
|
||||
const jsFromPtl = new Set(_jsFromPtl);
|
||||
|
||||
let subGoals = [];
|
||||
for (const item of jsFromPtl) subGoals.push(CompiledJs(item));
|
||||
for (const item of js) if (!jsFromPtl.has(js)) subGoals.push(sfu(item));
|
||||
await target.need(subGoals);
|
||||
});
|
||||
const UtilScripts = task("util-scripts", async target => {
|
||||
const [files] = await target.need(UtilScriptFiles);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue