* Fix ss02, ss04, ss06, ss13, ss17's application on i and j (#2033).

This commit is contained in:
be5invis 2023-10-10 01:24:55 -07:00
parent 1f80407eed
commit 344d8e95d8
24 changed files with 441 additions and 402 deletions

1
changes/27.2.1.md Normal file
View file

@ -0,0 +1 @@
* Fix `ss02`, `ss04`, `ss06`, `ss13`, `ss17`'s application on `i` and `j` (#2033).

View file

@ -34,7 +34,11 @@ function markLookups(table, sink, sinkDirect) {
if (lookup.type === "gsub_chaining" || lookup.type === "gpos_chaining") { if (lookup.type === "gsub_chaining" || lookup.type === "gpos_chaining") {
for (let st of lookup.rules) { for (let st of lookup.rules) {
if (!st || !st.apply) continue; if (!st || !st.apply) continue;
for (const app of st.apply) sink.add(app.lookup); for (const app of st.apply) {
if (!app.lookup.name)
throw new Error("Unreachable: lookup name should be present");
sink.add(app.lookup.name);
}
} }
} }
} }
@ -46,7 +50,7 @@ function markLookupsStart(table, sink, sinkDirect) {
for (let f in table.features) { for (let f in table.features) {
const feature = table.features[f]; const feature = table.features[f];
if (!feature) continue; if (!feature) continue;
for (const l of feature) { for (const l of feature.lookups) {
sink.add(l); sink.add(l);
sinkDirect.add(l); sinkDirect.add(l);
} }
@ -65,9 +69,15 @@ function sweepFeatures(table, accessibleLookupsIds) {
for (let f in table.features) { for (let f in table.features) {
const feature = table.features[f]; const feature = table.features[f];
if (!feature) continue; if (!feature) continue;
const featureFiltered = []; const featureFiltered = {
for (const l of feature) if (accessibleLookupsIds.has(l)) featureFiltered.push(l); name: feature.name,
if (!featureFiltered.length) continue; tag: feature.tag,
lookups: []
};
for (const l of feature.lookups) {
if (accessibleLookupsIds.has(l)) featureFiltered.lookups.push(l);
}
if (!featureFiltered.lookups.length) continue;
features1[f] = featureFiltered; features1[f] = featureFiltered;
} }
table.features = features1; table.features = features1;
@ -151,7 +161,11 @@ function markGlyphsByLookup(gsub, lid, markedGlyphs) {
if (!atLeastOneMatch) continue rules; if (!atLeastOneMatch) continue rules;
} }
// If so traverse through the lookup applications // If so traverse through the lookup applications
for (const app of rule.apply) markGlyphsByLookup(gsub, app.lookup, markedGlyphs); for (const app of rule.apply) {
if (!app.lookup.name)
throw new Error("Unreachable: lookup name should be present");
markGlyphsByLookup(gsub, app.lookup.name, markedGlyphs);
}
} }
break; break;
} }

View file

@ -106,9 +106,9 @@ class FeatureStore {
return this.m_mapping.get(id); return this.m_mapping.get(id);
} }
fill(id, data) { fill(id, data) {
const tag = id.slice(0, 4); const tag = data.tag;
const lookups = []; const lookups = [];
for (const lid of data) { for (const lid of data.lookups) {
const lookup = this.lookupStore.query(lid); const lookup = this.lookupStore.query(lid);
if (lookup) lookups.push(lookup); if (lookup) lookups.push(lookup);
} }
@ -215,8 +215,9 @@ const GsubChainingHandler = {
const inputEnds = st.inputEnds; const inputEnds = st.inputEnds;
const applications = []; const applications = [];
for (const ap of st.apply) { for (const ap of st.apply) {
const lookup = store.query(ap.lookup); if (!ap.lookup.name) throw new Error("Unreachable: lookup name must not be null");
if (!lookup) continue out; const lookup = store.query(ap.lookup.name);
if (!lookup) throw new Error(`Cannot find lookup '${ap.lookup.name}'`);
applications.push({ at: ap.at - inputBegins, apply: lookup }); applications.push({ at: ap.at - inputBegins, apply: lookup });
} }
dst.rules.push({ match, inputBegins, inputEnds, applications }); dst.rules.push({ match, inputBegins, inputEnds, applications });

View file

@ -2,7 +2,7 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [linreg clamp mix fallback] from"../../support/utils.mjs" import [linreg clamp mix fallback] from"../../support/utils.mjs"
import [getGrTree getGrMesh IsSuperscript IsSubscript] from"../../support/gr.mjs" import [getGrTree IsSuperscript IsSubscript] from"../../support/gr.mjs"
import [AnyCv DotlessOrNot CvDecompose MathSansSerif] from"../../support/gr.mjs" import [AnyCv DotlessOrNot CvDecompose MathSansSerif] from"../../support/gr.mjs"
import [NumeratorForm DenominatorForm] from"../../support/gr.mjs" import [NumeratorForm DenominatorForm] from"../../support/gr.mjs"
import [Transform] from"../../support/geometry/transform.mjs" import [Transform] from"../../support/geometry/transform.mjs"

View file

@ -1,7 +1,7 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix clamp fallback SuffixCfg] from"../../support/utils.mjs" import [mix clamp fallback SuffixCfg] from"../../support/utils.mjs"
import [AnyCv getGrMesh VS01 Zero] from"../../support/gr.mjs" import [VS01 Zero] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback SuffixCfg] from"../../support/utils.mjs" import [mix linreg clamp fallback SuffixCfg] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,7 +1,6 @@
$$include '../../meta/macros.ptl' $$include '../../meta/macros.ptl'
import [mix linreg clamp fallback] from"../../support/utils.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs"
import [AnyCv getGrMesh] from"../../support/gr.mjs"
glyph-module glyph-module

View file

@ -1,5 +1,3 @@
import [AddCommonFeature AddFeature AddLookup] from"./table-util.mjs"
extern Map extern Map
extern Set extern Set
@ -21,27 +19,22 @@ define MarkInteractions : new Map : list
define MkmkStackingLimit : new Set { 'above' 'below' } define MkmkStackingLimit : new Set { 'above' 'below' }
export : define [buildMarkMkmk sink glyphStore markGlyphs] : begin export : define [buildMarkMkmk gpos glyphStore markGlyphs] : begin
define validMarkClasses : new Set MarkClasses define validMarkClasses : new Set MarkClasses
define mark : object define mark : object
feature : AddFeature sink 'mark' feature : gpos.addCommonFeature : gpos.createFeature 'mark'
lookupMap : new Map lookupMap : new Map
lookupNames : new Set
createLookup : function [] {.type 'gpos_mark_to_base' .marks {.} .bases {.}} createLookup : function [] {.type 'gpos_mark_to_base' .marks {.} .bases {.}}
define mkmk : object define mkmk : object
feature : AddFeature sink 'mkmk' feature : gpos.addCommonFeature : gpos.createFeature 'mkmk'
lookupMap : new Map lookupMap : new Map
lookupNames : new Set
createLookup : function [] {.type 'gpos_mark_to_mark' .marks {.} .bases {.}} createLookup : function [] {.type 'gpos_mark_to_mark' .marks {.} .bases {.}}
AddCommonFeature sink mark.feature
AddCommonFeature sink mkmk.feature
# Populate the marks # Populate the marks
foreach cls [items-of MarkClasses] : begin foreach cls [items-of MarkClasses] : begin
local markLookup : ensureLookup sink mark cls local markLookup : ensureLookup gpos mark cls
local mkmkLookup : ensureLookup sink mkmk cls local mkmkLookup : ensureLookup gpos mkmk cls
foreach { gn glyph } [glyphStore.namedEntries] : begin foreach { gn glyph } [glyphStore.namedEntries] : begin
if glyph.markAnchors.(cls) : begin if glyph.markAnchors.(cls) : begin
@ -52,8 +45,8 @@ export : define [buildMarkMkmk sink glyphStore markGlyphs] : begin
# Populate the bases # Populate the bases
foreach cls [items-of MarkClasses] : begin foreach cls [items-of MarkClasses] : begin
local markLookup : ensureLookup sink mark cls local markLookup : ensureLookup gpos mark cls
local mkmkLookup : ensureLookup sink mkmk cls local mkmkLookup : ensureLookup gpos mkmk cls
foreach { gn glyph } [glyphStore.namedEntries] : begin foreach { gn glyph } [glyphStore.namedEntries] : begin
if glyph.baseAnchors.(cls) : begin if glyph.baseAnchors.(cls) : begin
@ -61,8 +54,8 @@ export : define [buildMarkMkmk sink glyphStore markGlyphs] : begin
: then : addBaseAnchor mkmkLookup gn cls glyph.baseAnchors.(cls) : then : addBaseAnchor mkmkLookup gn cls glyph.baseAnchors.(cls)
: else : addBaseAnchor markLookup gn cls glyph.baseAnchors.(cls) : else : addBaseAnchor markLookup gn cls glyph.baseAnchors.(cls)
foreach lidMark mark.lookupNames : foreach lidMkmk mkmk.lookupNames foreach markLookup [mark.lookupMap.values] : foreach mkmkLookup [mkmk.lookupMap.values]
sink.lookupDep.push { lidMark lidMkmk } gpos.setDependency markLookup mkmkLookup
foreach { cls lookup } mkmk.lookupMap : begin foreach { cls lookup } mkmk.lookupMap : begin
local interactionMarkSet : new Set ([MarkInteractions.get cls] || { cls }) local interactionMarkSet : new Set ([MarkInteractions.get cls] || { cls })
@ -77,17 +70,14 @@ export : define [buildMarkMkmk sink glyphStore markGlyphs] : begin
if (interactionMarkSet.size > 1) : begin if (interactionMarkSet.size > 1) : begin
markGlyphs.markGlyphSets.push : Array.from includeSet markGlyphs.markGlyphSets.push : Array.from includeSet
define [ensureLookup sink feat cls] : begin define [ensureLookup gpos feat cls] : begin
local existing : feat.lookupMap.get cls local existing : feat.lookupMap.get cls
if existing : return existing if existing : return existing
local novel : feat.createLookup local lookup : gpos.createLookup : feat.createLookup
local lid : AddLookup sink novel feat.feature.addLookup lookup
feat.feature.lookups.push lid feat.lookupMap.set cls lookup
feat.lookupNames.add lid return lookup
feat.lookupMap.set cls novel
return novel
define [addMarkAnchor lookup gn cls anchor] : begin define [addMarkAnchor lookup gn cls anchor] : begin
local a : object local a : object

View file

@ -1,6 +1,6 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [AddCommonFeature AddFeature AddLookup AddFeatureLookup ChainRuleBuilder BeginLookupBlock EndLookupBlock UkMapToLookup UkMap2ToLookup] from"./table-util.mjs" import [UkMapToLookup UkMap2ToLookup] from"./table-util.mjs"
import [Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript LeaningMark LeaningMarkSpacer] from"../support/gr.mjs" import [Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript LeaningMark LeaningMarkSpacer] from"../support/gr.mjs"
import as UnicodeKnowledge from"../meta/unicode-knowledge.mjs" import as UnicodeKnowledge from"../meta/unicode-knowledge.mjs"
@ -10,29 +10,28 @@ define-macro Ccmp-Group : syntax-rules
`[Ccmp-Group @description @body] `[Ccmp-Group @description @body]
dirty `[$ExecCcmpGroup$ [function [export-lookup chain-rule] @[formOf body]]] dirty `[$ExecCcmpGroup$ [function [export-lookup chain-rule] @[formOf body]]]
export : define [buildCCMP sink glyphStore markGlyphs] : begin export : define [buildCCMP gsub glyphStore markGlyphs] : begin
local anyMark : Array.from markGlyphs.all local anyMark : Array.from markGlyphs.all
local aboveMark : filterMarkByClass markGlyphs 'above' local aboveMark : filterMarkByClass markGlyphs 'above'
define ccmp : AddFeature sink 'ccmp' define ccmp : gsub.addCommonFeature : gsub.createFeature 'ccmp'
AddCommonFeature sink ccmp
define [$ExecCcmpGroup$ fn] : begin define [$ExecCcmpGroup$ fn] : begin
local addedLookups {} local addedLookups {}
define [export-lookup lookupName] : begin define [export-lookup lookup] : begin
ccmp.lookups.push lookupName ccmp.addLookup lookup
addedLookups.push lookupName addedLookups.push lookup
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
define {chain-rule} : ChainRuleBuilder sink define {chain-rule} : gsub.ChainRuleBuilder
fn export-lookup chain-rule fn export-lookup chain-rule
for [local j 1] (j < addedLookups.length) [inc j] : begin for [local j 1] (j < addedLookups.length) [inc j] : begin
sink.lookupDep.push {addedLookups.(j - 1) addedLookups.(j)} gsub.setDependency addedLookups.(j - 1) addedLookups.(j)
EndLookupBlock rec sink gsub.endBlock rec
Ccmp-Group "Mark transforms" : begin Ccmp-Group "Mark transforms" : begin
@ -52,7 +51,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
define [GrekUpperTonosTf] : UkMapToLookup UnicodeKnowledge.upperGrekMarkToTonosTf define [GrekUpperTonosTf] : UkMapToLookup UnicodeKnowledge.upperGrekMarkToTonosTf
# Dotless transform # Dotless transform
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above'] .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
.rules : list .rules : list
@ -60,17 +59,17 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule groupGrekUpperTonos [GrekUpperTonosTf] chain-rule groupGrekUpperTonos [GrekUpperTonosTf]
# Iota transform # Iota transform
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below'] .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
.rules : list .rules : list
chain-rule groupLF [IotaLF] chain-rule groupLF [IotaLF]
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_ligature' .type 'gsub_ligature'
.substitutions : UkMap2ToLookup UnicodeKnowledge.markCompositionTf .substitutions : UkMap2ToLookup UnicodeKnowledge.markCompositionTf
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_multiple' .type 'gsub_multiple'
.substitutions : object .substitutions : object
'parenAbove' { 'leftParenAbove' 'rightParenAbove' } 'parenAbove' { 'leftParenAbove' 'rightParenAbove' }
@ -89,13 +88,13 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
TieMarkFrom.push gid TieMarkFrom.push gid
TieMarkTo.push [TieMark.get g] TieMarkTo.push [TieMark.get g]
define lookupTieMarkLigature : AddLookup sink : object define lookupTieMarkLigature : gsub.createLookup : object
.type 'gsub_ligature' .type 'gsub_ligature'
.substitutions : {}.concat .substitutions : {}.concat
TieMarkFrom.map : lambda [gnFrom idx] TieMarkFrom.map : lambda [gnFrom idx]
object [from {'cgj' gnFrom}] [to TieMarkTo.(idx)] object [from {'cgj' gnFrom}] [to TieMarkTo.(idx)]
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list : object .rules : list : object
match {[TieGlyphs.concat TieMarkTo] {'cgj'} TieMarkFrom} match {[TieGlyphs.concat TieMarkTo] {'cgj'} TieMarkFrom}
@ -105,7 +104,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
Ccmp-Group "Special dot-above transformation" : begin Ccmp-Group "Special dot-above transformation" : begin
# b-dot, d-dot, h-dot, k-dot # b-dot, d-dot, h-dot, k-dot
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_ligature' .type 'gsub_ligature'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above'] .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
.substitutions : list .substitutions : list
@ -134,11 +133,11 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
markSpacer.push [LeaningMarkSpacer.get g] markSpacer.push [LeaningMarkSpacer.get g]
splitMapping.push { gn {[LeaningMarkSpacer.get g] [LeaningMark.get g]} } splitMapping.push { gn {[LeaningMarkSpacer.get g] [LeaningMark.get g]} }
define lookupTurnMarkIntoLeaningAndSpacer : AddLookup sink : object define lookupTurnMarkIntoLeaningAndSpacer : gsub.createLookup : object
.type 'gsub_multiple' .type 'gsub_multiple'
.substitutions : Object.fromEntries splitMapping .substitutions : Object.fromEntries splitMapping
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs mkCenter] .ignoreGlyphs [filterMarkByClassNegated markGlyphs mkCenter]
.rules : list .rules : list
@ -158,7 +157,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
if [IsSuperscript.get g] : superscripts.push gn if [IsSuperscript.get g] : superscripts.push gn
if [IsSubscript.get g] : subscripts.push gn if [IsSubscript.get g] : subscripts.push gn
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.ignoreGlyphs anyMark .ignoreGlyphs anyMark
.rules : list .rules : list
@ -230,7 +229,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
e.push ('toneSandhiMid' + toneStart + toneMid + toneEnd) e.push ('toneSandhiMid' + toneStart + toneMid + toneEnd)
return (f ~> e) return (f ~> e)
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneToToneStart 0] {'tone0'} chain-rule [ToneToToneStart 0] {'tone0'}
@ -239,7 +238,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule [ToneToToneStart 3] {'tone3'} chain-rule [ToneToToneStart 3] {'tone3'}
chain-rule [ToneToToneStart 4] {'tone4'} chain-rule [ToneToToneStart 4] {'tone4'}
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneStartOrMidAt 0] [ToneStartToToneMid 0] chain-rule [ToneStartOrMidAt 0] [ToneStartToToneMid 0]
@ -248,7 +247,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule [ToneStartOrMidAt 3] [ToneStartToToneMid 3] chain-rule [ToneStartOrMidAt 3] [ToneStartToToneMid 3]
chain-rule [ToneStartOrMidAt 4] [ToneStartToToneMid 4] chain-rule [ToneStartOrMidAt 4] [ToneStartToToneMid 4]
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneStartOrMidAt 0] [ToneToToneEnd 0] chain-rule [ToneStartOrMidAt 0] [ToneToToneEnd 0]
@ -257,7 +256,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule [ToneStartOrMidAt 3] [ToneToToneEnd 3] chain-rule [ToneStartOrMidAt 3] [ToneToToneEnd 3]
chain-rule [ToneStartOrMidAt 4] [ToneToToneEnd 4] chain-rule [ToneStartOrMidAt 4] [ToneToToneEnd 4]
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneSandhiToToneStart 0] {'toneSandhi0'} chain-rule [ToneSandhiToToneStart 0] {'toneSandhi0'}
@ -266,7 +265,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule [ToneSandhiToToneStart 3] {'toneSandhi3'} chain-rule [ToneSandhiToToneStart 3] {'toneSandhi3'}
chain-rule [ToneSandhiToToneStart 4] {'toneSandhi4'} chain-rule [ToneSandhiToToneStart 4] {'toneSandhi4'}
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiStartToToneMid 0] chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiStartToToneMid 0]
@ -275,7 +274,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiStartToToneMid 3] chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiStartToToneMid 3]
chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiStartToToneMid 4] chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiStartToToneMid 4]
export-lookup : AddLookup sink : object export-lookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiToToneEnd 0] chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiToToneEnd 0]
@ -286,11 +285,9 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin
return ccmp return ccmp
export : define [buildCCMPPostCvSs sink ccmpFeature glyphStore markGlyphs] : begin export : define [buildCCMPPostCvSs gsub ccmp glyphStore markGlyphs] : begin
local anyMark : Array.from markGlyphs.all local anyMark : Array.from markGlyphs.all
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
define ccmp : AddFeature sink 'ccmp'
define triggerGlyphs_Normal { } define triggerGlyphs_Normal { }
define triggerGlyphs_Y { } define triggerGlyphs_Y { }
@ -299,14 +296,14 @@ export : define [buildCCMPPostCvSs sink ccmpFeature glyphStore markGlyphs] : beg
[OgonekTrY.get g] : triggerGlyphs_Y.push gid [OgonekTrY.get g] : triggerGlyphs_Y.push gid
true : triggerGlyphs_Normal.push gid true : triggerGlyphs_Normal.push gid
define ogonekSpacerNormal : AddLookup sink : object define ogonekSpacerNormal : gsub.createLookup : object
.type 'gsub_multiple' .type 'gsub_multiple'
.substitutions UnicodeKnowledge.ogonekBelowToTRTf .substitutions UnicodeKnowledge.ogonekBelowToTRTf
define ogonekSpacerY : AddLookup sink : object define ogonekSpacerY : gsub.createLookup : object
.type 'gsub_single' .type 'gsub_single'
.substitutions UnicodeKnowledge.ogonekBelowToTRTf_Y .substitutions UnicodeKnowledge.ogonekBelowToTRTf_Y
define lookupMarks1 : AddLookup sink : object ccmp.addLookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below'] .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
.rules : list .rules : list
@ -321,8 +318,7 @@ export : define [buildCCMPPostCvSs sink ccmpFeature glyphStore markGlyphs] : beg
.inputEnds 2 .inputEnds 2
.apply {{.at 1 .lookup ogonekSpacerNormal}} .apply {{.at 1 .lookup ogonekSpacerNormal}}
ccmpFeature.lookups.push lookupMarks1 gsub.endBlock rec
EndLookupBlock rec sink
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -1,6 +1,5 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [AddLookup AddCommonFeature PickCommonFeature AddFeatureLookup PickLookup BeginLookupBlock EndLookupBlock ChainRuleBuilder] from"./table-util.mjs"
import [Cv AnyCv CvDecompose RightDependentLink RightDependentTrigger] from"../support/gr.mjs" import [Cv AnyCv CvDecompose RightDependentLink RightDependentTrigger] from"../support/gr.mjs"
extern Map extern Map
@ -11,20 +10,68 @@ define look-around null
define [FeatureName tag] : tag + '_cvss' define [FeatureName tag] : tag + '_cvss'
define [SsLookupName tag] : 'lookup_ss_' + tag define [SsLookupName tag] : 'lookup_ss_' + tag
define [CvLookupName tag] : 'lookup_cv_' + tag define [CvLookupName tag] : 'lookup_cv_' + tag
define [CvSpecificLookupName tag rank] : 'lookup_cv_' + tag + '_' + rank
define [CvDecomposeLookupName tag] : 'lookup_cv_decompose_' + tag define [CvDecomposeLookupName tag] : 'lookup_cv_decompose_' + tag
export : define [buildCVSS sink para glyphStore] : begin class CvLookupManager
define {chain-rule} : ChainRuleBuilder sink public [new table tag feature] : begin
set this.table table
set this.tag tag
set this.feature feature
local rec : BeginLookupBlock sink set this.decompositionLookups {}
local cvLookupNameSet : new Set
local ssLookupNameSet : new Set
# Build decomposition lookups set this.substitutionLookups {}
set this.altrenatesLookup null
set this.singleSubstLookups {}
public [addDecompositionLookup lookup] : begin
this.decompositionLookups.push lookup
this.feature.addLookup lookup
public [alternateSubst] : begin
if this.altrenatesLookup : return this.altrenatesLookup
define lookupName : 'lookup_cv_2_alternates_' + this.tag
define lookup : this.table.pickLookup lookupName {.type 'gsub_alternate' .substitutions {.}}
this.feature.addLookup lookup
this.substitutionLookups.push lookup
set this.altrenatesLookup lookup
return lookup
public [singleSubst rank] : begin
if this.singleSubstLookups.(rank) : return this.singleSubstLookups.(rank)
define lookupName : 'lookup_cv_1_single_' + this.tag + '_' + rank
define lookup : this.table.pickLookup lookupName {.type 'gsub_single' .substitutions {.}}
this.feature.addLookup lookup
this.substitutionLookups.push lookup
set this.singleSubstLookups.(rank) lookup
return lookup
public [linkDeps] : begin
foreach a [items-of this.decompositionLookups] : begin
foreach b [items-of this.substitutionLookups] : begin
this.table.setDependency a b
for [local i 1] (i < this.substitutionLookups.length) [inc i]
this.table.setDependency this.substitutionLookups.(i - 1) this.substitutionLookups.(i)
export : define [buildCVSS gsub para glyphStore] : begin
local rec : gsub.beginBlock
local cvs : new Map
do "Initialize CV feature atlas"
foreach {name prime} para.variants.primes : if prime.tag : begin
define feature : gsub.addCommonFeature : gsub.createFeature prime.tag
local cvLookupManager : new CvLookupManager gsub prime.tag feature
cvs.set prime.tag cvLookupManager
do "Build decomposition lookups"
local decompositions : new Map local decompositions : new Map
local cvDecompositionLookupNameSet : new Set
local cvTagToDecompositionLookups : new Map
foreach { gn glyph } [glyphStore.namedEntries] : if [CvDecompose.get glyph] : do foreach { gn glyph } [glyphStore.namedEntries] : if [CvDecompose.get glyph] : do
local decomp : object local decomp : object
parts : CvDecompose.get glyph parts : CvDecompose.get glyph
@ -38,73 +85,52 @@ export : define [buildCVSS sink para glyphStore] : begin
foreach { gn decomp } decompositions : if decomp.influences.size : do foreach { gn decomp } decompositions : if decomp.influences.size : do
define lookupName : CvDecomposeLookupName : [[Array.from decomp.influences].sort].join '/' define lookupName : CvDecomposeLookupName : [[Array.from decomp.influences].sort].join '/'
define lookup : PickLookup sink lookupName {.type 'gsub_multiple' .substitutions {.}} define lookup : gsub.pickLookup lookupName {.type 'gsub_multiple' .substitutions {.}}
cvDecompositionLookupNameSet.add lookupName
set lookup.substitutions.(gn) decomp.parts set lookup.substitutions.(gn) decomp.parts
foreach cvTag decomp.influences : begin foreach cvTag decomp.influences : [cvs.get cvTag].addDecompositionLookup lookup
local s : cvTagToDecompositionLookups.get cvTag
if [not s] : begin
set s : new Set
cvTagToDecompositionLookups.set cvTag s
s.add lookupName
define [addCvMapping tag src dst rank] : begin do "cvxx"
define feature : PickCommonFeature sink [FeatureName tag] local cvGrs {}
define lookupName : CvLookupName tag foreach {name prime} para.variants.primes : foreach {vn variant} prime.variants : begin
define lookup : PickLookup sink lookupName {.type 'gsub_alternate' .substitutions {.}} if (prime.tag && variant.rank) : cvGrs.push : Cv prime.tag variant.rank
cvGrs.sort Cv.compare
if [not : cvLookupNameSet.has lookupName] : begin foreach gr [items-of cvGrs] : begin
AddFeatureLookup feature lookupName local cvAlt : [cvs.get gr.tag].alternateSubst
cvLookupNameSet.add lookupName
local decompLookups : cvTagToDecompositionLookups.get tag
if decompLookups : foreach d decompLookups : AddFeatureLookup feature d
if [not lookup.substitutions.(src)] : set lookup.substitutions.(src) { }
set lookup.substitutions.(src).(rank - 1) dst
define [addSsSubstitution tag decomp src dst] : begin
define feature : PickCommonFeature sink [FeatureName tag]
define lookupName : SsLookupName composition.tag
define lookup : PickLookup sink lookupName {.type 'gsub_single' .substitutions {.}}
if [not : ssLookupNameSet.has lookupName] : begin
AddFeatureLookup feature lookupName
ssLookupNameSet.add lookupName
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
local decompLookups : cvTagToDecompositionLookups.get pv.tag
if decompLookups : foreach d decompLookups : AddFeatureLookup feature d
set lookup.substitutions.(src) dst
# cvxx
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : do
foreach [gr : items-of : AnyCv.query glyph] : if gr.tag : begin
addCvMapping gr.tag gn [glyphStore.ensureExists : gr.get glyph] gr.rank
# ssxx
foreach {name composition} para.variants.composites : if composition.tag : do
define decomp : composition.decompose para para.variants.selectorTree
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
local gr : Cv pv.tag pv.rank
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
local substituted : gr.get glyph local subst : gr.get glyph
if substituted : addSsSubstitution composition.tag decomp gn substituted if (subst && subst != gn) : begin
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { }
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst
# If there are holes in the alternates list, fill them do "ssxx" : foreach {name composition} para.variants.composites : if composition.tag : begin
foreach lutn cvLookupNameSet : begin define feature : gsub.addCommonFeature : gsub.createFeature composition.tag
local st [PickLookup sink lutn].substitutions
define decomp : composition.decompose para para.variants.selectorTree
local ssGrs {}
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
ssGrs.push : Cv pv.tag pv.rank
foreach lookup [items-of [cvs.get pv.tag].decompositionLookups] : begin
feature.addLookup lookup
ssGrs.sort Cv.compare
foreach gr [items-of ssGrs] : begin
local cvSingle : [cvs.get gr.tag].singleSubst gr.rank
feature.addLookup cvSingle
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
local subst : gr.get glyph
if (subst && subst != gn) : begin
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst
do "Cleanup and link dependency"
foreach cv [cvs.values] : begin
local st cv.altrenatesLookup.substitutions
foreach { k v } [pairs-of st] : foreach idx [range 0 v.length] : if [not v.(idx)] foreach { k v } [pairs-of st] : foreach idx [range 0 v.length] : if [not v.(idx)]
set v.(idx) k set v.(idx) k
# Lookup dependency cv.linkDeps
foreach lutnDe cvDecompositionLookupNameSet : foreach lutnCv cvLookupNameSet : begin
sink.lookupDep.push { lutnDe lutnCv }
foreach lutnDe cvDecompositionLookupNameSet : foreach lutnSs ssLookupNameSet : begin
sink.lookupDep.push { lutnDe lutnSs }
foreach lutnCv cvLookupNameSet : foreach lutnSs ssLookupNameSet : begin
sink.lookupDep.push { lutnCv lutnSs }
EndLookupBlock rec sink gsub.endBlock rec
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -1,16 +1,15 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [AddCommonFeature AddFeature AddLookup BeginLookupBlock EndLookupBlock ChainRuleBuilder] from"./table-util.mjs"
import [NumeratorForm DenominatorForm] from"../support/gr.mjs" import [NumeratorForm DenominatorForm] from"../support/gr.mjs"
# Name-driven feature pairs # Name-driven feature pairs
export : define [buildFrac sink glyphStore] : begin export : define [buildFrac gsub glyphStore] : begin
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
define frac : AddFeature sink 'frac' define frac : gsub.addCommonFeature : gsub.createFeature 'frac'
define { chain-rule reverse-rule } : ChainRuleBuilder sink define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
define subSolidus : AddLookup sink : object define subSolidus : gsub.createLookup : object
.type 'gsub_single' .type 'gsub_single'
.substitutions : object ['solidus' 'fractionBar'] ['slash' 'fractionBar'] .substitutions : object ['solidus' 'fractionBar'] ['slash' 'fractionBar']
@ -26,24 +25,22 @@ export : define [buildFrac sink glyphStore] : begin
numSet.push numForm numSet.push numForm
denSet.push denForm denSet.push denForm
define subDen : AddLookup sink : object define subDen : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule [{'fractionBar'}.concat denSet] [digitSet ~> denSet] chain-rule [{'fractionBar'}.concat denSet] [digitSet ~> denSet]
define subNum : AddLookup sink : object define subNum : gsub.createLookup : object
.type 'gsub_reverse' .type 'gsub_reverse'
.rules : list .rules : list
reverse-rule [digitSet ~> numSet] [{'fractionBar'}.concat numSet] reverse-rule [digitSet ~> numSet] [{'fractionBar'}.concat numSet]
frac.lookups.push subSolidus frac.addLookup subSolidus
frac.lookups.push subDen frac.addLookup subDen
frac.lookups.push subNum frac.addLookup subNum
sink.lookupDep.push {subSolidus subDen} gsub.setDependency subSolidus subDen
sink.lookupDep.push {subSolidus subNum} gsub.setDependency subSolidus subNum
AddCommonFeature sink frac gsub.endBlock rec
EndLookupBlock rec sink
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -1,8 +1,6 @@
import [AddCommonFeature AddFeature AddLookup BeginLookupBlock EndLookupBlock] from"./table-util.mjs"
# Name-driven feature pairs # Name-driven feature pairs
export : define [buildGrFeature sink glyphStore gr] : begin export : define [buildGrFeature gsub glyphStore gr] : begin
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
local mapping {.} local mapping {.}
foreach { gnSrc glyph } [glyphStore.namedEntries] : begin foreach { gnSrc glyph } [glyphStore.namedEntries] : begin
@ -12,11 +10,10 @@ export : define [buildGrFeature sink glyphStore gr] : begin
set mapping.(gnSrc) gnDst set mapping.(gnSrc) gnDst
if [objectIsNotEmpty mapping] : begin if [objectIsNotEmpty mapping] : begin
define lookup1 : AddLookup sink {.type 'gsub_single' .substitutions mapping} define lookup1 : gsub.createLookup {.type 'gsub_single' .substitutions mapping}
define feature1 : AddFeature sink gr.otlTag define feature1 : gsub.addCommonFeature : gsub.createFeature gr.otlTag
feature1.lookups.push lookup1 feature1.addLookup lookup1
AddCommonFeature sink feature1
EndLookupBlock rec sink gsub.endBlock rec
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -1,6 +1,5 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [AddCommonFeature AddFeature ChainRuleBuilder BeginLookupBlock EndLookupBlock] from"./table-util.mjs"
extern Map extern Map
extern Set extern Set
@ -12,62 +11,52 @@ define look-around null
define advance : lambda [t] null define advance : lambda [t] null
define ident : lambda [t] : t.map : lambda [x] x define ident : lambda [t] : t.map : lambda [x] x
export : define [buildLigations sink para plm] : begin export : define [buildLigations gsub para plm] : begin
# Initialize features # Initialize features
define features : new Map define features : new Map
foreach [ { featureTag } : pairs-of plm] : begin foreach [ { featureTag } : pairs-of plm] : begin
local feature : AddCommonFeature sink : AddFeature sink featureTag local feature : gsub.addCommonFeature : gsub.createFeature featureTag
features.set featureTag feature features.set featureTag feature
buildLigationsImpl sink para [DoLigGroupT sink plm features] buildLigationsImpl gsub para [DoLigGroupT gsub plm features]
define [DoLigGroupT sink plm features] : function [F] : begin define [DoLigGroupT gsub plm features] : function [F] : begin
define deDupeGroups : new Map define deDupeGroups : new Map
# Groupwise deduplicate of the lookups we just added # Groupwise deduplicate of the lookups we just added
# Push the lookups if they are indeed new
define [deDupe lookupsToPush] : begin define [deDupe lookupsToPush] : begin
local h '' local h : JSON.stringify lookupsToPush
foreach obj [lookupsToPush.values] : begin
set h : h + [JSON.stringify obj] + '\n'
local g : deDupeGroups.get h local g : deDupeGroups.get h
if g : return { false g } if g : return { false g }
deDupeGroups.set h lookupsToPush # We now actually push the lookups
return { true lookupsToPush }
local results {}
local rec : gsub.beginBlock
local lastLookupInGroup null
foreach raw [items-of lookupsToPush] : begin
local lookup : gsub.createLookup raw
if lastLookupInGroup : gsub.setDependency lastLookupInGroup lookup
set lastLookupInGroup lookup
results.push lookup
gsub.endBlock rec
deDupeGroups.set h results
return { true results }
# Execute body function F, collect lookups and add them into the GSUB # Execute body function F, collect lookups and add them into the GSUB
foreach [ { featureTag groups } : pairs-of plm] : begin foreach [ { featureTag groups } : pairs-of plm] : begin
define feature : features.get featureTag define feature : features.get featureTag
define { fUnique lookupsToPush } : deDupe define { fUnique lookups } : deDupe : DoFeatureLigGroup gsub feature groups F
DoFeatureLigGroup sink feature groups F foreach lookup [items-of lookups] : feature.addLookup lookup
# Set feature lookups define [DoFeatureLigGroup gsub feature groups F] : begin
foreach ln [lookupsToPush.keys] : feature.lookups.push ln
if fUnique : begin
define rec : BeginLookupBlock sink
# Add lookups into the sink
foreach { ln obj } lookupsToPush : begin
if sink.lookups.(ln) : throw : new Error "Lookup name conflict \(ln)"
set sink.lookups.(ln) obj
# Set in-group priority
local lastLookupInGroup null
foreach ln [lookupsToPush.keys] : begin
if lastLookupInGroup : sink.lookupDep.push { lastLookupInGroup ln }
set lastLookupInGroup ln
EndLookupBlock rec sink
define [DoFeatureLigGroup sink feature groups F] : begin
define [hasLG ln] : [groups.indexOf ln] >= 0 define [hasLG ln] : [groups.indexOf ln] >= 0
define lookupNamePrefix : 'lig_' + feature.tag + '_' define lookupNamePrefix : 'lig_' + feature.tag + '_'
define lookupsToPush : new Map define lookupsToPush {}
define [AddLookup obj] : begin
lookupsToPush.set (lookupNamePrefix + (feature.lookups.length + lookupsToPush.size)) obj
define [filterNulls _rules] : begin define [filterNulls _rules] : begin
if [not _rules] : return _rules if [not _rules] : return _rules
@ -77,13 +66,13 @@ define [DoFeatureLigGroup sink feature groups F] : begin
define [CreateLigationLookup _rules] : begin define [CreateLigationLookup _rules] : begin
define rules : filterNulls _rules define rules : filterNulls _rules
if (rules && rules.length) : AddLookup if (rules && rules.length) : lookupsToPush.push
.type 'gsub_chaining' .type 'gsub_chaining'
.rules rules .rules rules
define [CreateReverseLigationLookup _rules] : begin define [CreateReverseLigationLookup _rules] : begin
define rules : filterNulls _rules define rules : filterNulls _rules
if (rules && rules.length) : AddLookup if (rules && rules.length) : lookupsToPush.push
.type 'gsub_reverse' .type 'gsub_reverse'
.rules rules .rules rules
@ -99,8 +88,8 @@ define-macro LigGroup : syntax-rules
################################################################################################### ###################################################################################################
################################################################################################### ###################################################################################################
define [buildLigationsImpl sink para $LigGroup$] : begin define [buildLigationsImpl gsub para $LigGroup$] : begin
define { chain-rule reverse-rule } : ChainRuleBuilder sink define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
define less {'less'} define less {'less'}
define lessAndEquiv {'less' 'less.lig.shift0' 'less.lig.shift0.anti'} define lessAndEquiv {'less' 'less.lig.shift0' 'less.lig.shift0.anti'}

View file

@ -1,27 +1,26 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [CopyLanguage AddFeature AddLookup BeginLookupBlock EndLookupBlock ChainRuleBuilder] from"./table-util.mjs"
import [CvDecompose] from"../support/gr.mjs" import [CvDecompose] from"../support/gr.mjs"
extern Set extern Set
export : define [buildLOCL sink para glyphStore] : begin export : define [buildLOCL gsub para glyphStore] : begin
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
define { chain-rule } : ChainRuleBuilder sink define { chain-rule } : gsub.ChainRuleBuilder
define cyrlSRB : CopyLanguage sink 'cyrl_SRB ' 'cyrl_DFLT' define cyrlSRB : gsub.copyLanguage 'cyrl_SRB ' 'cyrl_DFLT'
define cyrlMKD : CopyLanguage sink 'cyrl_MKD ' 'cyrl_DFLT' define cyrlMKD : gsub.copyLanguage 'cyrl_MKD ' 'cyrl_DFLT'
define cyrlBGR : CopyLanguage sink 'cyrl_BGR ' 'cyrl_DFLT' define cyrlBGR : gsub.copyLanguage 'cyrl_BGR ' 'cyrl_DFLT'
define latnVIT : CopyLanguage sink 'latn_VIT ' 'latn_DFLT' define latnVIT : gsub.copyLanguage 'latn_VIT ' 'latn_DFLT'
# SRB # SRB
define loclSRB : AddFeature sink 'locl' define loclSRB : gsub.createFeature 'locl'
cyrlSRB.features.unshift loclSRB.name cyrlSRB.addFeature loclSRB
cyrlMKD.features.unshift loclSRB.name cyrlMKD.addFeature loclSRB
loclSRB.lookups.push : AddLookup sink : object loclSRB.addLookup : gsub.createLookup
type 'gsub_single' .type 'gsub_single'
substitutions : if para.isItalic .substitutions : if para.isItalic
object object
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB' 'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.SRB' 'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.SRB'
@ -33,11 +32,10 @@ export : define [buildLOCL sink para glyphStore] : begin
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB' 'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
# BGR # BGR
define loclBGR : AddFeature sink 'locl' define loclBGR : cyrlBGR.addFeature : gsub.createFeature 'locl'
cyrlBGR.features.unshift loclBGR.name loclBGR.addLookup : gsub.createLookup
loclBGR.lookups.push : AddLookup sink : object .type 'gsub_single'
type 'gsub_single' .substitutions : object
substitutions : object
'cyrl/ve' : glyphStore.ensureExists 'cyrl/ve.BGR' 'cyrl/ve' : glyphStore.ensureExists 'cyrl/ve.BGR'
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.italic' 'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.italic'
'cyrl/De' : glyphStore.ensureExists 'cyrl/De.BGR' 'cyrl/De' : glyphStore.ensureExists 'cyrl/De.BGR'
@ -64,15 +62,14 @@ export : define [buildLOCL sink para glyphStore] : begin
'cyrl/yeri' : glyphStore.ensureExists 'cyrl/yeri.BGR' 'cyrl/yeri' : glyphStore.ensureExists 'cyrl/yeri.BGR'
# VIT # VIT
define loclVIT : AddFeature sink 'locl' define loclVIT : latnVIT.addFeature : gsub.createFeature 'locl'
latnVIT.features.unshift loclVIT.name
define [sx s] : lambda [t] : t.map : lambda [x] "\(x)/\(s)" define [sx s] : lambda [t] : t.map : lambda [x] "\(x)/\(s)"
do "Decompose Vietnamese glyphs" do "Decompose Vietnamese glyphs"
local decompositionSubstitutions {.} local decompositionSubstitutions {.}
loclVIT.lookups.push : AddLookup sink loclVIT.addLookup : gsub.createLookup
.type 'gsub_multiple' .type 'gsub_multiple'
.substitutions decompositionSubstitutions .substitutions decompositionSubstitutions
@ -87,7 +84,7 @@ export : define [buildLOCL sink para glyphStore] : begin
local decomp : CvDecompose.get g local decomp : CvDecompose.get g
set decompositionSubstitutions.(gn) decomp set decompositionSubstitutions.(gn) decomp
loclVIT.lookups.push : AddLookup sink loclVIT.addLookup : gsub.createLookup
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule chain-rule
@ -99,4 +96,4 @@ export : define [buildLOCL sink para glyphStore] : begin
{'breveAbove'} ~> nothing {'breveAbove'} ~> nothing
viShiftableMarks ~> [sx 'viCenter'] viShiftableMarks ~> [sx 'viCenter']
EndLookupBlock.Front rec sink gsub.endBlockAtFront rec

View file

@ -1,12 +1,12 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [AddCommonFeature AddFeature AddLookup ChainRuleBuilder QueryRelatedGlyphs BeginLookupBlock EndLookupBlock] from"./table-util.mjs" import [QueryRelatedGlyphs] from"./table-util.mjs"
export : define [buildGsubThousands sink para] : begin export : define [buildGsubThousands gsub para] : begin
local rec : BeginLookupBlock sink local rec : gsub.beginBlock
define Thousand : AddFeature sink 'THND' define Thousand : gsub.addCommonFeature : gsub.createFeature 'THND'
define {chain-rule reverse-rule} : ChainRuleBuilder sink define {chain-rule reverse-rule} : gsub.ChainRuleBuilder
define numberGlyphIDs { define numberGlyphIDs {
'zero.lnum' 'one.lnum' 'two.lnum' 'three.lnum' 'four.lnum' 'zero.lnum' 'one.lnum' 'two.lnum' 'three.lnum' 'four.lnum'
'five.lnum' 'six.lnum' 'seven.lnum' 'eight.lnum' 'nine.lnum' 'five.lnum' 'six.lnum' 'seven.lnum' 'eight.lnum' 'nine.lnum'
@ -14,7 +14,7 @@ export : define [buildGsubThousands sink para] : begin
define [nd s] : numberGlyphIDs.map : lambda [x] "\(x).nd\(s)" define [nd s] : numberGlyphIDs.map : lambda [x] "\(x).nd\(s)"
define lookupThousand1 : AddLookup sink : object define a : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule ({'period'} ~> null) (numberGlyphIDs ~> [nd 2]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) chain-rule ({'period'} ~> null) (numberGlyphIDs ~> [nd 2]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
@ -25,13 +25,13 @@ export : define [buildGsubThousands sink para] : begin
chain-rule ([nd 4] ~> null) (numberGlyphIDs ~> [nd 3]) chain-rule ([nd 4] ~> null) (numberGlyphIDs ~> [nd 3])
chain-rule ([nd 3] ~> null) (numberGlyphIDs ~> [nd 2]) chain-rule ([nd 3] ~> null) (numberGlyphIDs ~> [nd 2])
define lookupThousand2 : AddLookup sink : object define b : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_chaining' .type 'gsub_chaining'
.rules : list .rules : list
chain-rule (numberGlyphIDs ~> [nd 0]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) chain-rule (numberGlyphIDs ~> [nd 0]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
chain-rule ([nd 0] ~> null) (numberGlyphIDs ~> [nd 0]) chain-rule ([nd 0] ~> null) (numberGlyphIDs ~> [nd 0])
define lookupThousand3 : AddLookup sink : object define c : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_reverse' .type 'gsub_reverse'
.rules : list .rules : list
reverse-rule ([nd 0] ~> [nd 1]) ([nd 0] ~> null) reverse-rule ([nd 0] ~> [nd 1]) ([nd 0] ~> null)
@ -42,7 +42,7 @@ export : define [buildGsubThousands sink para] : begin
reverse-rule ([nd 0] ~> [nd 6]) ([nd 5] ~> null) reverse-rule ([nd 0] ~> [nd 6]) ([nd 5] ~> null)
reverse-rule ([nd 0] ~> [nd 1]) ([nd 6] ~> null) reverse-rule ([nd 0] ~> [nd 1]) ([nd 6] ~> null)
gsub.setDependency a b
gsub.setDependency b c
Thousand.lookups.push lookupThousand1 lookupThousand2 lookupThousand3 gsub.endBlock rec
AddCommonFeature sink Thousand
EndLookupBlock rec sink

View file

@ -1,7 +1,7 @@
import as toposort from 'toposort' import as toposort from 'toposort'
import as Gr from"../support/gr.mjs" import as Gr from"../support/gr.mjs"
import [CreateEmptyTable FinalizeTable MoveBackUtilityLookups] from"./table-util.mjs" import [CreateEmptyTable] from"./table-util.mjs"
import [buildLigations] from"./gsub-ligation.mjs" import [buildLigations] from"./gsub-ligation.mjs"
import [buildCCMP buildCCMPPostCvSs] from"./gsub-ccmp.mjs" import [buildCCMP buildCCMPPostCvSs] from"./gsub-ccmp.mjs"
@ -70,15 +70,14 @@ define [buildGSUB para glyphStore markGlyphs] : begin
# Builds last, but the lookups are added into the beginning of the lookup list # Builds last, but the lookups are added into the beginning of the lookup list
buildLOCL gsub para glyphStore buildLOCL gsub para glyphStore
MoveBackUtilityLookups gsub gsub.finalize
FinalizeTable gsub
return gsub return gsub
# GPOS # GPOS
define [buildGPOS para glyphStore markGlyphs] : begin define [buildGPOS para glyphStore markGlyphs] : begin
define gpos : CreateEmptyTable define gpos : CreateEmptyTable
buildMarkMkmk gpos glyphStore markGlyphs buildMarkMkmk gpos glyphStore markGlyphs
FinalizeTable gpos gpos.finalize
return gpos return gpos
# GDEF # GDEF

View file

@ -1,133 +1,137 @@
import toposort from 'toposort' import toposort from 'toposort'
import [AnyCv] from"../support/gr.mjs" import [AnyCv] from"../support/gr.mjs"
export : define [CreateEmptyTable] {.languages {.} .features {.} .lookups {.} .lookupDep {}}
extern Map extern Map
extern Set extern Set
export : define [PickLanguage sink tag] : begin export : define [CreateEmptyTable] : new LayoutTable
if sink.languages.(tag) : return sink.languages.(tag)
define lang {.features {}} class LayoutTable
set sink.languages.(tag) lang public [new] : begin
set this.languages {.}
set this.features {.}
set this.lookups {.}
set this.lookupDep {}
set this.lookupOrder {}
public [pickLanguage tag] : begin
if this.languages.(tag) : return this.languages.(tag)
define lang : new LayoutLanguage tag
set this.languages.(tag) lang
return lang return lang
export : define [CopyLanguage sink tag tagFrom] : begin public [copyLanguage tag tagFrom] : begin
define langFrom : PickLanguage sink tagFrom define langFrom : this.pickLanguage tagFrom
define langTo : PickLanguage sink tag define langTo : this.pickLanguage tag
foreach [feat : items-of langFrom.features] : langTo.features.push feat foreach [feat : items-of langFrom.features] : langTo.features.push feat
return langTo return langTo
export : define [AddLangFeature lang fea] : begin public [createFeature tag] : begin
define index : lang.features.indexOf fea.name local feature : new LayoutFeature tag
if (index < 0) : lang.features.push fea.name
export : define [AddFeature sink tag] : begin
define lookupArray {}
local n 0 local n 0
while true : begin while true : begin
if [not sink.features.(tag + '_' + n)] : begin if [not this.features.(tag + '_' + n)] : begin
set sink.features.(tag + '_' + n) lookupArray set feature.name : tag + '_' + n
return {.tag tag .name (tag + '_' + n) .lookups lookupArray} set this.features.(tag + '_' + n) feature
return feature
set n : n + 1 set n : n + 1
export : define [PickFeature sink name] : begin public [addCommonFeature fea] : begin
if sink.features.(name) : return { .name name .lookups sink.features.(name) } define dfltDflt : this.pickLanguage 'DFLT_DFLT'
define featObj { .name name .lookups {} } define latnDflt : this.pickLanguage 'latn_DFLT'
set sink.features.(name) featObj.lookups define grekDflt : this.pickLanguage 'grek_DFLT'
return featObj define cyrlDflt : this.pickLanguage 'cyrl_DFLT'
export : define [PickCommonFeature sink name] : begin dfltDflt.addFeature fea
if sink.features.(name) : return { .name name .lookups sink.features.(name) } latnDflt.addFeature fea
define featObj { .name name .lookups {} } grekDflt.addFeature fea
set sink.features.(name) featObj.lookups cyrlDflt.addFeature fea
AddCommonFeature sink featObj
return featObj
export : define [AddFeatureLookup fea lookupName] : begin
define index : fea.lookups.indexOf lookupName
if (index < 0) : fea.lookups.push lookupName
export : define [AddLookup sink data _prefix] : begin
local prefix : _prefix || '_lut_'
local n 0
while true : begin
if [not sink.lookups.(prefix + n)] : begin
set sink.lookups.(prefix + n) data
return (prefix + n)
set n : n + 1
export : define [PickLookup sink name fallback] : begin
if sink.lookups.(name) : return sink.lookups.(name)
set sink.lookups.(name) fallback
return sink.lookups.(name)
export : define [AddCommonFeature sink fea] : begin
define dfltDflt : PickLanguage sink 'DFLT_DFLT'
define latnDflt : PickLanguage sink 'latn_DFLT'
define grekDflt : PickLanguage sink 'grek_DFLT'
define cyrlDflt : PickLanguage sink 'cyrl_DFLT'
AddLangFeature dfltDflt fea
AddLangFeature latnDflt fea
AddLangFeature grekDflt fea
AddLangFeature cyrlDflt fea
return fea return fea
define UtilityLookupPrefix '.utility-single.' public [createLookup data _prefix] : begin
local prefix : _prefix || 'lookup/'
local n 0
while true : begin
if [not this.lookups.(prefix + n)] : begin
return : this.addLookupNoCheck (prefix + n) data
set n : n + 1
export : define [BeginLookupBlock sink] : begin public [pickLookup name fallback] : begin
if this.lookups.(name) : return this.lookups.(name)
return : this.addLookupNoCheck name fallback
public [addLookupNoCheck name data] : begin
local lookup : Object.fromEntries : Object.entries data
set lookup.name name
set this.lookups.(name) lookup
return lookup
public [setDependency a b] : begin
if [not a.name] : throw : new Error "Invalid lookup"
if [not b.name] : throw : new Error "Invalid lookup"
this.lookupDep.push { a.name b.name }
public [beginBlock] : begin
return : object return : object
existingLookupNames : new Set : Object.keys sink.lookups existingLookupNames : new Set : Object.keys this.lookups
define [IsUtilityLookupId name] : [name.slice 0 UtilityLookupPrefix.length] === UtilityLookupPrefix public [endBlock rec] : begin
local currentLookupNames : new Set : Object.keys this.lookups
export : define [EndLookupBlock rec sink] : begin
local currentLookupNames : new Set : Object.keys sink.lookups
foreach existing rec.existingLookupNames : foreach current currentLookupNames foreach existing rec.existingLookupNames : foreach current currentLookupNames
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current]) if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
sink.lookupDep.push { existing current } this.lookupDep.push { existing current }
set EndLookupBlock.Front : lambda [rec sink] : begin public [endBlockAtFront rec] : begin
local currentLookupNames : new Set : Object.keys sink.lookups local currentLookupNames : new Set : Object.keys this.lookups
foreach existing rec.existingLookupNames : foreach current currentLookupNames foreach existing rec.existingLookupNames : foreach current currentLookupNames
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current]) if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
sink.lookupDep.push { current existing } this.lookupDep.push { current existing }
export : define [MoveBackUtilityLookups sink] : begin public [finalize] : begin
local lns : new Set : Object.keys sink.lookups local lns : new Set : Object.keys this.lookups
foreach [front lns] : foreach [rear lns] foreach [front lns] : foreach [rear lns]
if (![IsUtilityLookupId front] && [IsUtilityLookupId rear]) if (![IsUtilityLookupId front] && [IsUtilityLookupId rear])
sink.lookupDep.push { front rear } this.lookupDep.push { front rear }
set this.lookupOrder : toposort this.lookupDep
foreach [{key lang} : pairs-of this.languages] : begin
if lang.features : lang.features.sort
public [ChainRuleBuilder] : begin
local table this
export : define [ChainRuleBuilder sink] : begin
define [createNewLookup f t] : begin define [createNewLookup f t] : begin
local subst {.} local subst {.}
foreach [j : range 0 f.length] : set subst.(f.(j)) t.(j) foreach [j : range 0 f.length] : set subst.(f.(j)) t.(j)
return : AddLookup sink {.type 'gsub_single' .substitutions subst} UtilityLookupPrefix return : table.createLookup {.type 'gsub_single' .substitutions subst} UtilityLookupPrefix
define [getSubLookup left right] : piecewise define [getSubLookup left right] : piecewise
[not right] null [not right] null
([typeof right] === "string") right ([typeof right] === "string") : throw : new Error "Invalid substitution"
(right <@ Function) : getSubLookup left [right left] (right <@ Function) : getSubLookup left [right left]
true : begin true : begin
local found null local found null
local maxMatch 0 local maxMatch 0
local lookupKeys : [Object.keys sink.lookups].reverse
foreach [name : items-of lookupKeys] : begin local lookupKeys : [Object.keys table.lookups].reverse
local st sink.lookups.(name).substitutions foreach [name : items-of lookupKeys] : if [IsUtilityLookupId name] : begin
if [IsUtilityLookupId name] : begin local lookup table.lookups.(name)
local st lookup.substitutions
local compatible true local compatible true
local matchCount 0 local matchCount 0
foreach [j : range 0 left.length] : begin foreach [j : range 0 left.length] : begin
if (st.(left.(j)) && st.(left.(j)) !== right.(j)) : set compatible false if (st.(left.(j)) && st.(left.(j)) !== right.(j)) : set compatible false
if (st.(left.(j)) === right.(j)) : inc matchCount if (st.(left.(j)) === right.(j)) : inc matchCount
if (compatible && (!found || matchCount > maxMatch)) : begin if (compatible && (!found || matchCount > maxMatch)) : begin
set found name set found lookup
set maxMatch matchCount set maxMatch matchCount
if found : begin if found : begin
local st sink.lookups.(found).substitutions local st found.substitutions
foreach [j : range 0 left.length] : set st.(left.(j)) right.(j) foreach [j : range 0 left.length] : set st.(left.(j)) right.(j)
return found return found
@ -147,8 +151,8 @@ export : define [ChainRuleBuilder sink] : begin
foreach [j : range 0 terms.length] : begin foreach [j : range 0 terms.length] : begin
local term terms.(j) local term terms.(j)
rule.match.push : Array.from : new Set term.left rule.match.push : Array.from : new Set term.left
local lutn : getSubLookup term.left term.right local lookup : getSubLookup term.left term.right
if lutn : rule.apply.push {.at j .lookup lutn} if lookup : rule.apply.push {.at j .lookup {.name lookup.name}}
return rule return rule
define [reverse-rule] : begin define [reverse-rule] : begin
@ -179,6 +183,32 @@ export : define [ChainRuleBuilder sink] : begin
return {chain-rule reverse-rule} return {chain-rule reverse-rule}
class LayoutFeature
public [new tag] : begin
set this.tag tag
set this.name tag
set this.lookups {}
public [addLookup lookup] : begin
if [not lookup.name] : throw : new Error "Invalid lookup"
define index : this.lookups.indexOf lookup.name
if (index < 0) : this.lookups.push lookup.name
return lookup
class LayoutLanguage
public [new tag] : begin
set this.tag tag
set this.features {}
public [addFeature feature] : begin
if [not feature.name] : throw : new Error "Invalid feature"
define index : this.features.indexOf feature.name
if (index < 0) : this.features.push feature.name
return feature
define UtilityLookupPrefix '.utility-single/'
define [IsUtilityLookupId name] : [name.slice 0 UtilityLookupPrefix.length] === UtilityLookupPrefix
export : define [QueryRelatedGlyphs glyphs para entries] : begin export : define [QueryRelatedGlyphs glyphs para entries] : begin
define sink {} define sink {}
foreach [gid : items-of entries] : if glyphs.(gid) : begin foreach [gid : items-of entries] : if glyphs.(gid) : begin

View file

@ -189,6 +189,14 @@ export function Cv(tag, rank, groupRank, description) {
return rel; return rel;
} }
Cv.compare = function (a, b) {
if (a.tag < b.tag) return -1;
if (a.tag > b.tag) return 1;
if (a.rank < b.rank) return -1;
if (a.rank > b.rank) return 1;
return 0;
};
export const DotlessOrNot = { export const DotlessOrNot = {
query(glyph) { query(glyph) {
if (Dotless.get(glyph)) return [Dotless]; if (Dotless.get(glyph)) return [Dotless];
@ -265,11 +273,13 @@ function getGrTreeImpl(gid, grSetList, fnGidToGlyph, sink) {
export function getGrMesh(gidList, grq, fnGidToGlyph) { export function getGrMesh(gidList, grq, fnGidToGlyph) {
if (typeof gidList === "string" || !Array.isArray(gidList)) if (typeof gidList === "string" || !Array.isArray(gidList))
throw new TypeError(`glyphs must be a glyph array!`); throw new TypeError(`glyphs must be a glyph array!`);
const allGrSet = new Set(); const allGrSet = new Set();
for (const g of gidList) { for (const g of gidList) {
for (const gr of grq.query(fnGidToGlyph(g))) allGrSet.add(gr); for (const gr of grq.query(fnGidToGlyph(g))) allGrSet.add(gr);
} }
const allGrList = Array.from(allGrSet); const allGrList = Array.from(allGrSet).sort(Cv.compare).reverse();
let ret = []; let ret = [];
for (const gr of allGrList) { for (const gr of allGrList) {
const col = []; const col = [];