package harfbuzz
import (
"fmt"
"sort"
ot "github.com/go-text/typesetting/font/opentype"
"github.com/go-text/typesetting/font/opentype/tables"
ucd "github.com/go-text/typesetting/internal/unicodedata"
"github.com/go-text/typesetting/language"
)
const (
posStart = iota
posRaToBecomeReph
posPreM
posPreC
posBaseC
posAfterMain
posAboveC
posBeforeSub
posBelowC
posAfterSub
posBeforePost
posPostC
posAfterPost
posSmvd
posEnd
)
var _ otComplexShaper = (*complexShaperIndic )(nil )
type complexShaperIndic struct {
complexShaperNil
plan indicShapePlan
}
const (
consonantFlags = 1 <<indSM_ex_C | 1 <<indSM_ex_CS |
1 <<indSM_ex_Ra | 1 <<indSM_ex_CM | 1 <<indSM_ex_V |
1 <<indSM_ex_PLACEHOLDER | 1 <<indSM_ex_DOTTEDCIRCLE
joinerFlags = 1 <<indSM_ex_ZWJ | 1 <<indSM_ex_ZWNJ
)
func isOneOf(info *GlyphInfo , flags uint32 ) bool {
if info .ligated () {
return false
}
return 1 <<info .complexCategory &flags != 0
}
func isJoiner(info *GlyphInfo ) bool {
return isOneOf (info , joinerFlags )
}
func isConsonant(info *GlyphInfo ) bool {
return isOneOf (info , consonantFlags )
}
func isHalant(info *GlyphInfo ) bool {
return isOneOf (info , 1 <<indSM_ex_H )
}
func (info *GlyphInfo ) setIndicProperties () {
u := info .codepoint
type_ := indicGetCategories (u )
info .complexCategory , info .complexAux = uint8 (type_ &0xFF ), uint8 (type_ >>8 )
}
type indicWouldSubstituteFeature struct {
lookups []lookupMap
zeroContext bool
}
func newIndicWouldSubstituteFeature(map_ *otMap , featureTag tables .Tag , zeroContext bool ) indicWouldSubstituteFeature {
var out indicWouldSubstituteFeature
out .zeroContext = zeroContext
out .lookups = map_ .getStageLookups (0 , map_ .getFeatureStage (0 , featureTag ))
return out
}
func (ws indicWouldSubstituteFeature ) wouldSubstitute (glyphs []GID , font *Font ) bool {
for _ , lk := range ws .lookups {
if otLayoutLookupWouldSubstitute (font , lk .index , glyphs , ws .zeroContext ) {
return true
}
}
return false
}
const (
rephPosAfterMain = posAfterMain
rephPosBeforeSub = posBeforeSub
rephPosAfterSub = posAfterSub
rephPosBeforePost = posBeforePost
rephPosAfterPost = posAfterPost
)
const (
rephModeImplicit = iota
rephModeExplicit
rephModeLogRepha
)
const (
blwfModePreAndPost = iota
blwfModePostOnly
)
type indicConfig struct {
script language .Script
hasOldSpec bool
virama rune
rephPos uint8
rephMode uint8
blwfMode uint8
}
var indicConfigs = [...]indicConfig {
{0 , false , 0 , rephPosBeforePost , rephModeImplicit , blwfModePreAndPost },
{language .Devanagari , true , 0x094D , rephPosBeforePost , rephModeImplicit , blwfModePreAndPost },
{language .Bengali , true , 0x09CD , rephPosAfterSub , rephModeImplicit , blwfModePreAndPost },
{language .Gurmukhi , true , 0x0A4D , rephPosBeforeSub , rephModeImplicit , blwfModePreAndPost },
{language .Gujarati , true , 0x0ACD , rephPosBeforePost , rephModeImplicit , blwfModePreAndPost },
{language .Oriya , true , 0x0B4D , rephPosAfterMain , rephModeImplicit , blwfModePreAndPost },
{language .Tamil , true , 0x0BCD , rephPosAfterPost , rephModeImplicit , blwfModePreAndPost },
{language .Telugu , true , 0x0C4D , rephPosAfterPost , rephModeExplicit , blwfModePostOnly },
{language .Kannada , true , 0x0CCD , rephPosAfterPost , rephModeImplicit , blwfModePostOnly },
{language .Malayalam , true , 0x0D4D , rephPosAfterMain , rephModeLogRepha , blwfModePreAndPost },
}
var indicFeatures = [...]otMapFeature {
{ot .NewTag ('n' , 'u' , 'k' , 't' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('a' , 'k' , 'h' , 'n' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('r' , 'p' , 'h' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('r' , 'k' , 'r' , 'f' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('p' , 'r' , 'e' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('b' , 'l' , 'w' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('a' , 'b' , 'v' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('h' , 'a' , 'l' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('p' , 's' , 't' , 'f' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('v' , 'a' , 't' , 'u' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('c' , 'j' , 'c' , 't' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('i' , 'n' , 'i' , 't' ), ffManualJoiners | ffPerSyllable },
{ot .NewTag ('p' , 'r' , 'e' , 's' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('a' , 'b' , 'v' , 's' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('b' , 'l' , 'w' , 's' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('p' , 's' , 't' , 's' ), ffGlobalManualJoiners | ffPerSyllable },
{ot .NewTag ('h' , 'a' , 'l' , 'n' ), ffGlobalManualJoiners | ffPerSyllable },
}
const (
indicNukt = iota
indicAkhn
indicRphf
indicRkrf
indicPref
indicBlwf
indicAbvf
indicHalf
indicPstf
indicVatu
indicCjct
indicInit
indicPres
indicAbvs
indicBlws
indicPsts
indicHaln
indicNumFeatures
indicBasicFeatures = indicInit
)
func (cs *complexShaperIndic ) collectFeatures (plan *otShapePlanner ) {
map_ := &plan .otMap
map_ .addGSUBPause (setupSyllablesIndic )
map_ .enableFeatureExt (ot .NewTag ('l' , 'o' , 'c' , 'l' ), ffPerSyllable , 1 )
map_ .enableFeatureExt (ot .NewTag ('c' , 'c' , 'm' , 'p' ), ffPerSyllable , 1 )
i := 0
map_ .addGSUBPause (cs .initialReorderingIndic )
for ; i < indicBasicFeatures ; i ++ {
map_ .addFeatureExt (indicFeatures [i ].tag , indicFeatures [i ].flags , 1 )
map_ .addGSUBPause (nil )
}
map_ .addGSUBPause (cs .plan .finalReorderingIndic )
for ; i < indicNumFeatures ; i ++ {
map_ .addFeatureExt (indicFeatures [i ].tag , indicFeatures [i ].flags , 1 )
}
}
func (complexShaperIndic ) overrideFeatures (plan *otShapePlanner ) {
plan .otMap .disableFeature (ot .NewTag ('l' , 'i' , 'g' , 'a' ))
plan .otMap .addGSUBPause (nil )
}
type indicShapePlan struct {
blwf indicWouldSubstituteFeature
pstf indicWouldSubstituteFeature
vatu indicWouldSubstituteFeature
rphf indicWouldSubstituteFeature
pref indicWouldSubstituteFeature
maskArray [indicNumFeatures ]GlyphMask
config indicConfig
viramaGlyph GID
isOldSpec bool
}
func (indicPlan *indicShapePlan ) loadViramaGlyph (font *Font ) GID {
if indicPlan .viramaGlyph == ^GID (0 ) {
glyph , ok := font .face .NominalGlyph (indicPlan .config .virama )
if indicPlan .config .virama == 0 || !ok {
glyph = 0
}
indicPlan .viramaGlyph = glyph
}
return indicPlan .viramaGlyph
}
func (cs *complexShaperIndic ) dataCreate (plan *otShapePlan ) {
var indicPlan indicShapePlan
indicPlan .config = indicConfigs [0 ]
for i := 1 ; i < len (indicConfigs ); i ++ {
if plan .props .Script == indicConfigs [i ].script {
indicPlan .config = indicConfigs [i ]
break
}
}
indicPlan .isOldSpec = indicPlan .config .hasOldSpec && ((plan .otMap .chosenScript [0 ] & 0x000000FF ) != '2' )
indicPlan .viramaGlyph = ^GID (0 )
zeroContext := !indicPlan .isOldSpec && plan .props .Script != language .Malayalam
indicPlan .rphf = newIndicWouldSubstituteFeature (&plan .otMap , ot .NewTag ('r' , 'p' , 'h' , 'f' ), zeroContext )
indicPlan .pref = newIndicWouldSubstituteFeature (&plan .otMap , ot .NewTag ('p' , 'r' , 'e' , 'f' ), zeroContext )
indicPlan .blwf = newIndicWouldSubstituteFeature (&plan .otMap , ot .NewTag ('b' , 'l' , 'w' , 'f' ), zeroContext )
indicPlan .pstf = newIndicWouldSubstituteFeature (&plan .otMap , ot .NewTag ('p' , 's' , 't' , 'f' ), zeroContext )
indicPlan .vatu = newIndicWouldSubstituteFeature (&plan .otMap , ot .NewTag ('v' , 'a' , 't' , 'u' ), zeroContext )
for i := range indicPlan .maskArray {
if indicFeatures [i ].flags &ffGLOBAL != 0 {
indicPlan .maskArray [i ] = 0
} else {
indicPlan .maskArray [i ] = plan .otMap .getMask1 (indicFeatures [i ].tag )
}
}
cs .plan = indicPlan
}
func (indicPlan *indicShapePlan ) consonantPositionFromFace (consonant , virama GID , font *Font ) uint8 {
glyphs := [3 ]GID {virama , consonant , virama }
if indicPlan .blwf .wouldSubstitute (glyphs [0 :2 ], font ) ||
indicPlan .blwf .wouldSubstitute (glyphs [1 :3 ], font ) ||
indicPlan .vatu .wouldSubstitute (glyphs [0 :2 ], font ) ||
indicPlan .vatu .wouldSubstitute (glyphs [1 :3 ], font ) {
return posBelowC
}
if indicPlan .pstf .wouldSubstitute (glyphs [0 :2 ], font ) ||
indicPlan .pstf .wouldSubstitute (glyphs [1 :3 ], font ) {
return posPostC
}
if indicPlan .pref .wouldSubstitute (glyphs [0 :2 ], font ) ||
indicPlan .pref .wouldSubstitute (glyphs [1 :3 ], font ) {
return posPostC
}
return posBaseC
}
func (cs *complexShaperIndic ) setupMasks (plan *otShapePlan , buffer *Buffer , _ *Font ) {
info := buffer .Info
for i := range info {
info [i ].setIndicProperties ()
}
}
func setupSyllablesIndic(_ *otShapePlan , _ *Font , buffer *Buffer ) bool {
findSyllablesIndic (buffer )
iter , count := buffer .syllableIterator ()
for start , end := iter .next (); start < count ; start , end = iter .next () {
buffer .unsafeToBreak (start , end )
}
return false
}
func foundSyllableIndic(syllableType uint8 , ts , te int , info []GlyphInfo , syllableSerial *uint8 ) {
for i := ts ; i < te ; i ++ {
info [i ].syllable = (*syllableSerial << 4 ) | syllableType
}
*syllableSerial ++
if *syllableSerial == 16 {
*syllableSerial = 1
}
}
func (indicPlan *indicShapePlan ) updateConsonantPositionsIndic (font *Font , buffer *Buffer ) {
virama := indicPlan .loadViramaGlyph (font )
if virama != 0 {
info := buffer .Info
for i := range info {
if info [i ].complexAux == posBaseC {
consonant := info [i ].Glyph
info [i ].complexAux = indicPlan .consonantPositionFromFace (consonant , virama , font )
}
}
}
}
func (indicPlan *indicShapePlan ) initialReorderingConsonantSyllable (font *Font , buffer *Buffer , start , end int ) {
info := buffer .Info
if buffer .Props .Script == language .Kannada &&
start +3 <= end &&
isOneOf (&info [start ], 1 <<indSM_ex_Ra ) &&
isOneOf (&info [start +1 ], 1 <<indSM_ex_H ) &&
isOneOf (&info [start +2 ], 1 <<indSM_ex_ZWJ ) {
buffer .mergeClusters (start +1 , start +3 )
info [start +1 ], info [start +2 ] = info [start +2 ], info [start +1 ]
}
base := end
hasReph := false
{
limit := start
if indicPlan .maskArray [indicRphf ] != 0 && start +3 <= end &&
((indicPlan .config .rephMode == rephModeImplicit && !isJoiner (&info [start +2 ])) ||
(indicPlan .config .rephMode == rephModeExplicit && info [start +2 ].complexCategory == indSM_ex_ZWJ )) {
glyphs := [3 ]GID {info [start ].Glyph , info [start +1 ].Glyph , 0 }
if indicPlan .config .rephMode == rephModeExplicit {
glyphs [2 ] = info [start +2 ].Glyph
}
if indicPlan .rphf .wouldSubstitute (glyphs [:2 ], font ) ||
(indicPlan .config .rephMode == rephModeExplicit &&
indicPlan .rphf .wouldSubstitute (glyphs [:3 ], font )) {
limit += 2
for limit < end && isJoiner (&info [limit ]) {
limit ++
}
base = start
hasReph = true
}
} else if indicPlan .config .rephMode == rephModeLogRepha && info [start ].complexCategory == indSM_ex_Repha {
limit += 1
for limit < end && isJoiner (&info [limit ]) {
limit ++
}
base = start
hasReph = true
}
{
i := end
seenBelow := false
for do := true ; do ; do = i > limit {
i --
if isConsonant (&info [i ]) {
if info [i ].complexAux != posBelowC &&
(info [i ].complexAux != posPostC || seenBelow ) {
base = i
break
}
if info [i ].complexAux == posBelowC {
seenBelow = true
}
base = i
} else {
if start < i &&
info [i ].complexCategory == indSM_ex_ZWJ &&
info [i -1 ].complexCategory == indSM_ex_H {
break
}
}
}
}
if hasReph && base == start && limit -base <= 2 {
hasReph = false
}
}
for i := start ; i < base ; i ++ {
info [i ].complexAux = min8 (posPreC , info [i ].complexAux )
}
if base < end {
info [base ].complexAux = posBaseC
}
if hasReph {
info [start ].complexAux = posRaToBecomeReph
}
if indicPlan .isOldSpec {
disallowDoubleHalants := buffer .Props .Script == language .Kannada
for i := base + 1 ; i < end ; i ++ {
if info [i ].complexCategory == indSM_ex_H {
var j int
for j = end - 1 ; j > i ; j -- {
if isConsonant (&info [j ]) ||
(disallowDoubleHalants && info [j ].complexCategory == indSM_ex_H ) {
break
}
}
if info [j ].complexCategory != indSM_ex_H && j > i {
if debugMode {
fmt .Printf ("INDIC - halant: switching glyph %d to %d (and shifting between)" , i , j )
}
t := info [i ]
copy (info [i :j ], info [i +1 :])
info [j ] = t
}
break
}
}
}
{
var lastPos uint8 = posStart
for i := start ; i < end ; i ++ {
if 1 <<info [i ].complexCategory &(joinerFlags |1 <<indSM_ex_N |1 <<indSM_ex_RS |1 <<indSM_ex_CM |1 <<indSM_ex_H ) != 0 {
info [i ].complexAux = lastPos
if info [i ].complexCategory == indSM_ex_H && info [i ].complexAux == posPreM {
for j := i ; j > start ; j -- {
if info [j -1 ].complexAux != posPreM {
info [i ].complexAux = info [j -1 ].complexAux
break
}
}
}
} else if info [i ].complexAux != posSmvd {
if info [i ].complexCategory == indSM_ex_MPst &&
i > start && info [i -1 ].complexCategory == indSM_ex_SM {
info [i -1 ].complexAux = info [i ].complexAux
}
lastPos = info [i ].complexAux
}
}
}
{
last := base
for i := base + 1 ; i < end ; i ++ {
if isConsonant (&info [i ]) {
for j := last + 1 ; j < i ; j ++ {
if info [j ].complexAux < posSmvd {
info [j ].complexAux = info [i ].complexAux
}
}
last = i
} else if ic := info [i ].complexCategory ; ic == indSM_ex_M || ic == indSM_ex_MPst {
last = i
}
}
}
{
syllable := info [start ].syllable
for i := start ; i < end ; i ++ {
info [i ].syllable = uint8 (i - start )
}
if debugMode {
fmt .Printf ("INDIC - post-base: sorting between glyph %d and %d\n" , start , end )
}
subSlice := info [start :end ]
sort .SliceStable (subSlice , func (i , j int ) bool { return subSlice [i ].complexAux < subSlice [j ].complexAux })
firstLeftMatra := end
lastLeftMatra := end
base = end
for i := start ; i < end ; i ++ {
if info [i ].complexAux == posBaseC {
base = i
break
} else if info [i ].complexAux == posPreM {
if firstLeftMatra == end {
firstLeftMatra = i
}
lastLeftMatra = i
}
}
if firstLeftMatra < lastLeftMatra {
buffer .reverseRange (firstLeftMatra , lastLeftMatra +1 )
i := firstLeftMatra
for j := i ; j <= lastLeftMatra ; j ++ {
if ic := info [j ].complexCategory ; ic == indSM_ex_M || ic == indSM_ex_MPst {
buffer .reverseRange (i , j +1 )
i = j + 1
}
}
}
if indicPlan .isOldSpec || end -start > 127 {
buffer .mergeClusters (base , end )
} else {
for i := base ; i < end ; i ++ {
if info [i ].syllable != 255 {
ma , mi := i , i
j := start + int (info [i ].syllable )
for j != i {
mi = min (mi , j )
ma = max (ma , j )
next := start + int (info [j ].syllable )
info [j ].syllable = 255
j = next
}
buffer .mergeClusters (max (base , mi ), ma +1 )
}
}
}
for i := start ; i < end ; i ++ {
info [i ].syllable = syllable
}
}
{
var mask GlyphMask
for i := start ; i < end && info [i ].complexAux == posRaToBecomeReph ; i ++ {
info [i ].Mask |= indicPlan .maskArray [indicRphf ]
}
mask = indicPlan .maskArray [indicHalf ]
if !indicPlan .isOldSpec &&
indicPlan .config .blwfMode == blwfModePreAndPost {
mask |= indicPlan .maskArray [indicBlwf ]
}
for i := start ; i < base ; i ++ {
info [i ].Mask |= mask
}
mask = 0
if base < end {
info [base ].Mask |= mask
}
mask = indicPlan .maskArray [indicBlwf ] |
indicPlan .maskArray [indicAbvf ] |
indicPlan .maskArray [indicPstf ]
for i := base + 1 ; i < end ; i ++ {
info [i ].Mask |= mask
}
}
if indicPlan .isOldSpec &&
buffer .Props .Script == language .Devanagari {
for i := start ; i +1 < base ; i ++ {
if info [i ].complexCategory == indSM_ex_Ra &&
info [i +1 ].complexCategory == indSM_ex_H &&
(i +2 == base ||
info [i +2 ].complexCategory != indSM_ex_ZWJ ) {
info [i ].Mask |= indicPlan .maskArray [indicBlwf ]
info [i +1 ].Mask |= indicPlan .maskArray [indicBlwf ]
}
}
}
prefLen := 2
if indicPlan .maskArray [indicPref ] != 0 && base +prefLen < end {
for i := base + 1 ; i +prefLen -1 < end ; i ++ {
var glyphs [2 ]GID
for j := 0 ; j < prefLen ; j ++ {
glyphs [j ] = info [i +j ].Glyph
}
if indicPlan .pref .wouldSubstitute (glyphs [:prefLen ], font ) {
for j := 0 ; j < prefLen ; j ++ {
info [i ].Mask |= indicPlan .maskArray [indicPref ]
i ++
}
break
}
}
}
for i := start + 1 ; i < end ; i ++ {
if isJoiner (&info [i ]) {
nonJoiner := info [i ].complexCategory == indSM_ex_ZWNJ
j := i
for do := true ; do ; do = (j > start && !isConsonant (&info [j ])) {
j --
if nonJoiner {
info [j ].Mask &= ^indicPlan .maskArray [indicHalf ]
}
}
}
}
}
func (indicPlan *indicShapePlan ) initialReorderingStandaloneCluster (font *Font , buffer *Buffer , start , end int ) {
indicPlan .initialReorderingConsonantSyllable (font , buffer , start , end )
}
func (indicPlan *indicShapePlan ) initialReorderingSyllableIndic (font *Font , buffer *Buffer , start , end int ) {
syllableType := (buffer .Info [start ].syllable & 0x0F )
switch syllableType {
case indicVowelSyllable , indicConsonantSyllable :
indicPlan .initialReorderingConsonantSyllable (font , buffer , start , end )
case indicBrokenCluster , indicStandaloneCluster :
indicPlan .initialReorderingStandaloneCluster (font , buffer , start , end )
}
}
func (cs *complexShaperIndic ) initialReorderingIndic (_ *otShapePlan , font *Font , buffer *Buffer ) bool {
if debugMode {
fmt .Println ("INDIC - start reordering indic initial" )
}
cs .plan .updateConsonantPositionsIndic (font , buffer )
ret := syllabicInsertDottedCircles (font , buffer , indicBrokenCluster ,
indSM_ex_DOTTEDCIRCLE , indSM_ex_Repha , posEnd )
iter , count := buffer .syllableIterator ()
for start , end := iter .next (); start < count ; start , end = iter .next () {
cs .plan .initialReorderingSyllableIndic (font , buffer , start , end )
}
if debugMode {
fmt .Println ("INDIC - end reordering indic initial" )
}
return ret
}
func (indicPlan *indicShapePlan ) finalReorderingSyllableIndic (buffer *Buffer , start , end int ) {
info := buffer .Info
viramaGlyph := indicPlan .viramaGlyph
if viramaGlyph != 0 {
for i := start ; i < end ; i ++ {
if info [i ].Glyph == viramaGlyph &&
info [i ].ligated () && info [i ].multiplied () {
info [i ].complexCategory = indSM_ex_H
info [i ].clearLigatedAndMultiplied ()
}
}
}
tryPref := indicPlan .maskArray [indicPref ] != 0
var base int
for base = start ; base < end ; base ++ {
if info [base ].complexAux >= posBaseC {
if tryPref && base +1 < end {
for i := base + 1 ; i < end ; i ++ {
if (info [i ].Mask & indicPlan .maskArray [indicPref ]) != 0 {
if !(info [i ].substituted () && info [i ].ligatedAndDidntMultiply ()) {
base = i
for base < end && isHalant (&info [base ]) {
base ++
}
if base < end {
info [base ].complexAux = posBaseC
}
tryPref = false
}
break
}
if base == end {
break
}
}
}
if buffer .Props .Script == language .Malayalam {
for i := base + 1 ; i < end ; i ++ {
for i < end && isJoiner (&info [i ]) {
i ++
}
if i == end || !isHalant (&info [i ]) {
break
}
i ++
for i < end && isJoiner (&info [i ]) {
i ++
}
if i < end && isConsonant (&info [i ]) && info [i ].complexAux == posBelowC {
base = i
info [base ].complexAux = posBaseC
}
}
}
if start < base && info [base ].complexAux > posBaseC {
base --
}
break
}
}
if base == end && start < base && isOneOf (&info [base -1 ], 1 <<indSM_ex_ZWJ ) {
base --
}
if base < end {
for start < base && isOneOf (&info [base ], 1 <<indSM_ex_N |1 <<indSM_ex_H ) {
base --
}
}
if start +1 < end && start < base {
newPos := base - 1
if base == end {
newPos = base - 2
}
if buffer .Props .Script != language .Malayalam && buffer .Props .Script != language .Tamil {
search :
for newPos > start && !isOneOf (&info [newPos ], 1 <<indSM_ex_M |1 <<indSM_ex_MPst |1 <<indSM_ex_H ) {
newPos --
}
if isHalant (&info [newPos ]) && info [newPos ].complexAux != posPreM {
if newPos +1 < end {
if info [newPos +1 ].complexCategory == indSM_ex_ZWJ {
if newPos > start {
newPos --
goto search
}
}
}
} else {
newPos = start
}
}
if start < newPos && info [newPos ].complexAux != posPreM {
for i := newPos ; i > start ; i -- {
if info [i -1 ].complexAux == posPreM {
oldPos := i - 1
if oldPos < base && base <= newPos {
base --
}
if debugMode {
fmt .Printf ("INDIC - matras: switching glyph %d to %d (and shifting between)" , oldPos , newPos )
}
tmp := info [oldPos ]
copy (info [oldPos :newPos ], info [oldPos +1 :])
info [newPos ] = tmp
buffer .mergeClusters (newPos , min (end , base +1 ))
newPos --
}
}
} else {
for i := start ; i < base ; i ++ {
if info [i ].complexAux == posPreM {
buffer .mergeClusters (i , min (end , base +1 ))
break
}
}
}
}
if start +1 < end && info [start ].complexAux == posRaToBecomeReph &&
(info [start ].complexCategory == indSM_ex_Repha ) != info [start ].ligatedAndDidntMultiply () {
var newRephPos int
rephPos := indicPlan .config .rephPos
if rephPos == rephPosAfterPost {
goto reph_step_5
}
{
newRephPos = start + 1
for newRephPos < base && !isHalant (&info [newRephPos ]) {
newRephPos ++
}
if newRephPos < base && isHalant (&info [newRephPos ]) {
if newRephPos +1 < base && isJoiner (&info [newRephPos +1 ]) {
newRephPos ++
}
goto reph_move
}
}
if rephPos == rephPosAfterMain {
newRephPos = base
for newRephPos +1 < end && info [newRephPos +1 ].complexAux <= posAfterMain {
newRephPos ++
}
if newRephPos < end {
goto reph_move
}
}
if rephPos == rephPosAfterSub {
newRephPos = base
for newRephPos +1 < end &&
(1 <<info [newRephPos +1 ].complexAux )&(1 <<posPostC |1 <<posAfterPost |1 <<posSmvd ) == 0 {
newRephPos ++
}
if newRephPos < end {
goto reph_move
}
}
reph_step_5 :
{
newRephPos = start + 1
for newRephPos < base && !isHalant (&info [newRephPos ]) {
newRephPos ++
}
if newRephPos < base && isHalant (&info [newRephPos ]) {
if newRephPos +1 < base && isJoiner (&info [newRephPos +1 ]) {
newRephPos ++
}
goto reph_move
}
}
{
newRephPos = end - 1
for newRephPos > start && info [newRephPos ].complexAux == posSmvd {
newRephPos --
}
if isHalant (&info [newRephPos ]) {
for i := base + 1 ; i < newRephPos ; i ++ {
if ic := info [i ].complexCategory ; ic == indSM_ex_M || ic == indSM_ex_MPst {
newRephPos --
}
}
}
goto reph_move
}
reph_move :
{
if debugMode {
fmt .Printf ("INDIC - reph: switching glyph %d to %d (and shifting between)" , start , newRephPos )
}
buffer .mergeClusters (start , newRephPos +1 )
reph := info [start ]
copy (info [start :newRephPos ], info [start +1 :])
info [newRephPos ] = reph
if start < base && base <= newRephPos {
base --
}
}
}
if tryPref && base +1 < end {
for i := base + 1 ; i < end ; i ++ {
if (info [i ].Mask & indicPlan .maskArray [indicPref ]) != 0 {
if info [i ].ligatedAndDidntMultiply () {
newPos := base
if buffer .Props .Script != language .Malayalam && buffer .Props .Script != language .Tamil {
for newPos > start && !isOneOf (&info [newPos -1 ], 1 <<indSM_ex_M |1 <<indSM_ex_MPst |1 <<indSM_ex_H ) {
newPos --
}
}
if newPos > start && isHalant (&info [newPos -1 ]) {
if newPos < end && isJoiner (&info [newPos ]) {
newPos ++
}
}
{
oldPos := i
buffer .mergeClusters (newPos , oldPos +1 )
if debugMode {
fmt .Printf ("INDIC - pre-base: switching glyph %d to %d (and shifting between)" , oldPos , newPos )
}
tmp := info [oldPos ]
copy (info [newPos +1 :], info [newPos :oldPos ])
info [newPos ] = tmp
if newPos <= base && base < oldPos {
base ++
}
}
}
break
}
}
}
if info [start ].complexAux == posPreM {
const flagRange = 1 <<ucd .Unassigned |
1 <<ucd .Co |
1 <<ucd .Cs |
1 <<ucd .Ll |
1 <<ucd .Lm |
1 <<ucd .Lo |
1 <<ucd .Lt |
1 <<ucd .Lu |
1 <<ucd .Mc |
1 <<ucd .Me |
1 <<ucd .Mn
if start == 0 || 1 <<info [start -1 ].unicode .generalCategory ()&flagRange == 0 {
info [start ].Mask |= indicPlan .maskArray [indicInit ]
} else {
buffer .unsafeToBreak (start -1 , start +1 )
}
}
}
func (indicPlan *indicShapePlan ) finalReorderingIndic (plan *otShapePlan , font *Font , buffer *Buffer ) bool {
if len (buffer .Info ) == 0 {
return false
}
if debugMode {
fmt .Println ("INDIC - start reordering indic final" )
}
iter , count := buffer .syllableIterator ()
for start , end := iter .next (); start < count ; start , end = iter .next () {
indicPlan .finalReorderingSyllableIndic (buffer , start , end )
}
if debugMode {
fmt .Println ("INDIC - end reordering indic final" )
}
return false
}
func (ci complexShaperIndic ) preprocessText (_ *otShapePlan , buffer *Buffer , _ *Font ) {
preprocessTextVowelConstraints (buffer )
}
func (cs *complexShaperIndic ) decompose (c *otNormalizeContext , ab rune ) (rune , rune , bool ) {
switch ab {
case 0x0931 ,
0x09DC ,
0x09DD ,
0x0B94 :
return 0 , 0 , false
}
return ucd .Decompose (ab )
}
func (cs *complexShaperIndic ) compose (c *otNormalizeContext , a , b rune ) (rune , bool ) {
if ucd .LookupGeneralCategory (a ).IsMark () {
return 0 , false
}
if a == 0x09AF && b == 0x09BC {
return 0x09DF , true
}
return ucd .Compose (a , b )
}
func (complexShaperIndic ) marksBehavior () (zeroWidthMarks , bool ) {
return zeroWidthMarksNone , false
}
func (complexShaperIndic ) normalizationPreference () normalizationMode {
return nmComposedDiacriticsNoShortCircuit
}
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 .