• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cshum / imagor / 4851669446

01 May 2023 01:52PM UTC coverage: 90.837% (-0.1%) from 90.949%
4851669446

Pull #359

github

Adrian Shum
feat(vips): pdf page selection support with page(n) filter
Pull Request #359: feat(vips): pdf page selection support with page(n) filter

53 of 53 new or added lines in 4 files covered. (100.0%)

5641 of 6210 relevant lines covered (90.84%)

1.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.58
/vips/process.go
1
package vips
2

3
import (
4
        "context"
5
        "github.com/cshum/imagor"
6
        "github.com/cshum/imagor/imagorpath"
7
        "go.uber.org/zap"
8
        "math"
9
        "strconv"
10
        "strings"
11
        "time"
12
)
13

14
var imageTypeMap = map[string]ImageType{
15
        "gif":    ImageTypeGIF,
16
        "jpeg":   ImageTypeJPEG,
17
        "jpg":    ImageTypeJPEG,
18
        "magick": ImageTypeMagick,
19
        "pdf":    ImageTypePDF,
20
        "png":    ImageTypePNG,
21
        "svg":    ImageTypeSVG,
22
        "tiff":   ImageTypeTIFF,
23
        "webp":   ImageTypeWEBP,
24
        "heif":   ImageTypeHEIF,
25
        "bmp":    ImageTypeBMP,
26
        "avif":   ImageTypeAVIF,
27
        "jp2":    ImageTypeJP2K,
28
}
29

30
// Process implements imagor.Processor interface
31
func (v *Processor) Process(
32
        ctx context.Context, blob *imagor.Blob, p imagorpath.Params, load imagor.LoadFunc,
33
) (*imagor.Blob, error) {
1✔
34
        ctx = withContext(ctx)
1✔
35
        defer contextDone(ctx)
1✔
36
        var (
1✔
37
                thumbnailNotSupported bool
1✔
38
                upscale               = true
1✔
39
                stretch               = p.Stretch
1✔
40
                thumbnail             = false
1✔
41
                stripExif             bool
1✔
42
                orient                int
1✔
43
                img                   *Image
1✔
44
                format                = ImageTypeUnknown
1✔
45
                maxN                  = v.MaxAnimationFrames
1✔
46
                maxBytes              int
1✔
47
                page                  = 1
1✔
48
                focalRects            []focal
1✔
49
                err                   error
1✔
50
        )
1✔
51
        if p.Trim {
2✔
52
                thumbnailNotSupported = true
1✔
53
        }
1✔
54
        if p.FitIn {
2✔
55
                upscale = false
1✔
56
        }
1✔
57
        if maxN == 0 || maxN < -1 {
2✔
58
                maxN = 1
1✔
59
        }
1✔
60
        if blob != nil && !blob.SupportsAnimation() {
2✔
61
                maxN = 1
1✔
62
        }
1✔
63
        for _, p := range p.Filters {
2✔
64
                if v.disableFilters[p.Name] {
2✔
65
                        continue
1✔
66
                }
67
                switch p.Name {
1✔
68
                case "format":
1✔
69
                        if imageType, ok := imageTypeMap[p.Args]; ok {
2✔
70
                                format = supportedSaveFormat(imageType)
1✔
71
                                if !IsAnimationSupported(format) {
2✔
72
                                        // no frames if export format not support animation
1✔
73
                                        maxN = 1
1✔
74
                                }
1✔
75
                        }
76
                        break
1✔
77
                case "max_frames":
1✔
78
                        if n, _ := strconv.Atoi(p.Args); n > 0 && (maxN == -1 || n < maxN) {
2✔
79
                                maxN = n
1✔
80
                        }
1✔
81
                        break
1✔
82
                case "stretch":
1✔
83
                        stretch = true
1✔
84
                        break
1✔
85
                case "upscale":
1✔
86
                        upscale = true
1✔
87
                        break
1✔
88
                case "no_upscale":
1✔
89
                        upscale = false
1✔
90
                        break
1✔
91
                case "fill", "background_color":
1✔
92
                        if args := strings.Split(p.Args, ","); args[0] == "auto" {
2✔
93
                                thumbnailNotSupported = true
1✔
94
                        }
1✔
95
                        break
1✔
96
                case "page":
×
97
                        if n, _ := strconv.Atoi(p.Args); n > 0 {
×
98
                                page = n
×
99
                        }
×
100
                        break
×
101
                case "orient":
1✔
102
                        if n, _ := strconv.Atoi(p.Args); n > 0 {
2✔
103
                                orient = n
1✔
104
                                thumbnailNotSupported = true
1✔
105
                        }
1✔
106
                        break
1✔
107
                case "max_bytes":
1✔
108
                        if n, _ := strconv.Atoi(p.Args); n > 0 {
2✔
109
                                maxBytes = n
1✔
110
                                thumbnailNotSupported = true
1✔
111
                        }
1✔
112
                        break
1✔
113
                case "trim", "focal", "rotate":
1✔
114
                        thumbnailNotSupported = true
1✔
115
                        break
1✔
116
                case "strip_exif":
1✔
117
                        stripExif = true
1✔
118
                        break
1✔
119
                }
120
        }
121

122
        if !thumbnailNotSupported &&
1✔
123
                p.CropBottom == 0.0 && p.CropTop == 0.0 && p.CropLeft == 0.0 && p.CropRight == 0.0 {
2✔
124
                // apply shrink-on-load where possible
1✔
125
                if p.FitIn {
2✔
126
                        if p.Width > 0 || p.Height > 0 {
2✔
127
                                w := p.Width
1✔
128
                                h := p.Height
1✔
129
                                if w == 0 {
2✔
130
                                        w = v.MaxWidth
1✔
131
                                }
1✔
132
                                if h == 0 {
2✔
133
                                        h = v.MaxHeight
1✔
134
                                }
1✔
135
                                size := SizeDown
1✔
136
                                if upscale {
2✔
137
                                        size = SizeBoth
1✔
138
                                }
1✔
139
                                if img, err = v.NewThumbnail(
1✔
140
                                        ctx, blob, w, h, InterestingNone, size, maxN, page,
1✔
141
                                ); err != nil {
1✔
142
                                        return nil, err
×
143
                                }
×
144
                                thumbnail = true
1✔
145
                        }
146
                } else if stretch {
2✔
147
                        if p.Width > 0 && p.Height > 0 {
2✔
148
                                if img, err = v.NewThumbnail(
1✔
149
                                        ctx, blob, p.Width, p.Height,
1✔
150
                                        InterestingNone, SizeForce, maxN, page,
1✔
151
                                ); err != nil {
1✔
152
                                        return nil, err
×
153
                                }
×
154
                                thumbnail = true
1✔
155
                        }
156
                } else {
1✔
157
                        if p.Width > 0 && p.Height > 0 {
2✔
158
                                interest := InterestingNone
1✔
159
                                if p.Smart {
2✔
160
                                        interest = InterestingAttention
1✔
161
                                        thumbnail = true
1✔
162
                                } else if (p.VAlign == imagorpath.VAlignTop && p.HAlign == "") ||
2✔
163
                                        (p.HAlign == imagorpath.HAlignLeft && p.VAlign == "") {
2✔
164
                                        interest = InterestingLow
1✔
165
                                        thumbnail = true
1✔
166
                                } else if (p.VAlign == imagorpath.VAlignBottom && p.HAlign == "") ||
2✔
167
                                        (p.HAlign == imagorpath.HAlignRight && p.VAlign == "") {
2✔
168
                                        interest = InterestingHigh
1✔
169
                                        thumbnail = true
1✔
170
                                } else if (p.VAlign == "" || p.VAlign == "middle") &&
2✔
171
                                        (p.HAlign == "" || p.HAlign == "center") {
2✔
172
                                        interest = InterestingCentre
1✔
173
                                        thumbnail = true
1✔
174
                                }
1✔
175
                                if thumbnail {
2✔
176
                                        if img, err = v.NewThumbnail(
1✔
177
                                                ctx, blob, p.Width, p.Height,
1✔
178
                                                interest, SizeBoth, maxN, page,
1✔
179
                                        ); err != nil {
1✔
180
                                                return nil, err
×
181
                                        }
×
182
                                }
183
                        } else if p.Width > 0 && p.Height == 0 {
2✔
184
                                if img, err = v.NewThumbnail(
1✔
185
                                        ctx, blob, p.Width, v.MaxHeight,
1✔
186
                                        InterestingNone, SizeBoth, maxN, page,
1✔
187
                                ); err != nil {
1✔
188
                                        return nil, err
×
189
                                }
×
190
                                thumbnail = true
1✔
191
                        } else if p.Height > 0 && p.Width == 0 {
2✔
192
                                if img, err = v.NewThumbnail(
1✔
193
                                        ctx, blob, v.MaxWidth, p.Height,
1✔
194
                                        InterestingNone, SizeBoth, maxN, page,
1✔
195
                                ); err != nil {
1✔
196
                                        return nil, err
×
197
                                }
×
198
                                thumbnail = true
1✔
199
                        }
200
                }
201
        }
202
        if !thumbnail {
2✔
203
                if thumbnailNotSupported {
2✔
204
                        if img, err = v.NewImage(ctx, blob, maxN, page); err != nil {
1✔
205
                                return nil, err
×
206
                        }
×
207
                } else {
1✔
208
                        if img, err = v.NewThumbnail(
1✔
209
                                ctx, blob, v.MaxWidth, v.MaxHeight,
1✔
210
                                InterestingNone, SizeDown, maxN, page,
1✔
211
                        ); err != nil {
2✔
212
                                return nil, err
1✔
213
                        }
1✔
214
                }
215
        }
216
        // this should be called BEFORE vipscontext.contextDone
217
        defer img.Close()
1✔
218

1✔
219
        if orient > 0 {
2✔
220
                // orient rotate before resize
1✔
221
                if err = img.Rotate(getAngle(orient)); err != nil {
1✔
222
                        return nil, err
×
223
                }
×
224
        }
225
        var (
1✔
226
                quality    int
1✔
227
                origWidth  = float64(img.Width())
1✔
228
                origHeight = float64(img.PageHeight())
1✔
229
        )
1✔
230
        if format == ImageTypeUnknown {
2✔
231
                if blob.BlobType() == imagor.BlobTypeAVIF {
2✔
232
                        // meta loader determined as heif
1✔
233
                        format = ImageTypeAVIF
1✔
234
                } else {
2✔
235
                        format = img.Format()
1✔
236
                }
1✔
237
        }
238
        if v.Debug {
2✔
239
                v.Logger.Debug("image",
1✔
240
                        zap.Int("width", img.Width()),
1✔
241
                        zap.Int("height", img.Height()),
1✔
242
                        zap.Int("page_height", img.PageHeight()))
1✔
243
        }
1✔
244
        for _, p := range p.Filters {
2✔
245
                if v.disableFilters[p.Name] {
2✔
246
                        continue
1✔
247
                }
248
                switch p.Name {
1✔
249
                case "quality":
1✔
250
                        quality, _ = strconv.Atoi(p.Args)
1✔
251
                        break
1✔
252
                case "autojpg":
1✔
253
                        format = ImageTypeJPEG
1✔
254
                        break
1✔
255
                case "focal":
1✔
256
                        args := strings.FieldsFunc(p.Args, argSplit)
1✔
257
                        switch len(args) {
1✔
258
                        case 4:
1✔
259
                                f := focal{}
1✔
260
                                f.Left, _ = strconv.ParseFloat(args[0], 64)
1✔
261
                                f.Top, _ = strconv.ParseFloat(args[1], 64)
1✔
262
                                f.Right, _ = strconv.ParseFloat(args[2], 64)
1✔
263
                                f.Bottom, _ = strconv.ParseFloat(args[3], 64)
1✔
264
                                if f.Left < 1 && f.Top < 1 && f.Right <= 1 && f.Bottom <= 1 {
2✔
265
                                        f.Left *= origWidth
1✔
266
                                        f.Right *= origWidth
1✔
267
                                        f.Top *= origHeight
1✔
268
                                        f.Bottom *= origHeight
1✔
269
                                }
1✔
270
                                if f.Right > f.Left && f.Bottom > f.Top {
2✔
271
                                        focalRects = append(focalRects, f)
1✔
272
                                }
1✔
273
                        case 2:
1✔
274
                                f := focal{}
1✔
275
                                f.Left, _ = strconv.ParseFloat(args[0], 64)
1✔
276
                                f.Top, _ = strconv.ParseFloat(args[1], 64)
1✔
277
                                if f.Left < 1 && f.Top < 1 {
2✔
278
                                        f.Left *= origWidth
1✔
279
                                        f.Top *= origHeight
1✔
280
                                }
1✔
281
                                f.Right = f.Left + 1
1✔
282
                                f.Bottom = f.Top + 1
1✔
283
                                focalRects = append(focalRects, f)
1✔
284
                        }
285
                        break
1✔
286
                }
287
        }
288
        if err := v.process(ctx, img, p, load, thumbnail, stretch, upscale, focalRects); err != nil {
2✔
289
                return nil, WrapErr(err)
1✔
290
        }
1✔
291
        if p.Meta {
2✔
292
                // metadata without export
1✔
293
                return imagor.NewBlobFromJsonMarshal(metadata(img, format, stripExif)), nil
1✔
294
        }
1✔
295
        format = supportedSaveFormat(format) // convert to supported export format
1✔
296
        for {
2✔
297
                buf, err := v.export(img, format, quality)
1✔
298
                if err != nil {
1✔
299
                        return nil, WrapErr(err)
×
300
                }
×
301
                if maxBytes > 0 && (quality > 10 || quality == 0) && format != ImageTypePNG {
2✔
302
                        ln := len(buf)
1✔
303
                        if v.Debug {
2✔
304
                                v.Logger.Debug("max_bytes",
1✔
305
                                        zap.Int("bytes", ln),
1✔
306
                                        zap.Int("quality", quality),
1✔
307
                                )
1✔
308
                        }
1✔
309
                        if ln > maxBytes {
2✔
310
                                if quality == 0 {
2✔
311
                                        quality = 80
1✔
312
                                }
1✔
313
                                delta := float64(ln) / float64(maxBytes)
1✔
314
                                switch {
1✔
315
                                case delta > 3:
1✔
316
                                        quality = quality * 25 / 100
1✔
317
                                case delta > 1.5:
1✔
318
                                        quality = quality * 50 / 100
1✔
319
                                default:
1✔
320
                                        quality = quality * 75 / 100
1✔
321
                                }
322
                                if err := ctx.Err(); err != nil {
1✔
323
                                        return nil, WrapErr(err)
×
324
                                }
×
325
                                continue
1✔
326
                        }
327
                }
328
                blob := imagor.NewBlobFromBytes(buf)
1✔
329
                if typ, ok := ImageMimeTypes[format]; ok {
2✔
330
                        blob.SetContentType(typ)
1✔
331
                }
1✔
332
                return blob, nil
1✔
333
        }
334
}
335

336
func (v *Processor) process(
337
        ctx context.Context, img *Image, p imagorpath.Params, load imagor.LoadFunc, thumbnail, stretch, upscale bool, focalRects []focal,
338
) error {
1✔
339
        var (
1✔
340
                origWidth  = float64(img.Width())
1✔
341
                origHeight = float64(img.PageHeight())
1✔
342
                cropLeft,
1✔
343
                cropTop,
1✔
344
                cropRight,
1✔
345
                cropBottom float64
1✔
346
        )
1✔
347
        if p.CropRight > 0 || p.CropLeft > 0 || p.CropBottom > 0 || p.CropTop > 0 {
2✔
348
                // percentage
1✔
349
                cropLeft = math.Max(p.CropLeft, 0)
1✔
350
                cropTop = math.Max(p.CropTop, 0)
1✔
351
                cropRight = p.CropRight
1✔
352
                cropBottom = p.CropBottom
1✔
353
                if p.CropLeft < 1 && p.CropTop < 1 && p.CropRight <= 1 && p.CropBottom <= 1 {
2✔
354
                        cropLeft = math.Round(cropLeft * origWidth)
1✔
355
                        cropTop = math.Round(cropTop * origHeight)
1✔
356
                        cropRight = math.Round(cropRight * origWidth)
1✔
357
                        cropBottom = math.Round(cropBottom * origHeight)
1✔
358
                }
1✔
359
                if cropRight == 0 {
2✔
360
                        cropRight = origWidth - 1
1✔
361
                }
1✔
362
                if cropBottom == 0 {
2✔
363
                        cropBottom = origHeight - 1
1✔
364
                }
1✔
365
                cropRight = math.Min(cropRight, origWidth-1)
1✔
366
                cropBottom = math.Min(cropBottom, origHeight-1)
1✔
367
        }
368
        if p.Trim {
2✔
369
                if l, t, w, h, err := findTrim(ctx, img, p.TrimBy, p.TrimTolerance); err == nil {
2✔
370
                        cropLeft = math.Max(cropLeft, float64(l))
1✔
371
                        cropTop = math.Max(cropTop, float64(t))
1✔
372
                        if cropRight > 0 {
2✔
373
                                cropRight = math.Min(cropRight, float64(l+w))
1✔
374
                        } else {
2✔
375
                                cropRight = float64(l + w)
1✔
376
                        }
1✔
377
                        if cropBottom > 0 {
2✔
378
                                cropBottom = math.Min(cropBottom, float64(t+h))
1✔
379
                        } else {
2✔
380
                                cropBottom = float64(t + h)
1✔
381
                        }
1✔
382
                }
383
        }
384
        if cropRight > cropLeft && cropBottom > cropTop {
2✔
385
                if err := img.ExtractArea(
1✔
386
                        int(cropLeft), int(cropTop), int(cropRight-cropLeft), int(cropBottom-cropTop),
1✔
387
                ); err != nil {
1✔
388
                        return err
×
389
                }
×
390
        }
391
        var (
1✔
392
                w = p.Width
1✔
393
                h = p.Height
1✔
394
        )
1✔
395
        if w == 0 && h == 0 {
2✔
396
                w = img.Width()
1✔
397
                h = img.PageHeight()
1✔
398
        } else if w == 0 {
3✔
399
                w = img.Width() * h / img.PageHeight()
1✔
400
                if !upscale && w > img.Width() {
1✔
401
                        w = img.Width()
×
402
                }
×
403
        } else if h == 0 {
2✔
404
                h = img.PageHeight() * w / img.Width()
1✔
405
                if !upscale && h > img.PageHeight() {
1✔
406
                        h = img.PageHeight()
×
407
                }
×
408
        }
409
        if !thumbnail {
2✔
410
                if p.FitIn {
2✔
411
                        if upscale || w < img.Width() || h < img.PageHeight() {
2✔
412
                                if err := img.Thumbnail(w, h, InterestingNone); err != nil {
1✔
413
                                        return err
×
414
                                }
×
415
                        }
416
                } else if stretch {
2✔
417
                        if upscale || (w < img.Width() && h < img.PageHeight()) {
2✔
418
                                if err := img.ThumbnailWithSize(
1✔
419
                                        w, h, InterestingNone, SizeForce,
1✔
420
                                ); err != nil {
1✔
421
                                        return err
×
422
                                }
×
423
                        }
424
                } else if upscale || w < img.Width() || h < img.PageHeight() {
2✔
425
                        interest := InterestingCentre
1✔
426
                        if p.Smart {
1✔
427
                                interest = InterestingAttention
×
428
                        } else if float64(w)/float64(h) > float64(img.Width())/float64(img.PageHeight()) {
2✔
429
                                if p.VAlign == imagorpath.VAlignTop {
2✔
430
                                        interest = InterestingLow
1✔
431
                                } else if p.VAlign == imagorpath.VAlignBottom {
3✔
432
                                        interest = InterestingHigh
1✔
433
                                }
1✔
434
                        } else {
1✔
435
                                if p.HAlign == imagorpath.HAlignLeft {
2✔
436
                                        interest = InterestingLow
1✔
437
                                } else if p.HAlign == imagorpath.HAlignRight {
3✔
438
                                        interest = InterestingHigh
1✔
439
                                }
1✔
440
                        }
441
                        if len(focalRects) > 0 {
2✔
442
                                focalX, focalY := parseFocalPoint(focalRects...)
1✔
443
                                if err := v.FocalThumbnail(
1✔
444
                                        img, w, h,
1✔
445
                                        (focalX-cropLeft)/float64(img.Width()),
1✔
446
                                        (focalY-cropTop)/float64(img.PageHeight()),
1✔
447
                                ); err != nil {
1✔
448
                                        return err
×
449
                                }
×
450
                        } else {
1✔
451
                                if err := v.Thumbnail(img, w, h, interest, SizeBoth); err != nil {
1✔
452
                                        return err
×
453
                                }
×
454
                        }
455
                        if _, err := v.CheckResolution(img, nil); err != nil {
2✔
456
                                return err
1✔
457
                        }
1✔
458
                }
459
        }
460
        if p.HFlip {
2✔
461
                if err := img.Flip(DirectionHorizontal); err != nil {
1✔
462
                        return err
×
463
                }
×
464
        }
465
        if p.VFlip {
2✔
466
                if err := img.Flip(DirectionVertical); err != nil {
1✔
467
                        return err
×
468
                }
×
469
        }
470
        for i, filter := range p.Filters {
2✔
471
                if err := ctx.Err(); err != nil {
1✔
472
                        return err
×
473
                }
×
474
                if v.disableFilters[filter.Name] {
2✔
475
                        continue
1✔
476
                }
477
                if v.MaxFilterOps > 0 && i >= v.MaxFilterOps {
2✔
478
                        if v.Debug {
2✔
479
                                v.Logger.Debug("max-filter-ops-exceeded",
1✔
480
                                        zap.String("name", filter.Name), zap.String("args", filter.Args))
1✔
481
                        }
1✔
482
                        break
1✔
483
                }
484
                start := time.Now()
1✔
485
                var args []string
1✔
486
                if filter.Args != "" {
2✔
487
                        args = strings.Split(filter.Args, ",")
1✔
488
                }
1✔
489
                if fn := v.Filters[filter.Name]; fn != nil {
2✔
490
                        if err := fn(ctx, img, load, args...); err != nil {
1✔
491
                                return err
×
492
                        }
×
493
                } else if filter.Name == "fill" {
2✔
494
                        if err := v.fill(ctx, img, w, h,
1✔
495
                                p.PaddingLeft, p.PaddingTop, p.PaddingRight, p.PaddingBottom,
1✔
496
                                filter.Args); err != nil {
1✔
497
                                return err
×
498
                        }
×
499
                }
500
                if v.Debug {
2✔
501
                        v.Logger.Debug("filter",
1✔
502
                                zap.String("name", filter.Name), zap.String("args", filter.Args),
1✔
503
                                zap.Duration("took", time.Since(start)))
1✔
504
                }
1✔
505
        }
506
        return nil
1✔
507
}
508

509
// Metadata image attributes
510
type Metadata struct {
511
        Format      string         `json:"format"`
512
        ContentType string         `json:"content_type"`
513
        Width       int            `json:"width"`
514
        Height      int            `json:"height"`
515
        Orientation int            `json:"orientation"`
516
        Pages       int            `json:"pages"`
517
        Bands       int            `json:"bands"`
518
        Exif        map[string]any `json:"exif"`
519
}
520

521
func metadata(img *Image, format ImageType, stripExif bool) *Metadata {
1✔
522
        pages := 1
1✔
523
        if IsAnimationSupported(format) {
2✔
524
                pages = img.Height() / img.PageHeight()
1✔
525
        }
1✔
526
        exif := map[string]any{}
1✔
527
        if !stripExif {
2✔
528
                exif = img.Exif()
1✔
529
        }
1✔
530
        return &Metadata{
1✔
531
                Format:      ImageTypes[format],
1✔
532
                ContentType: ImageMimeTypes[format],
1✔
533
                Width:       img.Width(),
1✔
534
                Height:      img.PageHeight(),
1✔
535
                Pages:       pages,
1✔
536
                Bands:       img.Bands(),
1✔
537
                Orientation: img.Orientation(),
1✔
538
                Exif:        exif,
1✔
539
        }
1✔
540
}
541

542
func supportedSaveFormat(format ImageType) ImageType {
1✔
543
        switch format {
1✔
544
        case ImageTypePNG, ImageTypeWEBP, ImageTypeTIFF, ImageTypeGIF, ImageTypeAVIF, ImageTypeHEIF, ImageTypeJP2K:
1✔
545
                if IsSaveSupported(format) {
2✔
546
                        return format
1✔
547
                }
1✔
548
                if format == ImageTypeAVIF && IsSaveSupported(ImageTypeHEIF) {
2✔
549
                        return ImageTypeAVIF
1✔
550
                }
1✔
551
        }
552
        return ImageTypeJPEG
1✔
553
}
554

555
func (v *Processor) export(image *Image, format ImageType, quality int) ([]byte, error) {
1✔
556
        switch format {
1✔
557
        case ImageTypePNG:
1✔
558
                opts := NewPngExportParams()
1✔
559
                return image.ExportPng(opts)
1✔
560
        case ImageTypeWEBP:
1✔
561
                opts := NewWebpExportParams()
1✔
562
                if quality > 0 {
2✔
563
                        opts.Quality = quality
1✔
564
                }
1✔
565
                return image.ExportWebp(opts)
1✔
566
        case ImageTypeTIFF:
1✔
567
                opts := NewTiffExportParams()
1✔
568
                if quality > 0 {
2✔
569
                        opts.Quality = quality
1✔
570
                }
1✔
571
                return image.ExportTiff(opts)
1✔
572
        case ImageTypeGIF:
1✔
573
                opts := NewGifExportParams()
1✔
574
                if quality > 0 {
2✔
575
                        opts.Quality = quality
1✔
576
                }
1✔
577
                return image.ExportGIF(opts)
1✔
578
        case ImageTypeAVIF:
1✔
579
                opts := NewAvifExportParams()
1✔
580
                if quality > 0 {
2✔
581
                        opts.Quality = quality
1✔
582
                }
1✔
583
                return image.ExportAvif(opts)
1✔
584
        case ImageTypeHEIF:
1✔
585
                opts := NewHeifExportParams()
1✔
586
                if quality > 0 {
2✔
587
                        opts.Quality = quality
1✔
588
                }
1✔
589
                return image.ExportHeif(opts)
1✔
590
        case ImageTypeJP2K:
×
591
                opts := NewJp2kExportParams()
×
592
                if quality > 0 {
×
593
                        opts.Quality = quality
×
594
                }
×
595
                return image.ExportJp2k(opts)
×
596
        default:
1✔
597
                opts := NewJpegExportParams()
1✔
598
                if v.MozJPEG {
1✔
599
                        opts.Quality = 75
×
600
                        opts.StripMetadata = true
×
601
                        opts.OptimizeCoding = true
×
602
                        opts.Interlace = true
×
603
                        opts.OptimizeScans = true
×
604
                        opts.TrellisQuant = true
×
605
                        opts.QuantTable = 3
×
606
                }
×
607
                if quality > 0 {
2✔
608
                        opts.Quality = quality
1✔
609
                }
1✔
610
                return image.ExportJpeg(opts)
1✔
611
        }
612
}
613

614
func argSplit(r rune) bool {
1✔
615
        return r == 'x' || r == ',' || r == ':'
1✔
616
}
1✔
617

618
type focal struct {
619
        Left   float64
620
        Right  float64
621
        Top    float64
622
        Bottom float64
623
}
624

625
func parseFocalPoint(focalRects ...focal) (focalX, focalY float64) {
1✔
626
        var sumWeight float64
1✔
627
        for _, f := range focalRects {
2✔
628
                sumWeight += (f.Right - f.Left) * (f.Bottom - f.Top)
1✔
629
        }
1✔
630
        for _, f := range focalRects {
2✔
631
                r := (f.Right - f.Left) * (f.Bottom - f.Top) / sumWeight
1✔
632
                focalX += (f.Left + f.Right) / 2 * r
1✔
633
                focalY += (f.Top + f.Bottom) / 2 * r
1✔
634
        }
1✔
635
        return
1✔
636
}
637

638
func findTrim(
639
        _ context.Context, img *Image, pos string, tolerance int,
640
) (l, t, w, h int, err error) {
1✔
641
        if isAnimated(img) {
2✔
642
                // skip animation support
1✔
643
                return
1✔
644
        }
1✔
645
        var x, y int
1✔
646
        if pos == imagorpath.TrimByBottomRight {
2✔
647
                x = img.Width() - 1
1✔
648
                y = img.PageHeight() - 1
1✔
649
        }
1✔
650
        if tolerance == 0 {
2✔
651
                tolerance = 1
1✔
652
        }
1✔
653
        l, t, w, h, err = img.FindTrim(float64(tolerance), x, y)
1✔
654
        return
1✔
655
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc