package harfbuzz
import (
"sort"
"github.com/go-text/typesetting/font"
ot "github.com/go-text/typesetting/font/opentype"
)
type rangeFlags struct {
flags GlyphMask
clusterFirst int
clusterLast int
}
type aatMap struct {
chainFlags [][]rangeFlags
}
func (map_ *aatMap ) resizeChainFlags (N int ) {
if cap (map_ .chainFlags ) >= N {
map_ .chainFlags = map_ .chainFlags [0 :N ]
} else {
map_ .chainFlags = make ([][]rangeFlags , N )
}
}
type aatFeatureRange struct {
info aatFeatureInfo
start, end int
}
type aatFeatureEvent struct {
index int
start bool
feature aatFeatureInfo
}
func (a aatFeatureEvent ) isLess (b aatFeatureEvent ) bool {
if a .index < b .index {
return true
} else if a .index > b .index {
return false
} else {
if !a .start && b .start {
return true
} else if a .start && !b .start {
return false
}
return a .feature .isLess (b .feature )
}
}
type aatFeatureInfo struct {
type_ aatLayoutFeatureType
setting aatLayoutFeatureSelector
isExclusive bool
}
func (fi aatFeatureInfo ) key () uint32 {
return uint32 (fi .type_ )<<16 | uint32 (fi .setting )
}
const selMask = ^aatLayoutFeatureSelector (1 )
func (a aatFeatureInfo ) isLess (b aatFeatureInfo ) bool {
if a .type_ != b .type_ {
return a .type_ < b .type_
}
if !a .isExclusive && (a .setting &selMask ) != (b .setting &selMask ) {
return a .setting < b .setting
}
return false
}
type aatMapBuilder struct {
tables *font .Font
props SegmentProperties
features []aatFeatureRange
currentFeatures []aatFeatureInfo
rangeFirst int
rangeLast int
}
func newAatMapBuilder(tables *font .Font , props SegmentProperties ) aatMapBuilder {
return aatMapBuilder {
tables : tables ,
props : props ,
rangeFirst : FeatureGlobalStart ,
rangeLast : FeatureGlobalEnd ,
}
}
func (mb *aatMapBuilder ) hasFeature (info aatFeatureInfo ) bool {
key := info .key ()
for i , j := 0 , len (mb .currentFeatures ); i < j ; {
h := i + (j -i )/2
entry := mb .currentFeatures [h ].key ()
if key < entry {
j = h
} else if entry < key {
i = h + 1
} else {
return true
}
}
return false
}
func (mb *aatMapBuilder ) compileMap (map_ *aatMap ) {
morx := mb .tables .Morx
map_ .resizeChainFlags (len (morx ))
for i , chain := range morx {
map_ .chainFlags [i ] = append (map_ .chainFlags [i ], rangeFlags {
flags : mb .compileMorxFlag (chain ),
clusterFirst : mb .rangeFirst ,
clusterLast : mb .rangeLast ,
})
}
}
func (mb *aatMapBuilder ) compileMorxFlag (chain font .MorxChain ) GlyphMask {
flags := chain .DefaultFlags
for _ , feature := range chain .Features {
type_ , setting := feature .FeatureType , feature .FeatureSetting
retry :
info := aatFeatureInfo {type_ , setting , false }
if mb .hasFeature (info ) {
flags &= feature .DisableFlags
flags |= feature .EnableFlags
} else if type_ == aatLayoutFeatureTypeLetterCase && setting == aatLayoutFeatureSelectorSmallCaps {
type_ = aatLayoutFeatureTypeLowerCase
setting = aatLayoutFeatureSelectorLowerCaseSmallCaps
goto retry
} else if type_ == aatLayoutFeatureTypeLanguageTagType && setting != 0 && langMatches (string (mb .tables .Ltag .Language (setting -1 )), string (mb .props .Language )) {
flags &= feature .DisableFlags
flags |= feature .EnableFlags
}
}
return flags
}
func (mb *aatMapBuilder ) addFeature (feature Feature ) {
feat := mb .tables .Feat
if len (feat .Names ) == 0 {
return
}
if feature .Tag == ot .NewTag ('a' , 'a' , 'l' , 't' ) {
if fn := feat .GetFeature (aatLayoutFeatureTypeCharacterAlternatives ); fn == nil || len (fn .SettingTable ) == 0 {
return
}
range_ := aatFeatureRange {
info : aatFeatureInfo {
type_ : aatLayoutFeatureTypeCharacterAlternatives ,
setting : aatLayoutFeatureSelector (feature .Value ),
isExclusive : true ,
},
start : feature .Start ,
end : feature .End ,
}
mb .features = append (mb .features , range_ )
return
}
mapping := aatLayoutFindFeatureMapping (feature .Tag )
if mapping == nil {
return
}
featureName := feat .GetFeature (mapping .aatFeatureType )
if featureName == nil || len (featureName .SettingTable ) == 0 {
if mapping .aatFeatureType == aatLayoutFeatureTypeLowerCase &&
mapping .selectorToEnable == aatLayoutFeatureSelectorLowerCaseSmallCaps {
featureName = feat .GetFeature (aatLayoutFeatureTypeLetterCase )
if featureName == nil || len (featureName .SettingTable ) == 0 {
return
}
} else {
return
}
}
var info aatFeatureInfo
info .type_ = mapping .aatFeatureType
if feature .Value != 0 {
info .setting = mapping .selectorToEnable
} else {
info .setting = mapping .selectorToDisable
}
info .isExclusive = featureName .IsExclusive ()
mb .features = append (mb .features , aatFeatureRange {
info : info ,
start : feature .Start ,
end : feature .End ,
})
}
func (mb *aatMapBuilder ) compile (m *aatMap ) {
if len (mb .features ) == 0 {
mb .compileMap (m )
return
}
featureEvents := make ([]aatFeatureEvent , 0 , 2 *len (mb .features )+1 )
for _ , feature := range mb .features {
if feature .start == feature .end {
continue
}
featureEvents = append (featureEvents , aatFeatureEvent {
index : feature .start ,
start : true ,
feature : feature .info ,
}, aatFeatureEvent {
index : feature .end ,
start : false ,
feature : feature .info ,
})
}
sort .SliceStable (featureEvents , func (i , j int ) bool { return featureEvents [i ].isLess (featureEvents [j ]) })
{
featureEvents = append (featureEvents , aatFeatureEvent {
index : -1 ,
start : false ,
feature : aatFeatureInfo {},
})
}
var activeFeatures []aatFeatureInfo
lastIndex := 0
for _ , event := range featureEvents {
if event .index != lastIndex {
mb .currentFeatures = activeFeatures
mb .rangeFirst = lastIndex
mb .rangeLast = event .index - 1
if len (mb .currentFeatures ) != 0 {
sort .SliceStable (mb .currentFeatures , func (i , j int ) bool {
return mb .currentFeatures [i ].isLess (mb .currentFeatures [j ])
})
j := 0
for i := 1 ; i < len (mb .currentFeatures ); i ++ {
if mb .currentFeatures [i ].type_ != mb .currentFeatures [j ].type_ ||
(!mb .currentFeatures [i ].isExclusive && ((mb .currentFeatures [i ].setting & selMask ) != (mb .currentFeatures [j ].setting & selMask ))) {
j ++
mb .currentFeatures [j ] = mb .currentFeatures [i ]
}
}
mb .currentFeatures = mb .currentFeatures [:j +1 ]
}
mb .compileMap (m )
lastIndex = event .index
}
if event .start {
activeFeatures = append (activeFeatures , event .feature )
} else {
for i , f := range activeFeatures {
if f .key () == event .feature .key () {
activeFeatures = append (activeFeatures [:i ], activeFeatures [i +1 :]...)
break
}
}
}
}
for _ , chainFlags := range m .chainFlags {
chainFlags [len (chainFlags )-1 ].clusterLast = FeatureGlobalEnd
}
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .