fastimage 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.textile +8 -7
- data/lib/fastimage.rb +95 -20
- data/lib/fastimage/fbr.rb +67 -0
- data/test/fixtures/test.tiff +0 -0
- data/test/fixtures/test2.tiff +0 -0
- data/test/test.rb +5 -3
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc7eb99ff0f61cddf103ac901c5bda2d37f83752
|
4
|
+
data.tar.gz: fccb0a33cfccefc1400c4d7c3c27a1124d2b6400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 000bc8011c9da184973abc60e5b119fd5a8ef8b1d3657cd80dd26ae12c67298d1dcd73ad7d2ed60c783be8d4542f1f57228e6c0b888ebb313499739173c5ec7b
|
7
|
+
data.tar.gz: fa02a8dfdaf54cc85e94fa468839b2c2fb718f35c4199959859109925f20c3fa63fcf6574fcce83c22a13cda94ccc4caed6f05eb82a907a635d4bc7f59c9fe55
|
data/README.textile
CHANGED
@@ -10,7 +10,7 @@ But the image is not locally stored - it's on another asset server, or in the cl
|
|
10
10
|
|
11
11
|
You don't want to download the entire image to your app server - it could be many tens of kilobytes, or even megabytes just to get this information. For most image types, the size of the image is simply stored at the start of the file. For JPEG files it's a little bit more complex, but even so you do not need to fetch much of the image to find the size.
|
12
12
|
|
13
|
-
FastImage does this minimal fetch for image types GIF, JPEG, PNG and BMP. And it doesn't rely on installing external libraries such as RMagick (which relies on ImageMagick or GraphicsMagick) or ImageScience (which relies on FreeImage).
|
13
|
+
FastImage does this minimal fetch for image types GIF, JPEG, PNG, TIFF and BMP. And it doesn't rely on installing external libraries such as RMagick (which relies on ImageMagick or GraphicsMagick) or ImageScience (which relies on FreeImage).
|
14
14
|
|
15
15
|
You only need supply the uri, and FastImage will do the rest.
|
16
16
|
|
@@ -70,13 +70,13 @@ h2. Tests
|
|
70
70
|
|
71
71
|
You'll need to @gem install fakeweb@ to be able to run the tests.
|
72
72
|
|
73
|
-
bc.. ruby
|
74
|
-
|
75
|
-
Started
|
76
|
-
...............
|
77
|
-
Finished in 0.059392 seconds.
|
73
|
+
bc.. $ ruby test.rb
|
74
|
+
Run options:
|
78
75
|
|
79
|
-
|
76
|
+
# Running tests:
|
77
|
+
|
78
|
+
Finished tests in 1.033640s, 23.2189 tests/s, 82.2337 assertions/s.
|
79
|
+
24 tests, 85 assertions, 0 failures, 0 errors, 0 skips
|
80
80
|
|
81
81
|
h2. References
|
82
82
|
|
@@ -84,6 +84,7 @@ h2. References
|
|
84
84
|
* "DZone - Determine Image Size":http://snippets.dzone.com/posts/show/805
|
85
85
|
* "Antti Kupila - Getting JPG dimensions with AS3 without loading the entire file":http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/
|
86
86
|
* "imagesize gem documentation":http://imagesize.rubyforge.org/
|
87
|
+
* "EXIF Reader":https://github.com/remvee/exifr
|
87
88
|
|
88
89
|
h2. Licence
|
89
90
|
|
data/lib/fastimage.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
# No external libraries such as ImageMagick are used here, this is a very lightweight solution to
|
9
9
|
# finding image information.
|
10
10
|
#
|
11
|
-
# FastImage knows about GIF, JPEG, BMP and PNG files.
|
11
|
+
# FastImage knows about GIF, JPEG, BMP, TIFF and PNG files.
|
12
12
|
#
|
13
13
|
# FastImage can also read files from the local filesystem by supplying the path instead of a uri.
|
14
14
|
# In this case FastImage uses the open-uri library to read the file in chunks of 256 bytes until
|
@@ -37,13 +37,17 @@
|
|
37
37
|
# * http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/
|
38
38
|
# * http://pennysmalls.wordpress.com/2008/08/19/find-jpeg-dimensions-fast-in-pure-ruby-no-ima/
|
39
39
|
# * http://imagesize.rubyforge.org/
|
40
|
+
# * https://github.com/remvee/exifr
|
40
41
|
#
|
41
42
|
|
42
43
|
require 'net/https'
|
43
44
|
require 'open-uri'
|
45
|
+
require 'fastimage/fbr.rb'
|
44
46
|
|
45
47
|
class FastImage
|
46
48
|
attr_reader :size, :type
|
49
|
+
|
50
|
+
attr_reader :bytes_read
|
47
51
|
|
48
52
|
class FastImageException < StandardError # :nodoc:
|
49
53
|
end
|
@@ -55,6 +59,8 @@ class FastImage
|
|
55
59
|
end
|
56
60
|
class SizeNotFound < FastImageException # :nodoc:
|
57
61
|
end
|
62
|
+
class CannotParseImage < FastImageException # :nodoc:
|
63
|
+
end
|
58
64
|
|
59
65
|
DefaultTimeout = 2
|
60
66
|
|
@@ -69,7 +75,7 @@ class FastImage
|
|
69
75
|
# If you wish FastImage to raise if it cannot size the image for any reason, then pass
|
70
76
|
# :raise_on_failure => true in the options.
|
71
77
|
#
|
72
|
-
# FastImage knows about GIF, JPEG, BMP and PNG files.
|
78
|
+
# FastImage knows about GIF, JPEG, BMP, TIFF and PNG files.
|
73
79
|
#
|
74
80
|
# === Example
|
75
81
|
#
|
@@ -133,6 +139,8 @@ class FastImage
|
|
133
139
|
# => nil
|
134
140
|
# File.open("/some/local/file.gif", "r") {|io| FastImage.type(io)}
|
135
141
|
# => :gif
|
142
|
+
# FastImage.type("test/fixtures/test.tiff")
|
143
|
+
# => :tiff
|
136
144
|
#
|
137
145
|
# === Supported options
|
138
146
|
# [:timeout]
|
@@ -164,7 +172,9 @@ class FastImage
|
|
164
172
|
end
|
165
173
|
end
|
166
174
|
end
|
175
|
+
|
167
176
|
raise SizeNotFound if options[:raise_on_failure] && @property == :size && !@size
|
177
|
+
|
168
178
|
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET,
|
169
179
|
ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT
|
170
180
|
raise ImageFetchFailure if options[:raise_on_failure]
|
@@ -172,6 +182,15 @@ class FastImage
|
|
172
182
|
raise ImageFetchFailure if options[:raise_on_failure]
|
173
183
|
rescue UnknownImageType
|
174
184
|
raise UnknownImageType if options[:raise_on_failure]
|
185
|
+
rescue CannotParseImage
|
186
|
+
if options[:raise_on_failure]
|
187
|
+
if @property == :size
|
188
|
+
raise SizeNotFound
|
189
|
+
else
|
190
|
+
raise ImageFetchFailure
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
175
194
|
end
|
176
195
|
|
177
196
|
private
|
@@ -198,10 +217,14 @@ class FastImage
|
|
198
217
|
|
199
218
|
raise ImageFetchFailure unless res.is_a?(Net::HTTPSuccess)
|
200
219
|
|
201
|
-
|
202
|
-
|
220
|
+
@read_fiber = Fiber.new do
|
221
|
+
res.read_body do |str|
|
222
|
+
Fiber.yield str
|
223
|
+
end
|
203
224
|
end
|
204
225
|
|
226
|
+
parse_packets
|
227
|
+
|
205
228
|
break # needed to actively quit out of the fetch
|
206
229
|
end
|
207
230
|
end
|
@@ -231,9 +254,13 @@ class FastImage
|
|
231
254
|
end
|
232
255
|
|
233
256
|
def fetch_using_read(readable)
|
234
|
-
|
235
|
-
|
257
|
+
@read_fiber = Fiber.new do
|
258
|
+
while str = readable.read(LocalFileChunkSize)
|
259
|
+
Fiber.yield str
|
260
|
+
end
|
236
261
|
end
|
262
|
+
|
263
|
+
parse_packets
|
237
264
|
end
|
238
265
|
|
239
266
|
def fetch_using_open_uri
|
@@ -242,20 +269,21 @@ class FastImage
|
|
242
269
|
end
|
243
270
|
end
|
244
271
|
|
245
|
-
|
246
|
-
|
247
|
-
def parse_packet(str)
|
248
|
-
@str = (@unused_str || "") + str
|
272
|
+
def parse_packets
|
273
|
+
@str = ""
|
249
274
|
@str.force_encoding("ASCII-8BIT") if has_encoding?
|
250
275
|
@strpos = 0
|
276
|
+
@bytes_read = 0
|
277
|
+
|
251
278
|
begin
|
252
279
|
result = send("parse_#{@property}")
|
253
280
|
if result
|
254
281
|
instance_variable_set("@#{@property}", result)
|
255
|
-
|
282
|
+
else
|
283
|
+
raise CannotParseImage
|
256
284
|
end
|
257
|
-
rescue
|
258
|
-
|
285
|
+
rescue FiberError
|
286
|
+
raise CannotParseImage
|
259
287
|
end
|
260
288
|
end
|
261
289
|
|
@@ -274,15 +302,25 @@ class FastImage
|
|
274
302
|
end
|
275
303
|
|
276
304
|
def get_chars(n)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
@strpos += n
|
305
|
+
while @strpos + n - 1 >= @str.size
|
306
|
+
unused_str = @str[@strpos..-1]
|
307
|
+
new_string = @read_fiber.resume
|
308
|
+
raise CannotParseImage if !new_string
|
309
|
+
|
283
310
|
# we are dealing with bytes here, so force the encoding
|
284
|
-
has_encoding?
|
311
|
+
if has_encoding?
|
312
|
+
new_string.force_encoding("ASCII-8BIT")
|
313
|
+
end
|
314
|
+
|
315
|
+
@bytes_read += new_string.size
|
316
|
+
|
317
|
+
@str = unused_str + new_string
|
318
|
+
@strpos = 0
|
285
319
|
end
|
320
|
+
|
321
|
+
result = @str[@strpos..(@strpos + n - 1)]
|
322
|
+
@strpos += n
|
323
|
+
result
|
286
324
|
end
|
287
325
|
|
288
326
|
def get_byte
|
@@ -304,6 +342,10 @@ class FastImage
|
|
304
342
|
:jpeg
|
305
343
|
when 0x89.chr + "P"
|
306
344
|
:png
|
345
|
+
when "II"
|
346
|
+
:tiff
|
347
|
+
when "MM"
|
348
|
+
:tiff
|
307
349
|
else
|
308
350
|
raise UnknownImageType
|
309
351
|
end
|
@@ -353,4 +395,37 @@ class FastImage
|
|
353
395
|
d = get_chars(29)[14..28]
|
354
396
|
d.unpack("C")[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS')
|
355
397
|
end
|
398
|
+
|
399
|
+
def parse_size_for_tiff
|
400
|
+
byte_order = get_chars(2)
|
401
|
+
case byte_order
|
402
|
+
when 'II'; short, long = 'v', 'V'
|
403
|
+
when 'MM'; short, long = 'n', 'N'
|
404
|
+
end
|
405
|
+
get_chars(2) # 42
|
406
|
+
|
407
|
+
offset = get_chars(4).unpack(long)[0]
|
408
|
+
get_chars(offset - 8)
|
409
|
+
|
410
|
+
width = height = nil
|
411
|
+
|
412
|
+
tag_count = get_chars(2).unpack(short)[0]
|
413
|
+
tag_count.downto(1) do
|
414
|
+
type = get_chars(2).unpack(short)[0]
|
415
|
+
get_chars(6)
|
416
|
+
data = get_chars(2).unpack(short)[0]
|
417
|
+
case type
|
418
|
+
when 0x0100 # image width
|
419
|
+
width = data
|
420
|
+
when 0x0101 # image height
|
421
|
+
height = data
|
422
|
+
end
|
423
|
+
if width && height
|
424
|
+
return [width, height]
|
425
|
+
end
|
426
|
+
get_chars(2)
|
427
|
+
end
|
428
|
+
|
429
|
+
raise CannotParseImage
|
430
|
+
end
|
356
431
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
|
2
|
+
# (c) 2008 Aman Gupta (tmm1)
|
3
|
+
|
4
|
+
unless defined? Fiber
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
class FiberError < StandardError; # :nodoc:
|
8
|
+
end
|
9
|
+
|
10
|
+
class Fiber # :nodoc:
|
11
|
+
def initialize
|
12
|
+
raise ArgumentError, 'new Fiber requires a block' unless block_given?
|
13
|
+
|
14
|
+
@yield = Queue.new
|
15
|
+
@resume = Queue.new
|
16
|
+
|
17
|
+
@thread = Thread.new{ @yield.push [yield(*@resume.pop)] }
|
18
|
+
@thread.abort_on_exception = true
|
19
|
+
@thread[:fiber] = self
|
20
|
+
end
|
21
|
+
attr_reader :thread
|
22
|
+
|
23
|
+
def resume *args
|
24
|
+
raise FiberError, 'dead fiber called' unless @thread.alive?
|
25
|
+
@resume.push(args)
|
26
|
+
result = @yield.pop
|
27
|
+
result.size > 1 ? result : result.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def yield *args
|
31
|
+
@yield.push(args)
|
32
|
+
result = @resume.pop
|
33
|
+
result.size > 1 ? result : result.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.yield *args
|
37
|
+
if fiber = Thread.current[:fiber]
|
38
|
+
fiber.yield(*args)
|
39
|
+
else
|
40
|
+
raise FiberError, 'not inside a fiber'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.current
|
45
|
+
if Thread.current == Thread.main
|
46
|
+
return Thread.main[:fiber] ||= RootFiber.new
|
47
|
+
end
|
48
|
+
|
49
|
+
Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class RootFiber < Fiber # :nodoc:
|
58
|
+
def initialize
|
59
|
+
# XXX: what is a root fiber anyway?
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.yield *args
|
63
|
+
raise FiberError, "can't yield from root fiber"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
Binary file
|
Binary file
|
data/test/test.rb
CHANGED
@@ -3,9 +3,9 @@ require 'rubygems'
|
|
3
3
|
require 'test/unit'
|
4
4
|
|
5
5
|
PathHere = File.dirname(__FILE__)
|
6
|
+
$LOAD_PATH.unshift File.join(PathHere, "..", "lib")
|
6
7
|
|
7
|
-
require
|
8
|
-
|
8
|
+
require 'fastimage'
|
9
9
|
require 'fakeweb'
|
10
10
|
|
11
11
|
FixturePath = File.join(PathHere, "fixtures")
|
@@ -16,7 +16,9 @@ GoodFixtures = {
|
|
16
16
|
"test.jpg"=>[:jpeg, [882, 470]],
|
17
17
|
"test.png"=>[:png, [30, 20]],
|
18
18
|
"test2.jpg"=>[:jpeg, [250, 188]],
|
19
|
-
"test3.jpg"=>[:jpeg, [630,367]]
|
19
|
+
"test3.jpg"=>[:jpeg, [630, 367]],
|
20
|
+
"test.tiff"=>[:tiff, [85, 67]],
|
21
|
+
"test2.tiff"=>[:tiff, [333, 225]]
|
20
22
|
}
|
21
23
|
|
22
24
|
BadFixtures = [
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastimage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Sykes
|
@@ -21,6 +21,7 @@ files:
|
|
21
21
|
- MIT-LICENSE
|
22
22
|
- README.textile
|
23
23
|
- lib/fastimage.rb
|
24
|
+
- lib/fastimage/fbr.rb
|
24
25
|
- test/fixtures/faulty.jpg
|
25
26
|
- test/fixtures/test.bmp
|
26
27
|
- test/fixtures/test.gif
|
@@ -29,6 +30,8 @@ files:
|
|
29
30
|
- test/fixtures/test.png
|
30
31
|
- test/fixtures/test2.jpg
|
31
32
|
- test/fixtures/test3.jpg
|
33
|
+
- test/fixtures/test.tiff
|
34
|
+
- test/fixtures/test2.tiff
|
32
35
|
- test/fixtures/folder with spaces/test.bmp
|
33
36
|
- test/test.rb
|
34
37
|
homepage: http://github.com/sdsykes/fastimage
|