mirror of https://github.com/go-gitea/gitea
Refactor backend SVG package and add tests (#26335)
Introduce a well-tested `svg.Normalize` function. Make `RenderHTML` faster and more stable.pull/26320/head^2
parent
12c249c5ca
commit
5db4c8db93
@ -0,0 +1,59 @@ |
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package svg |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"regexp" |
||||||
|
"sync" |
||||||
|
) |
||||||
|
|
||||||
|
type normalizeVarsStruct struct { |
||||||
|
reXMLDoc, |
||||||
|
reComment, |
||||||
|
reAttrXMLNs, |
||||||
|
reAttrSize, |
||||||
|
reAttrClassPrefix *regexp.Regexp |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
normalizeVars *normalizeVarsStruct |
||||||
|
normalizeVarsOnce sync.Once |
||||||
|
) |
||||||
|
|
||||||
|
// Normalize normalizes the SVG content: set default width/height, remove unnecessary tags/attributes
|
||||||
|
// It's designed to work with valid SVG content. For invalid SVG content, the returned content is not guaranteed.
|
||||||
|
func Normalize(data []byte, size int) []byte { |
||||||
|
normalizeVarsOnce.Do(func() { |
||||||
|
normalizeVars = &normalizeVarsStruct{ |
||||||
|
reXMLDoc: regexp.MustCompile(`(?s)<\?xml.*?>`), |
||||||
|
reComment: regexp.MustCompile(`(?s)<!--.*?-->`), |
||||||
|
|
||||||
|
reAttrXMLNs: regexp.MustCompile(`(?s)\s+xmlns\s*=\s*"[^"]*"`), |
||||||
|
reAttrSize: regexp.MustCompile(`(?s)\s+(width|height)\s*=\s*"[^"]+"`), |
||||||
|
reAttrClassPrefix: regexp.MustCompile(`(?s)\s+class\s*=\s*"`), |
||||||
|
} |
||||||
|
}) |
||||||
|
data = normalizeVars.reXMLDoc.ReplaceAll(data, nil) |
||||||
|
data = normalizeVars.reComment.ReplaceAll(data, nil) |
||||||
|
|
||||||
|
data = bytes.TrimSpace(data) |
||||||
|
svgTag, svgRemaining, ok := bytes.Cut(data, []byte(">")) |
||||||
|
if !ok || !bytes.HasPrefix(svgTag, []byte(`<svg`)) { |
||||||
|
return data |
||||||
|
} |
||||||
|
normalized := bytes.Clone(svgTag) |
||||||
|
normalized = normalizeVars.reAttrXMLNs.ReplaceAll(normalized, nil) |
||||||
|
normalized = normalizeVars.reAttrSize.ReplaceAll(normalized, nil) |
||||||
|
normalized = normalizeVars.reAttrClassPrefix.ReplaceAll(normalized, []byte(` class="`)) |
||||||
|
normalized = bytes.TrimSpace(normalized) |
||||||
|
normalized = fmt.Appendf(normalized, ` width="%d" height="%d"`, size, size) |
||||||
|
if !bytes.Contains(normalized, []byte(` class="`)) { |
||||||
|
normalized = append(normalized, ` class="svg"`...) |
||||||
|
} |
||||||
|
normalized = append(normalized, '>') |
||||||
|
normalized = append(normalized, svgRemaining...) |
||||||
|
return normalized |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package svg |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestNormalize(t *testing.T) { |
||||||
|
res := Normalize([]byte("foo"), 1) |
||||||
|
assert.Equal(t, "foo", string(res)) |
||||||
|
|
||||||
|
res = Normalize([]byte(`<?xml version="1.0"?> |
||||||
|
<!-- |
||||||
|
comment |
||||||
|
--> |
||||||
|
<svg xmlns = "...">content</svg>`), 1) |
||||||
|
assert.Equal(t, `<svg width="1" height="1" class="svg">content</svg>`, string(res)) |
||||||
|
|
||||||
|
res = Normalize([]byte(`<svg |
||||||
|
width="100" |
||||||
|
class="svg-icon" |
||||||
|
>content</svg>`), 16) |
||||||
|
|
||||||
|
assert.Equal(t, `<svg class="svg-icon" width="16" height="16">content</svg>`, string(res)) |
||||||
|
} |
Loading…
Reference in new issue