go-chart/text.go

167 lines
4.8 KiB
Go
Raw Normal View History

2016-07-29 19:36:29 -04:00
package chart
import (
"strings"
)
2016-07-29 19:36:29 -04:00
// TextHorizontalAlign is an enum for the horizontal alignment options.
2016-07-31 19:54:09 -04:00
type TextHorizontalAlign int
2016-07-29 19:36:29 -04:00
const (
// TextHorizontalAlignUnset is the unset state for text horizontal alignment.
2016-07-31 19:54:09 -04:00
TextHorizontalAlignUnset TextHorizontalAlign = 0
2016-07-29 19:36:29 -04:00
// TextHorizontalAlignLeft aligns a string horizontally so that it's left ligature starts at horizontal pixel 0.
2016-07-31 19:54:09 -04:00
TextHorizontalAlignLeft TextHorizontalAlign = 1
2016-07-29 19:36:29 -04:00
// TextHorizontalAlignCenter left aligns a string horizontally so that there are equal pixels
// to the left and to the right of a string within a box.
2016-07-31 19:54:09 -04:00
TextHorizontalAlignCenter TextHorizontalAlign = 2
2016-07-29 19:36:29 -04:00
// TextHorizontalAlignRight right aligns a string horizontally so that the right ligature ends at the right-most pixel
// of a box.
2016-07-31 19:54:09 -04:00
TextHorizontalAlignRight TextHorizontalAlign = 3
2016-07-29 19:36:29 -04:00
)
// TextWrap is an enum for the word wrap options.
2016-07-31 19:54:09 -04:00
type TextWrap int
2016-07-29 19:36:29 -04:00
const (
// TextWrapUnset is the unset state for text wrap options.
2016-07-31 19:54:09 -04:00
TextWrapUnset TextWrap = 0
2016-07-29 19:36:29 -04:00
// TextWrapNone will spill text past horizontal boundaries.
2016-07-31 19:54:09 -04:00
TextWrapNone TextWrap = 1
2016-07-29 19:36:29 -04:00
// TextWrapWord will split a string on words (i.e. spaces) to fit within a horizontal boundary.
2016-07-31 19:54:09 -04:00
TextWrapWord TextWrap = 2
2016-07-29 19:36:29 -04:00
// TextWrapRune will split a string on a rune (i.e. utf-8 codepage) to fit within a horizontal boundary.
2016-07-31 19:54:09 -04:00
TextWrapRune TextWrap = 3
2016-07-29 19:36:29 -04:00
)
// TextVerticalAlign is an enum for the vertical alignment options.
2016-07-31 19:54:09 -04:00
type TextVerticalAlign int
2016-07-29 19:36:29 -04:00
const (
// TextVerticalAlignUnset is the unset state for vertical alignment options.
2016-07-31 19:54:09 -04:00
TextVerticalAlignUnset TextVerticalAlign = 0
2016-07-29 19:36:29 -04:00
// TextVerticalAlignBaseline aligns text according to the "baseline" of the string, or where a normal ascender begins.
2016-07-31 19:54:09 -04:00
TextVerticalAlignBaseline TextVerticalAlign = 1
2016-07-29 19:36:29 -04:00
// TextVerticalAlignBottom aligns the text according to the lowers pixel of any of the ligatures (ex. g or q both extend below the baseline).
2016-07-31 19:54:09 -04:00
TextVerticalAlignBottom TextVerticalAlign = 2
2016-07-29 19:36:29 -04:00
// TextVerticalAlignMiddle aligns the text so that there is an equal amount of space above and below the top and bottom of the ligatures.
2016-07-31 19:54:09 -04:00
TextVerticalAlignMiddle TextVerticalAlign = 3
// TextVerticalAlignMiddleBaseline aligns the text vertically so that there is an equal number of pixels above and below the baseline of the string.
2016-07-31 19:54:09 -04:00
TextVerticalAlignMiddleBaseline TextVerticalAlign = 4
2016-07-29 19:36:29 -04:00
// TextVerticalAlignTop alignts the text so that the top of the ligatures are at y-pixel 0 in the container.
2016-07-31 19:54:09 -04:00
TextVerticalAlignTop TextVerticalAlign = 5
2016-07-29 19:36:29 -04:00
)
var (
// Text contains utilities for text.
Text = &text{}
)
// TextStyle encapsulates text style options.
type TextStyle struct {
2016-07-31 19:54:09 -04:00
HorizontalAlign TextHorizontalAlign
VerticalAlign TextVerticalAlign
Wrap TextWrap
2016-07-29 19:36:29 -04:00
}
type text struct{}
2016-07-29 21:24:25 -04:00
func (t text) WrapFit(r Renderer, value string, width int, style Style) []string {
switch style.TextWrap {
case TextWrapRune:
return t.WrapFitRune(r, value, width, style)
case TextWrapWord:
return t.WrapFitWord(r, value, width, style)
2016-07-29 19:36:29 -04:00
}
return []string{value}
}
func (t text) WrapFitWord(r Renderer, value string, width int, style Style) []string {
style.WriteToRenderer(r)
var output []string
var line string
var word string
var textBox Box
for _, c := range value {
if c == rune('\n') { // commit the line to output
output = append(output, t.Trim(line+word))
line = ""
word = ""
continue
}
textBox = r.MeasureText(line + word + string(c))
if textBox.Width() >= width {
output = append(output, t.Trim(line))
line = word
word = string(c)
continue
}
if c == rune(' ') || c == rune('\t') {
line = line + word + string(c)
word = ""
continue
}
word = word + string(c)
}
return append(output, t.Trim(line+word))
}
func (t text) WrapFitRune(r Renderer, value string, width int, style Style) []string {
style.WriteToRenderer(r)
var output []string
var line string
var textBox Box
for _, c := range value {
if c == rune('\n') {
output = append(output, line)
line = ""
continue
}
textBox = r.MeasureText(line + string(c))
if textBox.Width() >= width {
output = append(output, line)
line = string(c)
continue
}
line = line + string(c)
}
return t.appendLast(output, line)
}
func (t text) Trim(value string) string {
return strings.Trim(value, " \t\n\r")
}
2016-07-29 21:24:25 -04:00
func (t text) MeasureLines(r Renderer, lines []string, style Style) Box {
style.WriteTextOptionsToRenderer(r)
var output Box
for index, line := range lines {
lineBox := r.MeasureText(line)
2019-02-13 19:09:26 -05:00
output.Right = MaxInt(lineBox.Right, output.Right)
2016-07-29 21:24:25 -04:00
output.Bottom += lineBox.Height()
if index < len(lines)-1 {
output.Bottom += +style.GetTextLineSpacing()
}
}
return output
}
2016-07-29 19:36:29 -04:00
func (t text) appendLast(lines []string, text string) []string {
if len(lines) == 0 {
return []string{text}
}
lastLine := lines[len(lines)-1]
lines[len(lines)-1] = lastLine + text
return lines
}