fastimage 2.1.0 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/README.textile +8 -2
  3. data/lib/fastimage.rb +334 -37
  4. metadata +18 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8bdd1b1858b1101956c8d27e85509ad1c68a436b
4
- data.tar.gz: 54021e4718ed2bf2480dd7972b59a8b6b711061c
2
+ SHA256:
3
+ metadata.gz: 074e776db55bfd4766292bca262afc562d4d38d07d86f56f1299a666b4dc3e30
4
+ data.tar.gz: 7e079013cbd7802773119a5a6b7f25377e2878c1afbeb610c68d8a8a069ad0a1
5
5
  SHA512:
6
- metadata.gz: cf0bfcba525fbacbb09919754faf51e3702bdfba09430601db181485e5626949a0f6ed8797a34e37e1f13d19100fe4bf020e6f3ef7c77aad40efe544f7f3007d
7
- data.tar.gz: bc307c6eda1000ad5a60be2bcfd98b18fa7bb33faf4bbf4db20c5fa4db3cb3653b55f39dba1653e83d0a8a8d3ec33ffe3a20da434d539993ae3b3447e8f3dea4
6
+ metadata.gz: 4c82693c20723bc22b41e9839104097b0cb435c392e8ddd19edff93aeaa1802f3319c6ad49f1b82ba3926bd1bdcc5e04870d02401ff20e745e61d26a81473c6b
7
+ data.tar.gz: 230fbfd667aab5c59ce7bc53909454d2d9134fdb3c6705173d5df264a3c8a36df2d879c959df1ab4662d5e216a554ada7ba482ba3e0872b9c000d087c91e5b29
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.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
 
@@ -92,6 +92,10 @@ h2. Documentation
92
92
 
93
93
  "http://sdsykes.github.io/fastimage/rdoc/FastImage.html":http://sdsykes.github.io/fastimage/rdoc/FastImage.html
94
94
 
95
+ h2. Maintainer
96
+
97
+ FastImage is maintained by Stephen Sykes (@sdsykes). Support this project by using "LibPixel":https://libpixel.com cloud based image resizing and processing service.
98
+
95
99
  h2. Benchmark
96
100
 
97
101
  It's way faster than conventional methods (for example the image_size gem) for most types of file when fetching over the wire.
@@ -128,7 +132,7 @@ h2. Tests
128
132
 
129
133
  You'll need to @gem install fakeweb@ and possibly also @gem install test-unit@ to be able to run the tests.
130
134
 
131
- bc.. $ ruby test.rb
135
+ bc.. $ ruby test/test.rb
132
136
  Run options:
133
137
 
134
138
  # Running tests:
@@ -151,6 +155,8 @@ h2. FastImage in other languages
151
155
  * "PHP by tommoor":https://github.com/tommoor/fastimage
152
156
  * "Node.js by ShogunPanda":https://github.com/ShogunPanda/fastimage
153
157
  * "Objective C by kylehickinson":https://github.com/kylehickinson/FastImage
158
+ * "Android by qstumn":https://github.com/qstumn/FastImageSize
159
+ * "Flutter by ky1vstar":https://github.com/ky1vstar/fastimage.dart
154
160
 
155
161
  h2. Licence
156
162
 
data/lib/fastimage.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # coding: ASCII-8BIT
2
3
 
3
4
  # FastImage finds the size or type of an image given its uri.
@@ -69,7 +70,7 @@ if RUBY_VERSION < "2.2"
69
70
  end
70
71
 
71
72
  class FastImage
72
- attr_reader :size, :type, :content_length, :orientation
73
+ attr_reader :size, :type, :content_length, :orientation, :animated
73
74
 
74
75
  attr_reader :bytes_read
75
76
 
@@ -88,6 +89,8 @@ class FastImage
88
89
 
89
90
  LocalFileChunkSize = 256 unless const_defined?(:LocalFileChunkSize)
90
91
 
92
+ SUPPORTED_IMAGE_TYPES = [:bmp, :gif, :jpeg, :png, :tiff, :psd, :heic, :heif, :webp, :svg, :ico, :cur].freeze
93
+
91
94
  # Returns an array containing the width and height of the image.
92
95
  # It will return nil if the image could not be fetched, or if the image type was not recognised.
93
96
  #
@@ -178,6 +181,34 @@ class FastImage
178
181
  new(uri, options.merge(:type_only=>true)).type
179
182
  end
180
183
 
184
+ # Returns a boolean value indicating the image is animated.
185
+ # It will return nil if the image could not be fetched, or if the image type was not recognised.
186
+ #
187
+ # By default there is a timeout of 2 seconds for opening and reading from a remote server.
188
+ # This can be changed by passing a :timeout => number_of_seconds in the options.
189
+ #
190
+ # If you wish FastImage to raise if it cannot find the type of the image for any reason, then pass
191
+ # :raise_on_failure => true in the options.
192
+ #
193
+ # === Example
194
+ #
195
+ # require 'fastimage'
196
+ #
197
+ # FastImage.animated?("test/fixtures/test.gif")
198
+ # => false
199
+ # FastImage.animated?("test/fixtures/animated.gif")
200
+ # => true
201
+ #
202
+ # === Supported options
203
+ # [:timeout]
204
+ # Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
205
+ # [:raise_on_failure]
206
+ # If set to true causes an exception to be raised if the image type cannot be found for any reason.
207
+ #
208
+ def self.animated?(uri, options={})
209
+ new(uri, options.merge(:animated_only=>true)).animated
210
+ end
211
+
181
212
  def initialize(uri, options={})
182
213
  @uri = uri
183
214
  @options = {
@@ -188,7 +219,15 @@ class FastImage
188
219
  :http_header => {}
189
220
  }.merge(options)
190
221
 
191
- @property = @options[:type_only] ? :type : :size
222
+ @property = if @options[:animated_only]
223
+ :animated
224
+ elsif @options[:type_only]
225
+ :type
226
+ else
227
+ :size
228
+ end
229
+
230
+ @type, @state = nil
192
231
 
193
232
  if uri.respond_to?(:read)
194
233
  fetch_using_read(uri)
@@ -211,12 +250,11 @@ class FastImage
211
250
  raise SizeNotFound if @options[:raise_on_failure] && @property == :size && !@size
212
251
 
213
252
  rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET,
214
- ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT
215
- raise ImageFetchFailure if @options[:raise_on_failure]
216
- rescue NoMethodError # 1.8.7p248 can raise this due to a net/http bug
253
+ Errno::ENETUNREACH, ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT,
254
+ OpenSSL::SSL::SSLError
217
255
  raise ImageFetchFailure if @options[:raise_on_failure]
218
256
  rescue UnknownImageType
219
- raise UnknownImageType if @options[:raise_on_failure]
257
+ raise if @options[:raise_on_failure]
220
258
  rescue CannotParseImage
221
259
  if @options[:raise_on_failure]
222
260
  if @property == :size
@@ -239,6 +277,17 @@ class FastImage
239
277
  fetch_using_http_from_parsed_uri
240
278
  end
241
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
+
242
291
  def fetch_using_http_from_parsed_uri
243
292
  http_header = {'Accept-Encoding' => 'identity'}.merge(@options[:http_header])
244
293
 
@@ -247,15 +296,10 @@ class FastImage
247
296
  if res.is_a?(Net::HTTPRedirection) && @redirect_count < 4
248
297
  @redirect_count += 1
249
298
  begin
250
- newly_parsed_uri = URI.parse(res['Location'])
251
- # The new location may be relative - check for that
252
- if protocol_relative_url?(res['Location'])
253
- @parsed_uri = URI.parse("#{@parsed_uri.scheme}:#{res['Location']}")
254
- elsif newly_parsed_uri.scheme != "http" && newly_parsed_uri.scheme != "https"
255
- @parsed_uri.path = res['Location']
256
- else
257
- @parsed_uri = newly_parsed_uri
258
- end
299
+ location = res['Location']
300
+ raise ImageFetchFailure if location.nil? || location.empty?
301
+
302
+ @parsed_uri = URI.join(@parsed_uri, escaped_location(location))
259
303
  rescue URI::InvalidURIError
260
304
  else
261
305
  fetch_using_http_from_parsed_uri
@@ -315,7 +359,7 @@ class FastImage
315
359
  proxy = proxy_uri
316
360
 
317
361
  if proxy
318
- @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)
319
363
  else
320
364
  @http = Net::HTTP.new(@parsed_uri.host, @parsed_uri.port)
321
365
  end
@@ -326,6 +370,7 @@ class FastImage
326
370
  end
327
371
 
328
372
  def fetch_using_read(readable)
373
+ readable.rewind if readable.respond_to?(:rewind)
329
374
  # Pathnames respond to read, but always return the first
330
375
  # chunk of the file unlike an IO (even though the
331
376
  # docuementation for it refers to IO). Need to supply
@@ -361,7 +406,7 @@ class FastImage
361
406
 
362
407
  begin
363
408
  result = send("parse_#{@property}")
364
- if result
409
+ if result != nil
365
410
  # extract exif orientation if it was found
366
411
  if @property == :size && result.size == 3
367
412
  @orientation = result.pop
@@ -383,9 +428,19 @@ class FastImage
383
428
  send("parse_size_for_#{@type}")
384
429
  end
385
430
 
431
+ def parse_animated
432
+ @type = parse_type unless @type
433
+ @type == :gif ? send("parse_animated_for_#{@type}") : nil
434
+ end
435
+
386
436
  def fetch_using_base64(uri)
387
- data = uri.split(',')[1]
388
- fetch_using_read StringIO.new(Base64.decode64(data))
437
+ decoded = begin
438
+ Base64.decode64(uri.split(',')[1])
439
+ rescue
440
+ raise CannotParseImage
441
+ end
442
+ @content_length = decoded.size
443
+ fetch_using_read StringIO.new(decoded)
389
444
  end
390
445
 
391
446
  module StreamUtil # :nodoc:
@@ -419,19 +474,20 @@ class FastImage
419
474
 
420
475
  # Peeking beyond the end of the input will raise
421
476
  def peek(n)
422
- while @strpos + n - 1 >= @str.size
477
+ while @strpos + n > @str.size
423
478
  unused_str = @str[@strpos..-1]
479
+
424
480
  new_string = @read_fiber.resume
481
+ new_string = @read_fiber.resume if new_string.is_a? Net::ReadAdapter
425
482
  raise CannotParseImage if !new_string
426
-
427
483
  # we are dealing with bytes here, so force the encoding
428
- new_string.force_encoding("ASCII-8BIT") if String.method_defined? :force_encoding
484
+ new_string.force_encoding("ASCII-8BIT") if new_string.respond_to? :force_encoding
429
485
 
430
486
  @str = unused_str + new_string
431
487
  @strpos = 0
432
488
  end
433
489
 
434
- result = @str[@strpos..(@strpos + n - 1)]
490
+ @str[@strpos, n]
435
491
  end
436
492
 
437
493
  def read(n)
@@ -449,7 +505,7 @@ class FastImage
449
505
  new_string = @read_fiber.resume
450
506
  raise CannotParseImage if !new_string
451
507
 
452
- new_string.force_encoding("ASCII-8BIT") if String.method_defined? :force_encoding
508
+ new_string.force_encoding("ASCII-8BIT") if new_string.respond_to? :force_encoding
453
509
 
454
510
  fetched += new_string.size
455
511
  @str = new_string
@@ -475,20 +531,38 @@ class FastImage
475
531
  when 0x89.chr + "P"
476
532
  :png
477
533
  when "II", "MM"
478
- :tiff
534
+ case @stream.peek(11)[8..10]
535
+ when "APC", "CR\002"
536
+ nil # do not recognise CRW or CR2 as tiff
537
+ else
538
+ :tiff
539
+ end
479
540
  when '8B'
480
541
  :psd
481
542
  when "\0\0"
482
- # ico has either a 1 (for ico format) or 2 (for cursor) at offset 3
483
543
  case @stream.peek(3).bytes.to_a.last
544
+ when 0
545
+ # http://www.ftyps.com/what.html
546
+ # HEIC is composed of nested "boxes". Each box has a header composed of
547
+ # - Size (32 bit integer)
548
+ # - Box type (4 chars)
549
+ # - Extended size: only if size === 1, the type field is followed by 64 bit integer of extended size
550
+ # - Payload: Type-dependent
551
+ case @stream.peek(12)[4..-1]
552
+ when "ftypheic"
553
+ :heic
554
+ when "ftypmif1"
555
+ :heif
556
+ end
557
+ # ico has either a 1 (for ico format) or 2 (for cursor) at offset 3
484
558
  when 1 then :ico
485
559
  when 2 then :cur
486
560
  end
487
561
  when "RI"
488
562
  :webp if @stream.peek(12)[8..11] == "WEBP"
489
563
  when "<s"
490
- :svg
491
- when "<?"
564
+ :svg if @stream.peek(4) == "<svg"
565
+ when /\s\s|\s<|<[?!]/, 0xef.chr + 0xbb.chr
492
566
  # Peek 10 more chars each time, and if end of file is reached just raise
493
567
  # unknown. We assume the <svg tag cannot be within 10 chars of the end of
494
568
  # the file, and is within the first 250 chars.
@@ -509,8 +583,222 @@ class FastImage
509
583
  end
510
584
  alias_method :parse_size_for_cur, :parse_size_for_ico
511
585
 
586
+ class Heic # :nodoc:
587
+ def initialize(stream)
588
+ @stream = stream
589
+ end
590
+
591
+ def width_and_height
592
+ @max_size = nil
593
+ @primary_box = nil
594
+ @ipma_boxes = []
595
+ @ispe_boxes = []
596
+ @final_size = nil
597
+
598
+ catch :finish do
599
+ read_boxes!
600
+ end
601
+
602
+ @final_size
603
+ end
604
+
605
+ private
606
+
607
+ def read_boxes!(max_read_bytes = nil)
608
+ end_pos = max_read_bytes.nil? ? nil : @stream.pos + max_read_bytes
609
+ index = 0
610
+
611
+ loop do
612
+ return if end_pos && @stream.pos >= end_pos
613
+
614
+ box_type, box_size = read_box_header!
615
+
616
+ case box_type
617
+ when "meta"
618
+ handle_meta_box(box_size)
619
+ when "pitm"
620
+ handle_pitm_box(box_size)
621
+ when "ipma"
622
+ handle_ipma_box(box_size)
623
+ when "hdlr"
624
+ handle_hdlr_box(box_size)
625
+ when "iprp", "ipco"
626
+ read_boxes!(box_size)
627
+ when "ispe"
628
+ handle_ispe_box(box_size, index)
629
+ when "mdat"
630
+ throw :finish
631
+ else
632
+ @stream.read(box_size)
633
+ end
634
+
635
+ index += 1
636
+ end
637
+ end
638
+
639
+ def handle_ispe_box(box_size, index)
640
+ throw :finish if box_size < 12
641
+
642
+ data = @stream.read(box_size)
643
+ width, height = data[4...12].unpack("N2")
644
+ @ispe_boxes << { index: index, size: [width, height] }
645
+ end
646
+
647
+ def handle_hdlr_box(box_size)
648
+ throw :finish if box_size < 12
649
+
650
+ data = @stream.read(box_size)
651
+ throw :finish if data[8...12] != "pict"
652
+ end
653
+
654
+ def handle_ipma_box(box_size)
655
+ @stream.read(3)
656
+ flags3 = read_uint8!
657
+ entries_count = read_uint32!
658
+
659
+ entries_count.times do
660
+ id = read_uint16!
661
+ essen_count = read_uint8!
662
+
663
+ essen_count.times do
664
+ property_index = read_uint8! & 0x7F
665
+
666
+ if flags3 & 1 == 1
667
+ property_index = (property_index << 7) + read_uint8!
668
+ end
669
+
670
+ @ipma_boxes << { id: id, property_index: property_index - 1 }
671
+ end
672
+ end
673
+ end
674
+
675
+ def handle_pitm_box(box_size)
676
+ data = @stream.read(box_size)
677
+ @primary_box = data[4...6].unpack("S>")[0]
678
+ end
679
+
680
+ def handle_meta_box(box_size)
681
+ throw :finish if box_size < 4
682
+
683
+ @stream.read(4)
684
+ read_boxes!(box_size - 4)
685
+
686
+ throw :finish if !@primary_box
687
+
688
+ primary_indices = @ipma_boxes
689
+ .select { |box| box[:id] == @primary_box }
690
+ .map { |box| box[:property_index] }
691
+
692
+ ispe_box = @ispe_boxes.find do |box|
693
+ primary_indices.include?(box[:index])
694
+ end
695
+
696
+ if ispe_box
697
+ @final_size = ispe_box[:size]
698
+ end
699
+
700
+ throw :finish
701
+ end
702
+
703
+ def read_box_header!
704
+ size = read_uint32!
705
+ type = @stream.read(4)
706
+ [type, size - 8]
707
+ end
708
+
709
+ def read_uint8!
710
+ @stream.read(1).unpack("C")[0]
711
+ end
712
+
713
+ def read_uint16!
714
+ @stream.read(2).unpack("S>")[0]
715
+ end
716
+
717
+ def read_uint32!
718
+ @stream.read(4).unpack("N")[0]
719
+ end
720
+ end
721
+
722
+ def parse_size_for_heic
723
+ heic = Heic.new(@stream)
724
+ heic.width_and_height
725
+ end
726
+
727
+ def parse_size_for_heif
728
+ heic = Heic.new(@stream)
729
+ heic.width_and_height
730
+ end
731
+
732
+ class Gif # :nodoc:
733
+ def initialize(stream)
734
+ @stream = stream
735
+ end
736
+
737
+ def width_and_height
738
+ @stream.read(11)[6..10].unpack('SS')
739
+ end
740
+
741
+ # Checks if a delay between frames exists and if it does, then the GIFs is
742
+ # animated
743
+ def animated?
744
+ frames = 0
745
+
746
+ # "GIF" + version (3) + width (2) + height (2)
747
+ @stream.skip(10)
748
+
749
+ # fields (1) + bg color (1) + pixel ratio (1)
750
+ fields = @stream.read(3).unpack("CCC")[0]
751
+ if fields & 0x80 != 0 # Global Color Table
752
+ # 2 * (depth + 1) colors, each occupying 3 bytes (RGB)
753
+ @stream.skip(3 * 2 ** ((fields & 0x7) + 1))
754
+ end
755
+
756
+ loop do
757
+ block_type = @stream.read(1).unpack("C")[0]
758
+
759
+ if block_type == 0x21 # Graphic Control Extension
760
+ # extension type (1) + size (1)
761
+ size = @stream.read(2).unpack("CC")[1]
762
+ @stream.skip(size)
763
+ skip_sub_blocks
764
+ elsif block_type == 0x2C # Image Descriptor
765
+ frames += 1
766
+ return true if frames > 1
767
+
768
+ # left position (2) + top position (2) + width (2) + height (2) + fields (1)
769
+ fields = @stream.read(9).unpack("SSSSC")[4]
770
+ if fields & 0x80 != 0 # Local Color Table
771
+ # 2 * (depth + 1) colors, each occupying 3 bytes (RGB)
772
+ @stream.skip(3 * 2 ** ((fields & 0x7) + 1))
773
+ end
774
+
775
+ @stream.skip(1) # LZW min code size (1)
776
+ skip_sub_blocks
777
+ else
778
+ break # unrecognized block
779
+ end
780
+ end
781
+
782
+ false
783
+ end
784
+
785
+ private
786
+
787
+ def skip_sub_blocks
788
+ loop do
789
+ size = @stream.read(1).unpack("C")[0]
790
+ if size == 0
791
+ break
792
+ else
793
+ @stream.skip(size)
794
+ end
795
+ end
796
+ end
797
+ end
798
+
512
799
  def parse_size_for_gif
513
- @stream.read(11)[6..10].unpack('SS')
800
+ gif = Gif.new(@stream)
801
+ gif.width_and_height
514
802
  end
515
803
 
516
804
  def parse_size_for_png
@@ -518,6 +806,7 @@ class FastImage
518
806
  end
519
807
 
520
808
  def parse_size_for_jpeg
809
+ exif = nil
521
810
  loop do
522
811
  @state = case @state
523
812
  when nil
@@ -533,7 +822,8 @@ class FastImage
533
822
  io = StringIO.new(data)
534
823
  if io.read(4) == "Exif"
535
824
  io.read(2)
536
- @exif = Exif.new(IOStream.new(io)) rescue nil
825
+ new_exif = Exif.new(IOStream.new(io)) rescue nil
826
+ exif ||= new_exif # only use the first APP1 segment
537
827
  end
538
828
  :started
539
829
  when 0xe0..0xef
@@ -553,8 +843,8 @@ class FastImage
553
843
  @stream.skip(3)
554
844
  height = @stream.read_int
555
845
  width = @stream.read_int
556
- width, height = height, width if @exif && @exif.rotated?
557
- return [width, height, @exif ? @exif.orientation : 1]
846
+ width, height = height, width if exif && exif.rotated?
847
+ return [width, height, exif ? exif.orientation : 1]
558
848
  end
559
849
  end
560
850
  end
@@ -563,10 +853,10 @@ class FastImage
563
853
  d = @stream.read(32)[14..28]
564
854
  header = d.unpack("C")[0]
565
855
 
566
- result = if header == 40
567
- d[4..-1].unpack('l<l<')
568
- else
856
+ result = if header == 12
569
857
  d[4..8].unpack('SS')
858
+ else
859
+ d[4..-1].unpack('l<l<')
570
860
  end
571
861
 
572
862
  # ImageHeight is expressed in pixels. The absolute value is necessary because ImageHeight can be negative
@@ -575,7 +865,7 @@ class FastImage
575
865
 
576
866
  def parse_size_for_webp
577
867
  vp8 = @stream.read(16)[12..15]
578
- len = @stream.read(4).unpack("V")
868
+ _len = @stream.read(4).unpack("V")
579
869
  case vp8
580
870
  when "VP8 "
581
871
  parse_size_vp8
@@ -617,6 +907,7 @@ class FastImage
617
907
 
618
908
  def initialize(stream)
619
909
  @stream = stream
910
+ @width, @height, @orientation = nil
620
911
  parse_exif
621
912
  end
622
913
 
@@ -696,6 +987,7 @@ class FastImage
696
987
  class Svg # :nodoc:
697
988
  def initialize(stream)
698
989
  @stream = stream
990
+ @width, @height, @ratio, @viewbox_width, @viewbox_height = nil
699
991
  parse_svg
700
992
  end
701
993
 
@@ -732,7 +1024,7 @@ class FastImage
732
1024
  return if @width
733
1025
  elsif attr_name.join =~ /viewbox/i
734
1026
  values = attr_value.split(/\s/)
735
- if values[2].to_f > 0 && values[3].to_f > 0
1027
+ if values[2].to_f > 0 && values[3].to_f > 0
736
1028
  @ratio = values[2].to_f / values[3].to_f
737
1029
  @viewbox_width = values[2].to_i
738
1030
  @viewbox_height = values[3].to_i
@@ -766,4 +1058,9 @@ class FastImage
766
1058
  svg = Svg.new(@stream)
767
1059
  svg.width_and_height
768
1060
  end
1061
+
1062
+ def parse_animated_for_gif
1063
+ gif = Gif.new(@stream)
1064
+ gif.animated?
1065
+ end
769
1066
  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.0
4
+ version: 2.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Sykes
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2021-02-28 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
@@ -81,25 +81,24 @@ homepage: http://github.com/sdsykes/fastimage
81
81
  licenses:
82
82
  - MIT
83
83
  metadata: {}
84
- post_install_message:
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
102
- signing_key:
100
+ rubygems_version: 3.1.6
101
+ signing_key:
103
102
  specification_version: 4
104
103
  summary: FastImage - Image info fast
105
104
  test_files: []