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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7183bc59ab57bc659f3d38e0c2bd1fa250689d1e4647dfcc68e0eddceb4c2260
4
- data.tar.gz: 882038f115a0e2d78684f02a19672138d8a68a3e36af0ac74d736a7d4056083a
3
+ metadata.gz: fd1d723cd97a91199630354e541d2a740aa41bae8ccb07554c1e723096a1b6dc
4
+ data.tar.gz: b4f5a8bf26b69da95543cb8885f5add2b72504ea88f83c4097bc1c25a13c622f
5
5
  SHA512:
6
- metadata.gz: c2a6a1f35e3458693ae0d0c5b9eaca4fd5ddcd70164e070f67f80236723befc57d677d0de673bf94afb0416458cde7bc04648f7bd512d5682984af6ccb4adeae
7
- data.tar.gz: b6ca975270f79fb9100aaeb1d2083f30adf7196c77783f663b741f84144c49bc6b42338ba220b050fca4637746f57328adff2d88f9fc9b48c829ff08d9157e72
6
+ metadata.gz: e2ba7e0be3c929f9da2b1c7311ae7a246341f081cc8057e8293417a8a73634ec5844d0ce7a691c3d5c771d99924b311c59a2a9b9bf2e2baae84675a3967a8614
7
+ data.tar.gz: '08cfc8124f2d6e2bd9d505315b1af7ed87e42ae4ff497a88fafd240e9594943721a6f72e7f196d5dffb1a43a1c8489e9b1b427e4042e149aedcf21a22f0506ba'
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  [![Version](https://img.shields.io/gem/v/dynamic_image.svg?style=flat)](https://rubygems.org/gems/dynamic_image)
2
- [![Build Status](https://travis-ci.org/elektronaut/dynamic_image.svg?branch=master)](https://travis-ci.org/elektronaut/dynamic_image)
2
+ ![Build](https://github.com/elektronaut/dynamic_image/workflows/Build/badge.svg)
3
3
  [![Code Climate](https://codeclimate.com/github/elektronaut/dynamic_image/badges/gpa.svg)](https://codeclimate.com/github/elektronaut/dynamic_image)
4
4
  [![Code Climate](https://codeclimate.com/github/elektronaut/dynamic_image/badges/coverage.svg)](https://codeclimate.com/github/elektronaut/dynamic_image)
5
5
  [![Inline docs](http://inch-ci.org/github/elektronaut/dynamic_image.svg)](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
 
@@ -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(DynamicImage::ProcessedImage.new(@record, options))
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(processed_image)
86
- send_data(
87
- processed_image.cropped_and_resized(requested_size),
88
- content_type: processed_image.content_type,
89
- disposition: "inline"
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
@@ -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.send :include, DynamicImage::Routing
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 ||= StringIO.new(@data, "rb").read(8)
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
@@ -149,8 +149,8 @@ module DynamicImage
149
149
  @uncropped
150
150
  end
151
151
 
152
- def vector(x, y)
153
- Vector2d.new(x, y)
152
+ def vector(width, height)
153
+ Vector2d.new(width, height)
154
154
  end
155
155
  end
156
156
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dynamic_image/jobs/create_variant"
@@ -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
@@ -82,8 +82,8 @@ module DynamicImage
82
82
 
83
83
  private
84
84
 
85
- def vector(x, y)
86
- Vector2d.new(x, y)
85
+ def vector(width, height)
86
+ Vector2d.new(width, height)
87
87
  end
88
88
  end
89
89
  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
- # * CMYK images are converted to sRGB
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.colorspace("sRGB") if needs_colorspace_conversion?
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
- return image unless gif?
89
+ gif? ? DynamicImage::ImageReader.new(image.coalesce.to_blob).read : image
90
+ end
74
91
 
75
- DynamicImage::ImageReader.new(image.coalesce.to_blob).read
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
- if record.cropped? || size != record.size
81
- image.crop(image_sizing.crop_geometry_string(size))
82
- image.resize(size)
83
- end
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 find_or_create_variant(size)
88
- record.variants.find_by(variant_params(size)) ||
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" || 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/png" => "PNG",
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
- height: size.y.round,
157
- crop_width: crop_size.x,
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
@@ -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: [:show]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicImage
4
- VERSION = "2.0.21"
4
+ VERSION = "2.1.0"
5
5
  end
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.21
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-04-04 00:00:00.000000000 Z
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: []