Skip to content

Commit e48cc03

Browse files
committed
word wrap implemented
1 parent 315dff9 commit e48cc03

File tree

3 files changed

+85
-20
lines changed

3 files changed

+85
-20
lines changed

context.go

+36-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ const (
3636
FillRuleEvenOdd
3737
)
3838

39+
type Align int
40+
41+
const (
42+
AlignLeft Align = iota
43+
AlignCenter
44+
AlignRight
45+
)
46+
3947
type Context struct {
4048
width int
4149
height int
@@ -504,11 +512,29 @@ func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) {
504512
d.DrawString(s)
505513
}
506514

507-
func (dc *Context) DrawStringWrapped(s string, x, y, w float64) {
508-
lines := wordWrap(dc, s, w)
515+
// DrawStringWrapped word-wraps the specified string to the given max width
516+
// and then draws it at the specified anchor point using the given line
517+
// spacing and text alignment.
518+
func (dc *Context) DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) {
519+
lines := dc.WordWrap(s, width)
520+
h := float64(len(lines)) * dc.fontHeight * lineSpacing
521+
h -= (lineSpacing - 1) * dc.fontHeight
522+
x -= ax * width
523+
y -= ay * h
524+
switch align {
525+
case AlignLeft:
526+
ax = 0
527+
case AlignCenter:
528+
ax = 0.5
529+
x += width / 2
530+
case AlignRight:
531+
ax = 1
532+
x += width
533+
}
534+
ay = 1
509535
for _, line := range lines {
510-
dc.DrawStringAnchored(line, x, y, 0, 0)
511-
y += dc.fontHeight * 1.5
536+
dc.DrawStringAnchored(line, x, y, ax, ay)
537+
y += dc.fontHeight * lineSpacing
512538
}
513539
}
514540

@@ -522,6 +548,12 @@ func (dc *Context) MeasureString(s string) (w, h float64) {
522548
return float64(a >> 6), dc.fontHeight
523549
}
524550

551+
// WordWrap wraps the specified string to the given max width and current
552+
// font face.
553+
func (dc *Context) WordWrap(s string, w float64) []string {
554+
return wordWrap(dc, s, w)
555+
}
556+
525557
// Transformation Matrix Operations
526558

527559
// Identity resets the current transformation matrix to the identity matrix.

examples/wrap.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import "github.com/fogleman/gg"
4+
5+
const TEXT = "Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people's hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me."
6+
7+
func main() {
8+
const W = 1024
9+
const H = 1024
10+
const P = 16
11+
dc := gg.NewContext(W, H)
12+
dc.SetRGB(1, 1, 1)
13+
dc.Clear()
14+
dc.DrawLine(W/2, 0, W/2, H)
15+
dc.DrawLine(0, H/2, W, H/2)
16+
dc.DrawRectangle(P, P, W-P-P, H-P-P)
17+
dc.SetRGBA(0, 0, 1, 0.25)
18+
dc.SetLineWidth(3)
19+
dc.Stroke()
20+
dc.SetRGB(0, 0, 0)
21+
dc.LoadFontFace("/Library/Fonts/Arial Bold.ttf", 18)
22+
dc.DrawStringWrapped("UPPER LEFT", P, P, 0, 0, 0, 1.5, gg.AlignLeft)
23+
dc.DrawStringWrapped("UPPER RIGHT", W-P, P, 1, 0, 0, 1.5, gg.AlignRight)
24+
dc.DrawStringWrapped("BOTTOM LEFT", P, H-P, 0, 1, 0, 1.5, gg.AlignLeft)
25+
dc.DrawStringWrapped("BOTTOM RIGHT", W-P, H-P, 1, 1, 0, 1.5, gg.AlignRight)
26+
dc.DrawStringWrapped("UPPER MIDDLE", W/2, P, 0.5, 0, 0, 1.5, gg.AlignCenter)
27+
dc.DrawStringWrapped("LOWER MIDDLE", W/2, H-P, 0.5, 1, 0, 1.5, gg.AlignCenter)
28+
dc.DrawStringWrapped("LEFT MIDDLE", P, H/2, 0, 0.5, 0, 1.5, gg.AlignLeft)
29+
dc.DrawStringWrapped("RIGHT MIDDLE", W-P, H/2, 1, 0.5, 0, 1.5, gg.AlignRight)
30+
dc.LoadFontFace("/Library/Fonts/Arial.ttf", 12)
31+
dc.DrawStringWrapped(TEXT, W/2-P, H/2-P, 1, 1, W/3, 1.75, gg.AlignLeft)
32+
dc.DrawStringWrapped(TEXT, W/2+P, H/2-P, 0, 1, W/3, 2, gg.AlignLeft)
33+
dc.DrawStringWrapped(TEXT, W/2-P, H/2+P, 1, 0, W/3, 2.25, gg.AlignLeft)
34+
dc.DrawStringWrapped(TEXT, W/2+P, H/2+P, 0, 0, W/3, 2.5, gg.AlignLeft)
35+
dc.SavePNG("out.png")
36+
}

wrap.go

+13-16
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,26 @@ func wordWrap(m measureStringer, s string, width float64) []string {
2929
var result []string
3030
for _, line := range strings.Split(s, "\n") {
3131
fields := splitOnSpace(line)
32-
widths := make([]float64, len(fields))
33-
for i, field := range fields {
34-
widths[i], _ = m.MeasureString(field)
32+
if len(fields)%2 == 1 {
33+
fields = append(fields, "")
3534
}
36-
start := 0
37-
total := 0.0
35+
x := ""
3836
for i := 0; i < len(fields); i += 2 {
39-
if total+widths[i] > width {
40-
if i == start {
41-
end := i + 2
42-
result = append(result, strings.Join(fields[start:end], ""))
43-
start, total = end, 0
37+
w, _ := m.MeasureString(x + fields[i])
38+
if w > width {
39+
if x == "" {
40+
result = append(result, fields[i])
41+
x = ""
4442
continue
4543
} else {
46-
end := i
47-
result = append(result, strings.Join(fields[start:end], ""))
48-
start, total = end, 0
44+
result = append(result, x)
45+
x = ""
4946
}
5047
}
51-
total += widths[i] + widths[i+1]
48+
x += fields[i] + fields[i+1]
5249
}
53-
if start < len(fields) {
54-
result = append(result, strings.Join(fields[start:], ""))
50+
if x != "" {
51+
result = append(result, x)
5552
}
5653
}
5754
for i, line := range result {

0 commit comments

Comments
 (0)