dynamic_image 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +4 -4
- data/lib/dynamic_image.rb +15 -15
- data/lib/dynamic_image/controller.rb +4 -4
- data/lib/dynamic_image/digest_verifier.rb +3 -3
- data/lib/dynamic_image/errors.rb +1 -0
- data/lib/dynamic_image/helper.rb +3 -3
- data/lib/dynamic_image/image_reader.rb +24 -9
- data/lib/dynamic_image/metadata.rb +3 -3
- data/lib/dynamic_image/model.rb +9 -7
- data/lib/dynamic_image/model/transformations.rb +71 -0
- data/lib/dynamic_image/model/validations.rb +1 -1
- data/lib/dynamic_image/processed_image.rb +12 -12
- data/lib/dynamic_image/railtie.rb +2 -2
- data/lib/dynamic_image/version.rb +1 -1
- data/lib/rails/generators/dynamic_image/resource/resource_generator.rb +12 -13
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1600a4e263630c8a9936e6f8309add967434b81
|
4
|
+
data.tar.gz: 1ad5ff2c9b894a0fd774797cc64395ed72d7b556
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f60bf400b04b6c8b9b7fe9d59ea967796bd58171fbf258ba326349e128bd35bd50cbe49b779a82e6d7d3db0f70636ff7808d79dae2a1f0d1a4f15b96516bf00
|
7
|
+
data.tar.gz: 74063c221b4948e5121f0be2f5b6a8244ed4bb25b9c3a44609cfcab902e648ccfee69697b060bdda5186183061ad950f2e71b9e382d674939d7b1367953b7f20
|
data/Rakefile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
APP_RAKEFILE =
|
5
|
-
load
|
4
|
+
APP_RAKEFILE = "spec/internal/Rakefile".freeze
|
5
|
+
load "rails/tasks/engine.rake"
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new
|
8
8
|
|
data/lib/dynamic_image.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "mini_magick"
|
4
|
+
require "dis"
|
5
|
+
require "vector2d"
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
7
|
+
require "dynamic_image/belongs_to"
|
8
|
+
require "dynamic_image/controller"
|
9
|
+
require "dynamic_image/digest_verifier"
|
10
|
+
require "dynamic_image/errors"
|
11
|
+
require "dynamic_image/helper"
|
12
|
+
require "dynamic_image/image_reader"
|
13
|
+
require "dynamic_image/image_sizing"
|
14
|
+
require "dynamic_image/metadata"
|
15
|
+
require "dynamic_image/model"
|
16
|
+
require "dynamic_image/processed_image"
|
17
|
+
require "dynamic_image/railtie"
|
18
|
+
require "dynamic_image/routing"
|
19
19
|
|
20
20
|
module DynamicImage
|
21
21
|
cattr_accessor :digest_verifier
|
@@ -35,7 +35,7 @@ module DynamicImage
|
|
35
35
|
send_data(
|
36
36
|
@record.data,
|
37
37
|
content_type: @record.content_type,
|
38
|
-
disposition:
|
38
|
+
disposition: "inline"
|
39
39
|
)
|
40
40
|
end
|
41
41
|
end
|
@@ -60,7 +60,7 @@ module DynamicImage
|
|
60
60
|
return unless stale?(@record)
|
61
61
|
respond_to do |format|
|
62
62
|
format.html do
|
63
|
-
render(file: File.join(File.dirname(__FILE__),
|
63
|
+
render(file: File.join(File.dirname(__FILE__), "templates/show"),
|
64
64
|
layout: false, locals: { options: options })
|
65
65
|
end
|
66
66
|
format.any(:gif, :jpeg, :png, :tiff) do
|
@@ -77,14 +77,14 @@ module DynamicImage
|
|
77
77
|
send_data(
|
78
78
|
image.cropped_and_resized(requested_size),
|
79
79
|
content_type: image.content_type,
|
80
|
-
disposition:
|
80
|
+
disposition: "inline"
|
81
81
|
)
|
82
82
|
end
|
83
83
|
|
84
84
|
def verify_signed_params
|
85
85
|
key = [:action, :id, :size].map do |k|
|
86
86
|
k == :id ? params.require(k).to_i : params.require(k)
|
87
|
-
end.join(
|
87
|
+
end.join("-")
|
88
88
|
DynamicImage.digest_verifier.verify(key, params[:digest])
|
89
89
|
end
|
90
90
|
end
|
@@ -20,7 +20,7 @@ module DynamicImage
|
|
20
20
|
class DigestVerifier
|
21
21
|
def initialize(secret, options = {})
|
22
22
|
@secret = secret
|
23
|
-
@digest = options[:digest] ||
|
23
|
+
@digest = options[:digest] || "SHA1"
|
24
24
|
end
|
25
25
|
|
26
26
|
# Generates a digest for a string.
|
@@ -47,11 +47,11 @@ module DynamicImage
|
|
47
47
|
|
48
48
|
res = 0
|
49
49
|
b.each_byte { |byte| res |= byte ^ l.shift }
|
50
|
-
res
|
50
|
+
res.zero?
|
51
51
|
end
|
52
52
|
|
53
53
|
def generate_digest(data)
|
54
|
-
require
|
54
|
+
require "openssl" unless defined?(OpenSSL)
|
55
55
|
OpenSSL::HMAC.hexdigest(
|
56
56
|
OpenSSL::Digest.const_get(@digest).new,
|
57
57
|
@secret,
|
data/lib/dynamic_image/errors.rb
CHANGED
@@ -7,5 +7,6 @@ module DynamicImage
|
|
7
7
|
class InvalidHeader < DynamicImage::Errors::Error; end
|
8
8
|
class InvalidSignature < DynamicImage::Errors::Error; end
|
9
9
|
class InvalidSizeOptions < DynamicImage::Errors::Error; end
|
10
|
+
class InvalidTransformation < DynamicImage::Errors::Error; end
|
10
11
|
end
|
11
12
|
end
|
data/lib/dynamic_image/helper.rb
CHANGED
@@ -115,7 +115,7 @@ module DynamicImage
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def dynamic_image_digest(record, action, size = nil)
|
118
|
-
key = [action ||
|
118
|
+
key = [action || "show", record.id, size].compact.join("-")
|
119
119
|
DynamicImage.digest_verifier.generate(key)
|
120
120
|
end
|
121
121
|
|
@@ -152,9 +152,9 @@ module DynamicImage
|
|
152
152
|
action = options[:action].try(:to_s)
|
153
153
|
size_opts = options.extract!(:size, :crop, :upscale)
|
154
154
|
if size_opts[:size]
|
155
|
-
image_sizing(record, size_opts, (action ==
|
155
|
+
image_sizing(record, size_opts, (action == "uncropped"))
|
156
156
|
else
|
157
|
-
(action ==
|
157
|
+
(action == "original" ? record.real_size : record.size).floor.to_s
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
@@ -27,21 +27,36 @@ module DynamicImage
|
|
27
27
|
@file_header ||= StringIO.new(@data, "rb").read(10)
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def gif_magic_bytes
|
31
31
|
[
|
32
|
-
# GIF
|
33
32
|
"\x47\x49\x46\x38\x37\x61".force_encoding("binary"),
|
34
|
-
"\x47\x49\x46\x38\x39\x61".force_encoding("binary")
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
"\x47\x49\x46\x38\x39\x61".force_encoding("binary")
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def jpeg_magic_bytes
|
38
|
+
[
|
38
39
|
"\xff\xd8\xff\xdb".force_encoding("binary"),
|
39
40
|
Regexp.new("\xff\xd8\xff\xe0(.*){2}JFIF".force_encoding("binary")),
|
40
41
|
Regexp.new("\xff\xd8\xff\xe1(.*){2}Exif".force_encoding("binary")),
|
41
|
-
"\xff\xd8\xff\xee\x00\x0e".force_encoding("binary")
|
42
|
-
|
42
|
+
"\xff\xd8\xff\xee\x00\x0e".force_encoding("binary") # Adobe JPEG
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
def magic_bytes
|
47
|
+
gif_magic_bytes + png_magic_bytes + jpeg_magic_bytes + tiff_magic_bytes
|
48
|
+
end
|
49
|
+
|
50
|
+
def png_magic_bytes
|
51
|
+
[
|
52
|
+
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a".force_encoding("binary")
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
def tiff_magic_bytes
|
57
|
+
[
|
43
58
|
"\x49\x49\x2a\x00".force_encoding("binary"),
|
44
|
-
"\x4d\x4d\x00\x2a".force_encoding("binary")
|
59
|
+
"\x4d\x4d\x00\x2a".force_encoding("binary")
|
45
60
|
]
|
46
61
|
end
|
47
62
|
end
|
data/lib/dynamic_image/model.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dynamic_image/model/dimensions"
|
4
|
+
require "dynamic_image/model/transformations"
|
5
|
+
require "dynamic_image/model/validations"
|
5
6
|
|
6
7
|
module DynamicImage
|
7
8
|
# = DynamicImage Model
|
@@ -62,6 +63,7 @@ module DynamicImage
|
|
62
63
|
extend ActiveSupport::Concern
|
63
64
|
include Dis::Model
|
64
65
|
include DynamicImage::Model::Dimensions
|
66
|
+
include DynamicImage::Model::Transformations
|
65
67
|
include DynamicImage::Model::Validations
|
66
68
|
|
67
69
|
included do
|
@@ -70,17 +72,17 @@ module DynamicImage
|
|
70
72
|
|
71
73
|
# Returns true if the image is in the CMYK colorspace
|
72
74
|
def cmyk?
|
73
|
-
colorspace ==
|
75
|
+
colorspace == "cmyk"
|
74
76
|
end
|
75
77
|
|
76
78
|
# Returns true if the image is in the grayscale colorspace
|
77
79
|
def gray?
|
78
|
-
colorspace ==
|
80
|
+
colorspace == "gray"
|
79
81
|
end
|
80
82
|
|
81
83
|
# Returns true if the image is in the RGB colorspace
|
82
84
|
def rgb?
|
83
|
-
colorspace ==
|
85
|
+
colorspace == "rgb"
|
84
86
|
end
|
85
87
|
|
86
88
|
# Finds a web safe content type. GIF, JPEG and PNG images are allowed,
|
@@ -89,14 +91,14 @@ module DynamicImage
|
|
89
91
|
if safe_content_types.include?(content_type)
|
90
92
|
content_type
|
91
93
|
else
|
92
|
-
|
94
|
+
"image/jpeg"
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
96
98
|
# Includes a timestamp fingerprint in the URL param, so
|
97
99
|
# that rendered images can be cached indefinitely.
|
98
100
|
def to_param
|
99
|
-
[id, updated_at.utc.to_s(cache_timestamp_format)].join(
|
101
|
+
[id, updated_at.utc.to_s(cache_timestamp_format)].join("-")
|
100
102
|
end
|
101
103
|
|
102
104
|
private
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module DynamicImage
|
4
|
+
module Model
|
5
|
+
# = DynamicImage Model Transformations
|
6
|
+
#
|
7
|
+
module Transformations
|
8
|
+
# Rotates the image
|
9
|
+
def rotate(degrees = 90)
|
10
|
+
degrees = degrees.to_i % 360
|
11
|
+
|
12
|
+
return self if degrees.zero?
|
13
|
+
|
14
|
+
if (degrees % 90).nonzero?
|
15
|
+
raise DynamicImage::Errors::InvalidTransformation,
|
16
|
+
"angle must be a multiple of 90 degrees"
|
17
|
+
end
|
18
|
+
|
19
|
+
transform_image do |image|
|
20
|
+
image.rotate(degrees)
|
21
|
+
rotate_dimensions(real_size.x, real_size.y, degrees)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def rotate_dimensions(width, height, degrees)
|
28
|
+
(degrees / 90).times do
|
29
|
+
width, height = height, width
|
30
|
+
|
31
|
+
self.real_width = width
|
32
|
+
self.real_height = height
|
33
|
+
|
34
|
+
self.crop_gravity_x, self.crop_gravity_y = rotated_crop_gravity(width)
|
35
|
+
|
36
|
+
next unless cropped?
|
37
|
+
|
38
|
+
self.crop_start_x, self.crop_start_y,
|
39
|
+
self.crop_width, self.crop_height = rotated_crop(width)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def rotated_crop(new_width)
|
44
|
+
return nil unless cropped?
|
45
|
+
[
|
46
|
+
new_width - (crop_start_y + crop_height),
|
47
|
+
crop_start_x,
|
48
|
+
crop_height,
|
49
|
+
crop_width
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
def rotated_crop_gravity(new_width)
|
54
|
+
return nil unless crop_gravity?
|
55
|
+
[new_width - crop_gravity_y, crop_gravity_x]
|
56
|
+
end
|
57
|
+
|
58
|
+
def processed_image
|
59
|
+
DynamicImage::ProcessedImage.new(self, uncropped: true)
|
60
|
+
end
|
61
|
+
|
62
|
+
def transform_image
|
63
|
+
read_image_metadata if data_changed?
|
64
|
+
self.data = processed_image.normalized do |image|
|
65
|
+
yield(image) if block_given?
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -80,7 +80,7 @@ module DynamicImage
|
|
80
80
|
def validate_crop_bounds
|
81
81
|
required_size = crop_start + crop_size
|
82
82
|
if required_size.x > real_size.x || required_size.y > real_size.y
|
83
|
-
errors.add(:crop_size,
|
83
|
+
errors.add(:crop_size, "is out of bounds")
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -10,7 +10,7 @@ module DynamicImage
|
|
10
10
|
@record = record
|
11
11
|
@uncropped = options[:uncropped] ? true : false
|
12
12
|
@format = options[:format].to_s.upcase if options[:format]
|
13
|
-
@format =
|
13
|
+
@format = "JPEG" if defined?(@format) && @format == "JPG"
|
14
14
|
end
|
15
15
|
|
16
16
|
# Returns the content type of the processed image.
|
@@ -62,7 +62,7 @@ module DynamicImage
|
|
62
62
|
process_data do |image|
|
63
63
|
image.combine_options do |combined|
|
64
64
|
combined.auto_orient
|
65
|
-
combined.colorspace(
|
65
|
+
combined.colorspace("sRGB") if needs_colorspace_conversion?
|
66
66
|
yield(combined) if block_given?
|
67
67
|
optimize(combined)
|
68
68
|
end
|
@@ -85,7 +85,7 @@ module DynamicImage
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def gif?
|
88
|
-
content_type ==
|
88
|
+
content_type == "image/gif"
|
89
89
|
end
|
90
90
|
|
91
91
|
def image_sizing
|
@@ -102,7 +102,7 @@ module DynamicImage
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def optimize(image)
|
105
|
-
image.layers
|
105
|
+
image.layers "optimize" if gif?
|
106
106
|
image.strip
|
107
107
|
end
|
108
108
|
|
@@ -118,14 +118,14 @@ module DynamicImage
|
|
118
118
|
|
119
119
|
def record_format
|
120
120
|
case record.content_type
|
121
|
-
when
|
122
|
-
|
123
|
-
when
|
124
|
-
|
125
|
-
when
|
126
|
-
|
127
|
-
when
|
128
|
-
|
121
|
+
when "image/png"
|
122
|
+
"PNG"
|
123
|
+
when "image/gif"
|
124
|
+
"GIF"
|
125
|
+
when "image/jpeg", "image/pjpeg"
|
126
|
+
"JPEG"
|
127
|
+
when "image/tiff"
|
128
|
+
"TIFF"
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module DynamicImage
|
4
4
|
class Railtie < ::Rails::Railtie
|
5
|
-
initializer
|
5
|
+
initializer "dynamic_image" do
|
6
6
|
ActionDispatch::Routing::Mapper.send :include, DynamicImage::Routing
|
7
7
|
|
8
8
|
config.after_initialize do |app|
|
9
|
-
secret = app.key_generator.generate_key(
|
9
|
+
secret = app.key_generator.generate_key("dynamic_image")
|
10
10
|
DynamicImage.digest_verifier = DynamicImage::DigestVerifier.new(secret)
|
11
11
|
end
|
12
12
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/rails/resource/resource_generator"
|
5
5
|
|
6
6
|
module DynamicImage
|
7
7
|
module Generators
|
8
8
|
class ResourceGenerator < Rails::Generators::ResourceGenerator
|
9
|
-
desc
|
9
|
+
desc "Creates a DynamicImage resource"
|
10
10
|
|
11
11
|
def initialize(args, *options)
|
12
12
|
super(inject_dynamic_image_attributes(args), *options)
|
@@ -14,10 +14,9 @@ module DynamicImage
|
|
14
14
|
|
15
15
|
def add_controller_extension
|
16
16
|
inject_into_file(
|
17
|
-
File.join(
|
18
|
-
|
19
|
-
|
20
|
-
"#{file_name.pluralize}_controller.rb"),
|
17
|
+
File.join("app/controllers",
|
18
|
+
class_path,
|
19
|
+
"#{file_name.pluralize}_controller.rb"),
|
21
20
|
after: "ApplicationController\n"
|
22
21
|
) do
|
23
22
|
" include DynamicImage::Controller\n\n private\n\n" \
|
@@ -27,7 +26,7 @@ module DynamicImage
|
|
27
26
|
|
28
27
|
def add_model_extension
|
29
28
|
inject_into_file(
|
30
|
-
File.join(
|
29
|
+
File.join("app/models", class_path, "#{file_name}.rb"),
|
31
30
|
after: "ActiveRecord::Base\n"
|
32
31
|
) do
|
33
32
|
" include DynamicImage::Model\n"
|
@@ -36,7 +35,7 @@ module DynamicImage
|
|
36
35
|
|
37
36
|
def alter_resource_routes
|
38
37
|
gsub_file(
|
39
|
-
File.join(
|
38
|
+
File.join("config", "routes.rb"),
|
40
39
|
" resources :#{file_name.pluralize}",
|
41
40
|
" image_resources :#{file_name.pluralize}"
|
42
41
|
)
|
@@ -54,13 +53,13 @@ module DynamicImage
|
|
54
53
|
|
55
54
|
def dynamic_image_attributes
|
56
55
|
%w(
|
57
|
-
content_hash:string
|
56
|
+
content_hash:string content_type:string
|
58
57
|
content_length:integer
|
59
58
|
filename:string
|
60
59
|
colorspace:string
|
61
|
-
real_width:integer
|
62
|
-
crop_width:integer
|
63
|
-
crop_start_x:integer
|
60
|
+
real_width:integer real_height:integer
|
61
|
+
crop_width:integer crop_height:integer
|
62
|
+
crop_start_x:integer crop_start_y:integer
|
64
63
|
crop_gravity_x:integer crop_gravity_y:integer
|
65
64
|
)
|
66
65
|
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.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Inge Jørgensen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -127,6 +127,7 @@ files:
|
|
127
127
|
- lib/dynamic_image/metadata.rb
|
128
128
|
- lib/dynamic_image/model.rb
|
129
129
|
- lib/dynamic_image/model/dimensions.rb
|
130
|
+
- lib/dynamic_image/model/transformations.rb
|
130
131
|
- lib/dynamic_image/model/validations.rb
|
131
132
|
- lib/dynamic_image/processed_image.rb
|
132
133
|
- lib/dynamic_image/railtie.rb
|