-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshape.go
129 lines (103 loc) · 2.7 KB
/
shape.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gopersian
import (
"strings"
"golang.org/x/text/unicode/bidi"
)
func adjustLetterShape(previousChar, currentChar, nextChar rune) rune {
shape := currentChar
previousIn := false // in the Arabic Alphabet or not
nextIn := false // in the Arabic Alphabet or not
for _, s := range alphabet {
if s.equals(previousChar) { // previousChar in the Arabic Alphabet ?
previousIn = true
}
if s.equals(nextChar) { // nextChar in the Persian Alphabet ?
nextIn = true
}
}
for _, s := range alphabet {
if !s.equals(currentChar) { // currentChar in the Persian Alphabet ?
continue
}
if previousIn && nextIn { // between two Persian Alphabet, return the medium shape
for s := range isolatedAfter {
if s.equals(previousChar) {
return letterFromRune(currentChar).Initial
}
}
return letterFromRune(currentChar).Medial
}
if nextIn { // beginning (because the previous is not in the Arabic Alphabet)
return letterFromRune(currentChar).Initial
}
if previousIn { // final (because the next is not in the Arabic Alphabet)
for s := range isolatedAfter {
if s.equals(previousChar) {
return letterFromRune(currentChar).Isolated
}
}
return letterFromRune(currentChar).Final
}
if !previousIn && !nextIn {
return letterFromRune(currentChar).Isolated
}
}
return shape
}
// Shape returns the glyph representation of the given text
func Shape(val string) string {
var prev, next rune
runes := []rune(val)
length := len(runes)
newText := make([]rune, 0, length)
for i, current := range runes {
// get the previous char
if (i - 1) < 0 {
prev = 0
} else {
prev = runes[i-1]
}
// get the next char
if (i + 1) <= length-1 {
next = runes[i+1]
} else {
next = 0
}
// get the current char representation or return the same if unnecessary
glyph := adjustLetterShape(prev, current, next)
// append the new char representation to the newText
newText = append(newText, glyph)
}
return string(newText)
}
// Reorder reorder runes by checking directions.
func Reorder(val string) (string, error) {
p := new(bidi.Paragraph)
_, err := p.SetString(val)
if err != nil {
return "", err
}
ordering, err := p.Order()
if err != nil {
return "", err
}
builder := strings.Builder{}
olen := ordering.NumRuns()
for i := 0; i < olen; i++ {
index := i
if ordering.Direction() == bidi.RightToLeft {
index = olen - i - 1
}
r := ordering.Run(index)
str := r.String()
if r.Direction() == bidi.RightToLeft {
str = bidi.ReverseString(r.String())
}
builder.WriteString(str)
}
return builder.String(), nil
}
// Bidi shapes and reorder the string.
func Bidi(val string) (string, error) {
return Reorder(Shape(val))
}