Skip to content

Commit 9fb8aca

Browse files
committed
ADD: extract colors
1 parent 256083f commit 9fb8aca

4 files changed

Lines changed: 103 additions & 38 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ Analyze an SVG image
1717
[![golang](https://www.vectorlogo.zone/logos/golang/golang-ar21.svg)](https://golang.org/ "Programming language")
1818
[![NodePing](https://www.vectorlogo.zone/logos/nodeping/nodeping-ar21.svg)](https://nodeping.com?rid=201109281250J5K3P "Uptime monitoring")
1919
[![Twitter](https://www.vectorlogo.zone/logos/twitter/twitter-ar21.svg)](https://github.com/twitter/twemoji/blob/gh-pages/v/14.0.2/svg/1f955.svg "Logo")
20+
21+
* [JoshVarga/svgparser](https://github.com/JoshVarga/svgparser)
22+
* [mazznoer/csscolorparser](https://github.com/mazznoer/csscolorparser)
23+
* [tdewolff/parse/v2/css](https://github.com/tdewolff/parse/v2/css)

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.22.1
44

55
require (
66
github.com/JoshVarga/svgparser v0.0.0-20200804023048-5eaba627a7d1 // indirect
7+
github.com/mazznoer/csscolorparser v0.1.4
8+
github.com/tdewolff/parse/v2 v2.7.15
79
golang.org/x/net v0.27.0 // indirect
810
golang.org/x/text v0.16.0 // indirect
911
)

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
github.com/JoshVarga/svgparser v0.0.0-20200804023048-5eaba627a7d1 h1:RAQocNl+YQYGPt5yh4SR5zFUIHKrXnLhjIGhHO4Vwnc=
22
github.com/JoshVarga/svgparser v0.0.0-20200804023048-5eaba627a7d1/go.mod h1:tMmgUTWcco9d1ZmK7zjxuTv7XWZhyutXIsgu0uJ3gDw=
3+
github.com/mazznoer/csscolorparser v0.1.4 h1:1bAWPWdoMc5qdAXVlixS9rpZLw/vaq5InX/lj3tfRQQ=
4+
github.com/mazznoer/csscolorparser v0.1.4/go.mod h1:OQRVvgCyHDCAquR1YWfSwwaDcM0LhnSffGnlbOew/3I=
5+
github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw=
6+
github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
7+
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
38
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
49
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
510
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=

lib/SvgCheck.go

Lines changed: 92 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@ import (
77
"unicode/utf8"
88

99
"github.com/JoshVarga/svgparser"
10+
"github.com/mazznoer/csscolorparser"
11+
"github.com/tdewolff/parse/v2"
12+
"github.com/tdewolff/parse/v2/css"
1013
)
1114

1215
type SvgCheckResult struct {
13-
SvgHeight string `json:"svgheight"`
14-
SvgWidth string `json:"svgwidth"`
15-
ViewBox string `json:"viewbox"`
16-
Namespace bool `json:"namespace"`
17-
Namespaces []string `json:"namespaces"`
18-
TextCount int `json:"text_count"`
19-
ForeignObjectCount int `json:"foreignobject_count"`
20-
ImageCount int `json:"image_count"`
16+
SvgHeight string `json:"svgheight"`
17+
SvgWidth string `json:"svgwidth"`
18+
ViewBox string `json:"viewbox"`
19+
Namespace bool `json:"namespace"`
20+
Namespaces []string `json:"namespaces"`
21+
TextCount int `json:"text_count"`
22+
ForeignObjectCount int `json:"foreignobject_count"`
23+
ImageCount int `json:"image_count"`
24+
TagCountMap map[string]int `json:"tag_counts"`
25+
Colors []string `json:"colors"`
2126
}
2227

2328
func SvgCheck(logger *slog.Logger, raw []byte) (*SvgCheckResult, error) {
@@ -36,37 +41,31 @@ func SvgCheck(logger *slog.Logger, raw []byte) (*SvgCheckResult, error) {
3641
}
3742

3843
result := &SvgCheckResult{
39-
SvgWidth: rootElement.Attributes["width"],
40-
SvgHeight: rootElement.Attributes["height"],
41-
ViewBox: rootElement.Attributes["viewBox"],
44+
SvgWidth: rootElement.Attributes["width"],
45+
SvgHeight: rootElement.Attributes["height"],
46+
ViewBox: rootElement.Attributes["viewBox"],
47+
TagCountMap: make(map[string]int),
4248
}
43-
/*
44-
if svgNamespace {
45-
46-
namespaces, _ := shared.GetNamespaces(bytes.NewReader(raw))
47-
//fmt.Printf("namespace: %v", namespaces)
48-
f.RecordResult("svgNamespace", namespaces.Default == "http://www.w3.org/2000/svg", map[string]interface{}{
49-
"namespace": namespaces.Default,
50-
})
51-
52-
if len(svgNamespaces) == 0 {
53-
f.RecordResult("svgNoAdditionalNamespaces", len(namespaces.Additional) == 0, map[string]interface{}{
54-
"namespaces": namespaces.Additional,
55-
})
56-
} else if len(svgNamespaces) == 1 && svgNamespaces[0] == "*" {
57-
// no check
58-
} else {
59-
for key, value := range namespaces.Additional {
60-
_, keyExists := svgNamespaceSet[key]
61-
_, valueExists := svgNamespaceSet[value]
62-
f.RecordResult("svgAdditionalNamespaces", keyExists || valueExists, map[string]interface{}{
63-
"namespaceUrl": value,
64-
"namespaceValue": key,
65-
})
66-
}
67-
}
68-
}
69-
*/
49+
50+
// walk all elements and count them
51+
colorMap := make(map[string]bool)
52+
walkElements(rootElement, func(node *svgparser.Element) {
53+
result.TagCountMap[node.Name]++
54+
55+
processColor(logger, node.Attributes["fill"], colorMap)
56+
processColor(logger, node.Attributes["stroke"], colorMap)
57+
processColor(logger, node.Attributes["stop-color"], colorMap)
58+
processColor(logger, node.Attributes["color"], colorMap)
59+
60+
processStyle(logger, node.Attributes["style"], colorMap)
61+
})
62+
i := 0
63+
result.Colors = make([]string, len(colorMap))
64+
for k := range colorMap {
65+
result.Colors[i] = k
66+
i++
67+
}
68+
result.Namespaces = getNamespaces(rootElement)
7069

7170
textNodes := rootElement.FindAll("text")
7271
result.TextCount = len(textNodes)
@@ -79,3 +78,58 @@ func SvgCheck(logger *slog.Logger, raw []byte) (*SvgCheckResult, error) {
7978

8079
return result, nil
8180
}
81+
82+
func walkElements(node *svgparser.Element, f func(*svgparser.Element)) {
83+
f(node)
84+
for _, child := range node.Children {
85+
walkElements(child, f)
86+
}
87+
}
88+
89+
func processColor(logger *slog.Logger, rawColor string, colorMap map[string]bool) {
90+
if rawColor == "" || rawColor == "none" {
91+
return
92+
}
93+
color, colorErr := csscolorparser.Parse(rawColor)
94+
if colorErr != nil {
95+
logger.Error("Unable to parse color", "err", colorErr, "color", rawColor)
96+
} else {
97+
colorMap[color.HexString()] = true
98+
}
99+
}
100+
101+
func processStyle(logger *slog.Logger, rawStyle string, colorMap map[string]bool) {
102+
if rawStyle == "" {
103+
return
104+
}
105+
106+
input := parse.NewInputString(rawStyle)
107+
parser := css.NewParser(input, true)
108+
for {
109+
gt, _, data := parser.Next()
110+
if gt == css.ErrorGrammar {
111+
logger.Error("Unable to parse style", "style", rawStyle, "data", data)
112+
break
113+
}
114+
if gt == css.DeclarationGrammar {
115+
attr := string(data)
116+
if attr == "fill" || attr == "stroke" || attr == "color" || attr == "stop-color" {
117+
for _, val := range parser.Values() {
118+
processColor(logger, string(val.Data), colorMap)
119+
}
120+
}
121+
}
122+
}
123+
}
124+
125+
// this doesn't work. Need to use the xml.decoder stuff
126+
func getNamespaces(node *svgparser.Element) []string {
127+
namespaces := make([]string, 0)
128+
for key := range node.Attributes {
129+
fmt.Println(key)
130+
if strings.Contains(key, "xmlns") {
131+
namespaces = append(namespaces, key)
132+
}
133+
}
134+
return namespaces
135+
}

0 commit comments

Comments
 (0)