image_size 2.1.2 → 3.0.2
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 +4 -4
- data/.github/workflows/check.yml +27 -4
- data/.github/workflows/rubocop.yml +16 -0
- data/.rubocop.yml +12 -0
- data/.rubocop_todo.yml +3 -3
- data/CHANGELOG.markdown +14 -0
- data/Gemfile +2 -0
- data/README.markdown +19 -13
- data/image_size.gemspec +4 -3
- data/lib/image_size/chunky_reader.rb +44 -0
- data/lib/image_size/reader.rb +64 -0
- data/lib/image_size/seekable_io_reader.rb +29 -0
- data/lib/image_size/stream_io_reader.rb +22 -0
- data/lib/image_size/string_reader.rb +21 -0
- data/lib/image_size/uri_reader.rb +90 -0
- data/lib/image_size.rb +53 -65
- data/spec/image_size/chunky_reader_spec.rb +69 -0
- data/spec/image_size/seekable_io_reader_spec.rb +52 -0
- data/spec/image_size_spec.rb +59 -7
- data/spec/images/.gitattributes +1 -0
- data/spec/images/empty +0 -0
- data/spec/images/svg/crlf.72x100.svg +3 -0
- data/spec/images/svg/long.72x100.svg +20 -0
- data/spec/images/svg/long.crlf.72x100.svg +20 -0
- data/spec/images/xbm/crlf.16x32.xbm +11 -0
- data/spec/images/xpm/crlf.24x32.xpm +40 -0
- metadata +45 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e594fe6ec7b9018dd68867a5feaf557c6c22e7ed667876089fcbe35d6f3104c
|
4
|
+
data.tar.gz: a221e99d54a933452712df549e2b8b05ceb85c407b27f482042d35c97dbb95c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbc4b3ec562040c3316edfc7412110eec1eef87028100f156e155a65bb38bccba7199b1094daa5ddc62317229a0fbe7241af0ba2089adf6ee03ab7eb4d85c634
|
7
|
+
data.tar.gz: db58b0bee4cf294cf44ac5b4afe01c8cd0420b0155e220e378755081ca1a655adb1ac30695966f150b094c648befbd8fb96f31d9a835afbefe1bffbc7ce717b4
|
data/.github/workflows/check.yml
CHANGED
@@ -19,8 +19,10 @@ jobs:
|
|
19
19
|
- '2.6'
|
20
20
|
- '2.7'
|
21
21
|
- '3.0'
|
22
|
+
- '3.1'
|
22
23
|
- jruby-9.1
|
23
24
|
- jruby-9.2
|
25
|
+
- jruby-9.3
|
24
26
|
fail-fast: false
|
25
27
|
steps:
|
26
28
|
- uses: actions/checkout@v2
|
@@ -28,13 +30,34 @@ jobs:
|
|
28
30
|
with:
|
29
31
|
ruby-version: "${{ matrix.ruby }}"
|
30
32
|
bundler-cache: true
|
31
|
-
- run: bundle exec rspec
|
32
|
-
|
33
|
+
- run: bundle exec rspec --format documentation
|
34
|
+
legacy:
|
33
35
|
runs-on: ubuntu-latest
|
36
|
+
container: ${{ matrix.container }}
|
37
|
+
strategy:
|
38
|
+
matrix:
|
39
|
+
container:
|
40
|
+
- rspec/ci:1.8.7
|
41
|
+
- rspec/ci:1.9.3
|
42
|
+
fail-fast: false
|
43
|
+
steps:
|
44
|
+
- uses: actions/checkout@v2
|
45
|
+
- run: bundle install
|
46
|
+
- run: bundle exec rspec --format documentation
|
47
|
+
windows:
|
48
|
+
runs-on: windows-latest
|
49
|
+
strategy:
|
50
|
+
matrix:
|
51
|
+
ruby:
|
52
|
+
- '2.6'
|
53
|
+
- '2.7'
|
54
|
+
- '3.0'
|
55
|
+
- '3.1'
|
56
|
+
fail-fast: false
|
34
57
|
steps:
|
35
58
|
- uses: actions/checkout@v2
|
36
59
|
- uses: ruby/setup-ruby@v1
|
37
60
|
with:
|
38
|
-
ruby-version:
|
61
|
+
ruby-version: "${{ matrix.ruby }}"
|
39
62
|
bundler-cache: true
|
40
|
-
- run: bundle exec
|
63
|
+
- run: bundle exec rspec --format documentation
|
@@ -0,0 +1,16 @@
|
|
1
|
+
name: rubocop
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
pull_request:
|
5
|
+
schedule:
|
6
|
+
- cron: 45 4 * * 3
|
7
|
+
jobs:
|
8
|
+
rubocop:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v2
|
12
|
+
- uses: ruby/setup-ruby@v1
|
13
|
+
with:
|
14
|
+
ruby-version: '3.1'
|
15
|
+
bundler-cache: true
|
16
|
+
- run: bundle exec rubocop
|
data/.rubocop.yml
CHANGED
@@ -15,6 +15,9 @@ Layout/CaseIndentation:
|
|
15
15
|
Layout/EndAlignment:
|
16
16
|
EnforcedStyleAlignWith: variable
|
17
17
|
|
18
|
+
Layout/FirstHashElementIndentation:
|
19
|
+
EnforcedStyle: consistent
|
20
|
+
|
18
21
|
Layout/LineLength:
|
19
22
|
Max: 120
|
20
23
|
|
@@ -50,6 +53,9 @@ Style/EmptyCaseCondition:
|
|
50
53
|
Style/Encoding:
|
51
54
|
Enabled: false
|
52
55
|
|
56
|
+
Style/FileRead:
|
57
|
+
Enabled: false
|
58
|
+
|
53
59
|
Style/HashEachMethods:
|
54
60
|
Enabled: true
|
55
61
|
|
@@ -65,12 +71,18 @@ Style/HashTransformValues:
|
|
65
71
|
Style/IfUnlessModifier:
|
66
72
|
Enabled: false
|
67
73
|
|
74
|
+
Style/NumericPredicate:
|
75
|
+
Enabled: false
|
76
|
+
|
68
77
|
Style/ParallelAssignment:
|
69
78
|
Enabled: false
|
70
79
|
|
71
80
|
Style/SafeNavigation:
|
72
81
|
Enabled: false
|
73
82
|
|
83
|
+
Style/SlicingWithRange:
|
84
|
+
Enabled: false
|
85
|
+
|
74
86
|
Style/TrailingCommaInArrayLiteral:
|
75
87
|
EnforcedStyleForMultiline: comma
|
76
88
|
|
data/.rubocop_todo.yml
CHANGED
@@ -9,17 +9,17 @@
|
|
9
9
|
# Offense count: 8
|
10
10
|
# Configuration parameters: IgnoredMethods.
|
11
11
|
Metrics/AbcSize:
|
12
|
-
|
12
|
+
Enabled: false
|
13
13
|
|
14
14
|
# Offense count: 5
|
15
15
|
# Configuration parameters: IgnoredMethods.
|
16
16
|
Metrics/CyclomaticComplexity:
|
17
|
-
|
17
|
+
Enabled: false
|
18
18
|
|
19
19
|
# Offense count: 2
|
20
20
|
# Configuration parameters: IgnoredMethods.
|
21
21
|
Metrics/PerceivedComplexity:
|
22
|
-
|
22
|
+
Enabled: false
|
23
23
|
|
24
24
|
# Offense count: 8
|
25
25
|
# Cop supports --auto-correct.
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## v3.0.2 (2022-05-19)
|
6
|
+
|
7
|
+
* Fix handling empty files [#20](https://github.com/toy/image_size/issues/20) [@toy](https://github.com/toy)
|
8
|
+
|
9
|
+
## v3.0.1 (2021-10-21)
|
10
|
+
|
11
|
+
* Fix reading file chunks starting after EOF and reading chunks non-consecutively [toy/image_optim_rails#12](https://github.com/toy/image_optim_rails/issues/12) [@toy](https://github.com/toy)
|
12
|
+
|
13
|
+
## v3.0.0 (2021-10-17)
|
14
|
+
|
15
|
+
* Read only required chunks of data for files and seekable IOs [@toy](https://github.com/toy)
|
16
|
+
* Raise `FormatError` whenever reading data returns less data than expected [#12](https://github.com/toy/image_size/issues/12) [@toy](https://github.com/toy)
|
17
|
+
* Add `w`/`width` and `h`/`height` accessors to `Size` [@toy](https://github.com/toy)
|
18
|
+
|
5
19
|
## v2.1.2 (2021-08-21)
|
6
20
|
|
7
21
|
* Fix for pcx on big endian systems by forcing reading dimensions in little endian byte order [#15](https://github.com/toy/image_size/issues/15) [#16](https://github.com/toy/image_size/pull/16) [@mtasaka](https://github.com/mtasaka)
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
|
4
4
|
# image_size
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
Measure image size using pure Ruby.
|
7
|
+
Formats: `apng`, `bmp`, `cur`, `gif`, `ico`, `j2c`, `jp2`, `jpeg`, `jpx`, `mng`, `pam`, `pbm`, `pcx`, `pgm`, `png`, `ppm`, `psd`, `svg`, `swf`, `tiff`, `webp`, `xbm`, `xpm`.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -17,26 +17,32 @@ gem install image_size
|
|
17
17
|
Add to your `Gemfile`:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
gem 'image_size', '~>
|
20
|
+
gem 'image_size', '~> 3.0'
|
21
21
|
```
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
image_size = ImageSize.path('spec/
|
26
|
+
image_size = ImageSize.path('spec/images/jpeg/436x429.jpeg')
|
27
27
|
|
28
28
|
image_size.format #=> :jpec
|
29
|
-
image_size.width #=>
|
30
|
-
image_size.height #=>
|
31
|
-
image_size.w #=>
|
32
|
-
image_size.h #=>
|
33
|
-
image_size.size #=> [
|
29
|
+
image_size.width #=> 436
|
30
|
+
image_size.height #=> 429
|
31
|
+
image_size.w #=> 436
|
32
|
+
image_size.h #=> 429
|
33
|
+
image_size.size #=> [436, 429]
|
34
|
+
image_size.size.to_s #=> "436x429"
|
35
|
+
"#{image_size.size}" #=> "436x429"
|
36
|
+
image_size.size.width #=> 436
|
37
|
+
image_size.size.height #=> 429
|
38
|
+
image_size.size.w #=> 436
|
39
|
+
image_size.size.h #=> 429
|
34
40
|
```
|
35
41
|
|
36
42
|
Or using `IO` object:
|
37
43
|
|
38
44
|
```ruby
|
39
|
-
image_size = File.open('spec/
|
45
|
+
image_size = File.open('spec/images/jpeg/436x429.jpeg', 'rb'){ |fh| ImageSize.new(fh) }
|
40
46
|
```
|
41
47
|
|
42
48
|
Any object responding to `read` and `eof?`:
|
@@ -68,14 +74,14 @@ So rewind if needed before passing to `ImageSize` and/or rewind after passing to
|
|
68
74
|
```ruby
|
69
75
|
require 'image_size'
|
70
76
|
|
71
|
-
File.open('spec/
|
77
|
+
File.open('spec/images/jpeg/436x429.jpeg', 'rb') do |fh|
|
72
78
|
image_size = ImageSize.new(fh)
|
73
79
|
|
74
80
|
fh.rewind
|
75
81
|
data = fh.read
|
76
82
|
end
|
77
83
|
|
78
|
-
File.open('spec/
|
84
|
+
File.open('spec/images/jpeg/436x429.jpeg', 'rb') do |fh|
|
79
85
|
data = fh.read
|
80
86
|
fh.rewind
|
81
87
|
|
@@ -88,4 +94,4 @@ end
|
|
88
94
|
This code is free to use under the terms of the [Ruby's licence](LICENSE.txt).
|
89
95
|
|
90
96
|
Original author: Keisuke Minami <keisuke@rccn.com>.\
|
91
|
-
Further development 2010-
|
97
|
+
Further development 2010-2022 Ivan Kuchin https://github.com/toy/image_size
|
data/image_size.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_size'
|
5
|
-
s.version = '
|
5
|
+
s.version = '3.0.2'
|
6
6
|
s.summary = %q{Measure image size using pure Ruby}
|
7
7
|
s.description = %q{Measure following file dimensions: apng, bmp, cur, gif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm, xpm}
|
8
8
|
s.homepage = "https://github.com/toy/#{s.name}"
|
@@ -22,7 +22,8 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.require_paths = %w[lib]
|
23
23
|
|
24
24
|
s.add_development_dependency 'rspec', '~> 3.0'
|
25
|
-
if RUBY_VERSION >= '2.
|
26
|
-
s.add_development_dependency 'rubocop', '~> 1.
|
25
|
+
if RUBY_VERSION >= '2.5'
|
26
|
+
s.add_development_dependency 'rubocop', '~> 1.22'
|
27
|
+
s.add_development_dependency 'rubocop-rspec', '~> 2.0'
|
27
28
|
end
|
28
29
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_size/reader'
|
4
|
+
|
5
|
+
class ImageSize
|
6
|
+
module ChunkyReader # :nodoc:
|
7
|
+
include Reader
|
8
|
+
|
9
|
+
# Size of a chunk in which to read
|
10
|
+
def chunk_size
|
11
|
+
4096
|
12
|
+
end
|
13
|
+
|
14
|
+
# Including class should define method chunk that accepts the chunk number
|
15
|
+
# and returns a string of chunk_size length or shorter for last chunk, or
|
16
|
+
# nil for further chunks.
|
17
|
+
# Determines required chunks, takes parts of them to construct desired
|
18
|
+
# substring, behaves same as str[start, length] except start can't be
|
19
|
+
# negative.
|
20
|
+
def [](offset, length)
|
21
|
+
raise ArgumentError, "expected offset not to be negative, got #{offset}" if offset < 0
|
22
|
+
return if length < 0
|
23
|
+
|
24
|
+
first = offset / chunk_size
|
25
|
+
return unless (first_chunk = chunk(first))
|
26
|
+
|
27
|
+
last = (offset + length - 1) / chunk_size
|
28
|
+
|
29
|
+
if first >= last
|
30
|
+
first_chunk[offset - (first * chunk_size), length]
|
31
|
+
else
|
32
|
+
return unless (first_piece = first_chunk[offset - (first * chunk_size), chunk_size])
|
33
|
+
|
34
|
+
chunks = (first.succ...last).map{ |i| chunk(i) }.unshift(first_piece)
|
35
|
+
|
36
|
+
if (last_chunk = chunk(last))
|
37
|
+
chunks.push(last_chunk[0, offset + length - (last * chunk_size)])
|
38
|
+
end
|
39
|
+
|
40
|
+
chunks.join
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
class ImageSize
|
7
|
+
module Reader # :nodoc:
|
8
|
+
class << self
|
9
|
+
def open(input)
|
10
|
+
case
|
11
|
+
when input.is_a?(String)
|
12
|
+
yield StringReader.new(input)
|
13
|
+
when input.is_a?(StringIO)
|
14
|
+
yield StringReader.new(input.string)
|
15
|
+
when input.respond_to?(:read) && input.respond_to?(:eof?)
|
16
|
+
yield for_io(input)
|
17
|
+
when input.is_a?(Pathname)
|
18
|
+
input.open('rb'){ |f| yield for_io(f) }
|
19
|
+
else
|
20
|
+
raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{input.class}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def for_io(io)
|
27
|
+
if io.respond_to?(:stat) && !io.stat.file?
|
28
|
+
StreamIOReader.new(io)
|
29
|
+
else
|
30
|
+
begin
|
31
|
+
io.seek(0, IO::SEEK_CUR)
|
32
|
+
SeekableIOReader.new(io)
|
33
|
+
rescue Errno::ESPIPE, Errno::EINVAL
|
34
|
+
StreamIOReader.new(io)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch(offset, length)
|
41
|
+
chunk = self[offset, length]
|
42
|
+
|
43
|
+
unless chunk && chunk.length == length
|
44
|
+
raise FormatError, "Expected #{length} bytes at offset #{offset}, got #{chunk.inspect}"
|
45
|
+
end
|
46
|
+
|
47
|
+
chunk
|
48
|
+
end
|
49
|
+
|
50
|
+
def unpack(offset, length, format)
|
51
|
+
fetch(offset, length).unpack(format)
|
52
|
+
end
|
53
|
+
|
54
|
+
if ''.respond_to?(:unpack1)
|
55
|
+
def unpack1(offset, length, format)
|
56
|
+
fetch(offset, length).unpack1(format)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
def unpack1(offset, length, format)
|
60
|
+
fetch(offset, length).unpack(format)[0]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_size/chunky_reader'
|
4
|
+
|
5
|
+
class ImageSize
|
6
|
+
class SeekableIOReader # :nodoc:
|
7
|
+
include ChunkyReader
|
8
|
+
|
9
|
+
def initialize(io)
|
10
|
+
@io = io
|
11
|
+
@pos = 0
|
12
|
+
@chunks = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def chunk(i)
|
18
|
+
unless @chunks.key?(i)
|
19
|
+
@io.seek((chunk_size * i) - @pos, IO::SEEK_CUR)
|
20
|
+
data = @io.read(chunk_size)
|
21
|
+
@pos = chunk_size * i
|
22
|
+
@pos += data.length if data
|
23
|
+
@chunks[i] = data
|
24
|
+
end
|
25
|
+
|
26
|
+
@chunks[i]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_size/chunky_reader'
|
4
|
+
|
5
|
+
class ImageSize
|
6
|
+
class StreamIOReader # :nodoc:
|
7
|
+
include ChunkyReader
|
8
|
+
|
9
|
+
def initialize(io)
|
10
|
+
@io = io
|
11
|
+
@chunks = []
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def chunk(i)
|
17
|
+
@chunks << @io.read(chunk_size) while i >= @chunks.length && !@io.eof?
|
18
|
+
|
19
|
+
@chunks[i]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_size/reader'
|
4
|
+
|
5
|
+
class ImageSize
|
6
|
+
class StringReader # :nodoc:
|
7
|
+
include Reader
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
@string = if string.respond_to?(:encoding) && string.encoding.name != 'ASCII-8BIT'
|
11
|
+
string.dup.force_encoding('ASCII-8BIT')
|
12
|
+
else
|
13
|
+
string
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](offset, length)
|
18
|
+
@string[offset, length]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_size/reader'
|
4
|
+
require 'image_size/chunky_reader'
|
5
|
+
|
6
|
+
require 'net/https'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
# This is a hacky experiment and not part of public API
|
10
|
+
#
|
11
|
+
# It adds ability to fetch size of image from http server while downloading only
|
12
|
+
# needed chunks if the server recognises Range header
|
13
|
+
class ImageSize
|
14
|
+
class URIReader # :nodoc:
|
15
|
+
include ChunkyReader
|
16
|
+
|
17
|
+
def initialize(uri, redirects = 5)
|
18
|
+
if !@http || @http.address != uri.host || @http.port != uri.port
|
19
|
+
@http.finish if @http
|
20
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
21
|
+
@http.use_ssl = true if uri.scheme == 'https'
|
22
|
+
@http.start
|
23
|
+
end
|
24
|
+
|
25
|
+
@request_uri = uri.request_uri
|
26
|
+
response = request_chunk(0)
|
27
|
+
|
28
|
+
case response
|
29
|
+
when Net::HTTPRedirection
|
30
|
+
raise "Too many redirects: #{response['location']}" unless redirects > 0
|
31
|
+
|
32
|
+
initialize(uri + response['location'], redirects - 1)
|
33
|
+
when Net::HTTPOK
|
34
|
+
@body = response.body
|
35
|
+
when Net::HTTPPartialContent
|
36
|
+
@chunks = { 0 => response.body }
|
37
|
+
when Net::HTTPRequestedRangeNotSatisfiable
|
38
|
+
@body = ''
|
39
|
+
else
|
40
|
+
raise "Unexpected response: #{response}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](offset, length)
|
45
|
+
if @body
|
46
|
+
@body[offset, length]
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def chunk(i)
|
53
|
+
unless @chunks.key?(i)
|
54
|
+
response = request_chunk(i)
|
55
|
+
case response
|
56
|
+
when Net::HTTPPartialContent
|
57
|
+
@chunks[i] = response.body
|
58
|
+
else
|
59
|
+
raise "Unexpected response: #{response}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@chunks[i]
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def request_chunk(i)
|
69
|
+
@http.get(@request_uri, 'Range' => "bytes=#{chunk_size * i}-#{(chunk_size * (i + 1)) - 1}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module Reader # :nodoc:
|
74
|
+
class << self
|
75
|
+
def open_with_uri(input, &block)
|
76
|
+
if input.is_a?(URI)
|
77
|
+
yield URIReader.new(input)
|
78
|
+
else
|
79
|
+
open_without_uri(input, &block)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
alias_method :open_without_uri, :open
|
83
|
+
alias_method :open, :open_with_uri
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.url(url)
|
88
|
+
new(url.is_a?(URI) ? url : URI(url))
|
89
|
+
end
|
90
|
+
end
|
data/lib/image_size.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# encoding: BINARY
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'image_size/reader'
|
5
|
+
require 'image_size/seekable_io_reader'
|
6
|
+
require 'image_size/stream_io_reader'
|
7
|
+
require 'image_size/string_reader'
|
5
8
|
|
6
9
|
# Determine image format and size
|
7
10
|
class ImageSize
|
@@ -13,38 +16,23 @@ class ImageSize
|
|
13
16
|
def to_s
|
14
17
|
join('x')
|
15
18
|
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class ImageReader # :nodoc:
|
19
|
-
attr_reader :data
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
elsif data_or_io.respond_to?(:read) && data_or_io.respond_to?(:eof?)
|
25
|
-
data_or_io
|
26
|
-
else
|
27
|
-
raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{data_or_io.class}"
|
28
|
-
end
|
29
|
-
@data = String.new # not frozen
|
20
|
+
# get first element
|
21
|
+
def width
|
22
|
+
self[0]
|
30
23
|
end
|
24
|
+
alias_method :w, :width
|
31
25
|
|
32
|
-
|
33
|
-
def
|
34
|
-
|
35
|
-
data = @io.read(CHUNK)
|
36
|
-
break unless data
|
37
|
-
|
38
|
-
data.force_encoding(@data.encoding) if data.respond_to?(:encoding)
|
39
|
-
@data << data
|
40
|
-
end
|
41
|
-
@data[offset, length]
|
26
|
+
# get second element
|
27
|
+
def height
|
28
|
+
self[1]
|
42
29
|
end
|
30
|
+
alias_method :h, :height
|
43
31
|
end
|
44
32
|
|
45
33
|
# Given path to image finds its format, width and height
|
46
34
|
def self.path(path)
|
47
|
-
|
35
|
+
new(Pathname.new(path))
|
48
36
|
end
|
49
37
|
|
50
38
|
# Used for svg
|
@@ -59,11 +47,10 @@ class ImageSize
|
|
59
47
|
|
60
48
|
# Given image as any class responding to read and eof? or data as String, finds its format and dimensions
|
61
49
|
def initialize(data)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@width, @height = send("size_of_#{@format}", ir)
|
50
|
+
Reader.open(data) do |ir|
|
51
|
+
@format = detect_format(ir)
|
52
|
+
@width, @height = send("size_of_#{@format}", ir) if @format
|
53
|
+
end
|
67
54
|
end
|
68
55
|
|
69
56
|
# Image format
|
@@ -89,6 +76,7 @@ private
|
|
89
76
|
def detect_format(ir)
|
90
77
|
head = ir[0, 1024]
|
91
78
|
case
|
79
|
+
when head.nil? || head.empty? then nil
|
92
80
|
when head[0, 6] =~ /GIF8[79]a/ then :gif
|
93
81
|
when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
|
94
82
|
when head[0, 8] == "\212MNG\r\n\032\n" then :mng
|
@@ -100,7 +88,7 @@ private
|
|
100
88
|
when head =~ %r{/\* XPM \*/} then :xpm
|
101
89
|
when head[0, 4] == '8BPS' then :psd
|
102
90
|
when head[0, 3] =~ /[FC]WS/ then :swf
|
103
|
-
when head =~ SVG_R || (head =~ XML_R && ir[0, 4096]
|
91
|
+
when head =~ SVG_R || (head =~ XML_R && ir[0, 4096] =~ SVG_R) then :svg
|
104
92
|
when head[0, 2] =~ /\n[\0-\5]/ then :pcx
|
105
93
|
when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
|
106
94
|
when head[0, 4] == "\0\0\1\0" then :ico
|
@@ -117,7 +105,7 @@ private
|
|
117
105
|
break if ['IDAT', 'IEND', nil].include?(type)
|
118
106
|
return :apng if type == 'acTL'
|
119
107
|
|
120
|
-
length = ir
|
108
|
+
length = ir.unpack1(offset, 4, 'N')
|
121
109
|
offset += 8 + length + 4
|
122
110
|
end
|
123
111
|
:png
|
@@ -144,7 +132,7 @@ private
|
|
144
132
|
end
|
145
133
|
|
146
134
|
def size_of_gif(ir)
|
147
|
-
ir
|
135
|
+
ir.unpack(6, 4, 'vv')
|
148
136
|
end
|
149
137
|
|
150
138
|
def size_of_mng(ir)
|
@@ -152,7 +140,7 @@ private
|
|
152
140
|
raise FormatError, 'MHDR not in place for MNG'
|
153
141
|
end
|
154
142
|
|
155
|
-
ir
|
143
|
+
ir.unpack(16, 8, 'NN')
|
156
144
|
end
|
157
145
|
|
158
146
|
def size_of_png(ir)
|
@@ -160,7 +148,7 @@ private
|
|
160
148
|
raise FormatError, 'IHDR not in place for PNG'
|
161
149
|
end
|
162
150
|
|
163
|
-
ir
|
151
|
+
ir.unpack(16, 8, 'NN')
|
164
152
|
end
|
165
153
|
alias_method :size_of_apng, :size_of_png
|
166
154
|
|
@@ -178,11 +166,11 @@ private
|
|
178
166
|
offset += 1 until section_marker != ir[offset + 1, 1]
|
179
167
|
raise FormatError, 'EOF in JPEG' if ir[offset, 1].nil?
|
180
168
|
|
181
|
-
|
169
|
+
code, length = ir.unpack(offset, 4, 'xCn')
|
182
170
|
offset += 4
|
183
171
|
|
184
172
|
if JPEG_CODE_CHECK.include?(code)
|
185
|
-
return ir
|
173
|
+
return ir.unpack(offset + 1, 4, 'nn').reverse
|
186
174
|
end
|
187
175
|
|
188
176
|
offset += length - 2
|
@@ -190,11 +178,11 @@ private
|
|
190
178
|
end
|
191
179
|
|
192
180
|
def size_of_bmp(ir)
|
193
|
-
header_size = ir
|
181
|
+
header_size = ir.unpack1(14, 4, 'V')
|
194
182
|
if header_size == 12
|
195
|
-
ir
|
183
|
+
ir.unpack(18, 4, 'vv')
|
196
184
|
else
|
197
|
-
ir
|
185
|
+
ir.unpack(18, 8, 'VV').map do |n|
|
198
186
|
if n > 0x7fff_ffff
|
199
187
|
0x1_0000_0000 - n # absolute value of converted to signed
|
200
188
|
else
|
@@ -216,7 +204,7 @@ private
|
|
216
204
|
def size_of_pam(ir)
|
217
205
|
width = height = nil
|
218
206
|
offset = 3
|
219
|
-
|
207
|
+
until width && height
|
220
208
|
if ir[offset, 1] == '#'
|
221
209
|
offset += 1 until ["\n", '', nil].include?(ir[offset, 1])
|
222
210
|
offset += 1
|
@@ -235,7 +223,6 @@ private
|
|
235
223
|
raise FormatError, "Unexpected data in PAM header: #{chunk.inspect}"
|
236
224
|
end
|
237
225
|
offset += $&.length
|
238
|
-
break if width && height
|
239
226
|
end
|
240
227
|
end
|
241
228
|
[width, height]
|
@@ -259,23 +246,23 @@ private
|
|
259
246
|
end
|
260
247
|
|
261
248
|
def size_of_psd(ir)
|
262
|
-
ir
|
249
|
+
ir.unpack(14, 8, 'NN').reverse
|
263
250
|
end
|
264
251
|
|
265
252
|
def size_of_tiff(ir)
|
266
|
-
endian2b = ir
|
253
|
+
endian2b = ir.fetch(0, 4) == "II*\000" ? 'v' : 'n'
|
267
254
|
endian4b = endian2b.upcase
|
268
255
|
packspec = [nil, 'C', nil, endian2b, endian4b, nil, 'c', nil, endian2b, endian4b]
|
269
256
|
|
270
|
-
offset = ir
|
271
|
-
num_dirent = ir
|
257
|
+
offset = ir.unpack1(4, 4, endian4b)
|
258
|
+
num_dirent = ir.unpack1(offset, 2, endian2b)
|
272
259
|
offset += 2
|
273
260
|
num_dirent = offset + (num_dirent * 12)
|
274
261
|
|
275
262
|
width = height = nil
|
276
263
|
until width && height
|
277
|
-
ifd = ir
|
278
|
-
raise FormatError, 'Reached end of directory entries in TIFF' if
|
264
|
+
ifd = ir.fetch(offset, 12)
|
265
|
+
raise FormatError, 'Reached end of directory entries in TIFF' if offset > num_dirent
|
279
266
|
|
280
267
|
tag, type = ifd.unpack(endian2b * 2)
|
281
268
|
offset += 12
|
@@ -294,14 +281,14 @@ private
|
|
294
281
|
end
|
295
282
|
|
296
283
|
def size_of_pcx(ir)
|
297
|
-
parts = ir
|
284
|
+
parts = ir.unpack(4, 8, 'v4')
|
298
285
|
[parts[2] - parts[0] + 1, parts[3] - parts[1] + 1]
|
299
286
|
end
|
300
287
|
|
301
288
|
def size_of_swf(ir)
|
302
|
-
value_bit_length = ir
|
303
|
-
bit_length = 5 + value_bit_length * 4
|
304
|
-
rect_bits = ir
|
289
|
+
value_bit_length = ir.unpack1(8, 1, 'B5').to_i(2)
|
290
|
+
bit_length = 5 + (value_bit_length * 4)
|
291
|
+
rect_bits = ir.unpack1(8, (bit_length / 8) + 1, "B#{bit_length}")
|
305
292
|
values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
|
306
293
|
x_min, x_max, y_min, y_max = values
|
307
294
|
[(x_max - x_min) / 20, (y_max - y_min) / 20]
|
@@ -309,7 +296,8 @@ private
|
|
309
296
|
|
310
297
|
def size_of_svg(ir)
|
311
298
|
attributes = {}
|
312
|
-
ir
|
299
|
+
svg_tag = ir[0, 1024][SVG_R, 1] || ir[0, 4096][SVG_R, 1]
|
300
|
+
svg_tag.scan(/(\S+)=(?:'([^']*)'|"([^"]*)"|([^'"\s]*))/) do |name, v0, v1, v2|
|
313
301
|
attributes[name] = v0 || v1 || v2
|
314
302
|
end
|
315
303
|
dpi = self.class.dpi
|
@@ -330,20 +318,20 @@ private
|
|
330
318
|
end
|
331
319
|
|
332
320
|
def size_of_ico(ir)
|
333
|
-
ir
|
321
|
+
ir.unpack(6, 2, 'CC').map{ |v| v.zero? ? 256 : v }
|
334
322
|
end
|
335
323
|
alias_method :size_of_cur, :size_of_ico
|
336
324
|
|
337
325
|
def size_of_webp(ir)
|
338
|
-
case ir
|
326
|
+
case ir.fetch(12, 4)
|
339
327
|
when 'VP8 '
|
340
|
-
ir
|
328
|
+
ir.unpack(26, 4, 'vv').map{ |v| v & 0x3fff }
|
341
329
|
when 'VP8L'
|
342
|
-
n = ir
|
343
|
-
[(n & 0x3fff) + 1, (n >> 14 & 0x3fff) + 1]
|
330
|
+
n = ir.unpack1(21, 4, 'V')
|
331
|
+
[(n & 0x3fff) + 1, ((n >> 14) & 0x3fff) + 1]
|
344
332
|
when 'VP8X'
|
345
|
-
w16, w8, h16, h8 = ir
|
346
|
-
[(w16 | w8 << 16) + 1, (h16 | h8 << 16) + 1]
|
333
|
+
w16, w8, h16, h8 = ir.unpack(24, 6, 'vCvC')
|
334
|
+
[(w16 | (w8 << 16)) + 1, (h16 | (h8 << 16)) + 1]
|
347
335
|
end
|
348
336
|
end
|
349
337
|
|
@@ -355,13 +343,13 @@ private
|
|
355
343
|
break if stop && offset >= stop
|
356
344
|
break if ir[offset, 4] == '' || ir[offset, 4].nil?
|
357
345
|
|
358
|
-
size = ir
|
359
|
-
type = ir
|
346
|
+
size = ir.unpack1(offset, 4, 'N')
|
347
|
+
type = ir.fetch(offset + 4, 4)
|
360
348
|
|
361
349
|
data_offset = 8
|
362
350
|
case size
|
363
351
|
when 1
|
364
|
-
size = ir
|
352
|
+
size = ir.unpack1(offset, 8, 'Q>')
|
365
353
|
data_offset = 16
|
366
354
|
raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
|
367
355
|
when 2..7
|
@@ -373,7 +361,7 @@ private
|
|
373
361
|
offset += data_offset
|
374
362
|
in_header = true
|
375
363
|
elsif in_header && type == 'ihdr'
|
376
|
-
return ir
|
364
|
+
return ir.unpack(offset + data_offset, 8, 'NN').reverse
|
377
365
|
else
|
378
366
|
break if size.zero? # box to the end of file
|
379
367
|
|
@@ -384,6 +372,6 @@ private
|
|
384
372
|
alias_method :size_of_jpx, :size_of_jp2
|
385
373
|
|
386
374
|
def size_of_j2c(ir)
|
387
|
-
ir
|
375
|
+
ir.unpack(8, 8, 'NN')
|
388
376
|
end
|
389
377
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
require 'image_size/chunky_reader'
|
6
|
+
|
7
|
+
describe ImageSize::ChunkyReader do
|
8
|
+
context :[] do
|
9
|
+
test_reader = Class.new do
|
10
|
+
include ImageSize::ChunkyReader
|
11
|
+
|
12
|
+
def initialize(string)
|
13
|
+
@string = string
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def chunk(i)
|
19
|
+
@string[i * chunk_size, chunk_size]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
custom_chunk_size_reader = Class.new(test_reader) do
|
24
|
+
def chunk_size
|
25
|
+
100
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
{
|
30
|
+
'empty string' => '',
|
31
|
+
'a bit of data' => 'foo bar baz',
|
32
|
+
'a lot of data' => File.open('GPL', 'rb', &:read),
|
33
|
+
}.each do |data_description, data|
|
34
|
+
{
|
35
|
+
'default' => test_reader.new(data),
|
36
|
+
'custom' => custom_chunk_size_reader.new(data),
|
37
|
+
}.each do |chunk_size_description, reader|
|
38
|
+
context "for #{data_description} using reader with #{chunk_size_description} chunk size" do
|
39
|
+
it 'raises ArgumentError for negative offset' do
|
40
|
+
[-1, 0, 1, 100].each do |length|
|
41
|
+
expect{ reader[-1, length] }.to raise_exception(ArgumentError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'behaves same as fetching a string for any offset and length' do
|
46
|
+
full_chunks = data.length / reader.chunk_size
|
47
|
+
offsets = [0, 1, full_chunks - 1, full_chunks, full_chunks + 1].map do |i|
|
48
|
+
[-1, 0, 1].map do |add|
|
49
|
+
(i * reader.chunk_size) + add
|
50
|
+
end
|
51
|
+
end.flatten
|
52
|
+
|
53
|
+
offsets.each do |offset|
|
54
|
+
next if offset < 0
|
55
|
+
|
56
|
+
offsets.each do |offset_b|
|
57
|
+
length = offset_b - offset
|
58
|
+
expect(reader[offset, length]).to eq(data[offset, length]),
|
59
|
+
"for offset #{offset} and length #{length}\n"\
|
60
|
+
"expected: #{data[offset, length].inspect}\n"\
|
61
|
+
" got: #{reader[offset, length].inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
require 'image_size/seekable_io_reader'
|
6
|
+
|
7
|
+
describe ImageSize::SeekableIOReader do
|
8
|
+
context :[] do
|
9
|
+
def ios
|
10
|
+
@ios ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def io
|
14
|
+
File.open('GPL', 'rb').tap do |io|
|
15
|
+
ios << io
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
ios.pop.close until ios.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_reader
|
24
|
+
ImageSize::SeekableIOReader.new(io)
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:content){ io.read }
|
28
|
+
|
29
|
+
it 'reads as expected when pieces are read consecutively' do
|
30
|
+
reader = new_reader
|
31
|
+
0.step(content.length + 4096, 100) do |offset|
|
32
|
+
expect(reader[offset, 100]).to eq(content[offset, 100])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'reads as expected when pieces are read backwards' do
|
37
|
+
reader = new_reader
|
38
|
+
(content.length + 4096).step(0, -100) do |offset|
|
39
|
+
expect(reader[offset, 100]).to eq(content[offset, 100])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'reads as expected when pieces are read in random order' do
|
44
|
+
100.times do
|
45
|
+
reader = new_reader
|
46
|
+
0.step(content.length + 4096, 100).to_a.shuffle.each do |offset|
|
47
|
+
expect(reader[offset, 100]).to eq(content[offset, 100])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/image_size_spec.rb
CHANGED
@@ -1,15 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rspec'
|
4
|
+
|
4
5
|
require 'image_size'
|
6
|
+
require 'image_size/uri_reader'
|
7
|
+
|
5
8
|
require 'tempfile'
|
9
|
+
require 'shellwords'
|
10
|
+
require 'webrick'
|
6
11
|
|
7
12
|
describe ImageSize do
|
8
|
-
|
13
|
+
before :all do
|
14
|
+
@server = WEBrick::HTTPServer.new({
|
15
|
+
:Logger => WEBrick::Log.new(StringIO.new),
|
16
|
+
:AccessLog => [],
|
17
|
+
:BindAddress => '127.0.0.1',
|
18
|
+
:Port => 0, # get the next available port
|
19
|
+
:DocumentRoot => '.',
|
20
|
+
})
|
21
|
+
@server_thread = Thread.new{ @server.start }
|
22
|
+
@server_base_url = URI("http://127.0.0.1:#{@server.config[:Port]}/")
|
23
|
+
end
|
9
24
|
|
10
|
-
|
11
|
-
|
12
|
-
|
25
|
+
after :all do
|
26
|
+
@server.shutdown
|
27
|
+
@server_thread.join
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir['spec/**/*'].each do |path|
|
31
|
+
next unless File.file?(path)
|
13
32
|
|
14
33
|
describe "for #{path}" do
|
15
34
|
let(:name){ File.basename(path) }
|
@@ -27,6 +46,15 @@ describe ImageSize do
|
|
27
46
|
}
|
28
47
|
end
|
29
48
|
let(:file_data){ File.open(path, 'rb', &:read) }
|
49
|
+
let(:file_size){ file_data.length }
|
50
|
+
|
51
|
+
before do
|
52
|
+
max_file_size = 16_384
|
53
|
+
|
54
|
+
if file_size > max_file_size
|
55
|
+
raise "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})"
|
56
|
+
end
|
57
|
+
end
|
30
58
|
|
31
59
|
context 'given as data' do
|
32
60
|
it 'gets format and dimensions' do
|
@@ -43,20 +71,33 @@ describe ImageSize do
|
|
43
71
|
image_size = ImageSize.new(io)
|
44
72
|
expect(image_size).to have_attributes(attributes)
|
45
73
|
expect(io).not_to be_closed
|
46
|
-
|
74
|
+
if file_size.zero?
|
75
|
+
expect(io.pos).to be_zero
|
76
|
+
else
|
77
|
+
expect(io.pos).to_not be_zero
|
78
|
+
end
|
47
79
|
io.rewind
|
48
80
|
expect(io.read).to eq(file_data)
|
49
81
|
end
|
50
82
|
end
|
51
83
|
end
|
52
84
|
|
85
|
+
context 'given as unseekable IO' do
|
86
|
+
it 'gets format and dimensions' do
|
87
|
+
IO.popen(%W[cat #{path}].shelljoin, 'rb') do |io|
|
88
|
+
image_size = ImageSize.new(io)
|
89
|
+
expect(image_size).to have_attributes(attributes)
|
90
|
+
expect(io).not_to be_closed
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
53
95
|
context 'given as StringIO' do
|
54
96
|
it 'gets format and dimensions' do
|
55
97
|
io = StringIO.new(file_data)
|
56
98
|
image_size = ImageSize.new(io)
|
57
99
|
expect(image_size).to have_attributes(attributes)
|
58
100
|
expect(io).not_to be_closed
|
59
|
-
expect(io.pos).to_not be_zero
|
60
101
|
io.rewind
|
61
102
|
expect(io.read).to eq(file_data)
|
62
103
|
end
|
@@ -71,7 +112,11 @@ describe ImageSize do
|
|
71
112
|
image_size = ImageSize.new(io)
|
72
113
|
expect(image_size).to have_attributes(attributes)
|
73
114
|
expect(io).not_to be_closed
|
74
|
-
|
115
|
+
if file_size.zero?
|
116
|
+
expect(io.pos).to be_zero
|
117
|
+
else
|
118
|
+
expect(io.pos).to_not be_zero
|
119
|
+
end
|
75
120
|
io.rewind
|
76
121
|
expect(io.read).to eq(file_data)
|
77
122
|
end
|
@@ -84,6 +129,13 @@ describe ImageSize do
|
|
84
129
|
expect(image_size).to have_attributes(attributes)
|
85
130
|
end
|
86
131
|
end
|
132
|
+
|
133
|
+
context 'fetching from webserver' do
|
134
|
+
it 'gets format and dimensions' do
|
135
|
+
image_size = ImageSize.url(@server_base_url + path)
|
136
|
+
expect(image_size).to have_attributes(attributes)
|
137
|
+
end
|
138
|
+
end
|
87
139
|
end
|
88
140
|
end
|
89
141
|
|
@@ -0,0 +1 @@
|
|
1
|
+
* -text
|
data/spec/images/empty
ADDED
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!--[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]-->
|
3
|
+
<!--[ [[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]] ]-->
|
4
|
+
<!--[ [ [[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]] ] ]-->
|
5
|
+
<!--[ [ [ [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] ] ] ]-->
|
6
|
+
<!--[ [ [ [ [[[[[[[[[[[[]]]]]]]]]]]] ] ] ] ]-->
|
7
|
+
<!--[ [ [ [ [ [[[[[[[[]]]]]]]] ] ] ] ] ]-->
|
8
|
+
<!--[ [ [ [ [ [ [[[[]]]] ] ] ] ] ] ]-->
|
9
|
+
<!--[ [ [ [ [ [ [ ] ] ] ] ] ] ]-->
|
10
|
+
<!--[ [ [ [ [ [ [ ] ] ] ] ] ] ]-->
|
11
|
+
<!--[ [ [ [ [ [ [[[[]]]] ] ] ] ] ] ]-->
|
12
|
+
<!--[ [ [ [ [ [[[[[[[[]]]]]]]] ] ] ] ] ]-->
|
13
|
+
<!--[ [ [ [ [[[[[[[[[[[[]]]]]]]]]]]] ] ] ] ]-->
|
14
|
+
<!--[ [ [ [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] ] ] ]-->
|
15
|
+
<!--[ [ [[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]] ] ]-->
|
16
|
+
<!--[ [[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]] ]-->
|
17
|
+
<!--[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]-->
|
18
|
+
<svg width="1in" height="100" xmlns="http://www.w3.org/2000/svg">
|
19
|
+
<circle cx="36" cy="50" r="30" stroke="black" stroke-width="10" fill="red" />
|
20
|
+
</svg>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!--[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]-->
|
3
|
+
<!--[ [[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]] ]-->
|
4
|
+
<!--[ [ [[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]] ] ]-->
|
5
|
+
<!--[ [ [ [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] ] ] ]-->
|
6
|
+
<!--[ [ [ [ [[[[[[[[[[[[]]]]]]]]]]]] ] ] ] ]-->
|
7
|
+
<!--[ [ [ [ [ [[[[[[[[]]]]]]]] ] ] ] ] ]-->
|
8
|
+
<!--[ [ [ [ [ [ [[[[]]]] ] ] ] ] ] ]-->
|
9
|
+
<!--[ [ [ [ [ [ [ ] ] ] ] ] ] ]-->
|
10
|
+
<!--[ [ [ [ [ [ [ ] ] ] ] ] ] ]-->
|
11
|
+
<!--[ [ [ [ [ [ [[[[]]]] ] ] ] ] ] ]-->
|
12
|
+
<!--[ [ [ [ [ [[[[[[[[]]]]]]]] ] ] ] ] ]-->
|
13
|
+
<!--[ [ [ [ [[[[[[[[[[[[]]]]]]]]]]]] ] ] ] ]-->
|
14
|
+
<!--[ [ [ [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] ] ] ]-->
|
15
|
+
<!--[ [ [[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]] ] ]-->
|
16
|
+
<!--[ [[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]] ]-->
|
17
|
+
<!--[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]-->
|
18
|
+
<svg width="1in" height="100" xmlns="http://www.w3.org/2000/svg">
|
19
|
+
<circle cx="36" cy="50" r="30" stroke="black" stroke-width="10" fill="red" />
|
20
|
+
</svg>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#define cursor_width 16
|
2
|
+
#define cursor_height 32
|
3
|
+
static unsigned char cursor_bits[] = {
|
4
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
5
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
6
|
+
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x84, 0x10,
|
7
|
+
0xe8, 0x0b, 0x90, 0x04, 0xa8, 0x0a, 0x88, 0x08,
|
8
|
+
0xfe, 0x3f, 0x88, 0x08, 0xa8, 0x0a, 0x90, 0x04,
|
9
|
+
0xe8, 0x0b, 0x84, 0x10, 0x80, 0x00, 0x00, 0x00,
|
10
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
11
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/* XPM */
|
2
|
+
static char *test2[] = {
|
3
|
+
/* columns rows colors chars-per-pixel */
|
4
|
+
"24 32 2 1 ",
|
5
|
+
" c red",
|
6
|
+
". c white",
|
7
|
+
/* pixels */
|
8
|
+
"........................",
|
9
|
+
"........................",
|
10
|
+
"........................",
|
11
|
+
"........................",
|
12
|
+
"........................",
|
13
|
+
"........................",
|
14
|
+
".. .................. ",
|
15
|
+
".. ................ ",
|
16
|
+
"... .............. .",
|
17
|
+
".... ............ ..",
|
18
|
+
"..... .......... ...",
|
19
|
+
"...... ........ ....",
|
20
|
+
"....... ...... .....",
|
21
|
+
"........ .... ......",
|
22
|
+
"......... .. .......",
|
23
|
+
".......... ........",
|
24
|
+
"........... .........",
|
25
|
+
"............ .........",
|
26
|
+
"........... ........",
|
27
|
+
".......... . .......",
|
28
|
+
"......... ... ......",
|
29
|
+
"........ ..... .....",
|
30
|
+
"....... ....... ....",
|
31
|
+
"...... ......... ...",
|
32
|
+
"..... ........... ..",
|
33
|
+
".... ............. .",
|
34
|
+
"... ............... ",
|
35
|
+
".. ................. ",
|
36
|
+
".. ................... ",
|
37
|
+
"........................",
|
38
|
+
"........................",
|
39
|
+
"........................"
|
40
|
+
};
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_size
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keisuke Minami
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -31,14 +31,28 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '1.
|
34
|
+
version: '1.22'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '1.
|
41
|
+
version: '1.22'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rubocop-rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.0'
|
42
56
|
description: 'Measure following file dimensions: apng, bmp, cur, gif, ico, j2c, jp2,
|
43
57
|
jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm, xpm'
|
44
58
|
email:
|
@@ -47,6 +61,7 @@ extensions: []
|
|
47
61
|
extra_rdoc_files: []
|
48
62
|
files:
|
49
63
|
- ".github/workflows/check.yml"
|
64
|
+
- ".github/workflows/rubocop.yml"
|
50
65
|
- ".gitignore"
|
51
66
|
- ".rubocop.yml"
|
52
67
|
- ".rubocop_todo.yml"
|
@@ -57,11 +72,21 @@ files:
|
|
57
72
|
- README.markdown
|
58
73
|
- image_size.gemspec
|
59
74
|
- lib/image_size.rb
|
75
|
+
- lib/image_size/chunky_reader.rb
|
76
|
+
- lib/image_size/reader.rb
|
77
|
+
- lib/image_size/seekable_io_reader.rb
|
78
|
+
- lib/image_size/stream_io_reader.rb
|
79
|
+
- lib/image_size/string_reader.rb
|
80
|
+
- lib/image_size/uri_reader.rb
|
81
|
+
- spec/image_size/chunky_reader_spec.rb
|
82
|
+
- spec/image_size/seekable_io_reader_spec.rb
|
60
83
|
- spec/image_size_spec.rb
|
84
|
+
- spec/images/.gitattributes
|
61
85
|
- spec/images/bmp/v2.42x50.bmp
|
62
86
|
- spec/images/bmp/v3-bottom2top.42x50.bmp
|
63
87
|
- spec/images/bmp/v3-top2bottom.42x50.bmp
|
64
88
|
- spec/images/cur/32x256.cur
|
89
|
+
- spec/images/empty
|
65
90
|
- spec/images/gif/668x481.gif
|
66
91
|
- spec/images/ico/32x256.ico
|
67
92
|
- spec/images/jp2/163x402.jp2
|
@@ -82,6 +107,9 @@ files:
|
|
82
107
|
- spec/images/pnm/ascii.22x25.ppm
|
83
108
|
- spec/images/psd/16x20.psd
|
84
109
|
- spec/images/svg/72x100.svg
|
110
|
+
- spec/images/svg/crlf.72x100.svg
|
111
|
+
- spec/images/svg/long.72x100.svg
|
112
|
+
- spec/images/svg/long.crlf.72x100.svg
|
85
113
|
- spec/images/swf/450x200.swf
|
86
114
|
- spec/images/tiff/big-endian.68x49.tiff
|
87
115
|
- spec/images/tiff/little-endian.40x68.tiff
|
@@ -89,14 +117,16 @@ files:
|
|
89
117
|
- spec/images/webp/lossless.16x32.webp
|
90
118
|
- spec/images/webp/lossy.16x32.webp
|
91
119
|
- spec/images/xbm/16x32.xbm
|
120
|
+
- spec/images/xbm/crlf.16x32.xbm
|
92
121
|
- spec/images/xpm/24x32.xpm
|
122
|
+
- spec/images/xpm/crlf.24x32.xpm
|
93
123
|
homepage: https://github.com/toy/image_size
|
94
124
|
licenses:
|
95
125
|
- Ruby
|
96
126
|
metadata:
|
97
127
|
bug_tracker_uri: https://github.com/toy/image_size/issues
|
98
128
|
changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
|
99
|
-
documentation_uri: https://www.rubydoc.info/gems/image_size/
|
129
|
+
documentation_uri: https://www.rubydoc.info/gems/image_size/3.0.2
|
100
130
|
source_code_uri: https://github.com/toy/image_size
|
101
131
|
post_install_message:
|
102
132
|
rdoc_options: []
|
@@ -113,16 +143,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
143
|
- !ruby/object:Gem::Version
|
114
144
|
version: '0'
|
115
145
|
requirements: []
|
116
|
-
rubygems_version: 3.
|
146
|
+
rubygems_version: 3.3.11
|
117
147
|
signing_key:
|
118
148
|
specification_version: 4
|
119
149
|
summary: Measure image size using pure Ruby
|
120
150
|
test_files:
|
151
|
+
- spec/image_size/chunky_reader_spec.rb
|
152
|
+
- spec/image_size/seekable_io_reader_spec.rb
|
121
153
|
- spec/image_size_spec.rb
|
154
|
+
- spec/images/.gitattributes
|
122
155
|
- spec/images/bmp/v2.42x50.bmp
|
123
156
|
- spec/images/bmp/v3-bottom2top.42x50.bmp
|
124
157
|
- spec/images/bmp/v3-top2bottom.42x50.bmp
|
125
158
|
- spec/images/cur/32x256.cur
|
159
|
+
- spec/images/empty
|
126
160
|
- spec/images/gif/668x481.gif
|
127
161
|
- spec/images/ico/32x256.ico
|
128
162
|
- spec/images/jp2/163x402.jp2
|
@@ -143,6 +177,9 @@ test_files:
|
|
143
177
|
- spec/images/pnm/ascii.22x25.ppm
|
144
178
|
- spec/images/psd/16x20.psd
|
145
179
|
- spec/images/svg/72x100.svg
|
180
|
+
- spec/images/svg/crlf.72x100.svg
|
181
|
+
- spec/images/svg/long.72x100.svg
|
182
|
+
- spec/images/svg/long.crlf.72x100.svg
|
146
183
|
- spec/images/swf/450x200.swf
|
147
184
|
- spec/images/tiff/big-endian.68x49.tiff
|
148
185
|
- spec/images/tiff/little-endian.40x68.tiff
|
@@ -150,4 +187,6 @@ test_files:
|
|
150
187
|
- spec/images/webp/lossless.16x32.webp
|
151
188
|
- spec/images/webp/lossy.16x32.webp
|
152
189
|
- spec/images/xbm/16x32.xbm
|
190
|
+
- spec/images/xbm/crlf.16x32.xbm
|
153
191
|
- spec/images/xpm/24x32.xpm
|
192
|
+
- spec/images/xpm/crlf.24x32.xpm
|