1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2024-11-24 08:12:38 +02:00
imgproxy/processing_options.go

344 lines
6.5 KiB
Go

package main
/*
#cgo LDFLAGS: -s -w
#include <image_types.h>
*/
import "C"
import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
)
type imageType int
const (
imageTypeUnknown = C.UNKNOWN
imageTypeJPEG = C.JPEG
imageTypePNG = C.PNG
imageTypeWEBP = C.WEBP
imageTypeGIF = C.GIF
)
var imageTypes = map[string]imageType{
"jpeg": imageTypeJPEG,
"jpg": imageTypeJPEG,
"png": imageTypePNG,
"webp": imageTypeWEBP,
"gif": imageTypeGIF,
}
type gravityType int
const (
gravityCenter gravityType = iota
gravityNorth
gravityEast
gravitySouth
gravityWest
gravitySmart
)
var gravityTypes = map[string]gravityType{
"ce": gravityCenter,
"no": gravityNorth,
"ea": gravityEast,
"so": gravitySouth,
"we": gravityWest,
"sm": gravitySmart,
}
type resizeType int
const (
resizeFit resizeType = iota
resizeFill
resizeCrop
)
var resizeTypes = map[string]resizeType{
"fit": resizeFit,
"fill": resizeFill,
"crop": resizeCrop,
}
type processingOptions struct {
Resize resizeType
Width int
Height int
Gravity gravityType
Enlarge bool
Format imageType
Blur float32
Sharpen float32
}
func defaultProcessingOptions() processingOptions {
return processingOptions{
Resize: resizeFit,
Width: 0,
Height: 0,
Gravity: gravityCenter,
Enlarge: false,
Format: imageTypeJPEG,
Blur: 0,
Sharpen: 0,
}
}
func decodeURL(parts []string) (string, imageType, error) {
var imgType imageType = imageTypeJPEG
urlParts := strings.Split(strings.Join(parts, ""), ".")
if len(urlParts) > 2 {
return "", 0, errors.New("Invalid url encoding")
}
if len(urlParts) == 2 {
if f, ok := imageTypes[urlParts[1]]; ok {
imgType = f
} else {
return "", 0, fmt.Errorf("Invalid image format: %s", urlParts[1])
}
}
url, err := base64.RawURLEncoding.DecodeString(urlParts[0])
if err != nil {
return "", 0, errors.New("Invalid url encoding")
}
return string(url), imgType, nil
}
func applyWidthOption(po *processingOptions, args []string) error {
if len(args) > 1 {
return fmt.Errorf("Invalid width arguments: %v", args)
}
if w, err := strconv.Atoi(args[0]); err == nil || w >= 0 {
po.Width = w
} else {
return fmt.Errorf("Invalid width: %s", args[0])
}
return nil
}
func applyHeightOption(po *processingOptions, args []string) error {
if len(args) > 1 {
return fmt.Errorf("Invalid height arguments: %v", args)
}
if h, err := strconv.Atoi(args[0]); err == nil || po.Height >= 0 {
po.Height = h
} else {
return fmt.Errorf("Invalid height: %s", args[0])
}
return nil
}
func applyEnlargeOption(po *processingOptions, args []string) error {
if len(args) > 1 {
return fmt.Errorf("Invalid enlarge arguments: %v", args)
}
po.Enlarge = args[0] != "0"
return nil
}
func applySizeOption(po *processingOptions, args []string) (err error) {
if len(args) > 3 {
return fmt.Errorf("Invalid size arguments: %v", args)
}
if len(args) >= 1 {
if err = applyWidthOption(po, args[0:1]); err != nil {
return
}
}
if len(args) >= 2 {
if err = applyHeightOption(po, args[1:2]); err != nil {
return
}
}
if len(args) == 3 {
if err = applyEnlargeOption(po, args[2:3]); err != nil {
return
}
}
return nil
}
func applyResizeOption(po *processingOptions, args []string) error {
if len(args) > 4 {
return fmt.Errorf("Invalid resize arguments: %v", args)
}
if r, ok := resizeTypes[args[0]]; ok {
po.Resize = r
} else {
return fmt.Errorf("Invalid resize type: %s", args[0])
}
if len(args) > 1 {
if err := applySizeOption(po, args[1:]); err != nil {
return err
}
}
return nil
}
func applyGravityOption(po *processingOptions, args []string) error {
if len(args) > 1 {
return fmt.Errorf("Invalid resize arguments: %v", args)
}
if g, ok := gravityTypes[args[0]]; ok {
po.Gravity = g
} else {
return fmt.Errorf("Invalid gravity: %s", args[0])
}
return nil
}
func applyFormatOption(po *processingOptions, imgType imageType) error {
if !vipsTypeSupportSave[imgType] {
return errors.New("Resulting image type not supported")
}
po.Format = imgType
return nil
}
func applyProcessingOption(po *processingOptions, name string, args []string) error {
switch name {
case "resize":
if err := applyResizeOption(po, args); err != nil {
return err
}
case "size":
if err := applySizeOption(po, args); err != nil {
return err
}
case "width":
if err := applyWidthOption(po, args); err != nil {
return err
}
case "height":
if err := applyHeightOption(po, args); err != nil {
return err
}
case "enlarge":
if err := applyEnlargeOption(po, args); err != nil {
return err
}
case "gravity":
if err := applyGravityOption(po, args); err != nil {
return err
}
}
return nil
}
func parsePathAdvanced(parts []string) (string, processingOptions, error) {
var urlStart int
po := defaultProcessingOptions()
for i, part := range parts {
args := strings.Split(part, ":")
if len(args) == 1 {
urlStart = i
break
}
if err := applyProcessingOption(&po, args[0], args[1:]); err != nil {
return "", po, err
}
}
url, imgType, err := decodeURL(parts[urlStart:])
if err != nil {
return "", po, err
}
if err := applyFormatOption(&po, imgType); err != nil {
return "", po, errors.New("Resulting image type not supported")
}
return string(url), po, nil
}
func parsePathSimple(parts []string) (string, processingOptions, error) {
var po processingOptions
var err error
if len(parts) < 6 {
return "", po, errors.New("Invalid path")
}
po.Resize = resizeTypes[parts[0]]
if err = applyWidthOption(&po, parts[1:2]); err != nil {
return "", po, err
}
if err = applyHeightOption(&po, parts[2:3]); err != nil {
return "", po, err
}
if err = applyGravityOption(&po, parts[3:4]); err != nil {
return "", po, err
}
if err = applyEnlargeOption(&po, parts[4:5]); err != nil {
return "", po, err
}
url, imgType, err := decodeURL(parts[5:])
if err != nil {
return "", po, err
}
if err := applyFormatOption(&po, imgType); err != nil {
return "", po, errors.New("Resulting image type not supported")
}
return string(url), po, nil
}
func parsePath(r *http.Request) (string, processingOptions, error) {
path := r.URL.Path
parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
if len(parts) < 3 {
return "", processingOptions{}, errors.New("Invalid path")
}
// if err := validatePath(parts[0], strings.TrimPrefix(path, fmt.Sprintf("/%s", parts[0]))); err != nil {
// return "", processingOptions{}, err
// }
if _, ok := resizeTypes[parts[1]]; ok {
return parsePathSimple(parts[1:])
} else {
return parsePathAdvanced(parts[1:])
}
}