package harfbuzz
import (
"fmt"
"sort"
"github.com/go-text/typesetting/font"
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"
)
var _ otComplexShaper = (*complexShaperArabic )(nil )
const flagArabicHasStch = bsfShaper0
func isWord(genCat generalCategory ) bool {
const mask = 1 <<ucd .Unassigned |
1 <<ucd .Co |
1 <<ucd .Lm |
1 <<ucd .Lo |
1 <<ucd .Mc |
1 <<ucd .Me |
1 <<ucd .Mn |
1 <<ucd .Nd |
1 <<ucd .Nl |
1 <<ucd .No |
1 <<ucd .Sc |
1 <<ucd .Sk |
1 <<ucd .Sm |
1 <<ucd .So
return (1 <<genCat )&mask != 0
}
type arabicJoining byte
const (
ajU arabicJoining = 'U'
ajR arabicJoining = 'R'
ajAlaph arabicJoining = 'a'
ajDalathRish arabicJoining = 'd'
ajD arabicJoining = 'D'
ajC arabicJoining = 'C'
ajL arabicJoining = 'L'
ajT arabicJoining = 'T'
ajG arabicJoining = 'G'
)
const (
joiningTypeU = iota
joiningTypeL
joiningTypeR
joiningTypeD
joiningGroupAlaph
joiningGroupDalathRish
numStateMachineCols
joiningTypeT
joiningTypeC = joiningTypeD
)
func getJoiningType(u rune , genCat generalCategory ) uint8 {
if jType , ok := arabicJoinings [u ]; ok {
switch jType {
case ajU :
return joiningTypeU
case ajL :
return joiningTypeL
case ajR :
return joiningTypeR
case ajD :
return joiningTypeD
case ajAlaph :
return joiningGroupAlaph
case ajDalathRish :
return joiningGroupDalathRish
case ajT :
return joiningTypeT
case ajC :
return joiningTypeC
}
}
const mask = 1 <<ucd .Mn | 1 <<ucd .Me | 1 <<ucd .Cf
if 1 <<genCat &mask != 0 {
return joiningTypeT
}
return joiningTypeU
}
func featureIsSyriac(tag ot .Tag ) bool {
return '2' <= byte (tag ) && byte (tag ) <= '3'
}
var arabicFeatures = [...]ot .Tag {
ot .NewTag ('i' , 's' , 'o' , 'l' ),
ot .NewTag ('f' , 'i' , 'n' , 'a' ),
ot .NewTag ('f' , 'i' , 'n' , '2' ),
ot .NewTag ('f' , 'i' , 'n' , '3' ),
ot .NewTag ('m' , 'e' , 'd' , 'i' ),
ot .NewTag ('m' , 'e' , 'd' , '2' ),
ot .NewTag ('i' , 'n' , 'i' , 't' ),
}
const (
arabIsol = iota
arabFina
arabFin2
araFin3
arabMedi
arabMed2
arabInit
arabNone
arabStchFixed
arabStchRepeating
)
var arabicStateTable = [...][numStateMachineCols ]struct {
prevAction uint8
currAction uint8
nextState uint16
}{
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabNone , arabIsol , 1 }, {arabNone , arabIsol , 2 }, {arabNone , arabIsol , 1 }, {arabNone , arabIsol , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabNone , arabIsol , 1 }, {arabNone , arabIsol , 2 }, {arabNone , arabFin2 , 5 }, {arabNone , arabIsol , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabInit , arabFina , 1 }, {arabInit , arabFina , 3 }, {arabInit , arabFina , 4 }, {arabInit , arabFina , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabMedi , arabFina , 1 }, {arabMedi , arabFina , 3 }, {arabMedi , arabFina , 4 }, {arabMedi , arabFina , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabMed2 , arabIsol , 1 }, {arabMed2 , arabIsol , 2 }, {arabMed2 , arabFin2 , 5 }, {arabMed2 , arabIsol , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabIsol , arabIsol , 1 }, {arabIsol , arabIsol , 2 }, {arabIsol , arabFin2 , 5 }, {arabIsol , arabIsol , 6 }},
{{arabNone , arabNone , 0 }, {arabNone , arabIsol , 2 }, {arabNone , arabIsol , 1 }, {arabNone , arabIsol , 2 }, {arabNone , araFin3 , 5 }, {arabNone , arabIsol , 6 }},
}
type complexShaperArabic struct {
complexShaperNil
plan arabicShapePlan
}
func (complexShaperArabic ) marksBehavior () (zeroWidthMarks , bool ) {
return zeroWidthMarksByGdefLate , true
}
func (complexShaperArabic ) normalizationPreference () normalizationMode {
return nmDefault
}
func (cs *complexShaperArabic ) collectFeatures (plan *otShapePlanner ) {
map_ := &plan .otMap
map_ .enableFeature (ot .NewTag ('s' , 't' , 'c' , 'h' ))
map_ .addGSUBPause (recordStch )
map_ .enableFeatureExt (ot .NewTag ('c' , 'c' , 'm' , 'p' ), ffManualZWJ , 1 )
map_ .enableFeatureExt (ot .NewTag ('l' , 'o' , 'c' , 'l' ), ffManualZWJ , 1 )
map_ .addGSUBPause (nil )
for _ , arabFeat := range arabicFeatures {
hasFallback := plan .props .Script == language .Arabic && !featureIsSyriac (arabFeat )
fl := ffNone
if hasFallback {
fl = ffHasFallback
}
map_ .addFeatureExt (arabFeat , ffManualZWJ |fl , 1 )
map_ .addGSUBPause (nil )
}
map_ .enableFeatureExt (ot .NewTag ('r' , 'l' , 'i' , 'g' ), ffManualZWJ |ffHasFallback , 1 )
if plan .props .Script == language .Arabic {
map_ .addGSUBPause (arabicFallbackShape )
}
map_ .enableFeatureExt (ot .NewTag ('c' , 'a' , 'l' , 't' ), ffManualZWJ , 1 )
if !map_ .hasFeature (ot .NewTag ('r' , 'c' , 'l' , 't' )) {
map_ .addGSUBPause (nil )
}
map_ .enableFeatureExt (ot .NewTag ('l' , 'i' , 'g' , 'a' ), ffManualZWJ , 1 )
map_ .enableFeatureExt (ot .NewTag ('c' , 'l' , 'i' , 'g' ), ffManualZWJ , 1 )
map_ .enableFeatureExt (ot .NewTag ('m' , 's' , 'e' , 't' ), ffManualZWJ , 1 )
}
type arabicShapePlan struct {
fallbackPlan *arabicFallbackPlan
maskArray [len (arabicFeatures ) + 1 ]GlyphMask
doFallback bool
hasStch bool
}
func newArabicPlan(plan *otShapePlan ) arabicShapePlan {
var arabicPlan arabicShapePlan
arabicPlan .doFallback = plan .props .Script == language .Arabic
arabicPlan .hasStch = plan .otMap .getMask1 (ot .NewTag ('s' , 't' , 'c' , 'h' )) != 0
for i , arabFeat := range arabicFeatures {
arabicPlan .maskArray [i ] = plan .otMap .getMask1 (arabFeat )
arabicPlan .doFallback = arabicPlan .doFallback &&
(featureIsSyriac (arabFeat ) || plan .otMap .needsFallback (arabFeat ))
}
return arabicPlan
}
func (cs *complexShaperArabic ) dataCreate (plan *otShapePlan ) {
cs .plan = newArabicPlan (plan )
}
func applyArabicJoining(buffer *Buffer ) {
info := buffer .Info
prev , state := -1 , uint16 (0 )
for _ , u := range buffer .context [0 ] {
thisType := getJoiningType (u , ucd .LookupGeneralCategory (u ))
if thisType == joiningTypeT {
continue
}
entry := &arabicStateTable [state ][thisType ]
state = entry .nextState
break
}
for i := 0 ; i < len (info ); i ++ {
thisType := getJoiningType (info [i ].codepoint , info [i ].unicode .generalCategory ())
if thisType == joiningTypeT {
info [i ].complexAux = arabNone
continue
}
entry := &arabicStateTable [state ][thisType ]
if entry .prevAction != arabNone && prev != -1 {
info [prev ].complexAux = entry .prevAction
buffer .safeToInsertTatweel (prev , i +1 )
} else {
if prev == -1 {
if thisType >= joiningTypeR {
buffer .unsafeToConcatFromOutbuffer (0 , i +1 )
}
} else {
if thisType >= joiningTypeR ||
(2 <= state && state <= 5 ) {
buffer .unsafeToConcat (prev , i +1 )
}
}
}
info [i ].complexAux = entry .currAction
prev = i
state = entry .nextState
}
for _ , u := range buffer .context [1 ] {
thisType := getJoiningType (u , ucd .LookupGeneralCategory (u ))
if thisType == joiningTypeT {
continue
}
entry := &arabicStateTable [state ][thisType ]
if entry .prevAction != arabNone && prev != -1 {
info [prev ].complexAux = entry .prevAction
buffer .safeToInsertTatweel (prev , len (buffer .Info ))
} else if 2 <= state && state <= 5 {
buffer .unsafeToConcat (prev , len (buffer .Info ))
}
break
}
}
func mongolianVariationSelectors(buffer *Buffer ) {
info := buffer .Info
for i := 1 ; i < len (info ); i ++ {
if cp := info [i ].codepoint ; 0x180B <= cp && cp <= 0x180D || cp == 0x180F {
info [i ].complexAux = info [i -1 ].complexAux
}
}
}
func (arabicPlan arabicShapePlan ) setupMasks (buffer *Buffer , script language .Script ) {
applyArabicJoining (buffer )
if script == language .Mongolian {
mongolianVariationSelectors (buffer )
}
info := buffer .Info
for i := range info {
info [i ].Mask |= arabicPlan .maskArray [info [i ].complexAux ]
}
}
func (cs *complexShaperArabic ) setupMasks (plan *otShapePlan , buffer *Buffer , _ *Font ) {
cs .plan .setupMasks (buffer , plan .props .Script )
}
func arabicFallbackShape(plan *otShapePlan , font *Font , buffer *Buffer ) bool {
arabicPlan := plan .shaper .(*complexShaperArabic ).plan
if !arabicPlan .doFallback {
return false
}
fallbackPlan := arabicPlan .fallbackPlan
if fallbackPlan == nil {
fallbackPlan = newArabicFallbackPlan (plan , font )
}
fallbackPlan .shape (font , buffer )
return true
}
func recordStch(plan *otShapePlan , _ *Font , buffer *Buffer ) bool {
arabicPlan := plan .shaper .(*complexShaperArabic ).plan
if !arabicPlan .hasStch {
return false
}
info := buffer .Info
for i := range info {
if info [i ].multiplied () {
comp := info [i ].getLigComp ()
if comp %2 != 0 {
info [i ].complexAux = arabStchRepeating
} else {
info [i ].complexAux = arabStchFixed
}
buffer .scratchFlags |= flagArabicHasStch
}
}
return false
}
func inRange(sa uint8 ) bool {
return arabStchFixed <= sa && sa <= arabStchRepeating
}
func (cs *complexShaperArabic ) postprocessGlyphs (plan *otShapePlan , buffer *Buffer , font *Font ) {
if buffer .scratchFlags &flagArabicHasStch == 0 {
return
}
rtl := buffer .Props .Direction == RightToLeft
if !rtl {
buffer .Reverse ()
}
sign := Position (+1 )
if font .XScale < 0 {
sign = -1
}
const (
MEASURE = iota
CUT
)
var (
originCount = len (buffer .Info )
extraGlyphsNeeded = 0
)
for step := MEASURE ; step <= CUT ; step ++ {
info := buffer .Info
pos := buffer .Pos
j := len (info )
for i := originCount ; i != 0 ; i -- {
if sa := info [i -1 ].complexAux ; !inRange (sa ) {
if step == CUT {
j --
info [j ] = info [i -1 ]
pos [j ] = pos [i -1 ]
}
continue
}
var (
wTotal Position
wFixed Position
wRepeating Position
nFixed = 0
nRepeating = 0
)
end := i
for i != 0 && inRange (info [i -1 ].complexAux ) {
i --
width := font .GlyphHAdvance (info [i ].Glyph )
if info [i ].complexAux == arabStchFixed {
wFixed += width
nFixed ++
} else {
wRepeating += width
nRepeating ++
}
}
start := i
context := i
for context != 0 && !inRange (info [context -1 ].complexAux ) &&
((&info [context -1 ]).isDefaultIgnorable () ||
isWord ((&info [context -1 ]).unicode .generalCategory ())) {
context --
wTotal += pos [context ].XAdvance
}
i ++
if debugMode {
fmt .Printf ("ARABIC - step %d: stretch at (%d,%d,%d)\n" , step +1 , context , start , end )
fmt .Printf ("ARABIC - rest of word: count=%d width %d\n" , start -context , wTotal )
fmt .Printf ("ARABIC - fixed tiles: count=%d width=%d\n" , nFixed , wFixed )
fmt .Printf ("ARABIC - repeating tiles: count=%d width=%d\n" , nRepeating , wRepeating )
}
var nCopies int
wRemaining := wTotal - wFixed
if sign *wRemaining > sign *wRepeating && sign *wRepeating > 0 {
nCopies = int ((sign *wRemaining )/(sign *wRepeating ) - 1 )
}
var extraRepeatOverlap Position
shortfall := sign *wRemaining - sign *wRepeating *(Position (nCopies )+1 )
if shortfall > 0 && nRepeating > 0 {
nCopies ++
excess := (Position (nCopies )+1 )*sign *wRepeating - sign *wRemaining
if excess > 0 {
extraRepeatOverlap = excess / Position (nCopies *nRepeating )
wRemaining = 0
}
}
if step == MEASURE {
extraGlyphsNeeded += nCopies * nRepeating
if debugMode {
fmt .Printf ("ARABIC - will add extra %d copies of repeating tiles\n" , nCopies )
}
} else {
buffer .unsafeToBreak (context , end )
xOffset := wRemaining / 2
for k := end ; k > start ; k -- {
width := font .GlyphHAdvance (info [k -1 ].Glyph )
repeat := 1
if info [k -1 ].complexAux == arabStchRepeating {
repeat += nCopies
}
if debugMode {
fmt .Printf ("ARABIC - appending %d copies of glyph %d; j=%d\n" , repeat , info [k -1 ].codepoint , j )
}
pos [k -1 ].XAdvance = 0
for n := 0 ; n < repeat ; n ++ {
if rtl {
xOffset -= width
if n > 0 {
xOffset += extraRepeatOverlap
}
}
pos [k -1 ].XOffset = xOffset
j --
info [j ] = info [k -1 ]
pos [j ] = pos [k -1 ]
if !rtl {
xOffset += width
if n > 0 {
xOffset -= extraRepeatOverlap
}
}
}
}
}
}
if step == MEASURE {
buffer .Info = append (buffer .Info , make ([]GlyphInfo , extraGlyphsNeeded )...)
buffer .Pos = append (buffer .Pos , make ([]GlyphPosition , extraGlyphsNeeded )...)
}
}
if !rtl {
buffer .Reverse ()
}
}
var modifierCombiningMarks = [...]rune {
0x0654 ,
0x0655 ,
0x0658 ,
0x06DC ,
0x06E3 ,
0x06E7 ,
0x06E8 ,
0x08CA ,
0x08CB ,
0x08CD ,
0x08CE ,
0x08CF ,
0x08D3 ,
0x08F3 ,
}
func infoIsMcm(info *GlyphInfo ) bool {
u := info .codepoint
for i := 0 ; i < len (modifierCombiningMarks ); i ++ {
if u == modifierCombiningMarks [i ] {
return true
}
}
return false
}
func (cs *complexShaperArabic ) reorderMarks (_ *otShapePlan , buffer *Buffer , start , end int ) {
info := buffer .Info
if debugMode {
fmt .Printf ("ARABIC - Reordering marks from %d to %d\n" , start , end )
}
i := start
for cc := uint8 (220 ); cc <= 230 ; cc += 10 {
if debugMode {
fmt .Printf ("ARABIC - Looking for combining class %d's starting at %d\n" , cc , i )
}
for i < end && info [i ].getModifiedCombiningClass () < cc {
i ++
}
if debugMode {
fmt .Printf ("ARABIC - Looking for %d's stopped at %d\n" , cc , i )
}
if i == end {
break
}
if info [i ].getModifiedCombiningClass () > cc {
continue
}
j := i
for j < end && info [j ].getModifiedCombiningClass () == cc && infoIsMcm (&info [j ]) {
j ++
}
if i == j {
continue
}
if debugMode {
fmt .Printf ("ARABIC - Found %d's from %d to %d" , cc , i , j )
fmt .Printf ("ARABIC - Shifting %d's: %d %d" , cc , i , j )
}
var temp [shapeComplexMaxCombiningMarks ]GlyphInfo
buffer .mergeClusters (start , j )
copy (temp [:j -i ], info [i :])
copy (info [start +j -i :], info [start :i ])
copy (info [start :], temp [:j -i ])
newStart := start + j - i
newCc := mcc26
if cc == 220 {
newCc = mcc26
}
for start < newStart {
info [start ].setModifiedCombiningClass (newCc )
start ++
}
i = j
}
}
var arabicFallbackFeatures = [...]ot .Tag {
ot .NewTag ('i' , 'n' , 'i' , 't' ),
ot .NewTag ('m' , 'e' , 'd' , 'i' ),
ot .NewTag ('f' , 'i' , 'n' , 'a' ),
ot .NewTag ('i' , 's' , 'o' , 'l' ),
ot .NewTag ('r' , 'l' , 'i' , 'g' ),
ot .NewTag ('r' , 'l' , 'i' , 'g' ),
ot .NewTag ('r' , 'l' , 'i' , 'g' ),
}
type jointGlyphs struct {
glyphs, substitutes []gID
}
func (a jointGlyphs ) Len () int { return len (a .glyphs ) }
func (a jointGlyphs ) Swap (i , j int ) {
a .glyphs [i ], a .glyphs [j ] = a .glyphs [j ], a .glyphs [i ]
a .substitutes [i ], a .substitutes [j ] = a .substitutes [j ], a .substitutes [i ]
}
func (a jointGlyphs ) Less (i , j int ) bool { return a .glyphs [i ] < a .glyphs [j ] }
func arabicFallbackSynthesizeLookupSingle(ft *Font , featureIndex int ) *lookupGSUB {
var glyphs , substitutes []gID
for u := rune (firstArabicShape ); u <= lastArabicShape ; u ++ {
s := rune (arabicShaping [u -firstArabicShape ][featureIndex ])
uGlyph , hasU := ft .face .NominalGlyph (u )
sGlyph , hasS := ft .face .NominalGlyph (s )
if s == 0 || !hasU || !hasS || uGlyph == sGlyph || uGlyph > 0xFFFF || sGlyph > 0xFFFF {
continue
}
glyphs = append (glyphs , gID (uGlyph ))
substitutes = append (substitutes , gID (sGlyph ))
}
if len (glyphs ) == 0 {
return nil
}
sort .Stable (jointGlyphs {glyphs : glyphs , substitutes : substitutes })
return &lookupGSUB {
LookupOptions : font .LookupOptions {Flag : otIgnoreMarks },
Subtables : []tables .GSUBLookup {
tables .SingleSubs {Data : tables .SingleSubstData2 {
Coverage : tables .Coverage1 {Glyphs : glyphs },
SubstituteGlyphIDs : substitutes ,
}},
},
}
}
type glyphsIndirections struct {
glyphs []gID
indirections []int
}
func (a glyphsIndirections ) Len () int { return len (a .glyphs ) }
func (a glyphsIndirections ) Swap (i , j int ) {
a .glyphs [i ], a .glyphs [j ] = a .glyphs [j ], a .glyphs [i ]
a .indirections [i ], a .indirections [j ] = a .indirections [j ], a .indirections [i ]
}
func (a glyphsIndirections ) Less (i , j int ) bool { return a .glyphs [i ] < a .glyphs [j ] }
func arabicFallbackSynthesizeLookupLigature(ft *Font , ligatureTable []arabicTableEntry , lookupFlags uint16 ) *lookupGSUB {
var (
firstGlyphs []gID
firstGlyphsIndirection []int
)
for firstGlyphIdx , lig := range ligatureTable {
firstGlyph , ok := ft .face .NominalGlyph (lig .First )
if !ok {
continue
}
firstGlyphs = append (firstGlyphs , gID (firstGlyph ))
firstGlyphsIndirection = append (firstGlyphsIndirection , firstGlyphIdx )
}
if len (firstGlyphs ) == 0 {
return nil
}
sort .Stable (glyphsIndirections {glyphs : firstGlyphs , indirections : firstGlyphsIndirection })
var out tables .LigatureSubs
out .Coverage = tables .Coverage1 {Glyphs : firstGlyphs }
for _ , firstGlyphIdx := range firstGlyphsIndirection {
ligs := ligatureTable [firstGlyphIdx ].Ligatures
var ligatureSet tables .LigatureSet
for _ , v := range ligs {
ligatureU := v .ligature
ligatureGlyph , hasLigature := ft .face .NominalGlyph (ligatureU )
if !hasLigature {
continue
}
components := v .components
var componentGIDs []gID
for _ , componentU := range components {
componentGlyph , hasComponent := ft .face .NominalGlyph (componentU )
if !hasComponent {
break
}
componentGIDs = append (componentGIDs , gID (componentGlyph ))
}
if len (components ) != len (componentGIDs ) {
continue
}
ligatureSet .Ligatures = append (ligatureSet .Ligatures , tables .Ligature {
LigatureGlyph : gID (ligatureGlyph ),
ComponentGlyphIDs : componentGIDs ,
})
}
out .LigatureSets = append (out .LigatureSets , ligatureSet )
}
return &lookupGSUB {
LookupOptions : font .LookupOptions {Flag : lookupFlags },
Subtables : []tables .GSUBLookup {
out ,
},
}
}
func arabicFallbackSynthesizeLookup(font *Font , featureIndex int ) *lookupGSUB {
switch featureIndex {
case 0 , 1 , 2 , 3 :
return arabicFallbackSynthesizeLookupSingle (font , featureIndex )
case 4 :
return arabicFallbackSynthesizeLookupLigature (font , arabicLigature3Table [:], otIgnoreMarks )
case 5 :
return arabicFallbackSynthesizeLookupLigature (font , arabicLigatureTable [:], otIgnoreMarks )
case 6 :
return arabicFallbackSynthesizeLookupLigature (font , arabicLigatureMarkTable [:], 0 )
default :
panic ("unexpected arabic fallback feature index" )
}
}
const arabicFallbackMaxLookups = len (arabicFallbackFeatures )
type arabicFallbackPlan struct {
accelArray [arabicFallbackMaxLookups ]otLayoutLookupAccelerator
numLookups int
maskArray [arabicFallbackMaxLookups ]GlyphMask
}
func (fbPlan *arabicFallbackPlan ) initWin1256 (plan *otShapePlan , font *Font ) bool {
g1 , _ := font .face .NominalGlyph (0x0627 )
g2 , _ := font .face .NominalGlyph (0x0644 )
g3 , _ := font .face .NominalGlyph (0x0649 )
g4 , _ := font .face .NominalGlyph (0x064A )
g5 , _ := font .face .NominalGlyph (0x0652 )
if !(g1 == 199 && g2 == 225 && g3 == 236 && g4 == 237 && g5 == 250 ) {
return false
}
var j int
for _ , man := range arabicWin1256GsubLookups {
fbPlan .maskArray [j ] = plan .otMap .getMask1 (man .tag )
if fbPlan .maskArray [j ] != 0 {
if man .lookup != nil {
fbPlan .accelArray [j ].init (*man .lookup )
j ++
}
}
}
fbPlan .numLookups = j
return j > 0
}
func (fbPlan *arabicFallbackPlan ) initUnicode (plan *otShapePlan , font *Font ) bool {
var j int
for i , feat := range arabicFallbackFeatures {
fbPlan .maskArray [j ] = plan .otMap .getMask1 (feat )
if fbPlan .maskArray [j ] != 0 {
lk := arabicFallbackSynthesizeLookup (font , i )
if lk != nil {
fbPlan .accelArray [j ].init (*lk )
j ++
}
}
}
fbPlan .numLookups = j
return j > 0
}
func newArabicFallbackPlan(plan *otShapePlan , font *Font ) *arabicFallbackPlan {
var fbPlan arabicFallbackPlan
if fbPlan .initUnicode (plan , font ) {
return &fbPlan
}
if fbPlan .initWin1256 (plan , font ) {
return &fbPlan
}
return &arabicFallbackPlan {}
}
func (fbPlan *arabicFallbackPlan ) shape (font *Font , buffer *Buffer ) {
var c otApplyContext
c .reset (0 , font , buffer )
for i := 0 ; i < fbPlan .numLookups ; i ++ {
if fbPlan .accelArray [i ].lookup != nil {
c .setLookupMask (fbPlan .maskArray [i ])
c .substituteLookup (&fbPlan .accelArray [i ])
}
}
}
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 .