-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtextwrap.go
135 lines (117 loc) · 2.73 KB
/
textwrap.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
130
131
132
133
134
135
package textwrap
import (
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"strings"
"unicode"
"unicode/utf8"
)
// TextWrapFont measure and Wrap text
func TextWrapFont(s string, f font.Face, width float64) []string {
return TextWrap(s, func(s string) bool {
return measureWidth(f, s) > width
})
}
// TextWrap Wrap text without breaking words as much as possible;
// Support Chinese characters and super long words
func TextWrap(s string, over func(string) bool) []string {
var result []string
for _, l := range strings.Split(s, "\n") {
words := breakLine(l)
line := ""
for _, next := range words {
testLine := line + next
if !over(testLine) {
line = testLine
continue
}
if strings.TrimSpace(line) == "" {
line = next
continue
}
i, last := breakWord(line, over)
result = append(result, i...)
// 如果一个单词被分为了多行则需要处理最后一行,尝试拼接 next
// case:
// |Unite|
// |d St |
if last != "" {
if !over(last + next) {
line = last + next
continue
} else {
result = append(result, last)
}
}
// 超出之后下一个单词就是下一行的开始
line = next
}
if strings.TrimSpace(line) != "" {
word, last := breakWord(line, over)
result = append(result, word...)
if last != "" {
result = append(result, last)
}
}
}
for i, line := range result {
result[i] = strings.TrimSpace(line)
}
return result
}
// ParseFont Parse font file to FontFace
func ParseFont(fontBody []byte, fontSize float64) (font.Face, error) {
f, err := truetype.Parse(fontBody)
if err != nil {
return nil, err
}
face := truetype.NewFace(f, &truetype.Options{
Size: fontSize,
})
return face, nil
}
// return "break opportunities" (http://unicode.org/reports/tr14/)
func breakLine(x string) []string {
var result []string
pi := 0
prevKind := -1
rs := []rune(x)
for i, c := range rs {
var kind = -1
if !unicode.IsSpace(c) {
kind = utf8.RuneLen(c)
}
isWide := kind >= 3
// break on each Wide char and space
if (kind != prevKind || isWide) && i != 0 {
result = append(result, string(rs[pi:i]))
pi = i
}
prevKind = kind
}
result = append(result, string(rs[pi:]))
return result
}
func breakWord(s string, over func(string) bool) ([]string, string) {
var result []string
runes := []rune(s)
for len(runes) != 0 && over(string(runes)) {
max := 0
for i := range runes {
if over(string(runes[:i+1])) {
max = i
break
}
}
if max == 0 {
max = 1
}
result = append(result, string(runes[:max]))
runes = runes[max:]
}
return result, string(runes)
}
func measureWidth(f font.Face, s string) float64 {
advance := font.MeasureString(f, s)
return float64(advance >> 6)
}