2021-04-26 13:52:50 +02:00
|
|
|
package options
|
2018-09-07 15:37:25 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-05-08 13:42:48 +02:00
|
|
|
"net/http"
|
2018-09-07 15:37:25 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-10-10 16:35:17 +02:00
|
|
|
"sync"
|
2021-03-18 12:50:28 +02:00
|
|
|
"time"
|
2019-10-10 16:35:17 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
log "github.com/sirupsen/logrus"
|
2018-09-07 16:45:57 +02:00
|
|
|
|
2021-09-30 16:23:30 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/config"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/ierrors"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/imagetype"
|
2021-10-11 13:50:12 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/imath"
|
2021-09-30 16:23:30 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/structdiff"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/vips"
|
2018-09-07 16:45:57 +02:00
|
|
|
)
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
const maxClientHintDPR = 8
|
2018-10-02 14:20:23 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
var errExpiredURL = errors.New("Expired URL")
|
2018-10-04 14:27:49 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type GravityOptions struct {
|
|
|
|
Type GravityType
|
2019-06-20 14:49:25 +02:00
|
|
|
X, Y float64
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type ExtendOptions struct {
|
2019-12-25 12:50:02 +02:00
|
|
|
Enabled bool
|
2021-04-26 13:52:50 +02:00
|
|
|
Gravity GravityOptions
|
2019-12-25 12:50:02 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type CropOptions struct {
|
2021-01-18 17:14:15 +02:00
|
|
|
Width float64
|
|
|
|
Height float64
|
2021-04-26 13:52:50 +02:00
|
|
|
Gravity GravityOptions
|
2019-06-07 20:14:41 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type PaddingOptions struct {
|
2020-04-09 11:43:56 +02:00
|
|
|
Enabled bool
|
|
|
|
Top int
|
|
|
|
Right int
|
|
|
|
Bottom int
|
|
|
|
Left int
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type TrimOptions struct {
|
2020-01-17 11:54:50 +02:00
|
|
|
Enabled bool
|
|
|
|
Threshold float64
|
2020-04-07 14:06:28 +02:00
|
|
|
Smart bool
|
2021-04-26 13:52:50 +02:00
|
|
|
Color vips.Color
|
2020-04-07 14:06:28 +02:00
|
|
|
EqualHor bool
|
|
|
|
EqualVer bool
|
2020-01-17 11:54:50 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type WatermarkOptions struct {
|
2018-10-19 11:47:11 +02:00
|
|
|
Enabled bool
|
|
|
|
Opacity float64
|
|
|
|
Replicate bool
|
2021-04-26 13:52:50 +02:00
|
|
|
Gravity GravityOptions
|
2018-10-28 14:05:57 +02:00
|
|
|
Scale float64
|
2018-10-19 11:47:11 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type ProcessingOptions struct {
|
|
|
|
ResizingType ResizeType
|
2021-01-12 16:43:09 +02:00
|
|
|
Width int
|
|
|
|
Height int
|
2021-03-29 16:21:00 +02:00
|
|
|
MinWidth int
|
|
|
|
MinHeight int
|
2021-01-12 16:43:09 +02:00
|
|
|
Dpr float64
|
2021-04-26 13:52:50 +02:00
|
|
|
Gravity GravityOptions
|
2021-01-12 16:43:09 +02:00
|
|
|
Enlarge bool
|
2021-04-26 13:52:50 +02:00
|
|
|
Extend ExtendOptions
|
|
|
|
Crop CropOptions
|
|
|
|
Padding PaddingOptions
|
|
|
|
Trim TrimOptions
|
2021-01-13 16:51:19 +02:00
|
|
|
Rotate int
|
2021-04-26 13:52:50 +02:00
|
|
|
Format imagetype.Type
|
2021-01-12 16:43:09 +02:00
|
|
|
Quality int
|
2021-09-29 15:49:18 +02:00
|
|
|
FormatQuality map[imagetype.Type]int
|
2021-01-12 16:43:09 +02:00
|
|
|
MaxBytes int
|
|
|
|
Flatten bool
|
2021-04-26 13:52:50 +02:00
|
|
|
Background vips.Color
|
2021-01-12 16:43:09 +02:00
|
|
|
Blur float32
|
|
|
|
Sharpen float32
|
2021-04-27 12:23:22 +02:00
|
|
|
Pixelate int
|
2021-01-12 16:43:09 +02:00
|
|
|
StripMetadata bool
|
|
|
|
StripColorProfile bool
|
2021-01-13 15:57:29 +02:00
|
|
|
AutoRotate bool
|
2018-10-19 11:47:11 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
SkipProcessingFormats []imagetype.Type
|
2021-03-22 16:16:03 +02:00
|
|
|
|
2018-10-29 10:54:30 +02:00
|
|
|
CacheBuster string
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
Watermark WatermarkOptions
|
2018-10-19 11:47:11 +02:00
|
|
|
|
2019-06-25 11:19:15 +02:00
|
|
|
PreferWebP bool
|
|
|
|
EnforceWebP bool
|
2021-01-18 17:56:36 +02:00
|
|
|
PreferAvif bool
|
|
|
|
EnforceAvif bool
|
2019-06-25 11:19:15 +02:00
|
|
|
|
2019-08-13 13:42:47 +02:00
|
|
|
Filename string
|
|
|
|
|
2018-10-04 14:02:24 +02:00
|
|
|
UsedPresets []string
|
2021-09-29 15:49:18 +02:00
|
|
|
|
|
|
|
defaultQuality int
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2019-10-10 16:35:17 +02:00
|
|
|
var (
|
2021-04-26 13:52:50 +02:00
|
|
|
_newProcessingOptions ProcessingOptions
|
2019-10-10 16:35:17 +02:00
|
|
|
newProcessingOptionsOnce sync.Once
|
|
|
|
)
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func NewProcessingOptions() *ProcessingOptions {
|
2019-10-10 16:35:17 +02:00
|
|
|
newProcessingOptionsOnce.Do(func() {
|
2021-04-26 13:52:50 +02:00
|
|
|
_newProcessingOptions = ProcessingOptions{
|
|
|
|
ResizingType: ResizeFit,
|
2021-01-12 16:43:09 +02:00
|
|
|
Width: 0,
|
|
|
|
Height: 0,
|
2021-04-26 13:52:50 +02:00
|
|
|
Gravity: GravityOptions{Type: GravityCenter},
|
2021-01-12 16:43:09 +02:00
|
|
|
Enlarge: false,
|
2021-04-26 13:52:50 +02:00
|
|
|
Extend: ExtendOptions{Enabled: false, Gravity: GravityOptions{Type: GravityCenter}},
|
|
|
|
Padding: PaddingOptions{Enabled: false},
|
|
|
|
Trim: TrimOptions{Enabled: false, Threshold: 10, Smart: true},
|
2021-01-13 16:51:19 +02:00
|
|
|
Rotate: 0,
|
2021-01-13 13:58:36 +02:00
|
|
|
Quality: 0,
|
2021-01-12 16:43:09 +02:00
|
|
|
MaxBytes: 0,
|
2021-04-26 13:52:50 +02:00
|
|
|
Format: imagetype.Unknown,
|
|
|
|
Background: vips.Color{R: 255, G: 255, B: 255},
|
2021-01-12 16:43:09 +02:00
|
|
|
Blur: 0,
|
|
|
|
Sharpen: 0,
|
|
|
|
Dpr: 1,
|
2021-04-26 13:52:50 +02:00
|
|
|
Watermark: WatermarkOptions{Opacity: 1, Replicate: false, Gravity: GravityOptions{Type: GravityCenter}},
|
|
|
|
StripMetadata: config.StripMetadata,
|
|
|
|
StripColorProfile: config.StripColorProfile,
|
|
|
|
AutoRotate: config.AutoRotate,
|
2021-09-29 15:49:18 +02:00
|
|
|
|
|
|
|
// Basically, we need this to update ETag when `IMGPROXY_QUALITY` is changed
|
|
|
|
defaultQuality: config.Quality,
|
2019-10-10 16:35:17 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
po := _newProcessingOptions
|
2021-04-26 13:52:50 +02:00
|
|
|
po.SkipProcessingFormats = append([]imagetype.Type(nil), config.SkipProcessingFormats...)
|
|
|
|
po.UsedPresets = make([]string, 0, len(config.Presets))
|
2019-10-10 16:35:17 +02:00
|
|
|
|
2021-09-29 15:49:18 +02:00
|
|
|
po.FormatQuality = make(map[imagetype.Type]int)
|
|
|
|
for k, v := range config.FormatQuality {
|
|
|
|
po.FormatQuality[k] = v
|
|
|
|
}
|
|
|
|
|
2019-10-10 16:35:17 +02:00
|
|
|
return &po
|
2019-09-11 12:40:07 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (po *ProcessingOptions) GetQuality() int {
|
2021-01-13 13:58:36 +02:00
|
|
|
q := po.Quality
|
|
|
|
|
|
|
|
if q == 0 {
|
2021-09-29 15:49:18 +02:00
|
|
|
q = po.FormatQuality[po.Format]
|
2021-01-13 13:58:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if q == 0 {
|
2021-09-29 15:49:18 +02:00
|
|
|
q = po.defaultQuality
|
2021-01-13 13:58:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return q
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (po *ProcessingOptions) isPresetUsed(name string) bool {
|
2018-11-02 17:35:21 +02:00
|
|
|
for _, usedName := range po.UsedPresets {
|
|
|
|
if usedName == name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (po *ProcessingOptions) Diff() structdiff.Entries {
|
|
|
|
return structdiff.Diff(NewProcessingOptions(), po)
|
2018-11-02 17:35:21 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (po *ProcessingOptions) String() string {
|
2019-10-10 16:35:17 +02:00
|
|
|
return po.Diff().String()
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (po *ProcessingOptions) MarshalJSON() ([]byte, error) {
|
2019-10-10 16:35:17 +02:00
|
|
|
return po.Diff().MarshalJSON()
|
2019-09-16 11:53:45 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
func parseDimension(d *int, name, arg string) error {
|
|
|
|
if v, err := strconv.Atoi(arg); err == nil && v >= 0 {
|
|
|
|
*d = v
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid %s: %s", name, arg)
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-11 14:58:45 +02:00
|
|
|
func parseBoolOption(str string) bool {
|
|
|
|
b, err := strconv.ParseBool(str)
|
|
|
|
|
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
log.Warningf("`%s` is not a valid boolean value. Treated as false", str)
|
2019-11-11 14:58:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func isGravityOffcetValid(gravity GravityType, offset float64) bool {
|
|
|
|
if gravity == GravityCenter {
|
2019-06-20 14:49:25 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
return offset >= 0 && (gravity != GravityFocusPoint || offset <= 1)
|
2019-06-20 14:49:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func parseGravity(g *GravityOptions, args []string) error {
|
2019-06-20 14:49:25 +02:00
|
|
|
nArgs := len(args)
|
|
|
|
|
|
|
|
if nArgs > 3 {
|
|
|
|
return fmt.Errorf("Invalid gravity arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
if t, ok := gravityTypes[args[0]]; ok {
|
|
|
|
g.Type = t
|
2018-09-07 15:37:25 +02:00
|
|
|
} else {
|
2019-06-07 20:14:41 +02:00
|
|
|
return fmt.Errorf("Invalid gravity: %s", args[0])
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if g.Type == GravitySmart && nArgs > 1 {
|
2019-06-20 14:49:25 +02:00
|
|
|
return fmt.Errorf("Invalid gravity arguments: %v", args)
|
2021-04-26 13:52:50 +02:00
|
|
|
} else if g.Type == GravityFocusPoint && nArgs != 3 {
|
2019-06-20 14:49:25 +02:00
|
|
|
return fmt.Errorf("Invalid gravity arguments: %v", args)
|
|
|
|
}
|
2019-06-07 20:14:41 +02:00
|
|
|
|
2019-06-20 14:49:25 +02:00
|
|
|
if nArgs > 1 {
|
|
|
|
if x, err := strconv.ParseFloat(args[1], 64); err == nil && isGravityOffcetValid(g.Type, x) {
|
2019-06-07 20:14:41 +02:00
|
|
|
g.X = x
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid gravity X: %s", args[1])
|
|
|
|
}
|
2019-06-20 14:49:25 +02:00
|
|
|
}
|
2019-06-07 20:14:41 +02:00
|
|
|
|
2019-06-20 14:49:25 +02:00
|
|
|
if nArgs > 2 {
|
|
|
|
if y, err := strconv.ParseFloat(args[2], 64); err == nil && isGravityOffcetValid(g.Type, y) {
|
2019-06-07 20:14:41 +02:00
|
|
|
g.Y = y
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid gravity Y: %s", args[2])
|
|
|
|
}
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyWidthOption(po *ProcessingOptions, args []string) error {
|
2018-09-07 15:37:25 +02:00
|
|
|
if len(args) > 1 {
|
2019-06-07 20:14:41 +02:00
|
|
|
return fmt.Errorf("Invalid width arguments: %v", args)
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
return parseDimension(&po.Width, "width", args[0])
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyHeightOption(po *ProcessingOptions, args []string) error {
|
2019-06-07 20:14:41 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid height arguments: %v", args)
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
return parseDimension(&po.Height, "height", args[0])
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyMinWidthOption(po *ProcessingOptions, args []string) error {
|
2021-03-29 16:21:00 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid min width arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseDimension(&po.MinWidth, "min width", args[0])
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyMinHeightOption(po *ProcessingOptions, args []string) error {
|
2021-03-29 16:21:00 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid min height arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseDimension(&po.MinHeight, " min height", args[0])
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyEnlargeOption(po *ProcessingOptions, args []string) error {
|
2018-09-07 15:37:25 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid enlarge arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2019-11-11 14:58:45 +02:00
|
|
|
po.Enlarge = parseBoolOption(args[0])
|
2018-09-07 15:37:25 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyExtendOption(po *ProcessingOptions, args []string) error {
|
2019-12-25 12:50:02 +02:00
|
|
|
if len(args) > 4 {
|
2019-06-17 12:15:10 +02:00
|
|
|
return fmt.Errorf("Invalid extend arguments: %v", args)
|
2019-02-21 17:55:20 +02:00
|
|
|
}
|
|
|
|
|
2019-12-25 12:50:02 +02:00
|
|
|
po.Extend.Enabled = parseBoolOption(args[0])
|
|
|
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
if err := parseGravity(&po.Extend.Gravity, args[1:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if po.Extend.Gravity.Type == GravitySmart {
|
2019-12-25 12:50:02 +02:00
|
|
|
return errors.New("extend doesn't support smart gravity")
|
|
|
|
}
|
|
|
|
}
|
2019-02-21 17:55:20 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applySizeOption(po *ProcessingOptions, args []string) (err error) {
|
2019-12-25 12:50:02 +02:00
|
|
|
if len(args) > 7 {
|
2018-09-07 15:37:25 +02:00
|
|
|
return fmt.Errorf("Invalid size arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:33:22 +02:00
|
|
|
if len(args) >= 1 && len(args[0]) > 0 {
|
2018-09-07 15:37:25 +02:00
|
|
|
if err = applyWidthOption(po, args[0:1]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:33:22 +02:00
|
|
|
if len(args) >= 2 && len(args[1]) > 0 {
|
2018-09-07 15:37:25 +02:00
|
|
|
if err = applyHeightOption(po, args[1:2]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:55:20 +02:00
|
|
|
if len(args) >= 3 && len(args[2]) > 0 {
|
2018-09-07 15:37:25 +02:00
|
|
|
if err = applyEnlargeOption(po, args[2:3]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-25 12:50:02 +02:00
|
|
|
if len(args) >= 4 && len(args[3]) > 0 {
|
|
|
|
if err = applyExtendOption(po, args[3:]); err != nil {
|
2019-02-21 17:55:20 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-07 15:37:25 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyResizingTypeOption(po *ProcessingOptions, args []string) error {
|
2018-10-03 17:34:13 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid resizing type arguments: %v", args)
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if r, ok := resizeTypes[args[0]]; ok {
|
2019-10-11 13:05:20 +02:00
|
|
|
po.ResizingType = r
|
2018-09-07 15:37:25 +02:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid resize type: %s", args[0])
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:34:13 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyResizeOption(po *ProcessingOptions, args []string) error {
|
2019-12-25 12:50:02 +02:00
|
|
|
if len(args) > 8 {
|
2018-10-03 17:34:13 +02:00
|
|
|
return fmt.Errorf("Invalid resize arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args[0]) > 0 {
|
|
|
|
if err := applyResizingTypeOption(po, args[0:1]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-07 15:37:25 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
if err := applySizeOption(po, args[1:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyDprOption(po *ProcessingOptions, args []string) error {
|
2018-11-15 13:19:27 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid dpr arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2019-06-19 12:14:41 +02:00
|
|
|
if d, err := strconv.ParseFloat(args[0], 64); err == nil && d > 0 {
|
2018-11-15 13:19:27 +02:00
|
|
|
po.Dpr = d
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid dpr: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyGravityOption(po *ProcessingOptions, args []string) error {
|
2019-06-07 20:14:41 +02:00
|
|
|
return parseGravity(&po.Gravity, args)
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyCropOption(po *ProcessingOptions, args []string) error {
|
2019-06-07 20:14:41 +02:00
|
|
|
if len(args) > 5 {
|
|
|
|
return fmt.Errorf("Invalid crop arguments: %v", args)
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2021-01-18 17:14:15 +02:00
|
|
|
if w, err := strconv.ParseFloat(args[0], 64); err == nil && w >= 0 {
|
|
|
|
po.Crop.Width = w
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid crop width: %s", args[0])
|
2019-06-07 20:14:41 +02:00
|
|
|
}
|
2018-09-11 13:23:28 +02:00
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
if len(args) > 1 {
|
2021-01-18 17:14:15 +02:00
|
|
|
if h, err := strconv.ParseFloat(args[1], 64); err == nil && h >= 0 {
|
|
|
|
po.Crop.Height = h
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid crop height: %s", args[1])
|
2018-09-11 13:23:28 +02:00
|
|
|
}
|
2019-06-07 20:14:41 +02:00
|
|
|
}
|
2018-09-11 13:23:28 +02:00
|
|
|
|
2019-06-07 20:14:41 +02:00
|
|
|
if len(args) > 2 {
|
|
|
|
return parseGravity(&po.Crop.Gravity, args[2:])
|
2018-09-11 13:23:28 +02:00
|
|
|
}
|
|
|
|
|
2018-09-07 15:37:25 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyPaddingOption(po *ProcessingOptions, args []string) error {
|
2020-04-09 11:43:56 +02:00
|
|
|
nArgs := len(args)
|
|
|
|
|
|
|
|
if nArgs < 1 || nArgs > 4 {
|
|
|
|
return fmt.Errorf("Invalid padding arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
po.Padding.Enabled = true
|
|
|
|
|
|
|
|
if nArgs > 0 && len(args[0]) > 0 {
|
|
|
|
if err := parseDimension(&po.Padding.Top, "padding top (+all)", args[0]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
po.Padding.Right = po.Padding.Top
|
|
|
|
po.Padding.Bottom = po.Padding.Top
|
|
|
|
po.Padding.Left = po.Padding.Top
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 1 && len(args[1]) > 0 {
|
|
|
|
if err := parseDimension(&po.Padding.Right, "padding right (+left)", args[1]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
po.Padding.Left = po.Padding.Right
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 2 && len(args[2]) > 0 {
|
|
|
|
if err := parseDimension(&po.Padding.Bottom, "padding bottom", args[2]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 3 && len(args[3]) > 0 {
|
|
|
|
if err := parseDimension(&po.Padding.Left, "padding left", args[3]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if po.Padding.Top == 0 && po.Padding.Right == 0 && po.Padding.Bottom == 0 && po.Padding.Left == 0 {
|
|
|
|
po.Padding.Enabled = false
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyTrimOption(po *ProcessingOptions, args []string) error {
|
2020-04-07 14:06:28 +02:00
|
|
|
nArgs := len(args)
|
|
|
|
|
|
|
|
if nArgs > 4 {
|
|
|
|
return fmt.Errorf("Invalid trim arguments: %v", args)
|
2020-01-17 11:54:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if t, err := strconv.ParseFloat(args[0], 64); err == nil && t >= 0 {
|
|
|
|
po.Trim.Enabled = true
|
|
|
|
po.Trim.Threshold = t
|
|
|
|
} else {
|
2020-04-07 14:06:28 +02:00
|
|
|
return fmt.Errorf("Invalid trim threshold: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 1 && len(args[1]) > 0 {
|
2021-04-26 13:52:50 +02:00
|
|
|
if c, err := vips.ColorFromHex(args[1]); err == nil {
|
2020-04-07 14:06:28 +02:00
|
|
|
po.Trim.Color = c
|
|
|
|
po.Trim.Smart = false
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid trim color: %s", args[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 2 && len(args[2]) > 0 {
|
|
|
|
po.Trim.EqualHor = parseBoolOption(args[2])
|
|
|
|
}
|
|
|
|
|
|
|
|
if nArgs > 3 && len(args[3]) > 0 {
|
|
|
|
po.Trim.EqualVer = parseBoolOption(args[3])
|
2020-01-17 11:54:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyRotateOption(po *ProcessingOptions, args []string) error {
|
2021-01-13 16:51:19 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid rotate arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
if r, err := strconv.Atoi(args[0]); err == nil && r%90 == 0 {
|
|
|
|
po.Rotate = r
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid rotation angle: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyQualityOption(po *ProcessingOptions, args []string) error {
|
2018-10-30 14:20:02 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid quality arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2021-01-13 13:58:36 +02:00
|
|
|
if q, err := strconv.Atoi(args[0]); err == nil && q >= 0 && q <= 100 {
|
2018-10-30 14:20:02 +02:00
|
|
|
po.Quality = q
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid quality: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-29 15:49:18 +02:00
|
|
|
func applyFormatQualityOption(po *ProcessingOptions, args []string) error {
|
|
|
|
argsLen := len(args)
|
|
|
|
if len(args)%2 != 0 {
|
|
|
|
return fmt.Errorf("Missing quality for: %s", args[argsLen-1])
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < argsLen; i += 2 {
|
|
|
|
f, ok := imagetype.Types[args[i]]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Invalid image format: %s", args[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
if q, err := strconv.Atoi(args[i+1]); err == nil && q >= 0 && q <= 100 {
|
|
|
|
po.FormatQuality[f] = q
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid quality for %s: %s", args[i], args[i+1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyMaxBytesOption(po *ProcessingOptions, args []string) error {
|
2019-11-29 14:27:35 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid max_bytes arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
if max, err := strconv.Atoi(args[0]); err == nil && max >= 0 {
|
|
|
|
po.MaxBytes = max
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid max_bytes: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyBackgroundOption(po *ProcessingOptions, args []string) error {
|
2018-10-04 14:27:49 +02:00
|
|
|
switch len(args) {
|
|
|
|
case 1:
|
|
|
|
if len(args[0]) == 0 {
|
|
|
|
po.Flatten = false
|
2021-04-26 13:52:50 +02:00
|
|
|
} else if c, err := vips.ColorFromHex(args[0]); err == nil {
|
2018-10-04 14:27:49 +02:00
|
|
|
po.Flatten = true
|
|
|
|
po.Background = c
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid background argument: %s", err)
|
|
|
|
}
|
2018-10-02 14:20:23 +02:00
|
|
|
|
2018-10-04 14:27:49 +02:00
|
|
|
case 3:
|
|
|
|
po.Flatten = true
|
2018-10-02 14:20:23 +02:00
|
|
|
|
2019-04-11 16:07:09 +02:00
|
|
|
if r, err := strconv.ParseUint(args[0], 10, 8); err == nil && r <= 255 {
|
2018-10-04 14:27:49 +02:00
|
|
|
po.Background.R = uint8(r)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid background red channel: %s", args[0])
|
|
|
|
}
|
2018-10-02 14:20:23 +02:00
|
|
|
|
2019-04-11 16:07:09 +02:00
|
|
|
if g, err := strconv.ParseUint(args[1], 10, 8); err == nil && g <= 255 {
|
2018-10-04 14:27:49 +02:00
|
|
|
po.Background.G = uint8(g)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid background green channel: %s", args[1])
|
|
|
|
}
|
2018-10-02 14:20:23 +02:00
|
|
|
|
2019-04-11 16:07:09 +02:00
|
|
|
if b, err := strconv.ParseUint(args[2], 10, 8); err == nil && b <= 255 {
|
2018-10-04 14:27:49 +02:00
|
|
|
po.Background.B = uint8(b)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid background blue channel: %s", args[2])
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Invalid background arguments: %v", args)
|
|
|
|
}
|
2018-10-02 14:20:23 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyBlurOption(po *ProcessingOptions, args []string) error {
|
2018-09-07 16:46:16 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid blur arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2019-05-28 13:39:20 +02:00
|
|
|
if b, err := strconv.ParseFloat(args[0], 32); err == nil && b >= 0 {
|
2018-09-07 16:46:16 +02:00
|
|
|
po.Blur = float32(b)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid blur: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applySharpenOption(po *ProcessingOptions, args []string) error {
|
2018-09-07 16:46:16 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid sharpen arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2019-05-28 13:39:20 +02:00
|
|
|
if s, err := strconv.ParseFloat(args[0], 32); err == nil && s >= 0 {
|
2018-09-07 16:46:16 +02:00
|
|
|
po.Sharpen = float32(s)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid sharpen: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-17 14:52:40 +02:00
|
|
|
func applyPixelateOption(po *ProcessingOptions, args []string) error {
|
2021-04-27 12:23:22 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid pixelate arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
if p, err := strconv.Atoi(args[0]); err == nil && p >= 0 {
|
|
|
|
po.Pixelate = p
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid pixelate: %s", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyPresetOption(po *ProcessingOptions, args []string) error {
|
2018-09-07 19:41:06 +02:00
|
|
|
for _, preset := range args {
|
2021-04-26 13:52:50 +02:00
|
|
|
if p, ok := presets[preset]; ok {
|
2018-10-04 14:02:24 +02:00
|
|
|
if po.isPresetUsed(preset) {
|
2021-04-26 13:52:50 +02:00
|
|
|
log.Warningf("Recursive preset usage is detected: %s", preset)
|
2019-05-07 11:19:23 +02:00
|
|
|
continue
|
2018-10-04 14:02:24 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
po.UsedPresets = append(po.UsedPresets, preset)
|
2018-10-04 14:02:24 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if err := applyURLOptions(po, p); err != nil {
|
2018-09-12 15:46:43 +02:00
|
|
|
return err
|
2018-09-07 19:41:06 +02:00
|
|
|
}
|
|
|
|
} else {
|
2019-08-28 15:01:02 +02:00
|
|
|
return fmt.Errorf("Unknown preset: %s", preset)
|
2018-09-07 19:41:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyWatermarkOption(po *ProcessingOptions, args []string) error {
|
2018-10-28 14:05:57 +02:00
|
|
|
if len(args) > 7 {
|
|
|
|
return fmt.Errorf("Invalid watermark arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2018-10-19 11:47:11 +02:00
|
|
|
if o, err := strconv.ParseFloat(args[0], 64); err == nil && o >= 0 && o <= 1 {
|
|
|
|
po.Watermark.Enabled = o > 0
|
|
|
|
po.Watermark.Opacity = o
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid watermark opacity: %s", args[0])
|
|
|
|
}
|
|
|
|
|
2018-10-28 14:05:57 +02:00
|
|
|
if len(args) > 1 && len(args[1]) > 0 {
|
2018-10-19 11:47:11 +02:00
|
|
|
if args[1] == "re" {
|
|
|
|
po.Watermark.Replicate = true
|
2021-04-26 13:52:50 +02:00
|
|
|
} else if g, ok := gravityTypes[args[1]]; ok && g != GravityFocusPoint && g != GravitySmart {
|
2019-12-25 12:50:02 +02:00
|
|
|
po.Watermark.Gravity.Type = g
|
2018-10-19 11:47:11 +02:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid watermark position: %s", args[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 14:05:57 +02:00
|
|
|
if len(args) > 2 && len(args[2]) > 0 {
|
2018-10-19 11:47:11 +02:00
|
|
|
if x, err := strconv.Atoi(args[2]); err == nil {
|
2019-12-25 12:50:02 +02:00
|
|
|
po.Watermark.Gravity.X = float64(x)
|
2018-10-19 11:47:11 +02:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid watermark X offset: %s", args[2])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 14:05:57 +02:00
|
|
|
if len(args) > 3 && len(args[3]) > 0 {
|
2018-10-19 11:47:11 +02:00
|
|
|
if y, err := strconv.Atoi(args[3]); err == nil {
|
2019-12-25 12:50:02 +02:00
|
|
|
po.Watermark.Gravity.Y = float64(y)
|
2018-10-19 11:47:11 +02:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid watermark Y offset: %s", args[3])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 14:05:57 +02:00
|
|
|
if len(args) > 4 && len(args[4]) > 0 {
|
|
|
|
if s, err := strconv.ParseFloat(args[4], 64); err == nil && s >= 0 {
|
|
|
|
po.Watermark.Scale = s
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid watermark scale: %s", args[4])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-19 11:47:11 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyFormatOption(po *ProcessingOptions, args []string) error {
|
2018-09-11 14:26:27 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid format arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if f, ok := imagetype.Types[args[0]]; ok {
|
2018-09-11 14:26:27 +02:00
|
|
|
po.Format = f
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid image format: %s", args[0])
|
|
|
|
}
|
|
|
|
|
2018-09-07 15:37:25 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyCacheBusterOption(po *ProcessingOptions, args []string) error {
|
2018-10-29 10:54:30 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid cache buster arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
po.CacheBuster = args[0]
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applySkipProcessingFormatsOption(po *ProcessingOptions, args []string) error {
|
2021-03-22 16:16:03 +02:00
|
|
|
for _, format := range args {
|
2021-04-26 13:52:50 +02:00
|
|
|
if f, ok := imagetype.Types[format]; ok {
|
2021-03-22 16:16:03 +02:00
|
|
|
po.SkipProcessingFormats = append(po.SkipProcessingFormats, f)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Invalid image format in skip processing: %s", format)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyFilenameOption(po *ProcessingOptions, args []string) error {
|
2019-08-13 13:42:47 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid filename arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
po.Filename = args[0]
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyExpiresOption(po *ProcessingOptions, args []string) error {
|
2021-03-18 12:50:28 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid expires arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
timestamp, err := strconv.ParseInt(args[0], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Invalid expires argument: %v", args[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
if timestamp > 0 && timestamp < time.Now().Unix() {
|
2021-04-26 13:52:50 +02:00
|
|
|
return errExpiredURL
|
2021-03-18 12:50:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyStripMetadataOption(po *ProcessingOptions, args []string) error {
|
2020-09-01 13:55:00 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid strip metadata arguments: %v", args)
|
2020-07-01 15:45:33 +02:00
|
|
|
}
|
|
|
|
|
2020-09-01 13:55:00 +02:00
|
|
|
po.StripMetadata = parseBoolOption(args[0])
|
|
|
|
|
2020-07-01 15:45:33 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyStripColorProfileOption(po *ProcessingOptions, args []string) error {
|
2021-01-12 16:43:09 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid strip color profile arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
po.StripColorProfile = parseBoolOption(args[0])
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyAutoRotateOption(po *ProcessingOptions, args []string) error {
|
2021-01-13 15:57:29 +02:00
|
|
|
if len(args) > 1 {
|
|
|
|
return fmt.Errorf("Invalid auto rotate arguments: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
po.AutoRotate = parseBoolOption(args[0])
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyURLOption(po *ProcessingOptions, name string, args []string) error {
|
2018-09-07 15:37:25 +02:00
|
|
|
switch name {
|
2018-10-03 17:36:52 +02:00
|
|
|
case "resize", "rs":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyResizeOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "size", "s":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applySizeOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "resizing_type", "rt":
|
|
|
|
return applyResizingTypeOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "width", "w":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyWidthOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "height", "h":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyHeightOption(po, args)
|
2021-03-29 16:21:00 +02:00
|
|
|
case "min-width", "mw":
|
|
|
|
return applyMinWidthOption(po, args)
|
|
|
|
case "min-height", "mh":
|
|
|
|
return applyMinHeightOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "dpr":
|
|
|
|
return applyDprOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "enlarge", "el":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyEnlargeOption(po, args)
|
2019-02-21 17:55:20 +02:00
|
|
|
case "extend", "ex":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyExtendOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "gravity", "g":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyGravityOption(po, args)
|
2019-06-07 20:14:41 +02:00
|
|
|
case "crop", "c":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyCropOption(po, args)
|
2020-01-17 11:54:50 +02:00
|
|
|
case "trim", "t":
|
|
|
|
return applyTrimOption(po, args)
|
2020-04-09 11:43:56 +02:00
|
|
|
case "padding", "pd":
|
|
|
|
return applyPaddingOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "auto_rotate", "ar":
|
|
|
|
return applyAutoRotateOption(po, args)
|
|
|
|
case "rotate", "rot":
|
|
|
|
return applyRotateOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "background", "bg":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyBackgroundOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "blur", "bl":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyBlurOption(po, args)
|
2018-10-03 17:36:52 +02:00
|
|
|
case "sharpen", "sh":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applySharpenOption(po, args)
|
2021-04-27 12:23:22 +02:00
|
|
|
case "pixelate", "pix":
|
|
|
|
return applyPixelateOption(po, args)
|
2018-10-19 11:47:11 +02:00
|
|
|
case "watermark", "wm":
|
2019-08-13 13:56:14 +02:00
|
|
|
return applyWatermarkOption(po, args)
|
2020-07-01 15:50:47 +02:00
|
|
|
case "strip_metadata", "sm":
|
|
|
|
return applyStripMetadataOption(po, args)
|
2021-01-12 16:43:09 +02:00
|
|
|
case "strip_color_profile", "scp":
|
|
|
|
return applyStripColorProfileOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
// Saving options
|
|
|
|
case "quality", "q":
|
|
|
|
return applyQualityOption(po, args)
|
2021-09-29 15:49:18 +02:00
|
|
|
case "format_quality", "fq":
|
|
|
|
return applyFormatQualityOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "max_bytes", "mb":
|
|
|
|
return applyMaxBytesOption(po, args)
|
|
|
|
case "format", "f", "ext":
|
|
|
|
return applyFormatOption(po, args)
|
|
|
|
// Handling options
|
2021-03-22 16:16:03 +02:00
|
|
|
case "skip_processing", "skp":
|
|
|
|
return applySkipProcessingFormatsOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "cachebuster", "cb":
|
|
|
|
return applyCacheBusterOption(po, args)
|
2021-03-18 12:50:28 +02:00
|
|
|
case "expires", "exp":
|
|
|
|
return applyExpiresOption(po, args)
|
2021-04-16 18:35:20 +02:00
|
|
|
case "filename", "fn":
|
|
|
|
return applyFilenameOption(po, args)
|
|
|
|
// Presets
|
|
|
|
case "preset", "pr":
|
|
|
|
return applyPresetOption(po, args)
|
2018-09-12 15:46:43 +02:00
|
|
|
}
|
|
|
|
|
2019-08-13 13:56:14 +02:00
|
|
|
return fmt.Errorf("Unknown processing option: %s", name)
|
2018-09-12 15:46:43 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func applyURLOptions(po *ProcessingOptions, options urlOptions) error {
|
2019-07-22 13:27:18 +02:00
|
|
|
for _, opt := range options {
|
2021-04-26 13:52:50 +02:00
|
|
|
if err := applyURLOption(po, opt.Name, opt.Args); err != nil {
|
2018-09-12 15:46:43 +02:00
|
|
|
return err
|
|
|
|
}
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func defaultProcessingOptions(headers http.Header) (*ProcessingOptions, error) {
|
|
|
|
po := NewProcessingOptions()
|
2019-12-25 10:34:15 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
headerAccept := headers.Get("Accept")
|
2018-09-07 15:37:25 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if strings.Contains(headerAccept, "image/webp") {
|
|
|
|
po.PreferWebP = config.EnableWebpDetection || config.EnforceWebp
|
|
|
|
po.EnforceWebP = config.EnforceWebp
|
2018-09-07 19:41:06 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if strings.Contains(headerAccept, "image/avif") {
|
|
|
|
po.PreferAvif = config.EnableAvifDetection || config.EnforceAvif
|
|
|
|
po.EnforceAvif = config.EnforceAvif
|
2018-09-07 19:41:06 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if config.EnableClientHints {
|
2021-10-11 13:50:12 +02:00
|
|
|
if headerDPR := headers.Get("DPR"); len(headerDPR) > 0 {
|
|
|
|
if dpr, err := strconv.ParseFloat(headerDPR, 64); err == nil && (dpr > 0 && dpr <= maxClientHintDPR) {
|
|
|
|
po.Dpr = dpr
|
|
|
|
}
|
|
|
|
}
|
2021-04-26 13:52:50 +02:00
|
|
|
if headerViewportWidth := headers.Get("Viewport-Width"); len(headerViewportWidth) > 0 {
|
|
|
|
if vw, err := strconv.Atoi(headerViewportWidth); err == nil {
|
|
|
|
po.Width = vw
|
|
|
|
}
|
2018-11-02 18:38:41 +02:00
|
|
|
}
|
2021-04-26 13:52:50 +02:00
|
|
|
if headerWidth := headers.Get("Width"); len(headerWidth) > 0 {
|
|
|
|
if w, err := strconv.Atoi(headerWidth); err == nil {
|
2021-10-11 13:50:12 +02:00
|
|
|
po.Width = imath.Scale(w, 1/po.Dpr)
|
2021-04-26 13:52:50 +02:00
|
|
|
}
|
2018-11-14 15:46:59 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-26 13:52:50 +02:00
|
|
|
|
|
|
|
if _, ok := presets["default"]; ok {
|
2019-09-11 12:40:07 +02:00
|
|
|
if err := applyPresetOption(po, []string{"default"}); err != nil {
|
|
|
|
return po, err
|
2019-09-11 10:42:36 +02:00
|
|
|
}
|
2018-09-11 12:33:36 +02:00
|
|
|
}
|
|
|
|
|
2019-09-11 12:40:07 +02:00
|
|
|
return po, nil
|
2018-09-11 12:33:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func parsePathOptions(parts []string, headers http.Header) (*ProcessingOptions, string, error) {
|
2018-11-02 18:38:41 +02:00
|
|
|
po, err := defaultProcessingOptions(headers)
|
2018-09-11 12:33:36 +02:00
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2018-09-11 12:33:36 +02:00
|
|
|
}
|
2018-09-07 19:41:06 +02:00
|
|
|
|
|
|
|
options, urlParts := parseURLOptions(parts)
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if err = applyURLOptions(po, options); err != nil {
|
|
|
|
return nil, "", err
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
url, extension, err := DecodeURL(urlParts)
|
2018-09-07 15:37:25 +02:00
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2018-09-11 14:26:27 +02:00
|
|
|
if len(extension) > 0 {
|
2019-09-11 10:42:36 +02:00
|
|
|
if err = applyFormatOption(po, []string{extension}); err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2018-09-11 14:26:27 +02:00
|
|
|
}
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
return po, url, nil
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func parsePathPresets(parts []string, headers http.Header) (*ProcessingOptions, string, error) {
|
2019-05-16 12:24:42 +02:00
|
|
|
po, err := defaultProcessingOptions(headers)
|
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2019-05-16 12:24:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
presets := strings.Split(parts[0], ":")
|
|
|
|
urlParts := parts[1:]
|
|
|
|
|
2019-09-11 10:42:36 +02:00
|
|
|
if err = applyPresetOption(po, presets); err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2019-05-16 12:24:42 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
url, extension, err := DecodeURL(urlParts)
|
2019-05-16 12:24:42 +02:00
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2019-05-16 12:24:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(extension) > 0 {
|
2019-09-11 10:42:36 +02:00
|
|
|
if err = applyFormatOption(po, []string{extension}); err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", err
|
2019-05-16 12:24:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
return po, url, nil
|
2019-05-16 12:24:42 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func ParsePath(path string, headers http.Header) (*ProcessingOptions, string, error) {
|
|
|
|
if path == "" || path == "/" {
|
|
|
|
return nil, "", ierrors.New(404, fmt.Sprintf("Invalid path: %s", path), "Invalid URL")
|
2020-04-07 13:54:00 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
|
2020-07-08 14:57:24 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
var (
|
|
|
|
imageURL string
|
|
|
|
po *ProcessingOptions
|
|
|
|
err error
|
|
|
|
)
|
2018-09-07 15:37:25 +02:00
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
if config.OnlyPresets {
|
|
|
|
po, imageURL, err = parsePathPresets(parts, headers)
|
2018-10-05 17:17:36 +02:00
|
|
|
} else {
|
2021-04-26 13:52:50 +02:00
|
|
|
po, imageURL, err = parsePathOptions(parts, headers)
|
2018-10-05 17:17:36 +02:00
|
|
|
}
|
|
|
|
|
2018-10-18 13:01:51 +02:00
|
|
|
if err != nil {
|
2021-04-26 13:52:50 +02:00
|
|
|
return nil, "", ierrors.New(404, err.Error(), "Invalid URL")
|
2018-10-18 13:01:51 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
return po, imageURL, nil
|
2018-09-07 15:37:25 +02:00
|
|
|
}
|