discourse_fastimage 2.0.3 → 2.1.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 +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