image_size 1.5.0 → 2.1.1
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 +5 -13
- data/.github/workflows/check.yml +40 -0
- data/.rubocop.yml +81 -0
- data/.rubocop_todo.yml +28 -0
- data/CHANGELOG.markdown +121 -0
- data/GPL +340 -0
- data/Gemfile +3 -1
- data/LICENSE.txt +56 -0
- data/README.markdown +67 -31
- data/image_size.gemspec +12 -4
- data/lib/image_size.rb +169 -84
- data/spec/image_size_spec.rb +77 -50
- data/spec/images/cur/32x256.cur +0 -0
- data/spec/images/ico/32x256.ico +0 -0
- data/spec/images/jp2/163x402.jp2 +0 -0
- data/spec/images/jp2/176x373.jpx +0 -0
- data/spec/images/jp2/224x293.j2c +0 -0
- data/spec/images/jpeg/436x429.jpeg +0 -0
- data/spec/images/jpeg/extraneous-bytes.436x429.jpeg +0 -0
- data/spec/images/mng/61x42.mng +0 -0
- data/spec/images/{apng → png}/192x110.apng +0 -0
- data/spec/images/pnm/22x25.pam +8 -0
- data/spec/images/pnm/22x25.pbm +0 -0
- data/spec/images/pnm/22x25.pgm +4 -0
- data/spec/images/pnm/22x25.ppm +4 -0
- data/spec/images/pnm/ascii.22x25.pbm +27 -0
- data/spec/images/pnm/ascii.22x25.pgm +28 -0
- data/spec/images/pnm/ascii.22x25.ppm +28 -0
- data/spec/images/tiff/big-endian.68x49.tiff +0 -0
- data/spec/images/tiff/little-endian.40x68.tiff +0 -0
- metadata +76 -37
- data/.travis.yml +0 -15
- data/spec/images/cur/50x256.cur +0 -0
- data/spec/images/ico/256x27.ico +0 -0
- data/spec/images/jpeg/320x240.jpeg +0 -0
- data/spec/images/jpeg/extraneous-bytes.320x240.jpeg +0 -0
- data/spec/images/mng/612x132.mng +0 -0
- data/spec/images/pbm/85x55.pbm +0 -0
- data/spec/images/pgm/90x55.pgm +0 -5
- data/spec/images/tiff/48x64.tiff +0 -0
data/Gemfile
CHANGED
data/LICENSE.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Ruby is copyrighted free software by Keisuke Minami <keisuke@rccn.com>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
3
|
+
version 2 (see the file GPL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/README.markdown
CHANGED
@@ -1,55 +1,91 @@
|
|
1
|
+
[](https://rubygems.org/gems/image_size)
|
2
|
+
[](https://github.com/toy/image_size/actions/workflows/check.yml)
|
3
|
+
|
1
4
|
# image_size
|
2
5
|
|
3
6
|
measure image size using pure Ruby
|
4
|
-
formats: `apng`, `bmp`, `cur`, `gif`, `jpeg`, `
|
7
|
+
formats: `apng`, `bmp`, `cur`, `gif`, `ico`, `j2c`, `jp2`, `jpeg`, `jpx`, `mng`, `pam`, `pbm`, `pcx`, `pgm`, `png`, `ppm`, `psd`, `svg`, `swf`, `tiff`, `webp`, `xbm`, `xpm`
|
5
8
|
|
6
|
-
|
9
|
+
## Installation
|
7
10
|
|
8
|
-
|
11
|
+
```sh
|
12
|
+
gem install image_size
|
13
|
+
```
|
9
14
|
|
10
|
-
|
15
|
+
### Bundler
|
11
16
|
|
12
|
-
|
17
|
+
Add to your `Gemfile`:
|
13
18
|
|
14
|
-
|
19
|
+
```ruby
|
20
|
+
gem 'image_size', '~> 2.0'
|
21
|
+
```
|
15
22
|
|
16
23
|
## Usage
|
17
24
|
|
18
|
-
|
19
|
-
|
20
|
-
image_size.width #=> 320
|
21
|
-
image_size.height #=> 240
|
22
|
-
image_size.size #=> [320, 240]
|
25
|
+
```ruby
|
26
|
+
image_size = ImageSize.path('spec/test.jpg')
|
23
27
|
|
24
|
-
|
28
|
+
image_size.format #=> :jpec
|
29
|
+
image_size.width #=> 320
|
30
|
+
image_size.height #=> 240
|
31
|
+
image_size.w #=> 320
|
32
|
+
image_size.h #=> 240
|
33
|
+
image_size.size #=> [320, 240]
|
34
|
+
```
|
25
35
|
|
26
|
-
|
36
|
+
Or using `IO` object:
|
27
37
|
|
28
|
-
|
38
|
+
```ruby
|
39
|
+
image_size = File.open('spec/test.jpg', 'rb'){ |fh| ImageSize.new(fh) }
|
40
|
+
```
|
29
41
|
|
30
|
-
|
42
|
+
Any object responding to `read` and `eof?`:
|
31
43
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
44
|
+
```ruby
|
45
|
+
require 'image_size'
|
35
46
|
|
47
|
+
image_size = ImageSize.new(ARGF)
|
48
|
+
```
|
36
49
|
|
37
|
-
|
38
|
-
require 'open-uri'
|
50
|
+
Works with `open-uri` if needed:
|
39
51
|
|
40
|
-
|
41
|
-
|
42
|
-
|
52
|
+
```ruby
|
53
|
+
require 'image_size'
|
54
|
+
require 'open-uri'
|
43
55
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
56
|
+
image_size = URI.parse('http://www.rubycgi.org/image/ruby_gtk_book_title.jpg').open('rb') do |fh|
|
57
|
+
ImageSize.new(fh)
|
58
|
+
end
|
48
59
|
|
49
|
-
|
60
|
+
image_size = open('http://www.rubycgi.org/image/ruby_gtk_book_title.jpg', 'rb') do |fh|
|
61
|
+
ImageSize.new(fh)
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
Note that starting with version `2.0.0` the object given to `ImageSize` will not be rewound before or after use.
|
66
|
+
So rewind if needed before passing to `ImageSize` and/or rewind after passing to `ImageSize` before reading data.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
require 'image_size'
|
50
70
|
|
51
|
-
|
71
|
+
File.open('spec/test.jpg', 'rb') do |fh|
|
72
|
+
image_size = ImageSize.new(fh)
|
73
|
+
|
74
|
+
fh.rewind
|
75
|
+
data = fh.read
|
76
|
+
end
|
77
|
+
|
78
|
+
File.open('spec/test.jpg', 'rb') do |fh|
|
79
|
+
data = fh.read
|
80
|
+
fh.rewind
|
81
|
+
|
82
|
+
image_size = ImageSize.new(fh)
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
## Licence
|
52
87
|
|
53
|
-
|
88
|
+
This code is free to use under the terms of the [Ruby's licence](LICENSE.txt).
|
54
89
|
|
55
|
-
Original author:
|
90
|
+
Original author: Keisuke Minami <keisuke@rccn.com>.\
|
91
|
+
Further development 2010-2020 Ivan Kuchin https://github.com/toy/image_size
|
data/image_size.gemspec
CHANGED
@@ -2,14 +2,19 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_size'
|
5
|
-
s.version = '1.
|
5
|
+
s.version = '2.1.1'
|
6
6
|
s.summary = %q{Measure image size using pure Ruby}
|
7
|
-
s.description = %q{Measure following file dimensions: apng, bmp, cur, gif, jpeg,
|
8
|
-
s.homepage = "
|
7
|
+
s.description = %q{Measure following file dimensions: apng, bmp, cur, gif, ico, j2c, jp2, jpeg, jpx, mng, pam, pbm, pcx, pgm, png, ppm, psd, svg, swf, tiff, webp, xbm, xpm}
|
8
|
+
s.homepage = "https://github.com/toy/#{s.name}"
|
9
9
|
s.authors = ['Keisuke Minami', 'Ivan Kuchin']
|
10
10
|
s.license = 'Ruby'
|
11
11
|
|
12
|
-
s.
|
12
|
+
s.metadata = {
|
13
|
+
'bug_tracker_uri' => "https://github.com/toy/#{s.name}/issues",
|
14
|
+
'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
|
15
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/#{s.name}/#{s.version}",
|
16
|
+
'source_code_uri' => "https://github.com/toy/#{s.name}",
|
17
|
+
}
|
13
18
|
|
14
19
|
s.files = `git ls-files`.split("\n")
|
15
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -17,4 +22,7 @@ Gem::Specification.new do |s|
|
|
17
22
|
s.require_paths = %w[lib]
|
18
23
|
|
19
24
|
s.add_development_dependency 'rspec', '~> 3.0'
|
25
|
+
if RUBY_VERSION >= '2.4'
|
26
|
+
s.add_development_dependency 'rubocop', '~> 1.0'
|
27
|
+
end
|
20
28
|
end
|
data/lib/image_size.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
#
|
2
|
-
#
|
1
|
+
# encoding: BINARY
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require 'stringio'
|
4
|
-
require 'tempfile'
|
5
5
|
|
6
|
+
# Determine image format and size
|
6
7
|
class ImageSize
|
7
8
|
class FormatError < StandardError; end
|
8
9
|
|
10
|
+
# Array joining with 'x'
|
9
11
|
class Size < Array
|
10
12
|
# join using 'x'
|
11
13
|
def to_s
|
@@ -15,34 +17,26 @@ class ImageSize
|
|
15
17
|
|
16
18
|
class ImageReader # :nodoc:
|
17
19
|
attr_reader :data
|
20
|
+
|
18
21
|
def initialize(data_or_io)
|
19
|
-
@io =
|
20
|
-
when IO, StringIO, Tempfile
|
21
|
-
data_or_io.dup.tap(&:rewind)
|
22
|
-
when String
|
22
|
+
@io = if data_or_io.is_a?(String)
|
23
23
|
StringIO.new(data_or_io)
|
24
|
+
elsif data_or_io.respond_to?(:read) && data_or_io.respond_to?(:eof?)
|
25
|
+
data_or_io
|
24
26
|
else
|
25
|
-
raise ArgumentError
|
27
|
+
raise ArgumentError, "expected data as String or an object responding to read and eof?, got #{data_or_io.class}"
|
26
28
|
end
|
27
|
-
@
|
28
|
-
@data = ''
|
29
|
-
end
|
30
|
-
|
31
|
-
def close
|
32
|
-
@io.rewind
|
33
|
-
@io.close if IO === @io
|
29
|
+
@data = String.new # not frozen
|
34
30
|
end
|
35
31
|
|
36
32
|
CHUNK = 1024
|
37
33
|
def [](offset, length)
|
38
|
-
while offset + length
|
39
|
-
@read
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@data << data
|
45
|
-
end
|
34
|
+
while !@io.eof? && @data.length < offset + length
|
35
|
+
data = @io.read(CHUNK)
|
36
|
+
break unless data
|
37
|
+
|
38
|
+
data.force_encoding(@data.encoding) if data.respond_to?(:encoding)
|
39
|
+
@data << data
|
46
40
|
end
|
47
41
|
@data[offset, length]
|
48
42
|
end
|
@@ -50,7 +44,7 @@ class ImageSize
|
|
50
44
|
|
51
45
|
# Given path to image finds its format, width and height
|
52
46
|
def self.path(path)
|
53
|
-
open(path, 'rb'){ |f| new(f) }
|
47
|
+
File.open(path, 'rb'){ |f| new(f) }
|
54
48
|
end
|
55
49
|
|
56
50
|
# Used for svg
|
@@ -63,13 +57,13 @@ class ImageSize
|
|
63
57
|
@dpi = dpi.to_f
|
64
58
|
end
|
65
59
|
|
66
|
-
# Given image as
|
60
|
+
# Given image as any class responding to read and eof? or data as String, finds its format and dimensions
|
67
61
|
def initialize(data)
|
68
62
|
ir = ImageReader.new(data)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
ir
|
63
|
+
@format = detect_format(ir)
|
64
|
+
return unless @format
|
65
|
+
|
66
|
+
@width, @height = send("size_of_#{@format}", ir)
|
73
67
|
end
|
74
68
|
|
75
69
|
# Image format
|
@@ -77,11 +71,11 @@ class ImageSize
|
|
77
71
|
|
78
72
|
# Image width
|
79
73
|
attr_reader :width
|
80
|
-
|
74
|
+
alias_method :w, :width
|
81
75
|
|
82
76
|
# Image height
|
83
77
|
attr_reader :height
|
84
|
-
|
78
|
+
alias_method :h, :height
|
85
79
|
|
86
80
|
# get image width and height as an array which to_s method returns "#{width}x#{height}"
|
87
81
|
def size
|
@@ -90,29 +84,29 @@ class ImageSize
|
|
90
84
|
|
91
85
|
private
|
92
86
|
|
93
|
-
SVG_R = /<svg\b([^>]*)
|
87
|
+
SVG_R = /<svg\b([^>]*)>/.freeze
|
88
|
+
XML_R = /<\?xml|<!--/.freeze
|
94
89
|
def detect_format(ir)
|
95
90
|
head = ir[0, 1024]
|
96
91
|
case
|
97
|
-
when head[0, 6] =~ /GIF8[79]a/
|
98
|
-
when head[0, 8] == "\211PNG\r\n\032\n"
|
99
|
-
when head[0, 8] == "\212MNG\r\n\032\n"
|
100
|
-
when head[0, 2] == "\377\330"
|
101
|
-
when head[0, 2] == 'BM'
|
102
|
-
when head[0,
|
103
|
-
when head =~ /\#define\s+\S+\s+\d+/
|
104
|
-
when head[0, 4]
|
105
|
-
when head
|
106
|
-
when head
|
107
|
-
when head[0,
|
108
|
-
when head[0,
|
109
|
-
when head[
|
110
|
-
|
111
|
-
|
112
|
-
when head[0,
|
113
|
-
when head[0, 12]
|
114
|
-
when head[0, 4] == "\
|
115
|
-
when head[0, 4] == "\000\000\002\000" then :cur
|
92
|
+
when head[0, 6] =~ /GIF8[79]a/ then :gif
|
93
|
+
when head[0, 8] == "\211PNG\r\n\032\n" then detect_png_type(ir)
|
94
|
+
when head[0, 8] == "\212MNG\r\n\032\n" then :mng
|
95
|
+
when head[0, 2] == "\377\330" then :jpeg
|
96
|
+
when head[0, 2] == 'BM' then :bmp
|
97
|
+
when head[0, 3] =~ /P[1-6]\s|P7\n/ then detect_pnm_type(ir)
|
98
|
+
when head =~ /\#define\s+\S+\s+\d+/ then :xbm
|
99
|
+
when %W[II*\0 MM\0*].include?(head[0, 4]) then :tiff
|
100
|
+
when head =~ %r{/\* XPM \*/} then :xpm
|
101
|
+
when head[0, 4] == '8BPS' then :psd
|
102
|
+
when head[0, 3] =~ /[FC]WS/ then :swf
|
103
|
+
when head =~ SVG_R || (head =~ XML_R && ir[0, 4096][SVG_R]) then :svg
|
104
|
+
when head[0, 2] =~ /\n[\0-\5]/ then :pcx
|
105
|
+
when head[0, 12] =~ /RIFF(?m:....)WEBP/ then :webp
|
106
|
+
when head[0, 4] == "\0\0\1\0" then :ico
|
107
|
+
when head[0, 4] == "\0\0\2\0" then :cur
|
108
|
+
when head[0, 12] == "\0\0\0\fjP \r\n\207\n" then detect_jpeg2000_type(ir)
|
109
|
+
when head[0, 4] == "\377O\377Q" then :j2c
|
116
110
|
end
|
117
111
|
end
|
118
112
|
|
@@ -129,6 +123,26 @@ private
|
|
129
123
|
:png
|
130
124
|
end
|
131
125
|
|
126
|
+
def detect_pnm_type(ir)
|
127
|
+
case ir[0, 2]
|
128
|
+
when 'P1', 'P4' then :pbm
|
129
|
+
when 'P2', 'P5' then :pgm
|
130
|
+
when 'P3', 'P6' then :ppm
|
131
|
+
when 'P7' then :pam
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def detect_jpeg2000_type(ir)
|
136
|
+
return unless ir[16, 4] == 'ftyp'
|
137
|
+
|
138
|
+
# using xl-box would be weird, but doesn't seem to contradict specification
|
139
|
+
skip = ir[12, 4] == "\0\0\0\1" ? 16 : 8
|
140
|
+
case ir[12 + skip, 4]
|
141
|
+
when 'jp2 ' then :jp2
|
142
|
+
when 'jpx ' then :jpx
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
132
146
|
def size_of_gif(ir)
|
133
147
|
ir[6, 4].unpack('vv')
|
134
148
|
end
|
@@ -137,6 +151,7 @@ private
|
|
137
151
|
unless ir[12, 4] == 'MHDR'
|
138
152
|
raise FormatError, 'MHDR not in place for MNG'
|
139
153
|
end
|
154
|
+
|
140
155
|
ir[16, 8].unpack('NN')
|
141
156
|
end
|
142
157
|
|
@@ -144,16 +159,17 @@ private
|
|
144
159
|
unless ir[12, 4] == 'IHDR'
|
145
160
|
raise FormatError, 'IHDR not in place for PNG'
|
146
161
|
end
|
162
|
+
|
147
163
|
ir[16, 8].unpack('NN')
|
148
164
|
end
|
149
165
|
alias_method :size_of_apng, :size_of_png
|
150
166
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
]
|
167
|
+
JPEG_CODE_CHECK = %W[
|
168
|
+
\xC0 \xC1 \xC2 \xC3
|
169
|
+
\xC5 \xC6 \xC7
|
170
|
+
\xC9 \xCA \xCB
|
171
|
+
\xCD \xCE \xCF
|
172
|
+
].freeze
|
157
173
|
def size_of_jpeg(ir)
|
158
174
|
section_marker = "\xFF"
|
159
175
|
offset = 2
|
@@ -162,12 +178,13 @@ private
|
|
162
178
|
offset += 1 until section_marker != ir[offset + 1, 1]
|
163
179
|
raise FormatError, 'EOF in JPEG' if ir[offset, 1].nil?
|
164
180
|
|
165
|
-
|
181
|
+
_marker, code, length = ir[offset, 4].unpack('aan')
|
166
182
|
offset += 4
|
167
183
|
|
168
|
-
if
|
184
|
+
if JPEG_CODE_CHECK.include?(code)
|
169
185
|
return ir[offset + 1, 4].unpack('nn').reverse
|
170
186
|
end
|
187
|
+
|
171
188
|
offset += length - 2
|
172
189
|
end
|
173
190
|
end
|
@@ -191,12 +208,38 @@ private
|
|
191
208
|
header = ir[0, 1024]
|
192
209
|
header.gsub!(/^\#[^\n\r]*/m, '')
|
193
210
|
header =~ /^(P[1-6])\s+?(\d+)\s+?(\d+)/m
|
194
|
-
case $1
|
195
|
-
when 'P1', 'P4' then @format = :pbm
|
196
|
-
when 'P2', 'P5' then @format = :pgm
|
197
|
-
end
|
198
211
|
[$2.to_i, $3.to_i]
|
199
212
|
end
|
213
|
+
alias_method :size_of_pbm, :size_of_ppm
|
214
|
+
alias_method :size_of_pgm, :size_of_ppm
|
215
|
+
|
216
|
+
def size_of_pam(ir)
|
217
|
+
width = height = nil
|
218
|
+
offset = 3
|
219
|
+
loop do
|
220
|
+
if ir[offset, 1] == '#'
|
221
|
+
offset += 1 until ["\n", '', nil].include?(ir[offset, 1])
|
222
|
+
offset += 1
|
223
|
+
else
|
224
|
+
chunk = ir[offset, 32]
|
225
|
+
case chunk
|
226
|
+
when /\AWIDTH (\d+)\n/
|
227
|
+
width = $1.to_i
|
228
|
+
when /\AHEIGHT (\d+)\n/
|
229
|
+
height = $1.to_i
|
230
|
+
when /\AENDHDR\n/
|
231
|
+
break
|
232
|
+
when /\A(?:DEPTH|MAXVAL) \d+\n/, /\ATUPLTYPE \S+\n/
|
233
|
+
# ignore
|
234
|
+
else
|
235
|
+
raise FormatError, "Unexpected data in PAM header: #{chunk.inspect}"
|
236
|
+
end
|
237
|
+
offset += $&.length
|
238
|
+
break if width && height
|
239
|
+
end
|
240
|
+
end
|
241
|
+
[width, height]
|
242
|
+
end
|
200
243
|
|
201
244
|
def size_of_xbm(ir)
|
202
245
|
ir[0, 1024] =~ /^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)/mi
|
@@ -209,6 +252,7 @@ private
|
|
209
252
|
if data.length != length
|
210
253
|
raise FormatError, 'XPM size not found'
|
211
254
|
end
|
255
|
+
|
212
256
|
length += 1024
|
213
257
|
end
|
214
258
|
[$1.to_i, $2.to_i]
|
@@ -219,7 +263,7 @@ private
|
|
219
263
|
end
|
220
264
|
|
221
265
|
def size_of_tiff(ir)
|
222
|
-
endian2b =
|
266
|
+
endian2b = ir[0, 4] == "II*\000" ? 'v' : 'n'
|
223
267
|
endian4b = endian2b.upcase
|
224
268
|
packspec = [nil, 'C', nil, endian2b, endian4b, nil, 'c', nil, endian2b, endian4b]
|
225
269
|
|
@@ -232,17 +276,18 @@ private
|
|
232
276
|
until width && height
|
233
277
|
ifd = ir[offset, 12]
|
234
278
|
raise FormatError, 'Reached end of directory entries in TIFF' if ifd.nil? || offset > num_dirent
|
279
|
+
|
235
280
|
tag, type = ifd.unpack(endian2b * 2)
|
236
281
|
offset += 12
|
237
282
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
283
|
+
next if packspec[type].nil?
|
284
|
+
|
285
|
+
value = ifd[8, 4].unpack(packspec[type])[0]
|
286
|
+
case tag
|
287
|
+
when 0x0100
|
288
|
+
width = value
|
289
|
+
when 0x0101
|
290
|
+
height = value
|
246
291
|
end
|
247
292
|
end
|
248
293
|
[width, height]
|
@@ -257,7 +302,7 @@ private
|
|
257
302
|
value_bit_length = ir[8, 1].unpack('B5').first.to_i(2)
|
258
303
|
bit_length = 5 + value_bit_length * 4
|
259
304
|
rect_bits = ir[8, bit_length / 8 + 1].unpack("B#{bit_length}").first
|
260
|
-
values = rect_bits.unpack(
|
305
|
+
values = rect_bits[5..-1].unpack("a#{value_bit_length}" * 4).map{ |bits| bits.to_i(2) }
|
261
306
|
x_min, x_max, y_min, y_max = values
|
262
307
|
[(x_max - x_min) / 20, (y_max - y_min) / 20]
|
263
308
|
end
|
@@ -269,18 +314,18 @@ private
|
|
269
314
|
end
|
270
315
|
dpi = self.class.dpi
|
271
316
|
[attributes['width'], attributes['height']].map do |length|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
pixels.round if pixels
|
317
|
+
next unless length
|
318
|
+
|
319
|
+
pixels = case length.downcase.strip[/(?:em|ex|px|in|cm|mm|pt|pc|%)\z/]
|
320
|
+
when 'em', 'ex', '%' then nil
|
321
|
+
when 'in' then length.to_f * dpi
|
322
|
+
when 'cm' then length.to_f * dpi / 2.54
|
323
|
+
when 'mm' then length.to_f * dpi / 25.4
|
324
|
+
when 'pt' then length.to_f * dpi / 72
|
325
|
+
when 'pc' then length.to_f * dpi / 6
|
326
|
+
else length.to_f
|
283
327
|
end
|
328
|
+
pixels.round if pixels
|
284
329
|
end
|
285
330
|
end
|
286
331
|
|
@@ -301,4 +346,44 @@ private
|
|
301
346
|
[(w16 | w8 << 16) + 1, (h16 | h8 << 16) + 1]
|
302
347
|
end
|
303
348
|
end
|
349
|
+
|
350
|
+
def size_of_jp2(ir)
|
351
|
+
offset = 12
|
352
|
+
stop = nil
|
353
|
+
in_header = false
|
354
|
+
loop do
|
355
|
+
break if stop && offset >= stop
|
356
|
+
break if ir[offset, 4] == '' || ir[offset, 4].nil?
|
357
|
+
|
358
|
+
size = ir[offset, 4].unpack('N')[0]
|
359
|
+
type = ir[offset + 4, 4]
|
360
|
+
|
361
|
+
data_offset = 8
|
362
|
+
case size
|
363
|
+
when 1
|
364
|
+
size = ir[offset, 8].unpack('Q>')[0]
|
365
|
+
data_offset = 16
|
366
|
+
raise FormatError, "Unexpected xl-box size #{size}" if (1..15).include?(size)
|
367
|
+
when 2..7
|
368
|
+
raise FormatError, "Reserved box size #{size}"
|
369
|
+
end
|
370
|
+
|
371
|
+
if type == 'jp2h'
|
372
|
+
stop = offset + size unless size.zero?
|
373
|
+
offset += data_offset
|
374
|
+
in_header = true
|
375
|
+
elsif in_header && type == 'ihdr'
|
376
|
+
return ir[offset + data_offset, 8].unpack('NN').reverse
|
377
|
+
else
|
378
|
+
break if size.zero? # box to the end of file
|
379
|
+
|
380
|
+
offset += size
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
alias_method :size_of_jpx, :size_of_jp2
|
385
|
+
|
386
|
+
def size_of_j2c(ir)
|
387
|
+
ir[8, 8].unpack('NN')
|
388
|
+
end
|
304
389
|
end
|