image_vise 0.2.1 → 0.2.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.travis.yml +13 -0
  4. data/DEVELOPMENT.md +111 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +29 -0
  7. data/README.md +213 -0
  8. data/Rakefile +6 -0
  9. data/SECURITY.md +57 -0
  10. data/examples/config.ru +17 -0
  11. data/examples/custom_image_operator.rb +27 -0
  12. data/examples/error_handline_appsignal.rb +23 -0
  13. data/examples/error_handling_sentry.rb +25 -0
  14. data/image_vise.gemspec +43 -0
  15. data/lib/image_vise/fetchers/fetcher_file.rb +27 -0
  16. data/lib/image_vise/fetchers/fetcher_http.rb +42 -0
  17. data/lib/image_vise/file_response.rb +22 -0
  18. data/lib/image_vise/image_request.rb +70 -0
  19. data/lib/image_vise/operators/auto_orient.rb +10 -0
  20. data/lib/image_vise/operators/background_fill.rb +18 -0
  21. data/lib/image_vise/operators/crop.rb +32 -0
  22. data/lib/image_vise/operators/ellipse_stencil.rb +70 -0
  23. data/lib/image_vise/operators/fit_crop.rb +33 -0
  24. data/lib/image_vise/operators/force_jpg_out.rb +17 -0
  25. data/lib/image_vise/operators/geom.rb +16 -0
  26. data/lib/image_vise/operators/sRGB_v4_ICC_preference_displayclass.icc +0 -0
  27. data/lib/image_vise/operators/sharpen.rb +21 -0
  28. data/lib/image_vise/operators/srgb.rb +30 -0
  29. data/lib/image_vise/operators/strip_metadata.rb +10 -0
  30. data/lib/image_vise/pipeline.rb +64 -0
  31. data/lib/image_vise/render_engine.rb +298 -0
  32. data/lib/image_vise/version.rb +3 -0
  33. data/lib/image_vise/writers/auto_writer.rb +23 -0
  34. data/lib/image_vise/writers/jpeg_writer.rb +9 -0
  35. data/lib/image_vise.rb +177 -0
  36. data/spec/image_vise/auto_orient_spec.rb +10 -0
  37. data/spec/image_vise/background_fill_spec.rb +39 -0
  38. data/spec/image_vise/crop_spec.rb +20 -0
  39. data/spec/image_vise/ellipse_stencil_spec.rb +18 -0
  40. data/spec/image_vise/fetcher_file_spec.rb +48 -0
  41. data/spec/image_vise/fetcher_http_spec.rb +44 -0
  42. data/spec/image_vise/file_response_spec.rb +45 -0
  43. data/spec/image_vise/fit_crop_spec.rb +20 -0
  44. data/spec/image_vise/force_jpg_out_spec.rb +36 -0
  45. data/spec/image_vise/geom_spec.rb +33 -0
  46. data/spec/image_vise/image_request_spec.rb +62 -0
  47. data/spec/image_vise/pipeline_spec.rb +72 -0
  48. data/spec/image_vise/render_engine_spec.rb +336 -0
  49. data/spec/image_vise/sharpen_spec.rb +17 -0
  50. data/spec/image_vise/srgb_spec.rb +23 -0
  51. data/spec/image_vise/strip_metadata_spec.rb +14 -0
  52. data/spec/image_vise/writers/auto_writer_spec.rb +25 -0
  53. data/spec/image_vise/writers/jpeg_writer_spec.rb +32 -0
  54. data/spec/image_vise_spec.rb +110 -0
  55. data/spec/layers-with-blending.psd +0 -0
  56. data/spec/spec_helper.rb +112 -0
  57. data/spec/test_server.rb +61 -0
  58. data/spec/waterside_magic_hour.jpg +0 -0
  59. data/spec/waterside_magic_hour.psd +0 -0
  60. data/spec/waterside_magic_hour_adobergb.jpg +0 -0
  61. data/spec/waterside_magic_hour_gray.tif +0 -0
  62. data/spec/waterside_magic_hour_transp.png +0 -0
  63. metadata +63 -2
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageVise::SRGB do
4
+ it 'applies the profile, creating a perceptible difference with the original' do
5
+ # This test will function only if you have RMagick with LCMS2 support
6
+ # built-in. If you do, the two images will look _very_ much like one
7
+ # another.
8
+ #
9
+ # If you don't, the images will look remarkably different
10
+ # (the AdobeRGB version has color values that match AdobeRGB
11
+ # primaries, and will render diffrently in pretty much any
12
+ # viewer).
13
+ image = Magick::Image.read(test_image_adobergb_path).first
14
+ subject.apply!(image)
15
+ image.strip!
16
+ examine_image(image, "from-adobergb-SHOULD-LOOK-IDENTICAL")
17
+
18
+ image = Magick::Image.read(test_image_path).first
19
+ subject.apply!(image)
20
+ image.strip!
21
+ examine_image(image, "from-srgb-SHOULD-LOOK-IDENTICAL")
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageVise::StripMetadata do
4
+ it 'applies the strip! method to the image' do
5
+ image = Magick::Image.read(test_image_path).first
6
+ expect(image).to receive(:strip!).and_call_original
7
+ described_class.new.apply!(image)
8
+ end
9
+
10
+ it 'is registered with the operator registry' do
11
+ op = ImageVise.operator_from('strip_metadata')
12
+ expect(op).to eq(described_class)
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageVise::AutoWriter do
4
+ it 'writes out a file with alpha as a PNG' do
5
+ sample_image = Magick::Image.read(test_image_png_transparency).first
6
+ tf = Tempfile.new 'outt'
7
+ subject.write_image!(sample_image, metadata=nil, tf.path)
8
+
9
+ tf.rewind
10
+ expect(tf.size).not_to be_zero
11
+ fmt = MagicBytes.read_and_detect(tf)
12
+ expect(fmt.ext).to eq('png')
13
+ end
14
+
15
+ it 'writes out a file without alpha as JPEG' do
16
+ sample_image = Magick::Image.read(test_image_path_tif).first
17
+ tf = Tempfile.new 'outt'
18
+ subject.write_image!(sample_image, metadata=nil, tf.path)
19
+
20
+ tf.rewind
21
+ expect(tf.size).not_to be_zero
22
+ fmt = MagicBytes.read_and_detect(tf)
23
+ expect(fmt.ext).to eq('jpg')
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageVise::JPGWriter do
4
+ it 'writes out a file with alpha as a JPEG regardless' do
5
+ sample_image = Magick::Image.read(test_image_png_transparency).first
6
+ tf = Tempfile.new 'outt'
7
+
8
+ described_class.new(quality: 75).write_image!(sample_image, metadata=nil, tf.path)
9
+
10
+ tf.rewind
11
+ expect(tf.size).not_to be_zero
12
+ fmt = MagicBytes.read_and_detect(tf)
13
+ expect(fmt.ext).to eq('jpg')
14
+ end
15
+
16
+ it 'honors the JPEG compression quality setting, producing a smaller file with smaller quality' do
17
+ sample_image = Magick::Image.read(test_image_path_tif).first
18
+
19
+ tf_hi = Tempfile.new 'hi'
20
+ tf_lo = Tempfile.new 'lo'
21
+
22
+ described_class.new(quality: 50).write_image!(sample_image, metadata=nil, tf_hi.path)
23
+ described_class.new(quality: 10).write_image!(sample_image, metadata=nil, tf_lo.path)
24
+
25
+ tf_hi.rewind
26
+ tf_lo.rewind
27
+
28
+ expect(tf_hi.size).not_to be_zero
29
+ expect(tf_lo.size).not_to be_zero
30
+ expect(tf_lo.size).to be < tf_hi.size
31
+ end
32
+ end
@@ -0,0 +1,110 @@
1
+ require_relative 'spec_helper'
2
+ require 'rack/test'
3
+
4
+ describe ImageVise do
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ described_class.new
9
+ end
10
+
11
+ context 'ImageVise.allowed_hosts' do
12
+ it 'returns the allowed hosts and is empty by default' do
13
+ expect(described_class.allowed_hosts).to be_empty
14
+ end
15
+
16
+ it 'allows add_allowed_host! and reset_allowed_hosts!' do
17
+ described_class.add_allowed_host!('www.imageboard.im')
18
+ expect(described_class.allowed_hosts).to include('www.imageboard.im')
19
+ described_class.reset_allowed_hosts!
20
+ expect(described_class.allowed_hosts).not_to include('www.imageboard.im')
21
+ end
22
+ end
23
+
24
+ context 'ImageVise.secret_keys' do
25
+ it 'raises when asked for a key and no keys has been set' do
26
+ expect {
27
+ described_class.secret_keys
28
+ }.to raise_error("No keys set, add a key using `ImageVise.add_secret_key!(key)'")
29
+ end
30
+
31
+ it 'allows add_secret_key!(key) and reset_secret_keys!' do
32
+ described_class.add_secret_key!('l33t')
33
+ expect(described_class.secret_keys).to include('l33t')
34
+ described_class.reset_secret_keys!
35
+ expect {
36
+ expect(described_class.secret_keys)
37
+ }.to raise_error(/add a key using/)
38
+ end
39
+ end
40
+
41
+ describe 'ImageVise.new.call' do
42
+ it 'instantiates a new app and performs call() on it' do
43
+ expect_any_instance_of(ImageVise::RenderEngine).to receive(:call).with(:mock_env) { :yes }
44
+ ImageVise.new.call(:mock_env)
45
+ end
46
+ end
47
+
48
+ describe 'ImageVise.call' do
49
+ it 'instantiates a new app and performs call() on it' do
50
+ expect_any_instance_of(ImageVise::RenderEngine).to receive(:call).with(:mock_env) { :yes }
51
+ ImageVise.call(:mock_env)
52
+ end
53
+ end
54
+
55
+ describe '.image_params' do
56
+ it 'generates a Hash with paremeters for processing the resized image' do
57
+ params = ImageVise.image_params(src_url: 'http://host.com/image.jpg', secret: 'l33t') do |pipe|
58
+ pipe.fit_crop width: 128, height: 256, gravity: 'c'
59
+ end
60
+ expect(params).to be_kind_of(Hash)
61
+ expect(params[:q]).not_to be_empty
62
+ expect(params[:sig]).not_to be_empty
63
+ end
64
+ end
65
+
66
+ describe 'methods dealing with fetchers' do
67
+ it 'returns the fetchers for the default schemes' do
68
+ http = ImageVise.fetcher_for('http')
69
+ expect(http).to respond_to(:fetch_uri_to_tempfile)
70
+ file = ImageVise.fetcher_for('file')
71
+ expect(http).to respond_to(:fetch_uri_to_tempfile)
72
+
73
+ expect {
74
+ ImageVise.fetcher_for('undernet')
75
+ }.to raise_error(/No fetcher registered/)
76
+ end
77
+ end
78
+
79
+ describe '.image_path' do
80
+ it 'returns the path to the image within the application' do
81
+ path = ImageVise.image_path(src_url: 'file://tmp/img.jpg', secret: 'a') do |p|
82
+ p.ellipse_stencil
83
+ end
84
+ expect(path).to start_with('/')
85
+ end
86
+ end
87
+
88
+ describe 'methods dealing with the operator list' do
89
+ it 'have the basic operators already set up' do
90
+ oplist = ImageVise.defined_operator_names
91
+ expect(oplist).to include('sharpen')
92
+ expect(oplist).to include('crop')
93
+ end
94
+
95
+ it 'allows an operator to be added and retrieved' do
96
+ class CustomOp; end
97
+ ImageVise.add_operator 'custom_op', CustomOp
98
+ expect(ImageVise.operator_from(:custom_op)).to eq(CustomOp)
99
+ expect(ImageVise.operator_name_for(CustomOp.new)).to eq('custom_op')
100
+ expect(ImageVise.defined_operator_names).to include('custom_op')
101
+ end
102
+
103
+ it 'raises an exception when an operator key is requested that does not exist' do
104
+ class UnknownOp; end
105
+ expect {
106
+ ImageVise.operator_name_for(UnknownOp.new)
107
+ }.to raise_error(/not registered using ImageVise/)
108
+ end
109
+ end
110
+ end
Binary file
@@ -0,0 +1,112 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ require 'tmpdir'
5
+ require 'securerandom'
6
+
7
+ require 'addressable/uri'
8
+ require 'strenv'
9
+ require_relative 'test_server'
10
+
11
+ TEST_RENDERS_DIR = Dir.mktmpdir
12
+
13
+ module Examine
14
+ def examine_image(magick_image, name_tag = 'test-img')
15
+ # When doing TDD, waiting for stuff to open is a drag - allow
16
+ # it to be squelched using 2 envvars. Also viewing images
17
+ # makes no sense on CI unless we bother with artifacts.
18
+ # The first one is what Gitlab-CI sets for us.
19
+ return if ENV.key?("CI_BUILD_ID")
20
+ return if ENV.key?("SKIP_INTERACTIVE")
21
+
22
+ Dir.mkdir(TEST_RENDERS_DIR) unless File.exist?(TEST_RENDERS_DIR)
23
+ path = File.join(TEST_RENDERS_DIR, name_tag + '.png')
24
+ magick_image.format = 'png'
25
+ magick_image.write(path)
26
+ `open #{path}`
27
+ end
28
+
29
+ def examine_image_from_string(string)
30
+ # When doing TDD, waiting for stuff to open is a drag - allow
31
+ # it to be squelched using 2 envvars. Also viewing images
32
+ # makes no sense on CI unless we bother with artifacts.
33
+ # The first one is what Gitlab-CI sets for us.
34
+ return if ENV.key?("CI_BUILD_ID")
35
+ return if ENV.key?("SKIP_INTERACTIVE")
36
+
37
+ Dir.mkdir(TEST_RENDERS_DIR) unless File.exist?(TEST_RENDERS_DIR)
38
+ random_name = 'test-image-%s' % SecureRandom.hex(3)
39
+ path = File.join(TEST_RENDERS_DIR, random_name)
40
+ File.open(path, 'wb'){|f| f << string }
41
+ `open #{path}`
42
+ end
43
+ end
44
+
45
+ require 'simplecov'
46
+ SimpleCov.start do
47
+ add_filter "/spec/"
48
+ end
49
+
50
+ require_relative '../lib/image_vise'
51
+
52
+ RSpec.configure do | config |
53
+ config.order = 'random'
54
+ config.include Examine
55
+ config.before :suite do
56
+ TestServer.start(nil, ssl=false, port=9001)
57
+ end
58
+
59
+ config.after :each do
60
+ ImageVise.reset_allowed_hosts!
61
+ ImageVise.reset_secret_keys!
62
+ end
63
+
64
+ config.after :suite do
65
+ sleep 2
66
+ FileUtils.rm_rf(TEST_RENDERS_DIR)
67
+ end
68
+
69
+ def test_image_path
70
+ File.expand_path(__dir__ + '/waterside_magic_hour.jpg')
71
+ end
72
+
73
+ def test_image_path_psd
74
+ File.expand_path(__dir__ + '/waterside_magic_hour.psd')
75
+ end
76
+
77
+ def test_image_path_tif
78
+ File.expand_path(__dir__ + '/waterside_magic_hour_gray.tif')
79
+ end
80
+
81
+ def test_image_adobergb_path
82
+ File.expand_path(__dir__ + '/waterside_magic_hour_adobergb.jpg')
83
+ end
84
+
85
+ def test_image_png_transparency
86
+ File.expand_path(__dir__ + '/waterside_magic_hour_transp.png')
87
+ end
88
+
89
+ def public_url
90
+ 'http://localhost:9001/waterside_magic_hour.jpg'
91
+ end
92
+
93
+ def public_url_psd
94
+ 'http://localhost:9001/waterside_magic_hour.psd'
95
+ end
96
+
97
+ def public_url_psd_multilayer
98
+ 'http://localhost:9001/layers-with-blending.psd'
99
+ end
100
+
101
+ def public_url_tif
102
+ 'http://localhost:9001/waterside_magic_hour_gray.tif'
103
+ end
104
+
105
+ def public_url_png_transparency
106
+ 'http://localhost:9001/waterside_magic_hour_transp.png'
107
+ end
108
+
109
+ config.around :each do |e|
110
+ STRICT_ENV.with_protected_env { e.run }
111
+ end
112
+ end
@@ -0,0 +1,61 @@
1
+ require 'webrick'
2
+ include WEBrick
3
+
4
+ class ForbiddenServlet < HTTPServlet::AbstractServlet
5
+ def do_GET(req,res)
6
+ res['Content-Type'] = "text/plain"
7
+ res.status = 403
8
+ end
9
+ end
10
+
11
+ class TestServer
12
+ def self.start( log_file = nil, ssl = false, port = 9001 )
13
+ new(log_file, ssl, port).start
14
+ end
15
+
16
+ def initialize( log_file = nil, ssl = false, port = 9001 )
17
+ log_file ||= StringIO.new
18
+ log = WEBrick::Log.new(log_file)
19
+
20
+ options = {
21
+ :Port => port,
22
+ :Logger => log,
23
+ :AccessLog => [
24
+ [ log, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
25
+ [ log, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
26
+ ],
27
+ :DocumentRoot => File.expand_path(__dir__),
28
+ }
29
+
30
+ if ssl
31
+ options[:SSLEnable] = true
32
+ options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.open("spec/certs/cacert.pem").read)
33
+ options[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.open("spec/certs/privkey.pem").read)
34
+ options[:SSLCertName] = [ ["CN", WEBrick::Utils::getservername ] ]
35
+ end
36
+
37
+ @server = WEBrick::HTTPServer.new(options)
38
+ @server.mount("/forbidden", ForbiddenServlet)
39
+ end
40
+
41
+ def start
42
+ trap('INT') {
43
+ begin
44
+ @server.shutdown unless @server.nil?
45
+ rescue Object => e
46
+ $stderr.puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
47
+ end
48
+ }
49
+
50
+ @thread = Thread.new { @server.start }
51
+ Thread.pass
52
+ self
53
+ end
54
+
55
+ def join
56
+ if defined? @thread and @thread
57
+ @thread.join
58
+ end
59
+ self
60
+ end
61
+ end
Binary file
Binary file
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_vise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -184,7 +184,68 @@ email:
184
184
  executables: []
185
185
  extensions: []
186
186
  extra_rdoc_files: []
187
- files: []
187
+ files:
188
+ - ".gitignore"
189
+ - ".travis.yml"
190
+ - DEVELOPMENT.md
191
+ - Gemfile
192
+ - LICENSE.txt
193
+ - README.md
194
+ - Rakefile
195
+ - SECURITY.md
196
+ - examples/config.ru
197
+ - examples/custom_image_operator.rb
198
+ - examples/error_handline_appsignal.rb
199
+ - examples/error_handling_sentry.rb
200
+ - image_vise.gemspec
201
+ - lib/image_vise.rb
202
+ - lib/image_vise/fetchers/fetcher_file.rb
203
+ - lib/image_vise/fetchers/fetcher_http.rb
204
+ - lib/image_vise/file_response.rb
205
+ - lib/image_vise/image_request.rb
206
+ - lib/image_vise/operators/auto_orient.rb
207
+ - lib/image_vise/operators/background_fill.rb
208
+ - lib/image_vise/operators/crop.rb
209
+ - lib/image_vise/operators/ellipse_stencil.rb
210
+ - lib/image_vise/operators/fit_crop.rb
211
+ - lib/image_vise/operators/force_jpg_out.rb
212
+ - lib/image_vise/operators/geom.rb
213
+ - lib/image_vise/operators/sRGB_v4_ICC_preference_displayclass.icc
214
+ - lib/image_vise/operators/sharpen.rb
215
+ - lib/image_vise/operators/srgb.rb
216
+ - lib/image_vise/operators/strip_metadata.rb
217
+ - lib/image_vise/pipeline.rb
218
+ - lib/image_vise/render_engine.rb
219
+ - lib/image_vise/version.rb
220
+ - lib/image_vise/writers/auto_writer.rb
221
+ - lib/image_vise/writers/jpeg_writer.rb
222
+ - spec/image_vise/auto_orient_spec.rb
223
+ - spec/image_vise/background_fill_spec.rb
224
+ - spec/image_vise/crop_spec.rb
225
+ - spec/image_vise/ellipse_stencil_spec.rb
226
+ - spec/image_vise/fetcher_file_spec.rb
227
+ - spec/image_vise/fetcher_http_spec.rb
228
+ - spec/image_vise/file_response_spec.rb
229
+ - spec/image_vise/fit_crop_spec.rb
230
+ - spec/image_vise/force_jpg_out_spec.rb
231
+ - spec/image_vise/geom_spec.rb
232
+ - spec/image_vise/image_request_spec.rb
233
+ - spec/image_vise/pipeline_spec.rb
234
+ - spec/image_vise/render_engine_spec.rb
235
+ - spec/image_vise/sharpen_spec.rb
236
+ - spec/image_vise/srgb_spec.rb
237
+ - spec/image_vise/strip_metadata_spec.rb
238
+ - spec/image_vise/writers/auto_writer_spec.rb
239
+ - spec/image_vise/writers/jpeg_writer_spec.rb
240
+ - spec/image_vise_spec.rb
241
+ - spec/layers-with-blending.psd
242
+ - spec/spec_helper.rb
243
+ - spec/test_server.rb
244
+ - spec/waterside_magic_hour.jpg
245
+ - spec/waterside_magic_hour.psd
246
+ - spec/waterside_magic_hour_adobergb.jpg
247
+ - spec/waterside_magic_hour_gray.tif
248
+ - spec/waterside_magic_hour_transp.png
188
249
  homepage: https://github.com/WeTransfer/image_vise
189
250
  licenses:
190
251
  - MIT