image_size 3.2.0 → 3.4.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 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