package harfbuzz
import (
"fmt"
ucd "github.com/go-text/typesetting/internal/unicodedata"
)
const (
combiningClassAttachedBelowLeft = 200
combiningClassAttachedBelow = 202
combiningClassAttachedAbove = 214
combiningClassAttachedAboveRight = 216
combiningClassBelowLeft = 218
combiningClassBelow = 220
combiningClassBelowRight = 222
combiningClassLeft = 224
combiningClassRight = 226
combiningClassAboveLeft = 228
combiningClassAbove = 230
combiningClassAboveRight = 232
combiningClassDoubleBelow = 233
combiningClassDoubleAbove = 234
)
func recategorizeCombiningClass(u rune , klass uint8 ) uint8 {
if klass >= 200 {
return klass
}
if (u & ^0xFF ) == 0x0E00 {
if klass == 0 {
switch u {
case 0x0E31 , 0x0E34 , 0x0E35 , 0x0E36 , 0x0E37 , 0x0E47 , 0x0E4C , 0x0E4D , 0x0E4E :
klass = combiningClassAboveRight
case 0x0EB1 , 0x0EB4 , 0x0EB5 , 0x0EB6 , 0x0EB7 , 0x0EBB , 0x0ECC , 0x0ECD :
klass = combiningClassAbove
case 0x0EBC :
klass = combiningClassBelow
}
} else {
if u == 0x0E3A {
klass = combiningClassBelowRight
}
}
}
switch klass {
case mcc10 ,
mcc11 ,
mcc12 ,
mcc13 ,
mcc14 ,
mcc15 ,
mcc16 ,
mcc17 ,
mcc18 ,
mcc20 ,
mcc22 :
return combiningClassBelow
case mcc23 :
return combiningClassAttachedAbove
case mcc24 :
return combiningClassAboveRight
case mcc25 ,
mcc19 :
return combiningClassAboveLeft
case mcc26 :
return combiningClassAbove
case mcc21 :
case mcc27 ,
mcc28 ,
mcc30 ,
mcc31 ,
mcc33 ,
mcc34 ,
mcc35 ,
mcc36 :
return combiningClassAbove
case mcc29 ,
mcc32 :
return combiningClassBelow
case mcc103 :
return combiningClassBelowRight
case mcc107 :
return combiningClassAboveRight
case mcc118 :
return combiningClassBelow
case mcc122 :
return combiningClassAbove
case mcc129 :
return combiningClassBelow
case mcc130 :
return combiningClassAbove
case mcc132 :
return combiningClassBelow
}
return klass
}
func fallbackMarkPositionRecategorizeMarks(buffer *Buffer ) {
for i , info := range buffer .Info {
if info .unicode .generalCategory () == ucd .Mn {
combiningClass := info .getModifiedCombiningClass ()
combiningClass = recategorizeCombiningClass (info .codepoint , combiningClass )
buffer .Info [i ].setModifiedCombiningClass (combiningClass )
}
}
}
func zeroMarkAdvances(buffer *Buffer , start , end int , adjustOffsetsWhenZeroing bool ) {
info := buffer .Info
for i := start ; i < end ; i ++ {
if info [i ].unicode .generalCategory () != ucd .Mn {
continue
}
if adjustOffsetsWhenZeroing {
buffer .Pos [i ].XOffset -= buffer .Pos [i ].XAdvance
buffer .Pos [i ].YOffset -= buffer .Pos [i ].YAdvance
}
buffer .Pos [i ].XAdvance = 0
buffer .Pos [i ].YAdvance = 0
}
}
func positionMark(font *Font , buffer *Buffer , baseExtents *GlyphExtents ,
i int , combiningClass uint8 ,
) {
markExtents , ok := font .GlyphExtents (buffer .Info [i ].Glyph )
if !ok {
return
}
yGap := font .YScale / 16
pos := &buffer .Pos [i ]
pos .XOffset = 0
pos .YOffset = 0
switch combiningClass {
case combiningClassAttachedBelowLeft , combiningClassBelowLeft , combiningClassAboveLeft :
pos .XOffset += baseExtents .XBearing - markExtents .XBearing
case combiningClassAttachedAboveRight , combiningClassBelowRight , combiningClassAboveRight :
pos .XOffset += baseExtents .XBearing + baseExtents .Width - markExtents .Width - markExtents .XBearing
case combiningClassDoubleBelow , combiningClassDoubleAbove :
if buffer .Props .Direction == LeftToRight {
pos .XOffset += baseExtents .XBearing + baseExtents .Width - markExtents .Width /2 - markExtents .XBearing
break
} else if buffer .Props .Direction == RightToLeft {
pos .XOffset += baseExtents .XBearing - markExtents .Width /2 - markExtents .XBearing
break
}
fallthrough
case combiningClassAttachedBelow , combiningClassAttachedAbove , combiningClassBelow , combiningClassAbove :
fallthrough
default :
pos .XOffset += baseExtents .XBearing + (baseExtents .Width -markExtents .Width )/2 - markExtents .XBearing
}
switch combiningClass {
case combiningClassDoubleBelow , combiningClassBelowLeft , combiningClassBelow , combiningClassBelowRight :
baseExtents .Height -= yGap
fallthrough
case combiningClassAttachedBelowLeft , combiningClassAttachedBelow :
pos .YOffset = baseExtents .YBearing + baseExtents .Height - markExtents .YBearing
if (yGap > 0 ) == (pos .YOffset > 0 ) {
baseExtents .Height -= pos .YOffset
pos .YOffset = 0
}
baseExtents .Height += markExtents .Height
case combiningClassDoubleAbove , combiningClassAboveLeft , combiningClassAbove , combiningClassAboveRight :
baseExtents .YBearing += yGap
baseExtents .Height -= yGap
fallthrough
case combiningClassAttachedAbove , combiningClassAttachedAboveRight :
pos .YOffset = baseExtents .YBearing - (markExtents .YBearing + markExtents .Height )
if (yGap > 0 ) != (pos .YOffset > 0 ) {
correction := -pos .YOffset / 2
baseExtents .YBearing += correction
baseExtents .Height -= correction
pos .YOffset += correction
}
baseExtents .YBearing -= markExtents .Height
baseExtents .Height += markExtents .Height
}
}
func positionAroundBase(plan *otShapePlan , font *Font , buffer *Buffer ,
base , end int , adjustOffsetsWhenZeroing bool ,
) {
buffer .unsafeToBreak (base , end )
baseExtents , ok := font .GlyphExtents (buffer .Info [base ].Glyph )
if !ok {
zeroMarkAdvances (buffer , base +1 , end , adjustOffsetsWhenZeroing )
return
}
baseExtents .YBearing += buffer .Pos [base ].YOffset
baseExtents .XBearing = 0
baseExtents .Width = font .GlyphHAdvance (buffer .Info [base ].Glyph )
ligID := buffer .Info [base ].getLigID ()
numLigComponents := int32 (buffer .Info [base ].getLigNumComps ())
var xOffset , yOffset Position
if buffer .Props .Direction .isForward () {
xOffset -= buffer .Pos [base ].XAdvance
yOffset -= buffer .Pos [base ].YAdvance
}
var horizDir Direction
componentExtents := baseExtents
lastLigComponent := int32 (-1 )
lastCombiningClass := uint8 (255 )
clusterExtents := baseExtents
info := buffer .Info
for i := base + 1 ; i < end ; i ++ {
thisCombiningClass := info [i ].getModifiedCombiningClass ()
if thisCombiningClass != 0 {
if numLigComponents > 1 {
thisLigID := info [i ].getLigID ()
thisLigComponent := int32 (info [i ].getLigComp () - 1 )
if ligID == 0 || ligID != thisLigID || thisLigComponent >= numLigComponents {
thisLigComponent = numLigComponents - 1
}
if lastLigComponent != thisLigComponent {
lastLigComponent = thisLigComponent
lastCombiningClass = 255
componentExtents = baseExtents
if horizDir == 0 {
if plan .props .Direction .isHorizontal () {
horizDir = plan .props .Direction
} else {
horizDir = getHorizontalDirection (plan .props .Script )
}
}
if horizDir == LeftToRight {
componentExtents .XBearing += (thisLigComponent * componentExtents .Width ) / numLigComponents
} else {
componentExtents .XBearing += ((numLigComponents - 1 - thisLigComponent ) * componentExtents .Width ) / numLigComponents
}
componentExtents .Width /= numLigComponents
}
}
if lastCombiningClass != thisCombiningClass {
lastCombiningClass = thisCombiningClass
clusterExtents = componentExtents
}
positionMark (font , buffer , &clusterExtents , i , thisCombiningClass )
buffer .Pos [i ].XAdvance = 0
buffer .Pos [i ].YAdvance = 0
buffer .Pos [i ].XOffset += xOffset
buffer .Pos [i ].YOffset += yOffset
} else {
if buffer .Props .Direction .isForward () {
xOffset -= buffer .Pos [i ].XAdvance
yOffset -= buffer .Pos [i ].YAdvance
} else {
xOffset += buffer .Pos [i ].XAdvance
yOffset += buffer .Pos [i ].YAdvance
}
}
}
}
func positionCluster(plan *otShapePlan , font *Font , buffer *Buffer ,
start , end int , adjustOffsetsWhenZeroing bool ,
) {
if end -start < 2 {
return
}
info := buffer .Info
for i := start ; i < end ; i ++ {
if !info [i ].isUnicodeMark () {
var j int
for j = i + 1 ; j < end ; j ++ {
if info [j ].isHidden () || info [j ].isDefaultIgnorable () {
continue
}
if !info [j ].isUnicodeMark () {
break
}
}
positionAroundBase (plan , font , buffer , i , j , adjustOffsetsWhenZeroing )
i = j - 1
}
}
}
func fallbackMarkPosition(plan *otShapePlan , font *Font , buffer *Buffer ,
adjustOffsetsWhenZeroing bool ,
) {
var start int
info := buffer .Info
for i := 1 ; i < len (info ); i ++ {
if !info [i ].isUnicodeMark () && !info [i ].isHidden () && !info [i ].isDefaultIgnorable () {
positionCluster (plan , font , buffer , start , i , adjustOffsetsWhenZeroing )
start = i
}
}
positionCluster (plan , font , buffer , start , len (info ), adjustOffsetsWhenZeroing )
}
func fallbackSpaces(font *Font , buffer *Buffer ) {
if debugMode {
fmt .Println ("POSITION - applying fallback spaces" )
}
info := buffer .Info
pos := buffer .Pos
horizontal := buffer .Props .Direction .isHorizontal ()
for i , inf := range info {
if !inf .isUnicodeSpace () || inf .ligated () {
continue
}
if buffer .Invisible != 0 && info [i ].Glyph == buffer .Invisible {
if horizontal {
pos [i ].XAdvance = +font .XScale / 4
} else {
pos [i ].YAdvance = -font .YScale / 4
}
}
spaceType := inf .getUnicodeSpaceFallbackType ()
switch spaceType {
case notSpace , space :
case spaceEM , spaceEM2 , spaceEM3 , spaceEM4 , spaceEM5 , spaceEM6 , spaceEM16 :
if horizontal {
pos [i ].XAdvance = +(font .XScale + int32 (spaceType )/2 ) / int32 (spaceType )
} else {
pos [i ].YAdvance = -(font .YScale + int32 (spaceType )/2 ) / int32 (spaceType )
}
case space4EM18 :
if horizontal {
pos [i ].XAdvance = +font .XScale * 4 / 18
} else {
pos [i ].YAdvance = -font .YScale * 4 / 18
}
case spaceFigure :
for u := '0' ; u <= '9' ; u ++ {
if glyph , ok := font .face .NominalGlyph (u ); ok {
if horizontal {
pos [i ].XAdvance = font .GlyphHAdvance (glyph )
} else {
pos [i ].YAdvance = font .getGlyphVAdvance (glyph )
}
}
}
case spacePunctuation :
glyph , ok := font .face .NominalGlyph ('.' )
if !ok {
glyph , ok = font .face .NominalGlyph (',' )
}
if ok {
if horizontal {
pos [i ].XAdvance = font .GlyphHAdvance (glyph )
} else {
pos [i ].YAdvance = font .getGlyphVAdvance (glyph )
}
}
case spaceNarrow :
if horizontal {
pos [i ].XAdvance /= 2
} else {
pos [i ].YAdvance /= 2
}
}
}
}
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 .