image_size 1.4.2 → 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.
- checksums.yaml +5 -13
- data/.rubocop.yml +80 -0
- data/.rubocop_todo.yml +28 -0
- data/.travis.yml +25 -8
- data/CHANGELOG.markdown +117 -0
- data/Gemfile +7 -1
- data/README.markdown +69 -21
- data/image_size.gemspec +12 -4
- data/lib/image_size.rb +205 -87
- data/spec/image_size_spec.rb +79 -71
- data/spec/{test2.bmp → images/bmp/v2.42x50.bmp} +0 -0
- data/spec/{test3b.bmp → images/bmp/v3-bottom2top.42x50.bmp} +0 -0
- data/spec/{test3t.bmp → images/bmp/v3-top2bottom.42x50.bmp} +0 -0
- data/spec/images/cur/32x256.cur +0 -0
- data/spec/{test.gif → images/gif/668x481.gif} +0 -0
- data/spec/images/ico/32x256.ico +0 -0
- data/spec/images/jp2/163x402.jp2 +0 -0
- data/spec/images/jp2/176x373.jpx +0 -0
- data/spec/images/jp2/224x293.j2c +0 -0
- data/spec/images/jpeg/436x429.jpeg +0 -0
- data/spec/images/jpeg/extraneous-bytes.436x429.jpeg +0 -0
- data/spec/images/mng/61x42.mng +0 -0
- data/spec/{test.pcx → images/pcx/70x60.pcx} +0 -0
- data/spec/images/png/192x110.apng +0 -0
- data/spec/{test.png → images/png/640x532.png} +0 -0
- data/spec/images/pnm/22x25.pam +8 -0
- data/spec/images/pnm/22x25.pbm +0 -0
- data/spec/images/pnm/22x25.pgm +4 -0
- data/spec/images/pnm/22x25.ppm +4 -0
- data/spec/images/pnm/ascii.22x25.pbm +27 -0
- data/spec/images/pnm/ascii.22x25.pgm +28 -0
- data/spec/images/pnm/ascii.22x25.ppm +28 -0
- data/spec/{test.psd → images/psd/16x20.psd} +0 -0
- data/spec/{test.svg → images/svg/72x100.svg} +0 -0
- data/spec/{test.swf → images/swf/450x200.swf} +0 -0
- data/spec/images/tiff/big-endian.68x49.tiff +0 -0
- data/spec/images/tiff/little-endian.40x68.tiff +0 -0
- data/spec/images/webp/extended.16x32.webp +0 -0
- data/spec/images/webp/lossless.16x32.webp +0 -0
- data/spec/images/webp/lossy.16x32.webp +0 -0
- data/spec/{test.xbm → images/xbm/16x32.xbm} +0 -0
- data/spec/{test.xpm → images/xpm/24x32.xpm} +0 -0
- metadata +108 -55
- data/spec/test.cur +0 -0
- data/spec/test.ico +0 -0
- data/spec/test.jpg +0 -0
- data/spec/test.pbm +0 -0
- data/spec/test.pgm +0 -5
- data/spec/test.tif +0 -0
- data/spec/test2.jpg +0 -0
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YTAzMDA3MTIzYzJjYzgxNDIyNjRjZTlhMmNlZDFlNTY3MzU3MmQyOA==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9ecfd6172b63860fd5e18bf96cdfc79c7ab275f52643d6069ef4386978a70187
|
4
|
+
data.tar.gz: c0be8f8fea7b00fb2ad849598fb461238942a836f89dd63edc452432637e1fd5
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
MjI1NjRiNDFkMTE1ZGE3YzZmYjgwYTcyYmE2NDg5ZWY2YWM2NWVkZGEwNDVj
|
11
|
-
OWIyM2M0NjIwMmE5Y2Y1YzY5OWM4NTdlMjA4ZjQxMGMxYzNjMmI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MTI2YTEwM2IzNTAwYmExNGE5MTE1NGUyNGQyNTFlNTFiMTA2NzEyYzNlZDYw
|
14
|
-
MzMzYzFmMTQ4NjY0ZWI2YmUxNGNkYWJlYWVlMjZiMzZmNDczOTU0NDJlZTM2
|
15
|
-
ODUzZGUxYWJkYTEyOGIwNDdlYTQzNjdmOWQyMmUzNWEzYzIyMDE=
|
6
|
+
metadata.gz: 1536d248784158f8163a3fd7cde488af4a5dbd6e5ccfde7e40e491f5679db4bdd87a0834de00c077d3312090e36c677d6df29fed85cd3b45596b1303831fb33b
|
7
|
+
data.tar.gz: 2251c45b938da475db4a03ea570bdc3e2b513309817a874c9e0b4b458b83d67f7687af691a84a6f98aaa39de6241a5e4dc8f8177552f0ac9e051b494626398e0
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
- '*.gemspec'
|
6
|
+
NewCops: enable
|
7
|
+
|
8
|
+
Layout/AccessModifierIndentation:
|
9
|
+
EnforcedStyle: outdent
|
10
|
+
|
11
|
+
Layout/CaseIndentation:
|
12
|
+
EnforcedStyle: end
|
13
|
+
|
14
|
+
Layout/EndAlignment:
|
15
|
+
EnforcedStyleAlignWith: variable
|
16
|
+
|
17
|
+
Layout/LineLength:
|
18
|
+
Max: 120
|
19
|
+
|
20
|
+
Layout/SpaceBeforeBlockBraces:
|
21
|
+
EnforcedStyle: no_space
|
22
|
+
EnforcedStyleForEmptyBraces: no_space
|
23
|
+
|
24
|
+
Lint/PercentStringArray: # broken in rubocop 0.55.0
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Metrics/BlockLength:
|
28
|
+
Exclude:
|
29
|
+
- 'spec/**/*.rb'
|
30
|
+
|
31
|
+
Metrics/ClassLength:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Metrics/MethodLength:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Naming/MethodParameterName:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/AccessorGrouping:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/Alias:
|
44
|
+
EnforcedStyle: prefer_alias_method
|
45
|
+
|
46
|
+
Style/EmptyCaseCondition:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Style/Encoding:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/HashEachMethods:
|
53
|
+
Enabled: true
|
54
|
+
|
55
|
+
Style/HashSyntax:
|
56
|
+
EnforcedStyle: hash_rockets
|
57
|
+
|
58
|
+
Style/HashTransformKeys:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Style/HashTransformValues:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Style/IfUnlessModifier:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
Style/ParallelAssignment:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/SafeNavigation:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Style/TrailingCommaInArrayLiteral:
|
74
|
+
EnforcedStyleForMultiline: comma
|
75
|
+
|
76
|
+
Style/TrailingCommaInHashLiteral:
|
77
|
+
EnforcedStyleForMultiline: comma
|
78
|
+
|
79
|
+
Style/UnpackFirst:
|
80
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config --no-auto-gen-timestamp`
|
3
|
+
# using RuboCop version 0.89.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 8
|
10
|
+
# Configuration parameters: IgnoredMethods.
|
11
|
+
Metrics/AbcSize:
|
12
|
+
Max: 43
|
13
|
+
|
14
|
+
# Offense count: 5
|
15
|
+
# Configuration parameters: IgnoredMethods.
|
16
|
+
Metrics/CyclomaticComplexity:
|
17
|
+
Max: 21
|
18
|
+
|
19
|
+
# Offense count: 2
|
20
|
+
# Configuration parameters: IgnoredMethods.
|
21
|
+
Metrics/PerceivedComplexity:
|
22
|
+
Max: 21
|
23
|
+
|
24
|
+
# Offense count: 8
|
25
|
+
# Cop supports --auto-correct.
|
26
|
+
Style/PerlBackrefs:
|
27
|
+
Exclude:
|
28
|
+
- 'lib/image_size.rb'
|
data/.travis.yml
CHANGED
@@ -1,10 +1,27 @@
|
|
1
|
+
sudo: false
|
2
|
+
dist: trusty
|
1
3
|
language: ruby
|
4
|
+
before_install:
|
5
|
+
- gem install bundler || gem install bundler --version '< 2'
|
2
6
|
rvm:
|
3
|
-
- 1.8.7
|
4
|
-
- 1.9.
|
5
|
-
-
|
6
|
-
- 2.
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
7
|
+
- '1.8.7-p374'
|
8
|
+
- '1.9.3-p551'
|
9
|
+
- '2.0.0-p648'
|
10
|
+
- '2.1.10'
|
11
|
+
- '2.2.10'
|
12
|
+
- '2.3.8'
|
13
|
+
- '2.4.10'
|
14
|
+
- '2.5.8'
|
15
|
+
- '2.6.6'
|
16
|
+
- '2.7.1'
|
17
|
+
- 'jruby-9.1.17.0'
|
18
|
+
- 'jruby-9.2.11.1'
|
19
|
+
script: bundle exec rspec
|
20
|
+
matrix:
|
21
|
+
include:
|
22
|
+
- env: RUBOCOP=1
|
23
|
+
rvm: '2.6.6'
|
24
|
+
script: bundle exec rubocop
|
25
|
+
- env: CHECK_RUBIES=1
|
26
|
+
rvm: '2.6.6'
|
27
|
+
script: bundle exec travis_check_rubies
|
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# ChangeLog
|
2
|
+
|
3
|
+
## unreleased
|
4
|
+
|
5
|
+
## v2.1.0 (2020-08-09)
|
6
|
+
|
7
|
+
* Add handling of JPEG 2000: part 1 (jp2), part 2 (jpx) and codestream (j2c) [#13](https://github.com/toy/image_size/issues/13) [@toy](https://github.com/toy)
|
8
|
+
* Correct handling of pam images and cleanup handling of Netpbm images (pbm, pgm, pnm) [@toy](https://github.com/toy)
|
9
|
+
|
10
|
+
## v2.0.2 (2019-07-14)
|
11
|
+
|
12
|
+
* Remove deprecated `rubyforge_project` attribute from gemspec [rubygems/rubygems#2436](https://github.com/rubygems/rubygems/pull/2436) [@toy](https://github.com/toy)
|
13
|
+
|
14
|
+
## v2.0.1 (2019-05-17)
|
15
|
+
|
16
|
+
* Adapt to frozen string literals [@toy](https://github.com/toy)
|
17
|
+
|
18
|
+
## v2.0.0 (2018-05-01)
|
19
|
+
|
20
|
+
* Allow any class responding to `read` and `eof?` to be passed to `ImageSize` [@toy](https://github.com/toy)
|
21
|
+
* Introduce `rubocop` [@toy](https://github.com/toy)
|
22
|
+
* Use `File.open` instead of `Kernel#open` [@toy](https://github.com/toy)
|
23
|
+
* Don’t rewind `IO` before or after usage [@toy](https://github.com/toy)
|
24
|
+
* Enhance readme [@toy](https://github.com/toy)
|
25
|
+
|
26
|
+
## v1.5.0 (2016-11-20)
|
27
|
+
|
28
|
+
* Support `WEBP` images [@toy](https://github.com/toy)
|
29
|
+
* Cleanup `GIF`, `PPM` and `SWF` magic number matching [@toy](https://github.com/toy)
|
30
|
+
* Fix `GIF` magic number (matched `GIF8,a`) [@toy](https://github.com/toy)
|
31
|
+
* Detect `APNG` images by `acTL` chunk [@toy](https://github.com/toy)
|
32
|
+
* Support `MNG` images [@toy](https://github.com/toy)
|
33
|
+
|
34
|
+
## v1.4.2 (2016-02-18)
|
35
|
+
|
36
|
+
* Fixed license in gemspec to be Ruby [#10](https://github.com/toy/image_size/issues/10) [@toy](https://github.com/toy)
|
37
|
+
|
38
|
+
## v1.4.1 (2014-11-19)
|
39
|
+
|
40
|
+
* Missed `ICO` and `CUR` in description [@toy](https://github.com/toy)
|
41
|
+
|
42
|
+
## v1.4.0 (2014-11-19)
|
43
|
+
|
44
|
+
* Detecting `ICO` and `CUR` images [@toy](https://github.com/toy)
|
45
|
+
|
46
|
+
## v1.3.1 (2014-06-24)
|
47
|
+
|
48
|
+
* Fix reading `JPEGs` with extraneous bytes [@toy](https://github.com/toy)
|
49
|
+
|
50
|
+
## v1.3.0 (2014-04-06)
|
51
|
+
|
52
|
+
* Raise `FormatError` instead of `RuntimeError` [@toy](https://github.com/toy)
|
53
|
+
|
54
|
+
## v1.2.0 (2014-02-01)
|
55
|
+
|
56
|
+
* Basic handling of `SVG` (only width and height attributes) [@toy](https://github.com/toy)
|
57
|
+
* Enhance matching `PCX` [@toy](https://github.com/toy)
|
58
|
+
|
59
|
+
## v1.1.5 (2013-12-23)
|
60
|
+
|
61
|
+
* Fix reading dimensions of `BMP v2` and `BMP v3` [@toy](https://github.com/toy)
|
62
|
+
* Fix swapped `PSD` width (columns) and height (rows) [#9](https://github.com/toy/image_size/issues/9) [@toy](https://github.com/toy)
|
63
|
+
* Replace square test images with rectangle ones [@toy](https://github.com/toy)
|
64
|
+
|
65
|
+
## v1.1.4 (2013-11-05)
|
66
|
+
|
67
|
+
* Close instead of only rewinding `IO` instances [@toy](https://github.com/toy)
|
68
|
+
* Add `.travis.yml` and supporting files [#8](https://github.com/toy/image_size/pull/8) [@petergoldstein](https://github.com/petergoldstein)
|
69
|
+
|
70
|
+
## v1.1.3 (2013-07-24)
|
71
|
+
|
72
|
+
* Enforce binary encoding of data returned by `ImageReader#[]` [#6](https://github.com/toy/image_size/issues/6) [@toy](https://github.com/toy)
|
73
|
+
|
74
|
+
## v1.1.2 (2013-02-24)
|
75
|
+
|
76
|
+
* Explicitly set encoding to `ASCII-8BIT` as for `ruby2.0.0-p0` it will be `UTF-8` by default [#5](https://github.com/toy/image_size/pull/5) [@walf443](https://github.com/walf443)
|
77
|
+
|
78
|
+
## v1.1.1 (2012-06-19)
|
79
|
+
|
80
|
+
* Fix exception in message for exception [#3](https://github.com/toy/image_size/pull/3) [@yachi](https://github.com/yachi)
|
81
|
+
|
82
|
+
## v1.1.0 (2012-02-25)
|
83
|
+
|
84
|
+
* Rework most code [@toy](https://github.com/toy)
|
85
|
+
* `Size` class instead of dynamically adding `to_s` method to size array [@toy](https://github.com/toy)
|
86
|
+
* `ImageSize.path`, more examples [@toy](https://github.com/toy)
|
87
|
+
* Added support for `Tempfile`, as well as fixed bug when running `ImageSize` on the same `IO` stream twice [#2](https://github.com/toy/image_size/pull/2) [@kanevski](https://github.com/kanevski)
|
88
|
+
|
89
|
+
## v1.0.6 (2012-02-08)
|
90
|
+
|
91
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
92
|
+
|
93
|
+
## v1.0.5 (2012-02-04)
|
94
|
+
|
95
|
+
* Fix getting `SWF` dimensions for ruby 1.9 [@toy](https://github.com/toy)
|
96
|
+
* Fix determining `PCX` for ruby1.9 [@toy](https://github.com/toy)
|
97
|
+
* Enhance `SWF` checking [@toy](https://github.com/toy)
|
98
|
+
|
99
|
+
## v1.0.4 (2011-12-16)
|
100
|
+
|
101
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
102
|
+
|
103
|
+
## v1.0.3 (2011-02-21)
|
104
|
+
|
105
|
+
* Fix permissions on `lib/image_size.rb` from `700` to `644` [#1](https://github.com/toy/image_size/issues/1) [@toy](https://github.com/toy)
|
106
|
+
|
107
|
+
## v1.0.2 (2010-12-15)
|
108
|
+
|
109
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
110
|
+
|
111
|
+
## v1.0.1 (2010-12-15)
|
112
|
+
|
113
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
114
|
+
|
115
|
+
## v1.0.0 (2010-11-01)
|
116
|
+
|
117
|
+
* Initial commit [@toy](https://github.com/toy)
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -1,40 +1,87 @@
|
|
1
|
+
[](https://rubygems.org/gems/image_size)
|
2
|
+
[](https://travis-ci.org/toy/image_size)
|
3
|
+
|
1
4
|
# image_size
|
2
5
|
|
3
6
|
measure image size using pure Ruby
|
4
|
-
formats: `bmp`, `cur`, `gif`, `jpeg`, `
|
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
|
+
|
9
|
+
## Installation
|
5
10
|
|
6
|
-
|
11
|
+
```sh
|
12
|
+
gem install image_size
|
13
|
+
```
|
7
14
|
|
8
|
-
|
15
|
+
### Bundler
|
9
16
|
|
10
|
-
|
17
|
+
Add to your `Gemfile`:
|
11
18
|
|
12
|
-
|
19
|
+
```ruby
|
20
|
+
gem 'image_size', '~> 2.0'
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
image_size = ImageSize.path('spec/test.jpg')
|
27
|
+
|
28
|
+
image_size.format #=> :jpec
|
29
|
+
image_size.width #=> 320
|
30
|
+
image_size.height #=> 240
|
31
|
+
image_size.w #=> 320
|
32
|
+
image_size.h #=> 240
|
33
|
+
image_size.size #=> [320, 240]
|
34
|
+
```
|
35
|
+
|
36
|
+
Or using `IO` object:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
image_size = File.open('spec/test.jpg', 'rb'){ |fh| ImageSize.new(fh) }
|
40
|
+
```
|
41
|
+
|
42
|
+
Any object responding to `read` and `eof?`:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'image_size'
|
46
|
+
|
47
|
+
image_size = ImageSize.new(ARGF)
|
48
|
+
```
|
49
|
+
|
50
|
+
Works with `open-uri` if needed:
|
13
51
|
|
14
|
-
|
52
|
+
```ruby
|
53
|
+
require 'image_size'
|
54
|
+
require 'open-uri'
|
15
55
|
|
16
|
-
|
56
|
+
image_size = URI.parse('http://www.rubycgi.org/image/ruby_gtk_book_title.jpg').open('rb') do |fh|
|
57
|
+
ImageSize.new(fh)
|
58
|
+
end
|
17
59
|
|
18
|
-
|
60
|
+
image_size = open('http://www.rubycgi.org/image/ruby_gtk_book_title.jpg', 'rb') do |fh|
|
61
|
+
ImageSize.new(fh)
|
62
|
+
end
|
63
|
+
```
|
19
64
|
|
20
|
-
|
65
|
+
Note that starting with version `2.0.0` the object given to `ImageSize` will not be rewound before or after use.
|
66
|
+
So rewind if needed before passing to `ImageSize` and/or rewind after passing to `ImageSize` before reading data.
|
21
67
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
68
|
+
```ruby
|
69
|
+
require 'image_size'
|
25
70
|
|
71
|
+
File.open('spec/test.jpg', 'rb') do |fh|
|
72
|
+
image_size = ImageSize.new(fh)
|
26
73
|
|
27
|
-
|
28
|
-
|
74
|
+
fh.rewind
|
75
|
+
data = fh.read
|
76
|
+
end
|
29
77
|
|
30
|
-
|
31
|
-
|
32
|
-
|
78
|
+
File.open('spec/test.jpg', 'rb') do |fh|
|
79
|
+
data = fh.read
|
80
|
+
fh.rewind
|
33
81
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
82
|
+
image_size = ImageSize.new(fh)
|
83
|
+
end
|
84
|
+
```
|
38
85
|
|
39
86
|
## Licence
|
40
87
|
|
@@ -43,3 +90,4 @@ This code is free to use under the terms of the Ruby's licence.
|
|
43
90
|
## Contact
|
44
91
|
|
45
92
|
Original author: "Keisuke Minami": mailto:keisuke@rccn.com
|
93
|
+
Further development by Ivan Kuchin https://github.com/toy/image_size
|
data/image_size.gemspec
CHANGED
@@ -2,14 +2,19 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_size'
|
5
|
-
s.version = '1.
|
5
|
+
s.version = '2.1.0'
|
6
6
|
s.summary = %q{Measure image size using pure Ruby}
|
7
|
-
s.description = %q{Measure following file dimensions: bmp, cur, gif, jpeg,
|
8
|
-
s.homepage = "
|
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
|
+
s.homepage = "https://github.com/toy/#{s.name}"
|
9
9
|
s.authors = ['Keisuke Minami', 'Ivan Kuchin']
|
10
10
|
s.license = 'Ruby'
|
11
11
|
|
12
|
-
s.
|
12
|
+
s.metadata = {
|
13
|
+
'bug_tracker_uri' => "https://github.com/toy/#{s.name}/issues",
|
14
|
+
'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
|
15
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/#{s.name}/#{s.version}",
|
16
|
+
'source_code_uri' => "https://github.com/toy/#{s.name}",
|
17
|
+
}
|
13
18
|
|
14
19
|
s.files = `git ls-files`.split("\n")
|
15
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -17,4 +22,7 @@ Gem::Specification.new do |s|
|
|
17
22
|
s.require_paths = %w[lib]
|
18
23
|
|
19
24
|
s.add_development_dependency 'rspec', '~> 3.0'
|
25
|
+
if RUBY_VERSION >= '2.2'
|
26
|
+
s.add_development_dependency 'rubocop', '~> 0.59', '!= 0.78.0'
|
27
|
+
end
|
20
28
|
end
|
data/lib/image_size.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
#
|
2
|
-
#
|
1
|
+
# encoding: BINARY
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require 'stringio'
|
4
|
-
require 'tempfile'
|
5
5
|
|
6
|
+
# Determine image format and size
|
6
7
|
class ImageSize
|
7
8
|
class FormatError < StandardError; end
|
8
9
|
|
10
|
+
# Array joining with 'x'
|
9
11
|
class Size < Array
|
10
12
|
# join using 'x'
|
11
13
|
def to_s
|
@@ -15,34 +17,26 @@ class ImageSize
|
|
15
17
|
|
16
18
|
class ImageReader # :nodoc:
|
17
19
|
attr_reader :data
|
20
|
+
|
18
21
|
def initialize(data_or_io)
|
19
|
-
@io =
|
20
|
-
when IO, StringIO, Tempfile
|
21
|
-
data_or_io.dup.tap(&:rewind)
|
22
|
-
when String
|
22
|
+
@io = if data_or_io.is_a?(String)
|
23
23
|
StringIO.new(data_or_io)
|
24
|
+
elsif data_or_io.respond_to?(:read) && data_or_io.respond_to?(:eof?)
|
25
|
+
data_or_io
|
24
26
|
else
|
25
|
-
raise ArgumentError
|
27
|
+
raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{data_or_io.class}"
|
26
28
|
end
|
27
|
-
@
|
28
|
-
@data = ''
|
29
|
-
end
|
30
|
-
|
31
|
-
def close
|
32
|
-
@io.rewind
|
33
|
-
@io.close if IO === @io
|
29
|
+
@data = String.new # not frozen
|
34
30
|
end
|
35
31
|
|
36
32
|
CHUNK = 1024
|
37
33
|
def [](offset, length)
|
38
|
-
while offset + length
|
39
|
-
@read
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@data << data
|
45
|
-
end
|
34
|
+
while !@io.eof? && @data.length < offset + length
|
35
|
+
data = @io.read(CHUNK)
|
36
|
+
break unless data
|
37
|
+
|
38
|
+
data.force_encoding(@data.encoding) if data.respond_to?(:encoding)
|
39
|
+
@data << data
|
46
40
|
end
|
47
41
|
@data[offset, length]
|
48
42
|
end
|
@@ -50,7 +44,7 @@ class ImageSize
|
|
50
44
|
|
51
45
|
# Given path to image finds its format, width and height
|
52
46
|
def self.path(path)
|
53
|
-
open(path, 'rb'){ |f| new(f) }
|
47
|
+
File.open(path, 'rb'){ |f| new(f) }
|
54
48
|
end
|
55
49
|
|
56
50
|
# Used for svg
|
@@ -63,13 +57,13 @@ class ImageSize
|
|
63
57
|
@dpi = dpi.to_f
|
64
58
|
end
|
65
59
|
|
66
|
-
# Given image as
|
60
|
+
# Given image as any class responding to read and eof? or data as String, finds its format and dimensions
|
67
61
|
def initialize(data)
|
68
62
|
ir = ImageReader.new(data)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
ir
|
63
|
+
@format = detect_format(ir)
|
64
|
+
return unless @format
|
65
|
+
|
66
|
+
@width, @height = send("size_of_#{@format}", ir)
|
73
67
|
end
|
74
68
|
|
75
69
|
# Image format
|
@@ -77,42 +71,75 @@ class ImageSize
|
|
77
71
|
|
78
72
|
# Image width
|
79
73
|
attr_reader :width
|
80
|
-
|
74
|
+
alias_method :w, :width
|
81
75
|
|
82
76
|
# Image height
|
83
77
|
attr_reader :height
|
84
|
-
|
78
|
+
alias_method :h, :height
|
85
79
|
|
86
80
|
# get image width and height as an array which to_s method returns "#{width}x#{height}"
|
87
81
|
def size
|
88
|
-
if format
|
89
|
-
Size.new([width, height])
|
90
|
-
end
|
82
|
+
Size.new([width, height]) if format
|
91
83
|
end
|
92
84
|
|
93
85
|
private
|
94
86
|
|
95
|
-
SVG_R = /<svg\b([^>]*)
|
87
|
+
SVG_R = /<svg\b([^>]*)>/.freeze
|
88
|
+
XML_R = /<\?xml|<!--/.freeze
|
96
89
|
def detect_format(ir)
|
97
90
|
head = ir[0, 1024]
|
98
91
|
case
|
99
|
-
when head =~
|
100
|
-
when head[0, 8] == "\211PNG\r\n\032\n"
|
101
|
-
when head[0,
|
102
|
-
when head[0, 2] ==
|
103
|
-
when head
|
104
|
-
when head =~
|
105
|
-
when head
|
106
|
-
when head[0, 4]
|
107
|
-
when head =~
|
108
|
-
when head[0, 4] == '8BPS'
|
109
|
-
when head =~
|
110
|
-
when head[SVG_R]
|
111
|
-
|
112
|
-
|
113
|
-
when head[0,
|
114
|
-
when head[0, 4] == "\
|
115
|
-
when head[0,
|
92
|
+
when head[0, 6] =~ /GIF8[79]a/ then :gif
|
93
|
+
when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
|
94
|
+
when head[0, 8] == "\212MNG\r\n\032\n" then :mng
|
95
|
+
when head[0, 2] == "\377\330" then :jpeg
|
96
|
+
when head[0, 2] == 'BM' then :bmp
|
97
|
+
when head[0, 3] =~ /P[1-6]\s|P7\n/ then detect_pnm_type(ir)
|
98
|
+
when head =~ /\#define\s+\S+\s+\d+/ then :xbm
|
99
|
+
when %W[II*\0 MM\0*].include?(head[0, 4]) then :tiff
|
100
|
+
when head =~ %r{/\* XPM \*/} then :xpm
|
101
|
+
when head[0, 4] == '8BPS' then :psd
|
102
|
+
when head[0, 3] =~ /[FC]WS/ then :swf
|
103
|
+
when head =~ SVG_R || (head =~ XML_R && ir[0, 4096][SVG_R]) then :svg
|
104
|
+
when head[0, 2] =~ /\n[\0-\5]/ then :pcx
|
105
|
+
when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
|
106
|
+
when head[0, 4] == "\0\0\1\0" then :ico
|
107
|
+
when head[0, 4] == "\0\0\2\0" then :cur
|
108
|
+
when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
|
109
|
+
when head[0, 4] == "\377O\377Q" then :j2c
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def detect_png_type(ir)
|
114
|
+
offset = 8
|
115
|
+
loop do
|
116
|
+
type = ir[offset + 4, 4]
|
117
|
+
break if ['IDAT', 'IEND', nil].include?(type)
|
118
|
+
return :apng if type == 'acTL'
|
119
|
+
|
120
|
+
length = ir[offset, 4].unpack('N')[0]
|
121
|
+
offset += 8 + length + 4
|
122
|
+
end
|
123
|
+
:png
|
124
|
+
end
|
125
|
+
|
126
|
+
def detect_pnm_type(ir)
|
127
|
+
case ir[0, 2]
|
128
|
+
when 'P1', 'P4' then :pbm
|
129
|
+
when 'P2', 'P5' then :pgm
|
130
|
+
when 'P3', 'P6' then :ppm
|
131
|
+
when 'P7' then :pam
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def detect_jpeg2000_type(ir)
|
136
|
+
return unless ir[16, 4] == 'ftyp'
|
137
|
+
|
138
|
+
# using xl-box would be weird, but doesn't seem to contradict specification
|
139
|
+
skip = ir[12, 4] == "\0\0\0\1" ? 16 : 8
|
140
|
+
case ir[12 + skip, 4]
|
141
|
+
when 'jp2 ' then :jp2
|
142
|
+
when 'jpx ' then :jpx
|
116
143
|
end
|
117
144
|
end
|
118
145
|
|
@@ -120,19 +147,29 @@ private
|
|
120
147
|
ir[6, 4].unpack('vv')
|
121
148
|
end
|
122
149
|
|
150
|
+
def size_of_mng(ir)
|
151
|
+
unless ir[12, 4] == 'MHDR'
|
152
|
+
raise FormatError, 'MHDR not in place for MNG'
|
153
|
+
end
|
154
|
+
|
155
|
+
ir[16, 8].unpack('NN')
|
156
|
+
end
|
157
|
+
|
123
158
|
def size_of_png(ir)
|
124
159
|
unless ir[12, 4] == 'IHDR'
|
125
160
|
raise FormatError, 'IHDR not in place for PNG'
|
126
161
|
end
|
162
|
+
|
127
163
|
ir[16, 8].unpack('NN')
|
128
164
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
165
|
+
alias_method :size_of_apng, :size_of_png
|
166
|
+
|
167
|
+
JPEG_CODE_CHECK = %W[
|
168
|
+
\xC0 \xC1 \xC2 \xC3
|
169
|
+
\xC5 \xC6 \xC7
|
170
|
+
\xC9 \xCA \xCB
|
171
|
+
\xCD \xCE \xCF
|
172
|
+
].freeze
|
136
173
|
def size_of_jpeg(ir)
|
137
174
|
section_marker = "\xFF"
|
138
175
|
offset = 2
|
@@ -141,12 +178,13 @@ private
|
|
141
178
|
offset += 1 until section_marker != ir[offset + 1, 1]
|
142
179
|
raise FormatError, 'EOF in JPEG' if ir[offset, 1].nil?
|
143
180
|
|
144
|
-
|
181
|
+
_marker, code, length = ir[offset, 4].unpack('aan')
|
145
182
|
offset += 4
|
146
183
|
|
147
|
-
if
|
184
|
+
if JPEG_CODE_CHECK.include?(code)
|
148
185
|
return ir[offset + 1, 4].unpack('nn').reverse
|
149
186
|
end
|
187
|
+
|
150
188
|
offset += length - 2
|
151
189
|
end
|
152
190
|
end
|
@@ -170,12 +208,38 @@ private
|
|
170
208
|
header = ir[0, 1024]
|
171
209
|
header.gsub!(/^\#[^\n\r]*/m, '')
|
172
210
|
header =~ /^(P[1-6])\s+?(\d+)\s+?(\d+)/m
|
173
|
-
case $1
|
174
|
-
when 'P1', 'P4' then @format = :pbm
|
175
|
-
when 'P2', 'P5' then @format = :pgm
|
176
|
-
end
|
177
211
|
[$2.to_i, $3.to_i]
|
178
212
|
end
|
213
|
+
alias_method :size_of_pbm, :size_of_ppm
|
214
|
+
alias_method :size_of_pgm, :size_of_ppm
|
215
|
+
|
216
|
+
def size_of_pam(ir)
|
217
|
+
width = height = nil
|
218
|
+
offset = 3
|
219
|
+
loop do
|
220
|
+
if ir[offset, 1] == '#'
|
221
|
+
offset += 1 until ["\n", '', nil].include?(ir[offset, 1])
|
222
|
+
offset += 1
|
223
|
+
else
|
224
|
+
chunk = ir[offset, 32]
|
225
|
+
case chunk
|
226
|
+
when /\AWIDTH (\d+)\n/
|
227
|
+
width = $1.to_i
|
228
|
+
when /\AHEIGHT (\d+)\n/
|
229
|
+
height = $1.to_i
|
230
|
+
when /\AENDHDR\n/
|
231
|
+
break
|
232
|
+
when /\A(?:DEPTH|MAXVAL) \d+\n/, /\ATUPLTYPE \S+\n/
|
233
|
+
# ignore
|
234
|
+
else
|
235
|
+
raise FormatError, "Unexpected data in PAM header: #{chunk.inspect}"
|
236
|
+
end
|
237
|
+
offset += $&.length
|
238
|
+
break if width && height
|
239
|
+
end
|
240
|
+
end
|
241
|
+
[width, height]
|
242
|
+
end
|
179
243
|
|
180
244
|
def size_of_xbm(ir)
|
181
245
|
ir[0, 1024] =~ /^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)/mi
|
@@ -188,6 +252,7 @@ private
|
|
188
252
|
if data.length != length
|
189
253
|
raise FormatError, 'XPM size not found'
|
190
254
|
end
|
255
|
+
|
191
256
|
length += 1024
|
192
257
|
end
|
193
258
|
[$1.to_i, $2.to_i]
|
@@ -198,7 +263,7 @@ private
|
|
198
263
|
end
|
199
264
|
|
200
265
|
def size_of_tiff(ir)
|
201
|
-
endian2b =
|
266
|
+
endian2b = ir[0, 4] == "II*\000" ? 'v' : 'n'
|
202
267
|
endian4b = endian2b.upcase
|
203
268
|
packspec = [nil, 'C', nil, endian2b, endian4b, nil, 'c', nil, endian2b, endian4b]
|
204
269
|
|
@@ -211,17 +276,18 @@ private
|
|
211
276
|
until width && height
|
212
277
|
ifd = ir[offset, 12]
|
213
278
|
raise FormatError, 'Reached end of directory entries in TIFF' if ifd.nil? || offset > num_dirent
|
279
|
+
|
214
280
|
tag, type = ifd.unpack(endian2b * 2)
|
215
281
|
offset += 12
|
216
282
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
283
|
+
next if packspec[type].nil?
|
284
|
+
|
285
|
+
value = ifd[8, 4].unpack(packspec[type])[0]
|
286
|
+
case tag
|
287
|
+
when 0x0100
|
288
|
+
width = value
|
289
|
+
when 0x0101
|
290
|
+
height = value
|
225
291
|
end
|
226
292
|
end
|
227
293
|
[width, height]
|
@@ -236,12 +302,11 @@ private
|
|
236
302
|
value_bit_length = ir[8, 1].unpack('B5').first.to_i(2)
|
237
303
|
bit_length = 5 + value_bit_length * 4
|
238
304
|
rect_bits = ir[8, bit_length / 8 + 1].unpack("B#{bit_length}").first
|
239
|
-
values = rect_bits.unpack(
|
305
|
+
values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
|
240
306
|
x_min, x_max, y_min, y_max = values
|
241
307
|
[(x_max - x_min) / 20, (y_max - y_min) / 20]
|
242
308
|
end
|
243
309
|
|
244
|
-
DPI = 72
|
245
310
|
def size_of_svg(ir)
|
246
311
|
attributes = {}
|
247
312
|
ir.data[SVG_R, 1].scan(/(\S+)=(?:'([^']*)'|"([^"]*)"|([^'"\s]*))/) do |name, v0, v1, v2|
|
@@ -249,18 +314,18 @@ private
|
|
249
314
|
end
|
250
315
|
dpi = self.class.dpi
|
251
316
|
[attributes['width'], attributes['height']].map do |length|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
pixels.round if pixels
|
317
|
+
next unless length
|
318
|
+
|
319
|
+
pixels = case length.downcase.strip[/(?:em|ex|px|in|cm|mm|pt|pc|%)\z/]
|
320
|
+
when 'em', 'ex', '%' then nil
|
321
|
+
when 'in' then length.to_f * dpi
|
322
|
+
when 'cm' then length.to_f * dpi / 2.54
|
323
|
+
when 'mm' then length.to_f * dpi / 25.4
|
324
|
+
when 'pt' then length.to_f * dpi / 72
|
325
|
+
when 'pc' then length.to_f * dpi / 6
|
326
|
+
else length.to_f
|
263
327
|
end
|
328
|
+
pixels.round if pixels
|
264
329
|
end
|
265
330
|
end
|
266
331
|
|
@@ -268,4 +333,57 @@ private
|
|
268
333
|
ir[6, 2].unpack('CC').map{ |v| v.zero? ? 256 : v }
|
269
334
|
end
|
270
335
|
alias_method :size_of_cur, :size_of_ico
|
336
|
+
|
337
|
+
def size_of_webp(ir)
|
338
|
+
case ir[12, 4]
|
339
|
+
when 'VP8 '
|
340
|
+
ir[26, 4].unpack('vv').map{ |v| v & 0x3fff }
|
341
|
+
when 'VP8L'
|
342
|
+
n = ir[21, 4].unpack('V')[0]
|
343
|
+
[(n & 0x3fff) + 1, (n >> 14 & 0x3fff) + 1]
|
344
|
+
when 'VP8X'
|
345
|
+
w16, w8, h16, h8 = ir[24, 6].unpack('vCvC')
|
346
|
+
[(w16 | w8 << 16) + 1, (h16 | h8 << 16) + 1]
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def size_of_jp2(ir)
|
351
|
+
offset = 12
|
352
|
+
stop = nil
|
353
|
+
in_header = false
|
354
|
+
loop do
|
355
|
+
break if stop && offset >= stop
|
356
|
+
break if ir[offset, 4] == '' || ir[offset, 4].nil?
|
357
|
+
|
358
|
+
size = ir[offset, 4].unpack('N')[0]
|
359
|
+
type = ir[offset + 4, 4]
|
360
|
+
|
361
|
+
data_offset = 8
|
362
|
+
case size
|
363
|
+
when 1
|
364
|
+
size = ir[offset, 8].unpack('Q>')[0]
|
365
|
+
data_offset = 16
|
366
|
+
raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
|
367
|
+
when 2..7
|
368
|
+
raise FormatError, "Reserved box size #{size}"
|
369
|
+
end
|
370
|
+
|
371
|
+
if type == 'jp2h'
|
372
|
+
stop = offset + size unless size.zero?
|
373
|
+
offset += data_offset
|
374
|
+
in_header = true
|
375
|
+
elsif in_header && type == 'ihdr'
|
376
|
+
return ir[offset + data_offset, 8].unpack('NN').reverse
|
377
|
+
else
|
378
|
+
break if size.zero? # box to the end of file
|
379
|
+
|
380
|
+
offset += size
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
alias_method :size_of_jpx, :size_of_jp2
|
385
|
+
|
386
|
+
def size_of_j2c(ir)
|
387
|
+
ir[8, 8].unpack('NN')
|
388
|
+
end
|
271
389
|
end
|