image_size 3.2.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f086d46fca698649fed0a23adca9dc67e1fa3317d9f3c209f1fe7f07fcd2cb6b
4
- data.tar.gz: ff8f5243aadd37691eb7dabaa7e4b0d3b6fb212aba286a10d86165dc174eabff
3
+ metadata.gz: d88e4be6d69bedc0f976c9b73544a20ac08ab66d6b44ce1061d1374f9d2a9dc8
4
+ data.tar.gz: 5b2986943b8bbeb339d0c31d0a98c5847a1a86141d5e754adaf0bd01e3d8d60f
5
5
  SHA512:
6
- metadata.gz: 6ff23aa12846de6eaeb9a8ae02cde4a152f891e863056cf901732ef1c6cd1a580befab1cb62037feed500906901018cf8cdb64b63dd6538c07f0b84c5e8ce80e
7
- data.tar.gz: fe6d488afbac72d4a8c16e3d1457c9ae3acf82b87ac9bd6df34e4077f0a0166c47862eac796f71e34084bb7c1314e49f79963eba41ad73de935232b2f2c35bd1
6
+ metadata.gz: 1b75df7c37cbcca0886eade3e47b082cde0468331c954d0605130cba564bb7718d777628599fea7332e2ae1ef9f15b80ce77072352a109dadbf9749fea049572
7
+ data.tar.gz: ffc57b47280776fc7be3cb2866699f32f90a2e036b2dca703ac4262aa77750032ffb54700437958ba7b4948c934d5ea6e41f2acad4e7e71ddec84a98f18edabf
@@ -10,9 +10,6 @@ jobs:
10
10
  strategy:
11
11
  matrix:
12
12
  ruby:
13
- - '2.0'
14
- - '2.1'
15
- - '2.2'
16
13
  - '2.3'
17
14
  - '2.4'
18
15
  - '2.5'
@@ -20,12 +17,13 @@ jobs:
20
17
  - '2.7'
21
18
  - '3.0'
22
19
  - '3.1'
23
- - jruby-9.1
24
- - jruby-9.2
20
+ - '3.2'
21
+ - '3.3'
25
22
  - jruby-9.3
23
+ - jruby-9.4
26
24
  fail-fast: false
27
25
  steps:
28
- - uses: actions/checkout@v2
26
+ - uses: actions/checkout@v3
29
27
  - uses: ruby/setup-ruby@v1
30
28
  with:
31
29
  ruby-version: "${{ matrix.ruby }}"
@@ -37,11 +35,13 @@ jobs:
37
35
  strategy:
38
36
  matrix:
39
37
  container:
40
- - rspec/ci:1.8.7
41
38
  - rspec/ci:1.9.3
39
+ - ruby:2.0
40
+ - ruby:2.1
41
+ - ruby:2.2
42
42
  fail-fast: false
43
43
  steps:
44
- - uses: actions/checkout@v2
44
+ - uses: actions/checkout@v3
45
45
  - run: bundle install
46
46
  - run: bundle exec rspec --format documentation
47
47
  windows:
@@ -53,9 +53,11 @@ jobs:
53
53
  - '2.7'
54
54
  - '3.0'
55
55
  - '3.1'
56
+ - '3.2'
57
+ - '3.3'
56
58
  fail-fast: false
57
59
  steps:
58
- - uses: actions/checkout@v2
60
+ - uses: actions/checkout@v3
59
61
  - uses: ruby/setup-ruby@v1
60
62
  with:
61
63
  ruby-version: "${{ matrix.ruby }}"
@@ -8,9 +8,9 @@ jobs:
8
8
  rubocop:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v2
11
+ - uses: actions/checkout@v3
12
12
  - uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: '3.1'
14
+ ruby-version: '3'
15
15
  bundler-cache: true
16
16
  - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -15,9 +15,6 @@ Layout/CaseIndentation:
15
15
  Layout/EndAlignment:
16
16
  EnforcedStyleAlignWith: variable
17
17
 
18
- Layout/FirstHashElementIndentation:
19
- EnforcedStyle: consistent
20
-
21
18
  Layout/LineLength:
22
19
  Max: 120
23
20
 
@@ -25,9 +22,6 @@ Layout/SpaceBeforeBlockBraces:
25
22
  EnforcedStyle: no_space
26
23
  EnforcedStyleForEmptyBraces: no_space
27
24
 
28
- Lint/PercentStringArray: # broken in rubocop 0.55.0
29
- Enabled: false
30
-
31
25
  Metrics/BlockLength:
32
26
  Exclude:
33
27
  - 'spec/**/*.rb'
@@ -41,42 +35,18 @@ Metrics/MethodLength:
41
35
  Naming/MethodParameterName:
42
36
  Enabled: false
43
37
 
44
- Style/AccessorGrouping:
45
- Enabled: false
46
-
47
38
  Style/Alias:
48
39
  EnforcedStyle: prefer_alias_method
49
40
 
50
- Style/EmptyCaseCondition:
51
- Enabled: false
52
-
53
- Style/Encoding:
54
- Enabled: false
55
-
56
- Style/FileRead:
41
+ Style/ArgumentsForwarding:
57
42
  Enabled: false
58
43
 
59
- Style/HashEachMethods:
60
- Enabled: true
61
-
62
- Style/HashSyntax:
63
- EnforcedStyle: hash_rockets
64
-
65
- Style/HashTransformKeys:
66
- Enabled: false
67
-
68
- Style/HashTransformValues:
69
- Enabled: false
70
-
71
- Style/IfUnlessModifier:
44
+ Style/EmptyCaseCondition:
72
45
  Enabled: false
73
46
 
74
47
  Style/NumericPredicate:
75
48
  Enabled: false
76
49
 
77
- Style/ParallelAssignment:
78
- Enabled: false
79
-
80
50
  Style/SafeNavigation:
81
51
  Enabled: false
82
52
 
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## v3.4.0 (2024-01-16)
6
+
7
+ * Provide access to media types using media_type and media_types methods [#22](https://github.com/toy/image_size/issues/22) [@toy](https://github.com/toy)
8
+ * Allow fetching from HTTP server by requiring image_size/uri [@toy](https://github.com/toy)
9
+ * Fix for ArgumentError when requiring only image_size/uri_reader (without image_size) [@toy](https://github.com/toy)
10
+ * Require ruby 1.9.3 [@toy](https://github.com/toy)
11
+
12
+ ## v3.3.0 (2023-05-30)
13
+
14
+ * Support `HEIF` (`HEIC` and `AVIF`) images [#19](https://github.com/toy/image_size/issues/19) [@toy](https://github.com/toy)
15
+ * Fix handling `JPEG 2000` 64 bit size boxes [@toy](https://github.com/toy)
16
+
5
17
  ## v3.2.0 (2022-11-03)
6
18
 
7
19
  * Support `EMF` images [#21](https://github.com/toy/image_size/pull/21) [@opoudjis](https://github.com/opoudjis)
data/README.markdown CHANGED
@@ -1,11 +1,11 @@
1
1
  [![Gem Version](https://img.shields.io/gem/v/image_size?logo=rubygems)](https://rubygems.org/gems/image_size)
2
- [![Build Status](https://img.shields.io/github/workflow/status/toy/image_size/check/master?logo=github)](https://github.com/toy/image_size/actions/workflows/check.yml)
3
- [![Rubocop](https://img.shields.io/github/workflow/status/toy/image_size/rubocop/master?label=rubocop&logo=rubocop)](https://github.com/toy/image_size/actions/workflows/rubocop.yml)
2
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/toy/image_size/check.yml?logo=github)](https://github.com/toy/image_size/actions/workflows/check.yml)
3
+ [![Rubocop](https://img.shields.io/github/actions/workflow/status/toy/image_size/rubocop.yml?label=rubocop&logo=rubocop)](https://github.com/toy/image_size/actions/workflows/rubocop.yml)
4
4
 
5
5
  # image_size
6
6
 
7
- Measure image size using pure Ruby.
8
- Formats: `apng`, `bmp`, `cur`, `emf`, `gif`, `ico`, `j2c`, `jp2`, `jpeg`, `jpx`, `mng`, `pam`, `pbm`, `pcx`, `pgm`, `png`, `ppm`, `psd`, `svg`, `swf`, `tiff`, `webp`, `xbm`, `xpm`.
7
+ Measure image size/dimensions using pure Ruby.
8
+ Formats: `apng`, `avif`, `bmp`, `cur`, `emf`, `gif`, `heic`, `heif`, `ico`, `j2c`, `jp2`, `jpeg`, `jpx`, `mng`, `pam`, `pbm`, `pcx`, `pgm`, `png`, `ppm`, `psd`, `svg`, `swf`, `tiff`, `webp`, `xbm`, `xpm`.
9
9
 
10
10
  ## Installation
11
11
 
@@ -26,7 +26,7 @@ gem 'image_size', '~> 3.0'
26
26
  ```ruby
27
27
  image_size = ImageSize.path('spec/images/jpeg/436x429.jpeg')
28
28
 
29
- image_size.format #=> :jpec
29
+ image_size.format #=> :jpeg
30
30
  image_size.width #=> 436
31
31
  image_size.height #=> 429
32
32
  image_size.w #=> 436
@@ -38,6 +38,8 @@ image_size.size.width #=> 436
38
38
  image_size.size.height #=> 429
39
39
  image_size.size.w #=> 436
40
40
  image_size.size.h #=> 429
41
+ image_size.media_type #=> "image/jpeg"
42
+ image_size.media_types #=> ["image/jpeg"]
41
43
  ```
42
44
 
43
45
  Or using `IO` object:
@@ -96,8 +98,7 @@ If server recognises Range header, only needed chunks will be fetched even for T
96
98
  of data will be fetched, in most cases first few kilobytes (TIFF images is an exception).
97
99
 
98
100
  ```ruby
99
- require 'image_size'
100
- require 'image_size/uri_reader'
101
+ require 'image_size/uri'
101
102
 
102
103
  url = 'http://upload.wikimedia.org/wikipedia/commons/b/b4/Mardin_1350660_1350692_33_images.jpg'
103
104
  p ImageSize.url(url).size
@@ -144,4 +145,4 @@ puts Benchmark.measure{ p ImageSize.url(url).size }
144
145
  This code is free to use under the terms of the [Ruby's licence](LICENSE.txt).
145
146
 
146
147
  Original author: Keisuke Minami <keisuke@rccn.com>.\
147
- Further development 2010-2022 Ivan Kuchin https://github.com/toy/image_size
148
+ Further development 2010-2024 Ivan Kuchin https://github.com/toy/image_size
data/image_size.gemspec CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'image_size'
5
- s.version = '3.2.0'
6
- s.summary = %q{Measure image size using pure Ruby}
7
- s.description = %q{Measure following file dimensions: apng, bmp, cur, emf, gif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm, xpm}
5
+ s.version = '3.4.0'
6
+ s.summary = %q{Measure image size/dimensions using pure Ruby}
7
+ s.description = %q{Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic, heif, 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}"
9
9
  s.authors = ['Keisuke Minami', 'Ivan Kuchin']
10
10
  s.license = 'Ruby'
11
11
 
12
+ s.required_ruby_version = '>= 1.9.3'
13
+
12
14
  s.metadata = {
13
15
  'bug_tracker_uri' => "https://github.com/toy/#{s.name}/issues",
14
16
  'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ImageSize
4
+ class FormatError < StandardError; end
5
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'image_size/format_error'
4
+
5
+ require 'set'
6
+
7
+ class ImageSize
8
+ class ISOBMFF # :nodoc:
9
+ class Box # :nodoc:
10
+ attr_reader :type, :offset, :size, :relative_data_offset, :index
11
+
12
+ def initialize(attributes)
13
+ @type = attributes.fetch(:type)
14
+ @offset = attributes.fetch(:offset)
15
+ @size = attributes.fetch(:size) == 0 ? nil : attributes[:size]
16
+ @relative_data_offset = attributes.fetch(:relative_data_offset)
17
+ @index = attributes.fetch(:index)
18
+ end
19
+
20
+ def data_offset
21
+ offset + relative_data_offset
22
+ end
23
+
24
+ def data_size
25
+ size ? size - relative_data_offset : nil
26
+ end
27
+ end
28
+
29
+ class FullBox < Box # :nodoc:
30
+ attr_reader :version, :flags
31
+
32
+ def initialize(attributes)
33
+ super
34
+ @version = attributes.fetch(:version)
35
+ @flags = attributes.fetch(:flags)
36
+ end
37
+ end
38
+
39
+ S64_OVERFLOW = 1 << 63
40
+
41
+ def initialize(options = {})
42
+ @full = options.fetch(:full, []).to_set
43
+ @last = options.fetch(:last, []).to_set
44
+ @recurse = options.fetch(:recurse, []).to_set
45
+ end
46
+
47
+ def walk(reader, offset = 0, length = nil)
48
+ max_offset = length ? offset + length : S64_OVERFLOW
49
+ index = 1
50
+ while offset < max_offset && !['', nil].include?(reader[offset, 4])
51
+ size = reader.unpack1(offset, 4, 'N')
52
+ type = reader.fetch(offset + 4, 4)
53
+ relative_data_offset = 8
54
+
55
+ case size
56
+ when 1
57
+ size = reader.unpack1(offset + 8, 8, 'Q>')
58
+ relative_data_offset += 8
59
+ raise FormatError, "Unexpected ISOBMFF xl-box size #{size}" if size < 16
60
+ when 2..7
61
+ raise FormatError, "Reserved ISOBMFF box size #{size}"
62
+ end
63
+
64
+ attributes = {
65
+ type: type,
66
+ offset: offset,
67
+ size: size,
68
+ relative_data_offset: relative_data_offset,
69
+ index: index,
70
+ }
71
+
72
+ if @full.include?(type)
73
+ version_n_flags = reader.unpack1(offset + relative_data_offset, 4, 'N')
74
+ attributes[:version] = version_n_flags >> 24
75
+ attributes[:flags] = version_n_flags & 0xffffff
76
+
77
+ attributes[:relative_data_offset] += 4
78
+
79
+ yield FullBox.new(attributes)
80
+ else
81
+ yield Box.new(attributes)
82
+ end
83
+
84
+ break if size == 0 || @last.include?(type)
85
+
86
+ index += 1
87
+ offset += size
88
+ end
89
+ end
90
+
91
+ def recurse(reader, offset = 0, length = nil, &block)
92
+ walk(reader, offset, length) do |box|
93
+ yield box
94
+
95
+ recurse(reader, box.data_offset, box.data_size, &block) if @recurse.include?(box.type)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ImageSize
4
+ MEDIA_TYPES = {
5
+ apng: %w[image/apng image/vnd.mozilla.apng],
6
+ avif: %w[image/avif],
7
+ bmp: %w[image/bmp],
8
+ cur: %w[image/vnd.microsoft.icon],
9
+ emf: %w[image/emf],
10
+ gif: %w[image/gif],
11
+ heic: %w[image/heic image/heif],
12
+ ico: %w[image/x-icon image/vnd.microsoft.icon],
13
+ j2c: %w[image/j2c],
14
+ jp2: %w[image/jp2],
15
+ jpeg: %w[image/jpeg],
16
+ jpx: %w[image/jpx],
17
+ mng: %w[video/x-mng image/x-mng],
18
+ pam: %w[image/x-portable-arbitrarymap],
19
+ pbm: %w[image/x-portable-bitmap image/x-portable-anymap],
20
+ pcx: %w[image/x-pcx image/vnd.zbrush.pcx],
21
+ pgm: %w[image/x-portable-graymap image/x-portable-anymap],
22
+ png: %w[image/png],
23
+ ppm: %w[image/x-portable-pixmap image/x-portable-anymap],
24
+ psd: %w[image/vnd.adobe.photoshop],
25
+ svg: %w[image/svg+xml],
26
+ swf: %w[application/x-shockwave-flash application/vnd.adobe.flash.movie],
27
+ tiff: %w[image/tiff],
28
+ webp: %w[image/webp],
29
+ xbm: %w[image/x-xbitmap],
30
+ xpm: %w[image/x-xpixmap],
31
+ }.freeze
32
+ end
@@ -5,6 +5,19 @@ require 'stringio'
5
5
 
6
6
  class ImageSize
7
7
  module Reader # :nodoc:
8
+ class Stream # :nodoc:
9
+ def initialize(reader, offset)
10
+ @reader = reader
11
+ @offset = offset
12
+ end
13
+
14
+ def unpack1(length, format)
15
+ result = @reader.unpack1(@offset, length, format)
16
+ @offset += length
17
+ result
18
+ end
19
+ end
20
+
8
21
  class << self
9
22
  def open(input)
10
23
  case
@@ -60,5 +73,9 @@ class ImageSize
60
73
  fetch(offset, length).unpack(format)[0]
61
74
  end
62
75
  end
76
+
77
+ def stream(offset)
78
+ Stream.new(self, offset)
79
+ end
63
80
  end
64
81
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'image_size/uri_reader'
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'image_size'
3
4
  require 'image_size/reader'
4
5
  require 'image_size/chunky_reader'
5
6
 
data/lib/image_size.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # encoding: BINARY
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'image_size/isobmff'
5
+ require 'image_size/format_error'
6
+ require 'image_size/media_types'
4
7
  require 'image_size/reader'
5
8
  require 'image_size/seekable_io_reader'
6
9
  require 'image_size/stream_io_reader'
@@ -8,8 +11,6 @@ require 'image_size/string_reader'
8
11
 
9
12
  # Determine image format and size
10
13
  class ImageSize
11
- class FormatError < StandardError; end
12
-
13
14
  # Array joining with 'x'
14
15
  class Size < Array
15
16
  # join using 'x'
@@ -69,6 +70,19 @@ class ImageSize
69
70
  Size.new([width, height]) if format
70
71
  end
71
72
 
73
+ # Media type (formerly known as a MIME type)
74
+ def media_type
75
+ MEDIA_TYPES.fetch(format, []).first
76
+ end
77
+
78
+ # All media types:
79
+ # * commonly used and official like for apng and ico
80
+ # * main and compatible like for heic and pnm (pbm, pgm, ppm)
81
+ # * multiple unregistered like for mng
82
+ def media_types
83
+ MEDIA_TYPES.fetch(format, [])
84
+ end
85
+
72
86
  private
73
87
 
74
88
  SVG_R = /<svg\b([^>]*)>/.freeze
@@ -77,25 +91,27 @@ private
77
91
  head = ir[0, 1024]
78
92
  case
79
93
  when head.nil? || head.empty? then nil
80
- when head[0, 6] =~ /GIF8[79]a/ then :gif
94
+ when head[0, 6] =~ /\AGIF8[79]a\z/ then :gif
81
95
  when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
82
96
  when head[0, 8] == "\212MNG\r\n\032\n" then :mng
83
97
  when head[0, 2] == "\377\330" then :jpeg
84
98
  when head[0, 2] == 'BM' then :bmp
85
- when head[0, 3] =~ /P[1-6]\s|P7\n/ then detect_pnm_type(ir)
99
+ when head[0, 3] =~ /\AP([1-6]\s|7\n)\z/ then detect_pnm_type(ir)
86
100
  when head =~ /\#define\s+\S+\s+\d+/ then :xbm
87
101
  when %W[II*\0 MM\0*].include?(head[0, 4]) then :tiff
88
102
  when head =~ %r{/\* XPM \*/} then :xpm
89
103
  when head[0, 4] == '8BPS' then :psd
90
- when head[0, 3] =~ /[FC]WS/ then :swf
104
+ when head[0, 3] =~ /\A[FC]WS\z/ then :swf
91
105
  when head =~ SVG_R || (head =~ XML_R && ir[0, 4096] =~ SVG_R) then :svg
92
106
  when head[0, 2] =~ /\n[\0-\5]/ then :pcx
93
- when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
107
+ when head[0, 12] =~ /\ARIFF(?m:....)WEBP\z/ then :webp
94
108
  when head[0, 4] == "\0\0\1\0" then :ico
95
109
  when head[0, 4] == "\0\0\2\0" then :cur
96
110
  when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
97
111
  when head[0, 4] == "\377O\377Q" then :j2c
98
112
  when head[0, 4] == "\1\0\0\0" && head[40, 4] == ' EMF' then :emf
113
+ when head[4, 8] =~ /\Aftypavi[fs]\z/ then :avif
114
+ when head[4, 8] =~ /\Aftyp(hei[cs]|mif[12]|msf1)\z/ then :heic
99
115
  end
100
116
  end
101
117
 
@@ -107,7 +123,7 @@ private
107
123
  return :apng if type == 'acTL'
108
124
 
109
125
  length = ir.unpack1(offset, 4, 'N')
110
- offset += 8 + length + 4
126
+ offset += length + 8 + 4
111
127
  end
112
128
  :png
113
129
  end
@@ -126,7 +142,7 @@ private
126
142
 
127
143
  # using xl-box would be weird, but doesn't seem to contradict specification
128
144
  skip = ir[12, 4] == "\0\0\0\1" ? 16 : 8
129
- case ir[12 + skip, 4]
145
+ case ir[skip + 12, 4]
130
146
  when 'jp2 ' then :jp2
131
147
  when 'jpx ' then :jpx
132
148
  end
@@ -137,17 +153,13 @@ private
137
153
  end
138
154
 
139
155
  def size_of_mng(ir)
140
- unless ir[12, 4] == 'MHDR'
141
- raise FormatError, 'MHDR not in place for MNG'
142
- end
156
+ raise FormatError, 'MHDR not in place for MNG' unless ir[12, 4] == 'MHDR'
143
157
 
144
158
  ir.unpack(16, 8, 'NN')
145
159
  end
146
160
 
147
161
  def size_of_png(ir)
148
- unless ir[12, 4] == 'IHDR'
149
- raise FormatError, 'IHDR not in place for PNG'
150
- end
162
+ raise FormatError, 'IHDR not in place for PNG' unless ir[12, 4] == 'IHDR'
151
163
 
152
164
  ir.unpack(16, 8, 'NN')
153
165
  end
@@ -165,14 +177,12 @@ private
165
177
  loop do
166
178
  offset += 1 until [nil, section_marker].include? ir[offset, 1]
167
179
  offset += 1 until section_marker != ir[offset + 1, 1]
168
- raise FormatError, 'EOF in JPEG' if ir[offset, 1].nil?
180
+ raise FormatError, 'EOF in JPEG' unless ir[offset, 1]
169
181
 
170
182
  code, length = ir.unpack(offset, 4, 'xCn')
171
183
  offset += 4
172
184
 
173
- if JPEG_CODE_CHECK.include?(code)
174
- return ir.unpack(offset + 1, 4, 'nn').reverse
175
- end
185
+ return ir.unpack(offset + 1, 4, 'nn').reverse if JPEG_CODE_CHECK.include?(code)
176
186
 
177
187
  offset += length - 2
178
188
  end
@@ -237,9 +247,7 @@ private
237
247
  def size_of_xpm(ir)
238
248
  length = 1024
239
249
  until (data = ir[0, length]) =~ /"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*"/m
240
- if data.length != length
241
- raise FormatError, 'XPM size not found'
242
- end
250
+ raise FormatError, 'XPM size not found' if data.length != length
243
251
 
244
252
  length += 1024
245
253
  end
@@ -268,7 +276,7 @@ private
268
276
  tag, type = ifd.unpack(endian2b * 2)
269
277
  offset += 12
270
278
 
271
- next if packspec[type].nil?
279
+ next unless packspec[type]
272
280
 
273
281
  value = ifd[8, 4].unpack(packspec[type])[0]
274
282
  case tag
@@ -288,7 +296,7 @@ private
288
296
 
289
297
  def size_of_swf(ir)
290
298
  value_bit_length = ir.unpack1(8, 1, 'B5').to_i(2)
291
- bit_length = 5 + (value_bit_length * 4)
299
+ bit_length = (value_bit_length * 4) + 5
292
300
  rect_bits = ir.unpack1(8, (bit_length / 8) + 1, "B#{bit_length}")
293
301
  values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
294
302
  x_min, x_max, y_min, y_max = values
@@ -336,38 +344,13 @@ private
336
344
  end
337
345
  end
338
346
 
347
+ JP2_WALKER = ImageSize::ISOBMFF.new(
348
+ recurse: %w[jp2h],
349
+ last: %w[jp2h]
350
+ )
339
351
  def size_of_jp2(ir)
340
- offset = 12
341
- stop = nil
342
- in_header = false
343
- loop do
344
- break if stop && offset >= stop
345
- break if ir[offset, 4] == '' || ir[offset, 4].nil?
346
-
347
- size = ir.unpack1(offset, 4, 'N')
348
- type = ir.fetch(offset + 4, 4)
349
-
350
- data_offset = 8
351
- case size
352
- when 1
353
- size = ir.unpack1(offset, 8, 'Q>')
354
- data_offset = 16
355
- raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
356
- when 2..7
357
- raise FormatError, "Reserved box size #{size}"
358
- end
359
-
360
- if type == 'jp2h'
361
- stop = offset + size unless size.zero?
362
- offset += data_offset
363
- in_header = true
364
- elsif in_header && type == 'ihdr'
365
- return ir.unpack(offset + data_offset, 8, 'NN').reverse
366
- else
367
- break if size.zero? # box to the end of file
368
-
369
- offset += size
370
- end
352
+ JP2_WALKER.recurse(ir) do |box|
353
+ return ir.unpack(box.data_offset, 8, 'NN').reverse if box.type == 'ihdr'
371
354
  end
372
355
  end
373
356
  alias_method :size_of_jpx, :size_of_jp2
@@ -391,4 +374,66 @@ private
391
374
  (n.to_f * dpi / 2540).round
392
375
  end
393
376
  end
377
+
378
+ HEIF_WALKER = ImageSize::ISOBMFF.new(
379
+ recurse: %w[meta iprp ipco],
380
+ full: %w[meta hdlr pitm ipma ispe],
381
+ last: %w[meta]
382
+ )
383
+ def size_of_heif(ir)
384
+ pitm = nil
385
+ ipma = nil
386
+ ispes = {}
387
+ claps = {}
388
+ irots = {}
389
+
390
+ HEIF_WALKER.recurse(ir) do |box, _path|
391
+ case box.type
392
+ when 'hdlr'
393
+ raise FormatError, "hdlr box too small (#{box.data_size})" if box.data_size < 8
394
+
395
+ return nil unless ir[box.data_offset + 4, 4] == 'pict'
396
+ when 'pitm'
397
+ raise FormatError, 'second pitm box encountered' if pitm
398
+
399
+ pitm = box.version == 0 ? ir.unpack1(box.data_offset, 2, 'n') : ir.unpack1(box.data_offset, 4, 'N')
400
+ when 'ipma'
401
+ stream = ir.stream(box.data_offset)
402
+
403
+ property_index_16b = (box.flags & 1) == 1
404
+
405
+ ipma ||= {}
406
+ stream.unpack1(4, 'N').times do
407
+ item_id = box.version == 0 ? stream.unpack1(2, 'n') : stream.unpack1(4, 'N')
408
+ ipma[item_id] ||= Array.new(stream.unpack1(1, 'C')) do
409
+ property_index_16b ? stream.unpack1(2, 'n') & 0x7fff : stream.unpack1(1, 'C') & 0x7f
410
+ end
411
+ end
412
+ when 'ispe'
413
+ ispes[box.index] ||= ir.unpack(box.data_offset, 8, 'NN')
414
+ when 'clap'
415
+ width_n, width_d, height_n, height_d = ir.unpack(box.data_offset, 16, 'N4')
416
+ claps[box.index] ||= [Rational(width_n, width_d).round, Rational(height_n, height_d).round]
417
+ when 'irot'
418
+ irots[box.index] ||= ir.unpack1(box.data_offset, 1, 'C') & 0b11
419
+ end
420
+ end
421
+
422
+ return unless ipma
423
+
424
+ properties = ipma[pitm || ipma.keys.min]
425
+ return unless properties
426
+
427
+ dimensions = claps.values_at(*properties).compact.first || ispes.values_at(*properties).compact.first
428
+ return unless dimensions
429
+
430
+ irot = irots.values_at(*properties).compact.first
431
+ if irot && irot.odd?
432
+ dimensions.reverse
433
+ else
434
+ dimensions
435
+ end
436
+ end
437
+ alias_method :size_of_avif, :size_of_heif
438
+ alias_method :size_of_heic, :size_of_heif
394
439
  end
@@ -29,7 +29,7 @@ describe ImageSize::ChunkyReader do
29
29
  {
30
30
  'empty string' => '',
31
31
  'a bit of data' => 'foo bar baz',
32
- 'a lot of data' => File.open('GPL', 'rb', &:read),
32
+ 'a lot of data' => File.binread('GPL'),
33
33
  }.each do |data_description, data|
34
34
  {
35
35
  'default' => test_reader.new(data),
@@ -0,0 +1,352 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require 'stringio'
5
+
6
+ require 'image_size/string_reader'
7
+ require 'image_size/isobmff'
8
+
9
+ describe ImageSize::ISOBMFF do
10
+ boxes = Class.new do
11
+ def self.build(&block)
12
+ builder = new
13
+ builder.instance_eval(&block)
14
+ builder.string
15
+ end
16
+
17
+ def initialize
18
+ @io = StringIO.new
19
+ end
20
+
21
+ def box(type, size, size64 = nil)
22
+ @io << [size, type, size64].pack("Na4#{'Q>' if size64}")
23
+ yield if block_given?
24
+ end
25
+
26
+ def data(content)
27
+ @io << content
28
+ end
29
+
30
+ def string
31
+ @io.string
32
+ end
33
+ end
34
+
35
+ let(:instance){ described_class.new(options) }
36
+
37
+ let(:string_reader){ ImageSize::StringReader.new(data) }
38
+
39
+ describe '#walk' do
40
+ let(:options){ {} }
41
+
42
+ def is_expected
43
+ expect{ |b| instance.walk(string_reader, &b) }
44
+ end
45
+
46
+ describe 'for multiple boxes' do
47
+ let(:data) do
48
+ boxes.build do
49
+ box('abcd', 8 + 42){ data 'x' * 42 }
50
+ box('efgh', 8 + 10)
51
+ end
52
+ end
53
+
54
+ it do
55
+ is_expected.to yield_successive_args(
56
+ having_attributes(type: 'abcd', data_offset: 8, data_size: 42),
57
+ having_attributes(type: 'efgh', data_offset: 58, data_size: 10)
58
+ )
59
+ end
60
+ end
61
+
62
+ describe 'for empty data' do
63
+ let(:data){ '' }
64
+
65
+ it{ is_expected.not_to yield_control }
66
+ end
67
+
68
+ describe 'for not enough data' do
69
+ let(:data){ 'test' }
70
+
71
+ it{ is_expected.to raise_error ImageSize::FormatError }
72
+ end
73
+
74
+ describe 'for a box without content' do
75
+ let(:data){ boxes.build{ box('test', 8) } }
76
+
77
+ it{ is_expected.to yield_successive_args(having_attributes(type: 'test', data_offset: 8, data_size: 0)) }
78
+ end
79
+
80
+ describe 'for a box with content' do
81
+ let(:data){ boxes.build{ box('test', 8 + 42) } }
82
+
83
+ it do
84
+ is_expected.to yield_successive_args(having_attributes(type: 'test', data_offset: 8, data_size: 42))
85
+ end
86
+ end
87
+
88
+ describe 'for not enough data in second box' do
89
+ let(:data) do
90
+ boxes.build do
91
+ box('test', 8)
92
+ data 'test'
93
+ end
94
+ end
95
+
96
+ it{ is_expected.to yield_control.and raise_error ImageSize::FormatError }
97
+ end
98
+
99
+ describe 'for size-less box' do
100
+ let(:data){ boxes.build{ box('test', 0) } }
101
+
102
+ it do
103
+ is_expected.to yield_successive_args(having_attributes(type: 'test', data_offset: 8, data_size: nil))
104
+ end
105
+ end
106
+
107
+ (2..7).each do |size|
108
+ describe "for wrong small box size #{size}" do
109
+ let(:data){ boxes.build{ box('test', size) } }
110
+
111
+ it{ is_expected.to raise_error ImageSize::FormatError }
112
+ end
113
+ end
114
+
115
+ describe 'for a big box without content' do
116
+ let(:data){ boxes.build{ box('test', 1, 16) } }
117
+
118
+ it do
119
+ is_expected.to yield_successive_args(having_attributes(type: 'test', data_offset: 16, data_size: 0))
120
+ end
121
+ end
122
+
123
+ describe 'for a big box with content' do
124
+ let(:data){ boxes.build{ box('test', 1, 16 + 42) } }
125
+
126
+ it do
127
+ is_expected.to yield_successive_args(having_attributes(type: 'test', data_offset: 16, data_size: 42))
128
+ end
129
+ end
130
+
131
+ describe 'for a full box' do
132
+ let(:options){ { full: %w[test] } }
133
+
134
+ let(:data) do
135
+ boxes.build do
136
+ box('test', 8 + 42)
137
+ data 'abcd'
138
+ end
139
+ end
140
+
141
+ it do
142
+ is_expected.to yield_successive_args(
143
+ having_attributes(
144
+ type: 'test',
145
+ data_offset: 12,
146
+ data_size: 38,
147
+ version: 0x61,
148
+ flags: 0x626364
149
+ )
150
+ )
151
+ end
152
+ end
153
+
154
+ describe 'for a big full box' do
155
+ let(:options){ { full: %w[test] } }
156
+
157
+ let(:data) do
158
+ boxes.build do
159
+ box('test', 1, 16 + 42)
160
+ data 'abcd'
161
+ end
162
+ end
163
+
164
+ it do
165
+ is_expected.to yield_successive_args(
166
+ having_attributes(
167
+ type: 'test',
168
+ data_offset: 20,
169
+ data_size: 38,
170
+ version: 0x61,
171
+ flags: 0x626364
172
+ )
173
+ )
174
+ end
175
+ end
176
+
177
+ 16.times do |size|
178
+ describe "for wrong big box size #{size}" do
179
+ let(:data){ boxes.build{ box('test', 1, size) } }
180
+
181
+ it{ is_expected.to raise_error ImageSize::FormatError }
182
+ end
183
+ end
184
+
185
+ context 'given offset' do
186
+ let(:data) do
187
+ boxes.build do
188
+ box('test', 8)
189
+ box('fooo', 8)
190
+ box('barr', 8)
191
+ end
192
+ end
193
+
194
+ def is_expected
195
+ expect{ |b| instance.walk(string_reader, offset, &b) }
196
+ end
197
+
198
+ describe 'for offset at the end' do
199
+ let(:offset){ 24 }
200
+
201
+ it{ is_expected.not_to yield_control }
202
+ end
203
+
204
+ describe 'for offset at second box' do
205
+ let(:offset){ 8 }
206
+
207
+ it do
208
+ is_expected.to yield_successive_args(
209
+ having_attributes(type: 'fooo', data_offset: 16, data_size: 0),
210
+ having_attributes(type: 'barr', data_offset: 24, data_size: 0)
211
+ )
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'given offset and length' do
217
+ def is_expected
218
+ expect{ |b| instance.walk(string_reader, offset, length, &b) }
219
+ end
220
+
221
+ describe 'for offset at second box' do
222
+ let(:data) do
223
+ boxes.build do
224
+ box('test', 8)
225
+ box('fooo', 8)
226
+ box('barr', 8)
227
+ end
228
+ end
229
+ let(:offset){ 8 }
230
+ let(:length){ 8 }
231
+
232
+ it do
233
+ is_expected.to yield_successive_args(having_attributes(type: 'fooo', data_offset: 16, data_size: 0))
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ describe '#recurse' do
240
+ let(:data) do
241
+ boxes.build do
242
+ box('fooA', 8 + 8 + 2) do
243
+ box('fooB', 8 + 2){ data '11' }
244
+ end
245
+ box('barA', 8 + 8 + 8 + 2) do
246
+ box('barB', 8 + 8 + 2) do
247
+ box('barC', 8 + 2) do
248
+ data '22'
249
+ end
250
+ end
251
+ end
252
+ box('bazA', 8 + 8 + 2) do
253
+ box('bazB', 8 + 2){ data '33' }
254
+ end
255
+ end
256
+ end
257
+
258
+ context 'when configured to recures all' do
259
+ let(:options){ { recurse: %w[fooA barA barB bazA] } }
260
+
261
+ it 'recurses complete tree' do
262
+ enum = instance.to_enum(:recurse, string_reader)
263
+
264
+ expect(enum.next).to have_attributes(type: 'fooA', data_offset: 8, data_size: 10)
265
+
266
+ expect(enum.next).to have_attributes(type: 'fooB', data_offset: 16, data_size: 2)
267
+
268
+ expect(enum.next).to have_attributes(type: 'barA', data_offset: 26, data_size: 18)
269
+
270
+ expect(enum.next).to have_attributes(type: 'barB', data_offset: 34, data_size: 10)
271
+
272
+ expect(enum.next).to have_attributes(type: 'barC', data_offset: 42, data_size: 2)
273
+
274
+ expect(enum.next).to have_attributes(type: 'bazA', data_offset: 52, data_size: 10)
275
+
276
+ expect(enum.next).to have_attributes(type: 'bazB', data_offset: 60, data_size: 2)
277
+
278
+ expect{ enum.next }.to raise_exception(StopIteration)
279
+ end
280
+
281
+ it 'returns nil' do
282
+ expect(instance.recurse(string_reader){ :foo }).to be_nil
283
+ end
284
+ end
285
+
286
+ context 'when configured to recurse part' do
287
+ let(:options){ { recurse: %w[barA] } }
288
+
289
+ it 'recurses requested part' do
290
+ enum = instance.to_enum(:recurse, string_reader)
291
+
292
+ expect(enum.next).to have_attributes(type: 'fooA', data_offset: 8, data_size: 10)
293
+
294
+ expect(enum.next).to have_attributes(type: 'barA', data_offset: 26, data_size: 18)
295
+
296
+ expect(enum.next).to have_attributes(type: 'barB', data_offset: 34, data_size: 10)
297
+
298
+ expect(enum.next).to have_attributes(type: 'bazA', data_offset: 52, data_size: 10)
299
+
300
+ expect{ enum.next }.to raise_exception(StopIteration)
301
+ end
302
+
303
+ it 'returns nil' do
304
+ expect(instance.recurse(string_reader){ :foo }).to be_nil
305
+ end
306
+ end
307
+
308
+ context 'when configured to not recurse' do
309
+ let(:options){ {} }
310
+
311
+ it 'does not recurse' do
312
+ enum = instance.to_enum(:recurse, string_reader)
313
+
314
+ expect(enum.next).to have_attributes(type: 'fooA', data_offset: 8, data_size: 10)
315
+
316
+ expect(enum.next).to have_attributes(type: 'barA', data_offset: 26, data_size: 18)
317
+
318
+ expect(enum.next).to have_attributes(type: 'bazA', data_offset: 52, data_size: 10)
319
+
320
+ expect{ enum.next }.to raise_exception(StopIteration)
321
+ end
322
+
323
+ it 'returns nil' do
324
+ expect(instance.recurse(string_reader){ :foo }).to be_nil
325
+ end
326
+ end
327
+
328
+ context 'when configured to stop' do
329
+ let(:options){ { recurse: %w[fooA barA barB bazA], last: %w[barA] } }
330
+
331
+ it 'recurses complete tree' do
332
+ enum = instance.to_enum(:recurse, string_reader)
333
+
334
+ expect(enum.next).to have_attributes(type: 'fooA', data_offset: 8, data_size: 10)
335
+
336
+ expect(enum.next).to have_attributes(type: 'fooB', data_offset: 16, data_size: 2)
337
+
338
+ expect(enum.next).to have_attributes(type: 'barA', data_offset: 26, data_size: 18)
339
+
340
+ expect(enum.next).to have_attributes(type: 'barB', data_offset: 34, data_size: 10)
341
+
342
+ expect(enum.next).to have_attributes(type: 'barC', data_offset: 42, data_size: 2)
343
+
344
+ expect{ enum.next }.to raise_exception(StopIteration)
345
+ end
346
+
347
+ it 'returns nil' do
348
+ expect(instance.recurse(string_reader){ :foo }).to be_nil
349
+ end
350
+ end
351
+ end
352
+ end
@@ -10,6 +10,10 @@ require 'shellwords'
10
10
 
11
11
  require 'test_server'
12
12
 
13
+ RSpec.configure do |config|
14
+ config.order = :random
15
+ end
16
+
13
17
  describe ImageSize do
14
18
  before :all do
15
19
  @server = TestServer.new
@@ -19,6 +23,19 @@ describe ImageSize do
19
23
  @server.finish
20
24
  end
21
25
 
26
+ def retry_on(exception_class)
27
+ attempt = 1
28
+ begin
29
+ yield
30
+ rescue exception_class => e
31
+ warn "Attempt #{attempt}: #{e.inspect}"
32
+ raise unless attempt < 3
33
+
34
+ attempt += 1
35
+ retry
36
+ end
37
+ end
38
+
22
39
  def supported_formats
23
40
  ImageSize.private_instance_methods.map{ |name| name[/\Asize_of_(.*)\z/, 1] }.compact.sort
24
41
  end
@@ -45,19 +62,26 @@ describe ImageSize do
45
62
  describe "for #{path}" do
46
63
  let(:name){ File.basename(path) }
47
64
  let(:attributes) do
48
- match = /(\d+)x(\d+)\.([^.]+)$/.match(name)
49
- width, height, format = match[1].to_i, match[2].to_i, match[3].to_sym if match
65
+ if (match = /(\d+)x(\d+)\.([^.]+)$/.match(name))
66
+ width = match[1].to_i
67
+ height = match[2].to_i
68
+ format = match[3].to_sym
69
+ end
50
70
  size = format && [width, height]
71
+ media_types = ImageSize::MEDIA_TYPES[format] || []
72
+ media_type = format && media_types.first.to_s
51
73
  {
52
- :format => format,
53
- :width => width,
54
- :height => height,
55
- :w => width,
56
- :h => height,
57
- :size => size,
74
+ format: format,
75
+ width: width,
76
+ height: height,
77
+ w: width,
78
+ h: height,
79
+ size: size,
80
+ media_type: media_type,
81
+ media_types: media_types,
58
82
  }
59
83
  end
60
- let(:file_data){ File.open(path, 'rb', &:read) }
84
+ let(:file_data){ File.binread(path) }
61
85
  let(:file_size){ file_data.length }
62
86
 
63
87
  before do
@@ -148,14 +172,18 @@ describe ImageSize do
148
172
  context 'supporting range' do
149
173
  context 'without redirects' do
150
174
  it 'gets format and dimensions' do
151
- image_size = ImageSize.url(file_url)
175
+ image_size = retry_on Timeout::Error do
176
+ ImageSize.url(file_url)
177
+ end
152
178
  expect(image_size).to have_attributes(attributes)
153
179
  end
154
180
  end
155
181
 
156
182
  context 'with redirects' do
157
183
  it 'gets format and dimensions' do
158
- image_size = ImageSize.url("#{file_url}?redirect=5")
184
+ image_size = retry_on Timeout::Error do
185
+ ImageSize.url("#{file_url}?redirect=5")
186
+ end
159
187
  expect(image_size).to have_attributes(attributes)
160
188
  end
161
189
  end
@@ -163,7 +191,9 @@ describe ImageSize do
163
191
  context 'with too many redirects' do
164
192
  it 'gets format and dimensions' do
165
193
  expect do
166
- ImageSize.url("#{file_url}?redirect=6")
194
+ retry_on Timeout::Error do
195
+ ImageSize.url("#{file_url}?redirect=6")
196
+ end
167
197
  end.to raise_error(/Too many redirects/)
168
198
  end
169
199
  end
@@ -172,14 +202,18 @@ describe ImageSize do
172
202
  context 'not supporting range' do
173
203
  context 'without redirects' do
174
204
  it 'gets format and dimensions' do
175
- image_size = ImageSize.url("#{file_url}?ignore_range")
205
+ image_size = retry_on Timeout::Error do
206
+ ImageSize.url("#{file_url}?ignore_range")
207
+ end
176
208
  expect(image_size).to have_attributes(attributes)
177
209
  end
178
210
  end
179
211
 
180
212
  context 'with redirects' do
181
213
  it 'gets format and dimensions' do
182
- image_size = ImageSize.url("#{file_url}?ignore_range&redirect=5")
214
+ image_size = retry_on Timeout::Error do
215
+ ImageSize.url("#{file_url}?ignore_range&redirect=5")
216
+ end
183
217
  expect(image_size).to have_attributes(attributes)
184
218
  end
185
219
  end
@@ -187,7 +221,9 @@ describe ImageSize do
187
221
  context 'with too many redirects' do
188
222
  it 'gets format and dimensions' do
189
223
  expect do
190
- ImageSize.url("#{file_url}?ignore_range&redirect=6")
224
+ retry_on Timeout::Error do
225
+ ImageSize.url("#{file_url}?ignore_range&redirect=6")
226
+ end
191
227
  end.to raise_error(/Too many redirects/)
192
228
  end
193
229
  end
@@ -203,8 +239,8 @@ describe ImageSize do
203
239
  end
204
240
 
205
241
  {
206
- :png => "\211PNG\r\n\032\n",
207
- :jpeg => "\377\330",
242
+ png: "\211PNG\r\n\032\n",
243
+ jpeg: "\377\330",
208
244
  }.each do |type, data|
209
245
  it "raises FormatError if invalid #{type} given" do
210
246
  expect do
Binary file
Binary file
data/spec/test_server.rb CHANGED
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'webrick'
4
+ require 'stringio'
4
5
 
5
6
  class TestServer
6
7
  attr_reader :base_url
7
8
 
8
9
  def initialize(host = '127.0.0.1')
9
10
  server_options = {
10
- :Logger => WEBrick::Log.new(StringIO.new),
11
- :AccessLog => [],
12
- :BindAddress => host,
13
- :Port => 0, # get the next available port
14
- :DocumentRoot => '.',
15
- :RequestCallback => proc do |req, res|
11
+ Logger: WEBrick::Log.new(StringIO.new),
12
+ AccessLog: [],
13
+ BindAddress: host,
14
+ Port: 0, # get the next available port
15
+ DocumentRoot: '.',
16
+ RequestCallback: proc do |req, res|
16
17
  redirect = req.query['redirect'].to_i
17
18
  if redirect > 0
18
19
  res.set_redirect(
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: 3.2.0
4
+ version: 3.4.0
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: 2022-11-02 00:00:00.000000000 Z
12
+ date: 2024-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -53,9 +53,9 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '2.0'
56
- description: 'Measure following file dimensions: apng, bmp, cur, emf, gif, ico, j2c,
57
- jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm,
58
- xpm'
56
+ description: 'Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic,
57
+ heif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf,
58
+ tiff, webp, xbm, xpm'
59
59
  email:
60
60
  executables: []
61
61
  extensions: []
@@ -74,12 +74,17 @@ files:
74
74
  - image_size.gemspec
75
75
  - lib/image_size.rb
76
76
  - lib/image_size/chunky_reader.rb
77
+ - lib/image_size/format_error.rb
78
+ - lib/image_size/isobmff.rb
79
+ - lib/image_size/media_types.rb
77
80
  - lib/image_size/reader.rb
78
81
  - lib/image_size/seekable_io_reader.rb
79
82
  - lib/image_size/stream_io_reader.rb
80
83
  - lib/image_size/string_reader.rb
84
+ - lib/image_size/uri.rb
81
85
  - lib/image_size/uri_reader.rb
82
86
  - spec/image_size/chunky_reader_spec.rb
87
+ - spec/image_size/isobmff_spec.rb
83
88
  - spec/image_size/seekable_io_reader_spec.rb
84
89
  - spec/image_size_spec.rb
85
90
  - spec/images/.gitattributes
@@ -91,6 +96,11 @@ files:
91
96
  - spec/images/emf/77x77.emf
92
97
  - spec/images/empty
93
98
  - spec/images/gif/668x481.gif
99
+ - spec/images/heif/ap_maloletka_mariupol.452x301.heic
100
+ - spec/images/heif/maxar_mariupol.400x300.avif
101
+ - spec/images/heif/multiple.169x83.heic
102
+ - spec/images/heif/rotate.73x173.avif
103
+ - spec/images/heif/sequence.7x5.avif
94
104
  - spec/images/ico/32x256.ico
95
105
  - spec/images/jp2/163x402.jp2
96
106
  - spec/images/jp2/176x373.jpx
@@ -130,7 +140,7 @@ licenses:
130
140
  metadata:
131
141
  bug_tracker_uri: https://github.com/toy/image_size/issues
132
142
  changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
133
- documentation_uri: https://www.rubydoc.info/gems/image_size/3.2.0
143
+ documentation_uri: https://www.rubydoc.info/gems/image_size/3.4.0
134
144
  source_code_uri: https://github.com/toy/image_size
135
145
  post_install_message:
136
146
  rdoc_options: []
@@ -140,19 +150,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
150
  requirements:
141
151
  - - ">="
142
152
  - !ruby/object:Gem::Version
143
- version: '0'
153
+ version: 1.9.3
144
154
  required_rubygems_version: !ruby/object:Gem::Requirement
145
155
  requirements:
146
156
  - - ">="
147
157
  - !ruby/object:Gem::Version
148
158
  version: '0'
149
159
  requirements: []
150
- rubygems_version: 3.3.11
160
+ rubygems_version: 3.4.20
151
161
  signing_key:
152
162
  specification_version: 4
153
- summary: Measure image size using pure Ruby
163
+ summary: Measure image size/dimensions using pure Ruby
154
164
  test_files:
155
165
  - spec/image_size/chunky_reader_spec.rb
166
+ - spec/image_size/isobmff_spec.rb
156
167
  - spec/image_size/seekable_io_reader_spec.rb
157
168
  - spec/image_size_spec.rb
158
169
  - spec/images/.gitattributes
@@ -164,6 +175,11 @@ test_files:
164
175
  - spec/images/emf/77x77.emf
165
176
  - spec/images/empty
166
177
  - spec/images/gif/668x481.gif
178
+ - spec/images/heif/ap_maloletka_mariupol.452x301.heic
179
+ - spec/images/heif/maxar_mariupol.400x300.avif
180
+ - spec/images/heif/multiple.169x83.heic
181
+ - spec/images/heif/rotate.73x173.avif
182
+ - spec/images/heif/sequence.7x5.avif
167
183
  - spec/images/ico/32x256.ico
168
184
  - spec/images/jp2/163x402.jp2
169
185
  - spec/images/jp2/176x373.jpx