identify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/CHANGELOG +4 -0
  2. data/README.md +36 -0
  3. data/lib/identify.rb +175 -0
  4. metadata +64 -0
data/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ == 0.1.0 (2012-06-07)
2
+
3
+ * Initial version.
4
+
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Identify
2
+
3
+ A pure ruby image identification library.
4
+
5
+ ## Supported Formats
6
+
7
+ * BMP
8
+ * PNG
9
+ * JPG
10
+ * GIF
11
+ * XPM
12
+ * PPM
13
+ * PGM
14
+ * PNM
15
+ * PBM
16
+ * XBM
17
+ * TIFF
18
+
19
+ ## Example
20
+
21
+ ```ruby
22
+ require 'identify'
23
+
24
+ Identify.image File.binread("test/images/test.png", 1024) #=> {:format => "png", :width => 253, :height => 178}
25
+
26
+ ```
27
+
28
+ ## Testing
29
+
30
+ Tests require a local installation of ImageMagick and `identify` to be in your `$PATH`
31
+
32
+ # See Also
33
+ [http://rubygems.org/gems/imagesize](http://rubygems.org/gems/imagesize)
34
+
35
+ # License
36
+ [Creative Commons Attribution - CC BY](http://creativecommons.org/licenses/by/3.0)
data/lib/identify.rb ADDED
@@ -0,0 +1,175 @@
1
+ module Identify
2
+ VERSION = '0.1.0'
3
+
4
+ def self.image data
5
+ Image.identify(data)
6
+ end
7
+
8
+ class Image
9
+ def self.formats
10
+ @formats ||= []
11
+ end
12
+
13
+ def self.inherited klass
14
+ formats << klass
15
+ end
16
+
17
+ def self.identify data
18
+ format = formats.find {|klass| klass.handle? data}
19
+ format ? format.parse(data) : {}
20
+ end
21
+
22
+ def self.parse data
23
+ new.parse data
24
+ end
25
+
26
+ def as_hash format, width, height
27
+ {width: width.to_i, height: height.to_i, format: format}
28
+ end
29
+
30
+ class XPM < Image
31
+ def self.handle? data
32
+ %r{\A#{Regexp.escape('/* XPM */')}}.match(data)
33
+ end
34
+
35
+ def parse data
36
+ as_hash 'xpm', *data.scan(%r{"(\d+)\s+(\d+)(?=[\s\d]+")}).flatten
37
+ end
38
+ end
39
+
40
+ class PCX < Image
41
+ def self.handle? data
42
+ data[0].ord == 10
43
+ end
44
+
45
+ def parse data
46
+ header = data.unpack('C4S4')
47
+ as_hash 'pcx', header[6] - header[4] + 1, header[7] - header[5] + 1
48
+ end
49
+ end # PCX
50
+
51
+ class TIFF < Image
52
+ def self.handle? data
53
+ data[0..3] == "MM\x00\x2a" || data[0..3] == "II\x2a\x00"
54
+ end
55
+
56
+ def parse data
57
+ endian = data[0..1] == "MM" ? 'n' : 'v'
58
+ # IFD offset
59
+ offset = data.unpack("x4#{endian.upcase}").first
60
+ # Don't have enough header data
61
+ return {} if data.bytesize < offset + 14
62
+
63
+ nrec = data[offset, 2].unpack(endian).first
64
+ offset += 2
65
+ height = nil
66
+ width = nil
67
+ types = [nil, 'C', nil, endian, endian.upcase]
68
+
69
+ while nrec > 0 && data.bytesize > offset + 12
70
+ ifd = data[offset, 12]
71
+ type = ifd.unpack("x2#{endian}").first
72
+ case ifd.unpack(endian).first
73
+ when 0x0100
74
+ width = ifd[8, 4].unpack(types[type]).first
75
+ when 0x0101
76
+ height = ifd[8, 4].unpack(types[type]).first
77
+ end
78
+ nrec -= 1
79
+ offset += 12
80
+
81
+ break if width && height
82
+ end
83
+ as_hash 'tiff', width, height
84
+ end
85
+ end # TIFF
86
+
87
+ class BMP < Image
88
+ def self.handle? data
89
+ %r{\ABM}.match(data)
90
+ end
91
+
92
+ def parse data
93
+ as_hash 'bmp', *data.unpack("x18VV")
94
+ end
95
+ end # BMP
96
+
97
+ class PBM < Image
98
+ def self.handle? data
99
+ %r{\AP[1-6]}.match(data)
100
+ end
101
+
102
+ def parse data
103
+ type, dims = data[0..4096].split(/\n+/).reject {|line| %r{\A#}.match(line)}.take(2)
104
+ as_hash format(type), *dims.split(/\s+/)
105
+ end
106
+
107
+ def format type
108
+ case type
109
+ when 'P1', 'P4' then 'pbm'
110
+ when 'P2', 'P5' then 'pgm'
111
+ when 'P3', 'P6' then 'ppm'
112
+ end
113
+ end
114
+ end # PBM
115
+
116
+ class XBM < Image
117
+ def self.handle? data
118
+ %r{\A#define\s+.*width}i.match(data)
119
+ end
120
+
121
+ def parse data
122
+ as_hash 'xbm', *data.scan(%r{^#define\s+.*?_(?:width|height)\s+(\d+)}).flatten
123
+ end
124
+ end
125
+
126
+ class GIF < Image
127
+ def self.handle? data
128
+ data.index("GIF89a") == 0 || data.index("GIF87a")
129
+ end
130
+
131
+ def parse data
132
+ as_hash('gif', *data.unpack("x6vv"))
133
+ end
134
+ end # GIF
135
+
136
+ class PNG < Image
137
+ def self.handle? data
138
+ data[0..7] == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" # png 8-byte signature
139
+ end
140
+
141
+ def parse data
142
+ {}.tap do |meta|
143
+ meta.merge! as_hash('png', *data.unpack("x16NN")) if data.index("IHDR") == 12
144
+ end
145
+ end
146
+ end # PNG
147
+
148
+ class JFIF < Image
149
+ # http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#SOF
150
+ SOF = [0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf]
151
+
152
+ def self.handle? data
153
+ data.index("JFIF", 4) == 6
154
+ end
155
+
156
+ def parse data
157
+ index = 4
158
+ block = data[index].ord * 256 + data[index + 1].ord
159
+
160
+ {}.tap do |meta|
161
+ while (index += block) < data.size
162
+ break unless data[index].ord == 0xff
163
+ if SOF.include?(data[index + 1].ord)
164
+ meta.merge! as_hash('jpeg', *data.unpack("x#{index + 5}nn").reverse)
165
+ break
166
+ else
167
+ index += 2
168
+ block = data[index].ord * 256 + data[index + 1].ord
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end # JFIF
174
+ end # Image
175
+ end # Identify
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: identify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bharanee Rathna
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Image identification in pure Ruby
31
+ email:
32
+ - deepfryed@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/identify.rb
38
+ - README.md
39
+ - CHANGELOG
40
+ homepage: http://github.com/deepfryed/identify
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.24
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Identify image types and dimensions
64
+ test_files: []