* Replace autoRef with semantic inclusion for more stable results.

* Move files around to make repository organized better.
This commit is contained in:
be5invis 2020-10-17 15:45:00 -07:00
parent 400d8f3f38
commit 7c78329244
37 changed files with 1098 additions and 761 deletions

3
.gitignore vendored
View file

@ -43,8 +43,7 @@ release-archives/
testdrive/iosevka* testdrive/iosevka*
# Generated scripts # Generated scripts
font-src/gen/build-glyphs.js font-src/kits/**/*.js
font-src/gen/kits/**/*.js
font-src/meta/**/*.js font-src/meta/**/*.js
font-src/otl/**/*.js font-src/otl/**/*.js
font-src/glyphs/**/*.js font-src/glyphs/**/*.js

View file

@ -57,7 +57,7 @@ Iosevka supports Language-Specific Ligations, which is the ligation set enabled
To build Iosevka you should: 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 youve installed them, upgrade to the latest. 2. Install necessary libs by `npm install`. If youve installed them, upgrade to the latest.
3. `npm run build -- contents::iosevka`. 3. `npm run build -- contents::iosevka`.

View file

@ -1,24 +1,25 @@
"use strict"; "use strict";
const EmptyFont = require("./empty-font.js"); const EmptyFont = require("./empty-font");
const buildGlyphs = require("./build-glyphs.js"); const buildGlyphs = require("../glyphs/index");
const finalizeFont = require("./finalize/index"); const finalizeFont = require("./finalize/index");
const convertOtd = require("./otd-conv/index");
const { buildOtl } = require("../otl/index"); const { buildOtl } = require("../otl/index");
const { assignFontNames } = require("../meta/naming"); const { assignFontNames } = require("../meta/naming");
const { setFontMetrics } = require("../meta/aesthetics"); const { setFontMetrics } = require("../meta/aesthetics");
module.exports = function (para) { module.exports = function (para) {
const font = EmptyFont(); const otd = EmptyFont();
const gs = buildGlyphs(para); const gs = buildGlyphs(para);
assignFontNames(para, gs.metrics, font); assignFontNames(para, gs.metrics, otd);
setFontMetrics(para, gs.metrics, font); setFontMetrics(para, gs.metrics, otd);
const otl = buildOtl(para, gs.glyphStore); const otl = buildOtl(para, gs.glyphStore);
font.GSUB = otl.GSUB; otd.GSUB = otl.GSUB;
font.GPOS = otl.GPOS; otd.GPOS = otl.GPOS;
font.GDEF = otl.GDEF; otd.GDEF = otl.GDEF;
// Regulate // Regulate
const excludeChars = new Set(); 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 }; return { font, glyphStore: finalGs };
}; };

View file

@ -76,20 +76,8 @@ module.exports = function () {
ySuperscriptXSize: 665, ySuperscriptXSize: 665,
ySuperscriptYOffset: 491, ySuperscriptYOffset: 491,
ySuperscriptYSize: 716, ySuperscriptYSize: 716,
ulCodePageRange1: { ulCodePageRange1: 0x2000011f,
latin1: true, ulCodePageRange2: 0xc4000000
latin2: true,
greek: true,
cyrillic: true,
turkish: true,
vietnamese: true,
macRoman: true
},
ulCodePageRange2: {
cp852: true,
cp850: true,
ascii: true
}
}, },
post: { post: {
version: 2, version: 2,

View file

@ -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;

View file

@ -6,7 +6,7 @@ module.exports = function gcFont(glyphStore, excludedChars, restFont, cfg) {
markSweepOtl(restFont.GSUB); markSweepOtl(restFont.GSUB);
markSweepOtl(restFont.GPOS); markSweepOtl(restFont.GPOS);
const sink = mark(glyphStore, excludedChars, restFont, cfg); const sink = mark(glyphStore, excludedChars, restFont, cfg);
return sweep(glyphStore, restFont, sink); return sweep(glyphStore, sink);
}; };
function markSweepOtl(table) { function markSweepOtl(table) {
@ -30,6 +30,44 @@ function markSweepOtl(table) {
} }
table.features = features1; 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) { function markLookups(table, sink) {
if (!table || !table.features) return; if (!table || !table.features) return;
for (let f in table.features) { for (let f in table.features) {
@ -57,43 +95,6 @@ function markLookups(table, sink) {
} while (loop < 0xff && lookupSetChanged); } 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) { function markSubtable(sink, type, st, cfg) {
switch (type) { switch (type) {
case "gsub_single": case "gsub_single":
@ -127,156 +128,6 @@ function markSubtable(sink, type, st, cfg) {
} }
} }
function sweep(glyphStore, restFont, gnSet) { function sweep(glyphStore, gnSet) {
sweepOtl(restFont.GSUB, gnSet);
sweepOtl(restFont.GPOS, gnSet);
return glyphStore.filterByName(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;
}

View 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);
}

View file

@ -1,17 +1,12 @@
"use strict"; "use strict";
const autoRef = require("./autoref"); const finalizeGlyphs = require("./glyphs");
const TypoGeom = require("typo-geom");
const Point = require("../../support/point");
const CurveUtil = require("../../support/curve-util");
const gcFont = require("./gc"); 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 = forceMonospaceIfNeeded(para, glyphStore);
glyphStore = gcFont(glyphStore, excludedCodePoints, font, {}); glyphStore = gcFont(glyphStore, excludedCodePoints, restFont, {});
glyphStore = regulateGlyphStore(para, glyphStore); glyphStore = finalizeGlyphs(para, glyphStore);
extractGlyfCmap(glyphStore, font);
return 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);
}

View 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)
);
}

View 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 };
};

View 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;
}

View 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)
);
}

View 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;
}

View file

@ -114,14 +114,15 @@ glyph-block AutoBuild-Accents : begin
define construction : glyph-proc define construction : glyph-proc
include s_parts.0 AS_BASE ALSO_METRICS include s_parts.0 AS_BASE ALSO_METRICS
local nonTrivial false
foreach part [items-of : s_parts.slice 1] : if part : begin foreach part [items-of : s_parts.slice 1] : if part : begin
include part include part
if (part.markAnchors && part.markAnchors.bottomright) : begin if (part.markAnchors && part.markAnchors.bottomright) : begin
eject-contour 'serifRB' eject-contour 'serifRB'
if [AnyDerivingCv.query s_parts.0] : begin set nonTrivial true
if ([not nonTrivial] && [AnyDerivingCv.query s_parts.0]) : begin
local decomposeParts { } local decomposeParts { }
foreach part [items-of s_parts] : begin foreach part [items-of s_parts] : recursivelyDecompose part decomposeParts
recursivelyDecompose part decomposeParts
CvDecompose.set currentGlyph decomposeParts CvDecompose.set currentGlyph decomposeParts
define [RootGlyphProc goalName code parts] : begin define [RootGlyphProc goalName code parts] : begin
@ -195,12 +196,8 @@ define customDecompositions : object
."\u0247" "e\u0337" ."\u0247" "e\u0337"
."\u0290" "z\u0322" ."\u0290" "z\u0322"
."\u0256" "d\u0322"
."\u0273" "n\u0322"
."\u01AE" "T\u0322" ."\u01AE" "T\u0322"
."\u1D8F" "a\u0322"
."\u1D90" "\u0251\u0322" ."\u1D90" "\u0251\u0322"
."\u1D91" "\u0257\u0322"
."\u1E10" "D\u0326" ."\u1E10" "D\u0326"
."\u1E11" "d\u0326" ."\u1E11" "d\u0326"

View file

@ -3,6 +3,8 @@ $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from '../../support/utils' import [mix linreg clamp fallback] from '../../support/utils'
import [getGrMesh AnyDerivingCv CvDecompose] from "../../support/gr" import [getGrMesh AnyDerivingCv CvDecompose] from "../../support/gr"
extern Map
extern Set
glyph-module glyph-module
@ -27,12 +29,16 @@ glyph-block Autobuild-Enclosure-Shared : begin
glyph-block-export EnsureComponentGlyphT glyph-block-export EnsureComponentGlyphT
define [EnsureComponentGlyphT gidPart fnBuildup] : begin define [EnsureComponentGlyphT gidPart fnBuildup] : begin
local rs : new Set
local gniPart : fnBuildup gidPart local gniPart : fnBuildup gidPart
local grs : AnyDerivingCv.query [query-glyph gidPart] local grs : AnyDerivingCv.query [query-glyph gidPart]
if grs : foreach gr [items-of grs] : begin if grs : foreach gr [items-of grs] : begin
local relatedGidPart : gr.get [query-glyph gidPart] local relatedGidPart : gr.get [query-glyph gidPart]
local gniRelated : fnBuildup relatedGidPart 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 return gniPart
glyph-block-export CollectJobs glyph-block-export CollectJobs
@ -78,8 +84,11 @@ glyph-block Autobuild-Enclosure-Shared : begin
glyph-block-export applyRelations glyph-block-export applyRelations
define [applyRelations relApplications] : begin define [applyRelations relApplications] : begin
local rs : new Set
foreach {gr f t} [items-of relApplications] : begin 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 AutoBuild-Enclosure : begin
glyph-block-import CommonShapes glyph-block-import CommonShapes
@ -91,18 +100,30 @@ glyph-block AutoBuild-Enclosure : begin
define [AdjustDigitCount digits width] : Math.max 1 (digits * Width / width) define [AdjustDigitCount digits width] : Math.max 1 (digits * Width / width)
define [EnclosureStrokeScale dscale digits width] : dscale / [Math.pow [AdjustDigitCount digits width] 0.66] 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 [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 '/'] 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
set-width 0 if [not : query-glyph gniPart] : begin
include : miniatureFont.queryByNameEnsured gidPart if aliasGoal : begin
include : Upright create-glyph gniPart : composite-proc
include : Scale (dscale * xCompress) dscale refer-glyph aliasGoal.gn
include : Translate 0 (dscale * (-CAP / 2 + shift)) Translate (finalPlacement - aliasGoal.placement) 0
include : Translate 0 (CAP / 2 * dscale) : else : begin
include : Translate 0 (SymbolMid - CAP * dscale / 2) create-glyph gniPart : glyph-proc
include : Italify set-width 0
include : Translate finalPlacement 0 include : miniatureFont.queryByNameEnsured gidPart
include : Upright
include : Scale (dscale * xCompress) dscale
include : Translate 0 (dscale * (-CAP / 2 + shift))
include : Translate 0 (CAP / 2 * dscale)
include : Translate 0 (SymbolMid - CAP * dscale / 2)
include : Italify
include : Translate finalPlacement 0
aliasableInnerSubGlyphMap.set gniPartNoPlc : object
gn gniPart
placement finalPlacement
return gniPart return gniPart
define [EnsureInnerSubGlyphSeq miniatureFont prefix job dimens] : begin 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 { gn unicode parts w bal baly } job
define [object width mockInnerWidth dscale] dimens define [object width mockInnerWidth dscale] dimens
local finalParts : EnsureInnerSubGlyphSeq miniatureFont prefix job 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] foreach [gidPart : items-of finalParts] : include [refer-glyph gidPart]
include : Translate width 0 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 [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] 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 dimens : bracedDottdeDimens digits ww
define [object width mockInnerWidth dscale] dimens define [object width mockInnerWidth dscale] dimens
set-width width set-width width
include : refer-glyph gnb
include : EnclosureInner [if jobDecomposable gnb null] miniatureFont prefix job dimens 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.decomposableJobs] : CreateGlyphImpl true job
foreach job [items-of jobs.nonDecomposable] : CreateGlyphImpl false job foreach job [items-of jobs.nonDecomposable] : CreateGlyphImpl false job

View file

@ -3,6 +3,7 @@ $$include '../../meta/macros.ptl'
import [linreg clamp mix fallback] from '../../support/utils' import [linreg clamp mix fallback] from '../../support/utils'
import [AnyDerivingCv DotlessOrNot getGrTree getGrMesh CvDecompose] from "../../support/gr" import [AnyDerivingCv DotlessOrNot getGrTree getGrMesh CvDecompose] from "../../support/gr"
extern Set
glyph-module glyph-module
@ -46,13 +47,17 @@ glyph-block Autobuild-Transformed : begin
set relatedRecord.1 relSrcName set relatedRecord.1 relSrcName
relatedRecords.push relatedRecord relatedRecords.push relatedRecord
relSets.push {gr origDstName relDstName} relSets.push { gr origDstName relDstName }
return {[records.concat relatedRecords] relSets targetNameMap} 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 define [createSuperscripts _records] : begin
local {records relSets targetNameMap} : extendRelatedGlyphs _records 'sup' local {records relSets targetNameMap} : extendRelatedGlyphs _records 'sup'
@ -254,7 +259,7 @@ glyph-block Autobuild-Transformed : begin
list 0x1DAC 'mltail' list 0x1DAC 'mltail'
list 0x1DAD 'turnmleg' list 0x1DAD 'turnmleg'
list 0x1DAE 'nltail' list 0x1DAE 'nltail'
list 0x1DAF 'nrtailBR' list 0x1DAF 'nHookBottom'
list 0x1DB0 'smcpN' list 0x1DB0 'smcpN'
list 0x1DB1 'obar' list 0x1DB1 'obar'
list 0x1DB2 'latinphi' list 0x1DB2 'latinphi'

View file

@ -90,27 +90,19 @@ glyph-block Common-Derivatives : begin
if [not newid] : throw : new Error "Target ID not specified" if [not newid] : throw : new Error "Target ID not specified"
return : HCombine newid unicode id id spacing 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" if [not newid] : throw : new Error "Target ID not specified"
create-glyph newid unicode : glyph-proc create-glyph newid unicode : glyph-proc
set-width [query-glyph id1].advanceWidth include [refer-glyph id2] AS_BASE ALSO_METRICS
currentGlyph.dependsOn [query-glyph id1] include : Translate (-spacing * TanSlope) (-spacing)
currentGlyph.dependsOn [query-glyph id2] include [refer-glyph id1]
include : new-glyph : glyph-proc include : Translate (spacing * TanSlope / 2) (spacing / 2)
include [refer-glyph id2]
include : Upright
include : Translate 0 (-spacing)
include : new-glyph : glyph-proc
include [refer-glyph id1]
include : Upright
include : Translate 0 (spacing / 2)
include : Italify
define [VDual newid unicode id spacing] : begin define [VDual newid unicode id spacing] : begin
if [not newid] : throw : new Error "Target ID not specified" 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 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 glyph-block Recursive-Build : begin
define [Fork gs ps] : begin define [Fork gs ps] : begin

View file

@ -1,8 +1,8 @@
import '../support/glyph' as Glyph import '../support/glyph' as Glyph
import '../support/glyph-store' as GlyphStore import '../support/glyph-store' as GlyphStore
import '../support/point' as Point import '../support/point' as Point
import './kits/spiro-kit' as spirokit import '../kits/spiro-kit' as spirokit
import './kits/boole-kit' as BooleKit import '../kits/boole-kit' as BooleKit
import '../support/anchor' as Anchor import '../support/anchor' as Anchor
import [ designParameters ] from "../meta/aesthetics" 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$ 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 ### HERE WE GO
run-glyph-module '../glyphs/common/shapes.js' run-glyph-module './common/shapes.js'
run-glyph-module '../glyphs/common/derivatives.js' run-glyph-module './common/derivatives.js'
run-glyph-module '../glyphs/space/index.js' run-glyph-module './space/index.js'
run-glyph-module '../glyphs/marks/index.js' run-glyph-module './marks/index.js'
# Unified letters # Unified letters
run-glyph-module '../glyphs/letter/latin.js' run-glyph-module './letter/latin.js'
run-glyph-module '../glyphs/letter/greek.js' run-glyph-module './letter/greek.js'
run-glyph-module '../glyphs/letter/cyrillic.js' run-glyph-module './letter/cyrillic.js'
# Numbers # Numbers
run-glyph-module '../glyphs/number/index.js' run-glyph-module './number/index.js'
# Symbols # Symbols
run-glyph-module '../glyphs/symbol/punctuation.js' run-glyph-module './symbol/punctuation.js'
run-glyph-module '../glyphs/symbol/arrow.js' run-glyph-module './symbol/arrow.js'
run-glyph-module '../glyphs/symbol/geometric.js' run-glyph-module './symbol/geometric.js'
run-glyph-module '../glyphs/symbol/enclosure.js' run-glyph-module './symbol/enclosure.js'
run-glyph-module '../glyphs/symbol/math.js' run-glyph-module './symbol/math.js'
run-glyph-module '../glyphs/symbol/letter.js' run-glyph-module './symbol/letter.js'
run-glyph-module '../glyphs/symbol/braille.js' run-glyph-module './symbol/braille.js'
run-glyph-module '../glyphs/symbol/mosaic.js' run-glyph-module './symbol/mosaic.js'
run-glyph-module '../glyphs/symbol/pictograph.js' run-glyph-module './symbol/pictograph.js'
run-glyph-module '../glyphs/symbol/ligation.js' run-glyph-module './symbol/ligation.js'
# Autobuilds # Autobuilds
run-glyph-module '../glyphs/auto-build/accents.js' run-glyph-module './auto-build/accents.js'
run-glyph-module '../glyphs/auto-build/composite.js' run-glyph-module './auto-build/composite.js'
run-glyph-module '../glyphs/auto-build/transformed.js' run-glyph-module './auto-build/transformed.js'
return : object metrics glyphStore return : object metrics glyphStore

View file

@ -1160,7 +1160,6 @@ glyph-block Marks : begin
curl (Middle + dx - Width) (XH / 2 + dy) curl (Middle + dx - Width) (XH / 2 + dy)
create-glyph 'arrVStrokeOver' : glyph-proc create-glyph 'arrVStrokeOver' : glyph-proc
include : ForceUpright
set-width 0 set-width 0
local fine : 0.375 * OverlayStroke local fine : 0.375 * OverlayStroke
local dx : Width * 0 local dx : Width * 0
@ -1171,7 +1170,6 @@ glyph-block Marks : begin
curl (Middle + dx - Width) (XH / 2 + dy) curl (Middle + dx - Width) (XH / 2 + dy)
create-glyph 'arrDblVStrokeOver' : glyph-proc create-glyph 'arrDblVStrokeOver' : glyph-proc
include : ForceUpright
set-width 0 set-width 0
local fine : 0.3 * OverlayStroke local fine : 0.3 * OverlayStroke
local dx : Width * 0 local dx : Width * 0
@ -1186,7 +1184,6 @@ glyph-block Marks : begin
curl (Middle + dx - Width + gap) (XH / 2 + dy) curl (Middle + dx - Width + gap) (XH / 2 + dy)
create-glyph 'arrHStrokeOver' : glyph-proc create-glyph 'arrHStrokeOver' : glyph-proc
include : ForceUpright
set-width 0 set-width 0
local fine : 0.375 * OverlayStroke local fine : 0.375 * OverlayStroke
local dx : Width * 0.3 local dx : Width * 0.3
@ -1196,7 +1193,6 @@ glyph-block Marks : begin
curl (Middle - Width + dx) (XH / 2) curl (Middle - Width + dx) (XH / 2)
create-glyph 'arrDblHStrokeOver' : glyph-proc create-glyph 'arrDblHStrokeOver' : glyph-proc
include : ForceUpright
set-width 0 set-width 0
local fine : 0.3 * OverlayStroke local fine : 0.3 * OverlayStroke
local dx : Width * 0.3 local dx : Width * 0.3

View file

@ -13,16 +13,11 @@ glyph-block Symbol-Mosaic-NotDef : begin
set currentGlyph.autoRefPriority (-9998) set currentGlyph.autoRefPriority (-9998)
set currentGlyph.glyphRank (9998) set currentGlyph.glyphRank (9998)
create-glyph 'nonmarkingreturn' 0x000D : glyph-proc
set-width 0
set currentGlyph.autoRefPriority (-1)
create-glyph 'space' ' ' : glyph-proc create-glyph 'space' ' ' : glyph-proc
set currentGlyph.autoRefPriority (-100) set currentGlyph.autoRefPriority (-100)
local df : DivFrame para.diversityI local df : DivFrame para.diversityI
set-width df.width set-width df.width
create-glyph 'markDemoBaseSpace' 0xE00E : glyph-proc create-glyph 'markDemoBaseSpace' 0xE00E : glyph-proc
local df : DivFrame 1 local df : DivFrame 1
set-width df.width set-width df.width
@ -41,7 +36,8 @@ glyph-block Symbol-Mosaic-NotDef : begin
set-width 0 set-width 0
set currentGlyph.autoRefPriority (-9999) set currentGlyph.autoRefPriority (-9999)
alias 'cgj' 0x034F '.null' alias 'nonmarkingreturn' 0x000D 'zwsp'
alias 'cgj' 0x034F 'zwsp'
alias 'nbsp' 0xA0 'space' alias 'nbsp' 0xA0 'space'
alias 'threePerEmsp' 0x2004 'space' alias 'threePerEmsp' 0x2004 'space'
alias 'fourPerEmsp' 0x2005 'space' alias 'fourPerEmsp' 0x2005 'space'

View file

@ -443,17 +443,17 @@ glyph-block Symbol-Arrow : begin
do do
local hcDist : Math.max (halfstroke * 5) (arrowWidth / 2) 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 HCombine [MangleName 'uni21C5'] [MangleUnicode 0x21C5] [MangleName 'arrowup'] [MangleName 'arrowdown'] hcDist
vcombine [MangleName 'uni21C6'] [MangleUnicode 0x21C6] [MangleName 'arrowleft'] [MangleName 'arrowright'] (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) VCombine [MangleName 'uni21C7'] [MangleUnicode 0x21C7] [MangleName 'arrowleft'] [MangleName 'arrowleft'] (arrowSize * 1.75)
HCombine [MangleName 'uni21C8'] [MangleUnicode 0x21C8] [MangleName 'arrowup'] [MangleName 'arrowup'] hcDist 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 'uni21CA'] [MangleUnicode 0x21CA] [MangleName 'arrowdown'] [MangleName 'arrowdown'] hcDist
HCombine [MangleName 'uni21F5'] [MangleUnicode 0x21F5] [MangleName 'arrowdown'] [MangleName 'arrowup'] hcDist HCombine [MangleName 'uni21F5'] [MangleUnicode 0x21F5] [MangleName 'arrowdown'] [MangleName 'arrowup'] hcDist
vcombine [MangleName 'uni21CB'] [MangleUnicode 0x21CB] [MangleName 'arrowleftHR'] [MangleName 'arrowrightHR'] (arrowSize) VCombine [MangleName 'uni21CB'] [MangleUnicode 0x21CB] [MangleName 'arrowleftHR'] [MangleName 'arrowrightHR'] (arrowSize)
vcombine [MangleName 'uni21CC'] [MangleUnicode 0x21CC] [MangleName 'arrowrightHL'] [MangleName 'arrowleftHL'] (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 'uni21B9'] [MangleUnicode 0x21B9] [MangleName 'arrowbarleft'] [MangleName 'arrowbarright'] (arrowSize * 1.75)
do "Legacy computing arrows" do "Legacy computing arrows"
define bodyWidth : Math.min arrowWidth (arrowHeight * 0.6) define bodyWidth : Math.min arrowWidth (arrowHeight * 0.6)

View file

@ -3,9 +3,7 @@ $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from '../../support/utils' import [mix linreg clamp fallback] from '../../support/utils'
import [designParameters] from '../../meta/aesthetics' import [designParameters] from '../../meta/aesthetics'
import [maskBit maskBits popCountByte] from '../../support/mask-bit'
# Needed for bitwise operators
import '../../support/mask-bit' as maskBit
glyph-module glyph-module
@ -21,6 +19,39 @@ glyph-block Symbol-Braille : begin
local stress : 0.5 * [adviceBlackness 3.375] local stress : 0.5 * [adviceBlackness 3.375]
local dotRadius : PeriodRadius * [Math.min 1 (stress / HalfStroke)] 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 ### Computed Braille Patterns
### ###
### Bit to Dot mapping: ### Bit to Dot mapping:
@ -28,52 +59,12 @@ glyph-block Symbol-Braille : begin
### 1 4 ### 1 4
### 2 5 ### 2 5
### 6 7 ### 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 define stageConfig : list
if [query-glyph 'braille37'] : set [query-glyph 'braille37'].autoRefPriority 102 list 1 BrailleOutlineShape
if [query-glyph 'braille3'] : set [query-glyph 'braille3'].autoRefPriority 101 list 2 BrailleReferenceShape
if [query-glyph 'braille237'] : set [query-glyph 'braille237'].autoRefPriority 103 list 3 BrailleHalvesShape
if [query-glyph 'braille27'] : set [query-glyph 'braille27'].autoRefPriority 102
if [query-glyph 'braille23'] : set [query-glyph 'braille23'].autoRefPriority 102 foreach { stage F } [items-of stageConfig] : begin
if [query-glyph 'braille2'] : set [query-glyph 'braille2'].autoRefPriority 101 foreach [byte : range 1 256] : if (stage == [brailleBuildStage byte])
if [query-glyph 'braille1237'] : set [query-glyph 'braille1237'].autoRefPriority 104 create-glyph [brailleGlyphName byte] (0x2800 + byte) : F byte
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

View file

@ -305,7 +305,7 @@ glyph-block Symbol-Geometric : for-width-kinds WideWidth1
corner (cx - size * triangleOvershoot) (cy - size) corner (cx - size * triangleOvershoot) (cy - size)
define [TriangularVeeFill cx cy size] : intersection define [TriangularVeeFill cx cy size] : intersection
TriangleUpFill cx cy size TriangleDownFill cx cy size
union union
dispiro dispiro
widths.lhs GeometryStroke widths.lhs GeometryStroke

View file

@ -4,7 +4,7 @@ $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from '../../support/utils' import [mix linreg clamp fallback] from '../../support/utils'
import [designParameters] from '../../meta/aesthetics' import [designParameters] from '../../meta/aesthetics'
import '../../support/transform' as Transform import '../../support/transform' as Transform
import '../../support/mask-bit' as maskBit import [maskBit] from '../../support/mask-bit'
glyph-module glyph-module

View file

@ -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 [mix [mix PeriodSize commaLow 0.5] yCurlyQuotes 0.5]
include : FlipAround Middle yCurlyQuotes include : FlipAround Middle yCurlyQuotes
create-glyph 'lowDoubleQuote' 0x201E : glyph-proc define [DoubleQuoteProc gn] : glyph-proc
local dist : Width * 0.225 local dist : Width * 0.225
include : refer-glyph "lowSingleQuote" include : refer-glyph gn
include : Translate (-dist * 2) 0 include : Translate (-dist * 2) 0
include : refer-glyph "lowSingleQuote" include : refer-glyph gn
include : Translate dist 0 include : Translate dist 0
create-glyph 'openDoubleQuote' 0x201C : glyph-proc create-glyph 'lowDoubleQuote' 0x201E : DoubleQuoteProc 'lowSingleQuote'
include : refer-glyph "lowDoubleQuote" create-glyph 'openDoubleQuote' 0x201C : DoubleQuoteProc 'openSingleQuote'
include : FlipAround Middle [mix [mix PeriodSize commaLow 0.5] yCurlyQuotes 0.5] create-glyph 'closeDoubleQuote' 0x201D : DoubleQuoteProc 'closeSingleQuote'
create-glyph 'revertDoubleQuote' 0x201F : DoubleQuoteProc 'revertSingleQuote'
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 'prime' 0x2032 : glyph-proc create-glyph 'prime' 0x2032 : glyph-proc
local wide : Width * 0.1 local wide : Width * 0.1
@ -1592,15 +1582,18 @@ glyph-block Symbol-Punctuation-Ligation-Variants : begin
* "question" * "question"
* "exclam" * "exclam"
foreach [baseName : items-of dotLikePunctuations] : if [query-glyph baseName] define [LigD baseName suffix pDelta] : if [query-glyph baseName] : begin
create-glyph : glyph-proc local delta : Math.max 0 : Width / 2 - (XH - PeriodSize) / 2
include [refer-glyph baseName] AS_BASE ALSO_METRICS create-glyph (baseName + '.lig.' + suffix) : composite-proc
local delta : Math.max 0 : Width / 2 - (XH - PeriodSize) / 2 refer-glyph baseName
create-derived (baseName + '.lig.dRight') : Translate (delta * 1.0 ) 0 Translate (delta * pDelta ) 0
create-derived (baseName + '.lig.dRightHalf') : Translate (delta * 0.5 ) 0
create-derived (baseName + ".lig.dMid") : Translate 0 0 foreach [baseName : items-of dotLikePunctuations] : begin
create-derived (baseName + '.lig.dLeftHalf') : Translate (delta * (-0.5)) 0 LigD baseName 'dRight' 1.0
create-derived (baseName + '.lig.dLeft') : Translate (delta * (-1.0)) 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 Symbol-Other-Phonetic : begin
glyph-block-import CommonShapes glyph-block-import CommonShapes

View file

@ -2,6 +2,7 @@
const fs = require("fs-extra"); const fs = require("fs-extra");
const path = require("path"); const path = require("path");
const { FontIo } = require("ot-builder");
const Toml = require("@iarna/toml"); const Toml = require("@iarna/toml");
@ -15,7 +16,7 @@ module.exports = async function main(argv) {
const para = await getParameters(argv); const para = await getParameters(argv);
const { font, glyphStore } = BuildFont(para); const { font, glyphStore } = BuildFont(para);
if (argv.oCharMap) await saveCharMap(argv, glyphStore); if (argv.oCharMap) await saveCharMap(argv, glyphStore);
if (argv.o) await saveOTD(argv, font); if (argv.o) await saveTTF(argv, font);
}; };
// Parameter preparation // Parameter preparation
@ -81,9 +82,14 @@ async function tryParseToml(str) {
} }
} }
// Save OTD // Save TTF
async function saveOTD(argv, font) { async function saveTTF(argv, font) {
await fs.writeJSON(argv.o, 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 // Save character map file

View file

@ -1,5 +1,5 @@
import 'typo-geom' as TypoGeom import 'typo-geom' as TypoGeom
import '../../support/curve-util' as CurveUtil import '../support/curve-util' as CurveUtil
export : define [SetupBuilders args] : begin export : define [SetupBuilders args] : begin
define [object Glyph GlobalTransform] args define [object Glyph GlobalTransform] args

View file

@ -1,8 +1,8 @@
import 'spiro' as SpiroJs import 'spiro' as SpiroJs
import '../../support/spiro-expand' as SpiroExpansionContext import '../support/spiro-expand' as SpiroExpansionContext
import '../../support/curve-util' as CurveUtil import '../support/curve-util' as CurveUtil
import '../../support/transform' as Transform import '../support/transform' as Transform
import '../../support/utils' as [object fallback mix bez2 bez3] import '../support/utils' as [object fallback mix bez2 bez3]
export : define [SetupBuilders args] : begin export : define [SetupBuilders args] : begin
define [object para Glyph Contrast GlobalTransform Stroke] args define [object para Glyph Contrast GlobalTransform Stroke] args

View file

@ -107,7 +107,7 @@ define-macro glyph-block-import : syntax-rules
define allExports : object define allExports : object
Common-Derivatives `[select-variant italic-variant refer-glyph query-glyph alias turned 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 CommonShapes `[Rect SquareAt Ring RingAt DotAt RingStroke RingStrokeAt DotStrokeAt
CircleRing CircleRingAt CircleDotAt OShape OShapeOutline OBarLeftShape OBarRightShape CircleRing CircleRingAt CircleDotAt OShape OShapeOutline OBarLeftShape OBarRightShape

View file

@ -140,9 +140,6 @@ export : define [assignFontNames para metrics font] : begin
nameFont font WWS_PREFERRED_FAMILY family # WWS Preferred Family nameFont font WWS_PREFERRED_FAMILY family # WWS Preferred Family
nameFont font WWS_PREFERRED_STYLE style # WWS Preferred Style 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 {compatStyle compatFamilySuffix shortCompatFamilySuffix} : getStyleLinkedStyles para.naming.weight para.naming.width para.naming.slope
local compatFamily family local compatFamily family
if (compatFamilySuffix != "Regular") : set compatFamily : family + ' ' + compatFamilySuffix if (compatFamilySuffix != "Regular") : set compatFamily : family + ' ' + compatFamilySuffix

View file

@ -18,17 +18,17 @@ export : define [buildMarkMkmk sink glyphStore] : begin
foreach markCls [items-of MarkClasses] : begin foreach markCls [items-of MarkClasses] : begin
local [object markSubtable mkmkSubtable] : createMTSubtables glyphStore { markCls } local [object markSubtable mkmkSubtable] : createMTSubtables glyphStore { markCls }
if ([objectIsNotEmpty markSubtable.marks] && [objectIsNotEmpty markSubtable.bases]) : begin if ([objectIsNotEmpty markSubtable.marks] && [objectIsNotEmpty markSubtable.bases]) : begin
local markLookup : add-lookup sink {.type 'gpos_mark_to_base' .subtables { markSubtable }} local lidMark : add-lookup sink {.type 'gpos_mark_to_base' .subtables { markSubtable }}
mark.lookups.push markLookup mark.lookups.push lidMark
markLookupNames.push markLookup markLookupNames.push lidMark
if ([objectIsNotEmpty mkmkSubtable.marks] && [objectIsNotEmpty mkmkSubtable.bases]) : begin if ([objectIsNotEmpty mkmkSubtable.marks] && [objectIsNotEmpty mkmkSubtable.bases]) : begin
local mkmkLookup : add-lookup sink {.type 'gpos_mark_to_mark' .subtables { mkmkSubtable }} local lidMkmk : add-lookup sink {.type 'gpos_mark_to_mark' .subtables { mkmkSubtable }}
mkmk.lookups.push mkmkLookup mkmk.lookups.push lidMkmk
mkmkLookupNames.push mkmkLookup mkmkLookupNames.push lidMkmk
foreach markLookup [items-of markLookupNames] : foreach mkmkLookup [items-of mkmkLookupNames] foreach lidMark [items-of markLookupNames] : foreach lidMkmk [items-of mkmkLookupNames]
sink.lookupDep.push { markLookup mkmkLookup } sink.lookupDep.push { lidMark lidMkmk }
define [createMTSubtables glyphStore markClasses] : begin define [createMTSubtables glyphStore markClasses] : begin
local markSubtable {.marks {.} .bases {.}} local markSubtable {.marks {.} .bases {.}}

View file

@ -13,6 +13,7 @@ module.exports = class Glyph {
this.markAnchors = {}; this.markAnchors = {};
this.baseAnchors = {}; this.baseAnchors = {};
this.gizmo = Transform.Id(); this.gizmo = Transform.Id();
this.semanticInclusions = [];
this.dependencies = []; this.dependencies = [];
this.defaultTag = null; this.defaultTag = null;
} }
@ -51,6 +52,7 @@ module.exports = class Glyph {
this.contours[j++] = this.contours[i]; this.contours[j++] = this.contours[i];
} }
this.contours.length = j; this.contours.length = j;
this.semanticInclusions = [];
} }
// Inclusion // Inclusion
include(component, copyAnchors, copyWidth) { 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 (copyAnchors || g.isMarkSet) this.copyAnchors(g);
if (copyWidth && g.advanceWidth >= 0) this.advanceWidth = g.advanceWidth; if (copyWidth && g.advanceWidth >= 0) this.advanceWidth = g.advanceWidth;
this.dependsOn(g); 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) { cloneFromGlyph(g) {
this.includeGlyph(g, true, true); this.includeGlyph(g, true, true);
@ -102,15 +112,56 @@ module.exports = class Glyph {
this.glyphRank = g.glyphRank; this.glyphRank = g.glyphRank;
this.avoidBeingComposite = g.avoidBeingComposite; this.avoidBeingComposite = g.avoidBeingComposite;
} }
includeGeometry(geom, shiftX, shiftY) { 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) { for (const contour of geom.contours) {
let c = []; let c = [];
c.tag = contour.tag || geom.tag || this.defaultTag; c.tag = contour.tag || geom.tag || this.defaultTag;
for (const z of contour) c.push(Point.translated(z, shiftX, shiftY)); for (const z of contour) c.push(Point.translated(z, shiftX, shiftY));
this.contours.push(c); this.contours.push(c);
newContours.push(c);
} }
return newContours;
} }
combineAnchor(shift, baseThis, markThat, basesThat) { combineAnchor(shift, baseThis, markThat, basesThat) {
if (!baseThis || !markThat) return; if (!baseThis || !markThat) return;
shift.x = baseThis.x - markThat.x; shift.x = baseThis.x - markThat.x;
@ -129,10 +180,20 @@ module.exports = class Glyph {
if (g.baseAnchors) for (const k in g.baseAnchors) this.baseAnchors[k] = g.baseAnchors[k]; if (g.baseAnchors) for (const k in g.baseAnchors) this.baseAnchors[k] = g.baseAnchors[k];
} }
applyTransform(tfm, alsoAnchors) { applyTransform(tfm, alsoAnchors) {
for (const c of this.contours) for (const c of this.contours) {
for (let j = 0; j < c.length; j++) { for (let k = 0; k < c.length; k++) {
c[j] = Point.transformed(tfm, c[j]); 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) { if (alsoAnchors) {
for (const k in this.baseAnchors) for (const k in this.baseAnchors)
this.baseAnchors[k] = Anchor.transform(tfm, this.baseAnchors[k]); this.baseAnchors[k] = Anchor.transform(tfm, this.baseAnchors[k]);
@ -140,6 +201,35 @@ module.exports = class Glyph {
this.markAnchors[k] = Anchor.transform(tfm, this.markAnchors[k]); 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 // Anchors
setBaseAnchor(id, x, y) { setBaseAnchor(id, x, y) {
this.baseAnchors[id] = new Anchor(x, y).transform(this.gizmo); this.baseAnchors[id] = new Anchor(x, y).transform(this.gizmo);

View file

@ -1,5 +1,14 @@
"use strict"; "use strict";
module.exports = function maskBit(x, y) { exports.maskBit = function maskBit(x, y) {
return x & (1 << 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];
};

View file

@ -46,4 +46,8 @@ module.exports = class Transform {
-(-this.x * this.xy + this.y * this.xx) / denom -(-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;
}
}; };

View file

@ -14,7 +14,8 @@
"ejs": "^3.1.3", "ejs": "^3.1.3",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"object-assign": "^4.1.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", "patel": "^0.33.1",
"semver": "^7.3.2", "semver": "^7.3.2",
"spiro": "^2.0.0", "spiro": "^2.0.0",

View file

@ -7,11 +7,7 @@ const FgYellow = "\x1b[33m";
const Reset = "\x1b[0m"; const Reset = "\x1b[0m";
console.log("Checking External Dependencies"); console.log("Checking External Dependencies");
check("otfccdump");
check("otfccbuild");
check("ttfautohint"); check("ttfautohint");
check("otf2otc");
checkOptional("ttx");
function check(util) { function check(util) {
try { try {
@ -21,11 +17,3 @@ function check(util) {
console.error(FgRed + ` * External dependency <${util}> is not found.` + Reset); 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);
}
}

View file

@ -12,7 +12,7 @@ module.exports = build;
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
const path = require("path"); const Path = require("path");
const toml = require("@iarna/toml"); const toml = require("@iarna/toml");
const BUILD = ".build"; const BUILD = ".build";
@ -21,11 +21,13 @@ const SNAPSHOT_TMP = ".build/snapshot";
const DIST_SUPER_TTC = "dist/.super-ttc"; const DIST_SUPER_TTC = "dist/.super-ttc";
const ARCHIVE_DIR = "release-archives"; const ARCHIVE_DIR = "release-archives";
const OTF2OTC = "otf2otc";
const OTFCC_BUILD = "otfccbuild";
const TTX = "ttx"; const TTX = "ttx";
const PATEL_C = ["node", "./node_modules/patel/bin/patel-c"]; 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 = [ const webfontFormats = [
["woff2", "woff2"], ["woff2", "woff2"],
["woff", "woff"], ["woff", "woff"],
@ -41,11 +43,8 @@ const WEIGHT_NORMAL = "regular";
const SLOPE_NORMAL = "upright"; const SLOPE_NORMAL = "upright";
const DEFAULT_SUBFAMILY = "regular"; const DEFAULT_SUBFAMILY = "regular";
const BUILD_PLANS = path.relative(__dirname, path.resolve(__dirname, "./build-plans.toml")); const BUILD_PLANS = "build-plans.toml";
const PRIVATE_BUILD_PLANS = path.relative( const PRIVATE_BUILD_PLANS = "private-build-plans.toml";
__dirname,
path.resolve(__dirname, "./private-build-plans.toml")
);
// Save journal to build/ // Save journal to build/
build.setJournal(`${BUILD}/.verda-build-journal`); build.setJournal(`${BUILD}/.verda-build-journal`);
@ -56,8 +55,9 @@ build.setSelfTracking();
////// Oracles ////// ////// Oracles //////
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
const Version = oracle(`oracle:version`, async () => { const Version = oracle(`oracle:version`, async target => {
const package_json = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"))); 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; return package_json.version;
}); });
@ -422,15 +422,9 @@ const BuildRawTtf = file.make(
const [fi] = await target.need(FontInfoOf(fn), Version); const [fi] = await target.need(FontInfoOf(fn), Version);
const charmap = output.dir + "/" + fn + ".charmap"; const charmap = output.dir + "/" + fn + ".charmap";
await target.need(Scripts, Parameters, de`${output.dir}`); await target.need(Scripts, Parameters, de`${output.dir}`);
const otdPath = `${output.dir}/${output.name}.otd`; await node("font-src/index", { o: output.full, oCharMap: charmap, ...fi });
await node("font-src/index", { o: otdPath, oCharMap: charmap, ...fi });
await optimizedOtfcc(otdPath, output.full);
await rm(otdPath);
} }
); );
function optimizedOtfcc(from, to) {
return run(OTFCC_BUILD, from, ["-o", `${to}`], ["-O3", "--keep-average-char-width", "-q"]);
}
const BuildTTF = file.make( const BuildTTF = file.make(
(gr, fn) => `${BUILD}/ttf/${gr}/${fn}.ttf`, (gr, fn) => `${BUILD}/ttf/${gr}/${fn}.ttf`,
@ -557,25 +551,17 @@ const glyfTtc = file.make(
); );
async function buildGlyfTtc(target, parts, out) { async function buildGlyfTtc(target, parts, out) {
const [useFilter, useTtx] = await target.need( await target.need(de`${out.dir}`);
OptimizeWithFilter,
OptimizeWithTtx,
de`${out.dir}`
);
const [ttfInputs] = await target.need(parts.map(part => BuildTTF(part.dir, part.file))); const [ttfInputs] = await target.need(parts.map(part => BuildTTF(part.dir, part.file)));
const tmpTtc = `${out.dir}/${out.name}.unhinted.ttc`; const tmpTtc = `${out.dir}/${out.name}.unhinted.ttc`;
const ttfInputPaths = ttfInputs.map(p => p.full); const ttfInputPaths = ttfInputs.map(p => p.full);
const optimization = useFilter ? ["--filter-loop", useFilter] : useTtx ? ["--ttx-loop"] : []; await run(TTCIZE, "-u", ["-o", tmpTtc], ttfInputPaths);
await run(TTCIZE, optimization, ["-o", tmpTtc], ttfInputPaths);
await run("ttfautohint", tmpTtc, out.full); await run("ttfautohint", tmpTtc, out.full);
await rm(tmpTtc); await rm(tmpTtc);
} }
async function buildCompositeTtc(out, inputs) { async function buildCompositeTtc(out, inputs) {
await run( const inputPaths = inputs.map(f => f.full);
OTF2OTC, await run(TTCIZE, ["-o", out.full], inputPaths);
["-o", out.full],
inputs.map(f => f.full)
);
} }
const ExportSuperTtc = file.make( 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 = oracle(`pages-dir-path`, async target => {
const pagesDir = path.resolve(__dirname, "../Iosevka-Pages"); const pagesDir = Path.resolve(__dirname, "../Iosevka-Pages");
if (!fs.existsSync(pagesDir)) { if (!fs.existsSync(pagesDir)) {
return ""; return "";
} else { } else {
@ -686,7 +672,7 @@ const PagesDataExport = task(`pages:data-export`, async target => {
cm.full, cm.full,
cmi.full, cmi.full,
cmo.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` GroupContents`iosevka-sparkle`
); );
for (const dir of dirs) { 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; if (!pagesDir) return;
const dirs = await target.need(GroupContents`iosevka`); const dirs = await target.need(GroupContents`iosevka`);
for (const dir of dirs) { 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 JavaScriptFromPtl = computed("scripts-js-from-ptl", async target => {
const [ptl] = await target.need(ScriptFiles("ptl")); 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 CompiledJs = file.make(
const [jsFromPtl] = await target.need(JavaScriptFromPtl); p => p,
if (jsFromPtl.indexOf(path.full) >= 0) { async (target, out) => {
const ptl = path.full.replace(/\.js$/g, ".ptl"); const ptl = replaceExt(".ptl", out.full);
if (/\/glyphs\//.test(path.full)) { if (/\/glyphs\//.test(out.full)) await target.need(MARCOS);
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 => { const Scripts = task("scripts", async target => {
await target.need(Parameters); await target.need(Parameters);
const [jsFromPtl] = await target.need(JavaScriptFromPtl); const [_jsFromPtl] = await target.need(JavaScriptFromPtl);
await target.need(jsFromPtl);
const [js] = await target.need(ScriptFiles("js")); 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 UtilScripts = task("util-scripts", async target => {
const [files] = await target.need(UtilScriptFiles); const [files] = await target.need(UtilScriptFiles);