Browse Source

SVG sources support

DarthSim 6 years ago
parent
commit
5501c3cff7
7 changed files with 78 additions and 22 deletions
  1. 2 2
      Dockerfile
  2. 2 1
      docs/image_formats_support.md
  3. 2 1
      image_types.h
  4. 32 18
      process.go
  5. 2 0
      processing_options.go
  6. 22 0
      svg.go
  7. 16 0
      vips.h

+ 2 - 2
Dockerfile

@@ -11,7 +11,7 @@ WORKDIR /go/src/github.com/DarthSim/imgproxy
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
   && apk --no-cache upgrade \
   && apk add --no-cache curl ca-certificates go gcc g++ make musl-dev fftw-dev glib-dev expat-dev \
-    libjpeg-turbo-dev libpng-dev libwebp-dev giflib-dev libexif-dev lcms2-dev
+    libjpeg-turbo-dev libpng-dev libwebp-dev giflib-dev librsvg-dev libexif-dev lcms2-dev
 
 # Build ImageMagick
 RUN cd /root \
@@ -77,7 +77,7 @@ LABEL maintainer="Sergey Alexandrovich <darthsim@gmail.com>"
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
   && apk --no-cache upgrade \
   && apk add --no-cache bash ca-certificates fftw glib expat libjpeg-turbo libpng \
-    libwebp giflib libexif lcms2 \
+    libwebp giflib librsvg libgsf libexif lcms2 \
   && rm -rf /var/cache/apk*
 
 COPY --from=0 /usr/local/bin/imgproxy /usr/local/bin/

+ 2 - 1
docs/image_formats_support.md

@@ -6,7 +6,8 @@ At the moment, imgproxy supports only the most popular Web image formats:
 * JPEG;
 * WebP;
 * GIF;
-* ICO.
+* ICO;
+* SVG _(source only)_.
 
 ## GIF support
 

+ 2 - 1
image_types.h

@@ -4,5 +4,6 @@ enum types {
   PNG,
   WEBP,
   GIF,
-  ICO
+  ICO,
+  SVG
 };

+ 32 - 18
process.go

@@ -73,6 +73,9 @@ func initVips() {
 	if int(C.vips_type_find_load_go(C.int(imageTypeGIF))) != 0 {
 		vipsTypeSupportLoad[imageTypeGIF] = true
 	}
+	if int(C.vips_type_find_load_go(C.int(imageTypeSVG))) != 0 {
+		vipsTypeSupportLoad[imageTypeSVG] = true
+	}
 
 	// we load ICO with github.com/mat/besticon/ico and send decoded data to vips
 	vipsTypeSupportLoad[imageTypeICO] = true
@@ -140,7 +143,7 @@ func extractMeta(img *C.VipsImage) (int, int, int, bool) {
 	return width, height, angle, flip
 }
 
-func calcScale(width, height int, po *processingOptions) float64 {
+func calcScale(width, height int, po *processingOptions, imgtype imageType) float64 {
 	// If we're going only to crop, we need only to scale down to DPR.
 	// Scaling up while cropping is not optimal on this stage, we'll do it later if needed.
 	if po.Resize == resizeCrop {
@@ -173,7 +176,7 @@ func calcScale(width, height int, po *processingOptions) float64 {
 
 	scale = scale * po.Dpr
 
-	if !po.Enlarge && scale > 1 {
+	if !po.Enlarge && scale > 1 && imgtype != imageTypeSVG {
 		return 1
 	}
 
@@ -274,22 +277,31 @@ func transformImage(ctx context.Context, img **C.struct__VipsImage, data []byte,
 
 	hasAlpha := vipsImageHasAlpha(*img)
 
-	if scale := calcScale(imgWidth, imgHeight, po); scale != 1 {
-		// Do some shrink-on-load
-		if scale < 1.0 && data != nil {
-			if shrink := calcShink(scale, imgtype); shrink != 1 {
-				scale = scale * float64(shrink)
-
-				if tmp, err := vipsLoadImage(data, imgtype, shrink, false); err == nil {
-					C.swap_and_clear(img, tmp)
-				} else {
-					return err
+	if scale := calcScale(imgWidth, imgHeight, po, imgtype); scale != 1 {
+		if imgtype == imageTypeSVG && data != nil {
+			// Load SVG with desired scale
+			if tmp, err := vipsLoadImage(data, imgtype, 1, scale, false); err == nil {
+				C.swap_and_clear(img, tmp)
+			} else {
+				return err
+			}
+		} else {
+			// Do some shrink-on-load
+			if scale < 1.0 && data != nil {
+				if shrink := calcShink(scale, imgtype); shrink != 1 {
+					scale = scale * float64(shrink)
+
+					if tmp, err := vipsLoadImage(data, imgtype, shrink, 1.0, false); err == nil {
+						C.swap_and_clear(img, tmp)
+					} else {
+						return err
+					}
 				}
 			}
-		}
 
-		if err = resizeImage(img, scale, hasAlpha); err != nil {
-			return err
+			if err = resizeImage(img, scale, hasAlpha); err != nil {
+				return err
+			}
 		}
 
 		// Update actual image size after resize
@@ -506,7 +518,7 @@ func processImage(ctx context.Context) ([]byte, error) {
 		}
 	}
 
-	img, err := vipsLoadImage(data, imgtype, 1, po.Format == imageTypeGIF)
+	img, err := vipsLoadImage(data, imgtype, 1, 1.0, po.Format == imageTypeGIF)
 	if err != nil {
 		return nil, err
 	}
@@ -546,7 +558,7 @@ func vipsPrepareWatermark() error {
 		return nil
 	}
 
-	watermark, err = vipsLoadImage(data, imgtype, 1, false)
+	watermark, err = vipsLoadImage(data, imgtype, 1, 1.0, false)
 	if err != nil {
 		return err
 	}
@@ -593,7 +605,7 @@ func vipsPrepareWatermark() error {
 	return nil
 }
 
-func vipsLoadImage(data []byte, imgtype imageType, shrink int, allPages bool) (*C.struct__VipsImage, error) {
+func vipsLoadImage(data []byte, imgtype imageType, shrink int, svgScale float64, allPages bool) (*C.struct__VipsImage, error) {
 	var img *C.struct__VipsImage
 
 	err := C.int(0)
@@ -612,6 +624,8 @@ func vipsLoadImage(data []byte, imgtype imageType, shrink int, allPages bool) (*
 		err = C.vips_webpload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(shrink), &img)
 	case imageTypeGIF:
 		err = C.vips_gifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), pages, &img)
+	case imageTypeSVG:
+		err = C.vips_svgload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.double(svgScale), &img)
 	case imageTypeICO:
 		rawData, width, height, icoErr := icoData(data)
 		if icoErr != nil {

+ 2 - 0
processing_options.go

@@ -29,6 +29,7 @@ const (
 	imageTypeWEBP    = imageType(C.WEBP)
 	imageTypeGIF     = imageType(C.GIF)
 	imageTypeICO     = imageType(C.ICO)
+	imageTypeSVG     = imageType(C.SVG)
 )
 
 type processingHeaders struct {
@@ -45,6 +46,7 @@ var imageTypes = map[string]imageType{
 	"webp": imageTypeWEBP,
 	"gif":  imageTypeGIF,
 	"ico":  imageTypeICO,
+	"svg":  imageTypeSVG,
 }
 
 type gravityType int

+ 22 - 0
svg.go

@@ -0,0 +1,22 @@
+package main
+
+import (
+	"image"
+	goColor "image/color"
+	"io"
+)
+
+func init() {
+	// Register fake svg decoder. Since we need this only for type detecting, we can
+	// return fake image sizes
+	image.RegisterFormat(
+		"svg",
+		"<?xml ",
+		func(io.Reader) (image.Image, error) {
+			return image.NewRGBA(image.Rect(0, 0, 1, 1)), nil
+		},
+		func(io.Reader) (image.Config, error) {
+			return image.Config{ColorModel: goColor.RGBAModel, Width: 1, Height: 1}, nil
+		},
+	)
+}

+ 16 - 0
vips.h

@@ -12,6 +12,9 @@
 #define VIPS_SUPPORT_GIF \
   (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 3))
 
+#define VIPS_SUPPORT_SVG \
+  (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 3))
+
 #define VIPS_SUPPORT_MAGICK \
   (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 7))
 
@@ -52,6 +55,9 @@ vips_type_find_load_go(int imgtype) {
   if (imgtype == GIF) {
     return vips_type_find("VipsOperation", "gifload_buffer");
   }
+  if (imgtype == SVG) {
+    return vips_type_find("VipsOperation", "svgload_buffer");
+  }
   return 0;
 }
 
@@ -106,6 +112,16 @@ vips_gifload_go(void *buf, size_t len, int pages, VipsImage **out) {
   #endif
 }
 
+int
+vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out) {
+  #if VIPS_SUPPORT_SVG
+    return vips_svgload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "scale", scale, NULL);
+  #else
+    vips_error("vips_svgload_go", "Loading SVG is not supported");
+    return 1;
+  #endif
+}
+
 int
 vips_get_exif_orientation(VipsImage *image) {
 	const char *orientation;