image_size 2.0.2 → 3.0.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +69 -0
  3. data/.rubocop.yml +27 -4
  4. data/.rubocop_todo.yml +11 -8
  5. data/CHANGELOG.markdown +19 -0
  6. data/GPL +340 -0
  7. data/Gemfile +1 -3
  8. data/LICENSE.txt +56 -0
  9. data/README.markdown +22 -18
  10. data/image_size.gemspec +6 -5
  11. data/lib/image_size/chunky_reader.rb +44 -0
  12. data/lib/image_size/reader.rb +64 -0
  13. data/lib/image_size/seekable_io_reader.rb +28 -0
  14. data/lib/image_size/stream_io_reader.rb +22 -0
  15. data/lib/image_size/string_reader.rb +21 -0
  16. data/lib/image_size/uri_reader.rb +88 -0
  17. data/lib/image_size.rb +159 -85
  18. data/spec/image_size/chunky_reader_spec.rb +69 -0
  19. data/spec/image_size_spec.rb +45 -2
  20. data/spec/images/.gitattributes +1 -0
  21. data/spec/images/cur/32x256.cur +0 -0
  22. data/spec/images/ico/32x256.ico +0 -0
  23. data/spec/images/jp2/163x402.jp2 +0 -0
  24. data/spec/images/jp2/176x373.jpx +0 -0
  25. data/spec/images/jp2/224x293.j2c +0 -0
  26. data/spec/images/jpeg/436x429.jpeg +0 -0
  27. data/spec/images/jpeg/extraneous-bytes.436x429.jpeg +0 -0
  28. data/spec/images/mng/61x42.mng +0 -0
  29. data/spec/images/{apng → png}/192x110.apng +0 -0
  30. data/spec/images/pnm/22x25.pam +8 -0
  31. data/spec/images/pnm/22x25.pbm +0 -0
  32. data/spec/images/pnm/22x25.pgm +4 -0
  33. data/spec/images/pnm/22x25.ppm +4 -0
  34. data/spec/images/pnm/ascii.22x25.pbm +27 -0
  35. data/spec/images/pnm/ascii.22x25.pgm +28 -0
  36. data/spec/images/pnm/ascii.22x25.ppm +28 -0
  37. data/spec/images/svg/crlf.72x100.svg +3 -0
  38. data/spec/images/svg/long.72x100.svg +20 -0
  39. data/spec/images/svg/long.crlf.72x100.svg +20 -0
  40. data/spec/images/tiff/big-endian.68x49.tiff +0 -0
  41. data/spec/images/tiff/little-endian.40x68.tiff +0 -0
  42. data/spec/images/xbm/crlf.16x32.xbm +11 -0
  43. data/spec/images/xpm/crlf.24x32.xpm +40 -0
  44. metadata +86 -32
  45. data/.travis.yml +0 -24
  46. data/spec/images/cur/50x256.cur +0 -0
  47. data/spec/images/ico/256x27.ico +0 -0
  48. data/spec/images/jpeg/320x240.jpeg +0 -0
  49. data/spec/images/jpeg/extraneous-bytes.320x240.jpeg +0 -0
  50. data/spec/images/mng/612x132.mng +0 -0
  51. data/spec/images/pbm/85x55.pbm +0 -0
  52. data/spec/images/pgm/90x55.pgm +0 -5
  53. data/spec/images/tiff/48x64.tiff +0 -0
data/lib/image_size.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # encoding: BINARY
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'stringio'
4
+ require 'image_size/reader'
5
+ require 'image_size/seekable_io_reader'
6
+ require 'image_size/stream_io_reader'
7
+ require 'image_size/string_reader'
5
8
 
6
9
  # Determine image format and size
7
10
  class ImageSize
@@ -13,37 +16,23 @@ class ImageSize
13
16
  def to_s
14
17
  join('x')
15
18
  end
16
- end
17
19
 
18
- class ImageReader # :nodoc:
19
- attr_reader :data
20
- def initialize(data_or_io)
21
- @io = if data_or_io.is_a?(String)
22
- StringIO.new(data_or_io)
23
- elsif data_or_io.respond_to?(:read) && data_or_io.respond_to?(:eof?)
24
- data_or_io
25
- else
26
- raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{data_or_io.class}"
27
- end
28
- @data = String.new # not frozen
20
+ # get first element
21
+ def width
22
+ self[0]
29
23
  end
24
+ alias_method :w, :width
30
25
 
31
- CHUNK = 1024
32
- def [](offset, length)
33
- while !@io.eof? && @data.length < offset + length
34
- data = @io.read(CHUNK)
35
- break unless data
36
-
37
- data.force_encoding(@data.encoding) if data.respond_to?(:encoding)
38
- @data << data
39
- end
40
- @data[offset, length]
26
+ # get second element
27
+ def height
28
+ self[1]
41
29
  end
30
+ alias_method :h, :height
42
31
  end
43
32
 
44
33
  # Given path to image finds its format, width and height
45
34
  def self.path(path)
46
- File.open(path, 'rb'){ |f| new(f) }
35
+ new(Pathname.new(path))
47
36
  end
48
37
 
49
38
  # Used for svg
@@ -58,11 +47,10 @@ class ImageSize
58
47
 
59
48
  # Given image as any class responding to read and eof? or data as String, finds its format and dimensions
60
49
  def initialize(data)
61
- ir = ImageReader.new(data)
62
- @format = detect_format(ir)
63
- return unless @format
64
-
65
- @width, @height = send("size_of_#{@format}", ir)
50
+ Reader.open(data) do |ir|
51
+ @format = detect_format(ir)
52
+ @width, @height = send("size_of_#{@format}", ir) if @format
53
+ end
66
54
  end
67
55
 
68
56
  # Image format
@@ -84,28 +72,28 @@ class ImageSize
84
72
  private
85
73
 
86
74
  SVG_R = /<svg\b([^>]*)>/.freeze
75
+ XML_R = /<\?xml|<!--/.freeze
87
76
  def detect_format(ir)
88
77
  head = ir[0, 1024]
89
78
  case
90
- when head[0, 6] =~ /GIF8[79]a/ then :gif
91
- when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
92
- when head[0, 8] == "\212MNG\r\n\032\n" then :mng
93
- when head[0, 2] == "\377\330" then :jpeg
94
- when head[0, 2] == 'BM' then :bmp
95
- when head[0, 2] =~ /P[1-7]/ then :ppm
96
- when head =~ /\#define\s+\S+\s+\d+/ then :xbm
97
- when head[0, 4] == "II*\000" then :tiff
98
- when head[0, 4] == "MM\000*" then :tiff
99
- when head =~ %r{/\* XPM \*/} then :xpm
100
- when head[0, 4] == '8BPS' then :psd
101
- when head[0, 3] =~ /[FC]WS/ then :swf
102
- when head[SVG_R] ||
103
- head =~ /<\?xml|<!--/ && ir[0, 4096][SVG_R]
104
- then :svg
105
- when head[0, 2] =~ /\n[\000-\005]/ then :pcx
106
- when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
107
- when head[0, 4] == "\000\000\001\000" then :ico
108
- when head[0, 4] == "\000\000\002\000" then :cur
79
+ when head[0, 6] =~ /GIF8[79]a/ then :gif
80
+ when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
81
+ when head[0, 8] == "\212MNG\r\n\032\n" then :mng
82
+ when head[0, 2] == "\377\330" then :jpeg
83
+ when head[0, 2] == 'BM' then :bmp
84
+ when head[0, 3] =~ /P[1-6]\s|P7\n/ then detect_pnm_type(ir)
85
+ when head =~ /\#define\s+\S+\s+\d+/ then :xbm
86
+ when %W[II*\0 MM\0*].include?(head[0, 4]) then :tiff
87
+ when head =~ %r{/\* XPM \*/} then :xpm
88
+ when head[0, 4] == '8BPS' then :psd
89
+ when head[0, 3] =~ /[FC]WS/ then :swf
90
+ when head =~ SVG_R || (head =~ XML_R && ir[0, 4096] =~ SVG_R) then :svg
91
+ when head[0, 2] =~ /\n[\0-\5]/ then :pcx
92
+ when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
93
+ when head[0, 4] == "\0\0\1\0" then :ico
94
+ when head[0, 4] == "\0\0\2\0" then :cur
95
+ when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
96
+ when head[0, 4] == "\377O\377Q" then :j2c
109
97
  end
110
98
  end
111
99
 
@@ -116,14 +104,34 @@ private
116
104
  break if ['IDAT', 'IEND', nil].include?(type)
117
105
  return :apng if type == 'acTL'
118
106
 
119
- length = ir[offset, 4].unpack('N')[0]
107
+ length = ir.unpack1(offset, 4, 'N')
120
108
  offset += 8 + length + 4
121
109
  end
122
110
  :png
123
111
  end
124
112
 
113
+ def detect_pnm_type(ir)
114
+ case ir[0, 2]
115
+ when 'P1', 'P4' then :pbm
116
+ when 'P2', 'P5' then :pgm
117
+ when 'P3', 'P6' then :ppm
118
+ when 'P7' then :pam
119
+ end
120
+ end
121
+
122
+ def detect_jpeg2000_type(ir)
123
+ return unless ir[16, 4] == 'ftyp'
124
+
125
+ # using xl-box would be weird, but doesn't seem to contradict specification
126
+ skip = ir[12, 4] == "\0\0\0\1" ? 16 : 8
127
+ case ir[12 + skip, 4]
128
+ when 'jp2 ' then :jp2
129
+ when 'jpx ' then :jpx
130
+ end
131
+ end
132
+
125
133
  def size_of_gif(ir)
126
- ir[6, 4].unpack('vv')
134
+ ir.unpack(6, 4, 'vv')
127
135
  end
128
136
 
129
137
  def size_of_mng(ir)
@@ -131,7 +139,7 @@ private
131
139
  raise FormatError, 'MHDR not in place for MNG'
132
140
  end
133
141
 
134
- ir[16, 8].unpack('NN')
142
+ ir.unpack(16, 8, 'NN')
135
143
  end
136
144
 
137
145
  def size_of_png(ir)
@@ -139,15 +147,15 @@ private
139
147
  raise FormatError, 'IHDR not in place for PNG'
140
148
  end
141
149
 
142
- ir[16, 8].unpack('NN')
150
+ ir.unpack(16, 8, 'NN')
143
151
  end
144
152
  alias_method :size_of_apng, :size_of_png
145
153
 
146
- JPEG_CODE_CHECK = %W[
147
- \xC0 \xC1 \xC2 \xC3
148
- \xC5 \xC6 \xC7
149
- \xC9 \xCA \xCB
150
- \xCD \xCE \xCF
154
+ JPEG_CODE_CHECK = [
155
+ 0xC0, 0xC1, 0xC2, 0xC3,
156
+ 0xC5, 0xC6, 0xC7,
157
+ 0xC9, 0xCA, 0xCB,
158
+ 0xCD, 0xCE, 0xCF
151
159
  ].freeze
152
160
  def size_of_jpeg(ir)
153
161
  section_marker = "\xFF"
@@ -157,11 +165,11 @@ private
157
165
  offset += 1 until section_marker != ir[offset + 1, 1]
158
166
  raise FormatError, 'EOF in JPEG' if ir[offset, 1].nil?
159
167
 
160
- _marker, code, length = ir[offset, 4].unpack('aan')
168
+ code, length = ir.unpack(offset, 4, 'xCn')
161
169
  offset += 4
162
170
 
163
171
  if JPEG_CODE_CHECK.include?(code)
164
- return ir[offset + 1, 4].unpack('nn').reverse
172
+ return ir.unpack(offset + 1, 4, 'nn').reverse
165
173
  end
166
174
 
167
175
  offset += length - 2
@@ -169,11 +177,11 @@ private
169
177
  end
170
178
 
171
179
  def size_of_bmp(ir)
172
- header_size = ir[14, 4].unpack('V')[0]
180
+ header_size = ir.unpack1(14, 4, 'V')
173
181
  if header_size == 12
174
- ir[18, 4].unpack('vv')
182
+ ir.unpack(18, 4, 'vv')
175
183
  else
176
- ir[18, 8].unpack('VV').map do |n|
184
+ ir.unpack(18, 8, 'VV').map do |n|
177
185
  if n > 0x7fff_ffff
178
186
  0x1_0000_0000 - n # absolute value of converted to signed
179
187
  else
@@ -187,12 +195,37 @@ private
187
195
  header = ir[0, 1024]
188
196
  header.gsub!(/^\#[^\n\r]*/m, '')
189
197
  header =~ /^(P[1-6])\s+?(\d+)\s+?(\d+)/m
190
- case $1
191
- when 'P1', 'P4' then @format = :pbm
192
- when 'P2', 'P5' then @format = :pgm
193
- end
194
198
  [$2.to_i, $3.to_i]
195
199
  end
200
+ alias_method :size_of_pbm, :size_of_ppm
201
+ alias_method :size_of_pgm, :size_of_ppm
202
+
203
+ def size_of_pam(ir)
204
+ width = height = nil
205
+ offset = 3
206
+ until width && height
207
+ if ir[offset, 1] == '#'
208
+ offset += 1 until ["\n", '', nil].include?(ir[offset, 1])
209
+ offset += 1
210
+ else
211
+ chunk = ir[offset, 32]
212
+ case chunk
213
+ when /\AWIDTH (\d+)\n/
214
+ width = $1.to_i
215
+ when /\AHEIGHT (\d+)\n/
216
+ height = $1.to_i
217
+ when /\AENDHDR\n/
218
+ break
219
+ when /\A(?:DEPTH|MAXVAL) \d+\n/, /\ATUPLTYPE \S+\n/
220
+ # ignore
221
+ else
222
+ raise FormatError, "Unexpected data in PAM header: #{chunk.inspect}"
223
+ end
224
+ offset += $&.length
225
+ end
226
+ end
227
+ [width, height]
228
+ end
196
229
 
197
230
  def size_of_xbm(ir)
198
231
  ir[0, 1024] =~ /^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)/mi
@@ -212,23 +245,23 @@ private
212
245
  end
213
246
 
214
247
  def size_of_psd(ir)
215
- ir[14, 8].unpack('NN').reverse
248
+ ir.unpack(14, 8, 'NN').reverse
216
249
  end
217
250
 
218
251
  def size_of_tiff(ir)
219
- endian2b = ir[0, 4] == "II*\000" ? 'v' : 'n'
252
+ endian2b = ir.fetch(0, 4) == "II*\000" ? 'v' : 'n'
220
253
  endian4b = endian2b.upcase
221
254
  packspec = [nil, 'C', nil, endian2b, endian4b, nil, 'c', nil, endian2b, endian4b]
222
255
 
223
- offset = ir[4, 4].unpack(endian4b)[0]
224
- num_dirent = ir[offset, 2].unpack(endian2b)[0]
256
+ offset = ir.unpack1(4, 4, endian4b)
257
+ num_dirent = ir.unpack1(offset, 2, endian2b)
225
258
  offset += 2
226
259
  num_dirent = offset + (num_dirent * 12)
227
260
 
228
261
  width = height = nil
229
262
  until width && height
230
- ifd = ir[offset, 12]
231
- raise FormatError, 'Reached end of directory entries in TIFF' if ifd.nil? || offset > num_dirent
263
+ ifd = ir.fetch(offset, 12)
264
+ raise FormatError, 'Reached end of directory entries in TIFF' if offset > num_dirent
232
265
 
233
266
  tag, type = ifd.unpack(endian2b * 2)
234
267
  offset += 12
@@ -247,22 +280,23 @@ private
247
280
  end
248
281
 
249
282
  def size_of_pcx(ir)
250
- parts = ir[4, 8].unpack('S4')
283
+ parts = ir.unpack(4, 8, 'v4')
251
284
  [parts[2] - parts[0] + 1, parts[3] - parts[1] + 1]
252
285
  end
253
286
 
254
287
  def size_of_swf(ir)
255
- value_bit_length = ir[8, 1].unpack('B5').first.to_i(2)
256
- bit_length = 5 + value_bit_length * 4
257
- rect_bits = ir[8, bit_length / 8 + 1].unpack("B#{bit_length}").first
258
- values = rect_bits.unpack('@5' + "a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
288
+ value_bit_length = ir.unpack1(8, 1, 'B5').to_i(2)
289
+ bit_length = 5 + (value_bit_length * 4)
290
+ rect_bits = ir.unpack1(8, (bit_length / 8) + 1, "B#{bit_length}")
291
+ values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
259
292
  x_min, x_max, y_min, y_max = values
260
293
  [(x_max - x_min) / 20, (y_max - y_min) / 20]
261
294
  end
262
295
 
263
296
  def size_of_svg(ir)
264
297
  attributes = {}
265
- ir.data[SVG_R, 1].scan(/(\S+)=(?:'([^']*)'|"([^"]*)"|([^'"\s]*))/) do |name, v0, v1, v2|
298
+ svg_tag = ir[0, 1024][SVG_R, 1] || ir[0, 4096][SVG_R, 1]
299
+ svg_tag.scan(/(\S+)=(?:'([^']*)'|"([^"]*)"|([^'"\s]*))/) do |name, v0, v1, v2|
266
300
  attributes[name] = v0 || v1 || v2
267
301
  end
268
302
  dpi = self.class.dpi
@@ -283,20 +317,60 @@ private
283
317
  end
284
318
 
285
319
  def size_of_ico(ir)
286
- ir[6, 2].unpack('CC').map{ |v| v.zero? ? 256 : v }
320
+ ir.unpack(6, 2, 'CC').map{ |v| v.zero? ? 256 : v }
287
321
  end
288
322
  alias_method :size_of_cur, :size_of_ico
289
323
 
290
324
  def size_of_webp(ir)
291
- case ir[12, 4]
325
+ case ir.fetch(12, 4)
292
326
  when 'VP8 '
293
- ir[26, 4].unpack('vv').map{ |v| v & 0x3fff }
327
+ ir.unpack(26, 4, 'vv').map{ |v| v & 0x3fff }
294
328
  when 'VP8L'
295
- n = ir[21, 4].unpack('V')[0]
296
- [(n & 0x3fff) + 1, (n >> 14 & 0x3fff) + 1]
329
+ n = ir.unpack1(21, 4, 'V')
330
+ [(n & 0x3fff) + 1, ((n >> 14) & 0x3fff) + 1]
297
331
  when 'VP8X'
298
- w16, w8, h16, h8 = ir[24, 6].unpack('vCvC')
299
- [(w16 | w8 << 16) + 1, (h16 | h8 << 16) + 1]
332
+ w16, w8, h16, h8 = ir.unpack(24, 6, 'vCvC')
333
+ [(w16 | (w8 << 16)) + 1, (h16 | (h8 << 16)) + 1]
334
+ end
335
+ end
336
+
337
+ def size_of_jp2(ir)
338
+ offset = 12
339
+ stop = nil
340
+ in_header = false
341
+ loop do
342
+ break if stop && offset >= stop
343
+ break if ir[offset, 4] == '' || ir[offset, 4].nil?
344
+
345
+ size = ir.unpack1(offset, 4, 'N')
346
+ type = ir.fetch(offset + 4, 4)
347
+
348
+ data_offset = 8
349
+ case size
350
+ when 1
351
+ size = ir.unpack1(offset, 8, 'Q>')
352
+ data_offset = 16
353
+ raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
354
+ when 2..7
355
+ raise FormatError, "Reserved box size #{size}"
356
+ end
357
+
358
+ if type == 'jp2h'
359
+ stop = offset + size unless size.zero?
360
+ offset += data_offset
361
+ in_header = true
362
+ elsif in_header && type == 'ihdr'
363
+ return ir.unpack(offset + data_offset, 8, 'NN').reverse
364
+ else
365
+ break if size.zero? # box to the end of file
366
+
367
+ offset += size
368
+ end
300
369
  end
301
370
  end
371
+ alias_method :size_of_jpx, :size_of_jp2
372
+
373
+ def size_of_j2c(ir)
374
+ ir.unpack(8, 8, 'NN')
375
+ end
302
376
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+
5
+ require 'image_size/chunky_reader'
6
+
7
+ describe ImageSize::ChunkyReader do
8
+ context :[] do
9
+ test_reader = Class.new do
10
+ include ImageSize::ChunkyReader
11
+
12
+ def initialize(string)
13
+ @string = string
14
+ end
15
+
16
+ private
17
+
18
+ def chunk(i)
19
+ @string[i * chunk_size, chunk_size]
20
+ end
21
+ end
22
+
23
+ custom_chunk_size_reader = Class.new(test_reader) do
24
+ def chunk_size
25
+ 100
26
+ end
27
+ end
28
+
29
+ {
30
+ 'empty string' => '',
31
+ 'a bit of data' => 'foo bar baz',
32
+ 'a lot of data' => File.open('GPL', 'rb', &:read),
33
+ }.each do |data_description, data|
34
+ {
35
+ 'default' => test_reader.new(data),
36
+ 'custom' => custom_chunk_size_reader.new(data),
37
+ }.each do |chunk_size_description, reader|
38
+ context "for #{data_description} using reader with #{chunk_size_description} chunk size" do
39
+ it 'raises ArgumentError for negative offset' do
40
+ [-1, 0, 1, 100].each do |length|
41
+ expect{ reader[-1, length] }.to raise_exception(ArgumentError)
42
+ end
43
+ end
44
+
45
+ it 'behaves same as fetching a string for any offset and length' do
46
+ full_chunks = data.length / reader.chunk_size
47
+ offsets = [0, 1, full_chunks - 1, full_chunks, full_chunks + 1].map do |i|
48
+ [-1, 0, 1].map do |add|
49
+ (i * reader.chunk_size) + add
50
+ end
51
+ end.flatten
52
+
53
+ offsets.each do |offset|
54
+ next if offset < 0
55
+
56
+ offsets.each do |offset_b|
57
+ length = offset_b - offset
58
+ expect(reader[offset, length]).to eq(data[offset, length]),
59
+ "for offset #{offset} and length #{length}\n"\
60
+ "expected: #{data[offset, length].inspect}\n"\
61
+ " got: #{reader[offset, length].inspect}"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,11 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rspec'
4
+
4
5
  require 'image_size'
6
+ require 'image_size/uri_reader'
7
+
5
8
  require 'tempfile'
9
+ require 'shellwords'
10
+ require 'webrick'
6
11
 
7
12
  describe ImageSize do
8
- (Dir['spec/images/*/*.*'] + [__FILE__]).each do |path|
13
+ before :all do
14
+ @server = WEBrick::HTTPServer.new({
15
+ :Logger => WEBrick::Log.new(StringIO.new),
16
+ :AccessLog => [],
17
+ :BindAddress => '127.0.0.1',
18
+ :Port => 0, # get the next available port
19
+ :DocumentRoot => '.',
20
+ })
21
+ @server_thread = Thread.new{ @server.start }
22
+ @server_base_url = URI("http://localhost:#{@server.config[:Port]}/")
23
+ end
24
+
25
+ after :all do
26
+ @server.shutdown
27
+ @server_thread.join
28
+ end
29
+
30
+ max_filesize = 16_384
31
+
32
+ (Dir['spec/images/*/*.*'] + [__FILE__[%r{spec/.+?\z}]]).each do |path|
33
+ filesize = File.size(path)
34
+ warn "#{path} is too big #{filesize} (max #{max_filesize})" if filesize > max_filesize
35
+
9
36
  describe "for #{path}" do
10
37
  let(:name){ File.basename(path) }
11
38
  let(:attributes) do
@@ -45,13 +72,22 @@ describe ImageSize do
45
72
  end
46
73
  end
47
74
 
75
+ context 'given as unseekable IO' do
76
+ it 'gets format and dimensions' do
77
+ IO.popen(%W[cat #{path}].shelljoin, 'rb') do |io|
78
+ image_size = ImageSize.new(io)
79
+ expect(image_size).to have_attributes(attributes)
80
+ expect(io).not_to be_closed
81
+ end
82
+ end
83
+ end
84
+
48
85
  context 'given as StringIO' do
49
86
  it 'gets format and dimensions' do
50
87
  io = StringIO.new(file_data)
51
88
  image_size = ImageSize.new(io)
52
89
  expect(image_size).to have_attributes(attributes)
53
90
  expect(io).not_to be_closed
54
- expect(io.pos).to_not be_zero
55
91
  io.rewind
56
92
  expect(io.read).to eq(file_data)
57
93
  end
@@ -79,6 +115,13 @@ describe ImageSize do
79
115
  expect(image_size).to have_attributes(attributes)
80
116
  end
81
117
  end
118
+
119
+ context 'fetching from webserver' do
120
+ it 'gets format and dimensions' do
121
+ image_size = ImageSize.url(@server_base_url + path)
122
+ expect(image_size).to have_attributes(attributes)
123
+ end
124
+ end
82
125
  end
83
126
  end
84
127
 
@@ -0,0 +1 @@
1
+ * -text
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
File without changes
@@ -0,0 +1,8 @@
1
+ P7
2
+ WIDTH 22
3
+ HEIGHT 25
4
+ DEPTH 3
5
+ MAXVAL 255
6
+ TUPLTYPE RGB
7
+ ENDHDR
8
+ JMRGJONQVKNSRVZY^b{��|��osyz����Ǎ��v|�������UY_ORT�^_�ts�_b`FH�`aDGLHJPJMQfjnflplqu}�����u{�������nsw[`d]bgqv}aej>AFLLO�`c�ro�bdxORlsvZ_ccknS[^imrwz��������������ɑ��UY]���u{~��glosz~X^b�_^�li�lk���dimORWORWRUZY\a������������������UW\������]`ehmqOPU>AHfOR�lj�kjv}�KNSKOREILEIMTX[LQUAEJHJOZ]a_di���muz������=<BSUZrv|���}pt�jh�liPWZGJOIMPEILDHK=AE`eiuz~������}��������������XZ_MMR[^e�������ed�heHKPPSWFKNAFIMRV`hk���������nuxW\`V[_CHLu|����[^dBBGDHMBEJomr�hf�geJMSZ\aDILekn������dimPUYMNSBBGCDIFGLDEJ]`f���chlDGLT\^EMQ]MQ�ji�ecoehXadJMR���Y]a_afcdjORWKNTMTYJNRFFICCG]`d���z�==BFGLAEK�VX�ki�ec�nrLQWNSZdjmkqtBBHABGPX]^ltScgIMPCDHJJNQTX�����JKPPSX_^d�__�jf�ca�st�_bjX[jsw���^eh=@EIPUQX^RW\UY]JNR@AEegn���v{FFLmuy�]_�ec�gd�ca�zx�zx�po�cg~����y��YZ^CEIEGKSUZejnMRUZ]b���osxADIm[_�_`�ca�ec�db�st�|}�zy�vt�gh�ot�����Ϣ��ehlEGLX]`PRVX[_���dek7<@�RT�b_�a_�`^�ca�ux�{~�|z�yv�{w�rp�ii�����������ntwDDIgjn���`cjSSW�^^�gd�b`�`^�b`�yv�wx�xu�|x�yu�vs�vs�oo�ii�km������FHMimq���QX_x`d�fe�fb�da�b`�ca�wu�rp�vt�us�tr�vs�ts�sr�po�uv�ml�x|V[_gin���OU[�Z\�fc�ea�db�ca�b`�zx�tq�vt�us�wt�vs�wt�sq�rr�st�sq�giTX\SVZ���fbg�ce�eb�ca�ec�db�ec�yu�ur�sq�sr�vs�xu�xs�tq�xt�ws�xt�ihKMRchm���]NP�ge�eb�ca�db�ec�fd�zv�sq�rq�sr�rp�xu�so�rp�tq�qn�uq�bcHIN\bgo|�wQU�jh�fd�ca�b`�ca�ec�zy�xt�tr�vs�sr�sr�tq�sp�rp�kj�mk�^^HLPLOTfqv�UV�jf�ec�db�b`�cb�ca�ss�{w�us�tr�ur�wt�tq�qo�ro�lj�on�ZZ=BFNNTRZ_�YY�ie�db�db�`_�_^�`_�ij�~|�vs�ur�sq�pn�qo�ol�ji�mj�qn�Z]CFL]_dLIN�aa�fb�ca�cb�a`�__�]^x_^�{x�ws�wt�sp�pn�po�lknZ^�lk�pmxXYDINkrwkUX�mj�db�fdX[�Y\�`_�]]�gi�vs�us�xu�tr�nk�lj�nk�jj�mk�kjlPR?CGGIP�bb�pm�fd�da�Y\�]]�b_�`_�ru�ml�ur�wu�tr�pk�lg�oi�oj�pl�kiaMO;@EPGI�kj�he�fd�fc�b`�a^�b`�_^
Binary file
@@ -0,0 +1,4 @@
1
+ P5
2
+ 22 25
3
+ 255
4
+ MJQNU]��sÑ{��YRh�mLmGJMikp��z��r_aueALi�rXr^jZmz����ŗX�z�ky]k��hRRU\������W��`lPAT~|NNHHWPEJ]c�t��=Uv�s|VJLHG@dy�������ZM^��v|KSJEQg���t[ZG{�^BHEnx{M\Hj��hTNBDGE`�gGZLQ}yg_M�\adRNSMFC`�~=GE_yzPRipBBWj`LDJT��KS_n~w�i\q�d@OWVXMAg�zFtgz{w���j��ZEGUiQ]�sD_rwyx|���ss�ͩhG\R[�e;]wutw������u����sDj�cSm{vtv��������{w��Hl�We{zxvw����������~{Zi�Te{yxwv�����������qWV�ctywyxy�����������rMg�Qzywxyz�����������lIazY~zwvwy���������~�gKOo_yxvww~���������cANYe~xxutup�������w�cF_Jq{wxvtsd������x_�_HqZ�xzahvrm�����{�~VBIl�zweowu{{�������~Q?I||zzwuvt
@@ -0,0 +1,4 @@
1
+ P6
2
+ 22 25
3
+ 255
4
+ JMRGJONQVKNSRVZY^b{��|��osyz����Ǎ��v|�������UY_ORT�^_�ts�_b`FH�`aDGLHJPJMQfjnflplqu}�����u{�������nsw[`d]bgqv}aej>AFLLO�`c�ro�bdxORlsvZ_ccknS[^imrwz��������������ɑ��UY]���u{~��glosz~X^b�_^�li�lk���dimORWORWRUZY\a������������������UW\������]`ehmqOPU>AHfOR�lj�kjv}�KNSKOREILEIMTX[LQUAEJHJOZ]a_di���muz������=<BSUZrv|���}pt�jh�liPWZGJOIMPEILDHK=AE`eiuz~������}��������������XZ_MMR[^e�������ed�heHKPPSWFKNAFIMRV`hk���������nuxW\`V[_CHLu|����[^dBBGDHMBEJomr�hf�geJMSZ\aDILekn������dimPUYMNSBBGCDIFGLDEJ]`f���chlDGLT\^EMQ]MQ�ji�ecoehXadJMR���Y]a_afcdjORWKNTMTYJNRFFICCG]`d���z�==BFGLAEK�VX�ki�ec�nrLQWNSZdjmkqtBBHABGPX]^ltScgIMPCDHJJNQTX�����JKPPSX_^d�__�jf�ca�st�_bjX[jsw���^eh=@EIPUQX^RW\UY]JNR@AEegn���v{FFLmuy�]_�ec�gd�ca�zx�zx�po�cg~����y��YZ^CEIEGKSUZejnMRUZ]b���osxADIm[_�_`�ca�ec�db�st�|}�zy�vt�gh�ot�����Ϣ��ehlEGLX]`PRVX[_���dek7<@�RT�b_�a_�`^�ca�ux�{~�|z�yv�{w�rp�ii�����������ntwDDIgjn���`cjSSW�^^�gd�b`�`^�b`�yv�wx�xu�|x�yu�vs�vs�oo�ii�km������FHMimq���QX_x`d�fe�fb�da�b`�ca�wu�rp�vt�us�tr�vs�ts�sr�po�uv�ml�x|V[_gin���OU[�Z\�fc�ea�db�ca�b`�zx�tq�vt�us�wt�vs�wt�sq�rr�st�sq�giTX\SVZ���fbg�ce�eb�ca�ec�db�ec�yu�ur�sq�sr�vs�xu�xs�tq�xt�ws�xt�ihKMRchm���]NP�ge�eb�ca�db�ec�fd�zv�sq�rq�sr�rp�xu�so�rp�tq�qn�uq�bcHIN\bgo|�wQU�jh�fd�ca�b`�ca�ec�zy�xt�tr�vs�sr�sr�tq�sp�rp�kj�mk�^^HLPLOTfqv�UV�jf�ec�db�b`�cb�ca�ss�{w�us�tr�ur�wt�tq�qo�ro�lj�on�ZZ=BFNNTRZ_�YY�ie�db�db�`_�_^�`_�ij�~|�vs�ur�sq�pn�qo�ol�ji�mj�qn�Z]CFL]_dLIN�aa�fb�ca�cb�a`�__�]^x_^�{x�ws�wt�sp�pn�po�lknZ^�lk�pmxXYDINkrwkUX�mj�db�fdX[�Y\�`_�]]�gi�vs�us�xu�tr�nk�lj�nk�jj�mk�kjlPR?CGGIP�bb�pm�fd�da�Y\�]]�b_�`_�ru�ml�ur�wu�tr�pk�lg�oi�oj�pl�kiaMO;@EPGI�kj�he�fd�fc�b`�a^�b`�_^
@@ -0,0 +1,27 @@
1
+ P1
2
+ 22 25
3
+ 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 1 1 1 1 1 1 1
4
+ 1 1 1 1 1 1 0 0 1 0 0 0 0 1 0 1 1 1 1 1 1 1
5
+ 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 1 1 1 1 0
6
+ 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 1 1 1 1 1 0 1
7
+ 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 0
8
+ 1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 0 0 1 1
9
+ 1 1 1 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1
10
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0
11
+ 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1
12
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1
13
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 0 0 0
14
+ 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 1
15
+ 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 1 1 1 1 1 0 1
16
+ 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1
17
+ 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 1 0 1 1 0
18
+ 0 0 1 0 1 0 0 0 1 0 1 1 1 1 0 1 1 0 1 0 1 1
19
+ 1 0 0 1 0 1 0 0 1 0 0 1 1 1 0 1 1 1 0 1 1 0
20
+ 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 0 1 0 0 1
21
+ 1 0 1 1 0 1 0 1 0 1 0 1 1 1 1 1 0 1 1 1 0 1
22
+ 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 1 0 0 1 1 1
23
+ 1 0 0 1 1 0 1 0 0 1 0 1 1 1 1 1 1 0 0 1 1 1
24
+ 1 0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 0 1 1 1 0 1
25
+ 1 0 0 0 0 1 0 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1
26
+ 1 0 1 0 0 0 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 0
27
+ 1 0 0 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 0 1 1 0
@@ -0,0 +1,28 @@
1
+ P2
2
+ 22 25
3
+ 255
4
+ 77 74 81 78 85 93 129 129 115 127 195 145 123 169 147 89 82 104 135 109 76 109
5
+ 71 74 77 105 107 112 132 169 122 151 204 114 95 97 117 101 65 76 105 133 114 88
6
+ 114 94 106 90 109 122 165 164 178 187 197 151 88 134 122 130 107 121 93 107 128 127
7
+ 158 104 82 82 85 92 144 155 154 180 157 183 87 182 145 96 108 80 65 84 127 126
8
+ 124 78 78 72 72 87 80 69 74 93 99 157 116 160 139 61 85 118 141 115 124 127
9
+ 86 74 76 72 71 64 100 121 139 170 132 142 154 182 160 90 77 94 142 145 118 124
10
+ 75 83 74 69 81 103 137 170 158 116 91 90 71 123 173 94 66 72 69 110 120 123
11
+ 77 92 72 106 138 140 104 84 78 66 68 71 69 96 161 103 71 90 76 81 125 121
12
+ 103 95 77 134 92 97 100 82 78 83 77 70 67 96 185 126 61 71 69 95 127 121
13
+ 122 80 82 105 112 66 66 87 106 96 76 68 74 84 180 130 75 83 95 110 126 119
14
+ 131 105 92 113 140 100 64 79 87 86 88 77 65 103 189 122 70 116 103 122 123 119
15
+ 138 138 128 106 127 158 128 90 69 71 85 105 81 93 191 115 68 95 114 119 121 120
16
+ 124 142 140 137 115 115 168 205 169 104 71 92 82 91 185 101 59 93 119 117 116 119
17
+ 131 140 140 138 140 131 117 132 146 166 148 115 68 106 192 99 83 109 123 118 116 118
18
+ 138 137 137 140 138 135 136 129 123 119 138 140 72 108 178 87 101 123 122 120 118 119
19
+ 136 132 136 135 134 135 134 134 128 133 126 123 90 105 177 84 101 123 121 120 119 118
20
+ 138 134 136 135 136 135 136 133 131 132 134 113 87 86 163 99 116 121 119 121 120 121
21
+ 137 134 133 133 135 137 137 134 137 136 138 114 77 103 147 81 122 121 119 120 121 122
22
+ 139 133 132 132 129 137 132 132 134 131 135 108 73 97 122 89 126 122 119 118 119 121
23
+ 137 137 134 135 133 133 134 133 132 126 129 103 75 79 111 95 127 121 120 118 119 119
24
+ 126 140 135 134 134 136 134 131 133 127 131 99 65 78 89 101 126 120 120 117 116 117
25
+ 112 143 135 134 133 130 132 129 119 127 133 99 70 95 74 113 123 119 120 118 116 115
26
+ 100 139 136 136 133 131 132 120 95 127 132 95 72 113 90 128 120 122 97 104 118 114
27
+ 109 133 135 137 134 128 127 127 123 128 126 86 66 73 108 131 122 119 101 111 119 117
28
+ 123 123 135 136 134 130 127 129 129 130 126 81 63 73 124 124 122 122 119 117 118 116