Move around files so the repo will be organized as a monorepo.
This commit is contained in:
parent
65d1880a84
commit
08c69f0fd3
365 changed files with 1477 additions and 1262 deletions
94
packages/font-otl/src/gpos-mark-mkmk.ptl
Normal file
94
packages/font-otl/src/gpos-mark-mkmk.ptl
Normal file
|
@ -0,0 +1,94 @@
|
|||
extern Map
|
||||
extern Set
|
||||
|
||||
define MarkClasses {
|
||||
'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos' 'aboveBraceL' 'aboveBraceR'
|
||||
'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf' 'belowBraceL' 'belowBraceR'
|
||||
|
||||
'overlay' 'slash' 'strike'
|
||||
'cvDecompose' 'enclosureInner' 'enclosureInnerFirstHalf' 'enclosureInnerSecondHalf'
|
||||
'fracBuildUp'
|
||||
}
|
||||
|
||||
define MarkInteractions : new Map : list
|
||||
list 'aboveBraceL' {'aboveBraceL' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'}
|
||||
list 'aboveBraceR' {'aboveBraceR' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'}
|
||||
list 'belowBraceL' {'belowBraceL' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'}
|
||||
list 'belowBraceR' {'belowBraceR' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'}
|
||||
|
||||
|
||||
define MkmkStackingLimit : new Set { 'above' 'below' }
|
||||
|
||||
export : define [buildMarkMkmk gpos glyphStore markGlyphs] : begin
|
||||
define validMarkClasses : new Set MarkClasses
|
||||
|
||||
define mark : object
|
||||
feature : gpos.addCommonFeature : gpos.createFeature 'mark'
|
||||
lookupMap : new Map
|
||||
createLookup : function [] {.type 'gpos_mark_to_base' .marks {.} .bases {.}}
|
||||
define mkmk : object
|
||||
feature : gpos.addCommonFeature : gpos.createFeature 'mkmk'
|
||||
lookupMap : new Map
|
||||
createLookup : function [] {.type 'gpos_mark_to_mark' .marks {.} .bases {.}}
|
||||
|
||||
# Populate the marks
|
||||
foreach cls [items-of MarkClasses] : begin
|
||||
local markLookup : ensureLookup gpos mark cls
|
||||
local mkmkLookup : ensureLookup gpos mkmk cls
|
||||
|
||||
foreach { gn glyph } [glyphStore.namedEntries] : begin
|
||||
if glyph.markAnchors.(cls) : begin
|
||||
markGlyphs.all.add gn
|
||||
markGlyphs.markAttachClassDef.set gn cls
|
||||
addMarkAnchor markLookup gn cls glyph.markAnchors.(cls)
|
||||
addMarkAnchor mkmkLookup gn cls glyph.markAnchors.(cls)
|
||||
|
||||
# Populate the bases
|
||||
foreach cls [items-of MarkClasses] : begin
|
||||
local markLookup : ensureLookup gpos mark cls
|
||||
local mkmkLookup : ensureLookup gpos mkmk cls
|
||||
|
||||
foreach { gn glyph } [glyphStore.namedEntries] : begin
|
||||
if glyph.baseAnchors.(cls) : begin
|
||||
if [markGlyphs.all.has gn]
|
||||
: then : addBaseAnchor mkmkLookup gn cls glyph.baseAnchors.(cls)
|
||||
: else : addBaseAnchor markLookup gn cls glyph.baseAnchors.(cls)
|
||||
|
||||
foreach markLookup [mark.lookupMap.values] : foreach mkmkLookup [mkmk.lookupMap.values]
|
||||
gpos.setDependency markLookup mkmkLookup
|
||||
|
||||
foreach { cls lookup } mkmk.lookupMap : begin
|
||||
local interactionMarkSet : new Set ([MarkInteractions.get cls] || { cls })
|
||||
|
||||
local includeSet : new Set
|
||||
local ignoreSet : new Set
|
||||
foreach { gn c } markGlyphs.markAttachClassDef : if [interactionMarkSet.has c]
|
||||
then : includeSet.add gn
|
||||
else : ignoreSet.add gn
|
||||
|
||||
set lookup.ignoreGlyphs : Array.from ignoreSet
|
||||
if (interactionMarkSet.size > 1) : begin
|
||||
markGlyphs.markGlyphSets.push : Array.from includeSet
|
||||
|
||||
define [ensureLookup gpos feat cls] : begin
|
||||
local existing : feat.lookupMap.get cls
|
||||
if existing : return existing
|
||||
|
||||
local lookup : gpos.createLookup : feat.createLookup
|
||||
feat.feature.addLookup lookup
|
||||
feat.lookupMap.set cls lookup
|
||||
return lookup
|
||||
|
||||
define [addMarkAnchor lookup gn cls anchor] : begin
|
||||
local a : object
|
||||
class cls
|
||||
x anchor.x
|
||||
y anchor.y
|
||||
set lookup.marks.(gn) a
|
||||
|
||||
define [addBaseAnchor lookup gn cls anchor] : begin
|
||||
local a : object
|
||||
x anchor.x
|
||||
y anchor.y
|
||||
if [not lookup.bases.(gn)] : set lookup.bases.(gn) {.}
|
||||
set lookup.bases.(gn).(cls) a
|
333
packages/font-otl/src/gsub-ccmp.ptl
Normal file
333
packages/font-otl/src/gsub-ccmp.ptl
Normal file
|
@ -0,0 +1,333 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
import [UkMapToLookup UkMap2ToLookup] from "./table-util.mjs"
|
||||
import [Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript LeaningMark LeaningMarkSpacer] from "@iosevka/glyph/relation"
|
||||
import as UnicodeKnowledge from "@iosevka/font-glyphs/unicode-knowledge"
|
||||
|
||||
extern Set
|
||||
|
||||
define-macro Ccmp-Group : syntax-rules
|
||||
`[Ccmp-Group @description @body]
|
||||
dirty `[$ExecCcmpGroup$ [function [export-lookup chain-rule] @[formOf body]]]
|
||||
|
||||
export : define [buildCCMP gsub glyphStore markGlyphs] : begin
|
||||
local anyMark : Array.from markGlyphs.all
|
||||
local aboveMark : filterMarkByClass markGlyphs 'above'
|
||||
|
||||
define ccmp : gsub.addCommonFeature : gsub.createFeature 'ccmp'
|
||||
|
||||
define [$ExecCcmpGroup$ fn] : begin
|
||||
local addedLookups {}
|
||||
define [export-lookup lookup] : begin
|
||||
ccmp.addLookup lookup
|
||||
addedLookups.push lookup
|
||||
|
||||
|
||||
local rec : gsub.beginBlock
|
||||
define {chain-rule} : gsub.ChainRuleBuilder
|
||||
|
||||
fn export-lookup chain-rule
|
||||
|
||||
for [local j 1] (j < addedLookups.length) [inc j] : begin
|
||||
gsub.setDependency addedLookups.(j - 1) addedLookups.(j)
|
||||
|
||||
gsub.endBlock rec
|
||||
|
||||
Ccmp-Group "Mark transforms" : begin
|
||||
|
||||
define groupGrekUpperTonos {}
|
||||
define groupLF {}
|
||||
define dotlessFrom {}
|
||||
define dotlessTo {}
|
||||
|
||||
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== "."): begin
|
||||
if g.baseAnchors.lf : groupLF.push gn
|
||||
if g.baseAnchors.grekUpperTonos : groupGrekUpperTonos.push gn
|
||||
if [Dotless.get g] : begin
|
||||
dotlessFrom.push gn
|
||||
dotlessTo.push [Dotless.get g]
|
||||
|
||||
define [IotaLF] : UkMapToLookup UnicodeKnowledge.iotaBelowToLfTf
|
||||
define [GrekUpperTonosTf] : UkMapToLookup UnicodeKnowledge.upperGrekMarkToTonosTf
|
||||
|
||||
# Dotless transform
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
|
||||
.rules : list
|
||||
chain-rule (dotlessFrom ~> dotlessTo) (aboveMark ~> null)
|
||||
chain-rule groupGrekUpperTonos [GrekUpperTonosTf]
|
||||
|
||||
# Iota transform
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
|
||||
.rules : list
|
||||
chain-rule groupLF [IotaLF]
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_ligature'
|
||||
.substitutions : UkMap2ToLookup UnicodeKnowledge.markCompositionTf
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_multiple'
|
||||
.substitutions : object
|
||||
'parenAbove' { 'leftParenAbove' 'rightParenAbove' }
|
||||
'doubleParenAbove' { 'leftDoubleParenAbove' 'rightDoubleParenAbove' }
|
||||
'brackAbove' { 'leftBrackAbove' 'rightBrackAbove' }
|
||||
'parenBelow' { 'leftParenBelow' 'rightParenBelow' }
|
||||
|
||||
Ccmp-Group "Tie Mark Transform" : begin
|
||||
define TieMarkFrom {}
|
||||
define TieMarkTo {}
|
||||
define TieGlyphs {}
|
||||
|
||||
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
|
||||
if [TieGlyph.get g] : TieGlyphs.push gid
|
||||
if [TieMark.get g] : begin
|
||||
TieMarkFrom.push gid
|
||||
TieMarkTo.push [TieMark.get g]
|
||||
|
||||
define lookupTieMarkLigature : gsub.createLookup : object
|
||||
.type 'gsub_ligature'
|
||||
.substitutions : {}.concat
|
||||
TieMarkFrom.map : lambda [gnFrom idx]
|
||||
object [from {'cgj' gnFrom}] [to TieMarkTo.(idx)]
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list : object
|
||||
match {[TieGlyphs.concat TieMarkTo] {'cgj'} TieMarkFrom}
|
||||
inputBegins 1
|
||||
inputEnds 3
|
||||
apply {{.at 1 .lookup lookupTieMarkLigature}}
|
||||
|
||||
Ccmp-Group "Special dot-above transformation" : begin
|
||||
# b-dot, d-dot, h-dot, k-dot
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_ligature'
|
||||
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
|
||||
.substitutions : list
|
||||
object [from : list "b" "dotAbove"] [to "bDot"]
|
||||
object [from : list "d" "dotAbove"] [to "dDot"]
|
||||
object [from : list "h" "dotAbove"] [to "hDot"]
|
||||
object [from : list "k" "dotAbove"] [to "kDot"]
|
||||
|
||||
Ccmp-Group "Leaning Mark Trasnform" : begin
|
||||
define LeaningAnchorMap : list
|
||||
list 'above' 'leaningAbove'
|
||||
list 'below' 'leaningBelow'
|
||||
|
||||
foreach { mkCenter mkLeaning } [items-of LeaningAnchorMap] : begin
|
||||
local basesToConsider {}
|
||||
local markFrom {}
|
||||
local markTo {}
|
||||
local markSpacer {}
|
||||
local splitMapping {}
|
||||
|
||||
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin
|
||||
if (![markGlyphs.all.has gn] && g.baseAnchors.(mkLeaning)) : basesToConsider.push gn
|
||||
if (g.markAnchors.(mkCenter) && [LeaningMark.get g] && [LeaningMarkSpacer.get g]) : begin
|
||||
markFrom.push gn
|
||||
markTo.push [LeaningMark.get g]
|
||||
markSpacer.push [LeaningMarkSpacer.get g]
|
||||
splitMapping.push { gn {[LeaningMarkSpacer.get g] [LeaningMark.get g]} }
|
||||
|
||||
define lookupTurnMarkIntoLeaningAndSpacer : gsub.createLookup : object
|
||||
.type 'gsub_multiple'
|
||||
.substitutions : Object.fromEntries splitMapping
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs [filterMarkByClassNegated markGlyphs mkCenter]
|
||||
.rules : list
|
||||
object
|
||||
.match { [basesToConsider.concat markSpacer] markFrom }
|
||||
.inputBegins 1
|
||||
.inputEnds 2
|
||||
.apply {{.at 1 .lookup lookupTurnMarkIntoLeaningAndSpacer}}
|
||||
|
||||
|
||||
|
||||
Ccmp-Group "Rhotic Hook Transform" : begin
|
||||
define superscripts {}
|
||||
define subscripts {}
|
||||
|
||||
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin
|
||||
if [IsSuperscript.get g] : superscripts.push gn
|
||||
if [IsSubscript.get g] : subscripts.push gn
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs anyMark
|
||||
.rules : list
|
||||
chain-rule superscripts ({"rhoticHook"} ~> {"rhoticHook/sup"})
|
||||
chain-rule subscripts ({"rhoticHook"} ~> {"rhoticHook/sub"})
|
||||
|
||||
Ccmp-Group "Tone Transform" : begin
|
||||
define [ToneToToneStart toneEnd] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneStart [range 4 downtill 0] : begin
|
||||
f.push ('tone' + toneStart)
|
||||
e.push ('toneStart' + toneStart + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
define [ToneToToneEnd toneStart] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('tone' + toneEnd)
|
||||
e.push ('toneEnd' + toneStart + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
define [ToneStartOrMidAt y]: begin
|
||||
local f {}
|
||||
foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneStart' + y + toneEnd)
|
||||
foreach toneStart [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneMid' + toneStart + y + toneEnd)
|
||||
return f
|
||||
|
||||
define [ToneStartToToneMid toneStart] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneMid [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneStart' + toneMid + toneEnd)
|
||||
e.push ('toneMid' + toneStart + toneMid + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
define [ToneSandhiToToneStart toneEnd] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneStart [range 4 downtill 0] : begin
|
||||
f.push ('toneSandhi' + toneStart)
|
||||
e.push ('toneSandhiStart' + toneStart + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
define [ToneSandhiToToneEnd toneStart] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneSandhi' + toneEnd)
|
||||
e.push ('toneSandhiEnd' + toneStart + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
define [ToneSandhiStartOrMidAt y]: begin
|
||||
local f {}
|
||||
foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneSandhiStart' + y + toneEnd)
|
||||
foreach toneStart [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneSandhiMid' + toneStart + y + toneEnd)
|
||||
return f
|
||||
|
||||
define [ToneSandhiStartToToneMid toneStart] : begin
|
||||
local f {}
|
||||
local e {}
|
||||
foreach toneMid [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
|
||||
f.push ('toneSandhiStart' + toneMid + toneEnd)
|
||||
e.push ('toneSandhiMid' + toneStart + toneMid + toneEnd)
|
||||
return (f ~> e)
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneToToneStart 0] {'tone0'}
|
||||
chain-rule [ToneToToneStart 1] {'tone1'}
|
||||
chain-rule [ToneToToneStart 2] {'tone2'}
|
||||
chain-rule [ToneToToneStart 3] {'tone3'}
|
||||
chain-rule [ToneToToneStart 4] {'tone4'}
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneStartOrMidAt 0] [ToneStartToToneMid 0]
|
||||
chain-rule [ToneStartOrMidAt 1] [ToneStartToToneMid 1]
|
||||
chain-rule [ToneStartOrMidAt 2] [ToneStartToToneMid 2]
|
||||
chain-rule [ToneStartOrMidAt 3] [ToneStartToToneMid 3]
|
||||
chain-rule [ToneStartOrMidAt 4] [ToneStartToToneMid 4]
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneStartOrMidAt 0] [ToneToToneEnd 0]
|
||||
chain-rule [ToneStartOrMidAt 1] [ToneToToneEnd 1]
|
||||
chain-rule [ToneStartOrMidAt 2] [ToneToToneEnd 2]
|
||||
chain-rule [ToneStartOrMidAt 3] [ToneToToneEnd 3]
|
||||
chain-rule [ToneStartOrMidAt 4] [ToneToToneEnd 4]
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneSandhiToToneStart 0] {'toneSandhi0'}
|
||||
chain-rule [ToneSandhiToToneStart 1] {'toneSandhi1'}
|
||||
chain-rule [ToneSandhiToToneStart 2] {'toneSandhi2'}
|
||||
chain-rule [ToneSandhiToToneStart 3] {'toneSandhi3'}
|
||||
chain-rule [ToneSandhiToToneStart 4] {'toneSandhi4'}
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiStartToToneMid 0]
|
||||
chain-rule [ToneSandhiStartOrMidAt 1] [ToneSandhiStartToToneMid 1]
|
||||
chain-rule [ToneSandhiStartOrMidAt 2] [ToneSandhiStartToToneMid 2]
|
||||
chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiStartToToneMid 3]
|
||||
chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiStartToToneMid 4]
|
||||
|
||||
export-lookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiToToneEnd 0]
|
||||
chain-rule [ToneSandhiStartOrMidAt 1] [ToneSandhiToToneEnd 1]
|
||||
chain-rule [ToneSandhiStartOrMidAt 2] [ToneSandhiToToneEnd 2]
|
||||
chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiToToneEnd 3]
|
||||
chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiToToneEnd 4]
|
||||
|
||||
return ccmp
|
||||
|
||||
export : define [buildCCMPPostCvSs gsub ccmp glyphStore markGlyphs] : begin
|
||||
local anyMark : Array.from markGlyphs.all
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
define triggerGlyphs_Normal { }
|
||||
define triggerGlyphs_Y { }
|
||||
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== "."): begin
|
||||
if g.baseAnchors.trailing : piecewise
|
||||
[OgonekTrY.get g] : triggerGlyphs_Y.push gid
|
||||
true : triggerGlyphs_Normal.push gid
|
||||
|
||||
define ogonekSpacerNormal : gsub.createLookup : object
|
||||
.type 'gsub_multiple'
|
||||
.substitutions UnicodeKnowledge.ogonekBelowToTRTf
|
||||
define ogonekSpacerY : gsub.createLookup : object
|
||||
.type 'gsub_single'
|
||||
.substitutions UnicodeKnowledge.ogonekBelowToTRTf_Y
|
||||
|
||||
ccmp.addLookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
|
||||
.rules : list
|
||||
object
|
||||
.match { triggerGlyphs_Y [Object.keys UnicodeKnowledge.ogonekBelowToTRTf_Y] }
|
||||
.inputBegins 1
|
||||
.inputEnds 2
|
||||
.apply {{.at 1 .lookup ogonekSpacerY}}
|
||||
object
|
||||
.match { triggerGlyphs_Normal [Object.keys UnicodeKnowledge.ogonekBelowToTRTf] }
|
||||
.inputBegins 1
|
||||
.inputEnds 2
|
||||
.apply {{.at 1 .lookup ogonekSpacerNormal}}
|
||||
|
||||
gsub.endBlock rec
|
||||
|
||||
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
|
||||
|
||||
define [filterMarkByClass markGlyphs c] : begin
|
||||
local marks {}
|
||||
foreach { gn m } markGlyphs.markAttachClassDef : if (m == c) : marks.push gn
|
||||
return marks
|
||||
|
||||
define [filterMarkByClassNegated markGlyphs c] : begin
|
||||
local marks {}
|
||||
foreach { gn m } markGlyphs.markAttachClassDef : if (m != c) : marks.push gn
|
||||
return marks
|
142
packages/font-otl/src/gsub-cv-ss.ptl
Normal file
142
packages/font-otl/src/gsub-cv-ss.ptl
Normal file
|
@ -0,0 +1,142 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
import [AnyCv CvDecompose] from "@iosevka/glyph/relation"
|
||||
|
||||
extern Map
|
||||
extern Set
|
||||
|
||||
define look-around null
|
||||
|
||||
class CvLookupManager
|
||||
public [new table tag feature] : begin
|
||||
set this.table table
|
||||
set this.tag tag
|
||||
set this.feature feature
|
||||
|
||||
set this.decompositionLookup null
|
||||
set this.altrenatesLookup null
|
||||
set this.singleSubstLookups {}
|
||||
|
||||
public [addDecompositionLookup lookup] : begin
|
||||
this.decompositionLookups.push lookup
|
||||
this.feature.addLookup lookup
|
||||
|
||||
public [createDecompositionSubst] : begin
|
||||
if this.decompositionLookup : return this.decompositionLookup
|
||||
|
||||
define lookupName : 'lookup_cv_decompose_' + this.tag
|
||||
define lookup : this.table.pickLookup lookupName {.type 'gsub_multiple' .substitutions {.}}
|
||||
|
||||
this.feature.addLookup lookup
|
||||
set this.decompositionLookup lookup
|
||||
return lookup
|
||||
|
||||
public [createAlternateSubst] : begin
|
||||
if this.altrenatesLookup : return this.altrenatesLookup
|
||||
|
||||
define lookupName : 'lookup_cv_alternates_' + this.tag
|
||||
define lookup : this.table.pickLookup lookupName {.type 'gsub_alternate' .substitutions {.}}
|
||||
|
||||
this.feature.addLookup lookup
|
||||
set this.altrenatesLookup lookup
|
||||
return lookup
|
||||
|
||||
public [createSingleSubstFor rank] : begin
|
||||
if this.singleSubstLookups.(rank) : return this.singleSubstLookups.(rank)
|
||||
|
||||
define lookupName : 'lookup_cv_single_' + this.tag + '_' + rank
|
||||
define lookup : this.table.pickLookup lookupName {.type 'gsub_single' .substitutions {.}}
|
||||
|
||||
set this.singleSubstLookups.(rank) lookup
|
||||
return lookup
|
||||
|
||||
public [linkDeps] : begin
|
||||
if this.decompositionLookup : begin
|
||||
this.table.setDependency this.decompositionLookup this.altrenatesLookup
|
||||
foreach lookup [items-of this.singleSubstLookups] : if lookup : begin
|
||||
this.table.setDependency this.altrenatesLookup lookup
|
||||
|
||||
public [linkCrossDeps other] : begin
|
||||
this.table.setDependency this.altrenatesLookup other.altrenatesLookup
|
||||
foreach lookup [items-of this.singleSubstLookups] : if lookup : begin
|
||||
this.table.setDependency lookup other.altrenatesLookup
|
||||
|
||||
static [compare a b] : begin
|
||||
if (a.tag < b.tag) : return (-1)
|
||||
if (a.tag > b.tag) : return 1
|
||||
return 0
|
||||
|
||||
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"
|
||||
foreach { gn glyph } [glyphStore.namedEntries] : begin
|
||||
local parts : CvDecompose.get glyph
|
||||
if parts : foreach part [items-of parts] : begin
|
||||
local gPart : glyphStore.queryByNameEnsured part
|
||||
foreach [gr : items-of : AnyCv.query gPart] : if gr.tag : begin
|
||||
local lookup : [cvs.get gr.tag].createDecompositionSubst
|
||||
if [not lookup.substitutions.(gn)] : set lookup.substitutions.(gn) parts
|
||||
|
||||
do "cvxx"
|
||||
local cvGrs {}
|
||||
foreach {name prime} para.variants.primes : foreach {vn variant} prime.variants : begin
|
||||
if (variant.tag && variant.rank) : cvGrs.push variant
|
||||
cvGrs.sort AnyCv.compare
|
||||
|
||||
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
|
||||
foreach gr [items-of cvGrs] : begin
|
||||
local subst : gr.get glyph
|
||||
if (subst && subst != gn) : begin
|
||||
local cvAlt : [cvs.get gr.tag].createAlternateSubst
|
||||
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { }
|
||||
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst
|
||||
|
||||
do "ssxx" : foreach {name composition} para.variants.composites : if composition.tag : begin
|
||||
define feature : gsub.addCommonFeature : gsub.createFeature composition.tag
|
||||
|
||||
define decomp : composition.decompose para para.variants.selectorTree
|
||||
local ssGrs {}
|
||||
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
|
||||
ssGrs.push pv
|
||||
local dl [cvs.get pv.tag].decompositionLookup
|
||||
if dl : feature.addLookup dl
|
||||
ssGrs.sort AnyCv.compare
|
||||
|
||||
foreach gr [items-of ssGrs] : begin
|
||||
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
|
||||
feature.addLookup cvSingle
|
||||
|
||||
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
|
||||
foreach gr [items-of ssGrs] : begin
|
||||
local subst : gr.get glyph
|
||||
if (subst && subst != gn) : begin
|
||||
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
|
||||
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst
|
||||
|
||||
do "Cleanup and link dependency"
|
||||
local sortedCvs : Array.from [cvs.values]
|
||||
sortedCvs.sort CvLookupManager.compare
|
||||
|
||||
foreach cv [items-of sortedCvs] : begin
|
||||
local st cv.altrenatesLookup.substitutions
|
||||
foreach { k v } [pairs-of st] : foreach idx [range 0 v.length] : if [not v.(idx)]
|
||||
set v.(idx) k
|
||||
cv.linkDeps
|
||||
|
||||
for [local i 1] (i < sortedCvs.length) [inc i]
|
||||
sortedCvs.(i - 1).linkCrossDeps sortedCvs.(i)
|
||||
|
||||
gsub.endBlock rec
|
||||
|
||||
return cvs
|
||||
|
||||
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
|
46
packages/font-otl/src/gsub-frac.ptl
Normal file
46
packages/font-otl/src/gsub-frac.ptl
Normal file
|
@ -0,0 +1,46 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
import [NumeratorForm DenominatorForm] from "@iosevka/glyph/relation"
|
||||
|
||||
# Name-driven feature pairs
|
||||
export : define [buildFrac gsub glyphStore] : begin
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
define frac : gsub.addCommonFeature : gsub.createFeature 'frac'
|
||||
define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
|
||||
|
||||
define subSolidus : gsub.createLookup : object
|
||||
.type 'gsub_single'
|
||||
.substitutions : object ['solidus' 'fractionBar'] ['slash' 'fractionBar']
|
||||
|
||||
define digitSet { }
|
||||
define numSet { }
|
||||
define denSet { }
|
||||
|
||||
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
|
||||
local numForm : NumeratorForm.get g
|
||||
local denForm : DenominatorForm.get g
|
||||
if (numForm && denForm) : begin
|
||||
digitSet.push gid
|
||||
numSet.push numForm
|
||||
denSet.push denForm
|
||||
|
||||
define subDen : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule [{'fractionBar'}.concat denSet] [digitSet ~> denSet]
|
||||
|
||||
define subNum : gsub.createLookup : object
|
||||
.type 'gsub_reverse'
|
||||
.rules : list
|
||||
reverse-rule [digitSet ~> numSet] [{'fractionBar'}.concat numSet]
|
||||
|
||||
frac.addLookup subSolidus
|
||||
frac.addLookup subDen
|
||||
frac.addLookup subNum
|
||||
gsub.setDependency subSolidus subDen
|
||||
gsub.setDependency subSolidus subNum
|
||||
|
||||
gsub.endBlock rec
|
||||
|
||||
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
|
19
packages/font-otl/src/gsub-gr.ptl
Normal file
19
packages/font-otl/src/gsub-gr.ptl
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Name-driven feature pairs
|
||||
export : define [buildGrFeature gsub glyphStore gr] : begin
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
local mapping {.}
|
||||
foreach { gnSrc glyph } [glyphStore.namedEntries] : begin
|
||||
local gnDst : gr.get glyph
|
||||
local glyphDst : glyphStore.queryByName gnDst
|
||||
if (glyphDst && ([glyphStore.queryUnicodeOf glyph] || [glyphStore.queryUnicodeOf glyphDst]))
|
||||
set mapping.(gnSrc) gnDst
|
||||
|
||||
if [objectIsNotEmpty mapping] : begin
|
||||
define lookup1 : gsub.createLookup {.type 'gsub_single' .substitutions mapping}
|
||||
define feature1 : gsub.addCommonFeature : gsub.createFeature gr.otlTag
|
||||
feature1.addLookup lookup1
|
||||
|
||||
gsub.endBlock rec
|
||||
|
||||
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
|
1113
packages/font-otl/src/gsub-ligation.ptl
Normal file
1113
packages/font-otl/src/gsub-ligation.ptl
Normal file
File diff suppressed because it is too large
Load diff
99
packages/font-otl/src/gsub-locl.ptl
Normal file
99
packages/font-otl/src/gsub-locl.ptl
Normal file
|
@ -0,0 +1,99 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
import [CvDecompose] from "@iosevka/glyph/relation"
|
||||
|
||||
extern Set
|
||||
|
||||
export : define [buildLOCL gsub para glyphStore] : begin
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
define { chain-rule } : gsub.ChainRuleBuilder
|
||||
|
||||
define cyrlSRB : gsub.copyLanguage 'cyrl_SRB ' 'cyrl_DFLT'
|
||||
define cyrlMKD : gsub.copyLanguage 'cyrl_MKD ' 'cyrl_DFLT'
|
||||
define cyrlBGR : gsub.copyLanguage 'cyrl_BGR ' 'cyrl_DFLT'
|
||||
define latnVIT : gsub.copyLanguage 'latn_VIT ' 'latn_DFLT'
|
||||
|
||||
# SRB
|
||||
define loclSRB : gsub.createFeature 'locl'
|
||||
cyrlSRB.addFeature loclSRB
|
||||
cyrlMKD.addFeature loclSRB
|
||||
loclSRB.addLookup : gsub.createLookup
|
||||
.type 'gsub_single'
|
||||
.substitutions : if para.isItalic
|
||||
object
|
||||
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
|
||||
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.SRB'
|
||||
'cyrl/de' : glyphStore.ensureExists 'cyrl/de.SRB'
|
||||
'cyrl/gje' : glyphStore.ensureExists 'cyrl/gje.SRB'
|
||||
'cyrl/pe' : glyphStore.ensureExists 'cyrl/pe.SRB'
|
||||
'cyrl/te' : glyphStore.ensureExists 'cyrl/te.SRB'
|
||||
object
|
||||
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
|
||||
|
||||
# BGR
|
||||
define loclBGR : cyrlBGR.addFeature : gsub.createFeature 'locl'
|
||||
loclBGR.addLookup : gsub.createLookup
|
||||
.type 'gsub_single'
|
||||
.substitutions : object
|
||||
'cyrl/ve' : glyphStore.ensureExists 'cyrl/ve.BGR'
|
||||
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.italic'
|
||||
'cyrl/De' : glyphStore.ensureExists 'cyrl/De.BGR'
|
||||
'cyrl/de' : glyphStore.ensureExists 'cyrl/de.BGR'
|
||||
'cyrl/zhe' : glyphStore.ensureExists 'cyrl/zhe.BGR'
|
||||
'cyrl/ze' : glyphStore.ensureExists 'cyrl/ze.BGR'
|
||||
'cyrl/i' : glyphStore.ensureExists 'cyrl/i.BGR'
|
||||
'cyrl/ibreve' : glyphStore.ensureExists 'cyrl/ibreve.BGR'
|
||||
'cyrl/igrave' : glyphStore.ensureExists 'cyrl/igrave.BGR'
|
||||
'cyrl/ka' : glyphStore.ensureExists 'cyrl/ka.BGR'
|
||||
'cyrl/El' : glyphStore.ensureExists 'cyrl/El.BGR'
|
||||
'cyrl/el' : glyphStore.ensureExists 'cyrl/el.BGR'
|
||||
'cyrl/en' : glyphStore.ensureExists 'cyrl/en.BGR'
|
||||
'cyrl/pe' : glyphStore.ensureExists 'cyrl/pe.BGR'
|
||||
'cyrl/te' : glyphStore.ensureExists 'cyrl/te.BGR'
|
||||
'cyrl/Ef' : glyphStore.ensureExists 'cyrl/Ef.BGR'
|
||||
'cyrl/ef' : glyphStore.ensureExists 'cyrl/ef.BGR'
|
||||
'cyrl/che' : glyphStore.ensureExists 'cyrl/che.BGR'
|
||||
'cyrl/sha' : glyphStore.ensureExists 'cyrl/sha.BGR'
|
||||
'cyrl/shcha' : glyphStore.ensureExists 'cyrl/shcha.BGR'
|
||||
'cyrl/yu' : glyphStore.ensureExists 'cyrl/yu.BGR'
|
||||
'cyrl/tse' : glyphStore.ensureExists 'cyrl/tse.BGR'
|
||||
'cyrl/yer' : glyphStore.ensureExists 'cyrl/yer.BGR'
|
||||
'cyrl/yeri' : glyphStore.ensureExists 'cyrl/yeri.BGR'
|
||||
|
||||
# VIT
|
||||
define loclVIT : latnVIT.addFeature : gsub.createFeature 'locl'
|
||||
|
||||
define [sx s] : lambda [t] : t.map : lambda [x] "\(x)/\(s)"
|
||||
|
||||
do "Decompose Vietnamese glyphs"
|
||||
|
||||
local decompositionSubstitutions {.}
|
||||
loclVIT.addLookup : gsub.createLookup
|
||||
.type 'gsub_multiple'
|
||||
.substitutions decompositionSubstitutions
|
||||
|
||||
local viCircumflex 'ẦẤẨỀẾỂỒỐỔầấẩềếểồốổ'
|
||||
local viBreve 'ẮẰẲằắẳ'
|
||||
local viShiftableMarks { 'acuteAbove' 'graveAbove' 'hookAbove' }
|
||||
|
||||
foreach ch (viCircumflex + viBreve) : begin
|
||||
local u : ch.codePointAt 0
|
||||
local gn : glyphStore.queryNameByUnicode u
|
||||
local g : glyphStore.queryByNameEnsured gn
|
||||
local decomp : CvDecompose.get g
|
||||
set decompositionSubstitutions.(gn) decomp
|
||||
|
||||
loclVIT.addLookup : gsub.createLookup
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule
|
||||
{'A' 'a' 'E' 'e' 'O' 'o'} ~> nothing
|
||||
{'circumflexAbove'} ~> nothing
|
||||
viShiftableMarks ~> [sx 'viSide']
|
||||
chain-rule
|
||||
{'A' 'a'} ~> nothing
|
||||
{'breveAbove'} ~> nothing
|
||||
viShiftableMarks ~> [sx 'viCenter']
|
||||
|
||||
gsub.endBlockAtFront rec
|
102
packages/font-otl/src/gsub-texture.ptl
Normal file
102
packages/font-otl/src/gsub-texture.ptl
Normal file
|
@ -0,0 +1,102 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
extern Set
|
||||
|
||||
import [Texture CvDecompose] from "@iosevka/glyph/relation"
|
||||
|
||||
# Name-driven feature pairs
|
||||
export : define [buildGsubTexture gsub glyphStore markGlyphs cvs] : begin
|
||||
local anyMark : Array.from markGlyphs.all
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
define txtr : gsub.addCommonFeature : gsub.createFeature 'TXTR'
|
||||
define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
|
||||
define look-around null
|
||||
|
||||
local extLFrom { }
|
||||
local extRFrom { }
|
||||
local extLRFrom { }
|
||||
local extLTo { }
|
||||
local extRTo { }
|
||||
local extLRTo { }
|
||||
|
||||
local shrLFrom { }
|
||||
local shrRFrom { }
|
||||
local shrLRFrom { }
|
||||
local shrLTo { }
|
||||
local shrRTo { }
|
||||
local shrLRTo { }
|
||||
|
||||
local anyInfluence : new Set
|
||||
|
||||
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
|
||||
local extL : Texture.ExtL.get g
|
||||
local extR : Texture.ExtR.get g
|
||||
local extLR : Texture.ExtLR.get g
|
||||
local shrL : Texture.ShrL.get g
|
||||
local shrR : Texture.ShrR.get g
|
||||
local shrLR : Texture.ShrLR.get g
|
||||
|
||||
if extL : begin [extLFrom.push gid] [extLTo.push extL]
|
||||
if extR : begin [extRFrom.push gid] [extRTo.push extR]
|
||||
if extLR : begin [extLRFrom.push gid] [extLRTo.push extLR]
|
||||
if (extL || extR || extLR) : begin [anyInfluence.add gid]
|
||||
|
||||
if shrL : begin [shrLFrom.push gid] [shrLTo.push shrL]
|
||||
if shrR : begin [shrRFrom.push gid] [shrRTo.push shrR]
|
||||
if shrLR : begin [shrLRFrom.push gid] [shrLRTo.push shrLR]
|
||||
|
||||
# Build the lookup by case-analysis
|
||||
# Backtracks will be the "target"/"to" glyphs, while look-aheads will be the "from" glyphs
|
||||
define sub : gsub.createLookup
|
||||
.type 'gsub_chaining'
|
||||
.ignoreGlyphs anyMark
|
||||
.rules : list
|
||||
chain-rule # Extension to both sides
|
||||
[{}.concat shrRTo shrLRTo] ~> look-around
|
||||
extLRFrom ~> extLRTo
|
||||
[{}.concat shrLFrom shrLRFrom] ~> look-around
|
||||
chain-rule # Shrink to both sides
|
||||
[{}.concat extRTo extLRTo] ~> look-around
|
||||
shrLRFrom ~> shrLRTo
|
||||
[{}.concat extLFrom extLRFrom] ~> look-around
|
||||
chain-rule # Extension to the left
|
||||
[{}.concat shrRTo shrLRTo] ~> look-around
|
||||
extLFrom ~> extLTo
|
||||
chain-rule # Shrink to the left
|
||||
[{}.concat extRTo extLRTo] ~> look-around
|
||||
shrLFrom ~> shrLTo
|
||||
chain-rule # Extension to the right
|
||||
extRFrom ~> extRTo
|
||||
[{}.concat shrLFrom shrLRFrom] ~> look-around
|
||||
chain-rule # Shrink to the right
|
||||
shrRFrom ~> shrRTo
|
||||
[{}.concat extLFrom extLRFrom] ~> look-around
|
||||
|
||||
txtr.addLookup sub
|
||||
|
||||
# Decompose everything if we enable TXTR
|
||||
if cvs
|
||||
: then : begin
|
||||
# Reuse existing Cvdecompose data
|
||||
foreach cv [cvs.values] : begin
|
||||
if cv.decompositionLookup : begin
|
||||
txtr.addLookup cv.decompositionLookup
|
||||
gsub.setDependency cv.decompositionLookup sub
|
||||
: else : begin
|
||||
# Built it from scratch
|
||||
local decompose : gsub.createLookup
|
||||
.type 'gsub_multiple'
|
||||
.substitutions {.}
|
||||
|
||||
foreach { gn glyph } [glyphStore.namedEntries] : begin
|
||||
local parts : CvDecompose.get glyph
|
||||
if parts : begin
|
||||
local influenced : anyInfluence.has gn
|
||||
foreach part [items-of parts] : if [anyInfluence.has part] : set influenced true
|
||||
if influenced : set decompose.substitutions.(gn) parts
|
||||
|
||||
txtr.addLookup decompose
|
||||
gsub.setDependency decompose sub
|
||||
|
||||
gsub.endBlock rec
|
48
packages/font-otl/src/gsub-thousands.ptl
Normal file
48
packages/font-otl/src/gsub-thousands.ptl
Normal file
|
@ -0,0 +1,48 @@
|
|||
$$include './meta/macros.ptl'
|
||||
|
||||
import [QueryRelatedGlyphs] from "./table-util.mjs"
|
||||
|
||||
export : define [buildGsubThousands gsub para] : begin
|
||||
local rec : gsub.beginBlock
|
||||
|
||||
define Thousand : gsub.addCommonFeature : gsub.createFeature 'THND'
|
||||
define {chain-rule reverse-rule} : gsub.ChainRuleBuilder
|
||||
define numberGlyphIDs {
|
||||
'zero.lnum' 'one.lnum' 'two.lnum' 'three.lnum' 'four.lnum'
|
||||
'five.lnum' 'six.lnum' 'seven.lnum' 'eight.lnum' 'nine.lnum'
|
||||
}
|
||||
|
||||
define [nd s] : numberGlyphIDs.map : lambda [x] "\(x).nd\(s)"
|
||||
|
||||
define a : Thousand.addLookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule ({'period'} ~> null) (numberGlyphIDs ~> [nd 2]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
|
||||
chain-rule ([nd 2] ~> null) (numberGlyphIDs ~> [nd 1])
|
||||
chain-rule ([nd 1] ~> null) (numberGlyphIDs ~> [nd 6])
|
||||
chain-rule ([nd 6] ~> null) (numberGlyphIDs ~> [nd 5])
|
||||
chain-rule ([nd 5] ~> null) (numberGlyphIDs ~> [nd 4])
|
||||
chain-rule ([nd 4] ~> null) (numberGlyphIDs ~> [nd 3])
|
||||
chain-rule ([nd 3] ~> null) (numberGlyphIDs ~> [nd 2])
|
||||
|
||||
define b : Thousand.addLookup : gsub.createLookup : object
|
||||
.type 'gsub_chaining'
|
||||
.rules : list
|
||||
chain-rule (numberGlyphIDs ~> [nd 0]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
|
||||
chain-rule ([nd 0] ~> null) (numberGlyphIDs ~> [nd 0])
|
||||
|
||||
define c : Thousand.addLookup : gsub.createLookup : object
|
||||
.type 'gsub_reverse'
|
||||
.rules : list
|
||||
reverse-rule ([nd 0] ~> [nd 1]) ([nd 0] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 2]) ([nd 1] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 3]) ([nd 2] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 4]) ([nd 3] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 5]) ([nd 4] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 6]) ([nd 5] ~> null)
|
||||
reverse-rule ([nd 0] ~> [nd 1]) ([nd 6] ~> null)
|
||||
|
||||
gsub.setDependency a b
|
||||
gsub.setDependency b c
|
||||
|
||||
gsub.endBlock rec
|
122
packages/font-otl/src/index.ptl
Normal file
122
packages/font-otl/src/index.ptl
Normal file
|
@ -0,0 +1,122 @@
|
|||
import as Gr from "@iosevka/glyph/relation"
|
||||
|
||||
import [CreateEmptyTable] from "./table-util.mjs"
|
||||
|
||||
import [buildLigations] from "./gsub-ligation.mjs"
|
||||
import [buildCCMP buildCCMPPostCvSs] from "./gsub-ccmp.mjs"
|
||||
import [buildGrFeature] from "./gsub-gr.mjs"
|
||||
import [buildFrac] from "./gsub-frac.mjs"
|
||||
import [buildCVSS] from "./gsub-cv-ss.mjs"
|
||||
import [buildLOCL] from "./gsub-locl.mjs"
|
||||
import [buildGsubThousands] from "./gsub-thousands.mjs"
|
||||
import [buildGsubTexture] from "./gsub-texture.mjs"
|
||||
import [buildMarkMkmk] from "./gpos-mark-mkmk.mjs"
|
||||
|
||||
define GDEF_SIMPLE 1
|
||||
define GDEF_LIGATURE 2
|
||||
define GDEF_MARK 3
|
||||
|
||||
extern Set
|
||||
extern Map
|
||||
|
||||
# GSUB
|
||||
define [buildGSUB para glyphStore markGlyphs] : begin
|
||||
define gsub : CreateEmptyTable
|
||||
|
||||
# NWID / WWID
|
||||
if para.enableNwidWwid : begin
|
||||
buildGrFeature gsub glyphStore Gr.Nwid
|
||||
buildGrFeature gsub glyphStore Gr.Wwid
|
||||
|
||||
# lnum / onum
|
||||
buildGrFeature gsub glyphStore Gr.Lnum
|
||||
buildGrFeature gsub glyphStore Gr.Onum
|
||||
|
||||
# zero
|
||||
buildGrFeature gsub glyphStore Gr.Zero
|
||||
|
||||
# APLF
|
||||
if para.enableCvSs : begin
|
||||
buildGrFeature gsub glyphStore Gr.AplForm
|
||||
|
||||
# numr / dnom
|
||||
buildGrFeature gsub glyphStore Gr.NumeratorForm
|
||||
buildGrFeature gsub glyphStore Gr.DenominatorForm
|
||||
|
||||
# ccmp
|
||||
local ccmp : buildCCMP gsub glyphStore markGlyphs
|
||||
|
||||
# frac
|
||||
buildFrac gsub glyphStore
|
||||
|
||||
# Ligation
|
||||
if para.enableLigation : do
|
||||
define plm : Object.assign {.} para.ligation.defaultBuildup
|
||||
if (para.ligation.caltBuildup && para.ligation.caltBuildup.length) : begin
|
||||
set plm.calt para.ligation.caltBuildup
|
||||
buildLigations gsub para plm
|
||||
|
||||
# THND
|
||||
buildGsubThousands gsub para glyphStore
|
||||
|
||||
# cv##, ss##
|
||||
local cvs nothing
|
||||
if para.enableCvSs : begin
|
||||
set cvs : buildCVSS gsub para glyphStore
|
||||
|
||||
# ccmp post cv/ss (for Ogonek shape transform)
|
||||
buildCCMPPostCvSs gsub ccmp glyphStore markGlyphs
|
||||
|
||||
# locl
|
||||
# Builds last, but the lookups are added into the beginning of the lookup list
|
||||
buildLOCL gsub para glyphStore
|
||||
|
||||
# TXTR, "texture" feature
|
||||
if (para.buildTextureFeature && !para.isQuasiProportional) : begin
|
||||
buildGsubTexture gsub glyphStore markGlyphs cvs
|
||||
|
||||
gsub.finalize
|
||||
return gsub
|
||||
|
||||
# GPOS
|
||||
define [buildGPOS para glyphStore markGlyphs] : begin
|
||||
define gpos : CreateEmptyTable
|
||||
buildMarkMkmk gpos glyphStore markGlyphs
|
||||
gpos.finalize
|
||||
return gpos
|
||||
|
||||
# GDEF
|
||||
define [buildGDEF para glyphStore markGlyphs] : begin
|
||||
local GDEF { .glyphClassDef {.} .markAttachClassDef {.} .markGlyphSets {} }
|
||||
|
||||
foreach { gn glyph } [glyphStore.namedEntries] : piecewise
|
||||
[markGlyphs.all.has gn] : set GDEF.glyphClassDef.(gn) GDEF_MARK
|
||||
[[regex '_'].test gn] : set GDEF.glyphClassDef.(gn) GDEF_LIGATURE
|
||||
true : set GDEF.glyphClassDef.(gn) GDEF_SIMPLE
|
||||
|
||||
local n 1
|
||||
local m : new Map
|
||||
foreach { gn clsStr } markGlyphs.markAttachClassDef : begin
|
||||
local clsNum : m.get clsStr
|
||||
if [not clsNum] : begin
|
||||
set clsNum n
|
||||
set n (n + 1)
|
||||
m.set clsStr clsNum
|
||||
|
||||
set GDEF.markAttachClassDef.(gn) clsNum
|
||||
|
||||
set GDEF.markGlyphSets markGlyphs.markGlyphSets
|
||||
|
||||
return GDEF
|
||||
|
||||
export : define [buildOtl para glyphStore] : begin
|
||||
local markGlyphs { .all [new Set] .markAttachClassDef [new Map] .markGlyphSets {} }
|
||||
local GPOS : buildGPOS para glyphStore markGlyphs
|
||||
local GDEF : buildGDEF para glyphStore markGlyphs
|
||||
local GSUB : buildGSUB para glyphStore markGlyphs
|
||||
|
||||
# Make all diacritics join-l (Kitty #3716)
|
||||
foreach gnMark markGlyphs.all : begin
|
||||
Gr.Joining.or [glyphStore.queryByName gnMark] Gr.Joining.Classes.Left
|
||||
|
||||
return [object GSUB GPOS GDEF]
|
8
packages/font-otl/src/meta/macros.ptl
Normal file
8
packages/font-otl/src/meta/macros.ptl
Normal file
|
@ -0,0 +1,8 @@
|
|||
### Autoarg macro
|
||||
define-operator "--" 890 'right' : syntax-rules
|
||||
`(@l -- @r) [atom l] : dirty `[new $NamedParameterPair$ @{".quote" [formOf l]} @r]
|
||||
`(@{".quote" l} -- @r) : dirty `[new $NamedParameterPair$ @l @r]
|
||||
|
||||
### Arbitrary pair operator
|
||||
define-operator "~>" 880 'right' : syntax-rules
|
||||
`(@l ~> @r) `{.left @l .right @r}
|
237
packages/font-otl/src/table-util.ptl
Normal file
237
packages/font-otl/src/table-util.ptl
Normal file
|
@ -0,0 +1,237 @@
|
|||
import toposort from 'toposort'
|
||||
import [AnyCv] from "@iosevka/glyph/relation"
|
||||
|
||||
extern Map
|
||||
extern Set
|
||||
|
||||
export : define [CreateEmptyTable] : new LayoutTable
|
||||
|
||||
class LayoutTable
|
||||
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
|
||||
|
||||
public [copyLanguage tag tagFrom] : begin
|
||||
define langFrom : this.pickLanguage tagFrom
|
||||
define langTo : this.pickLanguage tag
|
||||
foreach [feat : items-of langFrom.features] : langTo.features.push feat
|
||||
return langTo
|
||||
|
||||
public [createFeature tag] : begin
|
||||
local feature : new LayoutFeature tag
|
||||
local n 0
|
||||
while true : begin
|
||||
if [not this.features.(tag + '_' + n)] : begin
|
||||
set feature.name : tag + '_' + n
|
||||
set this.features.(tag + '_' + n) feature
|
||||
return feature
|
||||
set n : n + 1
|
||||
|
||||
public [addCommonFeature fea] : begin
|
||||
define dfltDflt : this.pickLanguage 'DFLT_DFLT'
|
||||
define latnDflt : this.pickLanguage 'latn_DFLT'
|
||||
define grekDflt : this.pickLanguage 'grek_DFLT'
|
||||
define cyrlDflt : this.pickLanguage 'cyrl_DFLT'
|
||||
|
||||
dfltDflt.addFeature fea
|
||||
latnDflt.addFeature fea
|
||||
grekDflt.addFeature fea
|
||||
cyrlDflt.addFeature fea
|
||||
|
||||
return fea
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
existingLookupNames : new Set : Object.keys this.lookups
|
||||
|
||||
public [endBlock rec] : begin
|
||||
local currentLookupNames : new Set : Object.keys this.lookups
|
||||
foreach existing rec.existingLookupNames : foreach current currentLookupNames
|
||||
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
|
||||
this.lookupDep.push { existing current }
|
||||
|
||||
public [endBlockAtFront rec] : begin
|
||||
local currentLookupNames : new Set : Object.keys this.lookups
|
||||
foreach existing rec.existingLookupNames : foreach current currentLookupNames
|
||||
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
|
||||
this.lookupDep.push { current existing }
|
||||
|
||||
public [finalize] : begin
|
||||
local lns : new Set : Object.keys this.lookups
|
||||
foreach [front lns] : foreach [rear lns]
|
||||
if (![IsUtilityLookupId front] && [IsUtilityLookupId 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
|
||||
|
||||
define [createNewLookup f t] : begin
|
||||
local subst {.}
|
||||
foreach [j : range 0 f.length] : set subst.(f.(j)) t.(j)
|
||||
return : table.createLookup {.type 'gsub_single' .substitutions subst} UtilityLookupPrefix
|
||||
|
||||
define [getSubLookup left right] : piecewise
|
||||
[not right] null
|
||||
([typeof right] === "string") : throw : new Error "Invalid substitution"
|
||||
(right <@ Function) : getSubLookup left [right left]
|
||||
true : begin
|
||||
local found null
|
||||
local maxMatch 0
|
||||
|
||||
local lookupKeys : [Object.keys table.lookups].reverse
|
||||
foreach [name : items-of lookupKeys] : if [IsUtilityLookupId name] : begin
|
||||
local lookup table.lookups.(name)
|
||||
local st lookup.substitutions
|
||||
|
||||
local compatible true
|
||||
local matchCount 0
|
||||
foreach [j : range 0 left.length] : begin
|
||||
if (st.(left.(j)) && st.(left.(j)) !== right.(j)) : set compatible false
|
||||
if (st.(left.(j)) === right.(j)) : inc matchCount
|
||||
|
||||
if (compatible && (!found || matchCount > maxMatch)) : begin
|
||||
set found lookup
|
||||
set maxMatch matchCount
|
||||
|
||||
if found : begin
|
||||
local st found.substitutions
|
||||
foreach [j : range 0 left.length] : set st.(left.(j)) right.(j)
|
||||
return found
|
||||
|
||||
return : createNewLookup left right
|
||||
|
||||
define [chain-rule] : begin
|
||||
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x {.left x .right null}])
|
||||
local rule {.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 rule.inputBegins j
|
||||
set foundi true
|
||||
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
|
||||
set rule.inputEnds (j + 1)
|
||||
set founde true
|
||||
foreach [j : range 0 terms.length] : begin
|
||||
local term terms.(j)
|
||||
rule.match.push : Array.from : new Set term.left
|
||||
local lookup : getSubLookup term.left term.right
|
||||
if lookup : rule.apply.push {.at j .lookup {.name lookup.name}}
|
||||
return rule
|
||||
|
||||
define [reverse-rule] : begin
|
||||
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x {.left x .right null}])
|
||||
local rule {.match {} .to {} .inputIndex 0}
|
||||
local foundi false
|
||||
|
||||
foreach [j : range 0 terms.length] : begin
|
||||
local term terms.(j)
|
||||
local subst : new Map
|
||||
foreach [g : items-of term.left] : subst.set g g
|
||||
|
||||
if term.right : begin
|
||||
if foundi : throw : new Error "Duplicate substitutions in one reverse rule"
|
||||
set foundi true
|
||||
set rule.inputIndex j
|
||||
local toGlyphs : piecewise
|
||||
(term.right <@ Function) [term.right term.left]
|
||||
true term.right
|
||||
foreach [k : range 0 term.left.length]
|
||||
subst.set term.left.(k) (toGlyphs.(k) || term.left.(k))
|
||||
|
||||
set rule.to : Array.from [subst.values]
|
||||
|
||||
set rule.match.(j) : Array.from [subst.keys]
|
||||
|
||||
return 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
|
||||
define sink {}
|
||||
foreach [gid : items-of entries] : if glyphs.(gid) : begin
|
||||
sink.push gid
|
||||
if para.enableCvSs : foreach [gr : items-of : AnyCv.query glyphs.(gid)] : begin
|
||||
sink.push : gr.get glyphs.(gid)
|
||||
return sink
|
||||
|
||||
export : define [FinalizeTable table] : begin
|
||||
set table.lookupOrder : toposort table.lookupDep
|
||||
foreach [{key lang} : pairs-of table.languages] : begin
|
||||
if lang.features : lang.features.sort
|
||||
|
||||
export : define [UkMapToLookup m] : begin
|
||||
local l {}
|
||||
local r {}
|
||||
foreach { k v } [Object.entries m] : begin
|
||||
l.push k
|
||||
r.push v
|
||||
return { .left l .right r }
|
||||
|
||||
export : define [UkMap2ToLookup mm] : begin
|
||||
local res {}
|
||||
foreach { g1 second } [Object.entries mm] : foreach { g2 gTo } [Object.entries second] : begin
|
||||
res.push {.from {g1 g2} .to gTo}
|
||||
return res
|
Loading…
Add table
Add a link
Reference in a new issue