package harfbuzz
import (
ucd "github.com/go-text/typesetting/internal/unicodedata"
"github.com/go-text/typesetting/language"
)
var _ otComplexShaper = complexShaperThai {}
type complexShaperThai struct {
complexShaperNil
}
const (
tcNC = iota
tcAC
tcRC
tcDC
tcNOTCONSONANT
numConsonantTypes = tcNOTCONSONANT
)
func getConsonantType(u rune ) uint8 {
switch u {
case 0x0E1B , 0x0E1D , 0x0E1F :
return tcAC
case 0x0E0D , 0x0E10 :
return tcRC
case 0x0E0E , 0x0E0F :
return tcDC
}
if 0x0E01 <= u && u <= 0x0E2E {
return tcNC
}
return tcNOTCONSONANT
}
const (
tmAV = iota
tmBV
tmT
tmNOTMARK
numMarkTypes = tmNOTMARK
)
func getMarkType(u rune ) uint8 {
if u == 0x0E31 || (0x0E34 <= u && u <= 0x0E37 ) ||
u == 0x0E47 || (0x0E4D <= u && u <= 0x0E4E ) {
return tmAV
}
if 0x0E38 <= u && u <= 0x0E3A {
return tmBV
}
if 0x0E48 <= u && u <= 0x0E4C {
return tmT
}
return tmNOTMARK
}
const (
tcNOP = iota
tcSD
tcSL
tcSDL
tcRD
)
type thaiPuaMapping struct {
u, winPua, macPua rune
}
var (
sdMappings = [...]thaiPuaMapping {
{0x0E48 , 0xF70A , 0xF88B },
{0x0E49 , 0xF70B , 0xF88E },
{0x0E4A , 0xF70C , 0xF891 },
{0x0E4B , 0xF70D , 0xF894 },
{0x0E4C , 0xF70E , 0xF897 },
{0x0E38 , 0xF718 , 0xF89B },
{0x0E39 , 0xF719 , 0xF89C },
{0x0E3A , 0xF71A , 0xF89D },
{0x0000 , 0x0000 , 0x0000 },
}
sdlMappings = [...]thaiPuaMapping {
{0x0E48 , 0xF705 , 0xF88C },
{0x0E49 , 0xF706 , 0xF88F },
{0x0E4A , 0xF707 , 0xF892 },
{0x0E4B , 0xF708 , 0xF895 },
{0x0E4C , 0xF709 , 0xF898 },
{0x0000 , 0x0000 , 0x0000 },
}
slMappings = [...]thaiPuaMapping {
{0x0E48 , 0xF713 , 0xF88A },
{0x0E49 , 0xF714 , 0xF88D },
{0x0E4A , 0xF715 , 0xF890 },
{0x0E4B , 0xF716 , 0xF893 },
{0x0E4C , 0xF717 , 0xF896 },
{0x0E31 , 0xF710 , 0xF884 },
{0x0E34 , 0xF701 , 0xF885 },
{0x0E35 , 0xF702 , 0xF886 },
{0x0E36 , 0xF703 , 0xF887 },
{0x0E37 , 0xF704 , 0xF888 },
{0x0E47 , 0xF712 , 0xF889 },
{0x0E4D , 0xF711 , 0xF899 },
{0x0000 , 0x0000 , 0x0000 },
}
rdMappings = [...]thaiPuaMapping {
{0x0E0D , 0xF70F , 0xF89A },
{0x0E10 , 0xF700 , 0xF89E },
{0x0000 , 0x0000 , 0x0000 },
}
)
func thaiPuaShape(u rune , action uint8 , font *Font ) rune {
var puaMappings []thaiPuaMapping
switch action {
case tcNOP :
return u
case tcSD :
puaMappings = sdMappings [:]
case tcSDL :
puaMappings = sdlMappings [:]
case tcSL :
puaMappings = slMappings [:]
case tcRD :
puaMappings = rdMappings [:]
}
for _ , pua := range puaMappings {
if pua .u == u {
_ , ok := font .face .NominalGlyph (pua .winPua )
if ok {
return pua .winPua
}
_, ok = font .face .NominalGlyph (pua .macPua )
if ok {
return pua .macPua
}
break
}
}
return u
}
const (
tcT0 = iota
tcT1
tcT2
tcT3
numAboveStates
)
var thaiAboveStartState = [numConsonantTypes + 1 ] uint8 {
tcT0 ,
tcT1 ,
tcT0 ,
tcT0 ,
tcT3 ,
}
var thaiAboveStateMachine = [numAboveStates ][numMarkTypes ]struct {
action uint8
nextState uint8
}{
{{tcNOP , tcT3 }, {tcNOP , tcT0 }, {tcSD , tcT3 }},
{{tcSL , tcT2 }, {tcNOP , tcT1 }, {tcSDL , tcT2 }},
{{tcNOP , tcT3 }, {tcNOP , tcT2 }, {tcSL , tcT3 }},
{{tcNOP , tcT3 }, {tcNOP , tcT3 }, {tcNOP , tcT3 }},
}
const (
tbB0 = iota
tbB1
tbB2
numBelowStates
)
var thaiBelowStartState = [numConsonantTypes + 1 ] uint8 {
tbB0 ,
tbB0 ,
tbB1 ,
tbB2 ,
tbB2 ,
}
var thaiBelowStateMachine = [numBelowStates ][numMarkTypes ]struct {
action uint8
nextState uint8
}{
{{tcNOP , tbB0 }, {tcNOP , tbB2 }, {tcNOP , tbB0 }},
{{tcNOP , tbB1 }, {tcRD , tbB2 }, {tcNOP , tbB1 }},
{{tcNOP , tbB2 }, {tcSD , tbB2 }, {tcNOP , tbB2 }},
}
func doThaiPuaShaping(buffer *Buffer , font *Font ) {
aboveState := thaiAboveStartState [tcNOTCONSONANT ]
belowState := thaiBelowStartState [tcNOTCONSONANT ]
base := 0
info := buffer .Info
for i := range info {
mt := getMarkType (info [i ].codepoint )
if mt == tmNOTMARK {
ct := getConsonantType (info [i ].codepoint )
aboveState = thaiAboveStartState [ct ]
belowState = thaiBelowStartState [ct ]
base = i
continue
}
aboveEdge := &thaiAboveStateMachine [aboveState ][mt ]
belowEdge := &thaiBelowStateMachine [belowState ][mt ]
aboveState = aboveEdge .nextState
belowState = belowEdge .nextState
action := belowEdge .action
if aboveEdge .action != tcNOP {
action = aboveEdge .action
}
buffer .unsafeToBreak (base , i )
if action == tcRD {
info [base ].codepoint = thaiPuaShape (info [base ].codepoint , action , font )
} else {
info [i ].codepoint = thaiPuaShape (info [i ].codepoint , action , font )
}
}
}
func isSaraAm(x rune ) bool { return x & ^0x0080 == 0x0E33 }
func nikhahitFromSaraAm(x rune ) rune { return x - 0x0E33 + 0x0E4D }
func saraAaFromSaraAm(x rune ) rune { return x - 1 }
func isAboveBaseMark(x rune ) bool {
u := x & ^0x0080
return 0x0E34 <= u && u <= 0x0E37 ||
0x0E47 <= u && u <= 0x0E4E ||
u == 0x0E31 ||
u == 0x0E3B
}
func (complexShaperThai ) preprocessText (plan *otShapePlan , buffer *Buffer , font *Font ) {
buffer .clearOutput ()
count := len (buffer .Info )
for buffer .idx = 0 ; buffer .idx < count ; {
u := buffer .cur (0 ).codepoint
if !isSaraAm (u ) {
buffer .nextGlyph ()
continue
}
buffer .outputRune (nikhahitFromSaraAm (u ))
buffer .prev ().setContinuation (buffer )
buffer .replaceGlyph (saraAaFromSaraAm (u ))
end := len (buffer .outInfo )
buffer .outInfo [end -2 ].setGeneralCategory (ucd .Mn )
start := end - 2
for start > 0 && isAboveBaseMark (buffer .outInfo [start -1 ].codepoint ) {
start --
}
if start +2 < end {
buffer .mergeOutClusters (start , end )
t := buffer .outInfo [end -2 ]
copy (buffer .outInfo [start +1 :], buffer .outInfo [start :end -2 ])
buffer .outInfo [start ] = t
} else {
if start != 0 {
buffer .mergeOutClusters (start -1 , end )
}
}
}
buffer .swapBuffers ()
if plan .props .Script == language .Thai && !plan .otMap .foundScript [0 ] {
doThaiPuaShaping (buffer , font )
}
}
func (complexShaperThai ) marksBehavior () (zeroWidthMarks , bool ) {
return zeroWidthMarksByGdefLate , false
}
func (complexShaperThai ) normalizationPreference () normalizationMode {
return nmDefault
}
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 .