@@ -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
1215type 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
2328func 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