svg.gno
2.45 Kb · 78 lines
1// Package svg draws the small on-chain SVGs of GnoRadio (covers, tickets,
2// charts) with a strings.Builder, which keeps vm/qrender gas low.
3package svg
4
5import (
6 "encoding/base64"
7 "strconv"
8 "strings"
9
10 "gno.land/p/nt/markdown/sanitize/v0"
11)
12
13type Canvas struct{ b strings.Builder }
14
15// New opens a w×h SVG whose <style> holds css.
16func New(w, h int, css string) *Canvas {
17 c := &Canvas{}
18 c.b.WriteString(`<svg xmlns="http://www.w3.org/2000/svg" width="` + itoa(w) + `" height="` + itoa(h) +
19 `" viewBox="0 0 ` + itoa(w) + ` ` + itoa(h) + `"><style>` + css + `</style>`)
20 return c
21}
22
23func (c *Canvas) Raw(s string) { c.b.WriteString(s) }
24
25// Rect draws a rectangle; extra holds more attributes, e.g. ` mask="url(#h)"`.
26func (c *Canvas) Rect(x, y, w, h, rx int, fill, extra string) {
27 c.b.WriteString(`<rect x="` + itoa(x) + `" y="` + itoa(y) + `" width="` + itoa(w) + `" height="` + itoa(h) +
28 `" rx="` + itoa(rx) + `" fill="` + fill + `"` + extra + `/>`)
29}
30
31func (c *Canvas) Circle(cx, cy, r int, fill string) {
32 c.b.WriteString(`<circle cx="` + itoa(cx) + `" cy="` + itoa(cy) + `" r="` + itoa(r) + `" fill="` + fill + `"/>`)
33}
34
35// Text writes s escaped; attrs follows font-size, e.g. ` text-anchor="end" class="m"`.
36func (c *Canvas) Text(x, y int, s, fill string, size int, attrs string) {
37 c.b.WriteString(`<text x="` + itoa(x) + `" y="` + itoa(y) + `" fill="` + fill + `" font-size="` + itoa(size) +
38 `"` + attrs + `>` + Escape(s) + `</text>`)
39}
40
41// Data closes the SVG and returns it as a base64 data URI.
42func (c *Canvas) Data() string {
43 c.b.WriteString("</svg>")
44 return "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString([]byte(c.b.String()))
45}
46
47// Image closes the SVG and returns it as a markdown image.
48func (c *Canvas) Image(alt string) string {
49 return " + ")"
50}
51
52// Escape makes text safe inside SVG: titles and names may hold '&'.
53func Escape(s string) string {
54 s = strings.ReplaceAll(s, "&", "&")
55 s = strings.ReplaceAll(s, "<", "<")
56 return strings.ReplaceAll(s, ">", ">")
57}
58
59// Clip shortens s to n runes, ending with an ellipsis.
60func Clip(s string, n int) string {
61 r := []rune(s)
62 if len(r) <= n {
63 return s
64 }
65 return string(r[:n-1]) + "…"
66}
67
68// FNV is the 32-bit FNV-1a hash, used to pick a composition from a title.
69func FNV(s string) uint32 {
70 h := uint32(2166136261)
71 for i := 0; i < len(s); i++ {
72 h ^= uint32(s[i])
73 h *= 16777619
74 }
75 return h
76}
77
78func itoa(n int) string { return strconv.Itoa(n) }