image_size 3.3.0 → 3.5.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.
@@ -10,6 +10,24 @@ require 'shellwords'
10
10
 
11
11
  require 'test_server'
12
12
 
13
+ RSpec.configure do |config|
14
+ config.order = :random
15
+ end
16
+
17
+ class RestrictedIO
18
+ def initialize(data)
19
+ @io = StringIO.new(data)
20
+ end
21
+
22
+ def read(length = nil)
23
+ @io.read(length)
24
+ end
25
+
26
+ def eof?
27
+ @io.eof?
28
+ end
29
+ end
30
+
13
31
  describe ImageSize do
14
32
  before :all do
15
33
  @server = TestServer.new
@@ -19,6 +37,19 @@ describe ImageSize do
19
37
  @server.finish
20
38
  end
21
39
 
40
+ def retry_on(exception_class)
41
+ attempt = 1
42
+ begin
43
+ yield
44
+ rescue exception_class => e
45
+ warn "Attempt #{attempt}: #{e.inspect}"
46
+ raise unless attempt < 3
47
+
48
+ attempt += 1
49
+ retry
50
+ end
51
+ end
52
+
22
53
  def supported_formats
23
54
  ImageSize.private_instance_methods.map{ |name| name[/\Asize_of_(.*)\z/, 1] }.compact.sort
24
55
  end
@@ -45,26 +76,34 @@ describe ImageSize do
45
76
  describe "for #{path}" do
46
77
  let(:name){ File.basename(path) }
47
78
  let(:attributes) do
48
- match = /(\d+)x(\d+)\.([^.]+)$/.match(name)
49
- width, height, format = match[1].to_i, match[2].to_i, match[3].to_sym if match
50
- size = format && [width, height]
79
+ if (match = /(\d+)x(\d+)\.([^.]+)$/.match(name))
80
+ width = match[1].to_i
81
+ height = match[2].to_i
82
+ format = match[3].to_sym
83
+ end
84
+ size = [width, height] if format
85
+ media_types = ImageSize::MEDIA_TYPES[format] || []
86
+ media_type = format && media_types.first.to_s
51
87
  {
52
- :format => format,
53
- :width => width,
54
- :height => height,
55
- :w => width,
56
- :h => height,
57
- :size => size,
88
+ format: format,
89
+ width: width,
90
+ height: height,
91
+ w: width,
92
+ h: height,
93
+ size: size,
94
+ media_type: media_type,
95
+ media_types: media_types,
96
+ byte_size: file_size,
58
97
  }
59
98
  end
60
- let(:file_data){ File.open(path, 'rb', &:read) }
99
+ let(:file_data){ File.binread(path) }
61
100
  let(:file_size){ file_data.length }
62
101
 
63
102
  before do
64
103
  max_file_size = 16_384
65
104
 
66
105
  if file_size > max_file_size
67
- raise "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})"
106
+ fail "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})"
68
107
  end
69
108
  end
70
109
 
@@ -95,6 +134,8 @@ describe ImageSize do
95
134
  end
96
135
 
97
136
  context 'given as unseekable IO' do
137
+ let(:attributes){ super().merge(byte_size: nil) }
138
+
98
139
  it 'gets format and dimensions' do
99
140
  IO.popen(%W[cat #{path}].shelljoin, 'rb') do |io|
100
141
  image_size = ImageSize.new(io)
@@ -104,6 +145,16 @@ describe ImageSize do
104
145
  end
105
146
  end
106
147
 
148
+ context 'given an object allowing only read and eof?' do
149
+ let(:attributes){ super().merge(byte_size: nil) }
150
+
151
+ it 'gets format and dimensions' do
152
+ io = RestrictedIO.new(file_data)
153
+ image_size = ImageSize.new(io)
154
+ expect(image_size).to have_attributes(attributes)
155
+ end
156
+ end
157
+
107
158
  context 'given as StringIO' do
108
159
  it 'gets format and dimensions' do
109
160
  io = StringIO.new(file_data)
@@ -143,52 +194,74 @@ describe ImageSize do
143
194
  end
144
195
 
145
196
  context 'fetching from webserver' do
146
- let(:file_url){ @server.base_url + path }
197
+ let(:url){ "#{@server.base_url}#{path}#{query}" }
198
+
199
+ before{ ImageSize.chunk_size = 64 }
200
+ after{ ImageSize.chunk_size = nil }
201
+
202
+ subject do
203
+ retry_on Timeout::Error do
204
+ ImageSize.url(url)
205
+ end
206
+ end
147
207
 
148
208
  context 'supporting range' do
149
209
  context 'without redirects' do
210
+ let(:query){ '' }
211
+
150
212
  it 'gets format and dimensions' do
151
- image_size = ImageSize.url(file_url)
152
- expect(image_size).to have_attributes(attributes)
213
+ is_expected.to have_attributes(attributes)
153
214
  end
154
215
  end
155
216
 
156
217
  context 'with redirects' do
218
+ let(:query){ '?redirect=5' }
219
+
157
220
  it 'gets format and dimensions' do
158
- image_size = ImageSize.url("#{file_url}?redirect=5")
159
- expect(image_size).to have_attributes(attributes)
221
+ is_expected.to have_attributes(attributes)
160
222
  end
161
223
  end
162
224
 
163
225
  context 'with too many redirects' do
226
+ let(:query){ '?redirect=6' }
227
+
228
+ it 'raises error' do
229
+ expect{ subject }.to raise_error(/Too many redirects/)
230
+ end
231
+ end
232
+
233
+ context 'with unknown file size' do
234
+ let(:query){ '?unknown_file_size' }
235
+ let(:attributes){ super().merge(byte_size: file_size.zero? ? 0 : nil) }
236
+
164
237
  it 'gets format and dimensions' do
165
- expect do
166
- ImageSize.url("#{file_url}?redirect=6")
167
- end.to raise_error(/Too many redirects/)
238
+ is_expected.to have_attributes(attributes)
168
239
  end
169
240
  end
170
241
  end
171
242
 
172
243
  context 'not supporting range' do
244
+ let(:query){ '?ignore_range' }
245
+
173
246
  context 'without redirects' do
174
247
  it 'gets format and dimensions' do
175
- image_size = ImageSize.url("#{file_url}?ignore_range")
176
- expect(image_size).to have_attributes(attributes)
248
+ is_expected.to have_attributes(attributes)
177
249
  end
178
250
  end
179
251
 
180
252
  context 'with redirects' do
253
+ let(:query){ '?ignore_range&redirect=5' }
254
+
181
255
  it 'gets format and dimensions' do
182
- image_size = ImageSize.url("#{file_url}?ignore_range&redirect=5")
183
- expect(image_size).to have_attributes(attributes)
256
+ is_expected.to have_attributes(attributes)
184
257
  end
185
258
  end
186
259
 
187
260
  context 'with too many redirects' do
188
- it 'gets format and dimensions' do
189
- expect do
190
- ImageSize.url("#{file_url}?ignore_range&redirect=6")
191
- end.to raise_error(/Too many redirects/)
261
+ let(:query){ '?ignore_range&redirect=6' }
262
+
263
+ it 'raises error' do
264
+ expect{ subject }.to raise_error(/Too many redirects/)
192
265
  end
193
266
  end
194
267
  end
@@ -203,8 +276,8 @@ describe ImageSize do
203
276
  end
204
277
 
205
278
  {
206
- :png => "\211PNG\r\n\032\n",
207
- :jpeg => "\377\330",
279
+ png: "\211PNG\r\n\032\n",
280
+ jpeg: "\377\330",
208
281
  }.each do |type, data|
209
282
  it "raises FormatError if invalid #{type} given" do
210
283
  expect do
@@ -212,4 +285,164 @@ describe ImageSize do
212
285
  end.to raise_error(ImageSize::FormatError)
213
286
  end
214
287
  end
288
+
289
+ describe '.dpi' do
290
+ subject{ ImageSize.dpi }
291
+
292
+ after{ ImageSize.dpi = nil }
293
+
294
+ it 'is 72 by default' do
295
+ is_expected.to eql(72.0)
296
+ end
297
+
298
+ it 'is can be set and get converted to Float' do
299
+ ImageSize.dpi = 42
300
+
301
+ is_expected.to eql(42.0)
302
+ end
303
+
304
+ it 'is can not be set to 0' do
305
+ expect{ ImageSize.dpi = 0 }.to raise_error(ArgumentError)
306
+
307
+ is_expected.to eql(72.0)
308
+ end
309
+
310
+ it 'is can not be set to a negative value' do
311
+ expect{ ImageSize.dpi = -42 }.to raise_error(ArgumentError)
312
+
313
+ is_expected.to eql(72.0)
314
+ end
315
+
316
+ it 'can be reset' do
317
+ ImageSize.dpi = 42
318
+ ImageSize.dpi = nil
319
+
320
+ is_expected.to eql(72.0)
321
+ end
322
+ end
323
+
324
+ describe '.chunk_size' do
325
+ subject{ ImageSize.chunk_size }
326
+
327
+ after{ ImageSize.chunk_size = nil }
328
+
329
+ it 'is 4096 by default' do
330
+ is_expected.to eql(4096)
331
+ end
332
+
333
+ it 'is can be set to an Integer' do
334
+ ImageSize.chunk_size = 256
335
+
336
+ is_expected.to eql(256)
337
+ end
338
+
339
+ it 'is can not be set to 0' do
340
+ expect{ ImageSize.chunk_size = 0 }.to raise_error(ArgumentError)
341
+
342
+ is_expected.to eql(4096)
343
+ end
344
+
345
+ it 'is can not be set to a negative value' do
346
+ expect{ ImageSize.chunk_size = -1 }.to raise_error(ArgumentError)
347
+
348
+ is_expected.to eql(4096)
349
+ end
350
+
351
+ it 'is can not be set to a Float' do
352
+ expect{ ImageSize.chunk_size = 3.5 }.to raise_error(ArgumentError)
353
+
354
+ is_expected.to eql(4096)
355
+ end
356
+
357
+ it 'can be reset' do
358
+ ImageSize.chunk_size = 256
359
+ ImageSize.chunk_size = nil
360
+
361
+ is_expected.to eql(4096)
362
+ end
363
+ end
364
+
365
+ describe '.max_redirects' do
366
+ subject{ ImageSize.max_redirects }
367
+
368
+ after{ ImageSize.max_redirects = nil }
369
+
370
+ it 'is 4096 by default' do
371
+ is_expected.to eql(5)
372
+ end
373
+
374
+ it 'is can be set to an Integer' do
375
+ ImageSize.max_redirects = 3
376
+
377
+ is_expected.to eql(3)
378
+ end
379
+
380
+ it 'is can be set to 0' do
381
+ ImageSize.max_redirects = 0
382
+
383
+ is_expected.to eql(0)
384
+ end
385
+
386
+ it 'is can not be set to a negative value' do
387
+ expect{ ImageSize.max_redirects = -1 }.to raise_error(ArgumentError)
388
+
389
+ is_expected.to eql(5)
390
+ end
391
+
392
+ it 'is can not be set to a Float' do
393
+ expect{ ImageSize.max_redirects = 3.5 }.to raise_error(ArgumentError)
394
+
395
+ is_expected.to eql(5)
396
+ end
397
+
398
+ it 'can be reset' do
399
+ ImageSize.max_redirects = 3
400
+ ImageSize.max_redirects = nil
401
+
402
+ is_expected.to eql(5)
403
+ end
404
+ end
405
+
406
+ describe '.uri_checker' do
407
+ before{ ImageSize.chunk_size = 64 }
408
+ after do
409
+ ImageSize.chunk_size = nil
410
+ ImageSize.uri_checker = nil
411
+ end
412
+
413
+ it 'can be reset to the default checker' do
414
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.base_url.port }
415
+ ImageSize.uri_checker = nil
416
+
417
+ expect do
418
+ retry_on Timeout::Error do
419
+ ImageSize.url("#{@server.base_url}spec/images/empty")
420
+ end
421
+ end.not_to raise_error
422
+ end
423
+
424
+ it 'requires an object responding to call' do
425
+ expect{ ImageSize.uri_checker = Object.new }.to raise_error(ArgumentError)
426
+ end
427
+
428
+ it 'is checked before initial request' do
429
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.base_url.port }
430
+
431
+ expect do
432
+ retry_on Timeout::Error do
433
+ ImageSize.url("#{@server.base_url}spec/images/empty")
434
+ end
435
+ end.to raise_error('forbidden')
436
+ end
437
+
438
+ it 'is checked before redirect request' do
439
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.second_url.port }
440
+
441
+ expect do
442
+ retry_on Timeout::Error do
443
+ ImageSize.url("#{@server.base_url}spec/images/empty?redirect=1")
444
+ end
445
+ end.to raise_error('forbidden')
446
+ end
447
+ end
215
448
  end
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="430" height="430">
2
+ <g font-family="'Arial Black'" text-anchor="middle">
3
+ <text x="266.322" y="79.322" fill="#0057b7" stroke-width=".817" font-size="108.96" transform="scale(.80705 1.2391)">RUSSIAN</text>
4
+ <text x="276.58" y="163.322" fill="#0057b7" stroke-width=".794" font-size="105.9" transform="scale(.78387 1.2757)">WARSHIP</text>
5
+ <text x="268.809" y="252.317" fill="gold" stroke-width=".803" font-size="107.02" transform="scale(.79274 1.2615)">GO FUCK</text>
6
+ <text x="303.286" y="306.546" fill="gold" stroke-width=".725" font-size="96.629" transform="scale(.71575 1.3971)">YOURSELF!</text>
7
+ </g>
8
+ </svg><!-- today is day 1529 of full-scale russian invasion of Ukraine -->
9
+
10
+
11
+ <!-- pad to 768 bytes -->
data/spec/test_server.rb CHANGED
@@ -1,18 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'webrick'
4
+ require 'stringio'
4
5
 
5
6
  class TestServer
6
- attr_reader :base_url
7
+ class FileHandler < WEBrick::HTTPServlet::FileHandler
8
+ def service(req, res)
9
+ super
10
+ rescue WEBrick::HTTPStatus::PartialContent
11
+ if req.query['unknown_file_size']
12
+ m = %r{\Abytes (\d+)-(\d+)/\d+\z}.match(res['content-range'])
13
+ raise "Unexpected content-range: #{res['content-range']}" unless m
14
+
15
+ res['content-range'] = "bytes #{m[1]}-#{m[2]}/*"
16
+
17
+ # need to manually get the chunk, as webrick internally relies on content-range header with total size
18
+ if res.body.is_a?(IO)
19
+ offset = m[1].to_i
20
+ size = m[2].to_i - offset + 1
21
+
22
+ res.body.seek(offset, IO::SEEK_SET)
23
+ res.body = res.body.read(size)
24
+ end
25
+ end
26
+
27
+ raise
28
+ end
29
+ end
30
+
31
+ attr_reader :base_url, :second_url
7
32
 
8
33
  def initialize(host = '127.0.0.1')
9
34
  server_options = {
10
- :Logger => WEBrick::Log.new(StringIO.new),
11
- :AccessLog => [],
12
- :BindAddress => host,
13
- :Port => 0, # get the next available port
14
- :DocumentRoot => '.',
15
- :RequestCallback => proc do |req, res|
35
+ AccessLog: [],
36
+ BindAddress: host,
37
+ Port: 0, # get the next available port
38
+ RequestCallback: proc do |req, res|
16
39
  redirect = req.query['redirect'].to_i
17
40
  if redirect > 0
18
41
  res.set_redirect(
@@ -29,7 +52,11 @@ class TestServer
29
52
  end,
30
53
  }
31
54
 
55
+ server_options[:Logger] = WEBrick::Log.new(StringIO.new) unless ENV['CI']
56
+
32
57
  @server = WEBrick::HTTPServer.new(server_options)
58
+ @server.mount('/', FileHandler, '.')
59
+
33
60
  @server.listen(host, 0) # listen on second port
34
61
 
35
62
  @base_url = URI("http://#{host}:#{@server.listeners[0].addr[1]}/")
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_size
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keisuke Minami
8
8
  - Ivan Kuchin
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2023-05-30 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec
@@ -56,11 +55,11 @@ dependencies:
56
55
  description: 'Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic,
57
56
  heif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf,
58
57
  tiff, webp, xbm, xpm'
59
- email:
60
58
  executables: []
61
59
  extensions: []
62
60
  extra_rdoc_files: []
63
61
  files:
62
+ - ".github/dependabot.yml"
64
63
  - ".github/workflows/check.yml"
65
64
  - ".github/workflows/rubocop.yml"
66
65
  - ".gitignore"
@@ -76,10 +75,12 @@ files:
76
75
  - lib/image_size/chunky_reader.rb
77
76
  - lib/image_size/format_error.rb
78
77
  - lib/image_size/isobmff.rb
78
+ - lib/image_size/media_types.rb
79
79
  - lib/image_size/reader.rb
80
80
  - lib/image_size/seekable_io_reader.rb
81
81
  - lib/image_size/stream_io_reader.rb
82
82
  - lib/image_size/string_reader.rb
83
+ - lib/image_size/uri.rb
83
84
  - lib/image_size/uri_reader.rb
84
85
  - spec/image_size/chunky_reader_spec.rb
85
86
  - spec/image_size/isobmff_spec.rb
@@ -118,6 +119,7 @@ files:
118
119
  - spec/images/pnm/ascii.22x25.ppm
119
120
  - spec/images/psd/16x20.psd
120
121
  - spec/images/svg/72x100.svg
122
+ - spec/images/svg/768b.430x430.svg
121
123
  - spec/images/svg/crlf.72x100.svg
122
124
  - spec/images/svg/long.72x100.svg
123
125
  - spec/images/svg/long.crlf.72x100.svg
@@ -138,9 +140,8 @@ licenses:
138
140
  metadata:
139
141
  bug_tracker_uri: https://github.com/toy/image_size/issues
140
142
  changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
141
- documentation_uri: https://www.rubydoc.info/gems/image_size/3.3.0
143
+ documentation_uri: https://www.rubydoc.info/gems/image_size/3.5.0
142
144
  source_code_uri: https://github.com/toy/image_size
143
- post_install_message:
144
145
  rdoc_options: []
145
146
  require_paths:
146
147
  - lib
@@ -148,15 +149,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
149
  requirements:
149
150
  - - ">="
150
151
  - !ruby/object:Gem::Version
151
- version: '0'
152
+ version: 1.9.3
152
153
  required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  requirements:
154
155
  - - ">="
155
156
  - !ruby/object:Gem::Version
156
157
  version: '0'
157
158
  requirements: []
158
- rubygems_version: 3.4.12
159
- signing_key:
159
+ rubygems_version: 4.0.3
160
160
  specification_version: 4
161
161
  summary: Measure image size/dimensions using pure Ruby
162
162
  test_files:
@@ -197,6 +197,7 @@ test_files:
197
197
  - spec/images/pnm/ascii.22x25.ppm
198
198
  - spec/images/psd/16x20.psd
199
199
  - spec/images/svg/72x100.svg
200
+ - spec/images/svg/768b.430x430.svg
200
201
  - spec/images/svg/crlf.72x100.svg
201
202
  - spec/images/svg/long.72x100.svg
202
203
  - spec/images/svg/long.crlf.72x100.svg