Przeglądaj źródła

Better SVG detection

DarthSim 5 lat temu
rodzic
commit
af8366139f
7 zmienionych plików z 63 dodań i 9 usunięć
  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.
 - `imgproxy health` command.
 - (pro) `IMGPROXY_GIF_OPTIMIZE_FRAMES` & `IMGPROXY_GIF_OPTIMIZE_TRANSPARENCY` configs and `gif_options` processing option.
 - (pro) `IMGPROXY_GIF_OPTIMIZE_FRAMES` & `IMGPROXY_GIF_OPTIMIZE_TRANSPARENCY` configs and `gif_options` processing option.
 
 
+### Changed
+
+- Better SVG detection.
+
 ### Fixed
 ### Fixed
 - Fix detection of SVG starting with a comment.
 - Fix detection of SVG starting with a comment.
 
 

+ 3 - 0
config.go

@@ -163,6 +163,7 @@ type config struct {
 	MaxSrcResolution   int
 	MaxSrcResolution   int
 	MaxSrcFileSize     int
 	MaxSrcFileSize     int
 	MaxAnimationFrames int
 	MaxAnimationFrames int
+	MaxSvgCheckBytes   int
 
 
 	JpegProgressive       bool
 	JpegProgressive       bool
 	PngInterlaced         bool
 	PngInterlaced         bool
@@ -245,6 +246,7 @@ var conf = config{
 	TTL:                            3600,
 	TTL:                            3600,
 	MaxSrcResolution:               16800000,
 	MaxSrcResolution:               16800000,
 	MaxAnimationFrames:             1,
 	MaxAnimationFrames:             1,
+	MaxSvgCheckBytes:               32 * 1024,
 	SignatureSize:                  32,
 	SignatureSize:                  32,
 	PngQuantizationColors:          256,
 	PngQuantizationColors:          256,
 	Quality:                        80,
 	Quality:                        80,
@@ -294,6 +296,7 @@ func configure() {
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
 	intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")
 	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 {
 	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")
 		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.
 **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:
 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;
 * `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.
 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
 ## HEIC support
 
 
 imgproxy supports HEIC only when using libvips 8.8.0+. Official imgproxy Docker image supports HEIC out of the box.
 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)
 	downloadBufPool = newBufPool("download", conf.Concurrency, conf.DownloadBufferSize)
+
+	imagemeta.SetMaxSvgCheckRead(conf.MaxSvgCheckBytes)
 }
 }
 
 
 func checkDimensions(width, height int) error {
 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
 	return nil, ErrFormat
 }
 }

+ 40 - 9
imagemeta/svg.go

@@ -1,17 +1,48 @@
 package imagemeta
 package imagemeta
 
 
 import (
 import (
+	"encoding/xml"
 	"io"
 	"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
 }
 }