jr-paperclip 8.0.0.beta.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +7 -27
- data/.github/workflows/tests.yml +1 -1
- data/NEWS +1 -1
- data/README.md +33 -23
- data/VIPS_MIGRATION_GUIDE.md +8 -2
- data/lib/paperclip/geometry_detector_factory.rb +1 -1
- data/lib/paperclip/processor.rb +1 -1
- data/lib/paperclip/thumbnail.rb +29 -8
- data/lib/paperclip/version.rb +1 -1
- data/spec/paperclip/migration_guide_example_spec.rb +44 -0
- data/spec/paperclip/thumbnail_custom_options_spec.rb +60 -8
- data/spec/paperclip/thumbnail_spec.rb +57 -56
- metadata +3 -2
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a0a10e43e470a988afe5f30de3f2144f18bca45154654abdbf09b57817bee4b
|
|
4
|
+
data.tar.gz: 59df71bd9e4c0577f252165bf5b1d55202ffe8d363f0eae235d30db9c90b14fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ac11878b1797097dd0c6a369f39f03bc52153f2c9db2c2991d533b171a9f36fbec873b5859b55b378dbcb541d2d477f40de92d99456c7c3f5fb86c1d53eae5b
|
|
7
|
+
data.tar.gz: 8ec53d673eee2ddbfab2008ab3a3977d659dda2df53f9e7c34d9cef307b1889d53ead7ae3a3c4d4cebdc33452c9ec3e9a5c7abfefb36f46ca5e5f68ecf0bc644
|
|
@@ -7,32 +7,12 @@ assignees: ''
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Ruby version:
|
|
11
|
+
Rails version:
|
|
12
|
+
Paperclip version:
|
|
13
|
+
ImageMagick version:
|
|
14
|
+
libvips version:
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
Steps to reproduce the behavior:
|
|
15
|
-
1. Go to '...'
|
|
16
|
-
2. Click on '....'
|
|
17
|
-
3. Scroll down to '....'
|
|
18
|
-
4. See error
|
|
16
|
+
Please include your initializer, Paperclip related options, and any error message with the full backtrace.
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
A clear and concise description of what you expected to happen.
|
|
22
|
-
|
|
23
|
-
**Screenshots**
|
|
24
|
-
If applicable, add screenshots to help explain your problem.
|
|
25
|
-
|
|
26
|
-
**Desktop (please complete the following information):**
|
|
27
|
-
- OS: [e.g. iOS]
|
|
28
|
-
- Browser [e.g. chrome, safari]
|
|
29
|
-
- Version [e.g. 22]
|
|
30
|
-
|
|
31
|
-
**Smartphone (please complete the following information):**
|
|
32
|
-
- Device: [e.g. iPhone6]
|
|
33
|
-
- OS: [e.g. iOS8.1]
|
|
34
|
-
- Browser [e.g. stock browser, safari]
|
|
35
|
-
- Version [e.g. 22]
|
|
36
|
-
|
|
37
|
-
**Additional context**
|
|
38
|
-
Add any other context about the problem here.
|
|
18
|
+
If you are using an old version, have you checked the changelogs to see if your issue has been fixed in a later version?
|
data/.github/workflows/tests.yml
CHANGED
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
- name: Install dependencies
|
|
47
47
|
run: |
|
|
48
48
|
sudo apt-get update
|
|
49
|
-
sudo apt-get install -y ghostscript imagemagick libvips
|
|
49
|
+
sudo apt-get install -y ghostscript imagemagick libvips libvips-tools
|
|
50
50
|
sudo rm -f /etc/ImageMagick-6/policy.xml
|
|
51
51
|
|
|
52
52
|
- name: Run tests
|
data/NEWS
CHANGED
data/README.md
CHANGED
|
@@ -14,20 +14,17 @@ Legacy versions were dropped to reduce maintenance overhead and keep up with upd
|
|
|
14
14
|
|
|
15
15
|
Additional maintainers are very welcome.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
We plan to support and maintain paperclip, as well as clean it up.
|
|
17
|
+
## Version 8.0.0 brings major new features and improvements:
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
* Support for libvips backend alongside ImageMagick via the image_processing gem
|
|
20
|
+
* ImageMagick 7 support (uses `magick` command when available)
|
|
21
|
+
* Added `ALLOWED_IMAGEMAGICK_OPTIONS` security whitelist (GHSA-r4mg-4433-c7g3)
|
|
22
|
+
* Cross-platform convert_options support (-strip, -quality, -rotate, etc.)
|
|
24
23
|
|
|
25
|
-
#
|
|
24
|
+
Please check the [Image Processor](#image-processor) and [Post Processing](#post-processing) sections for more details and the [VIPS Migration Guide](https://github.com/jukra/jr-paperclip/blob/master/VIPS_MIGRATION_GUIDE.md) for migration instructions.
|
|
26
25
|
|
|
27
26
|
## Documentation valid for `master` branch
|
|
28
27
|
|
|
29
|
-
---
|
|
30
|
-
|
|
31
28
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
32
29
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
33
30
|
|
|
@@ -100,7 +97,7 @@ Requirements
|
|
|
100
97
|
|
|
101
98
|
### Ruby and Rails
|
|
102
99
|
|
|
103
|
-
Paperclip now requires Ruby version **>=
|
|
100
|
+
Paperclip now requires Ruby version **>= 3.0** and Rails version **>= 7.0**
|
|
104
101
|
(only if you're going to use Paperclip with Ruby on Rails).
|
|
105
102
|
|
|
106
103
|
### Image Processor
|
|
@@ -149,6 +146,9 @@ brew install vips
|
|
|
149
146
|
|
|
150
147
|
# Ubuntu/Debian
|
|
151
148
|
sudo apt install libvips
|
|
149
|
+
|
|
150
|
+
# If you also need the vips and vipsheader command line tools (for example custom processors)
|
|
151
|
+
sudo apt install libvips-tools
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
Then configure Paperclip to use it as the default backend in `config/initializers/paperclip.rb` (or in your environment configuration):
|
|
@@ -211,7 +211,7 @@ Paperclip is distributed as a gem, which is how it should be used in your app.
|
|
|
211
211
|
Include the gem in your Gemfile:
|
|
212
212
|
|
|
213
213
|
```ruby
|
|
214
|
-
gem "jr-paperclip", "~>
|
|
214
|
+
gem "jr-paperclip", "~> 8.0"
|
|
215
215
|
```
|
|
216
216
|
|
|
217
217
|
Or, if you want to get the latest, you can get master from the main paperclip repository:
|
|
@@ -723,6 +723,7 @@ has_attached_file :avatar, styles: { thumb: ["32x32#", :png] }
|
|
|
723
723
|
This will convert the "thumb" style to a 32x32 square in PNG format, regardless
|
|
724
724
|
of what was uploaded. If the format is not specified, it is kept the same (e.g.
|
|
725
725
|
JPGs will remain JPGs). `Paperclip::Thumbnail` uses the [image_processing](https://github.com/janko/image_processing) gem to process images. This allows support for both ImageMagick (via MiniMagick) and libvips backends.
|
|
726
|
+
|
|
726
727
|
[ImageMagick's geometry documentation](http://www.imagemagick.org/script/command-line-processing.php#geometry)
|
|
727
728
|
has more information on the accepted style formats, which are generally supported by both backends in Paperclip.
|
|
728
729
|
|
|
@@ -734,12 +735,14 @@ has_attached_file :image, styles: { regular: ['800x800>', :png]},
|
|
|
734
735
|
convert_options: { regular: "-posterize 3"}
|
|
735
736
|
```
|
|
736
737
|
|
|
737
|
-
|
|
738
|
+
Paperclip delegates to the `image_processing` gem, but **whitelists supported options** for security. For ImageMagick, most standard options are supported. For libvips, a curated set of common options is available (see table below) and `Paperclip::Thumbnail` class for details. Unsupported options are logged as warnings and skipped.
|
|
739
|
+
|
|
740
|
+
For full backend capabilities, refer to the image_processing documentation:
|
|
738
741
|
|
|
739
742
|
* **ImageMagick:** [ImageProcessing::MiniMagick documentation](https://github.com/janko/image_processing/blob/master/doc/minimagick.md)
|
|
740
743
|
* **libvips:** [ImageProcessing::Vips documentation](https://github.com/janko/image_processing/blob/master/doc/vips.md)
|
|
741
744
|
|
|
742
|
-
Common options like `-strip`, `-quality`, `-rotate`, `-blur` are supported on both backends
|
|
745
|
+
Common options like `-strip`, `-quality`, `-rotate`, `-blur` are supported on both backends. Backend-specific options (like `-density` for ImageMagick) may not be available on libvips.
|
|
743
746
|
|
|
744
747
|
### Cross-Platform Convert Options
|
|
745
748
|
|
|
@@ -757,6 +760,7 @@ Many common `convert_options` now work with **both** ImageMagick and libvips bac
|
|
|
757
760
|
| `-colorspace X` | Color space (Gray, sRGB, CMYK) | `convert_options: "-colorspace Gray"` |
|
|
758
761
|
| `-negate` | Invert colors | `convert_options: "-negate"` |
|
|
759
762
|
| `-flatten` | Flatten transparency | `convert_options: "-flatten"` |
|
|
763
|
+
| `-gamma N` | Adjust image gamma | `convert_options: "-gamma 1.5"` |
|
|
760
764
|
| `-auto-orient` | Auto-rotate via EXIF | `convert_options: "-auto-orient"` |
|
|
761
765
|
| `-interlace X` | Progressive output | `convert_options: "-interlace Plane"` |
|
|
762
766
|
|
|
@@ -770,14 +774,6 @@ has_attached_file :avatar,
|
|
|
770
774
|
|
|
771
775
|
**Note:** Some options only work with ImageMagick (e.g., `-density`, `-depth`, `-gravity`, `-crop`, `-trim`). When using the vips backend, these will be skipped with a warning logged.
|
|
772
776
|
|
|
773
|
-
ImageMagick supports a number of environment variables for controlling its resource limits. For example, you can enforce memory or execution time limits by setting the following variables in your application's process environment:
|
|
774
|
-
|
|
775
|
-
* `MAGICK_MEMORY_LIMIT=128MiB`
|
|
776
|
-
* `MAGICK_MAP_LIMIT=64MiB`
|
|
777
|
-
* `MAGICK_TIME_LIMIT=30`
|
|
778
|
-
|
|
779
|
-
For a full list of variables and description, see [ImageMagick's resources documentation](http://www.imagemagick.org/script/resources.php).
|
|
780
|
-
|
|
781
777
|
---
|
|
782
778
|
|
|
783
779
|
Image Processing Backends
|
|
@@ -787,16 +783,24 @@ jr-paperclip supports two image processing backends:
|
|
|
787
783
|
|
|
788
784
|
### ImageMagick (Default)
|
|
789
785
|
|
|
790
|
-
The traditional backend
|
|
786
|
+
The traditional backend. Paperclip uses the `image_processing` gem (via `mini_magick`) to generate shell commands for ImageMagick.
|
|
791
787
|
|
|
792
788
|
```ruby
|
|
793
789
|
has_attached_file :avatar,
|
|
794
790
|
styles: { thumb: "100x100#" }
|
|
795
791
|
```
|
|
796
792
|
|
|
793
|
+
ImageMagick supports a number of environment variables for controlling its resource limits. For example, you can enforce memory or execution time limits by setting the following variables in your application's process environment:
|
|
794
|
+
|
|
795
|
+
* `MAGICK_MEMORY_LIMIT=128MiB`
|
|
796
|
+
* `MAGICK_MAP_LIMIT=64MiB`
|
|
797
|
+
* `MAGICK_TIME_LIMIT=30`
|
|
798
|
+
|
|
799
|
+
For a full list of variables and description, see [ImageMagick's resources documentation](http://www.imagemagick.org/script/resources.php).
|
|
800
|
+
|
|
797
801
|
### libvips (Recommended for Performance)
|
|
798
802
|
|
|
799
|
-
libvips is significantly faster and uses less memory than ImageMagick.
|
|
803
|
+
libvips is significantly faster and uses less memory than ImageMagick. Paperclip uses the `image_processing` gem (via `ruby-vips`) to interface with libvips.
|
|
800
804
|
|
|
801
805
|
**Usage:**
|
|
802
806
|
|
|
@@ -820,6 +824,12 @@ has_attached_file :document,
|
|
|
820
824
|
}
|
|
821
825
|
```
|
|
822
826
|
|
|
827
|
+
libvips keeps a cache of recently executed operations. You can disable this for a drop in memory use:
|
|
828
|
+
```ruby
|
|
829
|
+
Vips::cache_set_max 0
|
|
830
|
+
```
|
|
831
|
+
**Note:** Typically this is not needed as libvips is already memory efficient. Disabling the cache is mostly useful in extremely memory-constrained environments (like AWS Lambda) where every MB counts. This [issue](https://github.com/libvips/ruby-vips/issues/225) has more information on memory usage.
|
|
832
|
+
|
|
823
833
|
---
|
|
824
834
|
|
|
825
835
|
Custom Attachment Processors
|
data/VIPS_MIGRATION_GUIDE.md
CHANGED
|
@@ -14,6 +14,9 @@ brew install vips
|
|
|
14
14
|
|
|
15
15
|
# Ubuntu/Debian
|
|
16
16
|
sudo apt install libvips
|
|
17
|
+
|
|
18
|
+
# If you also need the vips and vipsheader command line tools (for example custom processors)
|
|
19
|
+
sudo apt install libvips-tools
|
|
17
20
|
```
|
|
18
21
|
|
|
19
22
|
## Step 1: Update your Gemfile
|
|
@@ -23,7 +26,7 @@ sudo apt install libvips
|
|
|
23
26
|
Ensure you are using the latest version of the gem:
|
|
24
27
|
|
|
25
28
|
```ruby
|
|
26
|
-
gem "jr-paperclip", "~> 8.0
|
|
29
|
+
gem "jr-paperclip", "~> 8.0"
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
## Step 2: Gradual Migration (Per-Attachment)
|
|
@@ -73,7 +76,7 @@ has_attached_file :avatar,
|
|
|
73
76
|
|
|
74
77
|
### ImageMagick-Only Options
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
For example the following options only work with ImageMagick. When used with the vips backend, they will be skipped and a warning will be logged:
|
|
77
80
|
|
|
78
81
|
`-density`, `-depth`, `-gravity`, `-crop`, `-extent`, `-alpha`, `-background`, `-type`, `-posterize`, `-dither`, `-colors`, `-channel`, `-transpose`, `-transverse`, `-normalize`, `-equalize`, `-trim`, `-monochrome`
|
|
79
82
|
|
|
@@ -89,6 +92,9 @@ However, if you want to migrate a custom processor to libvips, you can now use t
|
|
|
89
92
|
module Paperclip
|
|
90
93
|
class MyVipsProcessor < Processor
|
|
91
94
|
def make
|
|
95
|
+
basename = File.basename(file.path, File.extname(file.path))
|
|
96
|
+
dst = Paperclip::TempfileFactory.new.generate("#{basename}.png")
|
|
97
|
+
|
|
92
98
|
# Use the new vips helper instead of convert
|
|
93
99
|
vips("thumbnail :src :dst 100",
|
|
94
100
|
src: File.expand_path(file.path),
|
|
@@ -48,7 +48,7 @@ module Paperclip
|
|
|
48
48
|
begin
|
|
49
49
|
require "vips"
|
|
50
50
|
rescue LoadError => e
|
|
51
|
-
raise Errors::CommandNotFoundError.new("Could not load ruby-vips. Please install libvips
|
|
51
|
+
raise Errors::CommandNotFoundError.new("Could not load ruby-vips. Please install libvips.")
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
begin
|
data/lib/paperclip/processor.rb
CHANGED
|
@@ -80,7 +80,7 @@ module Paperclip
|
|
|
80
80
|
begin
|
|
81
81
|
require "vips"
|
|
82
82
|
rescue LoadError
|
|
83
|
-
raise Errors::CommandNotFoundError.new("Could not load ruby-vips. Please install libvips
|
|
83
|
+
raise Errors::CommandNotFoundError.new("Could not load ruby-vips. Please install libvips.")
|
|
84
84
|
end
|
|
85
85
|
Vips::Image.new_from_file(file_path, **options)
|
|
86
86
|
end
|
data/lib/paperclip/thumbnail.rb
CHANGED
|
@@ -476,30 +476,51 @@ module Paperclip
|
|
|
476
476
|
pipeline.custom(&:flipver)
|
|
477
477
|
when "flop"
|
|
478
478
|
pipeline.custom(&:fliphor)
|
|
479
|
-
when "blur"
|
|
479
|
+
when "blur", "gaussian_blur", "gaussblur"
|
|
480
480
|
# Vips uses gaussblur with sigma parameter
|
|
481
481
|
# ImageMagick blur is "radiusxsigma", extract sigma
|
|
482
482
|
sigma = extract_blur_sigma(value)
|
|
483
|
-
sigma ? pipeline.
|
|
484
|
-
when "gaussian_blur"
|
|
485
|
-
sigma = extract_blur_sigma(value)
|
|
486
|
-
sigma ? pipeline.custom { |img| img.gaussblur(sigma) } : pipeline
|
|
483
|
+
sigma ? pipeline.gaussblur(sigma) : pipeline
|
|
487
484
|
when "sharpen"
|
|
488
485
|
# Vips sharpen has different parameters than ImageMagick
|
|
489
486
|
# Use sensible defaults for unsharp masking
|
|
490
487
|
pipeline.custom(&:sharpen)
|
|
491
|
-
when "colorspace"
|
|
488
|
+
when "colorspace", "colourspace"
|
|
492
489
|
# Vips uses British spelling "colourspace"
|
|
493
|
-
value ? pipeline.
|
|
490
|
+
value ? pipeline.colourspace(vips_colorspace(value)) : pipeline
|
|
491
|
+
when "gamma"
|
|
492
|
+
# Vips gamma uses keyword argument
|
|
493
|
+
value ? pipeline.custom { |img| img.gamma(exponent: value.to_f) } : pipeline
|
|
494
494
|
when "flatten"
|
|
495
495
|
pipeline.custom { |img| img.flatten(background: [255, 255, 255]) }
|
|
496
496
|
when "negate", "invert"
|
|
497
497
|
pipeline.custom(&:invert)
|
|
498
|
-
when "auto_orient"
|
|
498
|
+
when "auto_orient", "autorot"
|
|
499
499
|
pipeline.autorot
|
|
500
500
|
when "interlace"
|
|
501
501
|
# Vips handles interlacing via saver options
|
|
502
502
|
pipeline.saver(interlace: true)
|
|
503
|
+
# Vips-native options (direct vips method names)
|
|
504
|
+
when "median"
|
|
505
|
+
value ? pipeline.median(value.to_i) : pipeline
|
|
506
|
+
when "rot90"
|
|
507
|
+
pipeline.rot90
|
|
508
|
+
when "rot180"
|
|
509
|
+
pipeline.rot180
|
|
510
|
+
when "rot270"
|
|
511
|
+
pipeline.rot270
|
|
512
|
+
when "similarity"
|
|
513
|
+
# similarity for arbitrary rotation/scale
|
|
514
|
+
value ? pipeline.custom { |img| img.similarity(angle: value.to_f) } : pipeline
|
|
515
|
+
# Edge detection filters (no arguments)
|
|
516
|
+
when "canny"
|
|
517
|
+
pipeline.canny
|
|
518
|
+
when "sobel"
|
|
519
|
+
pipeline.sobel
|
|
520
|
+
when "prewitt"
|
|
521
|
+
pipeline.custom(&:prewitt)
|
|
522
|
+
when "scharr"
|
|
523
|
+
pipeline.custom(&:scharr)
|
|
503
524
|
else
|
|
504
525
|
# Unknown option - log warning for vips
|
|
505
526
|
Paperclip.log("Warning: -#{opt_name} is not supported with vips backend, skipping")
|
data/lib/paperclip/version.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
class MyVipsProcessor < Processor
|
|
5
|
+
def make
|
|
6
|
+
basename = File.basename(file.path, File.extname(file.path))
|
|
7
|
+
dst = Paperclip::TempfileFactory.new.generate("#{basename}.png")
|
|
8
|
+
|
|
9
|
+
# Use the new vips helper instead of convert
|
|
10
|
+
vips("thumbnail :src :dst 100",
|
|
11
|
+
src: File.expand_path(file.path),
|
|
12
|
+
dst: File.expand_path(dst.path))
|
|
13
|
+
dst
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe Paperclip::MyVipsProcessor do
|
|
19
|
+
let(:file) { File.open(fixture_file("50x50.png")) }
|
|
20
|
+
let(:options) { {} }
|
|
21
|
+
let(:attachment) { double }
|
|
22
|
+
let(:processor) { Paperclip::MyVipsProcessor.new(file, options, attachment) }
|
|
23
|
+
|
|
24
|
+
subject { processor.make }
|
|
25
|
+
|
|
26
|
+
it "processes the image using vips helper" do
|
|
27
|
+
expect(subject).to respond_to(:path)
|
|
28
|
+
expect(File.exist?(subject.path)).to be true
|
|
29
|
+
|
|
30
|
+
# Check width using vipsheader
|
|
31
|
+
require "shellwords"
|
|
32
|
+
dimensions = `identify -format "%wx%h" "#{Shellwords.escape(subject.path)}"`.strip
|
|
33
|
+
expect(dimensions).to eq("100x100")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "calls the vips command" do
|
|
37
|
+
expect(Paperclip).to receive(:run).with(
|
|
38
|
+
"vips",
|
|
39
|
+
"thumbnail :src :dst 100",
|
|
40
|
+
hash_including(:src, :dst),
|
|
41
|
+
)
|
|
42
|
+
processor.make
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -11,8 +11,8 @@ describe Paperclip::Thumbnail do
|
|
|
11
11
|
|
|
12
12
|
it "correctly applies options starting with +" do
|
|
13
13
|
# The user's specific options - with Shellwords, quoted values are parsed correctly
|
|
14
|
-
convert_options = '-coalesce +profile "!icc,*" +set date:modify
|
|
15
|
-
|
|
14
|
+
convert_options = '-coalesce -quality 90 +profile "!icc,*" +set date:modify ' \
|
|
15
|
+
"+set date:create +set date:timestamp -define jpeg:dct-method=float"
|
|
16
16
|
thumb = Paperclip::Thumbnail.new(@file, {
|
|
17
17
|
geometry: "100x100",
|
|
18
18
|
convert_options: convert_options,
|
|
@@ -20,15 +20,17 @@ describe Paperclip::Thumbnail do
|
|
|
20
20
|
}, @attachment)
|
|
21
21
|
|
|
22
22
|
# Spy on the pipeline to verify the correct methods/arguments are called on it.
|
|
23
|
-
allow(thumb).to receive(:
|
|
23
|
+
allow(thumb).to receive(:apply_imagemagick_option).and_call_original
|
|
24
24
|
expect { thumb.make }.not_to raise_error
|
|
25
25
|
|
|
26
26
|
# With Shellwords parsing, the outer quotes are stripped from "!icc,*" -> !icc,*
|
|
27
|
-
expect(thumb).to have_received(:
|
|
28
|
-
expect(thumb).to have_received(:
|
|
29
|
-
expect(thumb).to have_received(:
|
|
30
|
-
expect(thumb).to have_received(:
|
|
31
|
-
expect(thumb).to have_received(:
|
|
27
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "coalesce", nil, "-")
|
|
28
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "quality", "90", "-")
|
|
29
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "profile", "!icc,*", "+")
|
|
30
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "set", "date:modify", "+")
|
|
31
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "set", "date:create", "+")
|
|
32
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "set", "date:timestamp", "+")
|
|
33
|
+
expect(thumb).to have_received(:apply_imagemagick_option).with(anything, "define", "jpeg:dct-method=float", "-")
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -170,4 +172,54 @@ describe Paperclip::Thumbnail do
|
|
|
170
172
|
expect(output).to include("second_prop: Second Value")
|
|
171
173
|
end
|
|
172
174
|
end
|
|
175
|
+
|
|
176
|
+
context "with vips-specific convert_options" do
|
|
177
|
+
before do
|
|
178
|
+
begin
|
|
179
|
+
require "vips"
|
|
180
|
+
rescue LoadError
|
|
181
|
+
skip "libvips not installed"
|
|
182
|
+
end
|
|
183
|
+
@file = File.new(fixture_file("5k.png"), "rb")
|
|
184
|
+
@attachment = double("Attachment", options: {})
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
after { @file.close if @file && !@file.closed? }
|
|
188
|
+
|
|
189
|
+
it "processes vips-native options correctly" do
|
|
190
|
+
convert_options = "-rot90"
|
|
191
|
+
file = File.new(fixture_file("rotated.jpg"), "rb")
|
|
192
|
+
|
|
193
|
+
thumb = Paperclip::Thumbnail.new(file, {
|
|
194
|
+
geometry: "100x100",
|
|
195
|
+
convert_options: convert_options,
|
|
196
|
+
backend: :vips,
|
|
197
|
+
}, @attachment)
|
|
198
|
+
|
|
199
|
+
# Processes without error
|
|
200
|
+
result = nil
|
|
201
|
+
expect { result = thumb.make }.not_to raise_error
|
|
202
|
+
|
|
203
|
+
require "shellwords"
|
|
204
|
+
dimensions = `identify -format "%wx%h" "#{Shellwords.escape(result.path)}"`.strip
|
|
205
|
+
width, height = dimensions.split("x").map(&:to_i)
|
|
206
|
+
# After 90 degree rotate, width should be greater than height
|
|
207
|
+
expect(width).to be > height
|
|
208
|
+
file.close
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "logs warning for unsupported vips options" do
|
|
212
|
+
convert_options = "-coalesce -unknown_vips_option foo"
|
|
213
|
+
|
|
214
|
+
thumb = Paperclip::Thumbnail.new(@file, {
|
|
215
|
+
geometry: "100x100",
|
|
216
|
+
convert_options: convert_options,
|
|
217
|
+
backend: :vips,
|
|
218
|
+
}, @attachment)
|
|
219
|
+
|
|
220
|
+
expect(Paperclip).to receive(:log).with(/Warning.*coalesce.*not supported/)
|
|
221
|
+
expect(Paperclip).to receive(:log).with(/Warning.*unknown_vips_option.*not supported/)
|
|
222
|
+
expect { thumb.make }.not_to raise_error
|
|
223
|
+
end
|
|
224
|
+
end
|
|
173
225
|
end
|
|
@@ -71,20 +71,20 @@ describe Paperclip::Thumbnail do
|
|
|
71
71
|
|
|
72
72
|
# Process with vips backend
|
|
73
73
|
vips_processor = described_class.new(@file, {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
geometry: "50x50#",
|
|
75
|
+
backend: :vips,
|
|
76
|
+
style: :vips_thumb,
|
|
77
|
+
}, attachment)
|
|
78
78
|
vips_result = vips_processor.make
|
|
79
79
|
|
|
80
80
|
@file.rewind
|
|
81
81
|
|
|
82
82
|
# Process with image_magick backend
|
|
83
83
|
magick_processor = described_class.new(@file, {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
geometry: "50x50#",
|
|
85
|
+
backend: :image_magick,
|
|
86
|
+
style: :magick_thumb,
|
|
87
|
+
}, attachment)
|
|
88
88
|
magick_result = magick_processor.make
|
|
89
89
|
|
|
90
90
|
# Both should produce valid 50x50 images
|
|
@@ -108,20 +108,20 @@ describe Paperclip::Thumbnail do
|
|
|
108
108
|
|
|
109
109
|
# Large preview with vips (faster for large images)
|
|
110
110
|
vips_processor = described_class.new(@file, {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
geometry: "200x200>",
|
|
112
|
+
backend: :vips,
|
|
113
|
+
style: :preview,
|
|
114
|
+
}, attachment)
|
|
115
115
|
vips_result = vips_processor.make
|
|
116
116
|
|
|
117
117
|
@file.rewind
|
|
118
118
|
|
|
119
119
|
# Small thumbnail with image_magick
|
|
120
120
|
magick_processor = described_class.new(@file, {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
geometry: "32x32#",
|
|
122
|
+
backend: :image_magick,
|
|
123
|
+
style: :icon,
|
|
124
|
+
}, attachment)
|
|
125
125
|
magick_result = magick_processor.make
|
|
126
126
|
|
|
127
127
|
# Verify dimensions
|
|
@@ -186,11 +186,9 @@ describe Paperclip::Thumbnail do
|
|
|
186
186
|
|
|
187
187
|
context "with vips backend" do
|
|
188
188
|
before do
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
skip "libvips not installed"
|
|
193
|
-
end
|
|
189
|
+
require "vips"
|
|
190
|
+
rescue LoadError
|
|
191
|
+
skip "libvips not installed"
|
|
194
192
|
end
|
|
195
193
|
|
|
196
194
|
it "resizes image to specified dimensions" do
|
|
@@ -216,7 +214,7 @@ describe Paperclip::Thumbnail do
|
|
|
216
214
|
# Verify it detects logical dimensions (rotated from 300x200 to 200x300)
|
|
217
215
|
expect(processor.current_geometry.width).to eq(200)
|
|
218
216
|
result = processor.make
|
|
219
|
-
|
|
217
|
+
|
|
220
218
|
cmd = %[identify -format "%wx%h" "#{result.path}"]
|
|
221
219
|
# Original 300x200 with orientation 6 (90 deg CW).
|
|
222
220
|
# Post autorot: 200x300.
|
|
@@ -225,7 +223,8 @@ describe Paperclip::Thumbnail do
|
|
|
225
223
|
end
|
|
226
224
|
|
|
227
225
|
it "strips metadata when requested via convert_options" do
|
|
228
|
-
processor = described_class.new(@file, { geometry: "50x50", convert_options: "-strip", backend: :vips },
|
|
226
|
+
processor = described_class.new(@file, { geometry: "50x50", convert_options: "-strip", backend: :vips },
|
|
227
|
+
attachment)
|
|
229
228
|
result = processor.make
|
|
230
229
|
|
|
231
230
|
# identify -verbose shows less output when stripped
|
|
@@ -407,10 +406,10 @@ describe Paperclip::Thumbnail do
|
|
|
407
406
|
# -strip is cross-platform, should work without warning
|
|
408
407
|
expect(Paperclip).not_to receive(:log).with(/Warning/)
|
|
409
408
|
processor = described_class.new(@file, {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
409
|
+
geometry: "50x50",
|
|
410
|
+
backend: :vips,
|
|
411
|
+
convert_options: "-strip",
|
|
412
|
+
}, attachment)
|
|
414
413
|
processor.make
|
|
415
414
|
end
|
|
416
415
|
|
|
@@ -424,10 +423,10 @@ describe Paperclip::Thumbnail do
|
|
|
424
423
|
# -density is ImageMagick-only, should warn
|
|
425
424
|
expect(Paperclip).to receive(:log).with(/Warning.*density.*not supported.*vips/)
|
|
426
425
|
processor = described_class.new(@file, {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
426
|
+
geometry: "50x50",
|
|
427
|
+
backend: :vips,
|
|
428
|
+
convert_options: "-density 150",
|
|
429
|
+
}, attachment)
|
|
431
430
|
processor.make
|
|
432
431
|
end
|
|
433
432
|
end
|
|
@@ -439,10 +438,10 @@ describe Paperclip::Thumbnail do
|
|
|
439
438
|
# Helper to create a thumbnail with specific convert_options
|
|
440
439
|
def make_thumb_with_options(file, options_string)
|
|
441
440
|
thumb = Paperclip::Thumbnail.new(file, {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
441
|
+
geometry: "100x100",
|
|
442
|
+
convert_options: options_string,
|
|
443
|
+
backend: :image_magick,
|
|
444
|
+
}, attachment)
|
|
446
445
|
thumb.make
|
|
447
446
|
end
|
|
448
447
|
|
|
@@ -576,11 +575,11 @@ describe Paperclip::Thumbnail do
|
|
|
576
575
|
# Use JPEG to test interlacing (PNG reports format name instead)
|
|
577
576
|
file = File.new(fixture_file("rotated.jpg"), "rb")
|
|
578
577
|
thumb = Paperclip::Thumbnail.new(file, {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
578
|
+
geometry: "100x100",
|
|
579
|
+
convert_options: "-interlace Plane",
|
|
580
|
+
backend: :image_magick,
|
|
581
|
+
format: :jpg,
|
|
582
|
+
}, attachment)
|
|
584
583
|
result = thumb.make
|
|
585
584
|
|
|
586
585
|
interlace = `identify -format "%[interlace]" "#{result.path}"`.strip
|
|
@@ -602,10 +601,10 @@ describe Paperclip::Thumbnail do
|
|
|
602
601
|
# Use a square image for predictable crop results
|
|
603
602
|
file = File.new(fixture_file("50x50.png"), "rb")
|
|
604
603
|
thumb = Paperclip::Thumbnail.new(file, {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
604
|
+
geometry: "50x50", # Keep original size
|
|
605
|
+
convert_options: "-crop 25x25+0+0 +repage",
|
|
606
|
+
backend: :image_magick,
|
|
607
|
+
}, attachment)
|
|
609
608
|
result = thumb.make
|
|
610
609
|
|
|
611
610
|
dimensions = `identify -format "%wx%h" "#{result.path}"`.strip
|
|
@@ -759,11 +758,9 @@ describe Paperclip::Thumbnail do
|
|
|
759
758
|
let(:attachment) { double("Attachment", options: {}) }
|
|
760
759
|
|
|
761
760
|
before do
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
skip "libvips not installed"
|
|
766
|
-
end
|
|
761
|
+
require "vips"
|
|
762
|
+
rescue LoadError
|
|
763
|
+
skip "libvips not installed"
|
|
767
764
|
end
|
|
768
765
|
|
|
769
766
|
# Helper to create a thumbnail with vips backend and specific convert_options
|
|
@@ -1211,8 +1208,11 @@ describe Paperclip::Thumbnail do
|
|
|
1211
1208
|
end
|
|
1212
1209
|
|
|
1213
1210
|
def width; 100; end
|
|
1211
|
+
|
|
1214
1212
|
def height; 100; end
|
|
1213
|
+
|
|
1215
1214
|
def modifier; nil; end
|
|
1215
|
+
|
|
1216
1216
|
def auto_orient; end
|
|
1217
1217
|
end
|
|
1218
1218
|
|
|
@@ -1237,7 +1237,9 @@ describe Paperclip::Thumbnail do
|
|
|
1237
1237
|
end
|
|
1238
1238
|
|
|
1239
1239
|
def width; 151; end
|
|
1240
|
+
|
|
1240
1241
|
def height; 167; end
|
|
1242
|
+
|
|
1241
1243
|
def modifier; nil; end
|
|
1242
1244
|
end
|
|
1243
1245
|
|
|
@@ -1403,7 +1405,7 @@ describe Paperclip::Thumbnail do
|
|
|
1403
1405
|
@file,
|
|
1404
1406
|
geometry: "50x50",
|
|
1405
1407
|
frame_index: 5,
|
|
1406
|
-
format: :jpg
|
|
1408
|
+
format: :jpg,
|
|
1407
1409
|
)
|
|
1408
1410
|
end
|
|
1409
1411
|
|
|
@@ -1415,11 +1417,9 @@ describe Paperclip::Thumbnail do
|
|
|
1415
1417
|
|
|
1416
1418
|
context "with vips backend" do
|
|
1417
1419
|
before do
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
skip "libvips not installed"
|
|
1422
|
-
end
|
|
1420
|
+
require "vips"
|
|
1421
|
+
rescue LoadError
|
|
1422
|
+
skip "libvips not installed"
|
|
1423
1423
|
end
|
|
1424
1424
|
|
|
1425
1425
|
it "preserves animation when output is GIF" do
|
|
@@ -1431,7 +1431,8 @@ describe Paperclip::Thumbnail do
|
|
|
1431
1431
|
end
|
|
1432
1432
|
|
|
1433
1433
|
it "collapses animation when animated: false is set" do
|
|
1434
|
-
processor = Paperclip::Thumbnail.new(@file,
|
|
1434
|
+
processor = Paperclip::Thumbnail.new(@file,
|
|
1435
|
+
{ geometry: "50x50", format: :gif, animated: false, backend: :vips })
|
|
1435
1436
|
dst = processor.make
|
|
1436
1437
|
cmd = %[identify -format "%wx%h," "#{dst.path}"]
|
|
1437
1438
|
frames = `#{cmd}`.chomp.split(",").reject(&:empty?)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jr-paperclip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.0.0
|
|
4
|
+
version: 8.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jukka Rautanen
|
|
@@ -389,7 +389,6 @@ extra_rdoc_files: []
|
|
|
389
389
|
files:
|
|
390
390
|
- ".github/FUNDING.yml"
|
|
391
391
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
|
392
|
-
- ".github/ISSUE_TEMPLATE/custom.md"
|
|
393
392
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
|
394
393
|
- ".github/workflows/reviewdog.yml"
|
|
395
394
|
- ".github/workflows/tests.yml"
|
|
@@ -533,6 +532,7 @@ files:
|
|
|
533
532
|
- spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb
|
|
534
533
|
- spec/paperclip/media_type_spoof_detector_spec.rb
|
|
535
534
|
- spec/paperclip/meta_class_spec.rb
|
|
535
|
+
- spec/paperclip/migration_guide_example_spec.rb
|
|
536
536
|
- spec/paperclip/paperclip_missing_attachment_styles_spec.rb
|
|
537
537
|
- spec/paperclip/paperclip_spec.rb
|
|
538
538
|
- spec/paperclip/plural_cache_spec.rb
|
|
@@ -673,6 +673,7 @@ test_files:
|
|
|
673
673
|
- spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb
|
|
674
674
|
- spec/paperclip/media_type_spoof_detector_spec.rb
|
|
675
675
|
- spec/paperclip/meta_class_spec.rb
|
|
676
|
+
- spec/paperclip/migration_guide_example_spec.rb
|
|
676
677
|
- spec/paperclip/paperclip_missing_attachment_styles_spec.rb
|
|
677
678
|
- spec/paperclip/paperclip_spec.rb
|
|
678
679
|
- spec/paperclip/plural_cache_spec.rb
|