width.go

 1package uniseg
 2
 3// EastAsianAmbiguousWidth specifies the monospace width for East Asian
 4// characters classified as Ambiguous. The default is 1 but some rare fonts
 5// render them with a width of 2.
 6var EastAsianAmbiguousWidth = 1
 7
 8// runeWidth returns the monospace width for the given rune. The provided
 9// grapheme property is a value mapped by the [graphemeCodePoints] table.
10//
11// Every rune has a width of 1, except for runes with the following properties
12// (evaluated in this order):
13//
14//   - Control, CR, LF, Extend, ZWJ: Width of 0
15//   - \u2e3a, TWO-EM DASH: Width of 3
16//   - \u2e3b, THREE-EM DASH: Width of 4
17//   - East-Asian width Fullwidth and Wide: Width of 2 (Ambiguous and Neutral
18//     have a width of 1)
19//   - Regional Indicator: Width of 2
20//   - Extended Pictographic: Width of 2, unless Emoji Presentation is "No".
21func runeWidth(r rune, graphemeProperty int) int {
22	switch graphemeProperty {
23	case prControl, prCR, prLF, prExtend, prZWJ:
24		return 0
25	case prRegionalIndicator:
26		return 2
27	case prExtendedPictographic:
28		if property(emojiPresentation, r) == prEmojiPresentation {
29			return 2
30		}
31		return 1
32	}
33
34	switch r {
35	case 0x2e3a:
36		return 3
37	case 0x2e3b:
38		return 4
39	}
40
41	switch propertyEastAsianWidth(r) {
42	case prW, prF:
43		return 2
44	case prA:
45		return EastAsianAmbiguousWidth
46	}
47
48	return 1
49}
50
51// StringWidth returns the monospace width for the given string, that is, the
52// number of same-size cells to be occupied by the string.
53func StringWidth(s string) (width int) {
54	state := -1
55	for len(s) > 0 {
56		var w int
57		_, s, w, state = FirstGraphemeClusterInString(s, state)
58		width += w
59	}
60	return
61}