fastimage 2.1.0 → 2.2.4

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 +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: []