mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-25 19:24:38 +02:00
Remove the IMGPROXY_SVG_FIX_UNSUPPORTED
config. The problem it was solving is now fixed in librsvg
This commit is contained in:
@@ -27,6 +27,9 @@
|
||||
- (pro) Fix BlurHash generation when the `IMGPROXY_USE_LINEAR_COLORSPACE` config is set to `true`.
|
||||
- (pro) Fix detection of PDF files with a header offset.
|
||||
|
||||
### Removed
|
||||
- Remove the `IMGPROXY_SVG_FIX_UNSUPPORTED` config. The problem it was solving is now fixed in librsvg.
|
||||
|
||||
## [3.28.0] - 2025-03-31
|
||||
### Added
|
||||
- Add [IMGPROXY_BASE64_URL_INCLUDES_FILENAME](https://docs.imgproxy.net/latest/configuration/options#IMGPROXY_BASE64_URL_INCLUDES_FILENAME) config.
|
||||
|
@@ -65,7 +65,6 @@ var (
|
||||
AutoRotate bool
|
||||
EnforceThumbnail bool
|
||||
ReturnAttachment bool
|
||||
SvgFixUnsupported bool
|
||||
|
||||
AutoWebp bool
|
||||
EnforceWebp bool
|
||||
@@ -276,7 +275,6 @@ func Reset() {
|
||||
AutoRotate = true
|
||||
EnforceThumbnail = false
|
||||
ReturnAttachment = false
|
||||
SvgFixUnsupported = false
|
||||
|
||||
AutoWebp = false
|
||||
EnforceWebp = false
|
||||
@@ -508,7 +506,6 @@ func Configure() error {
|
||||
configurators.Bool(&AutoRotate, "IMGPROXY_AUTO_ROTATE")
|
||||
configurators.Bool(&EnforceThumbnail, "IMGPROXY_ENFORCE_THUMBNAIL")
|
||||
configurators.Bool(&ReturnAttachment, "IMGPROXY_RETURN_ATTACHMENT")
|
||||
configurators.Bool(&SvgFixUnsupported, "IMGPROXY_SVG_FIX_UNSUPPORTED")
|
||||
|
||||
if _, ok := os.LookupEnv("IMGPROXY_ENABLE_WEBP_DETECTION"); ok {
|
||||
log.Warning("IMGPROXY_ENABLE_WEBP_DETECTION is deprecated, use IMGPROXY_AUTO_WEBP instead")
|
||||
|
@@ -446,21 +446,6 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
||||
))
|
||||
}
|
||||
|
||||
// We're going to rasterize SVG. Since librsvg lacks the support of some SVG
|
||||
// features, we're going to replace them to minimize rendering error
|
||||
if originData.Type == imagetype.SVG && config.SvgFixUnsupported {
|
||||
fixed, changed, svgErr := svg.FixUnsupported(originData)
|
||||
checkErr(ctx, "svg_processing", svgErr)
|
||||
|
||||
if changed {
|
||||
// Since we'll replace origin data, it's better to close it to return
|
||||
// it's buffer to the pool
|
||||
originData.Close()
|
||||
|
||||
originData = fixed
|
||||
}
|
||||
}
|
||||
|
||||
resultData, err := func() (*imagedata.ImageData, error) {
|
||||
defer metrics.StartProcessingSegment(ctx, metrics.Meta{
|
||||
metrics.MetaProcessingOptions: metricsMeta[metrics.MetaProcessingOptions],
|
||||
|
133
svg/svg.go
133
svg/svg.go
@@ -2,31 +2,15 @@ package svg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
nanoid "github.com/matoous/go-nanoid/v2"
|
||||
"github.com/tdewolff/parse/v2"
|
||||
"github.com/tdewolff/parse/v2/xml"
|
||||
|
||||
"github.com/imgproxy/imgproxy/v3/imagedata"
|
||||
)
|
||||
|
||||
var feDropShadowName = []byte("feDropShadow")
|
||||
|
||||
var feDropShadowTemplate = strings.TrimSpace(`
|
||||
<feMerge result="dsin-%[1]s"><feMergeNode %[3]s /></feMerge>
|
||||
<feGaussianBlur %[4]s />
|
||||
<feOffset %[5]s result="dsof-%[2]s" />
|
||||
<feFlood %[6]s />
|
||||
<feComposite in2="dsof-%[2]s" operator="in" />
|
||||
<feMerge %[7]s>
|
||||
<feMergeNode />
|
||||
<feMergeNode in="dsin-%[1]s" />
|
||||
</feMerge>
|
||||
`)
|
||||
|
||||
func cloneHeaders(src map[string]string) map[string]string {
|
||||
if src == nil {
|
||||
return nil
|
||||
@@ -111,120 +95,3 @@ func Sanitize(data *imagedata.ImageData) (*imagedata.ImageData, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func replaceDropShadowNode(l *xml.Lexer, buf *bytes.Buffer) error {
|
||||
var (
|
||||
inAttrs strings.Builder
|
||||
blurAttrs strings.Builder
|
||||
offsetAttrs strings.Builder
|
||||
floodAttrs strings.Builder
|
||||
finalAttrs strings.Builder
|
||||
)
|
||||
|
||||
inID, _ := nanoid.New(8)
|
||||
offsetID, _ := nanoid.New(8)
|
||||
|
||||
hasStdDeviation := false
|
||||
hasDx := false
|
||||
hasDy := false
|
||||
|
||||
TOKEN_LOOP:
|
||||
for {
|
||||
tt, tdata := l.Next()
|
||||
|
||||
switch tt {
|
||||
case xml.ErrorToken:
|
||||
if l.Err() != io.EOF {
|
||||
return l.Err()
|
||||
}
|
||||
break TOKEN_LOOP
|
||||
case xml.EndTagToken, xml.StartTagCloseVoidToken:
|
||||
break TOKEN_LOOP
|
||||
case xml.AttributeToken:
|
||||
switch strings.ToLower(string(l.Text())) {
|
||||
case "in":
|
||||
inAttrs.Write(tdata)
|
||||
case "stddeviation":
|
||||
blurAttrs.Write(tdata)
|
||||
hasStdDeviation = true
|
||||
case "dx":
|
||||
offsetAttrs.Write(tdata)
|
||||
hasDx = true
|
||||
case "dy":
|
||||
offsetAttrs.Write(tdata)
|
||||
hasDy = true
|
||||
case "flood-color", "flood-opacity":
|
||||
floodAttrs.Write(tdata)
|
||||
default:
|
||||
finalAttrs.Write(tdata)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasStdDeviation {
|
||||
blurAttrs.WriteString(` stdDeviation="2"`)
|
||||
}
|
||||
|
||||
if !hasDx {
|
||||
offsetAttrs.WriteString(` dx="2"`)
|
||||
}
|
||||
|
||||
if !hasDy {
|
||||
offsetAttrs.WriteString(` dy="2"`)
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
buf, feDropShadowTemplate,
|
||||
inID, offsetID,
|
||||
inAttrs.String(),
|
||||
blurAttrs.String(),
|
||||
offsetAttrs.String(),
|
||||
floodAttrs.String(),
|
||||
finalAttrs.String(),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func FixUnsupported(data *imagedata.ImageData) (*imagedata.ImageData, bool, error) {
|
||||
if !bytes.Contains(data.Data, feDropShadowName) {
|
||||
return data, false, nil
|
||||
}
|
||||
|
||||
r := bytes.NewReader(data.Data)
|
||||
l := xml.NewLexer(parse.NewInput(r))
|
||||
|
||||
buf, cancel := imagedata.BorrowBuffer()
|
||||
|
||||
for {
|
||||
tt, tdata := l.Next()
|
||||
|
||||
switch tt {
|
||||
case xml.ErrorToken:
|
||||
if l.Err() != io.EOF {
|
||||
cancel()
|
||||
return nil, false, l.Err()
|
||||
}
|
||||
|
||||
newData := imagedata.ImageData{
|
||||
Data: buf.Bytes(),
|
||||
Type: data.Type,
|
||||
Headers: cloneHeaders(data.Headers),
|
||||
}
|
||||
newData.SetCancel(cancel)
|
||||
|
||||
return &newData, true, nil
|
||||
case xml.StartTagToken:
|
||||
if bytes.Equal(l.Text(), feDropShadowName) {
|
||||
if err := replaceDropShadowNode(l, buf); err != nil {
|
||||
cancel()
|
||||
return nil, false, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
buf.Write(tdata)
|
||||
default:
|
||||
buf.Write(tdata)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package svg
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
@@ -52,32 +51,6 @@ func (s *SvgTestSuite) TestSanitize() {
|
||||
s.Require().Equal(origin.Headers, actual.Headers)
|
||||
}
|
||||
|
||||
func (s *SvgTestSuite) TestFixUnsupportedDropShadow() {
|
||||
origin := s.readTestFile("test1.drop-shadow.svg")
|
||||
expected := s.readTestFile("test1.drop-shadow.fixed.svg")
|
||||
|
||||
actual, changed, err := FixUnsupported(origin)
|
||||
|
||||
// `FixUnsupported` generates random IDs, we need to replace them for the test
|
||||
re := regexp.MustCompile(`"ds(in|of)-.+?"`)
|
||||
actualData := re.ReplaceAllString(string(actual.Data), `"ds$1-test"`)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(changed)
|
||||
s.Require().Equal(string(expected.Data), actualData)
|
||||
s.Require().Equal(origin.Headers, actual.Headers)
|
||||
}
|
||||
|
||||
func (s *SvgTestSuite) TestFixUnsupportedNothingChanged() {
|
||||
origin := s.readTestFile("test1.svg")
|
||||
|
||||
actual, changed, err := FixUnsupported(origin)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().False(changed)
|
||||
s.Require().Equal(origin, actual)
|
||||
}
|
||||
|
||||
func TestSvg(t *testing.T) {
|
||||
suite.Run(t, new(SvgTestSuite))
|
||||
}
|
||||
|
Reference in New Issue
Block a user