image_size 1.4.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://img.shields.io/gem/v/image_size.svg?style=flat)](https://rubygems.org/gems/image_size)
|
2
|
+
[![Build Status](https://img.shields.io/travis/toy/image_size/master.svg?style=flat)](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
|