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.
- checksums.yaml +5 -5
- data/README.textile +8 -2
- data/lib/fastimage.rb +334 -37
- metadata +18 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 074e776db55bfd4766292bca262afc562d4d38d07d86f56f1299a666b4dc3e30
|
4
|
+
data.tar.gz: 7e079013cbd7802773119a5a6b7f25377e2878c1afbeb610c68d8a8a069ad0a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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[:
|
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
|
-
|
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
|
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
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
-
|
388
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
557
|
-
return [width, height,
|
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 ==
|
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
|
-
|
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.
|
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:
|
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
|
-
|
101
|
-
|
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: []
|