Iosevka/meta/features.ptl
2017-01-13 20:12:29 +08:00

446 lines
17 KiB
Plaintext

import 'topsort' as topsort
define-operator "~>" 880 'right' : syntax-rules
`(@l ~> @r) `{.left @l .right @r}
export : define [apply para glyphList] : begin
local markGlyphs {.all {} }
# GPOS
local lookup_mark
.type 'gpos_mark_to_base'
.subtables {}
local lookup_mkmk
.type 'gpos_mark_to_mark'
.subtables {}
local GPOS : object
languages
.DFLT_DFLT {.features {'mark0', 'mkmk0'}}
.latn_DFLT {.features {'mark0', 'mkmk0'}}
.grek_DFLT {.features {'mark0', 'mkmk0'}}
.cyrl_DFLT {.features {'mark0', 'mkmk0'}}
features
.mark0 {'l_mark'}
.mkmk0 {'l_mkmk'}
lookups
.l_mark lookup_mark
.l_mkmk lookup_mkmk
# mark and mkmk
define [createBaseInfo g th px py] : begin
local res {.}
local pushed false
foreach key [items-of : Object.keys g.anchors] : if (!th || th.(key)) : begin
set res.(key) : object
.x g.anchors.(key).(px || 'x')
.y g.anchors.(key).(py || 'y')
set pushed true
return : if pushed res nothing
define [createMTSSubtable lookup anchorClasses] : begin
local subtable {.marks {.} .bases {.}}
local th {.}
foreach [ac : items-of anchorClasses] : set th.(ac) true
foreach glyph [items-of glyphList] : if glyph.anchors : begin
local anchorKeys : Object.keys glyph.anchors
local hasAnchor false
foreach [key : items-of anchorKeys] : if th.(key) : set hasAnchor true
if hasAnchor : begin
local isMarkGlyph false
local markKey nothing
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
set isMarkGlyph true
set markKey key
if isMarkGlyph
: then : begin
set subtable.marks.(glyph.name) : object
class markKey
x glyph.anchors.(markKey).x
y glyph.anchors.(markKey).y
if (lookup == lookup_mkmk) : begin
local r : createBaseInfo glyph th 'mbx' 'mby'
if r : set subtable.bases.(glyph.name) r
: else : if (lookup == lookup_mark) : begin
local r : createBaseInfo glyph th 'x' 'y'
if r : set subtable.bases.(glyph.name) r
lookup.subtables.push subtable
foreach [marktag : items-of {'above' 'below' 'overlay' 'slash' 'topright' 'bottomright' 'trailing' 'lf'}] : begin
createMTSSubtable lookup_mark {marktag}
createMTSSubtable lookup_mkmk {marktag}
# GDEF
local GDEF {.glyphClassDef {.}}
foreach glyph [items-of glyphList] : begin
set GDEF.glyphClassDef.(glyph.name) : if [[regex '_'].test glyph.name] 2 1
if (glyph.anchors && [begin [local anchorKeys : Object.keys glyph.anchors] anchorKeys.length]) : begin
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
if [not markGlyphs.(key)] : set markGlyphs.(key) {}
markGlyphs.(key).push glyph.name
markGlyphs.all.push glyph.name
set GDEF.glyphClassDef.(glyph.name) 3
# GSUB
local commonList {}
local languages
.DFLT_DFLT {.features commonList}
.latn_DFLT {.features commonList}
.grek_DFLT {.features commonList}
.cyrl_DFLT {.features commonList}
.kana_DFLT {.features commonList}
.hani_DFLT {.features commonList}
local features {.}
local lookups {.}
define [lookup-single name f t] : begin
local subtable {.}
foreach [j : range 0 f.length] : set subtable.(f.(j)) t.(j)
set lookups.(name) {.type 'gsub_single' .subtables {subtable}}
define [getsublookup left right] : piecewise
([typeof right] === "string") right
(right <@ Function) : getsublookup left [right left]
true : begin
local found null
foreach [{name lookup} : pairs-of lookups] : match lookup
{.type "gsub_single" .subtables {st}} : begin
local check true
foreach [j : range 0 left.length] : if (st.(left.(j)) !== right.(j)) : set check false
if check : set found name
if found : return found
local name "_lookup_\([Object.keys lookups].length)"
lookup-single name left right
return name
define [chain-rule] : begin
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x (x ~> null)])
local subtable {.match {} .apply {} .inputBegins 0 .inputEnds 0}
local foundi false
local founde false
foreach [j : range 0 terms.length] : if (!foundi && terms.(j).right) : begin
set subtable.inputBegins j
set foundi true
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
set subtable.inputEnds (j + 1)
set founde true
foreach [j : range 0 terms.length] : begin
local term terms.(j)
subtable.match.push term.left
if term.right : begin
subtable.apply.push {.at j .lookup [getsublookup term.left term.right]}
return subtable
define [flatten] : begin
local ans {}
foreach [term : items-of : {}.slice.call arguments 0] : begin
if (term <@ Array)
: then : set ans : ans.concat term
: else : ans.push term
return ans
# ccmp
commonList.push 'ccmp'
set features.ccmp {'ccmp1' 'ccmp2'}
let [groupA {'A' 'a' 'u' 'cyrA' 'cyra'}] : set lookups.ccmp1
.type 'gsub_chaining'
.subtables : list
chain-rule
{'i' 'cyrUkrainiani' 'j' 'cyrje' 'iogonekBelow'} ~> {'dotlessi' 'dotlessi' 'dotlessj' 'dotlessj' 'iogonek.dotless'}
markGlyphs.above ~> null
chain-rule {'eta'} ({'iotaBelow'} ~> {'iotaLF'})
chain-rule {'eta'} markGlyphs.all ({'iotaBelow'} ~> {'iotaLF'})
chain-rule {'eta'} markGlyphs.all markGlyphs.all ({'iotaBelow'} ~> {'iotaLF'})
chain-rule {'eta'} markGlyphs.all markGlyphs.all markGlyphs.all ({'iotaBelow'} ~> {'iotaLF'})
chain-rule groupA ({'ogonekBelow'} ~> {'ogonekTR'})
chain-rule groupA markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
chain-rule groupA markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
chain-rule groupA markGlyphs.all markGlyphs.all markGlyphs.all ({'ogonekBelow'} ~> {'ogonekTR'})
set lookups.ccmp2
.type 'gsub_ligature'
.subtables : list : object
psilivaria {'commaAbove' 'graveAbove'}
psilioxia {'commaAbove' 'acuteAbove'}
psiliperispomeni {'commaAbove' 'perispomeniAbove'}
dasiavaria {'revCommaAbove' 'graveAbove'}
dasiaoxia {'revCommaAbove' 'acuteAbove'}
dasiaperispomeni {'revCommaAbove' 'perispomeniAbove'}
local lookupOrder {}
# calt and other ligations
if (para.spacing > 0) : do
local [stick style] : {'hyphen' 'equal'} ~> [lsx style]
local [stickClass style] : [lsx style] {'hyphen' 'equal'}
local less {'less'}
local hyphen {'hyphen'}
local exclam {'exclam'}
local greater {'greater'}
local [acops] {'less' 'greater' 'hyphen' 'equal' 'plus'}
local [acskip] {'slash' 'bar' 'at' 'ampersand' 'percent' 'numbersign'}
local [asterisk_center] ({'asterisk'} ~> {'asterisk.low'})
local [colon_center] ({'colon'} ~> {'colon.mid'})
local [tilde_center] ({'asciitilde'} ~> {'asciitilde.low'})
local [period_center] ({'period'} ~> {'period.center'})
define [lsx s] : lambda [t] : t.map : lambda [x] "lig\(x).\(s)"
local progLigNameMap
.XML_ {'brst', 'logic', 'ml'}
.XFS_ {'brst', 'logic', 'fsharp'}
.XFST {'brst', 'logic', 'fstar'}
.XHS_ {'arrow2', 'dotoper', 'logic', 'haskell'}
.XIDR {'arrow2', 'dotoper', 'logic', 'idris'}
.XELM {'arrow2', 'dotoper', 'logic', 'elm'}
.PURS {'arrow2', 'dotoper', 'logic', 'purescript'}
.XPTL {'arrow2', 'patel'}
.SWFT {'arrow2', 'swift'}
.XV__ {'arrow2', 'dotoper', 'logic', 'brst', 'coq'}
.calt {}
foreach
: ligationFeatureName : items-of : Object.keys progLigNameMap
: do
local mappedFeature : progLigNameMap.(ligationFeatureName) || {}
local ligationLookupName : 'lig_' + ligationFeatureName + '-' + mappedFeature
local [only subtable ln] : begin
if ([mappedFeature.indexOf ln] >= 0)
: then : return subtable
: else : begin
set subtable.apply {}
return subtable
commonList.push ligationFeatureName
local featLookups {}
local lastLookupName null
local [dedup ln0 obj] : begin
local h : JSON.stringify obj
foreach [{name lookup} : pairs-of lookups] : begin
local h1 : JSON.stringify lookup
if (h == h1) : return name
return ln0
local [includeLookup obj] : begin
local ln : dedup (ligationLookupName + featLookups.length) obj
if [not lookups.(ln)] : set lookups.(ln) obj
featLookups.push ln
if lastLookupName : lookupOrder.push {lastLookupName ln}
set lastLookupName ln
set features.(ligationFeatureName) featLookups
do "Operator centering"
define centerizeGroups {asterisk_center tilde_center colon_center period_center}
define [avoid-dot g] : if (g == period_center)
then (subtable => [only subtable "dotoper"])
else (subtable => subtable)
define [centerize-standard g] : includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule [g] [g] [acskip] [acskip] [acops] :> [avoid-dot g] # ::+
chain-rule [g] [g] [acskip] [acops] :> [avoid-dot g] # ::+
chain-rule [g] [g] [acops] :> [avoid-dot g] # ::+
chain-rule [g] [acskip] [acskip] [acops] :> [avoid-dot g] # :+
chain-rule [g] [acskip] [acops] :> [avoid-dot g] # :+
chain-rule [g] [acops] :> [avoid-dot g] # :+
chain-rule [acops] [acskip] [acskip] [g] :> [avoid-dot g] # +:
chain-rule [acops] [acskip] [g] :> [avoid-dot g] # +:
chain-rule [acops] [g] :> [avoid-dot g] # +:
define [centerize-cross g1 g2] : includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule [g1] [g1] [g2] :> [avoid-dot g1] :> [avoid-dot g2] # ::*
chain-rule [g1] [g2] :> [avoid-dot g1] :> [avoid-dot g2] # :*
includeLookup
.type 'gsub_chaining'
.subtables : list
# (* and *)
chain-rule {'parenLeft'} [asterisk_center] :> only 'brst' # (*
chain-rule [asterisk_center] {'parenRight'} :> only 'brst' # *)
# Operator centering
foreach [g : items-of centerizeGroups] : begin
centerize-standard g
foreach [g2 : items-of centerizeGroups] : if (g != g2) : centerize-cross g g2
# Operator centering : Trailing
local centerizeL : centerizeGroups.map : x => [x].left.0
local centerizeR : centerizeGroups.map : x => [x].right.0
includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule centerizeR [acskip] [acskip] (centerizeL ~> centerizeR)
chain-rule centerizeR [acskip] (centerizeL ~> centerizeR)
chain-rule centerizeR (centerizeL ~> centerizeR)
includeLookup
.type 'gsub_reverse'
.subtables : list
object
match {centerizeL [acskip] [acskip] centerizeR}
to centerizeR
inputIndex 0
object
match {centerizeL [acskip] centerizeR}
to centerizeR
inputIndex 0
object
match {centerizeL centerizeR}
to centerizeR
inputIndex 0
do "Colon chains" : includeLookup
.type 'gsub_chaining'
.subtables : list
# Colon chains
chain-rule {'colon.dright' 'colon.dmid'} ({'colon'} ~> {'colon.dmid'}) {'colon'}
chain-rule {'colon.dright' 'colon.dmid'} ({'colon'} ~> {'colon.dleft'})
chain-rule ({'colon'} ~> {'colon.dright'}) {'colon'}
do "Arrows"
includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule less [stick 'lc'] [stick 'cc'] [stick 'cc'] [stick 'cr'] greater # <----> <====>
chain-rule less [stick 'lc'] [stick 'cc'] [stick 'cr'] greater # <---> <===>
chain-rule less [stick 'lc'] [stick 'cr'] greater # <--> <==>
chain-rule less [stick 'lr'] greater # <->, <=>
chain-rule less [stick 'lj'] less # <-<, <=<
chain-rule less [stick 'lc'] [stick 'cc'] [stick 'cf'] # <---, <===
chain-rule less [stick 'lc'] [stick 'cf'] # <--, <==
chain-rule less (less ~> [lsx 'shift1']) [stick 'l1f'] # <<-, <<=
:> only 'arrow2'
chain-rule less (hyphen ~> [lsx 'lf']) # <-
chain-rule less (exclam ~> [lsx 'htmlcommentstart']) (hyphen ~> [lsx 'lxc']) (hyphen ~> [lsx 'cc']) (hyphen ~> [lsx 'cf']) # <!---
chain-rule less (exclam ~> [lsx 'htmlcommentstart']) (hyphen ~> [lsx 'lxc']) (hyphen ~> [lsx 'cf']) # <!--
chain-rule greater (greater ~> [lsx 'shiftN1']) [stick 'j1f'] # >>-, >>=
:> only 'arrow2'
chain-rule greater [stick 'jr'] greater # >->, >=>
chain-rule greater (hyphen ~> [lsx 'jf']) # >-
:> only 'arrow2'
chain-rule [stick 'fc'] [stick 'cc'] [stick 'cr'] greater # --->, ===>
chain-rule [stick 'fc'] [stick 'cr'] greater # -->, ==>
chain-rule [stick 'fr'] greater [stick 'jf'] # ->- =>=
:> only 'arrow2'
chain-rule [stick 'fr1'] (greater ~> [lsx 'shift1']) greater # ->>, =>>
:> only 'arrow2'
chain-rule [stick 'fr'] greater # ->, =>
chain-rule [stick 'fj'] less [stick 'lf'] # -<- =<=
:> only 'arrow2'
chain-rule [stick 'fj1'] (less ~> [lsx 'shiftN1']) less # -<<, =<<
:> only 'arrow2'
chain-rule [stick 'fj'] less # -<, =<
:> only 'arrow2'
# Extended arrow sticks
# --------------------->
includeLookup
.type 'gsub_reverse'
.subtables : list
object
match {{'hyphen' 'equal'} [stickClass 'fr']}
to [stickClass 'fz']
inputIndex 0
object
match {{'hyphen' 'equal'} [stickClass 'fc']}
to [stickClass 'fz']
inputIndex 0
object
match {{'hyphen' 'equal'} [stickClass 'fz']}
to [stickClass 'fz']
inputIndex 0
# <--------------------, <------------------->
includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule ([stickClass 'zf'] ~> [stickClass 'zc']) ([stickClass 'fr'] ~> [stickClass 'cr'])
chain-rule ([stickClass 'zf'] ~> [stickClass 'zc']) ([stickClass 'fc'] ~> [stickClass 'cc'])
chain-rule ([stickClass 'lf'] ~> [stickClass 'lc']) ([stickClass 'fz'] ~> [stickClass 'cz'])
chain-rule ([stickClass 'cf'] ~> [stickClass 'cc']) ([stickClass 'fz'] ~> [stickClass 'cz'])
chain-rule ([stickClass 'zf'] ~> [stickClass 'zc']) ([stickClass 'fz'] ~> [stickClass 'cz'])
chain-rule [stickClass 'lf'] [stick 'zf']
chain-rule [stickClass 'cf'] [stick 'zf']
chain-rule [stickClass 'zf'] [stick 'zf']
includeLookup
.type 'gsub_chaining'
.subtables : list
chain-rule ({'slash'} ~> {'slash.left'}) ({'backslash'} ~> {'backslash.right'}) # /\
:> only 'logic'
chain-rule ({'backslash'} ~> {'backslash.left'}) ({'slash'} ~> {'slash.right'}) # \/
:> only 'logic'
#opbd
local fwclose {'fwlcloseDoubleQuote' 'fwlcloseSingleQuote' 'dwlcjkSingleQuoteRight' 'dwlcjkDoubleQuoteRight' 'dwlparenRight'}
local hwclose {'closeDoubleQuote' 'closeSingleQuote' 'cjkSingleQuoteRight' 'cjkDoubleQuoteRight' 'parenRight'}
local fwopen {'fwropenDoubleQuote' 'fwropenSingleQuote' 'dwrcjkSingleQuoteLeft' 'dwrcjkDoubleQuoteLeft' 'dwrparenLeft'}
local hwopen {'openDoubleQuote' 'openSingleQuote' 'cjkSingleQuoteLeft' 'cjkDoubleQuoteLeft' 'parenLeft'}
local fwquoteopen {'fwropenDoubleQuote' 'fwropenSingleQuote'}
local hwquoteopen {'openDoubleQuote' 'openSingleQuote'}
local fwtrail {'dwlperiod' 'dwlcomma' 'dwlcjkperiod' 'dwlcjkcomma'}
local hwtrail {'period' 'comma' 'cjkperiod' 'cjkcomma'}
local fwmid {'dwccolon' 'dwcsemicolon'}
local hwmid {'colon' 'semicolon'}
commonList.push 'opbd'
set features.opbd {'opbd1'}
set lookups.opbd1
.type 'gsub_chaining'
.subtables : list
chain-rule [flatten fwtrail hwtrail fwopen hwopen fwmid hwmid] (fwopen ~> hwopen)
chain-rule (fwclose ~> hwclose) [flatten fwtrail hwtrail fwclose hwclose fwopen hwopen fwmid hwmid]
# locl, SRB
local srbSubtable null
if para.isItalic
: then : set srbSubtable : object
cyrbe 'cyrbe.serbian'
cyrghe 'cyrghe.serbian'
cyrde 'cyrde.serbian'
cyrpe 'cyrpe.serbian'
cyrte 'cyrte.serbian'
: else : set srbSubtable : object
cyrbe 'cyrbe.serbian'
set lookups.locl_srb {.type 'gsub_single' .subtables {srbSubtable}}
set features.locl_srb {'locl_srb'}
# locl, BGR
local bgrSubtable : object
cyrve 'cyrve.BGR'
cyrghe 'cyrghe.italic'
cyrde 'g'
cyrzhe 'cyrzhe.BGR'
cyrze 'cyrze.BGR'
cyri 'u'
cyribreve 'ubreve'
cyrka 'k'
cyrEl 'Lambda'
cyrel 'turnv'
cyrpe 'n'
cyrte 'm'
cyrsha 'cyrsha.italic'
cyrshcha 'cyrshcha.italic'
cyryu 'cyryu.BGR'
set lookups.locl_bgr {.type 'gsub_single' .subtables {bgrSubtable}}
set features.locl_bgr {'locl_bgr'}
# cvxx
foreach [glyph : items-of glyphList] : if glyph.featureSelector : begin
local fs glyph.featureSelector
foreach [feature : items-of : Object.keys fs] : begin
if [not lookups.(feature)] : begin
set features.(feature) {feature}
set lookups.(feature) {.type 'gsub_single' .subtables{{.}}}
commonList.push feature
set lookups.(feature).subtables.0.(glyph.name) fs.(feature)
# ssxx
local sscompose : if para.isItalic para.sscompose.italic para.sscompose.upright
foreach [name : items-of : Object.keys sscompose] : begin
commonList.push name
set features.(name) sscompose.(name)
set languages.'cyrl_SRB ' {.features [{'locl_srb'}.concat commonList]}
set languages.'cyrl_MKD ' {.features [{'locl_srb'}.concat commonList]}
set languages.'cyrl_BGR ' {.features [{'locl_bgr'}.concat commonList]}
local GSUB {.languages languages .features features .lookups lookups .lookupOrder [topsort lookupOrder]}
return [object GSUB GPOS GDEF]