Skip to content

Commit dec8719

Browse files
author
Aleksey Marin
committed
split definitions into separate files, mainly one type per file
1 parent c40928e commit dec8719

File tree

12 files changed

+1449
-1395
lines changed

12 files changed

+1449
-1395
lines changed

a_doc.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2017 The oksvg Authors. All rights reserved.
2+
//
3+
// created: 2/12/2017 by S.R.Wiley
4+
// The oksvg package provides a partial implementation of the SVG 2.0 standard.
5+
// It can perform all SVG2.0 path commands, including arc and miterclip. It also
6+
// has some additional capabilities like arc-clip. Svgdraw does
7+
// not implement all SVG features such as animation or markers, but it can draw
8+
// the many of open source SVG icons correctly. See Readme for
9+
// a list of features.
10+
11+
package oksvg

definitions.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package oksvg
2+
3+
import (
4+
"encoding/xml"
5+
)
6+
7+
// definition is used to store XML-tags of SVG source definitions data.
8+
type definition struct {
9+
ID, Tag string
10+
Attrs []xml.Attr
11+
}

draw.go

Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
package oksvg
2+
3+
import (
4+
"encoding/xml"
5+
"errors"
6+
"log"
7+
"strings"
8+
9+
"github.com/srwiley/rasterx"
10+
"golang.org/x/image/math/fixed"
11+
)
12+
13+
// svgFunc defines function interface to use as drawing implementation.
14+
type svgFunc func(c *IconCursor, attrs []xml.Attr) error
15+
16+
var (
17+
drawFuncs = map[string]svgFunc{
18+
"svg": svgF,
19+
"g": gF,
20+
"line": lineF,
21+
"stop": stopF,
22+
"rect": rectF,
23+
"circle": circleF,
24+
"ellipse": circleF, //circleF handles ellipse also
25+
"polyline": polylineF,
26+
"polygon": polygonF,
27+
"path": pathF,
28+
"desc": descF,
29+
"defs": defsF,
30+
"title": titleF,
31+
"linearGradient": linearGradientF,
32+
"radialGradient": radialGradientF,
33+
}
34+
35+
svgF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
36+
c.icon.ViewBox.X = 0
37+
c.icon.ViewBox.Y = 0
38+
c.icon.ViewBox.W = 0
39+
c.icon.ViewBox.H = 0
40+
var width, height float64
41+
var err error
42+
for _, attr := range attrs {
43+
switch attr.Name.Local {
44+
case "viewBox":
45+
err = c.GetPoints(attr.Value)
46+
if len(c.points) != 4 {
47+
return errParamMismatch
48+
}
49+
c.icon.ViewBox.X = c.points[0]
50+
c.icon.ViewBox.Y = c.points[1]
51+
c.icon.ViewBox.W = c.points[2]
52+
c.icon.ViewBox.H = c.points[3]
53+
case "width":
54+
width, err = parseFloat(attr.Value, 64)
55+
case "height":
56+
height, err = parseFloat(attr.Value, 64)
57+
}
58+
if err != nil {
59+
return err
60+
}
61+
}
62+
if c.icon.ViewBox.W == 0 {
63+
c.icon.ViewBox.W = width
64+
}
65+
if c.icon.ViewBox.H == 0 {
66+
c.icon.ViewBox.H = height
67+
}
68+
return nil
69+
}
70+
gF svgFunc = func(*IconCursor, []xml.Attr) error { return nil } // g does nothing but push the style
71+
rectF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
72+
var x, y, w, h, rx, ry float64
73+
var err error
74+
for _, attr := range attrs {
75+
switch attr.Name.Local {
76+
case "x":
77+
x, err = parseFloat(attr.Value, 64)
78+
case "y":
79+
y, err = parseFloat(attr.Value, 64)
80+
case "width":
81+
w, err = parseFloat(attr.Value, 64)
82+
case "height":
83+
h, err = parseFloat(attr.Value, 64)
84+
case "rx":
85+
rx, err = parseFloat(attr.Value, 64)
86+
case "ry":
87+
ry, err = parseFloat(attr.Value, 64)
88+
}
89+
if err != nil {
90+
return err
91+
}
92+
}
93+
if w == 0 || h == 0 {
94+
return nil
95+
}
96+
rasterx.AddRoundRect(x+c.curX, y+c.curY, w+x+c.curX, h+y+c.curY, rx, ry, 0, rasterx.RoundGap, &c.Path)
97+
return nil
98+
}
99+
circleF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
100+
var cx, cy, rx, ry float64
101+
var err error
102+
for _, attr := range attrs {
103+
switch attr.Name.Local {
104+
case "cx":
105+
cx, err = parseFloat(attr.Value, 64)
106+
case "cy":
107+
cy, err = parseFloat(attr.Value, 64)
108+
case "r":
109+
rx, err = parseFloat(attr.Value, 64)
110+
ry = rx
111+
case "rx":
112+
rx, err = parseFloat(attr.Value, 64)
113+
case "ry":
114+
ry, err = parseFloat(attr.Value, 64)
115+
}
116+
if err != nil {
117+
return err
118+
}
119+
}
120+
if rx == 0 || ry == 0 { // not drawn, but not an error
121+
return nil
122+
}
123+
c.EllipseAt(cx+c.curX, cy+c.curY, rx, ry)
124+
return nil
125+
}
126+
lineF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
127+
var x1, x2, y1, y2 float64
128+
var err error
129+
for _, attr := range attrs {
130+
switch attr.Name.Local {
131+
case "x1":
132+
x1, err = parseFloat(attr.Value, 64)
133+
case "x2":
134+
x2, err = parseFloat(attr.Value, 64)
135+
case "y1":
136+
y1, err = parseFloat(attr.Value, 64)
137+
case "y2":
138+
y2, err = parseFloat(attr.Value, 64)
139+
}
140+
if err != nil {
141+
return err
142+
}
143+
}
144+
c.Path.Start(fixed.Point26_6{
145+
X: fixed.Int26_6((x1 + c.curX) * 64),
146+
Y: fixed.Int26_6((y1 + c.curY) * 64)})
147+
c.Path.Line(fixed.Point26_6{
148+
X: fixed.Int26_6((x2 + c.curX) * 64),
149+
Y: fixed.Int26_6((y2 + c.curY) * 64)})
150+
return nil
151+
}
152+
polylineF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
153+
var err error
154+
for _, attr := range attrs {
155+
switch attr.Name.Local {
156+
case "points":
157+
err = c.GetPoints(attr.Value)
158+
if len(c.points)%2 != 0 {
159+
return errors.New("polygon has odd number of points")
160+
}
161+
}
162+
if err != nil {
163+
return err
164+
}
165+
}
166+
if len(c.points) > 4 {
167+
c.Path.Start(fixed.Point26_6{
168+
X: fixed.Int26_6((c.points[0] + c.curX) * 64),
169+
Y: fixed.Int26_6((c.points[1] + c.curY) * 64)})
170+
for i := 2; i < len(c.points)-1; i += 2 {
171+
c.Path.Line(fixed.Point26_6{
172+
X: fixed.Int26_6((c.points[i] + c.curX) * 64),
173+
Y: fixed.Int26_6((c.points[i+1] + c.curY) * 64)})
174+
}
175+
}
176+
return nil
177+
}
178+
polygonF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
179+
err := polylineF(c, attrs)
180+
if len(c.points) > 4 {
181+
c.Path.Stop(true)
182+
}
183+
return err
184+
}
185+
pathF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
186+
var err error
187+
for _, attr := range attrs {
188+
switch attr.Name.Local {
189+
case "d":
190+
err = c.CompilePath(attr.Value)
191+
}
192+
if err != nil {
193+
return err
194+
}
195+
}
196+
return nil
197+
}
198+
descF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
199+
c.inDescText = true
200+
c.icon.Descriptions = append(c.icon.Descriptions, "")
201+
return nil
202+
}
203+
titleF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
204+
c.inTitleText = true
205+
c.icon.Titles = append(c.icon.Titles, "")
206+
return nil
207+
}
208+
defsF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
209+
c.inDefs = true
210+
return nil
211+
}
212+
linearGradientF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
213+
var err error
214+
c.inGrad = true
215+
c.grad = &rasterx.Gradient{Points: [5]float64{0, 0, 1, 0, 0},
216+
IsRadial: false, Bounds: c.icon.ViewBox, Matrix: rasterx.Identity}
217+
for _, attr := range attrs {
218+
switch attr.Name.Local {
219+
case "id":
220+
id := attr.Value
221+
if len(id) >= 0 {
222+
c.icon.Grads[id] = c.grad
223+
} else {
224+
return errZeroLengthID
225+
}
226+
case "x1":
227+
c.grad.Points[0], err = readFraction(attr.Value)
228+
case "y1":
229+
c.grad.Points[1], err = readFraction(attr.Value)
230+
case "x2":
231+
c.grad.Points[2], err = readFraction(attr.Value)
232+
case "y2":
233+
c.grad.Points[3], err = readFraction(attr.Value)
234+
default:
235+
err = c.ReadGradAttr(attr)
236+
}
237+
if err != nil {
238+
return err
239+
}
240+
}
241+
return nil
242+
}
243+
radialGradientF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
244+
c.inGrad = true
245+
c.grad = &rasterx.Gradient{Points: [5]float64{0.5, 0.5, 0.5, 0.5, 0.5},
246+
IsRadial: true, Bounds: c.icon.ViewBox, Matrix: rasterx.Identity}
247+
var setFx, setFy bool
248+
var err error
249+
for _, attr := range attrs {
250+
switch attr.Name.Local {
251+
case "id":
252+
id := attr.Value
253+
if len(id) >= 0 {
254+
c.icon.Grads[id] = c.grad
255+
} else {
256+
return errZeroLengthID
257+
}
258+
case "r":
259+
c.grad.Points[4], err = readFraction(attr.Value)
260+
case "cx":
261+
c.grad.Points[0], err = readFraction(attr.Value)
262+
case "cy":
263+
c.grad.Points[1], err = readFraction(attr.Value)
264+
case "fx":
265+
setFx = true
266+
c.grad.Points[2], err = readFraction(attr.Value)
267+
case "fy":
268+
setFy = true
269+
c.grad.Points[3], err = readFraction(attr.Value)
270+
default:
271+
err = c.ReadGradAttr(attr)
272+
}
273+
if err != nil {
274+
return err
275+
}
276+
}
277+
if !setFx { // set fx to cx by default
278+
c.grad.Points[2] = c.grad.Points[0]
279+
}
280+
if !setFy { // set fy to cy by default
281+
c.grad.Points[3] = c.grad.Points[1]
282+
}
283+
return nil
284+
}
285+
stopF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
286+
var err error
287+
if c.inGrad {
288+
stop := rasterx.GradStop{Opacity: 1.0}
289+
for _, attr := range attrs {
290+
switch attr.Name.Local {
291+
case "offset":
292+
stop.Offset, err = readFraction(attr.Value)
293+
case "stop-color":
294+
//todo: add current color inherit
295+
stop.StopColor, err = ParseSVGColor(attr.Value)
296+
case "stop-opacity":
297+
stop.Opacity, err = parseFloat(attr.Value, 64)
298+
}
299+
if err != nil {
300+
return err
301+
}
302+
}
303+
c.grad.Stops = append(c.grad.Stops, stop)
304+
}
305+
return nil
306+
}
307+
useF svgFunc = func(c *IconCursor, attrs []xml.Attr) error {
308+
var (
309+
href string
310+
x, y float64
311+
err error
312+
)
313+
for _, attr := range attrs {
314+
switch attr.Name.Local {
315+
case "href":
316+
href = attr.Value
317+
case "x":
318+
x, err = parseFloat(attr.Value, 64)
319+
case "y":
320+
y, err = parseFloat(attr.Value, 64)
321+
}
322+
if err != nil {
323+
return err
324+
}
325+
}
326+
c.curX, c.curY = x, y
327+
defer func() {
328+
c.curX, c.curY = 0, 0
329+
}()
330+
if href == "" {
331+
return errors.New("only use tags with href is supported")
332+
}
333+
if !strings.HasPrefix(href, "#") {
334+
return errors.New("only the ID CSS selector is supported")
335+
}
336+
defs, ok := c.icon.Defs[href[1:]]
337+
if !ok {
338+
return errors.New("href ID in use statement was not found in saved defs")
339+
}
340+
for _, def := range defs {
341+
if def.Tag == "endg" {
342+
// pop style
343+
c.StyleStack = c.StyleStack[:len(c.StyleStack)-1]
344+
continue
345+
}
346+
if err = c.PushStyle(def.Attrs); err != nil {
347+
return err
348+
}
349+
df, ok := drawFuncs[def.Tag]
350+
if !ok {
351+
errStr := "Cannot process svg element " + def.Tag
352+
if c.ErrorMode == StrictErrorMode {
353+
return errors.New(errStr)
354+
} else if c.ErrorMode == WarnErrorMode {
355+
log.Println(errStr)
356+
}
357+
return nil
358+
}
359+
if err := df(c, def.Attrs); err != nil {
360+
return err
361+
}
362+
if def.Tag != "g" {
363+
// pop style
364+
c.StyleStack = c.StyleStack[:len(c.StyleStack)-1]
365+
}
366+
}
367+
return nil
368+
}
369+
)
370+
371+
func init() {
372+
// avoids cyclical static declaration
373+
// called on package initialization
374+
drawFuncs["use"] = useF
375+
}

0 commit comments

Comments
 (0)