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.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/README.textile +1 -1
  3. data/lib/fastimage.rb +138 -12
  4. metadata +15 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e956eb7172b275c005a02bed97037ab008774579
4
- data.tar.gz: cccc887b76405eed9d65e239cad4325eaf4dc437
2
+ SHA256:
3
+ metadata.gz: 53c0966ef5216c189e2950b1ad36c106777175f0d4dca9851abd34472fd9cb61
4
+ data.tar.gz: 29f5a6bc03623e184166e7f9c5e8c4379011b301904d4c690037d5b7b0ba0b33
5
5
  SHA512:
6
- metadata.gz: bf104614fb388b78077820a31b50c713a9a1c6579489758e7331f0247354e9347e27db7f672af36982d0faba4c4e4f44731583f4ae4d6d1eae8ec55657c6feaf
7
- data.tar.gz: 0d14f8f2ae9c597034ecc146f01884e33c3101ff2575679ac101bb3964c8af29f700dec14c4988955bde3600e6afb661a2ac8b62a633905cbb13175c19697035
6
+ metadata.gz: 28e8ec4debe6c2c477da19cafb1ad41e5de4d809136bd86621ae128372c32c1259abea833856f73dfcfe8ef3444bc5e1a148f91b52ee02d3292235c4d95fc542
7
+ data.tar.gz: ba8b4c6bb42146387b1a8af718fbbaf74a9a94532ee5adce082ff676095dad5d5e0eb613f1ec41d7729709847195fa81c4614f2d9d510d6c7def67f8970f168e
@@ -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.png?branch=master!: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
 
@@ -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[:type_only] ? :type : :size
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, OpenSSL::SSL::SSLError
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
- @parsed_uri = URI.join(@parsed_uri, URI.escape(res['Location']))
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
- :tiff
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.read(11)[6..10].unpack('SS')
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 == 40
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.1.3
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: 2018-05-01 00:00:00.000000000 Z
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
- rubyforge_project:
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