Files
Iosevka/meta/features.ptl
T

319 lines
11 KiB
Plaintext

import 'topsort' as topsort
import '../support/glyph' as Glyph
import '../support/transform' as Transform
import [progLigNameMap buildLigations] from './feature/ligation'
import './feature/opbd' as buildOPBD
import './feature/ccmp' as buildCCMP
define GDEF_SIMPLE 1
define GDEF_LIGATURE 2
define GDEF_MARK 3
define-operator "~>" 880 'right' : syntax-rules
`(@l ~> @r) `{.left @l .right @r}
# GSUB
define [buildGSUB para glyphs glyphList markGlyphs] : begin
local commonList {}
local languages
.DFLT_DFLT {.features commonList}
.latn_DFLT {.features commonList}
.grek_DFLT {.features commonList}
.cyrl_DFLT {.features commonList}
.kana_DFLT {.features commonList}
.hani_DFLT {.features commonList}
local features {.}
local lookups {.}
local lookupOrder {}
# Chaining lookup builder
define [lookup-single name f t] : begin
local subtable {.}
foreach [j : range 0 f.length] : set subtable.(f.(j)) t.(j)
set lookups.(name) {.type 'gsub_single' .subtables {subtable}}
define [getsublookup left right] : piecewise
[not right] null
([typeof right] === "string") right
(right <@ Function) : getsublookup left [right left]
true : begin
local found null
foreach [{name lookup} : pairs-of lookups] : match lookup
{.type "gsub_single" .subtables {st}} : begin
local check true
foreach [j : range 0 left.length] : if (st.(left.(j)) !== right.(j)) : set check false
if check : set found name
if found : return found
local name "_lookup_\([Object.keys lookups].length)"
lookup-single name left right
return name
define [chain-rule] : begin
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x (x ~> null)])
local subtable {.match {} .apply {} .inputBegins 0 .inputEnds 0}
local foundi false
local founde false
foreach [j : range 0 terms.length] : if (!foundi && terms.(j).right) : begin
set subtable.inputBegins j
set foundi true
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
set subtable.inputEnds (j + 1)
set founde true
foreach [j : range 0 terms.length] : begin
local term terms.(j)
subtable.match.push term.left
local lutn : getsublookup term.left term.right
if lutn : subtable.apply.push {.at j .lookup lutn}
return subtable
# hwid-fwid, lnum-onum
define [MakePairFrature tag1 tag2] : begin
local mapTag2 {.}
local mapTag1 {.}
define reHidden : regex "^\\."
define reTag1 : new RegExp ("\\." + tag1 + "$")
foreach [glyph : items-of glyphList] : begin
if ([reTag1.test glyph.name] && ![reHidden.test glyph.name]) : do
local gnTag2 : glyph.name.replace reTag1 ('.' + tag2)
if (glyphs.(gnTag2)) : begin
set mapTag2.(glyph.name) gnTag2
set mapTag1.(gnTag2) glyph.name
set lookups.(tag2) {.type 'gsub_single' .subtables {mapTag2}}
set lookups.(tag1) {.type 'gsub_single' .subtables {mapTag1}}
set features.(tag2) {tag2}
set features.(tag1) {tag1}
commonList.push tag2 tag1
MakePairFrature 'hwid' 'fwid'
MakePairFrature 'lnum' 'onum'
# CCMP
buildCCMP chain-rule markGlyphs commonList features lookups
# Ligation
if para.enableLigation : do
define plm : progLigNameMap
set plm.calt : para.customLigSet || plm.(para.defLigSet) || plm.calt
buildLigations chain-rule lookupOrder commonList features lookups plm glyphs
# CVxx/SSxx
if para.enableCvSs : begin
# cvxx
foreach [glyph : items-of glyphList] : if glyph.featureSelector : begin
local fs glyph.featureSelector
foreach [feature : items-of : Object.keys fs] : begin
if [not lookups.(feature)] : begin
set features.(feature) {feature}
set lookups.(feature) {.type 'gsub_single' .subtables{{.}}}
commonList.push feature
set lookups.(feature).subtables.0.(glyph.name) fs.(feature)
# ssxx
foreach [{name composition} : pairs-of para.variants] : begin
if (name.length === 4 && composition.__isComposite && [name.slice 0 2] === 'ss') : begin
commonList.push name
local tags {.}
foreach [{ch tag} : pairs-of composition.__cvmap] : set tags.(tag) true
set features.(name) : [Object.keys tags].filter (tag => tags.(tag))
# locl, SRB
local srbSubtable null
if para.isItalic
: then : set srbSubtable : object
cyrbe 'cyrbe.SRB'
cyrghe 'cyrghe.SRB'
cyrde 'cyrde.SRB'
cyrpe 'cyrpe.SRB'
cyrte 'cyrte.SRB'
: else : set srbSubtable : object
cyrbe 'cyrbe.SRB'
set lookups.locl_srb {.type 'gsub_single' .subtables {srbSubtable}}
set features.locl_srb {'locl_srb'}
# locl, BGR
local bgrSubtable : object
cyrve 'cyrve.BGR'
cyrghe 'cyrghe.italic'
cyrde 'g'
cyrzhe 'cyrzhe.BGR'
cyrze 'cyrze.BGR'
cyri 'u'
cyribreve 'ubreve'
cyrka 'k'
cyrEl 'Lambda'
cyrel 'turnv'
cyrpe 'n'
cyrte 'm'
cyrsha 'cyrsha.italic'
cyrshcha 'cyrshcha.italic'
cyryu 'cyryu.BGR'
cyrtse 'cyrtse.italic'
set lookups.locl_bgr {.type 'gsub_single' .subtables {bgrSubtable}}
set features.locl_bgr {'locl_bgr'}
set languages.'cyrl_SRB ' {.features [{'locl_srb'}.concat commonList]}
set languages.'cyrl_MKD ' {.features [{'locl_srb'}.concat commonList]}
set languages.'cyrl_BGR ' {.features [{'locl_bgr'}.concat commonList]}
return {.languages languages .features features .lookups lookups .lookupOrder [topsort lookupOrder]}
# GPOS
define [buildGPOS para glyphs glyphList markGlyphs] : begin
# mark and mkmk
define [createBaseInfo g th px py] : begin
local res {.}
local pushed false
foreach key [items-of : Object.keys g.anchors] : if (!th || th.(key)) : begin
set res.(key) : object
.x g.anchors.(key).(px || 'x')
.y g.anchors.(key).(py || 'y')
set pushed true
return : if pushed res nothing
define [createMTLookup lookupType anchorClasses] : begin
local subtable {.marks {.} .bases {.}}
local th {.}
foreach [ac : items-of anchorClasses] : set th.(ac) true
foreach glyph [items-of glyphList] : if glyph.anchors : begin
local anchorKeys : Object.keys glyph.anchors
local hasAnchor false
foreach [key : items-of anchorKeys] : if th.(key) : set hasAnchor true
if hasAnchor : begin
local isMarkGlyph false
local markKey nothing
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
set isMarkGlyph true
set markKey key
if isMarkGlyph
: then : begin
set subtable.marks.(glyph.name) : object
class markKey
x glyph.anchors.(markKey).x
y glyph.anchors.(markKey).y
if (lookupType == 'gpos_mark_to_mark'): begin
local r : createBaseInfo glyph th 'mbx' 'mby'
if r : set subtable.bases.(glyph.name) r
: else : if (lookupType == 'gpos_mark_to_base') : begin
local r : createBaseInfo glyph th 'x' 'y'
if r : set subtable.bases.(glyph.name) r
return {.type lookupType .subtables {subtable}}
local lookupSet {.}
local markLookupNames {}
local mkmkLookupNames {}
foreach [marktag : items-of {'above' 'below' 'overlay' 'slash' 'topright' 'bottomright' 'trailing' 'lf'}] : begin
set lookupSet.('lookup_mark_' + marktag) : createMTLookup 'gpos_mark_to_base' {marktag}
set lookupSet.('lookup_mkmk_' + marktag) : createMTLookup 'gpos_mark_to_mark' {marktag}
markLookupNames.push ('lookup_mark_' + marktag)
mkmkLookupNames.push ('lookup_mkmk_' + marktag)
return : object
languages
.DFLT_DFLT {.features {'mark0', 'mkmk0'}}
.latn_DFLT {.features {'mark0', 'mkmk0'}}
.grek_DFLT {.features {'mark0', 'mkmk0'}}
.cyrl_DFLT {.features {'mark0', 'mkmk0'}}
features
.mark0 markLookupNames
.mkmk0 mkmkLookupNames
lookups lookupSet
# GDEF
define [buildGDEF para glyphs glyphList markGlyphs] : begin
local GDEF {.glyphClassDef {.}}
foreach glyph [items-of glyphList] : begin
set GDEF.glyphClassDef.(glyph.name) : if [[regex '_'].test glyph.name] GDEF_LIGATURE GDEF_SIMPLE
if (glyph.anchors && [begin [local anchorKeys : Object.keys glyph.anchors] anchorKeys.length]) : begin
foreach key [items-of anchorKeys] : if (glyph.anchors.(key).type == 'mark') : begin
if [not markGlyphs.(key)] : set markGlyphs.(key) {}
markGlyphs.(key).push glyph.name
markGlyphs.all.push glyph.name
set GDEF.glyphClassDef.(glyph.name) GDEF_MARK
return GDEF
# Compatibility ligatures
define [interpretLookups gs lutns lookups] : begin
foreach [lutn : items-of lutns] : begin
local lut lookups.(lutn)
interpretLookup gs lut lookups
define [interpretLookup gs lut lookups] : match lut.type
"gsub_chaining" : begin
local j 0
while (j < gs.length) : begin
local incN 1
do : foreach [subtable : items-of lut.subtables] : begin
local matchT subtable.match
local ib subtable.inputBegins
local foundMatch true
for [local k 0] (foundMatch && k < matchT.length) [inc k] : begin
if [not gs.(j + k - ib)]
: then : set foundMatch false
: else : if ([matchT.(k).indexOf gs.(j + k - ib)] < 0) : set foundMatch false
if foundMatch : begin
foreach [app : items-of subtable.apply] : do
local aj : j - ib + app.at
local alut lookups.(app.lookup)
interpretLookupAt gs aj alut
set incN : incN + subtable.inputEnds - subtable.inputBegins
break nothing
set j : j + incN
"gsub_reverse" : begin
local j (gs.length - 1)
while (j >= 0) : begin
do : foreach [subtable : items-of lut.subtables] : begin
local matchT subtable.match
local ib subtable.inputIndex
local foundMatch true
for [local k 0] (foundMatch && k < matchT.length) [inc k] : begin
if [not gs.(j + k - ib)]
: then : set foundMatch false
: else : if ([matchT.(k).indexOf gs.(j + k - ib)] < 0) : set foundMatch false
if foundMatch : begin
set gs.(j) : subtable.to.[matchT.(ib).indexOf gs.(j)] || gs.(j)
set j : j - 1
"gsub_single" : begin
for [local j 0] (j < gs.length) [inc j] : begin
interpretLookupAt gs j lut
define [interpretLookupAt gs j lut] : match lut.type
"gsub_single" : foreach [subtable : items-of lut.subtables] : begin
if subtable.(gs.(j)) : begin
set gs.(j) subtable.(gs.(j))
export : define [apply para glyphs glyphList unicodeGlyphs] : begin
local markGlyphs {.all {} }
local GPOS : buildGPOS para glyphs glyphList markGlyphs
local GDEF : buildGDEF para glyphs glyphList markGlyphs
local GSUB : buildGSUB para glyphs glyphList markGlyphs
# Build compatibility ligatures
if (para.spacing > 0 && para.compLig) : foreach [cldef : items-of para.compLig] : do
if [not cldef.unicode] : break nothing
if [not cldef.featureTag] : break nothing
if [not GSUB.features.(cldef.featureTag)] : break nothing
if [not cldef.sequence] : break nothing
local gnames {}
for [local j 0] [j < cldef.sequence.length] [inc j] : begin
if [not unicodeGlyphs.[cldef.sequence.charCodeAt j]] : break nothing
gnames.push unicodeGlyphs.[cldef.sequence.charCodeAt j].name
interpretLookups gnames GSUB.features.(cldef.featureTag) GSUB.lookups
local g1 : new Glyph ('$clig.' + cldef.unicode)
set g1.advanceWidth 0
set g1.cmpPriority 1
set g1.unicode {cldef.unicode}
foreach [gn : items-of gnames] : begin
local g glyphs.(gn)
g1.apply-transform : new Transform 1 0 0 1 (-g1.advanceWidth) 0
g1.include g
g1.apply-transform : new Transform 1 0 0 1 (g1.advanceWidth) 0
set g1.advanceWidth : g1.advanceWidth + g.advanceWidth
set glyphs.(g1.name) g1
set unicodeGlyphs.(cldef.unicode) g1
glyphList.push g1
set GDEF.glyphClassDef.(g1.name) GDEF_LIGATURE
return [object GSUB GPOS GDEF]