Refactor OTL script
This commit is contained in:
parent
01cc069462
commit
e259814b62
19 changed files with 623 additions and 559 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -44,19 +44,18 @@ snapshot/iosevka*
|
||||||
snapshot/index.css
|
snapshot/index.css
|
||||||
|
|
||||||
# Generated scripts
|
# Generated scripts
|
||||||
.buildglyphs.all.patel
|
|
||||||
build-glyphs.js
|
|
||||||
parameters.js
|
|
||||||
support/glyph.js
|
|
||||||
support/point.js
|
|
||||||
support/anchor.js
|
support/anchor.js
|
||||||
|
support/glyph.js
|
||||||
|
support/parameters.js
|
||||||
|
support/point.js
|
||||||
support/transform.js
|
support/transform.js
|
||||||
support/simple-expand.js
|
support/simple-expand.js
|
||||||
support/spiroexpand.js
|
support/spiroexpand.js
|
||||||
support/spirokit.js
|
support/spirokit.js
|
||||||
support/utils.js
|
support/utils.js
|
||||||
|
gen/build-glyphs.js
|
||||||
meta/*.js
|
meta/*.js
|
||||||
meta/feature/*.js
|
otl/*.js
|
||||||
glyphs/*.js
|
glyphs/*.js
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
58
gen/build-font.js
Normal file
58
gen/build-font.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
const EmptyFont = require("./empty-font.js");
|
||||||
|
const buildGlyphs = require("./build-glyphs.js");
|
||||||
|
|
||||||
|
const { buildOtl } = require("../otl/index");
|
||||||
|
const { assignFontNames } = require("../meta/naming");
|
||||||
|
const { setFontMetrics } = require("../meta/aesthetics");
|
||||||
|
|
||||||
|
const regulateGlyphs = require("../support/regulate-glyph");
|
||||||
|
|
||||||
|
module.exports = function(para) {
|
||||||
|
const font = EmptyFont();
|
||||||
|
const gs = buildGlyphs(para);
|
||||||
|
|
||||||
|
assignFontNames(para, gs.metrics, font);
|
||||||
|
setFontMetrics(para, gs.metrics, font);
|
||||||
|
|
||||||
|
const otl = buildOtl(para, gs.glyphs, gs.glyphList, gs.unicodeGlyphs);
|
||||||
|
font.GSUB = otl.GSUB;
|
||||||
|
font.GPOS = otl.GPOS;
|
||||||
|
font.GDEF = otl.GDEF;
|
||||||
|
|
||||||
|
// Filtering
|
||||||
|
if (para.forceMonospace && para.spacing == 0) {
|
||||||
|
gs.glyphList = gs.glyphList.filter(g => !(g.advanceWidth > para.width));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regulate
|
||||||
|
const skew = Math.tan(((font.post.italicAngle || 0) / 180) * Math.PI);
|
||||||
|
const glyphList = regulateGlyphs(gs.glyphList, skew);
|
||||||
|
|
||||||
|
// finalize
|
||||||
|
const excludedCodePoints = new Set();
|
||||||
|
if (para.excludedCodePointRanges) {
|
||||||
|
for (const [start, end] of para.excludedCodePointRanges) {
|
||||||
|
for (let p = start; p <= end; p++) excludedCodePoints.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { glyf, cmap } = extractGlyfAndCmap(glyphList, excludedCodePoints);
|
||||||
|
font.glyf = glyf;
|
||||||
|
font.cmap = cmap;
|
||||||
|
|
||||||
|
return font;
|
||||||
|
};
|
||||||
|
|
||||||
|
function extractGlyfAndCmap(glyphList, excludedCodePoints) {
|
||||||
|
const glyf = {};
|
||||||
|
const cmap = {};
|
||||||
|
for (let g of glyphList) {
|
||||||
|
glyf[g.name] = g;
|
||||||
|
|
||||||
|
if (!g.unicode) continue;
|
||||||
|
for (let u of g.unicode) {
|
||||||
|
if (!excludedCodePoints.has(u - 0)) cmap[u] = g.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { glyf, cmap };
|
||||||
|
}
|
|
@ -8,8 +8,6 @@ import '../support/fairify' as fairify
|
||||||
|
|
||||||
import [mix linreg clamp fallback TempFont includeGlyphPart compsiteMarkSet] from '../support/utils'
|
import [mix linreg clamp fallback TempFont includeGlyphPart compsiteMarkSet] from '../support/utils'
|
||||||
import [calculateMetrics setFontMetrics MarksetDiv GenDivFrame] from '../meta/aesthetics'
|
import [calculateMetrics setFontMetrics MarksetDiv GenDivFrame] from '../meta/aesthetics'
|
||||||
import [assignFontNames] from '../meta/naming'
|
|
||||||
import '../meta/features' as Features
|
|
||||||
|
|
||||||
$$include '../meta/macros.ptl'
|
$$include '../meta/macros.ptl'
|
||||||
|
|
||||||
|
@ -25,7 +23,7 @@ define [tagged tag component] : begin
|
||||||
set component.tag tag
|
set component.tag tag
|
||||||
return component
|
return component
|
||||||
|
|
||||||
define [buildGlyphs para recursive recursiveCodes] : begin
|
export all : define [buildGlyphs para recursive recursiveCodes] : begin
|
||||||
define variantSelector para.variantSelector
|
define variantSelector para.variantSelector
|
||||||
local glyphList {}
|
local glyphList {}
|
||||||
local glyphs {.}
|
local glyphs {.}
|
||||||
|
@ -144,22 +142,3 @@ define [buildGlyphs para recursive recursiveCodes] : begin
|
||||||
|
|
||||||
return : object metrics glyphs glyphList unicodeGlyphs
|
return : object metrics glyphs glyphList unicodeGlyphs
|
||||||
|
|
||||||
# Main build procedure
|
|
||||||
export as build : define [buildFont font para] : begin
|
|
||||||
|
|
||||||
define gs : buildGlyphs para
|
|
||||||
|
|
||||||
assignFontNames para gs.metrics font
|
|
||||||
setFontMetrics para gs.metrics font
|
|
||||||
|
|
||||||
local otl : Features.apply para gs.glyphs gs.glyphList gs.unicodeGlyphs
|
|
||||||
set font.GSUB otl.GSUB
|
|
||||||
set font.GPOS otl.GPOS
|
|
||||||
set font.GDEF otl.GDEF
|
|
||||||
|
|
||||||
if (para.forceMonospace && para.spacing == 0) : begin
|
|
||||||
set gs.glyphList : gs.glyphList.filter [lambda [g] : g.advanceWidth <= para.width]
|
|
||||||
|
|
||||||
set font.glyfMap gs.glyphs
|
|
||||||
set font.glyf gs.glyphList
|
|
||||||
return font
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ const fs = require("fs-extra");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const argv = require("yargs").argv;
|
const argv = require("yargs").argv;
|
||||||
const buildGlyphs = require("./build-glyphs.js");
|
const buildFont = require("./build-font.js");
|
||||||
const EmptyFont = require("./empty-font.js");
|
const EmptyFont = require("./empty-font.js");
|
||||||
const parameters = require("../support/parameters");
|
const parameters = require("../support/parameters");
|
||||||
const formVariantData = require("../support/variant-data");
|
const formVariantData = require("../support/variant-data");
|
||||||
|
@ -20,21 +20,13 @@ main().catch(e => {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const font = await buildFont();
|
const para = await getParameters(argv);
|
||||||
|
const font = buildFont(para);
|
||||||
if (argv.charmap) await saveCharMap(font);
|
if (argv.charmap) await saveCharMap(font);
|
||||||
if (argv.o) await saveFont(font);
|
if (argv.o) await saveOtd(font);
|
||||||
}
|
|
||||||
|
|
||||||
async function tryParseToml(str) {
|
|
||||||
try {
|
|
||||||
return toml.parse(await fs.readFile(str, "utf-8"));
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parameter preparation
|
||||||
async function getParameters(argv) {
|
async function getParameters(argv) {
|
||||||
const PARAMETERS_TOML = path.resolve(__dirname, "../parameters.toml");
|
const PARAMETERS_TOML = path.resolve(__dirname, "../parameters.toml");
|
||||||
const PRIVATE_TOML = path.resolve(__dirname, "../private.toml");
|
const PRIVATE_TOML = path.resolve(__dirname, "../private.toml");
|
||||||
|
@ -71,13 +63,23 @@ async function getParameters(argv) {
|
||||||
return para;
|
return para;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Font building
|
async function tryParseToml(str) {
|
||||||
async function buildFont() {
|
try {
|
||||||
const emptyFont = EmptyFont();
|
return toml.parse(await fs.readFile(str, "utf-8"));
|
||||||
const para = await getParameters(argv);
|
} catch (e) {
|
||||||
const font = buildGlyphs.build(emptyFont, para);
|
throw new Error(
|
||||||
font.parameters = para;
|
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`
|
||||||
return font;
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save OTD
|
||||||
|
async function saveOtd(font) {
|
||||||
|
if (argv.o === "|") {
|
||||||
|
process.stdout.write(JSON.stringify(font));
|
||||||
|
} else {
|
||||||
|
await fs.writeFile(argv.o, JSON.stringify(font, null, " "));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save char map
|
// Save char map
|
||||||
|
@ -86,50 +88,18 @@ function objHashNonEmpty(obj) {
|
||||||
for (let k in obj) if (obj[k]) return true;
|
for (let k in obj) if (obj[k]) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCharMap(font) {
|
async function saveCharMap(font) {
|
||||||
const charmap = font.glyf.map(function(glyph) {
|
let charMap = [];
|
||||||
const isSpace = glyph.contours && glyph.contours.length ? 2 : 0;
|
for (const gid in font.glyf) {
|
||||||
return [
|
const glyph = font.glyf[gid];
|
||||||
|
if (!glyph) continue;
|
||||||
|
const isSpace = glyph.contours && glyph.contours.length;
|
||||||
|
charMap.push([
|
||||||
glyph.name,
|
glyph.name,
|
||||||
glyph.unicode,
|
glyph.unicode,
|
||||||
glyph.advanceWidth === 0 ? (objHashNonEmpty(glyph.anchors) ? 1 : isSpace ? 2 : 0) : 0
|
glyph.advanceWidth === 0 ? (objHashNonEmpty(glyph.anchors) ? 1 : isSpace ? 2 : 0) : 0
|
||||||
];
|
]);
|
||||||
});
|
|
||||||
await fs.writeFile(argv.charmap, JSON.stringify(charmap), "utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveFont(font) {
|
|
||||||
const skew = Math.tan(((font.post.italicAngle || 0) / 180) * Math.PI);
|
|
||||||
|
|
||||||
regulateGlyphs(font, skew);
|
|
||||||
|
|
||||||
// finalize
|
|
||||||
const excludedCodePoints = new Set();
|
|
||||||
if (font.parameters.excludedCodePointRanges) {
|
|
||||||
for (const [start, end] of font.parameters.excludedCodePointRanges) {
|
|
||||||
for (let p = start; p <= end; p++) excludedCodePoints.add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const o_glyf = {};
|
|
||||||
const o_cmap = {};
|
|
||||||
for (let g of font.glyf) {
|
|
||||||
o_glyf[g.name] = g;
|
|
||||||
|
|
||||||
if (!g.unicode) continue;
|
|
||||||
for (let u of g.unicode) {
|
|
||||||
if (!excludedCodePoints.has(u - 0)) o_cmap[u] = g.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare OTD
|
|
||||||
const otd = Object.assign({}, font);
|
|
||||||
otd.glyf = o_glyf;
|
|
||||||
otd.cmap = o_cmap;
|
|
||||||
otd.glyfMap = null;
|
|
||||||
if (argv.o === "|") {
|
|
||||||
process.stdout.write(JSON.stringify(otd));
|
|
||||||
} else {
|
|
||||||
await fs.writeFile(argv.o, JSON.stringify(otd, null, " "));
|
|
||||||
}
|
}
|
||||||
|
await fs.writeFile(argv.charmap, JSON.stringify(charMap), "utf8");
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ glyph-block Symbol-Mosaic-NotDef : begin
|
||||||
sketch # space
|
sketch # space
|
||||||
local df : DivFrame para.diversityI
|
local df : DivFrame para.diversityI
|
||||||
set-width df.width
|
set-width df.width
|
||||||
include df.markSet.e
|
|
||||||
save 'space' ' '
|
save 'space' ' '
|
||||||
save 'threePerEmsp' 0x2004
|
save 'threePerEmsp' 0x2004
|
||||||
save 'fourPerEmsp' 0x2005
|
save 'fourPerEmsp' 0x2005
|
||||||
|
@ -30,7 +29,6 @@ glyph-block Symbol-Mosaic-NotDef : begin
|
||||||
sketch # en-space
|
sketch # en-space
|
||||||
local df : DivFrame 1
|
local df : DivFrame 1
|
||||||
set-width df.width
|
set-width df.width
|
||||||
include df.markSet.e
|
|
||||||
save 'figureSpace' 0x2007
|
save 'figureSpace' 0x2007
|
||||||
save 'enquad' 0x2000
|
save 'enquad' 0x2000
|
||||||
save 'ensp' 0x2002
|
save 'ensp' 0x2002
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
define-operator "~>" 880 'right' : syntax-rules
|
|
||||||
`(@l ~> @r) `{.left @l .right @r}
|
|
||||||
|
|
||||||
define [flatten] : begin
|
|
||||||
local ans {}
|
|
||||||
foreach [term : items-of : {}.slice.call arguments 0] : begin
|
|
||||||
if (term <@ Array)
|
|
||||||
: then : set ans : ans.concat term
|
|
||||||
: else : ans.push term
|
|
||||||
return ans
|
|
||||||
|
|
||||||
export all : define [buildOPBD chain-rule lookupOrder commonList features lookups] : begin
|
|
||||||
local fwclose {'fwlcloseDoubleQuote' 'fwlcloseSingleQuote' 'dwlcjkSingleQuoteRight' 'dwlcjkDoubleQuoteRight' 'dwlparenRight'}
|
|
||||||
local hwclose {'closeDoubleQuote' 'closeSingleQuote' 'cjkSingleQuoteRight' 'cjkDoubleQuoteRight' 'opbdParenRight'}
|
|
||||||
local fwopen {'fwropenDoubleQuote' 'fwropenSingleQuote' 'dwrcjkSingleQuoteLeft' 'dwrcjkDoubleQuoteLeft' 'dwrparenLeft'}
|
|
||||||
local hwopen {'openDoubleQuote' 'openSingleQuote' 'cjkSingleQuoteLeft' 'cjkDoubleQuoteLeft' 'opbdParenLeft'}
|
|
||||||
local fwquoteopen {'fwropenDoubleQuote' 'fwropenSingleQuote'}
|
|
||||||
local hwquoteopen {'openDoubleQuote' 'openSingleQuote'}
|
|
||||||
local fwtrail {'dwlperiod' 'dwlcomma' 'dwlcjkperiod' 'dwlcjkcomma'}
|
|
||||||
local hwtrail {'period' 'comma' 'cjkperiod' 'cjkcomma'}
|
|
||||||
local fwmid {'dwccolon' 'dwcsemicolon'}
|
|
||||||
local hwmid {'colon' 'semicolon'}
|
|
||||||
|
|
||||||
commonList.push 'opbd'
|
|
||||||
set features.opbd {'opbd1'}
|
|
||||||
set lookups.opbd1
|
|
||||||
.type 'gsub_chaining'
|
|
||||||
.subtables : list
|
|
||||||
chain-rule [flatten fwtrail hwtrail fwopen hwopen fwmid hwmid] (fwopen ~> hwopen)
|
|
||||||
chain-rule (fwclose ~> hwclose) [flatten fwtrail hwtrail fwclose hwclose fwopen hwopen fwmid hwmid]
|
|
|
@ -1,246 +0,0 @@
|
||||||
import 'topsort' as topsort
|
|
||||||
import 'object-assign' as objectAssign
|
|
||||||
import '../support/glyph' as Glyph
|
|
||||||
import '../support/transform' as Transform
|
|
||||||
import [buildLigations] from './feature/ligation'
|
|
||||||
import './feature/opbd' as buildOPBD
|
|
||||||
import './feature/ccmp' as buildCCMP
|
|
||||||
import [BuildCompatLigatures] from './feature/compat-ligature'
|
|
||||||
|
|
||||||
define GDEF_SIMPLE 1
|
|
||||||
define GDEF_LIGATURE 2
|
|
||||||
define GDEF_MARK 3
|
|
||||||
|
|
||||||
define-operator "~>" 880 'right' : syntax-rules
|
|
||||||
`(@l ~> @r) `{.left @l .right @r}
|
|
||||||
|
|
||||||
# GSUB
|
|
||||||
define [buildGSUB para glyphs glyphList markGlyphs] : begin
|
|
||||||
local commonList {}
|
|
||||||
local languages
|
|
||||||
.DFLT_DFLT {.features commonList}
|
|
||||||
.latn_DFLT {.features commonList}
|
|
||||||
.grek_DFLT {.features commonList}
|
|
||||||
.cyrl_DFLT {.features commonList}
|
|
||||||
.kana_DFLT {.features commonList}
|
|
||||||
.hani_DFLT {.features commonList}
|
|
||||||
local features {.}
|
|
||||||
local lookups {.}
|
|
||||||
local lookupOrder {}
|
|
||||||
|
|
||||||
# Chaining lookup builder
|
|
||||||
define [lookup-single name f t] : begin
|
|
||||||
local subtable {.}
|
|
||||||
foreach [j : range 0 f.length] : set subtable.(f.(j)) t.(j)
|
|
||||||
set lookups.(name) {.type 'gsub_single' .subtables {subtable}}
|
|
||||||
define [getsublookup left right] : piecewise
|
|
||||||
[not right] null
|
|
||||||
([typeof right] === "string") right
|
|
||||||
(right <@ Function) : getsublookup left [right left]
|
|
||||||
true : begin
|
|
||||||
local found null
|
|
||||||
foreach [{name lookup} : pairs-of lookups] : match lookup
|
|
||||||
{.type "gsub_single" .subtables {st}} : begin
|
|
||||||
local check true
|
|
||||||
foreach [j : range 0 left.length] : if (st.(left.(j)) !== right.(j)) : set check false
|
|
||||||
if check : set found name
|
|
||||||
if found : return found
|
|
||||||
|
|
||||||
local name "_lookup_\([Object.keys lookups].length)"
|
|
||||||
lookup-single name left right
|
|
||||||
return name
|
|
||||||
define [chain-rule] : begin
|
|
||||||
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x (x ~> null)])
|
|
||||||
local subtable {.match {} .apply {} .inputBegins 0 .inputEnds 0}
|
|
||||||
local foundi false
|
|
||||||
local founde false
|
|
||||||
foreach [j : range 0 terms.length] : if (!foundi && terms.(j).right) : begin
|
|
||||||
set subtable.inputBegins j
|
|
||||||
set foundi true
|
|
||||||
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
|
|
||||||
set subtable.inputEnds (j + 1)
|
|
||||||
set founde true
|
|
||||||
foreach [j : range 0 terms.length] : begin
|
|
||||||
local term terms.(j)
|
|
||||||
subtable.match.push term.left
|
|
||||||
local lutn : getsublookup term.left term.right
|
|
||||||
if lutn : subtable.apply.push {.at j .lookup lutn}
|
|
||||||
return subtable
|
|
||||||
|
|
||||||
# hwid-fwid, lnum-onum
|
|
||||||
define [MakePairFrature tag1 tag2] : begin
|
|
||||||
local mapTag2 {.}
|
|
||||||
local mapTag1 {.}
|
|
||||||
define reHidden : regex "^\\."
|
|
||||||
define reTag1 : new RegExp ("\\." + tag1 + "$")
|
|
||||||
foreach [glyph : items-of glyphList] : begin
|
|
||||||
if ([reTag1.test glyph.name] && ![reHidden.test glyph.name]) : do
|
|
||||||
local gnTag2 : glyph.name.replace reTag1 ('.' + tag2)
|
|
||||||
if (glyphs.(gnTag2)) : begin
|
|
||||||
set mapTag2.(glyph.name) gnTag2
|
|
||||||
set mapTag1.(gnTag2) glyph.name
|
|
||||||
set lookups.(tag2) {.type 'gsub_single' .subtables {mapTag2}}
|
|
||||||
set lookups.(tag1) {.type 'gsub_single' .subtables {mapTag1}}
|
|
||||||
set features.(tag2) {tag2}
|
|
||||||
set features.(tag1) {tag1}
|
|
||||||
commonList.push tag2 tag1
|
|
||||||
|
|
||||||
MakePairFrature 'lnum' 'onum'
|
|
||||||
if (!para.forceMonospace || para.spacing > 0) : MakePairFrature 'hwid' 'fwid'
|
|
||||||
|
|
||||||
# CCMP
|
|
||||||
buildCCMP chain-rule markGlyphs commonList features lookups
|
|
||||||
|
|
||||||
# Ligation
|
|
||||||
if para.enableLigation : do
|
|
||||||
define plm : objectAssign {.} para.defaultBuildup
|
|
||||||
if (para.ligation.caltBuildup && para.ligation.caltBuildup.length) : begin
|
|
||||||
set plm.calt para.ligation.caltBuildup
|
|
||||||
buildLigations chain-rule lookupOrder commonList features lookups plm glyphs
|
|
||||||
|
|
||||||
# CVxx/SSxx
|
|
||||||
if para.enableCvSs : begin
|
|
||||||
# cvxx
|
|
||||||
foreach [glyph : items-of glyphList] : if glyph.featureSelector : begin
|
|
||||||
local fs glyph.featureSelector
|
|
||||||
foreach [feature : items-of : Object.keys fs] : begin
|
|
||||||
if [not lookups.(feature)] : begin
|
|
||||||
set features.(feature) {feature}
|
|
||||||
set lookups.(feature) {.type 'gsub_single' .subtables{{.}}}
|
|
||||||
commonList.push feature
|
|
||||||
set lookups.(feature).subtables.0.(glyph.name) fs.(feature)
|
|
||||||
# ssxx
|
|
||||||
foreach [{name composition} : pairs-of para.variants] : begin
|
|
||||||
if (name.length === 4 && composition.__isComposite && [name.slice 0 2] === 'ss') : begin
|
|
||||||
commonList.push name
|
|
||||||
local tags {.}
|
|
||||||
foreach [{ch tag} : pairs-of composition.__cvmap] : set tags.(tag) true
|
|
||||||
set features.(name) : [Object.keys tags].filter (tag => tags.(tag))
|
|
||||||
|
|
||||||
# locl, SRB
|
|
||||||
local srbSubtable null
|
|
||||||
if para.isItalic
|
|
||||||
: then : set srbSubtable : object
|
|
||||||
cyrbe 'cyrbe.SRB'
|
|
||||||
cyrghe 'cyrghe.SRB'
|
|
||||||
cyrde 'cyrde.SRB'
|
|
||||||
cyrpe 'cyrpe.SRB'
|
|
||||||
cyrte 'cyrte.SRB'
|
|
||||||
: else : set srbSubtable : object
|
|
||||||
cyrbe 'cyrbe.SRB'
|
|
||||||
set lookups.locl_srb {.type 'gsub_single' .subtables {srbSubtable}}
|
|
||||||
set features.locl_srb {'locl_srb'}
|
|
||||||
|
|
||||||
# locl, BGR
|
|
||||||
local bgrSubtable : object
|
|
||||||
cyrve 'cyrve.BGR'
|
|
||||||
cyrghe 'cyrghe.italic'
|
|
||||||
cyrde 'g'
|
|
||||||
cyrzhe 'cyrzhe.BGR'
|
|
||||||
cyrze 'cyrze.BGR'
|
|
||||||
cyri 'u'
|
|
||||||
cyribreve 'ubreve'
|
|
||||||
cyrka 'k'
|
|
||||||
cyrEl 'Lambda'
|
|
||||||
cyrel 'turnv'
|
|
||||||
cyrpe 'n'
|
|
||||||
cyrte 'm'
|
|
||||||
cyrsha 'cyrsha.italic'
|
|
||||||
cyrshcha 'cyrshcha.italic'
|
|
||||||
cyryu 'cyryu.BGR'
|
|
||||||
cyrtse 'cyrtse.italic'
|
|
||||||
set lookups.locl_bgr {.type 'gsub_single' .subtables {bgrSubtable}}
|
|
||||||
set features.locl_bgr {'locl_bgr'}
|
|
||||||
|
|
||||||
set languages.'cyrl_SRB ' {.features [{'locl_srb'}.concat commonList]}
|
|
||||||
set languages.'cyrl_MKD ' {.features [{'locl_srb'}.concat commonList]}
|
|
||||||
set languages.'cyrl_BGR ' {.features [{'locl_bgr'}.concat commonList]}
|
|
||||||
|
|
||||||
return {.languages languages .features features .lookups lookups .lookupOrder [topsort lookupOrder]}
|
|
||||||
|
|
||||||
# GPOS
|
|
||||||
define [buildGPOS para glyphs glyphList markGlyphs] : begin
|
|
||||||
# mark and mkmk
|
|
||||||
define [createBaseInfo g th px py] : begin
|
|
||||||
local res {.}
|
|
||||||
local pushed false
|
|
||||||
foreach key [items-of : Object.keys g.anchors] : if (!th || th.(key)) : begin
|
|
||||||
set res.(key) : object
|
|
||||||
.x g.anchors.(key).(px || 'x')
|
|
||||||
.y g.anchors.(key).(py || 'y')
|
|
||||||
set pushed true
|
|
||||||
return : if pushed res nothing
|
|
||||||
|
|
||||||
define [createMTLookup lookupType anchorClasses] : begin
|
|
||||||
local subtable {.marks {.} .bases {.}}
|
|
||||||
local th {.}
|
|
||||||
foreach [ac : items-of anchorClasses] : set th.(ac) true
|
|
||||||
foreach glyph [items-of glyphList] : if glyph.anchors : begin
|
|
||||||
local anchorKeys : Object.keys glyph.anchors
|
|
||||||
local hasAnchor false
|
|
||||||
foreach [key : items-of anchorKeys] : if th.(key) : set hasAnchor true
|
|
||||||
if hasAnchor : begin
|
|
||||||
local isMarkGlyph false
|
|
||||||
local markKey nothing
|
|
||||||
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
|
|
||||||
set isMarkGlyph true
|
|
||||||
set markKey key
|
|
||||||
if isMarkGlyph
|
|
||||||
: then : begin
|
|
||||||
set subtable.marks.(glyph.name) : object
|
|
||||||
class markKey
|
|
||||||
x glyph.anchors.(markKey).x
|
|
||||||
y glyph.anchors.(markKey).y
|
|
||||||
if (lookupType == 'gpos_mark_to_mark'): begin
|
|
||||||
local r : createBaseInfo glyph th 'mbx' 'mby'
|
|
||||||
if r : set subtable.bases.(glyph.name) r
|
|
||||||
: else : if (lookupType == 'gpos_mark_to_base') : begin
|
|
||||||
local r : createBaseInfo glyph th 'x' 'y'
|
|
||||||
if r : set subtable.bases.(glyph.name) r
|
|
||||||
return {.type lookupType .subtables {subtable}}
|
|
||||||
|
|
||||||
local lookupSet {.}
|
|
||||||
local markLookupNames {}
|
|
||||||
local mkmkLookupNames {}
|
|
||||||
|
|
||||||
foreach [marktag : items-of {'above' 'below' 'overlay' 'slash' 'topright' 'bottomright' 'trailing' 'lf'}] : begin
|
|
||||||
set lookupSet.('lookup_mark_' + marktag) : createMTLookup 'gpos_mark_to_base' {marktag}
|
|
||||||
set lookupSet.('lookup_mkmk_' + marktag) : createMTLookup 'gpos_mark_to_mark' {marktag}
|
|
||||||
markLookupNames.push ('lookup_mark_' + marktag)
|
|
||||||
mkmkLookupNames.push ('lookup_mkmk_' + marktag)
|
|
||||||
|
|
||||||
return : object
|
|
||||||
languages
|
|
||||||
.DFLT_DFLT {.features {'mark0', 'mkmk0'}}
|
|
||||||
.latn_DFLT {.features {'mark0', 'mkmk0'}}
|
|
||||||
.grek_DFLT {.features {'mark0', 'mkmk0'}}
|
|
||||||
.cyrl_DFLT {.features {'mark0', 'mkmk0'}}
|
|
||||||
features
|
|
||||||
.mark0 markLookupNames
|
|
||||||
.mkmk0 mkmkLookupNames
|
|
||||||
lookups lookupSet
|
|
||||||
|
|
||||||
# GDEF
|
|
||||||
define [buildGDEF para glyphs glyphList markGlyphs] : begin
|
|
||||||
local GDEF {.glyphClassDef {.}}
|
|
||||||
foreach glyph [items-of glyphList] : begin
|
|
||||||
set GDEF.glyphClassDef.(glyph.name) : if [[regex '_'].test glyph.name] GDEF_LIGATURE GDEF_SIMPLE
|
|
||||||
if (glyph.anchors && [begin [local anchorKeys : Object.keys glyph.anchors] anchorKeys.length]) : begin
|
|
||||||
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
|
|
||||||
if [not markGlyphs.(key)] : set markGlyphs.(key) {}
|
|
||||||
markGlyphs.(key).push glyph.name
|
|
||||||
markGlyphs.all.push glyph.name
|
|
||||||
set GDEF.glyphClassDef.(glyph.name) GDEF_MARK
|
|
||||||
return GDEF
|
|
||||||
|
|
||||||
export : define [apply para glyphs glyphList unicodeGlyphs] : begin
|
|
||||||
local markGlyphs {.all {} }
|
|
||||||
local GPOS : buildGPOS para glyphs glyphList markGlyphs
|
|
||||||
local GDEF : buildGDEF para glyphs glyphList markGlyphs
|
|
||||||
local GSUB : buildGSUB para glyphs glyphList markGlyphs
|
|
||||||
|
|
||||||
# Build compatibility ligatures
|
|
||||||
if (para.spacing > 0 && para.compLig) : begin
|
|
||||||
BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF para.compLig
|
|
||||||
|
|
||||||
return [object GSUB GPOS GDEF]
|
|
|
@ -1,5 +1,5 @@
|
||||||
import '../../support/glyph' as Glyph
|
import '../support/glyph' as Glyph
|
||||||
import '../../support/transform' as Transform
|
import '../support/transform' as Transform
|
||||||
|
|
||||||
define GDEF_SIMPLE 1
|
define GDEF_SIMPLE 1
|
||||||
define GDEF_LIGATURE 2
|
define GDEF_LIGATURE 2
|
54
otl/gpos-mark-mkmk.ptl
Normal file
54
otl/gpos-mark-mkmk.ptl
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import [add-common-feature add-feature add-lookup] from "./table-util"
|
||||||
|
|
||||||
|
define [createBaseInfo g th px py] : begin
|
||||||
|
local res {.}
|
||||||
|
local pushed false
|
||||||
|
foreach key [items-of : Object.keys g.anchors] : if (!th || th.(key)) : begin
|
||||||
|
set res.(key) : object
|
||||||
|
.x g.anchors.(key).(px || 'x')
|
||||||
|
.y g.anchors.(key).(py || 'y')
|
||||||
|
set pushed true
|
||||||
|
return : if pushed res nothing
|
||||||
|
|
||||||
|
define [createMTLookup glyphList lookupType anchorClasses] : begin
|
||||||
|
local subtable {.marks {.} .bases {.}}
|
||||||
|
local th {.}
|
||||||
|
foreach [ac : items-of anchorClasses] : set th.(ac) true
|
||||||
|
foreach glyph [items-of glyphList] : if glyph.anchors : begin
|
||||||
|
local anchorKeys : Object.keys glyph.anchors
|
||||||
|
local hasAnchor false
|
||||||
|
foreach [key : items-of anchorKeys] : if th.(key) : set hasAnchor true
|
||||||
|
if hasAnchor : begin
|
||||||
|
local isMarkGlyph false
|
||||||
|
local markKey nothing
|
||||||
|
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
|
||||||
|
set isMarkGlyph true
|
||||||
|
set markKey key
|
||||||
|
if isMarkGlyph
|
||||||
|
: then : begin
|
||||||
|
set subtable.marks.(glyph.name) : object
|
||||||
|
class markKey
|
||||||
|
x glyph.anchors.(markKey).x
|
||||||
|
y glyph.anchors.(markKey).y
|
||||||
|
if (lookupType == 'gpos_mark_to_mark'): begin
|
||||||
|
local r : createBaseInfo glyph th 'mbx' 'mby'
|
||||||
|
if r : set subtable.bases.(glyph.name) r
|
||||||
|
: else : if (lookupType == 'gpos_mark_to_base') : begin
|
||||||
|
local r : createBaseInfo glyph th 'x' 'y'
|
||||||
|
if r : set subtable.bases.(glyph.name) r
|
||||||
|
return {.type lookupType .subtables {subtable}}
|
||||||
|
|
||||||
|
export : define [buildMarkMkmk sink glyphList] : begin
|
||||||
|
define mark : add-feature sink 'mark'
|
||||||
|
define mkmk : add-feature sink 'mkmk'
|
||||||
|
add-common-feature sink mark
|
||||||
|
add-common-feature sink mkmk
|
||||||
|
|
||||||
|
local markLookupNames {}
|
||||||
|
local mkmkLookupNames {}
|
||||||
|
|
||||||
|
foreach [marktag : items-of {'above' 'below' 'overlay' 'slash' 'topright' 'bottomright' 'trailing' 'lf'}] : begin
|
||||||
|
define lookupNameMark : 'lookup_mark_' + marktag
|
||||||
|
define lookupNameMkmk : 'lookup_mkmk_' + marktag
|
||||||
|
mark.lookups.push : add-lookup sink : createMTLookup glyphList 'gpos_mark_to_base' {marktag}
|
||||||
|
mkmk.lookups.push : add-lookup sink : createMTLookup glyphList 'gpos_mark_to_mark' {marktag}
|
|
@ -1,11 +1,14 @@
|
||||||
|
import [add-common-feature add-feature add-lookup ChainRuleBuilder] from "./table-util"
|
||||||
|
|
||||||
define-operator "~>" 880 'right' : syntax-rules
|
define-operator "~>" 880 'right' : syntax-rules
|
||||||
`(@l ~> @r) `{.left @l .right @r}
|
`(@l ~> @r) `{.left @l .right @r}
|
||||||
|
|
||||||
export all : define [buildCCMP chain-rule markGlyphs commonList features lookups] : begin
|
export : define [buildCCMP sink markGlyphs] : begin
|
||||||
commonList.push 'ccmp'
|
define ccmp : add-feature sink 'ccmp'
|
||||||
set features.ccmp {'ccmp1' 'ccmp2'}
|
define chain-rule : ChainRuleBuilder sink
|
||||||
|
|
||||||
let [groupA {'A' 'a' 'u' 'cyrA' 'cyra'}] : set lookups.ccmp1
|
define groupA {'A' 'a' 'u' 'cyrA' 'cyra'}
|
||||||
|
define lookupCcmp1 : add-lookup sink : object
|
||||||
.type 'gsub_chaining'
|
.type 'gsub_chaining'
|
||||||
.subtables : list
|
.subtables : list
|
||||||
chain-rule
|
chain-rule
|
||||||
|
@ -20,7 +23,7 @@ export all : define [buildCCMP chain-rule markGlyphs commonList features lookups
|
||||||
chain-rule groupA markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
|
chain-rule groupA markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
|
||||||
chain-rule groupA markGlyphs.all markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
|
chain-rule groupA markGlyphs.all markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
|
||||||
|
|
||||||
set lookups.ccmp2
|
define lookupCcmp2 : add-lookup sink : object
|
||||||
.type 'gsub_ligature'
|
.type 'gsub_ligature'
|
||||||
.subtables : list : object
|
.subtables : list : object
|
||||||
psilivaria {'commaAbove' 'graveAbove'}
|
psilivaria {'commaAbove' 'graveAbove'}
|
||||||
|
@ -29,3 +32,6 @@ export all : define [buildCCMP chain-rule markGlyphs commonList features lookups
|
||||||
dasiavaria {'revCommaAbove' 'graveAbove'}
|
dasiavaria {'revCommaAbove' 'graveAbove'}
|
||||||
dasiaoxia {'revCommaAbove' 'acuteAbove'}
|
dasiaoxia {'revCommaAbove' 'acuteAbove'}
|
||||||
dasiaperispomeni {'revCommaAbove' 'perispomeniAbove'}
|
dasiaperispomeni {'revCommaAbove' 'perispomeniAbove'}
|
||||||
|
|
||||||
|
ccmp.lookups.push lookupCcmp1 lookupCcmp2
|
||||||
|
add-common-feature sink ccmp
|
28
otl/gsub-cv-ss.ptl
Normal file
28
otl/gsub-cv-ss.ptl
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import [add-common-feature pick-feature add-feature-lookup pick-lookup] from "./table-util"
|
||||||
|
|
||||||
|
define [FeatureName tag] : tag + '_cvss'
|
||||||
|
define [LookupName tag] : 'lookup_cvss_' + tag
|
||||||
|
|
||||||
|
export : define [buildCVSS sink para glyphList] : begin
|
||||||
|
if [not para.enableCvSs] : return nothing
|
||||||
|
|
||||||
|
# cvxx
|
||||||
|
foreach [glyph : items-of glyphList] : if glyph.featureSelector : begin
|
||||||
|
local fs glyph.featureSelector
|
||||||
|
foreach [{tag to} : pairs-of fs] : begin
|
||||||
|
local feature : pick-feature sink [FeatureName tag]
|
||||||
|
add-common-feature sink feature
|
||||||
|
|
||||||
|
local lookup : pick-lookup sink [LookupName tag] {.type 'gsub_single' .subtables{{.}}}
|
||||||
|
add-feature-lookup feature [LookupName tag]
|
||||||
|
|
||||||
|
set lookup.subtables.0.(glyph.name) to
|
||||||
|
|
||||||
|
# ssxx
|
||||||
|
foreach [{name composition} : pairs-of para.variants] : begin
|
||||||
|
if (name.length === 4 && composition.__isComposite && [name.slice 0 2] === 'ss') : begin
|
||||||
|
local feature : pick-feature sink [FeatureName name]
|
||||||
|
add-common-feature sink feature
|
||||||
|
|
||||||
|
foreach [{ch tag} : pairs-of composition.__cvmap]
|
||||||
|
add-feature-lookup feature [LookupName tag]
|
|
@ -1,3 +1,5 @@
|
||||||
|
import [add-common-feature add-feature ChainRuleBuilder] from "./table-util"
|
||||||
|
|
||||||
define-operator "~>" 880 'right' : syntax-rules
|
define-operator "~>" 880 'right' : syntax-rules
|
||||||
`(@l ~> @r) `{.left @l .right @r}
|
`(@l ~> @r) `{.left @l .right @r}
|
||||||
|
|
||||||
|
@ -7,7 +9,9 @@ define [just s] : lambda [t] : t.map : lambda [x] s
|
||||||
define preserved null
|
define preserved null
|
||||||
define advance : lambda [t] : t.map : lambda [x] x
|
define advance : lambda [t] : t.map : lambda [x] x
|
||||||
|
|
||||||
export : define [buildLigations chain-rule lookupOrder commonList features lookups plm glyphs] : foreach [ ligationFeatureName : items-of : Object.keys plm] : do
|
export : define [buildLigations sink plm glyphs] : foreach [ {featureName mappedFeature} : pairs-of plm] : do
|
||||||
|
|
||||||
|
define chain-rule : ChainRuleBuilder sink
|
||||||
|
|
||||||
define arrowStick {'hyphen' 'equal'}
|
define arrowStick {'hyphen' 'equal'}
|
||||||
define [stick style] : {'hyphen' 'equal'} ~> [lsx style]
|
define [stick style] : {'hyphen' 'equal'} ~> [lsx style]
|
||||||
|
@ -59,8 +63,7 @@ export : define [buildLigations chain-rule lookupOrder commonList features looku
|
||||||
g.push v
|
g.push v
|
||||||
* g
|
* g
|
||||||
|
|
||||||
local mappedFeature : plm.(ligationFeatureName) || {}
|
local ligationLookupName : 'lig_' + featureName + '-' + mappedFeature
|
||||||
local ligationLookupName : 'lig_' + ligationFeatureName + '-' + mappedFeature
|
|
||||||
|
|
||||||
define [hasLG ln] : [mappedFeature.indexOf ln] >= 0
|
define [hasLG ln] : [mappedFeature.indexOf ln] >= 0
|
||||||
|
|
||||||
|
@ -76,26 +79,23 @@ export : define [buildLigations chain-rule lookupOrder commonList features looku
|
||||||
(f <@ Function) : f left
|
(f <@ Function) : f left
|
||||||
true : return f
|
true : return f
|
||||||
|
|
||||||
commonList.push ligationFeatureName
|
define feature : add-common-feature sink : add-feature sink featureName
|
||||||
|
|
||||||
local featLookups {}
|
|
||||||
local lastLookupName null
|
local lastLookupName null
|
||||||
|
|
||||||
local [dedup ln0 obj] : begin
|
local [dedup ln0 obj] : begin
|
||||||
local h : JSON.stringify obj
|
local h : JSON.stringify obj
|
||||||
foreach [{name lookup} : pairs-of lookups] : begin
|
foreach [{name lookup} : pairs-of sink.lookups] : begin
|
||||||
local h1 : JSON.stringify lookup
|
local h1 : JSON.stringify lookup
|
||||||
if (h == h1) : return name
|
if (h == h1) : return name
|
||||||
return ln0
|
return ln0
|
||||||
|
|
||||||
local [includeLookup obj] : begin
|
local [includeLookup obj] : begin
|
||||||
local ln : dedup (ligationLookupName + featLookups.length) obj
|
local ln : dedup (ligationLookupName + feature.lookups.length) obj
|
||||||
if [not lookups.(ln)] : set lookups.(ln) obj
|
if [not sink.lookups.(ln)] : set sink.lookups.(ln) obj
|
||||||
featLookups.push ln
|
feature.lookups.push ln
|
||||||
if lastLookupName : lookupOrder.push {lastLookupName ln}
|
if lastLookupName : sink.lookupDep.push {lastLookupName ln}
|
||||||
set lastLookupName ln
|
set lastLookupName ln
|
||||||
|
|
||||||
set features.(ligationFeatureName) featLookups
|
|
||||||
|
|
||||||
do "Operator centering" : if [hasLG "center-ops"] : begin
|
do "Operator centering" : if [hasLG "center-ops"] : begin
|
||||||
define centerizeGroups : { asterisk_center caret_center tilde_center colon_center [if [hasLG 'dotoper'] period_center nothing]}.filter (x => x)
|
define centerizeGroups : { asterisk_center caret_center tilde_center colon_center [if [hasLG 'dotoper'] period_center nothing]}.filter (x => x)
|
45
otl/gsub-locl.ptl
Normal file
45
otl/gsub-locl.ptl
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import [copy-language add-feature add-lookup] from "./table-util"
|
||||||
|
|
||||||
|
export : define [buildLOCL sink para] : begin
|
||||||
|
define cyrlSRB : copy-language sink 'cyrl_SRB ' 'cyrl_DFLT'
|
||||||
|
define cyrlMKD : copy-language sink 'cyrl_MKD ' 'cyrl_DFLT'
|
||||||
|
define cyrlBGR : copy-language sink 'cyrl_BGR ' 'cyrl_DFLT'
|
||||||
|
|
||||||
|
# SRB
|
||||||
|
define loclSRB : add-feature sink 'locl'
|
||||||
|
cyrlSRB.features.unshift loclSRB.name
|
||||||
|
cyrlMKD.features.unshift loclSRB.name
|
||||||
|
loclSRB.lookups.push : add-lookup sink : object
|
||||||
|
type 'gsub_single'
|
||||||
|
subtables : list : if para.isItalic
|
||||||
|
object
|
||||||
|
cyrbe 'cyrbe.SRB'
|
||||||
|
cyrghe 'cyrghe.SRB'
|
||||||
|
cyrde 'cyrde.SRB'
|
||||||
|
cyrpe 'cyrpe.SRB'
|
||||||
|
cyrte 'cyrte.SRB'
|
||||||
|
object
|
||||||
|
cyrbe 'cyrbe.SRB'
|
||||||
|
|
||||||
|
# BGR
|
||||||
|
define loclBGR : add-feature sink 'locl'
|
||||||
|
cyrlBGR.features.unshift loclBGR.name
|
||||||
|
loclBGR.lookups.push : add-lookup sink : object
|
||||||
|
type 'gsub_single'
|
||||||
|
subtables : list : object
|
||||||
|
cyrve 'cyrve.BGR'
|
||||||
|
cyrghe 'cyrghe.italic'
|
||||||
|
cyrde 'g'
|
||||||
|
cyrzhe 'cyrzhe.BGR'
|
||||||
|
cyrze 'cyrze.BGR'
|
||||||
|
cyri 'u'
|
||||||
|
cyribreve 'ubreve'
|
||||||
|
cyrka 'k'
|
||||||
|
cyrEl 'Lambda'
|
||||||
|
cyrel 'turnv'
|
||||||
|
cyrpe 'n'
|
||||||
|
cyrte 'm'
|
||||||
|
cyrsha 'cyrsha.italic'
|
||||||
|
cyrshcha 'cyrshcha.italic'
|
||||||
|
cyryu 'cyryu.BGR'
|
||||||
|
cyrtse 'cyrtse.italic'
|
24
otl/gsub-pairing.ptl
Normal file
24
otl/gsub-pairing.ptl
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import [add-common-feature add-feature add-lookup] from "./table-util"
|
||||||
|
|
||||||
|
export : define [buildPairFeature sink tag1 tag2 glyphs glyphList] : begin
|
||||||
|
local mapTag2 {.}
|
||||||
|
local mapTag1 {.}
|
||||||
|
define reHidden : regex "^\\."
|
||||||
|
define reTag1 : new RegExp ("\\." + tag1 + "$")
|
||||||
|
foreach [glyph : items-of glyphList] : begin
|
||||||
|
if ([reTag1.test glyph.name] && ![reHidden.test glyph.name]) : do
|
||||||
|
local gnTag2 : glyph.name.replace reTag1 ('.' + tag2)
|
||||||
|
if (glyphs.(gnTag2)) : begin
|
||||||
|
set mapTag2.(glyph.name) gnTag2
|
||||||
|
set mapTag1.(gnTag2) glyph.name
|
||||||
|
|
||||||
|
define lookup1 : add-lookup sink {.type 'gsub_single' .subtables {mapTag1}}
|
||||||
|
define lookup2 : add-lookup sink {.type 'gsub_single' .subtables {mapTag2}}
|
||||||
|
|
||||||
|
define feature1 : add-feature sink tag1
|
||||||
|
feature1.lookups.push lookup1
|
||||||
|
define feature2 : add-feature sink tag2
|
||||||
|
feature2.lookups.push lookup2
|
||||||
|
|
||||||
|
add-common-feature sink feature1
|
||||||
|
add-common-feature sink feature2
|
74
otl/index.ptl
Normal file
74
otl/index.ptl
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import 'topsort' as topsort
|
||||||
|
import 'object-assign' as objectAssign
|
||||||
|
import '../support/glyph' as Glyph
|
||||||
|
import '../support/transform' as Transform
|
||||||
|
|
||||||
|
import [CreateEmptyTable] from "./table-util"
|
||||||
|
|
||||||
|
import [buildLigations] from './gsub-ligation'
|
||||||
|
import [buildCCMP] from './gsub-ccmp'
|
||||||
|
import [buildPairFeature] from './gsub-pairing'
|
||||||
|
import [buildCVSS] from './gsub-cv-ss'
|
||||||
|
import [buildLOCL] from './gsub-locl'
|
||||||
|
import [buildMarkMkmk] from "./gpos-mark-mkmk"
|
||||||
|
import [BuildCompatLigatures] from './compat-ligature'
|
||||||
|
|
||||||
|
define GDEF_SIMPLE 1
|
||||||
|
define GDEF_LIGATURE 2
|
||||||
|
define GDEF_MARK 3
|
||||||
|
|
||||||
|
# GSUB
|
||||||
|
define [buildGSUB para glyphs glyphList markGlyphs] : begin
|
||||||
|
define gsub : CreateEmptyTable
|
||||||
|
set gsub.lookupDep {}
|
||||||
|
|
||||||
|
buildPairFeature gsub 'lnum' 'onum' glyphs glyphList
|
||||||
|
if (!para.forceMonospace || para.spacing > 0) : begin
|
||||||
|
buildPairFeature gsub 'hwid' 'fwid' glyphs glyphList
|
||||||
|
|
||||||
|
# CCMP
|
||||||
|
buildCCMP gsub markGlyphs
|
||||||
|
|
||||||
|
# Ligation
|
||||||
|
if para.enableLigation : do
|
||||||
|
define plm : objectAssign {.} para.defaultBuildup
|
||||||
|
if (para.ligation.caltBuildup && para.ligation.caltBuildup.length) : begin
|
||||||
|
set plm.calt para.ligation.caltBuildup
|
||||||
|
buildLigations gsub plm glyphs
|
||||||
|
|
||||||
|
buildCVSS gsub para glyphList
|
||||||
|
buildLOCL gsub para
|
||||||
|
|
||||||
|
set gsub.lookupOrder : topsort gsub.lookupDep
|
||||||
|
return gsub
|
||||||
|
|
||||||
|
# GPOS
|
||||||
|
define [buildGPOS para glyphs glyphList markGlyphs] : begin
|
||||||
|
define gpos : CreateEmptyTable
|
||||||
|
buildMarkMkmk gpos glyphList
|
||||||
|
return gpos
|
||||||
|
|
||||||
|
# GDEF
|
||||||
|
define [buildGDEF para glyphs glyphList markGlyphs] : begin
|
||||||
|
local GDEF {.glyphClassDef {.}}
|
||||||
|
foreach glyph [items-of glyphList] : begin
|
||||||
|
set GDEF.glyphClassDef.(glyph.name) : if [[regex '_'].test glyph.name] GDEF_LIGATURE GDEF_SIMPLE
|
||||||
|
if (glyph.anchors && [begin [local anchorKeys : Object.keys glyph.anchors] anchorKeys.length]) : begin
|
||||||
|
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
|
||||||
|
if [not markGlyphs.(key)] : set markGlyphs.(key) {}
|
||||||
|
markGlyphs.(key).push glyph.name
|
||||||
|
markGlyphs.all.push glyph.name
|
||||||
|
set GDEF.glyphClassDef.(glyph.name) GDEF_MARK
|
||||||
|
return GDEF
|
||||||
|
|
||||||
|
export : define [buildOtl para glyphs glyphList unicodeGlyphs] : begin
|
||||||
|
local markGlyphs {.all {} }
|
||||||
|
local GPOS : buildGPOS para glyphs glyphList markGlyphs
|
||||||
|
local GDEF : buildGDEF para glyphs glyphList markGlyphs
|
||||||
|
local GSUB : buildGSUB para glyphs glyphList markGlyphs
|
||||||
|
|
||||||
|
# Build compatibility ligatures
|
||||||
|
if (para.spacing > 0 && para.compLig) : begin
|
||||||
|
BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF para.compLig
|
||||||
|
|
||||||
|
return [object GSUB GPOS GDEF]
|
103
otl/table-util.ptl
Normal file
103
otl/table-util.ptl
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
export : define [CreateEmptyTable] {.languages {.} .features {.} .lookups {.}}
|
||||||
|
|
||||||
|
export : define [pick-language sink tag] : begin
|
||||||
|
if sink.languages.(tag) : return sink.languages.(tag)
|
||||||
|
define lang {.features {}}
|
||||||
|
set sink.languages.(tag) lang
|
||||||
|
return lang
|
||||||
|
|
||||||
|
export : define [copy-language sink tag tagFrom] : begin
|
||||||
|
define langFrom : pick-language sink tagFrom
|
||||||
|
define langTo : pick-language sink tag
|
||||||
|
foreach [feat : items-of langFrom.features] : langTo.features.push feat
|
||||||
|
return langTo
|
||||||
|
|
||||||
|
export : define [add-lang-feature lang fea] : begin
|
||||||
|
define index : lang.features.indexOf fea.name
|
||||||
|
if (index < 0) : lang.features.push fea.name
|
||||||
|
|
||||||
|
export : define [add-feature sink tag] : begin
|
||||||
|
define lookupArray {}
|
||||||
|
local n 0
|
||||||
|
while true : begin
|
||||||
|
if [not sink.features.(tag + '_' + n)] : begin
|
||||||
|
set sink.features.(tag + '_' + n) lookupArray
|
||||||
|
return {.name (tag + '_' + n) .lookups lookupArray}
|
||||||
|
set n : n + 1
|
||||||
|
|
||||||
|
export : define [pick-feature sink name] : begin
|
||||||
|
if sink.features.(name) : return { .name name .lookups sink.features.(name) }
|
||||||
|
define featObj { .name name .lookups {} }
|
||||||
|
set sink.features.(name) featObj.lookups
|
||||||
|
return featObj
|
||||||
|
|
||||||
|
export : define [add-feature-lookup fea lookupName] : begin
|
||||||
|
define index : fea.lookups.indexOf lookupName
|
||||||
|
if (index < 0) : fea.lookups.push lookupName
|
||||||
|
|
||||||
|
export : define [add-lookup sink data] : begin
|
||||||
|
local n 0
|
||||||
|
while true : begin
|
||||||
|
if [not sink.lookups.('_lut_' + n)] : begin
|
||||||
|
set sink.lookups.('_lut_' + n) data
|
||||||
|
return ('_lut_' + n)
|
||||||
|
set n : n + 1
|
||||||
|
|
||||||
|
export : define [pick-lookup sink name fallback] : begin
|
||||||
|
if sink.lookups.(name) : return sink.lookups.(name)
|
||||||
|
set sink.lookups.(name) fallback
|
||||||
|
return sink.lookups.(name)
|
||||||
|
|
||||||
|
export : define [add-common-feature sink fea] : begin
|
||||||
|
define dfltDflt : pick-language sink 'dflt_DFLT'
|
||||||
|
define latnDflt : pick-language sink 'latn_DFLT'
|
||||||
|
define grekDflt : pick-language sink 'grek_DFLT'
|
||||||
|
define cyrlDflt : pick-language sink 'cyrl_DFLT'
|
||||||
|
|
||||||
|
add-lang-feature dfltDflt fea
|
||||||
|
add-lang-feature latnDflt fea
|
||||||
|
add-lang-feature grekDflt fea
|
||||||
|
add-lang-feature cyrlDflt fea
|
||||||
|
|
||||||
|
return fea
|
||||||
|
|
||||||
|
export : define [ChainRuleBuilder sink] : begin
|
||||||
|
define [lookup-single f t] : begin
|
||||||
|
local subtable {.}
|
||||||
|
foreach [j : range 0 f.length] : set subtable.(f.(j)) t.(j)
|
||||||
|
return : add-lookup sink {.type 'gsub_single' .subtables {subtable}}
|
||||||
|
|
||||||
|
define [getsublookup left right] : piecewise
|
||||||
|
[not right] null
|
||||||
|
([typeof right] === "string") right
|
||||||
|
(right <@ Function) : getsublookup left [right left]
|
||||||
|
true : begin
|
||||||
|
local found null
|
||||||
|
foreach [{name lookup} : pairs-of sink.lookups] : match lookup
|
||||||
|
{.type "gsub_single" .subtables {st}} : begin
|
||||||
|
local check true
|
||||||
|
foreach [j : range 0 left.length] : if (st.(left.(j)) !== right.(j)) : set check false
|
||||||
|
if check : set found name
|
||||||
|
if found : return found
|
||||||
|
|
||||||
|
return : lookup-single left right
|
||||||
|
|
||||||
|
define [chain-rule] : begin
|
||||||
|
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x {.left x .right null}])
|
||||||
|
local subtable {.match {} .apply {} .inputBegins 0 .inputEnds 0}
|
||||||
|
local foundi false
|
||||||
|
local founde false
|
||||||
|
foreach [j : range 0 terms.length] : if (!foundi && terms.(j).right) : begin
|
||||||
|
set subtable.inputBegins j
|
||||||
|
set foundi true
|
||||||
|
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
|
||||||
|
set subtable.inputEnds (j + 1)
|
||||||
|
set founde true
|
||||||
|
foreach [j : range 0 terms.length] : begin
|
||||||
|
local term terms.(j)
|
||||||
|
subtable.match.push term.left
|
||||||
|
local lutn : getsublookup term.left term.right
|
||||||
|
if lutn : subtable.apply.push {.at j .lookup lutn}
|
||||||
|
return subtable
|
||||||
|
|
||||||
|
return chain-rule
|
|
@ -67,11 +67,11 @@ function unlinkRef(g, dx, dy, glyf) {
|
||||||
return contours;
|
return contours;
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoref(glyf, excludeUnicodes) {
|
function autoref(gs, excludeUnicodeSet) {
|
||||||
suppressNaN(glyf);
|
suppressNaN(gs);
|
||||||
|
|
||||||
for (let j = 0; j < glyf.length; j++) {
|
for (let j = 0; j < gs.length; j++) {
|
||||||
const g = glyf[j];
|
const g = gs[j];
|
||||||
if (g.contours) {
|
if (g.contours) {
|
||||||
for (let k = 0; k < g.contours.length; k++) {
|
for (let k = 0; k < g.contours.length; k++) {
|
||||||
const contour = g.contours[k];
|
const contour = g.contours[k];
|
||||||
|
@ -81,44 +81,44 @@ function autoref(glyf, excludeUnicodes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refl-referencify, forward.
|
// Refl-referencify, forward.
|
||||||
for (let j = 0; j < glyf.length; j++) {
|
for (let j = 0; j < gs.length; j++) {
|
||||||
if (!glyf[j].contours.length || (glyf[j].references && glyf[j].references.length)) continue;
|
if (!gs[j].contours.length || (gs[j].references && gs[j].references.length)) continue;
|
||||||
for (let k = j + 1; k < glyf.length; k++) {
|
for (let k = j + 1; k < gs.length; k++) {
|
||||||
if (glyf[j].contours.length === glyf[k].contours.length) {
|
if (gs[j].contours.length === gs[k].contours.length) {
|
||||||
match(glyf[j], glyf[k], j);
|
match(gs[j], gs[k], j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// referencify, backward
|
// referencify, backward
|
||||||
for (let j = 0; j < glyf.length; j++) {
|
for (let j = 0; j < gs.length; j++) {
|
||||||
if (glyf[j].cmpPriority < 0) continue;
|
if (gs[j].cmpPriority < 0) continue;
|
||||||
if (!glyf[j].contours.length) continue;
|
if (!gs[j].contours.length) continue;
|
||||||
if (glyf[j].references && glyf[j].references.length) continue;
|
if (gs[j].references && gs[j].references.length) continue;
|
||||||
for (let k = glyf.length - 1; k >= 0; k--) {
|
for (let k = gs.length - 1; k >= 0; k--) {
|
||||||
if (glyf[j].contours.length > glyf[k].contours.length) continue;
|
if (gs[j].contours.length > gs[k].contours.length) continue;
|
||||||
if (
|
if (
|
||||||
glyf[j].contours.length === glyf[k].contours.length &&
|
gs[j].contours.length === gs[k].contours.length &&
|
||||||
!(glyf[k].references && glyf[k].references.length)
|
!(gs[k].references && gs[k].references.length)
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while (match(glyf[j], glyf[k], j)) "pass";
|
while (match(gs[j], gs[k], j)) "pass";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlink composite
|
// unlink composite
|
||||||
for (let j = 0; j < glyf.length; j++) {
|
for (let j = 0; j < gs.length; j++) {
|
||||||
if (!glyf[j].references || glyf[j].references.length === 0) continue;
|
if (!gs[j].references || gs[j].references.length === 0) continue;
|
||||||
if (
|
if (
|
||||||
!glyf[j].flatten &&
|
!gs[j].flatten &&
|
||||||
glyf[j].contours.length === 0 &&
|
gs[j].contours.length === 0 &&
|
||||||
!(glyf[j].unicode && excludeUnicodes.has(glyf[j].unicode[0]))
|
!(gs[j].unicode && excludeUnicodeSet.has(gs[j].unicode[0]))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
glyf[j].contours = unlinkRef(glyf[j], 0, 0, glyf);
|
gs[j].contours = unlinkRef(gs[j], 0, 0, gs);
|
||||||
glyf[j].references = [];
|
gs[j].references = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,17 +75,18 @@ function byGlyphPriority(a, b) {
|
||||||
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(font, skew) {
|
module.exports = function(gs, skew) {
|
||||||
const excludeUnicode = new Set();
|
const excludeUnicode = new Set();
|
||||||
excludeUnicode.add(0x80);
|
excludeUnicode.add(0x80);
|
||||||
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
|
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
|
||||||
|
|
||||||
// autoref
|
// autoref
|
||||||
font.glyf = font.glyf.map((g, j) => ((g.glyphOrder = j), g)).sort(byGlyphPriority);
|
gs = gs.map((g, j) => ((g.glyphOrder = j), g)).sort(byGlyphPriority);
|
||||||
autoRef(font.glyf, excludeUnicode);
|
autoRef(gs, excludeUnicode);
|
||||||
|
|
||||||
// regulate
|
// regulate
|
||||||
for (let g of font.glyf) regulateGlyph(g, skew);
|
for (let g of gs) regulateGlyph(g, skew);
|
||||||
|
|
||||||
// reorder
|
// reorder
|
||||||
font.glyf = font.glyf.sort((a, b) => a.glyphOrder - b.glyphOrder);
|
return gs.sort((a, b) => a.glyphOrder - b.glyphOrder);
|
||||||
};
|
};
|
||||||
|
|
|
@ -682,8 +682,9 @@ const UtilScriptFiles = computed("util-script-files", async target => {
|
||||||
const ScriptFiles = computed.group("script-files", async (target, ext) => {
|
const ScriptFiles = computed.group("script-files", async (target, ext) => {
|
||||||
const [gen, meta, glyphs, support] = await target.need(
|
const [gen, meta, glyphs, support] = await target.need(
|
||||||
ScriptsUnder(ext, `gen`),
|
ScriptsUnder(ext, `gen`),
|
||||||
ScriptsUnder(ext, `meta`),
|
|
||||||
ScriptsUnder(ext, `glyphs`),
|
ScriptsUnder(ext, `glyphs`),
|
||||||
|
ScriptsUnder(ext, `meta`),
|
||||||
|
ScriptsUnder(ext, `otl`),
|
||||||
ScriptsUnder(ext, `support`)
|
ScriptsUnder(ext, `support`)
|
||||||
);
|
);
|
||||||
return [...gen, ...meta, ...glyphs, ...support];
|
return [...gen, ...meta, ...glyphs, ...support];
|
||||||
|
@ -693,7 +694,7 @@ const JavaScriptFromPtl = computed("scripts-js-from-ptl", async target => {
|
||||||
return ptl.map(x => x.replace(/\.ptl$/g, ".js"));
|
return ptl.map(x => x.replace(/\.ptl$/g, ".js"));
|
||||||
});
|
});
|
||||||
|
|
||||||
const ScriptJS = file.glob(`{gen|glyphs|support|meta}/**/*.js`, async (target, path) => {
|
const ScriptJS = file.glob(`{gen|glyphs|meta|otl|support}/**/*.js`, async (target, path) => {
|
||||||
const [jsFromPtl] = await target.need(JavaScriptFromPtl);
|
const [jsFromPtl] = await target.need(JavaScriptFromPtl);
|
||||||
if (jsFromPtl.indexOf(path.full) >= 0) {
|
if (jsFromPtl.indexOf(path.full) >= 0) {
|
||||||
const ptl = path.full.replace(/\.js$/g, ".ptl");
|
const ptl = path.full.replace(/\.js$/g, ".ptl");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue