image_size 3.1.0 → 3.3.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 +4 -4
- data/.github/workflows/check.yml +10 -8
- data/.github/workflows/rubocop.yml +2 -2
- data/CHANGELOG.markdown +9 -0
- data/README.markdown +5 -5
- data/image_size.gemspec +3 -3
- data/lib/image_size/format_error.rb +5 -0
- data/lib/image_size/isobmff.rb +99 -0
- data/lib/image_size/reader.rb +17 -0
- data/lib/image_size.rb +96 -40
- data/spec/image_size/isobmff_spec.rb +352 -0
- data/spec/image_size_spec.rb +20 -0
- data/spec/images/emf/74x77.emf +0 -0
- data/spec/images/emf/77x77.emf +0 -0
- data/spec/images/heif/ap_maloletka_mariupol.452x301.heic +0 -0
- data/spec/images/heif/maxar_mariupol.400x300.avif +0 -0
- data/spec/images/heif/multiple.169x83.heic +0 -0
- data/spec/images/heif/rotate.73x173.avif +0 -0
- data/spec/images/heif/sequence.7x5.avif +0 -0
- metadata +26 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d0b43a481f6a18e76f7cf8325240014d2a1e07a52cfb462be37444e020ff036
|
|
4
|
+
data.tar.gz: 21edad5bbdd1a4e4b60953ee2769bd0648e8deb389f220f0dbd120f6a9dee09d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78e9a671390fc80747273ccd6555e2faa915c4186261f473e86aa274f3e93e75c5e26f4674f3fea307d8a94ca21e352dfeb44a2046f20fb453d7e394f56c13b5
|
|
7
|
+
data.tar.gz: 3dd7259403bb9aebc0d75834330f8db1b21d68bc619ec9204475e46e4c46b818ce28af2712d57a0c7ef3d7bb1a5922f3d91ba3a6b6779c7421e2fd2d9eaf4182
|
data/.github/workflows/check.yml
CHANGED
|
@@ -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,12 @@ jobs:
|
|
|
20
17
|
- '2.7'
|
|
21
18
|
- '3.0'
|
|
22
19
|
- '3.1'
|
|
23
|
-
-
|
|
24
|
-
- jruby-9.2
|
|
20
|
+
- '3.2'
|
|
25
21
|
- jruby-9.3
|
|
22
|
+
- jruby-9.4
|
|
26
23
|
fail-fast: false
|
|
27
24
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v3
|
|
29
26
|
- uses: ruby/setup-ruby@v1
|
|
30
27
|
with:
|
|
31
28
|
ruby-version: "${{ matrix.ruby }}"
|
|
@@ -39,9 +36,12 @@ jobs:
|
|
|
39
36
|
container:
|
|
40
37
|
- 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@
|
|
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'
|
|
56
57
|
fail-fast: false
|
|
58
|
+
continue-on-error: ${{ matrix.ruby == '3.2' }}
|
|
57
59
|
steps:
|
|
58
|
-
- uses: actions/checkout@
|
|
60
|
+
- uses: actions/checkout@v3
|
|
59
61
|
- uses: ruby/setup-ruby@v1
|
|
60
62
|
with:
|
|
61
63
|
ruby-version: "${{ matrix.ruby }}"
|
data/CHANGELOG.markdown
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## unreleased
|
|
4
4
|
|
|
5
|
+
## v3.3.0 (2023-05-30)
|
|
6
|
+
|
|
7
|
+
* Support `HEIF` (`HEIC` and `AVIF`) images [#19](https://github.com/toy/image_size/issues/19) [@toy](https://github.com/toy)
|
|
8
|
+
* Fix handling `JPEG 2000` 64 bit size boxes [@toy](https://github.com/toy)
|
|
9
|
+
|
|
10
|
+
## v3.2.0 (2022-11-03)
|
|
11
|
+
|
|
12
|
+
* Support `EMF` images [#21](https://github.com/toy/image_size/pull/21) [@opoudjis](https://github.com/opoudjis)
|
|
13
|
+
|
|
5
14
|
## v3.1.0 (2022-09-17)
|
|
6
15
|
|
|
7
16
|
* Document experimental fetching from http server [#18](https://github.com/toy/image_size/issues/18) [@toy](https://github.com/toy)
|
data/README.markdown
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[](https://rubygems.org/gems/image_size)
|
|
2
|
-
[](https://github.com/toy/image_size/actions/workflows/check.yml)
|
|
3
|
+
[](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`, `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
|
|
|
@@ -144,4 +144,4 @@ puts Benchmark.measure{ p ImageSize.url(url).size }
|
|
|
144
144
|
This code is free to use under the terms of the [Ruby's licence](LICENSE.txt).
|
|
145
145
|
|
|
146
146
|
Original author: Keisuke Minami <keisuke@rccn.com>.\
|
|
147
|
-
Further development 2010-
|
|
147
|
+
Further development 2010-2023 Ivan Kuchin https://github.com/toy/image_size
|
data/image_size.gemspec
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'image_size'
|
|
5
|
-
s.version = '3.
|
|
6
|
-
s.summary = %q{Measure image size using pure Ruby}
|
|
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}
|
|
5
|
+
s.version = '3.3.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'
|
|
@@ -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
|
data/lib/image_size/reader.rb
CHANGED
|
@@ -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
|
data/lib/image_size.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# encoding: BINARY
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'image_size/isobmff'
|
|
5
|
+
require 'image_size/format_error'
|
|
4
6
|
require 'image_size/reader'
|
|
5
7
|
require 'image_size/seekable_io_reader'
|
|
6
8
|
require 'image_size/stream_io_reader'
|
|
@@ -8,8 +10,6 @@ require 'image_size/string_reader'
|
|
|
8
10
|
|
|
9
11
|
# Determine image format and size
|
|
10
12
|
class ImageSize
|
|
11
|
-
class FormatError < StandardError; end
|
|
12
|
-
|
|
13
13
|
# Array joining with 'x'
|
|
14
14
|
class Size < Array
|
|
15
15
|
# join using 'x'
|
|
@@ -77,24 +77,27 @@ private
|
|
|
77
77
|
head = ir[0, 1024]
|
|
78
78
|
case
|
|
79
79
|
when head.nil? || head.empty? then nil
|
|
80
|
-
when head[0, 6] =~
|
|
80
|
+
when head[0, 6] =~ /\AGIF8[79]a\z/ then :gif
|
|
81
81
|
when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
|
|
82
82
|
when head[0, 8] == "\212MNG\r\n\032\n" then :mng
|
|
83
83
|
when head[0, 2] == "\377\330" then :jpeg
|
|
84
84
|
when head[0, 2] == 'BM' then :bmp
|
|
85
|
-
when head[0, 3] =~
|
|
85
|
+
when head[0, 3] =~ /\AP([1-6]\s|7\n)\z/ then detect_pnm_type(ir)
|
|
86
86
|
when head =~ /\#define\s+\S+\s+\d+/ then :xbm
|
|
87
87
|
when %W[II*\0 MM\0*].include?(head[0, 4]) then :tiff
|
|
88
88
|
when head =~ %r{/\* XPM \*/} then :xpm
|
|
89
89
|
when head[0, 4] == '8BPS' then :psd
|
|
90
|
-
when head[0, 3] =~
|
|
90
|
+
when head[0, 3] =~ /\A[FC]WS\z/ then :swf
|
|
91
91
|
when head =~ SVG_R || (head =~ XML_R && ir[0, 4096] =~ SVG_R) then :svg
|
|
92
92
|
when head[0, 2] =~ /\n[\0-\5]/ then :pcx
|
|
93
|
-
when head[0, 12] =~
|
|
93
|
+
when head[0, 12] =~ /\ARIFF(?m:....)WEBP\z/ then :webp
|
|
94
94
|
when head[0, 4] == "\0\0\1\0" then :ico
|
|
95
95
|
when head[0, 4] == "\0\0\2\0" then :cur
|
|
96
96
|
when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
|
|
97
97
|
when head[0, 4] == "\377O\377Q" then :j2c
|
|
98
|
+
when head[0, 4] == "\1\0\0\0" && head[40, 4] == ' EMF' then :emf
|
|
99
|
+
when head[4, 8] =~ /\Aftypavi[fs]\z/ then :avif
|
|
100
|
+
when head[4, 8] =~ /\Aftyp(hei[cs]|mif[12]|msf1)\z/ then :heic
|
|
98
101
|
end
|
|
99
102
|
end
|
|
100
103
|
|
|
@@ -106,7 +109,7 @@ private
|
|
|
106
109
|
return :apng if type == 'acTL'
|
|
107
110
|
|
|
108
111
|
length = ir.unpack1(offset, 4, 'N')
|
|
109
|
-
offset +=
|
|
112
|
+
offset += length + 8 + 4
|
|
110
113
|
end
|
|
111
114
|
:png
|
|
112
115
|
end
|
|
@@ -125,7 +128,7 @@ private
|
|
|
125
128
|
|
|
126
129
|
# using xl-box would be weird, but doesn't seem to contradict specification
|
|
127
130
|
skip = ir[12, 4] == "\0\0\0\1" ? 16 : 8
|
|
128
|
-
case ir[
|
|
131
|
+
case ir[skip + 12, 4]
|
|
129
132
|
when 'jp2 ' then :jp2
|
|
130
133
|
when 'jpx ' then :jpx
|
|
131
134
|
end
|
|
@@ -287,7 +290,7 @@ private
|
|
|
287
290
|
|
|
288
291
|
def size_of_swf(ir)
|
|
289
292
|
value_bit_length = ir.unpack1(8, 1, 'B5').to_i(2)
|
|
290
|
-
bit_length =
|
|
293
|
+
bit_length = (value_bit_length * 4) + 5
|
|
291
294
|
rect_bits = ir.unpack1(8, (bit_length / 8) + 1, "B#{bit_length}")
|
|
292
295
|
values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
|
|
293
296
|
x_min, x_max, y_min, y_max = values
|
|
@@ -335,38 +338,13 @@ private
|
|
|
335
338
|
end
|
|
336
339
|
end
|
|
337
340
|
|
|
341
|
+
JP2_WALKER = ImageSize::ISOBMFF.new(
|
|
342
|
+
:recurse => %w[jp2h],
|
|
343
|
+
:last => %w[jp2h]
|
|
344
|
+
)
|
|
338
345
|
def size_of_jp2(ir)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
in_header = false
|
|
342
|
-
loop do
|
|
343
|
-
break if stop && offset >= stop
|
|
344
|
-
break if ir[offset, 4] == '' || ir[offset, 4].nil?
|
|
345
|
-
|
|
346
|
-
size = ir.unpack1(offset, 4, 'N')
|
|
347
|
-
type = ir.fetch(offset + 4, 4)
|
|
348
|
-
|
|
349
|
-
data_offset = 8
|
|
350
|
-
case size
|
|
351
|
-
when 1
|
|
352
|
-
size = ir.unpack1(offset, 8, 'Q>')
|
|
353
|
-
data_offset = 16
|
|
354
|
-
raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
|
|
355
|
-
when 2..7
|
|
356
|
-
raise FormatError, "Reserved box size #{size}"
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
if type == 'jp2h'
|
|
360
|
-
stop = offset + size unless size.zero?
|
|
361
|
-
offset += data_offset
|
|
362
|
-
in_header = true
|
|
363
|
-
elsif in_header && type == 'ihdr'
|
|
364
|
-
return ir.unpack(offset + data_offset, 8, 'NN').reverse
|
|
365
|
-
else
|
|
366
|
-
break if size.zero? # box to the end of file
|
|
367
|
-
|
|
368
|
-
offset += size
|
|
369
|
-
end
|
|
346
|
+
JP2_WALKER.recurse(ir) do |box|
|
|
347
|
+
return ir.unpack(box.data_offset, 8, 'NN').reverse if box.type == 'ihdr'
|
|
370
348
|
end
|
|
371
349
|
end
|
|
372
350
|
alias_method :size_of_jpx, :size_of_jp2
|
|
@@ -374,4 +352,82 @@ private
|
|
|
374
352
|
def size_of_j2c(ir)
|
|
375
353
|
ir.unpack(8, 8, 'NN')
|
|
376
354
|
end
|
|
355
|
+
|
|
356
|
+
EMF_UMAX = 256**4
|
|
357
|
+
EMF_SMAX = EMF_UMAX / 2
|
|
358
|
+
|
|
359
|
+
def size_of_emf(ir)
|
|
360
|
+
left, top, right, bottom =
|
|
361
|
+
if RUBY_VERSION < '1.9'
|
|
362
|
+
ir.unpack(24, 16, 'V*').map{ |u| u < EMF_SMAX ? u : u - EMF_UMAX }
|
|
363
|
+
else
|
|
364
|
+
ir.unpack(24, 16, 'L<*')
|
|
365
|
+
end
|
|
366
|
+
dpi = self.class.dpi
|
|
367
|
+
[right - left + 1, bottom - top + 1].map do |n|
|
|
368
|
+
(n.to_f * dpi / 2540).round
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
HEIF_WALKER = ImageSize::ISOBMFF.new(
|
|
373
|
+
:recurse => %w[meta iprp ipco],
|
|
374
|
+
:full => %w[meta hdlr pitm ipma ispe],
|
|
375
|
+
:last => %w[meta]
|
|
376
|
+
)
|
|
377
|
+
def size_of_heif(ir)
|
|
378
|
+
pitm = nil
|
|
379
|
+
ipma = nil
|
|
380
|
+
ispes = {}
|
|
381
|
+
claps = {}
|
|
382
|
+
irots = {}
|
|
383
|
+
|
|
384
|
+
HEIF_WALKER.recurse(ir) do |box, _path|
|
|
385
|
+
case box.type
|
|
386
|
+
when 'hdlr'
|
|
387
|
+
raise FormatError, "hdlr box too small (#{box.data_size})" if box.data_size < 8
|
|
388
|
+
|
|
389
|
+
return nil unless ir[box.data_offset + 4, 4] == 'pict'
|
|
390
|
+
when 'pitm'
|
|
391
|
+
raise FormatError, 'second pitm box encountered' if pitm
|
|
392
|
+
|
|
393
|
+
pitm = box.version == 0 ? ir.unpack1(box.data_offset, 2, 'n') : ir.unpack1(box.data_offset, 4, 'N')
|
|
394
|
+
when 'ipma'
|
|
395
|
+
stream = ir.stream(box.data_offset)
|
|
396
|
+
|
|
397
|
+
property_index_16b = (box.flags & 1) == 1
|
|
398
|
+
|
|
399
|
+
ipma ||= {}
|
|
400
|
+
stream.unpack1(4, 'N').times do
|
|
401
|
+
item_id = box.version == 0 ? stream.unpack1(2, 'n') : stream.unpack1(4, 'N')
|
|
402
|
+
ipma[item_id] ||= Array.new(stream.unpack1(1, 'C')) do
|
|
403
|
+
property_index_16b ? stream.unpack1(2, 'n') & 0x7fff : stream.unpack1(1, 'C') & 0x7f
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
when 'ispe'
|
|
407
|
+
ispes[box.index] ||= ir.unpack(box.data_offset, 8, 'NN')
|
|
408
|
+
when 'clap'
|
|
409
|
+
width_n, width_d, height_n, height_d = ir.unpack(box.data_offset, 16, 'N4')
|
|
410
|
+
claps[box.index] ||= [Rational(width_n, width_d).round, Rational(height_n, height_d).round]
|
|
411
|
+
when 'irot'
|
|
412
|
+
irots[box.index] ||= ir.unpack1(box.data_offset, 1, 'C') & 0b11
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
return unless ipma
|
|
417
|
+
|
|
418
|
+
properties = ipma[pitm || ipma.keys.min]
|
|
419
|
+
return unless properties
|
|
420
|
+
|
|
421
|
+
dimensions = claps.values_at(*properties).compact.first || ispes.values_at(*properties).compact.first
|
|
422
|
+
return unless dimensions
|
|
423
|
+
|
|
424
|
+
irot = irots.values_at(*properties).compact.first
|
|
425
|
+
if irot && irot.odd?
|
|
426
|
+
dimensions.reverse
|
|
427
|
+
else
|
|
428
|
+
dimensions
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
alias_method :size_of_avif, :size_of_heif
|
|
432
|
+
alias_method :size_of_heic, :size_of_heif
|
|
377
433
|
end
|
|
@@ -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
|
data/spec/image_size_spec.rb
CHANGED
|
@@ -19,6 +19,26 @@ describe ImageSize do
|
|
|
19
19
|
@server.finish
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def supported_formats
|
|
23
|
+
ImageSize.private_instance_methods.map{ |name| name[/\Asize_of_(.*)\z/, 1] }.compact.sort
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe 'README' do
|
|
27
|
+
let(:readme){ File.read('README.markdown') }
|
|
28
|
+
|
|
29
|
+
it 'lists all supported formats' do
|
|
30
|
+
expect(readme[/^Formats: .*$/]).to eq("Formats: #{supported_formats.map{ |format| "`#{format}`" }.join(', ')}.")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'gemspec' do
|
|
35
|
+
let(:gemspec){ Gem::Specification.load('image_size.gemspec') }
|
|
36
|
+
|
|
37
|
+
it 'lists all supported formats in description' do
|
|
38
|
+
expect(gemspec.description).to eq("Measure following file dimensions: #{supported_formats.join(', ')}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
22
42
|
Dir['spec/**/*'].each do |path|
|
|
23
43
|
next unless File.file?(path)
|
|
24
44
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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.
|
|
4
|
+
version: 3.3.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:
|
|
12
|
+
date: 2023-05-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rspec
|
|
@@ -53,8 +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,
|
|
57
|
-
jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf,
|
|
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'
|
|
58
59
|
email:
|
|
59
60
|
executables: []
|
|
60
61
|
extensions: []
|
|
@@ -73,12 +74,15 @@ files:
|
|
|
73
74
|
- image_size.gemspec
|
|
74
75
|
- lib/image_size.rb
|
|
75
76
|
- lib/image_size/chunky_reader.rb
|
|
77
|
+
- lib/image_size/format_error.rb
|
|
78
|
+
- lib/image_size/isobmff.rb
|
|
76
79
|
- lib/image_size/reader.rb
|
|
77
80
|
- lib/image_size/seekable_io_reader.rb
|
|
78
81
|
- lib/image_size/stream_io_reader.rb
|
|
79
82
|
- lib/image_size/string_reader.rb
|
|
80
83
|
- lib/image_size/uri_reader.rb
|
|
81
84
|
- spec/image_size/chunky_reader_spec.rb
|
|
85
|
+
- spec/image_size/isobmff_spec.rb
|
|
82
86
|
- spec/image_size/seekable_io_reader_spec.rb
|
|
83
87
|
- spec/image_size_spec.rb
|
|
84
88
|
- spec/images/.gitattributes
|
|
@@ -86,8 +90,15 @@ files:
|
|
|
86
90
|
- spec/images/bmp/v3-bottom2top.42x50.bmp
|
|
87
91
|
- spec/images/bmp/v3-top2bottom.42x50.bmp
|
|
88
92
|
- spec/images/cur/32x256.cur
|
|
93
|
+
- spec/images/emf/74x77.emf
|
|
94
|
+
- spec/images/emf/77x77.emf
|
|
89
95
|
- spec/images/empty
|
|
90
96
|
- spec/images/gif/668x481.gif
|
|
97
|
+
- spec/images/heif/ap_maloletka_mariupol.452x301.heic
|
|
98
|
+
- spec/images/heif/maxar_mariupol.400x300.avif
|
|
99
|
+
- spec/images/heif/multiple.169x83.heic
|
|
100
|
+
- spec/images/heif/rotate.73x173.avif
|
|
101
|
+
- spec/images/heif/sequence.7x5.avif
|
|
91
102
|
- spec/images/ico/32x256.ico
|
|
92
103
|
- spec/images/jp2/163x402.jp2
|
|
93
104
|
- spec/images/jp2/176x373.jpx
|
|
@@ -127,7 +138,7 @@ licenses:
|
|
|
127
138
|
metadata:
|
|
128
139
|
bug_tracker_uri: https://github.com/toy/image_size/issues
|
|
129
140
|
changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
|
|
130
|
-
documentation_uri: https://www.rubydoc.info/gems/image_size/3.
|
|
141
|
+
documentation_uri: https://www.rubydoc.info/gems/image_size/3.3.0
|
|
131
142
|
source_code_uri: https://github.com/toy/image_size
|
|
132
143
|
post_install_message:
|
|
133
144
|
rdoc_options: []
|
|
@@ -144,12 +155,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
144
155
|
- !ruby/object:Gem::Version
|
|
145
156
|
version: '0'
|
|
146
157
|
requirements: []
|
|
147
|
-
rubygems_version: 3.
|
|
158
|
+
rubygems_version: 3.4.12
|
|
148
159
|
signing_key:
|
|
149
160
|
specification_version: 4
|
|
150
|
-
summary: Measure image size using pure Ruby
|
|
161
|
+
summary: Measure image size/dimensions using pure Ruby
|
|
151
162
|
test_files:
|
|
152
163
|
- spec/image_size/chunky_reader_spec.rb
|
|
164
|
+
- spec/image_size/isobmff_spec.rb
|
|
153
165
|
- spec/image_size/seekable_io_reader_spec.rb
|
|
154
166
|
- spec/image_size_spec.rb
|
|
155
167
|
- spec/images/.gitattributes
|
|
@@ -157,8 +169,15 @@ test_files:
|
|
|
157
169
|
- spec/images/bmp/v3-bottom2top.42x50.bmp
|
|
158
170
|
- spec/images/bmp/v3-top2bottom.42x50.bmp
|
|
159
171
|
- spec/images/cur/32x256.cur
|
|
172
|
+
- spec/images/emf/74x77.emf
|
|
173
|
+
- spec/images/emf/77x77.emf
|
|
160
174
|
- spec/images/empty
|
|
161
175
|
- spec/images/gif/668x481.gif
|
|
176
|
+
- spec/images/heif/ap_maloletka_mariupol.452x301.heic
|
|
177
|
+
- spec/images/heif/maxar_mariupol.400x300.avif
|
|
178
|
+
- spec/images/heif/multiple.169x83.heic
|
|
179
|
+
- spec/images/heif/rotate.73x173.avif
|
|
180
|
+
- spec/images/heif/sequence.7x5.avif
|
|
162
181
|
- spec/images/ico/32x256.ico
|
|
163
182
|
- spec/images/jp2/163x402.jp2
|
|
164
183
|
- spec/images/jp2/176x373.jpx
|