svg.gno
6.17 Kb ยท 316 lines
1package svg
2
3import (
4 "encoding/base64"
5 "strings"
6
7 "gno.land/p/demo/avl"
8 "gno.land/p/demo/ufmt"
9)
10
11type Canvas struct {
12 Width, Height int
13 ViewBox string
14 Elems []Elem
15 Style *avl.Tree
16}
17
18type Elem interface{ String() string }
19
20func NewCanvas(width, height int) *Canvas {
21 return &Canvas{
22 Width: width,
23 Height: height,
24 Style: nil,
25 }
26}
27
28func (c *Canvas) AddStyle(key, value string) *Canvas {
29 if c.Style == nil {
30 c.Style = avl.NewTree()
31 }
32 c.Style.Set(key, value)
33 return c
34}
35
36func (c *Canvas) WithViewBox(x, y, width, height int) *Canvas {
37 c.ViewBox = ufmt.Sprintf("%d %d %d %d", x, y, width, height)
38 return c
39}
40
41// Render renders your canvas
42func (c Canvas) Render(alt string) string {
43 base64SVG := base64.StdEncoding.EncodeToString([]byte(c.String()))
44 return ufmt.Sprintf("", alt, base64SVG)
45}
46
47func (c Canvas) String() string {
48 out := ""
49 out += ufmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" viewBox="%s">`, c.Width, c.Height, c.ViewBox)
50 if c.Style != nil {
51 out += "<style>"
52 c.Style.Iterate("", "", func(k string, val interface{}) bool {
53 v := val.(string)
54 out += ufmt.Sprintf("%s{%s}", k, v)
55 return false
56 })
57 out += "</style>"
58 }
59 for _, elem := range c.Elems {
60 out += elem.String()
61 }
62 out += "</svg>"
63 return out
64}
65
66func (c Canvas) Base64() string {
67 out := c.String()
68 return base64.StdEncoding.EncodeToString([]byte(out))
69}
70
71func (c *Canvas) Append(elem ...Elem) {
72 c.Elems = append(c.Elems, elem...)
73}
74
75type BaseAttrs struct {
76 ID string
77 Class string
78 Style string
79 Stroke string
80 StrokeWidth string
81 Opacity string
82 Transform string
83 Visibility string
84}
85
86func (b BaseAttrs) String() string {
87 var elems []string
88
89 if b.ID != "" {
90 elems = append(elems, `id="`+b.ID+`"`)
91 }
92 if b.Class != "" {
93 elems = append(elems, `class="`+b.Class+`"`)
94 }
95 if b.Style != "" {
96 elems = append(elems, `style="`+b.Style+`"`)
97 }
98 if b.Stroke != "" {
99 elems = append(elems, `stroke="`+b.Stroke+`"`)
100 }
101 if b.StrokeWidth != "" {
102 elems = append(elems, `stroke-width="`+b.StrokeWidth+`"`)
103 }
104 if b.Opacity != "" {
105 elems = append(elems, `opacity="`+b.Opacity+`"`)
106 }
107 if b.Transform != "" {
108 elems = append(elems, `transform="`+b.Transform+`"`)
109 }
110 if b.Visibility != "" {
111 elems = append(elems, `visibility="`+b.Visibility+`"`)
112 }
113 if len(elems) == 0 {
114 return ""
115 }
116 return strings.Join(elems, " ")
117}
118
119type Circle struct {
120 CX int // center X
121 CY int // center Y
122 R int // radius
123 Fill string
124 Attr BaseAttrs
125}
126
127func (c Circle) String() string {
128 return ufmt.Sprintf(`<circle cx="%d" cy="%d" r="%d" fill="%s" %s/>`, c.CX, c.CY, c.R, c.Fill, c.Attr.String())
129}
130
131func NewCircle(cx, cy, r int, fill string) *Circle {
132 return &Circle{
133 CX: cx,
134 CY: cy,
135 R: r,
136 Fill: fill,
137 }
138}
139
140func (c *Circle) WithClass(class string) *Circle {
141 c.Attr.Class = class
142 return c
143}
144
145type Ellipse struct {
146 CX int // center X
147 CY int // center Y
148 RX int // radius X
149 RY int // radius Y
150 Fill string
151 Attr BaseAttrs
152}
153
154func (e Ellipse) String() string {
155 return ufmt.Sprintf(`<ellipse cx="%d" cy="%d" rx="%d" ry="%d" fill="%s" %s/>`, e.CX, e.CY, e.RX, e.RY, e.Fill, e.Attr.String())
156}
157
158func NewEllipse(cx, cy int, fill string) *Ellipse {
159 return &Ellipse{
160 CX: cx,
161 CY: cy,
162 Fill: fill,
163 }
164}
165
166func (e *Ellipse) WithClass(class string) *Ellipse {
167 e.Attr.Class = class
168 return e
169}
170
171type Rectangle struct {
172 X, Y, Width, Height int
173 RX, RY int // corner radiuses
174 Fill string
175 Attr BaseAttrs
176}
177
178func (r Rectangle) String() string {
179 return ufmt.Sprintf(`<rect x="%d" y="%d" width="%d" height="%d" rx="%d" ry="%d" fill="%s" %s/>`, r.X, r.Y, r.Width, r.Height, r.RX, r.RY, r.Fill, r.Attr.String())
180}
181
182func NewRectangle(x, y, width, height int, fill string) *Rectangle {
183 return &Rectangle{
184 X: x,
185 Y: y,
186 Width: width,
187 Height: height,
188 Fill: fill,
189 }
190}
191
192func (r *Rectangle) WithClass(class string) *Rectangle {
193 r.Attr.Class = class
194 return r
195}
196
197type Path struct {
198 D string
199 Fill string
200 Attr BaseAttrs
201}
202
203func (p Path) String() string {
204 return ufmt.Sprintf(`<path d="%s" fill="%s" %s/>`, p.D, p.Fill, p.Attr.String())
205}
206
207func NewPath(d, fill string) *Path {
208 return &Path{
209 D: d,
210 Fill: fill,
211 }
212}
213
214func (p *Path) WithClass(class string) *Path {
215 p.Attr.Class = class
216 return p
217}
218
219type Polygon struct { // closed shape
220 Points string
221 Fill string
222 Attr BaseAttrs
223}
224
225func (p Polygon) String() string {
226 return ufmt.Sprintf(`<polygon points="%s" fill="%s" %s/>`, p.Points, p.Fill, p.Attr.String())
227}
228
229func NewPolygon(points, fill string) *Polygon {
230 return &Polygon{
231 Points: points,
232 Fill: fill,
233 }
234}
235
236func (p *Polygon) WithClass(class string) *Polygon {
237 p.Attr.Class = class
238 return p
239}
240
241type Polyline struct { // polygon but not necessarily closed
242 Points string
243 Fill string
244 Attr BaseAttrs
245}
246
247func (p Polyline) String() string {
248 return ufmt.Sprintf(`<polyline points="%s" fill="%s" %s/>`, p.Points, p.Fill, p.Attr.String())
249}
250
251func NewPolyline(points, fill string) *Polyline {
252 return &Polyline{
253 Points: points,
254 Fill: fill,
255 }
256}
257
258func (p *Polyline) WithClass(class string) *Polyline {
259 p.Attr.Class = class
260 return p
261}
262
263type Text struct {
264 X, Y int
265 DX, DY int // shift text pos horizontally/ vertically
266 Rotate string
267 Text, Fill string
268 Attr BaseAttrs
269}
270
271func (c Text) String() string {
272 return ufmt.Sprintf(`<text x="%d" y="%d" dx="%d" dy="%d" rotate="%s" fill="%s" %s>%s</text>`, c.X, c.Y, c.DX, c.DY, c.Rotate, c.Fill, c.Attr.String(), c.Text)
273}
274
275func NewText(x, y int, text, fill string) *Text {
276 return &Text{
277 X: x,
278 Y: y,
279 Text: text,
280 Fill: fill,
281 }
282}
283
284func (c *Text) WithClass(class string) *Text {
285 c.Attr.Class = class
286 return c
287}
288
289type Group struct {
290 Elems []Elem
291 Fill string
292 Attr BaseAttrs
293}
294
295func (g Group) String() string {
296 out := ""
297 for _, e := range g.Elems {
298 out += e.String()
299 }
300 return ufmt.Sprintf(`<g fill="%s" %s>%s</g>`, g.Fill, g.Attr.String(), out)
301}
302
303func NewGroup(fill string) *Group {
304 return &Group{
305 Fill: fill,
306 }
307}
308
309func (g *Group) Append(elem ...Elem) {
310 g.Elems = append(g.Elems, elem...)
311}
312
313func (g *Group) WithClass(class string) *Group {
314 g.Attr.Class = class
315 return g
316}