Selaa lähdekoodia

Better SVG detection

DarthSim 5 vuotta sitten
vanhempi
commit
af8366139f
7 muutettua tiedostoa jossa 63 lisäystä ja 9 poistoa
  1. 4 0
      CHANGELOG.md
  2. 3 0
      config.go
  3. 4 0
      docs/configuration.md
  4. 4 0
      docs/image_formats_support.md
  5. 2 0
      download.go
  6. 6 0
      imagemeta/image_meta.go
  7. 40 9
      imagemeta/svg.go

+ 4 - 0
CHANGELOG.md

@@ -7,6 +7,10 @@
 - `imgproxy health` command.
 - (pro) `IMGPROXY_GIF_OPTIMIZE_FRAMES` & `IMGPROXY_GIF_OPTIMIZE_TRANSPARENCY` configs and `gif_options` processing option.
 
+### Changed
+
+- Better SVG detection.
+
 ### Fixed
 - Fix detection of SVG starting with a comment.
 

+ 3 - 0
config.go

@@ -163,6 +163,7 @@ type config struct {
 	MaxSrcResolution   int
 	MaxSrcFileSize     int
 	MaxAnimationFrames int
+	MaxSvgCheckBytes   int
 
 	JpegProgressive       bool
 	PngInterlaced         bool
@@ -245,6 +246,7 @@ var conf = config{
 	TTL:                            3600,
 	MaxSrcResolution:               16800000,
 	MaxAnimationFrames:             1,
+	MaxSvgCheckBytes:               32 * 1024,
 	SignatureSize:                  32,
 	PngQuantizationColors:          256,
 	Quality:                        80,
@@ -294,6 +296,7 @@ func configure() {
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
 	intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")
+	intEnvConfig(&conf.MaxSvgCheckBytes, "IMGPROXY_MAX_SVG_CHECK_BYTES")
 
 	if _, ok := os.LookupEnv("IMGPROXY_MAX_GIF_FRAMES"); ok {
 		logWarning("`IMGPROXY_MAX_GIF_FRAMES` is deprecated and will be removed in future versions. Use `IMGPROXY_MAX_ANIMATION_FRAMES` instead")

+ 4 - 0
docs/configuration.md

@@ -53,6 +53,10 @@ imgproxy can process animated images (GIF, WebP), but since this operation is pr
 
 **Note:** imgproxy summarizes all frames resolutions while checking source image resolution.
 
+imgproxy reads some amount of bytes to check if the source image is SVG. By default it reads maximum of 32KB, but you can change this:
+
+* `IMGPROXY_MAX_SVG_CHECK_BYTES`: the maximum number of bytes imgproxy will read to recognize SVG. If imgproxy can't recognize your SVG, try to increase this number. Default: `32768` (32KB)
+
 You can also specify a secret to enable authorization with the HTTP `Authorization` header for use in production environments:
 
 * `IMGPROXY_SECRET`: the authorization token. If specified, the HTTP request should contain the `Authorization: Bearer %secret%` header;

+ 4 - 0
docs/image_formats_support.md

@@ -27,6 +27,10 @@ imgproxy supports SVG sources without limitations, but SVG results are not suppo
 
 When the source image is SVG and the SVG result is requested, imgproxy returns source image without modifications.
 
+imgproxy reads some amount of bytes to check if the source image is SVG. By default it reads maximum of 32KB, but you can change this:
+
+* `IMGPROXY_MAX_SVG_CHECK_BYTES`: the maximum number of bytes imgproxy will read to recognize SVG. If imgproxy can't recognize your SVG, try to increase this number. Default: `32768` (32KB)
+
 ## HEIC support
 
 imgproxy supports HEIC only when using libvips 8.8.0+. Official imgproxy Docker image supports HEIC out of the box.

+ 2 - 0
download.go

@@ -90,6 +90,8 @@ func initDownloading() {
 	}
 
 	downloadBufPool = newBufPool("download", conf.Concurrency, conf.DownloadBufferSize)
+
+	imagemeta.SetMaxSvgCheckRead(conf.MaxSvgCheckBytes)
 }
 
 func checkDimensions(width, height int) error {

+ 6 - 0
imagemeta/image_meta.go

@@ -88,5 +88,11 @@ func DecodeMeta(r io.Reader) (Meta, error) {
 		}
 	}
 
+	if ok, err := IsSVG(rr); err != nil {
+		return nil, err
+	} else if ok {
+		return &meta{format: "svg", width: 1, height: 1}, nil
+	}
+
 	return nil, ErrFormat
 }

+ 40 - 9
imagemeta/svg.go

@@ -1,17 +1,48 @@
 package imagemeta
 
 import (
+	"encoding/xml"
 	"io"
+	"sync/atomic"
 )
 
-func init() {
-	// Register fake svg decoder. Since we need this only for type detecting, we can
-	// return fake image sizes
-	decodeMeta := func(io.Reader) (Meta, error) {
-		return &meta{format: "svg", width: 1, height: 1}, nil
+var maxSvgBytes int64 = 32 * 1024
+
+type svgHeader struct {
+	XMLName xml.Name
+}
+
+func SetMaxSvgCheckRead(n int) {
+	atomic.StoreInt64(&maxSvgBytes, int64(n))
+}
+
+func IsSVG(r io.Reader) (bool, error) {
+	maxBytes := int(atomic.LoadInt64(&maxSvgBytes))
+
+	var h svgHeader
+
+	buf := make([]byte, 0, maxBytes)
+	b := make([]byte, 1024)
+
+	for {
+		n, err := r.Read(b)
+		if err != nil && err != io.EOF {
+			return false, err
+		}
+		if n <= 0 {
+			return false, nil
+		}
+
+		buf = append(buf, b[:n]...)
+
+		if xml.Unmarshal(buf, &h); h.XMLName.Local == "svg" {
+			return true, nil
+		}
+
+		if len(buf) >= maxBytes {
+			break
+		}
 	}
-	RegisterFormat("<?xml ", decodeMeta)
-	RegisterFormat("<svg", decodeMeta)
-	// We believe that file starting with HTML comment is SVG
-	RegisterFormat("<!--", decodeMeta)
+
+	return false, nil
 }