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
|
||||
|
||||
# Generated scripts
|
||||
.buildglyphs.all.patel
|
||||
build-glyphs.js
|
||||
parameters.js
|
||||
support/glyph.js
|
||||
support/point.js
|
||||
support/anchor.js
|
||||
support/glyph.js
|
||||
support/parameters.js
|
||||
support/point.js
|
||||
support/transform.js
|
||||
support/simple-expand.js
|
||||
support/spiroexpand.js
|
||||
support/spirokit.js
|
||||
support/utils.js
|
||||
gen/build-glyphs.js
|
||||
meta/*.js
|
||||
meta/feature/*.js
|
||||
otl/*.js
|
||||
glyphs/*.js
|
||||
|
||||
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 [calculateMetrics setFontMetrics MarksetDiv GenDivFrame] from '../meta/aesthetics'
|
||||
import [assignFontNames] from '../meta/naming'
|
||||
import '../meta/features' as Features
|
||||
|
||||
$$include '../meta/macros.ptl'
|
||||
|
||||
|
@ -25,7 +23,7 @@ define [tagged tag component] : begin
|
|||
set component.tag tag
|
||||
return component
|
||||
|
||||
define [buildGlyphs para recursive recursiveCodes] : begin
|
||||
export all : define [buildGlyphs para recursive recursiveCodes] : begin
|
||||
define variantSelector para.variantSelector
|
||||
local glyphList {}
|
||||
local glyphs {.}
|
||||
|
@ -144,22 +142,3 @@ define [buildGlyphs para recursive recursiveCodes] : begin
|
|||
|
||||
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 argv = require("yargs").argv;
|
||||
const buildGlyphs = require("./build-glyphs.js");
|
||||
const buildFont = require("./build-font.js");
|
||||
const EmptyFont = require("./empty-font.js");
|
||||
const parameters = require("../support/parameters");
|
||||
const formVariantData = require("../support/variant-data");
|
||||
|
@ -20,21 +20,13 @@ main().catch(e => {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
async function main() {
|
||||
const font = await buildFont();
|
||||
const para = await getParameters(argv);
|
||||
const font = buildFont(para);
|
||||
if (argv.charmap) await saveCharMap(font);
|
||||
if (argv.o) await saveFont(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}`
|
||||
);
|
||||
}
|
||||
if (argv.o) await saveOtd(font);
|
||||
}
|
||||
|
||||
// Parameter preparation
|
||||
async function getParameters(argv) {
|
||||
const PARAMETERS_TOML = path.resolve(__dirname, "../parameters.toml");
|
||||
const PRIVATE_TOML = path.resolve(__dirname, "../private.toml");
|
||||
|
@ -71,13 +63,23 @@ async function getParameters(argv) {
|
|||
return para;
|
||||
}
|
||||
|
||||
// Font building
|
||||
async function buildFont() {
|
||||
const emptyFont = EmptyFont();
|
||||
const para = await getParameters(argv);
|
||||
const font = buildGlyphs.build(emptyFont, para);
|
||||
font.parameters = para;
|
||||
return 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}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -86,50 +88,18 @@ function objHashNonEmpty(obj) {
|
|||
for (let k in obj) if (obj[k]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
async function saveCharMap(font) {
|
||||
const charmap = font.glyf.map(function(glyph) {
|
||||
const isSpace = glyph.contours && glyph.contours.length ? 2 : 0;
|
||||
return [
|
||||
let charMap = [];
|
||||
for (const gid in font.glyf) {
|
||||
const glyph = font.glyf[gid];
|
||||
if (!glyph) continue;
|
||||
const isSpace = glyph.contours && glyph.contours.length;
|
||||
charMap.push([
|
||||
glyph.name,
|
||||
glyph.unicode,
|
||||
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
|
||||
local df : DivFrame para.diversityI
|
||||
set-width df.width
|
||||
include df.markSet.e
|
||||
save 'space' ' '
|
||||
save 'threePerEmsp' 0x2004
|
||||
save 'fourPerEmsp' 0x2005
|
||||
|
@ -30,7 +29,6 @@ glyph-block Symbol-Mosaic-NotDef : begin
|
|||
sketch # en-space
|
||||
local df : DivFrame 1
|
||||
set-width df.width
|
||||
include df.markSet.e
|
||||
save 'figureSpace' 0x2007
|
||||
save 'enquad' 0x2000
|
||||
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/transform' as Transform
|
||||
import '../support/glyph' as Glyph
|
||||
import '../support/transform' as Transform
|
||||
|
||||
define GDEF_SIMPLE 1
|
||||
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
|
||||
`(@l ~> @r) `{.left @l .right @r}
|
||||
|
||||
export all : define [buildCCMP chain-rule markGlyphs commonList features lookups] : begin
|
||||
commonList.push 'ccmp'
|
||||
set features.ccmp {'ccmp1' 'ccmp2'}
|
||||
export : define [buildCCMP sink markGlyphs] : begin
|
||||
define ccmp : add-feature sink 'ccmp'
|
||||
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'
|
||||
.subtables : list
|
||||
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 markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
|
||||
|
||||
set lookups.ccmp2
|
||||
define lookupCcmp2 : add-lookup sink : object
|
||||
.type 'gsub_ligature'
|
||||
.subtables : list : object
|
||||
psilivaria {'commaAbove' 'graveAbove'}
|
||||
|
@ -29,3 +32,6 @@ export all : define [buildCCMP chain-rule markGlyphs commonList features lookups
|
|||
dasiavaria {'revCommaAbove' 'graveAbove'}
|
||||
dasiaoxia {'revCommaAbove' 'acuteAbove'}
|
||||
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
|
||||
`(@l ~> @r) `{.left @l .right @r}
|
||||
|
||||
|
@ -7,7 +9,9 @@ define [just s] : lambda [t] : t.map : lambda [x] s
|
|||
define preserved null
|
||||
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 [stick style] : {'hyphen' 'equal'} ~> [lsx style]
|
||||
|
@ -59,8 +63,7 @@ export : define [buildLigations chain-rule lookupOrder commonList features looku
|
|||
g.push v
|
||||
* g
|
||||
|
||||
local mappedFeature : plm.(ligationFeatureName) || {}
|
||||
local ligationLookupName : 'lig_' + ligationFeatureName + '-' + mappedFeature
|
||||
local ligationLookupName : 'lig_' + featureName + '-' + mappedFeature
|
||||
|
||||
define [hasLG ln] : [mappedFeature.indexOf ln] >= 0
|
||||
|
||||
|
@ -76,26 +79,23 @@ export : define [buildLigations chain-rule lookupOrder commonList features looku
|
|||
(f <@ Function) : f left
|
||||
true : return f
|
||||
|
||||
commonList.push ligationFeatureName
|
||||
define feature : add-common-feature sink : add-feature sink featureName
|
||||
|
||||
local featLookups {}
|
||||
local lastLookupName null
|
||||
|
||||
local [dedup ln0 obj] : begin
|
||||
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
|
||||
if (h == h1) : return name
|
||||
return ln0
|
||||
|
||||
local [includeLookup obj] : begin
|
||||
local ln : dedup (ligationLookupName + featLookups.length) obj
|
||||
if [not lookups.(ln)] : set lookups.(ln) obj
|
||||
featLookups.push ln
|
||||
if lastLookupName : lookupOrder.push {lastLookupName ln}
|
||||
local ln : dedup (ligationLookupName + feature.lookups.length) obj
|
||||
if [not sink.lookups.(ln)] : set sink.lookups.(ln) obj
|
||||
feature.lookups.push ln
|
||||
if lastLookupName : sink.lookupDep.push {lastLookupName ln}
|
||||
set lastLookupName ln
|
||||
|
||||
set features.(ligationFeatureName) featLookups
|
||||
|
||||
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)
|
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;
|
||||
}
|
||||
|
||||
function autoref(glyf, excludeUnicodes) {
|
||||
suppressNaN(glyf);
|
||||
function autoref(gs, excludeUnicodeSet) {
|
||||
suppressNaN(gs);
|
||||
|
||||
for (let j = 0; j < glyf.length; j++) {
|
||||
const g = glyf[j];
|
||||
for (let j = 0; j < gs.length; j++) {
|
||||
const g = gs[j];
|
||||
if (g.contours) {
|
||||
for (let k = 0; k < g.contours.length; k++) {
|
||||
const contour = g.contours[k];
|
||||
|
@ -81,44 +81,44 @@ function autoref(glyf, excludeUnicodes) {
|
|||
}
|
||||
|
||||
// Refl-referencify, forward.
|
||||
for (let j = 0; j < glyf.length; j++) {
|
||||
if (!glyf[j].contours.length || (glyf[j].references && glyf[j].references.length)) continue;
|
||||
for (let k = j + 1; k < glyf.length; k++) {
|
||||
if (glyf[j].contours.length === glyf[k].contours.length) {
|
||||
match(glyf[j], glyf[k], j);
|
||||
for (let j = 0; j < gs.length; j++) {
|
||||
if (!gs[j].contours.length || (gs[j].references && gs[j].references.length)) continue;
|
||||
for (let k = j + 1; k < gs.length; k++) {
|
||||
if (gs[j].contours.length === gs[k].contours.length) {
|
||||
match(gs[j], gs[k], j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// referencify, backward
|
||||
for (let j = 0; j < glyf.length; j++) {
|
||||
if (glyf[j].cmpPriority < 0) continue;
|
||||
if (!glyf[j].contours.length) continue;
|
||||
if (glyf[j].references && glyf[j].references.length) continue;
|
||||
for (let k = glyf.length - 1; k >= 0; k--) {
|
||||
if (glyf[j].contours.length > glyf[k].contours.length) continue;
|
||||
for (let j = 0; j < gs.length; j++) {
|
||||
if (gs[j].cmpPriority < 0) continue;
|
||||
if (!gs[j].contours.length) continue;
|
||||
if (gs[j].references && gs[j].references.length) continue;
|
||||
for (let k = gs.length - 1; k >= 0; k--) {
|
||||
if (gs[j].contours.length > gs[k].contours.length) continue;
|
||||
if (
|
||||
glyf[j].contours.length === glyf[k].contours.length &&
|
||||
!(glyf[k].references && glyf[k].references.length)
|
||||
gs[j].contours.length === gs[k].contours.length &&
|
||||
!(gs[k].references && gs[k].references.length)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
while (match(glyf[j], glyf[k], j)) "pass";
|
||||
while (match(gs[j], gs[k], j)) "pass";
|
||||
}
|
||||
}
|
||||
|
||||
// unlink composite
|
||||
for (let j = 0; j < glyf.length; j++) {
|
||||
if (!glyf[j].references || glyf[j].references.length === 0) continue;
|
||||
for (let j = 0; j < gs.length; j++) {
|
||||
if (!gs[j].references || gs[j].references.length === 0) continue;
|
||||
if (
|
||||
!glyf[j].flatten &&
|
||||
glyf[j].contours.length === 0 &&
|
||||
!(glyf[j].unicode && excludeUnicodes.has(glyf[j].unicode[0]))
|
||||
!gs[j].flatten &&
|
||||
gs[j].contours.length === 0 &&
|
||||
!(gs[j].unicode && excludeUnicodeSet.has(gs[j].unicode[0]))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
glyf[j].contours = unlinkRef(glyf[j], 0, 0, glyf);
|
||||
glyf[j].references = [];
|
||||
gs[j].contours = unlinkRef(gs[j], 0, 0, gs);
|
||||
gs[j].references = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,17 +75,18 @@ function byGlyphPriority(a, b) {
|
|||
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();
|
||||
excludeUnicode.add(0x80);
|
||||
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
|
||||
|
||||
// autoref
|
||||
font.glyf = font.glyf.map((g, j) => ((g.glyphOrder = j), g)).sort(byGlyphPriority);
|
||||
autoRef(font.glyf, excludeUnicode);
|
||||
gs = gs.map((g, j) => ((g.glyphOrder = j), g)).sort(byGlyphPriority);
|
||||
autoRef(gs, excludeUnicode);
|
||||
|
||||
// regulate
|
||||
for (let g of font.glyf) regulateGlyph(g, skew);
|
||||
for (let g of gs) regulateGlyph(g, skew);
|
||||
|
||||
// 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 [gen, meta, glyphs, support] = await target.need(
|
||||
ScriptsUnder(ext, `gen`),
|
||||
ScriptsUnder(ext, `meta`),
|
||||
ScriptsUnder(ext, `glyphs`),
|
||||
ScriptsUnder(ext, `meta`),
|
||||
ScriptsUnder(ext, `otl`),
|
||||
ScriptsUnder(ext, `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"));
|
||||
});
|
||||
|
||||
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);
|
||||
if (jsFromPtl.indexOf(path.full) >= 0) {
|
||||
const ptl = path.full.replace(/\.js$/g, ".ptl");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue