dynamic_image 2.0.21 → 2.1.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/README.md +2 -1
- data/lib/dynamic_image.rb +3 -1
- data/lib/dynamic_image/belongs_to.rb +1 -1
- data/lib/dynamic_image/controller.rb +23 -10
- data/lib/dynamic_image/engine.rb +1 -1
- data/lib/dynamic_image/image_reader.rb +11 -1
- data/lib/dynamic_image/image_sizing.rb +2 -2
- data/lib/dynamic_image/jobs.rb +3 -0
- data/lib/dynamic_image/jobs/create_variant.rb +18 -0
- data/lib/dynamic_image/model/dimensions.rb +2 -2
- data/lib/dynamic_image/processed_image.rb +50 -34
- data/lib/dynamic_image/profiles/sRGB_ICC_v4_Appearance.icc +0 -0
- data/lib/dynamic_image/routing.rb +2 -1
- data/lib/dynamic_image/version.rb +1 -1
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd1d723cd97a91199630354e541d2a740aa41bae8ccb07554c1e723096a1b6dc
|
4
|
+
data.tar.gz: b4f5a8bf26b69da95543cb8885f5add2b72504ea88f83c4097bc1c25a13c622f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2ba7e0be3c929f9da2b1c7311ae7a246341f081cc8057e8293417a8a73634ec5844d0ce7a691c3d5c771d99924b311c59a2a9b9bf2e2baae84675a3967a8614
|
7
|
+
data.tar.gz: '08cfc8124f2d6e2bd9d505315b1af7ed87e42ae4ff497a88fafd240e9594943721a6f72e7f196d5dffb1a43a1c8489e9b1b427e4042e149aedcf21a22f0506ba'
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
[](https://rubygems.org/gems/dynamic_image)
|
2
|
-
|
2
|
+

|
3
3
|
[](https://codeclimate.com/github/elektronaut/dynamic_image)
|
4
4
|
[](https://codeclimate.com/github/elektronaut/dynamic_image)
|
5
5
|
[](http://inch-ci.org/github/elektronaut/dynamic_image)
|
@@ -31,6 +31,7 @@ and enumeration attacks.
|
|
31
31
|
* Rails 5
|
32
32
|
* Ruby 2.4+
|
33
33
|
* ImageMagick command line tools
|
34
|
+
* ExifTool
|
34
35
|
|
35
36
|
## Documentation
|
36
37
|
|
data/lib/dynamic_image.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "mini_exiftool"
|
3
4
|
require "mini_magick"
|
4
5
|
require "dis"
|
5
6
|
require "vector2d"
|
@@ -12,11 +13,12 @@ require "dynamic_image/errors"
|
|
12
13
|
require "dynamic_image/helper"
|
13
14
|
require "dynamic_image/image_reader"
|
14
15
|
require "dynamic_image/image_sizing"
|
16
|
+
require "dynamic_image/jobs"
|
15
17
|
require "dynamic_image/metadata"
|
16
18
|
require "dynamic_image/model"
|
17
19
|
require "dynamic_image/processed_image"
|
18
20
|
require "dynamic_image/routing"
|
19
21
|
|
20
22
|
module DynamicImage
|
21
|
-
cattr_accessor :digest_verifier
|
23
|
+
cattr_accessor :digest_verifier, :process_later_limit
|
22
24
|
end
|
@@ -8,7 +8,7 @@ module DynamicImage
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def belongs_to_image(name, scope = nil, **options)
|
11
|
-
belongs_to(name, scope, options)
|
11
|
+
belongs_to(name, scope, **options)
|
12
12
|
|
13
13
|
define_method "#{name}=" do |new_image|
|
14
14
|
if new_image.present? && !new_image.is_a?(DynamicImage::Model)
|
@@ -44,13 +44,21 @@ module DynamicImage
|
|
44
44
|
private
|
45
45
|
|
46
46
|
def cache_expiration_header
|
47
|
-
expires_in 30.days, public: true
|
47
|
+
expires_in 30.days, public: true if response.status == 200
|
48
48
|
end
|
49
49
|
|
50
50
|
def find_record
|
51
51
|
@record = model.find(params[:id])
|
52
52
|
end
|
53
53
|
|
54
|
+
def process_later?(processed_image, size)
|
55
|
+
return false unless DynamicImage.process_later_limit
|
56
|
+
|
57
|
+
image_size = processed_image.record.size.x * processed_image.record.size.y
|
58
|
+
image_size > DynamicImage.process_later_limit &&
|
59
|
+
!processed_image.find_variant(size)
|
60
|
+
end
|
61
|
+
|
54
62
|
def render_image(options)
|
55
63
|
return unless stale?(@record)
|
56
64
|
|
@@ -59,8 +67,8 @@ module DynamicImage
|
|
59
67
|
render(file: File.join(File.dirname(__FILE__), "templates/show"),
|
60
68
|
layout: false, locals: { options: options })
|
61
69
|
end
|
62
|
-
format.any(:gif, :jpeg, :png, :tiff, :webp) do
|
63
|
-
send_image(
|
70
|
+
format.any(:gif, :jpeg, :jpg, :png, :tiff, :webp) do
|
71
|
+
send_image(@record, options)
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|
@@ -69,7 +77,7 @@ module DynamicImage
|
|
69
77
|
return unless stale?(@record)
|
70
78
|
|
71
79
|
respond_to do |format|
|
72
|
-
format.any(:gif, :jpeg, :png, :tiff, :webp) do
|
80
|
+
format.any(:gif, :jpeg, :jpg, :png, :tiff, :webp) do
|
73
81
|
send_data(@record.data,
|
74
82
|
filename: filename,
|
75
83
|
content_type: @record.content_type,
|
@@ -82,12 +90,17 @@ module DynamicImage
|
|
82
90
|
params[:format]
|
83
91
|
end
|
84
92
|
|
85
|
-
def send_image(
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
93
|
+
def send_image(image, options)
|
94
|
+
processed_image = DynamicImage::ProcessedImage.new(image, options)
|
95
|
+
if process_later?(processed_image, requested_size)
|
96
|
+
DynamicImage::Jobs::CreateVariant
|
97
|
+
.perform_later(image, options, requested_size.to_s)
|
98
|
+
head 503, retry_after: 10
|
99
|
+
else
|
100
|
+
send_data(processed_image.cropped_and_resized(requested_size),
|
101
|
+
content_type: processed_image.content_type,
|
102
|
+
disposition: "inline")
|
103
|
+
end
|
91
104
|
end
|
92
105
|
|
93
106
|
def verify_signed_params
|
data/lib/dynamic_image/engine.rb
CHANGED
@@ -31,7 +31,7 @@ module DynamicImage
|
|
31
31
|
ActiveSupport.on_load(:active_record) do
|
32
32
|
send :include, DynamicImage::BelongsTo
|
33
33
|
end
|
34
|
-
ActionDispatch::Routing::Mapper.
|
34
|
+
ActionDispatch::Routing::Mapper.include DynamicImage::Routing
|
35
35
|
|
36
36
|
ActionDispatch::ExceptionWrapper.rescue_responses.merge!(
|
37
37
|
"DynamicImage::Errors::InvalidSignature" => :unauthorized
|
@@ -21,6 +21,12 @@ module DynamicImage
|
|
21
21
|
@data = data
|
22
22
|
end
|
23
23
|
|
24
|
+
def exif
|
25
|
+
raise DynamicImage::Errors::InvalidHeader unless valid_header?
|
26
|
+
|
27
|
+
MiniExiftool.new(string_io)
|
28
|
+
end
|
29
|
+
|
24
30
|
def read
|
25
31
|
raise DynamicImage::Errors::InvalidHeader unless valid_header?
|
26
32
|
|
@@ -39,7 +45,11 @@ module DynamicImage
|
|
39
45
|
private
|
40
46
|
|
41
47
|
def file_header
|
42
|
-
@file_header ||=
|
48
|
+
@file_header ||= string_io.read(8)
|
49
|
+
end
|
50
|
+
|
51
|
+
def string_io
|
52
|
+
StringIO.new(@data, "rb")
|
43
53
|
end
|
44
54
|
end
|
45
55
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DynamicImage
|
4
|
+
module Jobs
|
5
|
+
# = Create variant
|
6
|
+
#
|
7
|
+
# Creates an image variant.
|
8
|
+
class CreateVariant < ActiveJob::Base
|
9
|
+
queue_as :dis
|
10
|
+
|
11
|
+
def perform(record, options, size)
|
12
|
+
size_v = Vector2d.parse(size)
|
13
|
+
DynamicImage::ProcessedImage.new(record, options)
|
14
|
+
.find_or_create_variant(size_v)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,6 +6,8 @@ module DynamicImage
|
|
6
6
|
# Handles all processing of images. Takes an instance of
|
7
7
|
# +DynamicImage::Model+ as argument.
|
8
8
|
class ProcessedImage
|
9
|
+
attr_reader :record
|
10
|
+
|
9
11
|
def initialize(record, options = {})
|
10
12
|
@record = record
|
11
13
|
@uncropped = options[:uncropped] ? true : false
|
@@ -40,10 +42,24 @@ module DynamicImage
|
|
40
42
|
find_or_create_variant(size).data
|
41
43
|
end
|
42
44
|
|
45
|
+
# Find or create a variant with the given size.
|
46
|
+
def find_or_create_variant(size)
|
47
|
+
find_variant(size) || create_variant(size)
|
48
|
+
rescue ActiveRecord::RecordNotUnique
|
49
|
+
find_variant(size)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find a variant with the given size.
|
53
|
+
def find_variant(size)
|
54
|
+
return nil unless record.persisted?
|
55
|
+
|
56
|
+
record.variants.find_by(variant_params(size))
|
57
|
+
end
|
58
|
+
|
43
59
|
# Normalizes the image.
|
44
60
|
#
|
45
61
|
# * Applies EXIF rotation
|
46
|
-
# *
|
62
|
+
# * Converts to sRGB
|
47
63
|
# * Strips metadata
|
48
64
|
# * Optimizes GIFs
|
49
65
|
# * Performs format conversion if the requested format is different
|
@@ -59,7 +75,7 @@ module DynamicImage
|
|
59
75
|
process_data do |image|
|
60
76
|
image.combine_options do |combined|
|
61
77
|
combined.auto_orient
|
62
|
-
combined
|
78
|
+
convert_to_srgb(image, combined)
|
63
79
|
yield(combined) if block_given?
|
64
80
|
optimize(combined)
|
65
81
|
end
|
@@ -70,27 +86,36 @@ module DynamicImage
|
|
70
86
|
private
|
71
87
|
|
72
88
|
def coalesced(image)
|
73
|
-
|
89
|
+
gif? ? DynamicImage::ImageReader.new(image.coalesce.to_blob).read : image
|
90
|
+
end
|
74
91
|
|
75
|
-
|
92
|
+
def convert_to_srgb(image, combined)
|
93
|
+
if image.data["profiles"].present? &&
|
94
|
+
exif.colorspacedata&.strip&.downcase == record.colorspace
|
95
|
+
combined.profile(srgb_profile)
|
96
|
+
end
|
97
|
+
combined.colorspace("sRGB") if record.cmyk?
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_variant(size)
|
101
|
+
record.variants.create(
|
102
|
+
variant_params(size).merge(filename: record.filename,
|
103
|
+
content_type: content_type,
|
104
|
+
data: crop_and_resize(size))
|
105
|
+
)
|
76
106
|
end
|
77
107
|
|
78
108
|
def crop_and_resize(size)
|
79
109
|
normalized do |image|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
110
|
+
next unless record.cropped? || size != record.size
|
111
|
+
|
112
|
+
image.crop(image_sizing.crop_geometry_string(size))
|
113
|
+
image.resize(size)
|
84
114
|
end
|
85
115
|
end
|
86
116
|
|
87
|
-
def
|
88
|
-
record.
|
89
|
-
record.variants.create(
|
90
|
-
variant_params(size).merge(filename: record.filename,
|
91
|
-
content_type: content_type,
|
92
|
-
data: crop_and_resize(size))
|
93
|
-
)
|
117
|
+
def exif
|
118
|
+
@exif ||= DynamicImage::ImageReader.new(record.data).exif
|
94
119
|
end
|
95
120
|
|
96
121
|
def format
|
@@ -102,7 +127,7 @@ module DynamicImage
|
|
102
127
|
end
|
103
128
|
|
104
129
|
def jpeg?
|
105
|
-
content_type == "image/jpeg"
|
130
|
+
content_type == "image/jpeg"
|
106
131
|
end
|
107
132
|
|
108
133
|
def image_sizing
|
@@ -110,10 +135,6 @@ module DynamicImage
|
|
110
135
|
DynamicImage::ImageSizing.new(record, uncropped: @uncropped)
|
111
136
|
end
|
112
137
|
|
113
|
-
def needs_colorspace_conversion?
|
114
|
-
record.cmyk?
|
115
|
-
end
|
116
|
-
|
117
138
|
def needs_format_conversion?
|
118
139
|
format != record_format
|
119
140
|
end
|
@@ -133,15 +154,9 @@ module DynamicImage
|
|
133
154
|
result
|
134
155
|
end
|
135
156
|
|
136
|
-
attr_reader :record
|
137
|
-
|
138
157
|
def record_format
|
139
|
-
{ "image/bmp" => "BMP",
|
140
|
-
"image/
|
141
|
-
"image/gif" => "GIF",
|
142
|
-
"image/jpeg" => "JPEG",
|
143
|
-
"image/pjpeg" => "JPEG",
|
144
|
-
"image/tiff" => "TIFF",
|
158
|
+
{ "image/bmp" => "BMP", "image/png" => "PNG", "image/gif" => "GIF",
|
159
|
+
"image/jpeg" => "JPEG", "image/pjpeg" => "JPEG", "image/tiff" => "TIFF",
|
145
160
|
"image/webp" => "WEBP" }[record.content_type]
|
146
161
|
end
|
147
162
|
|
@@ -149,15 +164,16 @@ module DynamicImage
|
|
149
164
|
raise DynamicImage::Errors::InvalidImage unless record.valid?
|
150
165
|
end
|
151
166
|
|
167
|
+
def srgb_profile
|
168
|
+
File.join(File.dirname(__FILE__), "profiles/sRGB_ICC_v4_Appearance.icc")
|
169
|
+
end
|
170
|
+
|
152
171
|
def variant_params(size)
|
153
172
|
crop_size, crop_start = image_sizing.crop_geometry(size)
|
154
173
|
|
155
|
-
{ width: size.x.round,
|
156
|
-
|
157
|
-
|
158
|
-
crop_height: crop_size.y,
|
159
|
-
crop_start_x: crop_start.x,
|
160
|
-
crop_start_y: crop_start.y,
|
174
|
+
{ width: size.x.round, height: size.y.round,
|
175
|
+
crop_width: crop_size.x, crop_height: crop_size.y,
|
176
|
+
crop_start_x: crop_start.x, crop_start_y: crop_start.y,
|
161
177
|
format: format }
|
162
178
|
end
|
163
179
|
end
|
Binary file
|
@@ -13,8 +13,9 @@ module DynamicImage
|
|
13
13
|
options = {
|
14
14
|
path: "#{resource_name}/:digest(/:size)",
|
15
15
|
constraints: { size: /\d+x\d+/ },
|
16
|
-
only: [
|
16
|
+
only: %i[show]
|
17
17
|
}.merge(options)
|
18
|
+
|
18
19
|
resources resource_name, options do
|
19
20
|
get :uncropped, on: :member
|
20
21
|
get :original, on: :member
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamic_image
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Inge Jørgensen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dis
|
@@ -30,6 +30,20 @@ dependencies:
|
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 1.0.6
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: mini_exiftool
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.10'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.10'
|
33
47
|
- !ruby/object:Gem::Dependency
|
34
48
|
name: mini_magick
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,6 +92,20 @@ dependencies:
|
|
78
92
|
- - ">="
|
79
93
|
- !ruby/object:Gem::Version
|
80
94
|
version: 2.2.1
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rails-controller-testing
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
81
109
|
- !ruby/object:Gem::Dependency
|
82
110
|
name: rspec-rails
|
83
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,6 +120,20 @@ dependencies:
|
|
92
120
|
- - "~>"
|
93
121
|
- !ruby/object:Gem::Version
|
94
122
|
version: 3.7.0
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: simplecov
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: 0.17.1
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: 0.17.1
|
95
137
|
- !ruby/object:Gem::Dependency
|
96
138
|
name: sqlite3
|
97
139
|
requirement: !ruby/object:Gem::Requirement
|
@@ -127,6 +169,8 @@ files:
|
|
127
169
|
- lib/dynamic_image/helper.rb
|
128
170
|
- lib/dynamic_image/image_reader.rb
|
129
171
|
- lib/dynamic_image/image_sizing.rb
|
172
|
+
- lib/dynamic_image/jobs.rb
|
173
|
+
- lib/dynamic_image/jobs/create_variant.rb
|
130
174
|
- lib/dynamic_image/metadata.rb
|
131
175
|
- lib/dynamic_image/model.rb
|
132
176
|
- lib/dynamic_image/model/dimensions.rb
|
@@ -134,6 +178,7 @@ files:
|
|
134
178
|
- lib/dynamic_image/model/validations.rb
|
135
179
|
- lib/dynamic_image/model/variants.rb
|
136
180
|
- lib/dynamic_image/processed_image.rb
|
181
|
+
- lib/dynamic_image/profiles/sRGB_ICC_v4_Appearance.icc
|
137
182
|
- lib/dynamic_image/routing.rb
|
138
183
|
- lib/dynamic_image/templates/show.html.erb
|
139
184
|
- lib/dynamic_image/version.rb
|
@@ -142,7 +187,7 @@ homepage: https://github.com/elektronaut/dynamic_image
|
|
142
187
|
licenses:
|
143
188
|
- MIT
|
144
189
|
metadata: {}
|
145
|
-
post_install_message:
|
190
|
+
post_install_message:
|
146
191
|
rdoc_options: []
|
147
192
|
require_paths:
|
148
193
|
- lib
|
@@ -158,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
203
|
version: '0'
|
159
204
|
requirements: []
|
160
205
|
rubygems_version: 3.1.2
|
161
|
-
signing_key:
|
206
|
+
signing_key:
|
162
207
|
specification_version: 4
|
163
208
|
summary: Rails plugin that simplifies image uploading and processing
|
164
209
|
test_files: []
|