package harfbuzz
import (
"fmt"
"github.com/go-text/typesetting/font"
ot "github.com/go-text/typesetting/font/opentype"
"github.com/go-text/typesetting/font/opentype/tables"
)
const (
otRightToLeft uint16 = 1 << iota
otIgnoreBaseGlyphs
otIgnoreLigatures
otIgnoreMarks
_
_ uint16 = 0x00E0
otMarkAttachmentType uint16 = 0xFF00
)
type otLayoutMappingCache = cache15_8_7
const maxNestingLevel = 6
func (c *otApplyContext ) applyString (proxy otProxyMeta , accel *otLayoutLookupAccelerator ) {
buffer := c .buffer
lookup := accel .lookup
if len (buffer .Info ) == 0 || c .lookupMask == 0 {
return
}
c .setLookupProps (lookup .Props ())
if !lookup .isReverse () {
if !proxy .inplace {
buffer .clearOutput ()
}
buffer .idx = 0
c .applyForward (accel )
if !proxy .inplace {
buffer .swapBuffers ()
}
} else {
buffer .idx = len (buffer .Info ) - 1
c .applyBackward (accel )
}
}
func (c *otApplyContext ) applyForward (accel *otLayoutLookupAccelerator ) bool {
buffer := c .buffer
info := buffer .Info
ret := false
for {
j := buffer .idx
for j < len (info ) &&
!(accel .digest .mayHave (gID (info [j ].Glyph )) &&
(info [j ].Mask &c .lookupMask ) != 0 &&
c .checkGlyphProperty (&info [j ], c .lookupProps )) {
j ++
}
if j > buffer .idx {
buffer .nextGlyphs (j - buffer .idx )
}
if buffer .idx >= len (info ) {
break
}
applied := accel .apply (c )
if applied {
ret = true
} else {
buffer .nextGlyph ()
}
}
return ret
}
func (c *otApplyContext ) applyBackward (accel *otLayoutLookupAccelerator ) bool {
ret := false
buffer := c .buffer
for do := true ; do ; do = buffer .idx >= 0 {
cur := buffer .cur (0 )
if accel .digest .mayHave (gID (cur .Glyph )) &&
(cur .Mask &c .lookupMask != 0 ) &&
c .checkGlyphProperty (cur , c .lookupProps ) {
applied := accel .apply (c )
ret = ret || applied
}
buffer .idx --
}
return ret
}
func (m *otMap ) apply (proxy otProxy , plan *otShapePlan , font *Font , buffer *Buffer ) {
tableIndex := proxy .tableIndex
i := 0
c := &m .applyContext
c .reset (tableIndex , font , buffer )
c .recurseFunc = proxy .recurseFunc
for stageI , stage := range m .stages [tableIndex ] {
if debugMode {
fmt .Printf ("\tAPPLY - stage %d\n" , stageI )
}
for ; i < stage .lastLookup ; i ++ {
lookup := m .lookups [tableIndex ][i ]
lookupIndex := lookup .index
if debugMode {
fmt .Printf ("\t\tLookup %d start\n" , lookupIndex )
}
accel := &proxy .accels [lookupIndex ]
if accel .digest .mayIntersects (buffer .digest ) {
c .lookupIndex = lookupIndex
c .lookupMask = lookup .mask
c .autoZWJ = lookup .autoZWJ
c .autoZWNJ = lookup .autoZWNJ
c .random = lookup .random
c .perSyllable = lookup .perSyllable
if len (c .buffer .Info ) > c .buffer .maxLen {
return
}
c .applyString (proxy .otProxyMeta , accel )
}
if debugMode {
fmt .Print ("\t\tLookup end : " )
if proxy .tableIndex == 0 {
fmt .Println (c .buffer .Info )
} else {
fmt .Println (c .buffer .Pos )
}
}
}
if stage .pauseFunc != nil {
if debugMode {
fmt .Println ("\t\tExecuting pause function" )
}
if stage .pauseFunc (plan , font , buffer ) {
buffer .updateDigest ()
}
}
}
}
func hasMachineKerning(kern font .Kernx ) bool {
for _ , subtable := range kern {
if _ , isType1 := subtable .Data .(font .Kern1 ); isType1 {
return true
}
}
return false
}
func hasCrossKerning(kern font .Kernx ) bool {
for _ , subtable := range kern {
if subtable .IsCrossStream () {
return true
}
}
return false
}
func (sp *otShapePlan ) otLayoutKern (font *Font , buffer *Buffer ) {
kern := font .face .Kern
c := newAatApplyContext (sp , font , buffer )
c .applyKernx (kern , font .kernAccels )
}
var otTagLatinScript = ot .NewTag ('l' , 'a' , 't' , 'n' )
func selectScript(table *font .Layout , scriptTags []tables .Tag ) (int , tables .Tag , bool ) {
for _ , tag := range scriptTags {
if scriptIndex := table .FindScript (tag ); scriptIndex != -1 {
return scriptIndex , tag , true
}
}
if scriptIndex := table .FindScript (tagDefaultScript ); scriptIndex != -1 {
return scriptIndex , tagDefaultScript , false
}
if scriptIndex := table .FindScript (tagDefaultLanguage ); scriptIndex != -1 {
return scriptIndex , tagDefaultLanguage , false
}
if scriptIndex := table .FindScript (otTagLatinScript ); scriptIndex != -1 {
return scriptIndex , otTagLatinScript , false
}
return NoScriptIndex , NoScriptIndex , false
}
func selectLanguage(table *font .Layout , scriptIndex int , languageTags []tables .Tag ) (int , bool ) {
if scriptIndex == NoScriptIndex {
return DefaultLanguageIndex , false
}
s := table .Scripts [scriptIndex ]
for _ , lang := range languageTags {
if languageIndex := s .FindLanguage (lang ); languageIndex != -1 {
return languageIndex , true
}
}
if languageIndex := s .FindLanguage (tagDefaultLanguage ); languageIndex != -1 {
return languageIndex , false
}
return DefaultLanguageIndex , false
}
func findFeature(g *font .Layout , featureTag tables .Tag ) uint16 {
if index , ok := g .FindFeatureIndex (featureTag ); ok {
return index
}
return NoFeatureIndex
}
func findFeatureForLang(table *font .Layout , scriptIndex , languageIndex int , featureTag tables .Tag ) uint16 {
if scriptIndex == NoScriptIndex {
return NoFeatureIndex
}
l := table .Scripts [scriptIndex ].GetLangSys (uint16 (languageIndex ))
for _ , fIndex := range l .FeatureIndices {
if featureTag == table .Features [fIndex ].Tag {
return fIndex
}
}
return NoFeatureIndex
}
func getRequiredFeature(g *font .Layout , scriptIndex , languageIndex int ) (uint16 , tables .Tag ) {
if scriptIndex == NoScriptIndex {
return NoFeatureIndex , 0
}
l := g .Scripts [scriptIndex ].GetLangSys (uint16 (languageIndex ))
if l .RequiredFeatureIndex == 0xFFFF {
return NoFeatureIndex , 0
}
index := l .RequiredFeatureIndex
return index , g .Features [index ].Tag
}
func getFeatureLookupsWithVar(table *font .Layout , featureIndex uint16 , variationsIndex int ) []uint16 {
if featureIndex == NoFeatureIndex {
return nil
}
if variationsIndex == noVariationsIndex {
return table .Features [featureIndex ].LookupListIndices
}
subs := table .FeatureVariations [variationsIndex ].Substitutions .Substitutions
for _ , sub := range subs {
if sub .FeatureIndex == featureIndex {
return sub .AlternateFeature .LookupListIndices
}
}
return nil
}
func otLayoutLookupWouldSubstitute(font *Font , lookupIndex uint16 , glyphs []GID , zeroContext bool ) bool {
gsub := font .face .GSUB
if int (lookupIndex ) >= len (gsub .Lookups ) {
return false
}
c := wouldApplyContext {glyphs , nil , zeroContext }
l := lookupGSUB (gsub .Lookups [lookupIndex ])
return l .wouldApply (&c , &font .gsubAccels [lookupIndex ])
}
func layoutSubstituteStart(font *Font , buffer *Buffer ) {
gdef := font .face .GDEF
hasClass := gdef .GlyphClassDef != nil
for i := range buffer .Info {
if hasClass {
buffer .Info [i ].glyphProps = gdef .GlyphProps (gID (buffer .Info [i ].Glyph ))
}
buffer .Info [i ].ligProps = 0
buffer .Info [i ].syllable = 0
}
}
func otLayoutDeleteGlyphsInplace(buffer *Buffer , filter func (*GlyphInfo ) bool ) {
var (
j int
info = buffer .Info
pos = buffer .Pos
)
for i := range info {
if filter (&info [i ]) {
cluster := info [i ].Cluster
if i +1 < len (buffer .Info ) && cluster == info [i +1 ].Cluster {
continue
}
if j != 0 {
if cluster < info [j -1 ].Cluster {
mask := info [i ].Mask
oldCluster := info [j -1 ].Cluster
for k := j ; k != 0 && info [k -1 ].Cluster == oldCluster ; k -- {
info [k -1 ].setCluster (cluster , mask )
}
}
continue
}
if i +1 < len (buffer .Info ) {
buffer .mergeClusters (i , i +2 )
}
continue
}
if j != i {
info [j ] = info [i ]
pos [j ] = pos [i ]
}
j ++
}
buffer .Info = buffer .Info [:j ]
buffer .Pos = buffer .Pos [:j ]
}
func otLayoutPositionStart(_ *Font , buffer *Buffer ) {
positionStartGPOS (buffer )
}
func otLayoutPositionFinishOffsets(font *Font , buffer *Buffer ) {
positionFinishOffsetsGPOS (font , buffer )
}
func glyphInfoSubstituted(info *GlyphInfo ) bool {
return (info .glyphProps & substituted ) != 0
}
func clearSubstitutionFlags(_ *otShapePlan , _ *Font , buffer *Buffer ) bool {
info := buffer .Info
for i := range info {
info [i ].glyphProps &= ^substituted
}
return false
}
func reverseGraphemes(b *Buffer ) {
b .reverseGroups (func (_ , gi2 *GlyphInfo ) bool { return gi2 .isContinuation () }, b .ClusterLevel == MonotoneGraphemes )
}
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 .