image_size 3.4.0 → 3.6.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.
@@ -14,6 +14,20 @@ RSpec.configure do |config|
14
14
  config.order = :random
15
15
  end
16
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
+
17
31
  describe ImageSize do
18
32
  before :all do
19
33
  @server = TestServer.new
@@ -56,18 +70,22 @@ describe ImageSize do
56
70
  end
57
71
  end
58
72
 
59
- Dir['spec/**/*'].each do |path|
60
- next unless File.file?(path)
73
+ def self.test_image_size(path, &block)
74
+ file_size = File.size(path)
75
+ max_file_size = 16_384
76
+ fail "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})" if file_size > max_file_size
61
77
 
62
78
  describe "for #{path}" do
63
79
  let(:name){ File.basename(path) }
80
+ let(:match){ name.match(/(?<width>\d+)x(?<height>\d+)(?:@(?<scale>\d)x)?\.(?<format>[^.]+)$/) }
81
+ let(:scale){ (match[:scale] || 1).to_i if match }
64
82
  let(:attributes) do
65
- if (match = /(\d+)x(\d+)\.([^.]+)$/.match(name))
66
- width = match[1].to_i
67
- height = match[2].to_i
68
- format = match[3].to_sym
83
+ if match
84
+ width = match[:width].to_i * scale
85
+ height = match[:height].to_i * scale
86
+ format = match[:format].to_sym
69
87
  end
70
- size = format && [width, height]
88
+ size = [width, height] if format
71
89
  media_types = ImageSize::MEDIA_TYPES[format] || []
72
90
  media_type = format && media_types.first.to_s
73
91
  {
@@ -79,18 +97,12 @@ describe ImageSize do
79
97
  size: size,
80
98
  media_type: media_type,
81
99
  media_types: media_types,
100
+ byte_size: file_size,
82
101
  }
83
102
  end
84
103
  let(:file_data){ File.binread(path) }
85
- let(:file_size){ file_data.length }
86
-
87
- before do
88
- max_file_size = 16_384
89
104
 
90
- if file_size > max_file_size
91
- raise "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})"
92
- end
93
- end
105
+ instance_exec(&block) if block
94
106
 
95
107
  context 'given as data' do
96
108
  it 'gets format and dimensions' do
@@ -119,6 +131,8 @@ describe ImageSize do
119
131
  end
120
132
 
121
133
  context 'given as unseekable IO' do
134
+ let(:attributes){ super().merge(byte_size: nil) }
135
+
122
136
  it 'gets format and dimensions' do
123
137
  IO.popen(%W[cat #{path}].shelljoin, 'rb') do |io|
124
138
  image_size = ImageSize.new(io)
@@ -128,6 +142,16 @@ describe ImageSize do
128
142
  end
129
143
  end
130
144
 
145
+ context 'given an object allowing only read and eof?' do
146
+ let(:attributes){ super().merge(byte_size: nil) }
147
+
148
+ it 'gets format and dimensions' do
149
+ io = RestrictedIO.new(file_data)
150
+ image_size = ImageSize.new(io)
151
+ expect(image_size).to have_attributes(attributes)
152
+ end
153
+ end
154
+
131
155
  context 'given as StringIO' do
132
156
  it 'gets format and dimensions' do
133
157
  io = StringIO.new(file_data)
@@ -167,64 +191,74 @@ describe ImageSize do
167
191
  end
168
192
 
169
193
  context 'fetching from webserver' do
170
- let(:file_url){ @server.base_url + path }
194
+ let(:url){ "#{@server.base_url}#{path}#{query}" }
195
+
196
+ before{ ImageSize.chunk_size = 64 }
197
+ after{ ImageSize.chunk_size = nil }
198
+
199
+ subject do
200
+ retry_on Timeout::Error do
201
+ ImageSize.url(url)
202
+ end
203
+ end
171
204
 
172
205
  context 'supporting range' do
173
206
  context 'without redirects' do
207
+ let(:query){ '' }
208
+
174
209
  it 'gets format and dimensions' do
175
- image_size = retry_on Timeout::Error do
176
- ImageSize.url(file_url)
177
- end
178
- expect(image_size).to have_attributes(attributes)
210
+ is_expected.to have_attributes(attributes)
179
211
  end
180
212
  end
181
213
 
182
214
  context 'with redirects' do
215
+ let(:query){ '?redirect=5' }
216
+
183
217
  it 'gets format and dimensions' do
184
- image_size = retry_on Timeout::Error do
185
- ImageSize.url("#{file_url}?redirect=5")
186
- end
187
- expect(image_size).to have_attributes(attributes)
218
+ is_expected.to have_attributes(attributes)
188
219
  end
189
220
  end
190
221
 
191
222
  context 'with too many redirects' do
223
+ let(:query){ '?redirect=6' }
224
+
225
+ it 'raises error' do
226
+ expect{ subject }.to raise_error(/Too many redirects/)
227
+ end
228
+ end
229
+
230
+ context 'with unknown file size' do
231
+ let(:query){ '?unknown_file_size' }
232
+ let(:attributes){ super().merge(byte_size: file_size.zero? ? 0 : nil) }
233
+
192
234
  it 'gets format and dimensions' do
193
- expect do
194
- retry_on Timeout::Error do
195
- ImageSize.url("#{file_url}?redirect=6")
196
- end
197
- end.to raise_error(/Too many redirects/)
235
+ is_expected.to have_attributes(attributes)
198
236
  end
199
237
  end
200
238
  end
201
239
 
202
240
  context 'not supporting range' do
241
+ let(:query){ '?ignore_range' }
242
+
203
243
  context 'without redirects' do
204
244
  it 'gets format and dimensions' do
205
- image_size = retry_on Timeout::Error do
206
- ImageSize.url("#{file_url}?ignore_range")
207
- end
208
- expect(image_size).to have_attributes(attributes)
245
+ is_expected.to have_attributes(attributes)
209
246
  end
210
247
  end
211
248
 
212
249
  context 'with redirects' do
250
+ let(:query){ '?ignore_range&redirect=5' }
251
+
213
252
  it 'gets format and dimensions' do
214
- image_size = retry_on Timeout::Error do
215
- ImageSize.url("#{file_url}?ignore_range&redirect=5")
216
- end
217
- expect(image_size).to have_attributes(attributes)
253
+ is_expected.to have_attributes(attributes)
218
254
  end
219
255
  end
220
256
 
221
257
  context 'with too many redirects' do
222
- it 'gets format and dimensions' do
223
- expect do
224
- retry_on Timeout::Error do
225
- ImageSize.url("#{file_url}?ignore_range&redirect=6")
226
- end
227
- end.to raise_error(/Too many redirects/)
258
+ let(:query){ '?ignore_range&redirect=6' }
259
+
260
+ it 'raises error' do
261
+ expect{ subject }.to raise_error(/Too many redirects/)
228
262
  end
229
263
  end
230
264
  end
@@ -232,6 +266,32 @@ describe ImageSize do
232
266
  end
233
267
  end
234
268
 
269
+ context 'for non images' do
270
+ test_image_size 'spec/test_server.rb'
271
+ test_image_size 'spec/images/empty'
272
+ end
273
+
274
+ context 'for images' do
275
+ Dir['spec/images/*/*.*'].each do |path|
276
+ if path.end_with?('.icns')
277
+ context 'without use_display_pixels' do
278
+ test_image_size path
279
+ end
280
+
281
+ context 'with use_display_pixels' do
282
+ before{ ImageSize.use_display_pixels = true }
283
+ after{ ImageSize.use_display_pixels = nil }
284
+
285
+ test_image_size path do
286
+ let(:scale){ 1 }
287
+ end
288
+ end
289
+ else
290
+ test_image_size path
291
+ end
292
+ end
293
+ end
294
+
235
295
  it 'raises ArgumentError if argument is not valid' do
236
296
  expect do
237
297
  ImageSize.new(Object)
@@ -248,4 +308,164 @@ describe ImageSize do
248
308
  end.to raise_error(ImageSize::FormatError)
249
309
  end
250
310
  end
311
+
312
+ describe '.dpi' do
313
+ subject{ ImageSize.dpi }
314
+
315
+ after{ ImageSize.dpi = nil }
316
+
317
+ it 'is 72 by default' do
318
+ is_expected.to eql(72.0)
319
+ end
320
+
321
+ it 'is can be set and get converted to Float' do
322
+ ImageSize.dpi = 42
323
+
324
+ is_expected.to eql(42.0)
325
+ end
326
+
327
+ it 'is can not be set to 0' do
328
+ expect{ ImageSize.dpi = 0 }.to raise_error(ArgumentError)
329
+
330
+ is_expected.to eql(72.0)
331
+ end
332
+
333
+ it 'is can not be set to a negative value' do
334
+ expect{ ImageSize.dpi = -42 }.to raise_error(ArgumentError)
335
+
336
+ is_expected.to eql(72.0)
337
+ end
338
+
339
+ it 'can be reset' do
340
+ ImageSize.dpi = 42
341
+ ImageSize.dpi = nil
342
+
343
+ is_expected.to eql(72.0)
344
+ end
345
+ end
346
+
347
+ describe '.chunk_size' do
348
+ subject{ ImageSize.chunk_size }
349
+
350
+ after{ ImageSize.chunk_size = nil }
351
+
352
+ it 'is 4096 by default' do
353
+ is_expected.to eql(4096)
354
+ end
355
+
356
+ it 'is can be set to an Integer' do
357
+ ImageSize.chunk_size = 256
358
+
359
+ is_expected.to eql(256)
360
+ end
361
+
362
+ it 'is can not be set to 0' do
363
+ expect{ ImageSize.chunk_size = 0 }.to raise_error(ArgumentError)
364
+
365
+ is_expected.to eql(4096)
366
+ end
367
+
368
+ it 'is can not be set to a negative value' do
369
+ expect{ ImageSize.chunk_size = -1 }.to raise_error(ArgumentError)
370
+
371
+ is_expected.to eql(4096)
372
+ end
373
+
374
+ it 'is can not be set to a Float' do
375
+ expect{ ImageSize.chunk_size = 3.5 }.to raise_error(ArgumentError)
376
+
377
+ is_expected.to eql(4096)
378
+ end
379
+
380
+ it 'can be reset' do
381
+ ImageSize.chunk_size = 256
382
+ ImageSize.chunk_size = nil
383
+
384
+ is_expected.to eql(4096)
385
+ end
386
+ end
387
+
388
+ describe '.max_redirects' do
389
+ subject{ ImageSize.max_redirects }
390
+
391
+ after{ ImageSize.max_redirects = nil }
392
+
393
+ it 'is 4096 by default' do
394
+ is_expected.to eql(5)
395
+ end
396
+
397
+ it 'is can be set to an Integer' do
398
+ ImageSize.max_redirects = 3
399
+
400
+ is_expected.to eql(3)
401
+ end
402
+
403
+ it 'is can be set to 0' do
404
+ ImageSize.max_redirects = 0
405
+
406
+ is_expected.to eql(0)
407
+ end
408
+
409
+ it 'is can not be set to a negative value' do
410
+ expect{ ImageSize.max_redirects = -1 }.to raise_error(ArgumentError)
411
+
412
+ is_expected.to eql(5)
413
+ end
414
+
415
+ it 'is can not be set to a Float' do
416
+ expect{ ImageSize.max_redirects = 3.5 }.to raise_error(ArgumentError)
417
+
418
+ is_expected.to eql(5)
419
+ end
420
+
421
+ it 'can be reset' do
422
+ ImageSize.max_redirects = 3
423
+ ImageSize.max_redirects = nil
424
+
425
+ is_expected.to eql(5)
426
+ end
427
+ end
428
+
429
+ describe '.uri_checker' do
430
+ before{ ImageSize.chunk_size = 64 }
431
+ after do
432
+ ImageSize.chunk_size = nil
433
+ ImageSize.uri_checker = nil
434
+ end
435
+
436
+ it 'can be reset to the default checker' do
437
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.base_url.port }
438
+ ImageSize.uri_checker = nil
439
+
440
+ expect do
441
+ retry_on Timeout::Error do
442
+ ImageSize.url("#{@server.base_url}spec/images/empty")
443
+ end
444
+ end.not_to raise_error
445
+ end
446
+
447
+ it 'requires an object responding to call' do
448
+ expect{ ImageSize.uri_checker = Object.new }.to raise_error(ArgumentError)
449
+ end
450
+
451
+ it 'is checked before initial request' do
452
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.base_url.port }
453
+
454
+ expect do
455
+ retry_on Timeout::Error do
456
+ ImageSize.url("#{@server.base_url}spec/images/empty")
457
+ end
458
+ end.to raise_error('forbidden')
459
+ end
460
+
461
+ it 'is checked before redirect request' do
462
+ ImageSize.uri_checker = proc{ |uri| fail 'forbidden' if uri.port == @server.second_url.port }
463
+
464
+ expect do
465
+ retry_on Timeout::Error do
466
+ ImageSize.url("#{@server.base_url}spec/images/empty?redirect=1")
467
+ end
468
+ end.to raise_error('forbidden')
469
+ end
470
+ end
251
471
  end
Binary file
@@ -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
@@ -4,15 +4,37 @@ require 'webrick'
4
4
  require 'stringio'
5
5
 
6
6
  class TestServer
7
- 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
8
32
 
9
33
  def initialize(host = '127.0.0.1')
10
34
  server_options = {
11
- Logger: WEBrick::Log.new(StringIO.new),
12
35
  AccessLog: [],
13
36
  BindAddress: host,
14
37
  Port: 0, # get the next available port
15
- DocumentRoot: '.',
16
38
  RequestCallback: proc do |req, res|
17
39
  redirect = req.query['redirect'].to_i
18
40
  if redirect > 0
@@ -30,7 +52,11 @@ class TestServer
30
52
  end,
31
53
  }
32
54
 
55
+ server_options[:Logger] = WEBrick::Log.new(StringIO.new) unless ENV['CI']
56
+
33
57
  @server = WEBrick::HTTPServer.new(server_options)
58
+ @server.mount('/', FileHandler, '.')
59
+
34
60
  @server.listen(host, 0) # listen on second port
35
61
 
36
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.4.0
4
+ version: 3.6.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: 2024-01-16 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
@@ -54,13 +53,13 @@ dependencies:
54
53
  - !ruby/object:Gem::Version
55
54
  version: '2.0'
56
55
  description: 'Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic,
57
- heif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf,
58
- tiff, webp, xbm, xpm'
59
- email:
56
+ heif, icns, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg,
57
+ swf, tiff, webp, xbm, xpm'
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"
@@ -101,6 +100,11 @@ files:
101
100
  - spec/images/heif/multiple.169x83.heic
102
101
  - spec/images/heif/rotate.73x173.avif
103
102
  - spec/images/heif/sequence.7x5.avif
103
+ - spec/images/icns/16x12.icns
104
+ - spec/images/icns/afp_scanpix_leta_pilipey_kyiv.256x256@1x.icns
105
+ - spec/images/icns/reuters_scanpix_leta_garanich_kyiv.256x256@2x.icns
106
+ - spec/images/icns/toc.512x512@1x.icns
107
+ - spec/images/icns/toc.512x512@2x.icns
104
108
  - spec/images/ico/32x256.ico
105
109
  - spec/images/jp2/163x402.jp2
106
110
  - spec/images/jp2/176x373.jpx
@@ -120,6 +124,7 @@ files:
120
124
  - spec/images/pnm/ascii.22x25.ppm
121
125
  - spec/images/psd/16x20.psd
122
126
  - spec/images/svg/72x100.svg
127
+ - spec/images/svg/768b.430x430.svg
123
128
  - spec/images/svg/crlf.72x100.svg
124
129
  - spec/images/svg/long.72x100.svg
125
130
  - spec/images/svg/long.crlf.72x100.svg
@@ -140,9 +145,8 @@ licenses:
140
145
  metadata:
141
146
  bug_tracker_uri: https://github.com/toy/image_size/issues
142
147
  changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
143
- documentation_uri: https://www.rubydoc.info/gems/image_size/3.4.0
148
+ documentation_uri: https://www.rubydoc.info/gems/image_size/3.6.0
144
149
  source_code_uri: https://github.com/toy/image_size
145
- post_install_message:
146
150
  rdoc_options: []
147
151
  require_paths:
148
152
  - lib
@@ -157,8 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
161
  - !ruby/object:Gem::Version
158
162
  version: '0'
159
163
  requirements: []
160
- rubygems_version: 3.4.20
161
- signing_key:
164
+ rubygems_version: 4.0.3
162
165
  specification_version: 4
163
166
  summary: Measure image size/dimensions using pure Ruby
164
167
  test_files:
@@ -180,6 +183,11 @@ test_files:
180
183
  - spec/images/heif/multiple.169x83.heic
181
184
  - spec/images/heif/rotate.73x173.avif
182
185
  - spec/images/heif/sequence.7x5.avif
186
+ - spec/images/icns/16x12.icns
187
+ - spec/images/icns/afp_scanpix_leta_pilipey_kyiv.256x256@1x.icns
188
+ - spec/images/icns/reuters_scanpix_leta_garanich_kyiv.256x256@2x.icns
189
+ - spec/images/icns/toc.512x512@1x.icns
190
+ - spec/images/icns/toc.512x512@2x.icns
183
191
  - spec/images/ico/32x256.ico
184
192
  - spec/images/jp2/163x402.jp2
185
193
  - spec/images/jp2/176x373.jpx
@@ -199,6 +207,7 @@ test_files:
199
207
  - spec/images/pnm/ascii.22x25.ppm
200
208
  - spec/images/psd/16x20.psd
201
209
  - spec/images/svg/72x100.svg
210
+ - spec/images/svg/768b.430x430.svg
202
211
  - spec/images/svg/crlf.72x100.svg
203
212
  - spec/images/svg/long.72x100.svg
204
213
  - spec/images/svg/long.crlf.72x100.svg