fastimage 2.1.3 → 2.2.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.
- checksums.yaml +5 -5
- data/README.textile +1 -1
- data/lib/fastimage.rb +138 -12
- metadata +15 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 53c0966ef5216c189e2950b1ad36c106777175f0d4dca9851abd34472fd9cb61
|
4
|
+
data.tar.gz: 29f5a6bc03623e184166e7f9c5e8c4379011b301904d4c690037d5b7b0ba0b33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28e8ec4debe6c2c477da19cafb1ad41e5de4d809136bd86621ae128372c32c1259abea833856f73dfcfe8ef3444bc5e1a148f91b52ee02d3292235c4d95fc542
|
7
|
+
data.tar.gz: ba8b4c6bb42146387b1a8af718fbbaf74a9a94532ee5adce082ff676095dad5d5e0eb613f1ec41d7729709847195fa81c4614f2d9d510d6c7def67f8970f168e
|
data/README.textile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
!https://img.shields.io/gem/dt/fastimage.svg!:https://rubygems.org/gems/fastimage
|
2
|
-
!https://travis-ci.org/sdsykes/fastimage.
|
2
|
+
!https://travis-ci.org/sdsykes/fastimage.svg?branch=master!:https://travis-ci.org/sdsykes/fastimage
|
3
3
|
|
4
4
|
h1. FastImage
|
5
5
|
|
data/lib/fastimage.rb
CHANGED
@@ -70,7 +70,7 @@ if RUBY_VERSION < "2.2"
|
|
70
70
|
end
|
71
71
|
|
72
72
|
class FastImage
|
73
|
-
attr_reader :size, :type, :content_length, :orientation
|
73
|
+
attr_reader :size, :type, :content_length, :orientation, :animated
|
74
74
|
|
75
75
|
attr_reader :bytes_read
|
76
76
|
|
@@ -179,6 +179,34 @@ class FastImage
|
|
179
179
|
new(uri, options.merge(:type_only=>true)).type
|
180
180
|
end
|
181
181
|
|
182
|
+
# Returns a boolean value indicating the image is animated.
|
183
|
+
# It will return nil if the image could not be fetched, or if the image type was not recognised.
|
184
|
+
#
|
185
|
+
# By default there is a timeout of 2 seconds for opening and reading from a remote server.
|
186
|
+
# This can be changed by passing a :timeout => number_of_seconds in the options.
|
187
|
+
#
|
188
|
+
# If you wish FastImage to raise if it cannot find the type of the image for any reason, then pass
|
189
|
+
# :raise_on_failure => true in the options.
|
190
|
+
#
|
191
|
+
# === Example
|
192
|
+
#
|
193
|
+
# require 'fastimage'
|
194
|
+
#
|
195
|
+
# FastImage.animated?("test/fixtures/test.gif")
|
196
|
+
# => false
|
197
|
+
# FastImage.animated?("test/fixtures/animated.gif")
|
198
|
+
# => true
|
199
|
+
#
|
200
|
+
# === Supported options
|
201
|
+
# [:timeout]
|
202
|
+
# Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
|
203
|
+
# [:raise_on_failure]
|
204
|
+
# If set to true causes an exception to be raised if the image type cannot be found for any reason.
|
205
|
+
#
|
206
|
+
def self.animated?(uri, options={})
|
207
|
+
new(uri, options.merge(:animated_only=>true)).animated
|
208
|
+
end
|
209
|
+
|
182
210
|
def initialize(uri, options={})
|
183
211
|
@uri = uri
|
184
212
|
@options = {
|
@@ -189,7 +217,13 @@ class FastImage
|
|
189
217
|
:http_header => {}
|
190
218
|
}.merge(options)
|
191
219
|
|
192
|
-
@property = @options[:
|
220
|
+
@property = if @options[:animated_only]
|
221
|
+
:animated
|
222
|
+
elsif @options[:type_only]
|
223
|
+
:type
|
224
|
+
else
|
225
|
+
:size
|
226
|
+
end
|
193
227
|
|
194
228
|
@type, @state = nil
|
195
229
|
|
@@ -214,7 +248,8 @@ class FastImage
|
|
214
248
|
raise SizeNotFound if @options[:raise_on_failure] && @property == :size && !@size
|
215
249
|
|
216
250
|
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET,
|
217
|
-
ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT,
|
251
|
+
Errno::ENETUNREACH, ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT,
|
252
|
+
OpenSSL::SSL::SSLError
|
218
253
|
raise ImageFetchFailure if @options[:raise_on_failure]
|
219
254
|
rescue NoMethodError # 1.8.7p248 can raise this due to a net/http bug
|
220
255
|
raise ImageFetchFailure if @options[:raise_on_failure]
|
@@ -242,6 +277,17 @@ class FastImage
|
|
242
277
|
fetch_using_http_from_parsed_uri
|
243
278
|
end
|
244
279
|
|
280
|
+
# Some invalid locations need escaping
|
281
|
+
def escaped_location(location)
|
282
|
+
begin
|
283
|
+
URI(location)
|
284
|
+
rescue URI::InvalidURIError
|
285
|
+
::URI::DEFAULT_PARSER.escape(location)
|
286
|
+
else
|
287
|
+
location
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
245
291
|
def fetch_using_http_from_parsed_uri
|
246
292
|
http_header = {'Accept-Encoding' => 'identity'}.merge(@options[:http_header])
|
247
293
|
|
@@ -250,7 +296,10 @@ class FastImage
|
|
250
296
|
if res.is_a?(Net::HTTPRedirection) && @redirect_count < 4
|
251
297
|
@redirect_count += 1
|
252
298
|
begin
|
253
|
-
|
299
|
+
location = res['Location']
|
300
|
+
raise ImageFetchFailure if location.nil? || location.empty?
|
301
|
+
|
302
|
+
@parsed_uri = URI.join(@parsed_uri, escaped_location(location))
|
254
303
|
rescue URI::InvalidURIError
|
255
304
|
else
|
256
305
|
fetch_using_http_from_parsed_uri
|
@@ -310,7 +359,7 @@ class FastImage
|
|
310
359
|
proxy = proxy_uri
|
311
360
|
|
312
361
|
if proxy
|
313
|
-
@http = Net::HTTP::Proxy(proxy.host, proxy.port).new(@parsed_uri.host, @parsed_uri.port)
|
362
|
+
@http = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(@parsed_uri.host, @parsed_uri.port)
|
314
363
|
else
|
315
364
|
@http = Net::HTTP.new(@parsed_uri.host, @parsed_uri.port)
|
316
365
|
end
|
@@ -321,6 +370,7 @@ class FastImage
|
|
321
370
|
end
|
322
371
|
|
323
372
|
def fetch_using_read(readable)
|
373
|
+
readable.rewind if readable.respond_to?(:rewind)
|
324
374
|
# Pathnames respond to read, but always return the first
|
325
375
|
# chunk of the file unlike an IO (even though the
|
326
376
|
# docuementation for it refers to IO). Need to supply
|
@@ -356,7 +406,7 @@ class FastImage
|
|
356
406
|
|
357
407
|
begin
|
358
408
|
result = send("parse_#{@property}")
|
359
|
-
if result
|
409
|
+
if result != nil
|
360
410
|
# extract exif orientation if it was found
|
361
411
|
if @property == :size && result.size == 3
|
362
412
|
@orientation = result.pop
|
@@ -378,6 +428,11 @@ class FastImage
|
|
378
428
|
send("parse_size_for_#{@type}")
|
379
429
|
end
|
380
430
|
|
431
|
+
def parse_animated
|
432
|
+
@type = parse_type unless @type
|
433
|
+
@type == :gif ? send("parse_animated_for_#{@type}") : nil
|
434
|
+
end
|
435
|
+
|
381
436
|
def fetch_using_base64(uri)
|
382
437
|
data = uri.split(',')[1]
|
383
438
|
decoded = Base64.decode64(data)
|
@@ -472,7 +527,12 @@ class FastImage
|
|
472
527
|
when 0x89.chr + "P"
|
473
528
|
:png
|
474
529
|
when "II", "MM"
|
475
|
-
|
530
|
+
case @stream.peek(11)[8..10]
|
531
|
+
when "APC", "CR\002"
|
532
|
+
nil # do not recognise CRW or CR2 as tiff
|
533
|
+
else
|
534
|
+
:tiff
|
535
|
+
end
|
476
536
|
when '8B'
|
477
537
|
:psd
|
478
538
|
when "\0\0"
|
@@ -484,7 +544,7 @@ class FastImage
|
|
484
544
|
when "RI"
|
485
545
|
:webp if @stream.peek(12)[8..11] == "WEBP"
|
486
546
|
when "<s"
|
487
|
-
:svg
|
547
|
+
:svg if @stream.peek(4) == "<svg"
|
488
548
|
when /<[?!]/
|
489
549
|
# Peek 10 more chars each time, and if end of file is reached just raise
|
490
550
|
# unknown. We assume the <svg tag cannot be within 10 chars of the end of
|
@@ -506,8 +566,69 @@ class FastImage
|
|
506
566
|
end
|
507
567
|
alias_method :parse_size_for_cur, :parse_size_for_ico
|
508
568
|
|
569
|
+
class Gif # :nodoc:
|
570
|
+
def initialize(stream)
|
571
|
+
@stream = stream
|
572
|
+
end
|
573
|
+
|
574
|
+
def width_and_height
|
575
|
+
@stream.read(11)[6..10].unpack('SS')
|
576
|
+
end
|
577
|
+
|
578
|
+
# Checks if a delay between frames exists and if it does, then the GIFs is
|
579
|
+
# animated
|
580
|
+
def animated?
|
581
|
+
delay = 0
|
582
|
+
|
583
|
+
@stream.read(10) # "GIF" + version (3) + width (2) + height (2)
|
584
|
+
|
585
|
+
fields = @stream.read(3).unpack("C")[0] # fields (1) + bg color (1) + pixel ratio (1)
|
586
|
+
|
587
|
+
# Skip Global Color Table if it exists
|
588
|
+
if fields & 0x80
|
589
|
+
# 2 * (depth + 1) colors, each occupying 3 bytes (RGB)
|
590
|
+
@stream.skip(3 * 2 ** ((fields & 0x7) + 1))
|
591
|
+
end
|
592
|
+
|
593
|
+
loop do
|
594
|
+
block_type = @stream.read(1).unpack("C")[0]
|
595
|
+
if block_type == 0x21
|
596
|
+
extension_type = @stream.read(1).unpack("C")[0]
|
597
|
+
size = @stream.read(1).unpack("C")[0]
|
598
|
+
if extension_type == 0xF9
|
599
|
+
delay = @stream.read(4).unpack("CSC")[1] # fields (1) + delay (2) + transparent index (1)
|
600
|
+
break
|
601
|
+
elsif extension_type == 0xFF
|
602
|
+
@stream.skip(size) # application ID (8) + version (3)
|
603
|
+
else
|
604
|
+
return # unrecognized extension
|
605
|
+
end
|
606
|
+
skip_sub_blocks
|
607
|
+
else
|
608
|
+
return # unrecognized block
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
delay > 0
|
613
|
+
end
|
614
|
+
|
615
|
+
private
|
616
|
+
|
617
|
+
def skip_sub_blocks
|
618
|
+
loop do
|
619
|
+
size = @stream.read(1).unpack("C")[0]
|
620
|
+
if size == 0
|
621
|
+
break
|
622
|
+
else
|
623
|
+
@stream.skip(size)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
509
629
|
def parse_size_for_gif
|
510
|
-
@stream
|
630
|
+
gif = Gif.new(@stream)
|
631
|
+
gif.width_and_height
|
511
632
|
end
|
512
633
|
|
513
634
|
def parse_size_for_png
|
@@ -562,10 +683,10 @@ class FastImage
|
|
562
683
|
d = @stream.read(32)[14..28]
|
563
684
|
header = d.unpack("C")[0]
|
564
685
|
|
565
|
-
result = if header ==
|
566
|
-
d[4..-1].unpack('l<l<')
|
567
|
-
else
|
686
|
+
result = if header == 12
|
568
687
|
d[4..8].unpack('SS')
|
688
|
+
else
|
689
|
+
d[4..-1].unpack('l<l<')
|
569
690
|
end
|
570
691
|
|
571
692
|
# ImageHeight is expressed in pixels. The absolute value is necessary because ImageHeight can be negative
|
@@ -767,4 +888,9 @@ class FastImage
|
|
767
888
|
svg = Svg.new(@stream)
|
768
889
|
svg.width_and_height
|
769
890
|
end
|
891
|
+
|
892
|
+
def parse_animated_for_gif
|
893
|
+
gif = Gif.new(@stream)
|
894
|
+
gif.animated?
|
895
|
+
end
|
770
896
|
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastimage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Sykes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: fakeweb
|
14
|
+
name: fakeweb-fi
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '10.5'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rdoc
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: test-unit
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: FastImage finds the size or type of an image given its uri by fetching
|
@@ -83,22 +83,21 @@ licenses:
|
|
83
83
|
metadata: {}
|
84
84
|
post_install_message:
|
85
85
|
rdoc_options:
|
86
|
-
- --charset=UTF-8
|
86
|
+
- "--charset=UTF-8"
|
87
87
|
require_paths:
|
88
88
|
- lib
|
89
89
|
required_ruby_version: !ruby/object:Gem::Requirement
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ">="
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: 1.9.2
|
94
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- -
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
requirements: []
|
100
|
-
|
101
|
-
rubygems_version: 2.0.14.1
|
100
|
+
rubygems_version: 3.0.6
|
102
101
|
signing_key:
|
103
102
|
specification_version: 4
|
104
103
|
summary: FastImage - Image info fast
|