discourse_fastimage 2.0.3 → 2.1.0

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 +4 -4
  2. data/README.textile +20 -13
  3. data/lib/fastimage.rb +97 -62
  4. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37620cc6b20562783bb7d7ac06cb535065a3e935
4
- data.tar.gz: 07baa13fdec2f77276878545814d4ef32487bf41
3
+ metadata.gz: a75deaf2e304cde6b7109dc87c50d3fe2866633a
4
+ data.tar.gz: ad9f971c2769571d02c14e409482399e0ae7a714
5
5
  SHA512:
6
- metadata.gz: e4084b661aa9e3f56af4bac6bf48630b3eedcbdb42f7a28e0fb6f7b64aecd8e42036ec4aeef46c0482439a0ce8be41743977c1d16b782804f85c2327195b1522
7
- data.tar.gz: bde96e05a4e9826421cc6335e4d08a6dd838eae1c2a0e34c9f1040296e562f40b0ccef6e94b49a04a005ee434ccf0a2e8a00bb1efad567d5291125f63f010e1e
6
+ metadata.gz: 73efc6a4b04c496a7d834ef2a7d9071111ef801b94a03a92f40700d9ff790d551a9fa6d642e1537b3d730a532762604766b97c673db0de7b6c8b5db465bcd302
7
+ data.tar.gz: 808365bbfc92ce320ff314f814548cb71d44ece029035a4a2f8d7f4a3e382fdf9e47f452427c4db70e575c92405de7c80377a212508bd6e0ccd6069493d22ee0
@@ -19,29 +19,29 @@ You only need supply the uri, and FastImage will do the rest.
19
19
 
20
20
  h2. Features
21
21
 
22
- Fastimage can also read local (and other) files - anything that is not parseable as a URI will be
23
- interpreted as a filename, and FastImage will attempt to open it with File#open.
22
+ FastImage can also read local (and other) files - anything that is not parseable as a URI will be interpreted as a filename, and FastImage will attempt to open it with @File#open@.
24
23
 
25
- FastImage will also automatically read from any object that responds to :read - for
26
- instance an IO object if that is passed instead of a URI.
24
+ FastImage will also automatically read from any object that responds to @:read@ - for instance an IO object if that is passed instead of a URI.
27
25
 
28
26
  FastImage will follow up to 4 HTTP redirects to get the image.
29
27
 
30
- FastImage will obey the http_proxy setting in your environment to route requests via a proxy. You can also pass a :proxy argument if you want to specify the proxy address in the call.
28
+ FastImage will obey the @http_proxy@ setting in your environment to route requests via a proxy. You can also pass a @:proxy@ argument if you want to specify the proxy address in the call.
31
29
 
32
- You can add a timeout to the request which will limit the request time by passing :timeout => number_of_seconds.
30
+ You can add a timeout to the request which will limit the request time by passing @:timeout => number_of_seconds@.
33
31
 
34
- FastImage normally replies will nil if it encounters an error, but you can pass :raise_on_failure => true to get an exception.
32
+ FastImage normally replies with @nil@ if it encounters an error, but you can pass @:raise_on_failure => true@ to get an exception.
35
33
 
36
34
  FastImage also provides a reader for the content length header provided in HTTP. This may be useful to assess the file size of an image, but do not rely on it exclusively - it will not be present in chunked responses for instance.
37
35
 
38
- FastImage accepts additional HTTP headers. This can be used to set a user agent or referrer which some servers require. Pass an :http_header argument to specify headers, e.g., :http_header => {'User-Agent' => 'Fake Browser'}.
36
+ FastImage accepts additional HTTP headers. This can be used to set a user agent or referrer which some servers require. Pass an @:http_header@ argument to specify headers, e.g., @:http_header => {'User-Agent' => 'Fake Browser'}@.
39
37
 
40
38
  FastImage can give you information about the parsed display orientation of an image with Exif data (jpeg or tiff).
41
39
 
40
+ FastImage also handles "Data URIs":https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs correctly.
41
+
42
42
  h2. Security
43
43
 
44
- As of v1.6.7 FastImage no longer uses openuri to open files, but directly calls File.open. But take care to sanitise the strings passed to FastImage; it will try to read from whatever is passed.
44
+ As of v1.6.7 FastImage no longer uses @openuri@ to open files, but directly calls @File.open@. Take care to sanitise the strings passed to FastImage; it will try to read from whatever is passed.
45
45
 
46
46
  h2. Examples
47
47
 
@@ -64,6 +64,8 @@ FastImage.size("http://stephensykes.com/images/ss.com_x.gif", :http_header => {'
64
64
  => [266, 56]
65
65
  FastImage.new("http://stephensykes.com/images/ExifOrientation3.jpg").orientation
66
66
  => 3
67
+ FastImage.size("data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==")
68
+ => [1, 1]
67
69
  </code></pre>
68
70
 
69
71
  h2. Installation
@@ -90,6 +92,10 @@ h2. Documentation
90
92
 
91
93
  "http://sdsykes.github.io/fastimage/rdoc/FastImage.html":http://sdsykes.github.io/fastimage/rdoc/FastImage.html
92
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
+
93
99
  h2. Benchmark
94
100
 
95
101
  It's way faster than conventional methods (for example the image_size gem) for most types of file when fetching over the wire.
@@ -99,7 +105,7 @@ irb> uri = "http://upload.wikimedia.org/wikipedia/commons/b/b4/Mardin_1350660_13
99
105
  irb> puts Benchmark.measure {open(uri, 'rb') {|fh| p ImageSize.new(fh).size}}
100
106
  [9545, 6623]
101
107
  0.680000 0.250000 0.930000 ( 7.571887)
102
-
108
+
103
109
  irb> puts Benchmark.measure {p FastImage.size(uri)}
104
110
  [9545, 6623]
105
111
  0.010000 0.000000 0.010000 ( 0.090640)
@@ -116,7 +122,7 @@ irb> uri = "http://upload.wikimedia.org/wikipedia/commons/1/11/Shinbutsureijoush
116
122
  irb> puts Benchmark.measure {open(uri, 'rb') {|fh| p ImageSize.new(fh).size}}
117
123
  [1120, 1559]
118
124
  1.080000 0.370000 1.450000 ( 13.766962)
119
-
125
+
120
126
  irb> puts Benchmark.measure {p FastImage.size(uri)}
121
127
  [1120, 1559]
122
128
  3.490000 3.810000 7.300000 ( 11.754315)
@@ -126,8 +132,8 @@ h2. Tests
126
132
 
127
133
  You'll need to @gem install fakeweb@ and possibly also @gem install test-unit@ to be able to run the tests.
128
134
 
129
- bc.. $ ruby test.rb
130
- Run options:
135
+ bc.. $ ruby test.rb
136
+ Run options:
131
137
 
132
138
  # Running tests:
133
139
 
@@ -149,6 +155,7 @@ h2. FastImage in other languages
149
155
  * "PHP by tommoor":https://github.com/tommoor/fastimage
150
156
  * "Node.js by ShogunPanda":https://github.com/ShogunPanda/fastimage
151
157
  * "Objective C by kylehickinson":https://github.com/kylehickinson/FastImage
158
+ * "Android by qstumn":https://github.com/qstumn/FastImageSize
152
159
 
153
160
  h2. Licence
154
161
 
@@ -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.
@@ -55,10 +56,18 @@
55
56
  #
56
57
 
57
58
  require 'net/https'
58
- require 'open-uri'
59
59
  require 'delegate'
60
60
  require 'pathname'
61
61
  require 'zlib'
62
+ require 'base64'
63
+ require 'uri'
64
+
65
+ # see http://stackoverflow.com/questions/5208851/i/41048816#41048816
66
+ if RUBY_VERSION < "2.2"
67
+ module URI
68
+ DEFAULT_PARSER = Parser.new(:HOSTNAME => "(?:(?:[a-zA-Z\\d](?:[-\\_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.)*(?:[a-zA-Z](?:[-\\_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.?")
69
+ end
70
+ end
62
71
 
63
72
  class FastImage
64
73
  attr_reader :size, :type, :content_length, :orientation
@@ -80,28 +89,6 @@ class FastImage
80
89
 
81
90
  LocalFileChunkSize = 256 unless const_defined?(:LocalFileChunkSize)
82
91
 
83
- # Parser object should respond to #parse
84
- # and raise a URI::InvalidURIError if something goes wrong
85
- def self.uri_parser=(parser)
86
- @uri_parser = parser
87
- end
88
-
89
- # Helper that sets URI parsing to use the Addressable gem
90
- def self.use_addressable_uri_parser
91
- require 'addressable/uri'
92
- self.uri_parser = Class.new do
93
- def self.parse(location)
94
- Addressable::URI.parse(location)
95
- rescue Addressable::URI::InvalidURIError
96
- raise URI::InvalidURIError
97
- end
98
- end
99
- end
100
-
101
- def self.parse_uri(location)
102
- (@uri_parser || URI).parse(location)
103
- end
104
-
105
92
  # Returns an array containing the width and height of the image.
106
93
  # It will return nil if the image could not be fetched, or if the image type was not recognised.
107
94
  #
@@ -204,11 +191,15 @@ class FastImage
204
191
 
205
192
  @property = @options[:type_only] ? :type : :size
206
193
 
194
+ @type, @state = nil
195
+
207
196
  if uri.respond_to?(:read)
208
197
  fetch_using_read(uri)
198
+ elsif uri.start_with?('data:')
199
+ fetch_using_base64(uri)
209
200
  else
210
201
  begin
211
- @parsed_uri = self.class.parse_uri(uri)
202
+ @parsed_uri = URI.parse(uri)
212
203
  rescue URI::InvalidURIError
213
204
  fetch_using_file_open
214
205
  else
@@ -222,11 +213,10 @@ class FastImage
222
213
 
223
214
  raise SizeNotFound if @options[:raise_on_failure] && @property == :size && !@size
224
215
 
225
- rescue Timeout::Error,
226
- Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET, Errno::ENOENT,
227
- Net::HTTPBadResponse,
228
- SocketError, EOFError, IOError, NoMethodError,
229
- ImageFetchFailure
216
+ rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET,
217
+ ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT
218
+ raise ImageFetchFailure if @options[:raise_on_failure]
219
+ rescue NoMethodError # 1.8.7p248 can raise this due to a net/http bug
230
220
  raise ImageFetchFailure if @options[:raise_on_failure]
231
221
  rescue UnknownImageType
232
222
  raise UnknownImageType if @options[:raise_on_failure]
@@ -241,6 +231,7 @@ class FastImage
241
231
 
242
232
  ensure
243
233
  uri.rewind if uri.respond_to?(:rewind)
234
+
244
235
  end
245
236
 
246
237
  private
@@ -259,9 +250,11 @@ class FastImage
259
250
  if res.is_a?(Net::HTTPRedirection) && @redirect_count < 4
260
251
  @redirect_count += 1
261
252
  begin
262
- newly_parsed_uri = self.class.parse_uri(res['Location'])
253
+ newly_parsed_uri = URI.parse(res['Location'])
263
254
  # The new location may be relative - check for that
264
- if newly_parsed_uri.scheme != "http" && newly_parsed_uri.scheme != "https"
255
+ if protocol_relative_url?(res['Location'])
256
+ @parsed_uri = URI.parse("#{@parsed_uri.scheme}:#{res['Location']}")
257
+ elsif newly_parsed_uri.scheme != "http" && newly_parsed_uri.scheme != "https"
265
258
  @parsed_uri.path = res['Location']
266
259
  else
267
260
  @parsed_uri = newly_parsed_uri
@@ -304,12 +297,16 @@ class FastImage
304
297
  end
305
298
  end
306
299
 
300
+ def protocol_relative_url?(url)
301
+ url.start_with?("//")
302
+ end
303
+
307
304
  def proxy_uri
308
305
  begin
309
306
  if @options[:proxy]
310
- proxy = self.class.parse_uri(@options[:proxy])
307
+ proxy = URI.parse(@options[:proxy])
311
308
  else
312
- proxy = ENV['http_proxy'] && ENV['http_proxy'] != "" ? self.class.parse_uri(ENV['http_proxy']) : nil
309
+ proxy = ENV['http_proxy'] && ENV['http_proxy'] != "" ? URI.parse(ENV['http_proxy']) : nil
313
310
  end
314
311
  rescue URI::InvalidURIError
315
312
  proxy = nil
@@ -319,16 +316,13 @@ class FastImage
319
316
 
320
317
  def setup_http
321
318
  proxy = proxy_uri
322
- use_ssl = (@parsed_uri.scheme == "https")
323
- port = @parsed_uri.port || (use_ssl ? 443 : 80)
324
319
 
325
320
  if proxy
326
- @http = Net::HTTP::Proxy(proxy.host, proxy.port).new(@parsed_uri.host, port)
321
+ @http = Net::HTTP::Proxy(proxy.host, proxy.port).new(@parsed_uri.host, @parsed_uri.port)
327
322
  else
328
- @http = Net::HTTP.new(@parsed_uri.host, port)
323
+ @http = Net::HTTP.new(@parsed_uri.host, @parsed_uri.port)
329
324
  end
330
-
331
- @http.use_ssl = use_ssl
325
+ @http.use_ssl = (@parsed_uri.scheme == "https")
332
326
  @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
333
327
  @http.open_timeout = @options[:timeout]
334
328
  @http.read_timeout = @options[:timeout]
@@ -359,6 +353,7 @@ class FastImage
359
353
  end
360
354
 
361
355
  def fetch_using_file_open
356
+ @content_length = File.size?(@uri)
362
357
  File.open(@uri) do |s|
363
358
  fetch_using_read(s)
364
359
  end
@@ -391,6 +386,11 @@ class FastImage
391
386
  send("parse_size_for_#{@type}")
392
387
  end
393
388
 
389
+ def fetch_using_base64(uri)
390
+ data = uri.split(',')[1]
391
+ fetch_using_read StringIO.new(Base64.decode64(data))
392
+ end
393
+
394
394
  module StreamUtil # :nodoc:
395
395
  def read_byte
396
396
  read(1)[0].ord
@@ -420,6 +420,7 @@ class FastImage
420
420
  @str = ''
421
421
  end
422
422
 
423
+ # Peeking beyond the end of the input will raise
423
424
  def peek(n)
424
425
  while @strpos + n - 1 >= @str.size
425
426
  unused_str = @str[@strpos..-1]
@@ -442,6 +443,24 @@ class FastImage
442
443
  @pos += n
443
444
  result
444
445
  end
446
+
447
+ def skip(n)
448
+ discarded = 0
449
+ fetched = @str[@strpos..-1].size
450
+ while n > fetched
451
+ discarded += @str[@strpos..-1].size
452
+ new_string = @read_fiber.resume
453
+ raise CannotParseImage if !new_string
454
+
455
+ new_string.force_encoding("ASCII-8BIT") if String.method_defined? :force_encoding
456
+
457
+ fetched += new_string.size
458
+ @str = new_string
459
+ @strpos = 0
460
+ end
461
+ @strpos = @strpos + n - discarded
462
+ @pos += n
463
+ end
445
464
  end
446
465
 
447
466
  class IOStream < SimpleDelegator # :nodoc:
@@ -449,7 +468,7 @@ class FastImage
449
468
  end
450
469
 
451
470
  def parse_type
452
- case @stream.peek(2)
471
+ parsed_type = case @stream.peek(2)
453
472
  when "BM"
454
473
  :bmp
455
474
  when "GI"
@@ -469,22 +488,21 @@ class FastImage
469
488
  when 2 then :cur
470
489
  end
471
490
  when "RI"
472
- if @stream.peek(12)[8..11] == "WEBP"
473
- :webp
474
- else
475
- raise UnknownImageType
476
- end
491
+ :webp if @stream.peek(12)[8..11] == "WEBP"
477
492
  when "<s"
478
493
  :svg
479
- when "<?"
480
- (10..200).step(10).each do |length|
481
- characters = @stream.peek(length) rescue nil
482
- raise UnknownImageType if characters.nil?
483
- return :svg if characters.include?("<svg")
494
+ when /<[?!]/
495
+ # Peek 10 more chars each time, and if end of file is reached just raise
496
+ # unknown. We assume the <svg tag cannot be within 10 chars of the end of
497
+ # the file, and is within the first 250 chars.
498
+ begin
499
+ :svg if (1..25).detect {|n| @stream.peek(10 * n).include?("<svg")}
500
+ rescue FiberError
501
+ nil
484
502
  end
485
- else
486
- raise UnknownImageType
487
503
  end
504
+
505
+ parsed_type or raise UnknownImageType
488
506
  end
489
507
 
490
508
  def parse_size_for_ico
@@ -503,10 +521,11 @@ class FastImage
503
521
  end
504
522
 
505
523
  def parse_size_for_jpeg
524
+ exif = nil
506
525
  loop do
507
526
  @state = case @state
508
527
  when nil
509
- @stream.read(2)
528
+ @stream.skip(2)
510
529
  :started
511
530
  when :started
512
531
  @stream.read_byte == 0xFF ? :sof : :started
@@ -518,7 +537,7 @@ class FastImage
518
537
  io = StringIO.new(data)
519
538
  if io.read(4) == "Exif"
520
539
  io.read(2)
521
- @exif = Exif.new(IOStream.new(io)) rescue nil
540
+ exif = Exif.new(IOStream.new(io)) rescue nil
522
541
  end
523
542
  :started
524
543
  when 0xe0..0xef
@@ -532,14 +551,14 @@ class FastImage
532
551
  end
533
552
  when :skipframe
534
553
  skip_chars = @stream.read_int - 2
535
- @stream.read(skip_chars)
554
+ @stream.skip(skip_chars)
536
555
  :started
537
556
  when :readsize
538
- _s = @stream.read(3)
557
+ @stream.skip(3)
539
558
  height = @stream.read_int
540
559
  width = @stream.read_int
541
- width, height = height, width if @exif && @exif.rotated?
542
- return [width, height, @exif ? @exif.orientation : 1]
560
+ width, height = height, width if exif && exif.rotated?
561
+ return [width, height, exif ? exif.orientation : 1]
543
562
  end
544
563
  end
545
564
  end
@@ -579,7 +598,7 @@ class FastImage
579
598
  end
580
599
 
581
600
  def parse_size_vp8l
582
- @stream.read(1) # 0x2f
601
+ @stream.skip(1) # 0x2f
583
602
  b1, b2, b3, b4 = @stream.read(4).bytes.to_a
584
603
  [1 + (((b2 & 0x3f) << 8) | b1), 1 + (((b4 & 0xF) << 10) | (b3 << 2) | ((b2 & 0xC0) >> 6))]
585
604
  end
@@ -602,6 +621,7 @@ class FastImage
602
621
 
603
622
  def initialize(stream)
604
623
  @stream = stream
624
+ @width, @height, @orientation = nil
605
625
  parse_exif
606
626
  end
607
627
 
@@ -652,7 +672,11 @@ class FastImage
652
672
  @stream.read(2) # 42
653
673
 
654
674
  offset = @stream.read(4).unpack(@long)[0]
655
- @stream.read(offset - 8)
675
+ if @stream.respond_to?(:skip)
676
+ @stream.skip(offset - 8)
677
+ else
678
+ @stream.read(offset - 8)
679
+ end
656
680
 
657
681
  parse_exif_ifd
658
682
 
@@ -677,6 +701,7 @@ class FastImage
677
701
  class Svg # :nodoc:
678
702
  def initialize(stream)
679
703
  @stream = stream
704
+ @width, @height, @ratio, @viewbox_width, @viewbox_height = nil
680
705
  parse_svg
681
706
  end
682
707
 
@@ -687,6 +712,10 @@ class FastImage
687
712
  [@width, @width / @ratio]
688
713
  elsif @height && @ratio
689
714
  [@height * @ratio, @height]
715
+ elsif @viewbox_width && @viewbox_height
716
+ [@viewbox_width, @viewbox_height]
717
+ else
718
+ nil
690
719
  end
691
720
  end
692
721
 
@@ -709,10 +738,16 @@ class FastImage
709
738
  return if @width
710
739
  elsif attr_name.join =~ /viewbox/i
711
740
  values = attr_value.split(/\s/)
712
- @ratio = values[2].to_f / values[3].to_f
741
+ if values[2].to_f > 0 && values[3].to_f > 0
742
+ @ratio = values[2].to_f / values[3].to_f
743
+ @viewbox_width = values[2].to_i
744
+ @viewbox_height = values[3].to_i
745
+ end
713
746
  end
714
- when /[<\w]/
747
+ when /\w/
715
748
  attr_name << char
749
+ when "<"
750
+ attr_name = [char]
716
751
  when ">"
717
752
  state = :stop if state == :started
718
753
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discourse_fastimage
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.1.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: 2016-06-20 00:00:00.000000000 Z
11
+ date: 2017-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fakeweb
@@ -77,7 +77,7 @@ files:
77
77
  - MIT-LICENSE
78
78
  - README.textile
79
79
  - lib/fastimage.rb
80
- homepage: http://github.com/discourse/fastimage
80
+ homepage: http://github.com/sdsykes/fastimage
81
81
  licenses:
82
82
  - MIT
83
83
  metadata: {}
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  version: '0'
99
99
  requirements: []
100
100
  rubyforge_project:
101
- rubygems_version: 2.5.1
101
+ rubygems_version: 2.6.11
102
102
  signing_key:
103
103
  specification_version: 4
104
104
  summary: FastImage - Image info fast