identify 0.1.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.
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: []