298 lines
No EOL
11 KiB
Text
298 lines
No EOL
11 KiB
Text
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
|
|
|
|
buildCCMP chain-rule markGlyphs commonList features lookups
|
|
if [not para.disableLigation] : do
|
|
define plm : progLigNameMap
|
|
set plm.calt : para.customLigSet || plm.(para.defLigSet) || plm.calt
|
|
buildLigations chain-rule lookupOrder commonList features lookups plm glyphs
|
|
if (para.spacing > 0 && [not para.noCJKV]) : begin
|
|
buildOPBD chain-rule lookupOrder commonList features lookups
|
|
|
|
# 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'}
|
|
|
|
if [not para.disableVariants] : 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))
|
|
|
|
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
|
|
local lookup_mark
|
|
.type 'gpos_mark_to_base'
|
|
.subtables {}
|
|
local lookup_mkmk
|
|
.type 'gpos_mark_to_mark'
|
|
.subtables {}
|
|
|
|
# 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}
|
|
|
|
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 {'l_mark'}
|
|
.mkmk0 {'l_mkmk'}
|
|
lookups
|
|
.l_mark lookup_mark
|
|
.l_mkmk lookup_mkmk
|
|
|
|
# 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] |