image_size 3.5.0 → 3.6.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 +2 -0
- data/.github/workflows/rubocop.yml +2 -0
- data/CHANGELOG.markdown +4 -0
- data/README.markdown +1 -1
- data/image_size.gemspec +2 -2
- data/lib/image_size/media_types.rb +1 -0
- data/lib/image_size.rb +91 -24
- data/spec/image_size/chunky_reader_spec.rb +1 -1
- data/spec/image_size/seekable_io_reader_spec.rb +1 -1
- data/spec/image_size_spec.rb +37 -14
- data/spec/images/icns/16x12.icns +0 -0
- data/spec/images/icns/afp_scanpix_leta_pilipey_kyiv.256x256@1x.icns +0 -0
- data/spec/images/icns/reuters_scanpix_leta_garanich_kyiv.256x256@2x.icns +0 -0
- data/spec/images/icns/toc.512x512@1x.icns +0 -0
- data/spec/images/icns/toc.512x512@2x.icns +0 -0
- metadata +14 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7880ff3fdbcfdbbc6e3248c79ef59adea338f21c63c285323c5b01bdf699477
|
|
4
|
+
data.tar.gz: b8ce40e97bc91a2f7e91a9ce3f47d17f06aa38a6a28f7e534837c1f668cc51e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 027e0f32e86acf19b92cb00404dfb2b26f0989ed13d07247512ff024d058a245d62b3b252e2605ff1d79a41636cb87874f2f556f3e2cdcaf2727efe3fc91efe2
|
|
7
|
+
data.tar.gz: b8a5074adaa1d08d6b3b8d9d1a5da5c5239e1ea46b345fc13ffbf5ba75736d826beaeceb38b09aec7267f6c934856c1237c9c514ecad3fb6e9701b0846352f0d
|
data/.github/workflows/check.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## unreleased
|
|
4
4
|
|
|
5
|
+
## v3.6.0 (2026-05-26)
|
|
6
|
+
|
|
7
|
+
* Support `.icns` Apple Icon Image format [@toy](https://github.com/toy)
|
|
8
|
+
|
|
5
9
|
## v3.5.0 (2026-05-03)
|
|
6
10
|
|
|
7
11
|
* Add `byte_size` method to expose the size of the image data in bytes [#27](https://github.com/toy/image_size/pull/27) [@dgodd](https://github.com/dgodd)
|
data/README.markdown
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# image_size
|
|
6
6
|
|
|
7
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`.
|
|
8
|
+
Formats: `apng`, `avif`, `bmp`, `cur`, `emf`, `gif`, `heic`, `heif`, `icns`, `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
|
|
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.
|
|
5
|
+
s.version = '3.6.0'
|
|
6
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}
|
|
7
|
+
s.description = %q{Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic, heif, icns, 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'
|
data/lib/image_size.rb
CHANGED
|
@@ -31,35 +31,40 @@ class ImageSize
|
|
|
31
31
|
alias_method :h, :height
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
class << self
|
|
35
|
+
# Given path to image finds its format, width and height
|
|
36
|
+
def path(path)
|
|
37
|
+
new(Pathname.new(path))
|
|
38
|
+
end
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
# DPI used for svg and emf
|
|
41
|
+
def dpi
|
|
42
|
+
@dpi || 72.0
|
|
43
|
+
end
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
# Set DPI used for svg and emf
|
|
46
|
+
def dpi=(dpi)
|
|
47
|
+
fail ArgumentError, "dpi should be nil or positive, got #{dpi}" unless dpi.nil? || dpi > 0
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
@dpi = dpi ? dpi.to_f : nil
|
|
50
|
+
end
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@chunk_size || 4096
|
|
54
|
-
end
|
|
52
|
+
# Use display pixels instead of physical/image pixels for icns
|
|
53
|
+
attr_accessor :use_display_pixels
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fail ArgumentError, "chunk_size should be a positive Integer or nil, got #{chunk_size}"
|
|
55
|
+
# Size of chunk to use by IO and URI readers
|
|
56
|
+
def chunk_size
|
|
57
|
+
@chunk_size || 4096
|
|
60
58
|
end
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
# Set size of chunk to use by IO and URI readers
|
|
61
|
+
def chunk_size=(chunk_size)
|
|
62
|
+
unless chunk_size.nil? || (chunk_size.is_a?(Integer) && chunk_size > 0)
|
|
63
|
+
fail ArgumentError, "chunk_size should be a positive Integer or nil, got #{chunk_size}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@chunk_size = chunk_size
|
|
67
|
+
end
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
# Given image as any class responding to read and eof? or data as String, finds its format and dimensions
|
|
@@ -106,6 +111,8 @@ private
|
|
|
106
111
|
|
|
107
112
|
SVG_R = /<svg\b([^>]*)>/.freeze
|
|
108
113
|
XML_R = /<\?xml|<!--/.freeze
|
|
114
|
+
private_constant :SVG_R, :XML_R
|
|
115
|
+
|
|
109
116
|
def detect_format(ir)
|
|
110
117
|
head = ir[0, 1024]
|
|
111
118
|
case
|
|
@@ -126,6 +133,7 @@ private
|
|
|
126
133
|
when head[0, 12] =~ /\ARIFF(?m:....)WEBP\z/ then :webp
|
|
127
134
|
when head[0, 4] == "\0\0\1\0" then :ico
|
|
128
135
|
when head[0, 4] == "\0\0\2\0" then :cur
|
|
136
|
+
when head[0, 4] == 'icns' then :icns
|
|
129
137
|
when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
|
|
130
138
|
when head[0, 4] == "\377O\377Q" then :j2c
|
|
131
139
|
when head[0, 4] == "\1\0\0\0" && head[40, 4] == ' EMF' then :emf
|
|
@@ -190,6 +198,8 @@ private
|
|
|
190
198
|
0xC9, 0xCA, 0xCB,
|
|
191
199
|
0xCD, 0xCE, 0xCF
|
|
192
200
|
].freeze
|
|
201
|
+
private_constant :JPEG_CODE_CHECK
|
|
202
|
+
|
|
193
203
|
def size_of_jpeg(ir)
|
|
194
204
|
section_marker = "\xFF"
|
|
195
205
|
offset = 2
|
|
@@ -351,6 +361,60 @@ private
|
|
|
351
361
|
end
|
|
352
362
|
alias_method :size_of_cur, :size_of_ico
|
|
353
363
|
|
|
364
|
+
ICNS_16X12 = %w[icm# icm4 icm8].freeze
|
|
365
|
+
ICNS_SQUARE = {
|
|
366
|
+
[16, 1] => %w[ic04 icp4 ics# ics4 ics8 is32 s8mk],
|
|
367
|
+
[16, 2] => %w[ic11],
|
|
368
|
+
[18, 1] => %w[icsb],
|
|
369
|
+
[18, 2] => %w[icsB],
|
|
370
|
+
[24, 1] => %w[sb24],
|
|
371
|
+
[24, 2] => %w[SB24],
|
|
372
|
+
[32, 1] => %w[ICN# ICON ic05 icl4 icl8 icp5 il32 l8mk],
|
|
373
|
+
[32, 2] => %w[ic12],
|
|
374
|
+
[48, 1] => %w[h8mk ich# ich4 ich8 icp6 ih32],
|
|
375
|
+
[128, 1] => %w[ic07 it32 t8mk],
|
|
376
|
+
[128, 2] => %w[ic13],
|
|
377
|
+
[256, 1] => %w[ic08],
|
|
378
|
+
[256, 2] => %w[ic14],
|
|
379
|
+
[512, 1] => %w[ic09],
|
|
380
|
+
[512, 2] => %w[ic10],
|
|
381
|
+
}.each_with_object({}){ |(spec, types), h| types.each{ |type| h[type] = spec } }.freeze
|
|
382
|
+
private_constant :ICNS_16X12, :ICNS_SQUARE
|
|
383
|
+
|
|
384
|
+
def size_of_icns(ir)
|
|
385
|
+
file_length = ir.unpack1(4, 4, 'N')
|
|
386
|
+
offset = 8
|
|
387
|
+
|
|
388
|
+
types = []
|
|
389
|
+
while offset < file_length
|
|
390
|
+
type = ir[offset, 4]
|
|
391
|
+
length = ir.unpack1(offset + 4, 4, 'N')
|
|
392
|
+
|
|
393
|
+
case type
|
|
394
|
+
when 'TOC ' # rely on table of contents
|
|
395
|
+
fail FormatError, "TOC length #{length} is not divisible by 8" unless length % 8 == 0
|
|
396
|
+
|
|
397
|
+
types = (1...(length / 8)).map{ |i| ir[offset + (8 * i), 4] }
|
|
398
|
+
break
|
|
399
|
+
when 'icnV', 'info', 'name' # not icons
|
|
400
|
+
when 'slct', 'sbtp', "\375\331/\250" # nested icns
|
|
401
|
+
else
|
|
402
|
+
types << type
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
offset += length
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
types.map do |type|
|
|
409
|
+
next [16, 12] if ICNS_16X12.include?(type)
|
|
410
|
+
|
|
411
|
+
side, scale = ICNS_SQUARE[type]
|
|
412
|
+
fail FormatError, "unknown icon type: #{type.inspect}" unless side
|
|
413
|
+
|
|
414
|
+
(self.class.use_display_pixels ? [side] : [side * scale]) * 2
|
|
415
|
+
end.max
|
|
416
|
+
end
|
|
417
|
+
|
|
354
418
|
def size_of_webp(ir)
|
|
355
419
|
case ir.fetch(12, 4)
|
|
356
420
|
when 'VP8 '
|
|
@@ -368,6 +432,8 @@ private
|
|
|
368
432
|
recurse: %w[jp2h],
|
|
369
433
|
last: %w[jp2h]
|
|
370
434
|
)
|
|
435
|
+
private_constant :JP2_WALKER
|
|
436
|
+
|
|
371
437
|
def size_of_jp2(ir)
|
|
372
438
|
JP2_WALKER.recurse(ir) do |box|
|
|
373
439
|
return ir.unpack(box.data_offset, 8, 'NN').reverse if box.type == 'ihdr'
|
|
@@ -381,6 +447,7 @@ private
|
|
|
381
447
|
|
|
382
448
|
EMF_UMAX = 256**4
|
|
383
449
|
EMF_SMAX = EMF_UMAX / 2
|
|
450
|
+
private_constant :EMF_UMAX, :EMF_SMAX
|
|
384
451
|
|
|
385
452
|
def size_of_emf(ir)
|
|
386
453
|
left, top, right, bottom =
|
|
@@ -400,6 +467,8 @@ private
|
|
|
400
467
|
full: %w[meta hdlr pitm ipma ispe],
|
|
401
468
|
last: %w[meta]
|
|
402
469
|
)
|
|
470
|
+
private_constant :HEIF_WALKER
|
|
471
|
+
|
|
403
472
|
def size_of_heif(ir)
|
|
404
473
|
pitm = nil
|
|
405
474
|
ipma = nil
|
|
@@ -456,6 +525,4 @@ private
|
|
|
456
525
|
end
|
|
457
526
|
alias_method :size_of_avif, :size_of_heif
|
|
458
527
|
alias_method :size_of_heic, :size_of_heif
|
|
459
|
-
|
|
460
|
-
private_constant :SVG_R, :XML_R, :JPEG_CODE_CHECK, :JP2_WALKER, :EMF_UMAX, :EMF_SMAX, :HEIF_WALKER
|
|
461
528
|
end
|
data/spec/image_size_spec.rb
CHANGED
|
@@ -70,16 +70,20 @@ describe ImageSize do
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
def self.test_image_size(path, &block)
|
|
74
|
+
file_size = File.size(path)
|
|
75
|
+
max_file_size = 16_384
|
|
76
|
+
fail "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})" if file_size > max_file_size
|
|
75
77
|
|
|
76
78
|
describe "for #{path}" do
|
|
77
79
|
let(:name){ File.basename(path) }
|
|
80
|
+
let(:match){ name.match(/(?<width>\d+)x(?<height>\d+)(?:@(?<scale>\d)x)?\.(?<format>[^.]+)$/) }
|
|
81
|
+
let(:scale){ (match[:scale] || 1).to_i if match }
|
|
78
82
|
let(:attributes) do
|
|
79
|
-
if
|
|
80
|
-
width = match[
|
|
81
|
-
height = match[
|
|
82
|
-
format = match[
|
|
83
|
+
if match
|
|
84
|
+
width = match[:width].to_i * scale
|
|
85
|
+
height = match[:height].to_i * scale
|
|
86
|
+
format = match[:format].to_sym
|
|
83
87
|
end
|
|
84
88
|
size = [width, height] if format
|
|
85
89
|
media_types = ImageSize::MEDIA_TYPES[format] || []
|
|
@@ -97,15 +101,8 @@ describe ImageSize do
|
|
|
97
101
|
}
|
|
98
102
|
end
|
|
99
103
|
let(:file_data){ File.binread(path) }
|
|
100
|
-
let(:file_size){ file_data.length }
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
max_file_size = 16_384
|
|
104
|
-
|
|
105
|
-
if file_size > max_file_size
|
|
106
|
-
fail "reduce resulting gem size, #{path} is too big (#{file_size} > #{max_file_size})"
|
|
107
|
-
end
|
|
108
|
-
end
|
|
105
|
+
instance_exec(&block) if block
|
|
109
106
|
|
|
110
107
|
context 'given as data' do
|
|
111
108
|
it 'gets format and dimensions' do
|
|
@@ -269,6 +266,32 @@ describe ImageSize do
|
|
|
269
266
|
end
|
|
270
267
|
end
|
|
271
268
|
|
|
269
|
+
context 'for non images' do
|
|
270
|
+
test_image_size 'spec/test_server.rb'
|
|
271
|
+
test_image_size 'spec/images/empty'
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
context 'for images' do
|
|
275
|
+
Dir['spec/images/*/*.*'].each do |path|
|
|
276
|
+
if path.end_with?('.icns')
|
|
277
|
+
context 'without use_display_pixels' do
|
|
278
|
+
test_image_size path
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
context 'with use_display_pixels' do
|
|
282
|
+
before{ ImageSize.use_display_pixels = true }
|
|
283
|
+
after{ ImageSize.use_display_pixels = nil }
|
|
284
|
+
|
|
285
|
+
test_image_size path do
|
|
286
|
+
let(:scale){ 1 }
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
else
|
|
290
|
+
test_image_size path
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
272
295
|
it 'raises ArgumentError if argument is not valid' do
|
|
273
296
|
expect do
|
|
274
297
|
ImageSize.new(Object)
|
|
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.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keisuke Minami
|
|
@@ -53,8 +53,8 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '2.0'
|
|
55
55
|
description: 'Measure following file dimensions: apng, avif, bmp, cur, emf, gif, heic,
|
|
56
|
-
heif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg,
|
|
57
|
-
tiff, webp, xbm, xpm'
|
|
56
|
+
heif, icns, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg,
|
|
57
|
+
swf, tiff, webp, xbm, xpm'
|
|
58
58
|
executables: []
|
|
59
59
|
extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
|
@@ -100,6 +100,11 @@ files:
|
|
|
100
100
|
- spec/images/heif/multiple.169x83.heic
|
|
101
101
|
- spec/images/heif/rotate.73x173.avif
|
|
102
102
|
- spec/images/heif/sequence.7x5.avif
|
|
103
|
+
- spec/images/icns/16x12.icns
|
|
104
|
+
- spec/images/icns/afp_scanpix_leta_pilipey_kyiv.256x256@1x.icns
|
|
105
|
+
- spec/images/icns/reuters_scanpix_leta_garanich_kyiv.256x256@2x.icns
|
|
106
|
+
- spec/images/icns/toc.512x512@1x.icns
|
|
107
|
+
- spec/images/icns/toc.512x512@2x.icns
|
|
103
108
|
- spec/images/ico/32x256.ico
|
|
104
109
|
- spec/images/jp2/163x402.jp2
|
|
105
110
|
- spec/images/jp2/176x373.jpx
|
|
@@ -140,7 +145,7 @@ licenses:
|
|
|
140
145
|
metadata:
|
|
141
146
|
bug_tracker_uri: https://github.com/toy/image_size/issues
|
|
142
147
|
changelog_uri: https://github.com/toy/image_size/blob/master/CHANGELOG.markdown
|
|
143
|
-
documentation_uri: https://www.rubydoc.info/gems/image_size/3.
|
|
148
|
+
documentation_uri: https://www.rubydoc.info/gems/image_size/3.6.0
|
|
144
149
|
source_code_uri: https://github.com/toy/image_size
|
|
145
150
|
rdoc_options: []
|
|
146
151
|
require_paths:
|
|
@@ -178,6 +183,11 @@ test_files:
|
|
|
178
183
|
- spec/images/heif/multiple.169x83.heic
|
|
179
184
|
- spec/images/heif/rotate.73x173.avif
|
|
180
185
|
- spec/images/heif/sequence.7x5.avif
|
|
186
|
+
- spec/images/icns/16x12.icns
|
|
187
|
+
- spec/images/icns/afp_scanpix_leta_pilipey_kyiv.256x256@1x.icns
|
|
188
|
+
- spec/images/icns/reuters_scanpix_leta_garanich_kyiv.256x256@2x.icns
|
|
189
|
+
- spec/images/icns/toc.512x512@1x.icns
|
|
190
|
+
- spec/images/icns/toc.512x512@2x.icns
|
|
181
191
|
- spec/images/ico/32x256.ico
|
|
182
192
|
- spec/images/jp2/163x402.jp2
|
|
183
193
|
- spec/images/jp2/176x373.jpx
|