image_processing 0.11.1 → 0.11.2
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.
Potentially problematic release.
This version of image_processing might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +7 -9
- data/image_processing.gemspec +1 -0
- data/lib/image_processing.rb +2 -0
- data/lib/image_processing/builder.rb +13 -0
- data/lib/image_processing/chainable.rb +12 -10
- data/lib/image_processing/mini_magick.rb +5 -13
- data/lib/image_processing/pipeline.rb +69 -36
- data/lib/image_processing/processor.rb +23 -0
- data/lib/image_processing/version.rb +1 -1
- data/lib/image_processing/vips.rb +17 -8
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99835dc44c17bab77f81bca431807996d31042d2
|
4
|
+
data.tar.gz: c6b0c1b08d70e871a90f63ae66e5a501edc7c521
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 529a954d4f587f756e55aa5316743f34747c5786c0c8ec729db1a9977c24bc5304575e9a96394d5270f234e180240fbd0f33035c0640e89993687377df4e744d
|
7
|
+
data.tar.gz: b6752c0ec43316e402b30bd54bd714b392bba09aac391979d4502c54f915cce2ceb989fcf3b366982c16084cc5208fa2168bb67a0d2097ba98b8c1a126fa966b
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
## 0.11.
|
1
|
+
## 0.11.2 (2018-03-31)
|
2
|
+
|
3
|
+
* [minimagick] Avoid `#resize_*` operations stripping data by switching back to `-resize` (@janko-m)
|
4
|
+
|
5
|
+
* [core] Make sure an empty destination file doesn't remain on processing errors when `:destination` is used (@janko-m)
|
6
|
+
|
7
|
+
* [vips] Fix `:alpha` not correctly adding alpha for certain types of images (@janko-m)
|
8
|
+
|
9
|
+
* [minimagick] Drop official support for GraphicsMagick (@janko-m)
|
10
|
+
|
11
|
+
## 0.11.1 (2018-03-27)
|
2
12
|
|
3
13
|
* [minimagick] Rename `#limit` to `#limits` to still allow adding `-limit` arguments directly (@janko-m)
|
4
14
|
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# ImageProcessing
|
2
2
|
|
3
3
|
Provides higher-level image processing functionality that is commonly needed
|
4
|
-
when accepting user uploads. Supports processing with [
|
5
|
-
[ImageMagick]/[GraphicsMagick].
|
4
|
+
when accepting user uploads. Supports processing with [libvips] and [ImageMagick].
|
6
5
|
|
7
6
|
The goal of this project is to have a single place where common image
|
8
7
|
processing helper methods are maintained, instead of Paperclip, CarrierWave,
|
@@ -11,7 +10,7 @@ Refile, Dragonfly and ActiveStorage each implementing their own versions.
|
|
11
10
|
## Installation
|
12
11
|
|
13
12
|
```rb
|
14
|
-
gem "image_processing", "~> 0.
|
13
|
+
gem "image_processing", "~> 0.11"
|
15
14
|
```
|
16
15
|
|
17
16
|
## Usage
|
@@ -103,17 +102,17 @@ See the **[wiki]** for additional "How To" guides for common scenarios.
|
|
103
102
|
|
104
103
|
## Contributing
|
105
104
|
|
106
|
-
Test suite requires `imagemagick
|
107
|
-
|
105
|
+
Test suite requires `imagemagick` and `libvips` to be installed. On Mac OS you
|
106
|
+
can install them with Homebrew:
|
108
107
|
|
109
108
|
```
|
110
|
-
$ brew install imagemagick
|
109
|
+
$ brew install imagemagick vips
|
111
110
|
```
|
112
111
|
|
113
112
|
Afterwards you can run tests with
|
114
113
|
|
115
114
|
```
|
116
|
-
$ rake test
|
115
|
+
$ bundle exec rake test
|
117
116
|
```
|
118
117
|
|
119
118
|
## Credits
|
@@ -125,9 +124,8 @@ The `ImageProcessing::MiniMagick` functionality was extracted from
|
|
125
124
|
|
126
125
|
[MIT](LICENSE.txt)
|
127
126
|
|
127
|
+
[libvps]: http://jcupitt.github.io/libvips/
|
128
128
|
[ImageMagick]: https://www.imagemagick.org
|
129
|
-
[GraphicsMagick]: http://www.graphicsmagick.org
|
130
|
-
[VIPS]: http://jcupitt.github.io/libvips/
|
131
129
|
[`ImageProcessing::Vips`]: /doc/vips.md#imageprocessingvips
|
132
130
|
[`ImageProcessing::MiniMagick`]: /doc/minimagick.md#imageprocessingminimagick
|
133
131
|
[refile-mini_magick]: https://github.com/refile/refile-mini_magick
|
data/image_processing.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = Dir["README.md", "LICENSE.txt", "CHANGELOG.md", "lib/**/*.rb", "*.gemspec"]
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
+
spec.add_development_dependency "rake"
|
19
20
|
spec.add_development_dependency "minitest", "~> 5.8"
|
20
21
|
spec.add_development_dependency "minitest-hooks", ">= 1.4.2"
|
21
22
|
spec.add_development_dependency "minispec-metadata"
|
data/lib/image_processing.rb
CHANGED
@@ -29,7 +29,7 @@ module ImageProcessing
|
|
29
29
|
|
30
30
|
def method_missing(name, *args)
|
31
31
|
if name.to_s.end_with?("!")
|
32
|
-
send(name.to_s.chomp("!"), *args).call
|
32
|
+
send(name.to_s.chomp("!"), *args).call
|
33
33
|
elsif name.to_s.end_with?("?")
|
34
34
|
super
|
35
35
|
else
|
@@ -37,26 +37,28 @@ module ImageProcessing
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def call(file = nil, **call_options)
|
40
|
+
def call(file = nil, destination: nil, **call_options)
|
41
41
|
options = default_options
|
42
42
|
options = options.merge(source: file) if file
|
43
|
+
options = options.merge(destination: destination) if destination
|
43
44
|
|
44
45
|
branch(options).call!(**call_options)
|
45
46
|
end
|
46
47
|
|
47
48
|
def branch(options)
|
48
|
-
options = options.merge(
|
49
|
-
|
49
|
+
options = options.merge(processor_class: self::Processor) unless self.is_a?(Builder)
|
50
|
+
|
51
|
+
Builder.new(options)
|
50
52
|
end
|
51
53
|
|
52
54
|
def default_options
|
53
55
|
@default_options ||= {
|
54
|
-
source:
|
55
|
-
loader:
|
56
|
-
saver:
|
57
|
-
format:
|
58
|
-
operations:
|
59
|
-
|
56
|
+
source: nil,
|
57
|
+
loader: {},
|
58
|
+
saver: {},
|
59
|
+
format: nil,
|
60
|
+
operations: [],
|
61
|
+
processor_class: nil,
|
60
62
|
}
|
61
63
|
end
|
62
64
|
end
|
@@ -17,27 +17,19 @@ module ImageProcessing
|
|
17
17
|
false
|
18
18
|
end
|
19
19
|
|
20
|
-
class Processor
|
20
|
+
class Processor < ImageProcessing::Processor
|
21
21
|
IMAGE_CLASS = ::MiniMagick::Tool
|
22
22
|
|
23
|
-
def apply_operation(name, magick, *args)
|
24
|
-
if respond_to?(name)
|
25
|
-
public_send(name, magick, *args)
|
26
|
-
else
|
27
|
-
magick.send(name, *args)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
23
|
def resize_to_limit(magick, width, height)
|
32
|
-
magick.
|
24
|
+
magick.resize "#{width}x#{height}>"
|
33
25
|
end
|
34
26
|
|
35
27
|
def resize_to_fit(magick, width, height)
|
36
|
-
magick.
|
28
|
+
magick.resize "#{width}x#{height}"
|
37
29
|
end
|
38
30
|
|
39
31
|
def resize_to_fill(magick, width, height, gravity: "Center")
|
40
|
-
magick.
|
32
|
+
magick.resize "#{width}x#{height}^"
|
41
33
|
magick.gravity gravity
|
42
34
|
magick.background "rgba(255,255,255,0.0)" # transparent
|
43
35
|
magick.extent "#{width}x#{height}"
|
@@ -46,7 +38,7 @@ module ImageProcessing
|
|
46
38
|
def resize_and_pad(magick, width, height, background: :transparent, gravity: "Center")
|
47
39
|
background = "rgba(255,255,255,0.0)" if background.to_s == "transparent"
|
48
40
|
|
49
|
-
magick.
|
41
|
+
magick.resize "#{width}x#{height}"
|
50
42
|
magick.background background
|
51
43
|
magick.gravity gravity
|
52
44
|
magick.extent "#{width}x#{height}"
|
@@ -2,58 +2,91 @@ require "tempfile"
|
|
2
2
|
|
3
3
|
module ImageProcessing
|
4
4
|
class Pipeline
|
5
|
-
|
5
|
+
DEFAULT_FORMAT = "jpg"
|
6
|
+
|
7
|
+
attr_reader :source, :loader, :saver, :format, :operations, :processor_class, :destination
|
6
8
|
|
7
9
|
def initialize(options)
|
8
|
-
|
10
|
+
options.each do |name, value|
|
11
|
+
value = normalize_source(value, options) if name == :source
|
12
|
+
instance_variable_set(:"@#{name}", value)
|
13
|
+
end
|
9
14
|
end
|
10
15
|
|
11
|
-
def call
|
12
|
-
|
16
|
+
def call(save: true)
|
17
|
+
processor = processor_class.new(self)
|
18
|
+
image = processor.load_image(source, **loader)
|
13
19
|
|
14
|
-
|
20
|
+
operations.each do |name, args|
|
21
|
+
image = processor.apply_operation(name, image, *args)
|
22
|
+
end
|
15
23
|
|
16
|
-
if
|
17
|
-
|
18
|
-
elsif
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
elsif default_options[:source].respond_to?(:to_path)
|
23
|
-
source = default_options[:source].to_path
|
24
|
+
if save == false
|
25
|
+
image
|
26
|
+
elsif destination
|
27
|
+
handle_destination do
|
28
|
+
processor.save_image(image, destination, **saver)
|
29
|
+
end
|
24
30
|
else
|
25
|
-
|
31
|
+
create_tempfile do |tempfile|
|
32
|
+
processor.save_image(image, tempfile.path, **saver)
|
33
|
+
end
|
26
34
|
end
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
37
|
+
def source_path
|
38
|
+
source if source.is_a?(String)
|
39
|
+
end
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
image = processor.apply_operation(name, image, *args)
|
36
|
-
end
|
37
|
-
end
|
41
|
+
def destination_format
|
42
|
+
format = File.extname(destination)[1..-1] if destination
|
43
|
+
format ||= self.format
|
44
|
+
format ||= File.extname(source_path)[1..-1] if source_path
|
38
45
|
|
39
|
-
|
46
|
+
format || DEFAULT_FORMAT
|
47
|
+
end
|
40
48
|
|
41
|
-
|
49
|
+
private
|
42
50
|
|
43
|
-
|
44
|
-
|
51
|
+
def create_tempfile
|
52
|
+
tempfile = Tempfile.new(["image_processing", ".#{destination_format}"], binmode: true)
|
45
53
|
|
46
|
-
|
54
|
+
yield tempfile
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
tempfile.open
|
57
|
+
tempfile
|
58
|
+
rescue
|
59
|
+
tempfile.close! if tempfile
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
|
63
|
+
# In case of processing errors, both libvips and imagemagick will leave the
|
64
|
+
# empty destination file they created, so this method makes sure it is
|
65
|
+
# deleted in case an exception is raised on saving the image.
|
66
|
+
def handle_destination
|
67
|
+
destination_existed = File.exist?(destination)
|
68
|
+
yield
|
69
|
+
rescue
|
70
|
+
File.delete(destination) if File.exist?(destination) && !destination_existed
|
71
|
+
raise
|
72
|
+
end
|
54
73
|
|
55
|
-
|
56
|
-
|
74
|
+
def normalize_source(source, options)
|
75
|
+
fail Error, "source file is not provided" unless source
|
76
|
+
|
77
|
+
image_class = options[:processor_class]::IMAGE_CLASS
|
78
|
+
|
79
|
+
if source.is_a?(image_class)
|
80
|
+
source
|
81
|
+
elsif source.is_a?(String)
|
82
|
+
source
|
83
|
+
elsif source.respond_to?(:path)
|
84
|
+
source.path
|
85
|
+
elsif source.respond_to?(:to_path)
|
86
|
+
source.to_path
|
87
|
+
else
|
88
|
+
fail Error, "source file needs to respond to #path, or be a String, a Pathname, or a #{image_class} object"
|
89
|
+
end
|
57
90
|
end
|
58
91
|
end
|
59
92
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ImageProcessing
|
2
|
+
class Processor
|
3
|
+
def initialize(pipeline)
|
4
|
+
@pipeline = pipeline
|
5
|
+
end
|
6
|
+
|
7
|
+
def apply_operation(name, image, *args)
|
8
|
+
if respond_to?(name)
|
9
|
+
public_send(name, image, *args)
|
10
|
+
else
|
11
|
+
image.send(name, *args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def custom(image, block)
|
16
|
+
block.call(image) || image
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :pipeline
|
22
|
+
end
|
23
|
+
end
|
@@ -14,19 +14,15 @@ module ImageProcessing
|
|
14
14
|
false
|
15
15
|
end
|
16
16
|
|
17
|
-
class Processor
|
17
|
+
class Processor < ImageProcessing::Processor
|
18
18
|
IMAGE_CLASS = ::Vips::Image
|
19
19
|
# libvips has this arbitrary number as a sanity-check upper bound on image
|
20
20
|
# size.
|
21
21
|
MAX_COORD = 10_000_000
|
22
22
|
|
23
23
|
def apply_operation(name, image, *args)
|
24
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
result = image.send(name, *args)
|
28
|
-
result.is_a?(::Vips::Image) ? result : image
|
29
|
-
end
|
24
|
+
result = super
|
25
|
+
result.is_a?(::Vips::Image) ? result : image
|
30
26
|
end
|
31
27
|
|
32
28
|
def resize_to_limit(image, width, height, **options)
|
@@ -48,7 +44,7 @@ module ImageProcessing
|
|
48
44
|
embed_options.reject! { |name, value| value.nil? }
|
49
45
|
|
50
46
|
image = image.thumbnail_image(width, height: height, **options)
|
51
|
-
image = image
|
47
|
+
image = add_alpha(image) if alpha && !has_alpha?(image)
|
52
48
|
image.gravity(gravity, width, height, **embed_options)
|
53
49
|
end
|
54
50
|
|
@@ -74,6 +70,19 @@ module ImageProcessing
|
|
74
70
|
|
75
71
|
private
|
76
72
|
|
73
|
+
# Port of libvips' vips_addalpha().
|
74
|
+
def add_alpha(image)
|
75
|
+
max_alpha = (image.interpretation == :grey16 || image.interpretation == :rgb16) ? 65535 : 255
|
76
|
+
image.bandjoin(max_alpha)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Port of libvips' vips_hasalpha().
|
80
|
+
def has_alpha?(image)
|
81
|
+
image.bands == 2 ||
|
82
|
+
(image.bands == 4 && image.interpretation != :cmyk) ||
|
83
|
+
image.bands > 4
|
84
|
+
end
|
85
|
+
|
77
86
|
def default_dimensions(width, height)
|
78
87
|
raise Error, "either width or height must be specified" unless width || height
|
79
88
|
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_processing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: minitest
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,10 +120,12 @@ files:
|
|
106
120
|
- README.md
|
107
121
|
- image_processing.gemspec
|
108
122
|
- lib/image_processing.rb
|
123
|
+
- lib/image_processing/builder.rb
|
109
124
|
- lib/image_processing/chainable.rb
|
110
125
|
- lib/image_processing/mini_magick.rb
|
111
126
|
- lib/image_processing/mini_magick/deprecated_api.rb
|
112
127
|
- lib/image_processing/pipeline.rb
|
128
|
+
- lib/image_processing/processor.rb
|
113
129
|
- lib/image_processing/version.rb
|
114
130
|
- lib/image_processing/vips.rb
|
115
131
|
homepage: https://github.com/janko-m/image_processing
|