cul_image_props 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,22 +1,23 @@
1
1
  module Cul
2
2
  module Image
3
3
  module Magic
4
- BMP = "\x42\x4D"
5
- GIF = "\x47\x49\x46\x38"
6
- JPEG = "\xFF\xD8"
7
- JPEG_FRAME_MARKERS = ["\xFF\xC0","\xFF\xC1","\xFF\xC2","\xFF\xC5","\xFF\xC6","\xFF\xC9","\xFF\xCA","\xFF\xCD","\xFF\xCE"]
4
+ BMP = [0x42, 0x4d] # "\x42\x4D"
5
+ GIF = [0x47,0x49,0x46,0x38] # "\x47\x49\x46\x38"
6
+ JPEG = [0xff,0xd8] # "\xFF\xD8"
7
+ JFM_BYTES = [[0xff,0xc0],[0xff,0xc1],[0xff,0xc2],[0xff,0xc5],[0xff,0xc6],[0xff,0xc9],[0xff,0xca],[0xff,0xcd],[0xff,0xce]]
8
+ JPEG_FRAME_MARKERS = JFM_BYTES.collect {|bytes| bytes.pack('C*')}
8
9
  JPEG_SEGMENTS = {
9
- "\xFF\xE0"=>"APP0",
10
- "\xFF\xE1"=>"APP1",
11
- "\xFF\xC1"=>"SOF1", # image data: extended sequential dct
12
- "\xFF\xC2"=>"SOF2", # image data: progressive dct
13
- "\xFF\xC4"=>"DHT", # image data: huffman table(s)
14
- "\xFF\xDA"=>"SOS", # image data: start of scan
15
- "\xFF\xDB"=>"DQT" # quantization tables
10
+ [0xFF,0xE0]=>"APP0",
11
+ [0xFF,0xE1]=>"APP1",
12
+ [0xFF,0xC1]=>"SOF1", # image data: extended sequential dct
13
+ [0xFF,0xC2]=>"SOF2", # image data: progressive dct
14
+ [0xFF,0xC4]=>"DHT", # image data: huffman table(s)
15
+ [0xFF,0xDA]=>"SOS", # image data: start of scan
16
+ [0xFF,0xDB]=>"DQT" # quantization tables
16
17
  }
17
- PNG = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
18
- TIFF_MOTOROLA_BE = "\x4d\x4d\x00\x2a"
19
- TIFF_INTEL_LE = "\x49\x49\x2a\x00"
18
+ PNG = [0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a] # "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
19
+ TIFF_MOTOROLA_BE = [0x4d,0x4d,0x00,0x2a] # "\x4d\x4d\x00\x2a"
20
+ TIFF_INTEL_LE = [0x49,0x49,0x2a,0x00] # "\x49\x49\x2a\x00"
20
21
  end
21
22
  end
22
23
  end
@@ -6,6 +6,14 @@ module Image
6
6
  module Properties
7
7
  module Exif
8
8
 
9
+ # return the value that should be compared to stringvalue[Fixnum] for a given number 'h'
10
+ def self.hex_comparison_value(h)
11
+ ("X".respond_to? :ord) ? [h].pack('C') : h
12
+ end
13
+ XFF = hex_comparison_value(0xff)
14
+ X00 = hex_comparison_value(0x00)
15
+ X01 = hex_comparison_value(0x01)
16
+ X02 = hex_comparison_value(0x02)
9
17
 
10
18
  # process an image file (expects an open file object)
11
19
  # this is the function that has to deal with all the arbitrary nasty bits
@@ -23,70 +31,76 @@ def self.process_file(f, opts={})
23
31
  # determine whether it's a JPEG or TIFF
24
32
  data = f.read(12)
25
33
  jfif = {}
26
- if [Cul::Image::Magic::TIFF_MOTOROLA_BE, Cul::Image::Magic::TIFF_INTEL_LE].include? data[0, 4]
34
+ if [Cul::Image::Magic::TIFF_MOTOROLA_BE, Cul::Image::Magic::TIFF_INTEL_LE].include? data[0, 4].unpack('C*')
27
35
  f.seek(0)
28
36
  endian = data[0,1]
29
37
  offset = 0
30
- elsif data[0,2] == Cul::Image::Magic::JPEG
38
+ elsif data[0,2].unpack('C*') == Cul::Image::Magic::JPEG
31
39
  # it's a JPEG file
32
40
  base = 0
33
- while data[2] == "\xFF" and ['JFIF', 'JFXX', 'OLYM', 'Phot'].include? data[6, 4]
41
+ while data[2] == XFF and ['JFIF', 'JFXX', 'OLYM', 'Phot'].include? data[6, 4]
34
42
  length = data[4,2].unpack('n')[0]
35
43
  f.read(length-8)
36
44
  # fake an EXIF beginning of file
37
- data = "\xFF\x00"+f.read(10)
45
+ data = [0xff,0x00].pack('C*') + f.read(10)
38
46
  fake_exif = 1
39
47
  base = base + length
40
48
  end
41
49
  # Big ugly patch to deal with APP2 (or other) data coming before APP1
42
50
  f.seek(0)
43
- data = f.read(base+8000) # in theory, this could be insufficient --gd
51
+ flen = base+8000
52
+ data = f.read(flen) # in theory, this could be insufficient --gd
44
53
 
45
54
  base = 2
46
55
  fptr = base
47
56
  while true
48
- if data[fptr,2]=="\xFF\xE1"
57
+ if data[fptr,2].unpack('C*') == [0xff,0xe1]
49
58
  if data[fptr+4,4] == "Exif"
50
59
  base = fptr-2
51
60
  break
52
61
  end
53
62
  fptr += 2
54
63
  fptr += data[fptr+2,2].unpack('n')[0]
55
- elsif data[fptr,2]=="\xFF\xE2"
64
+ elsif data[fptr,2].unpack('C*') == [0xff,0xe2]
56
65
  fptr += 2
57
66
  fptr += data[fptr+2,2].unpack('n')[0]
58
- elsif data[fptr,2]=="\xFF\xE0"
67
+ elsif data[fptr,2].unpack('C*') == [0xff,0xe0]
59
68
  offset = fptr
60
69
  fptr += 2
61
70
  fptr += data[fptr,2].unpack('n')[0]
62
-
63
- exif = EXIF_TAGS[0x0128]
71
+ if data.length < offset + 16
72
+ lack = (offset + 16 - data.length)
73
+ data.concat f.read(lack)
74
+ end
75
+ exif = EXIF_TAGS[0x0128] # resolution unit
64
76
  label = 'Image ' + exif.name
65
- if (data[offset+11] == 0x00)
66
- jfif[label] = IFD_Tag.new(exif.value[1],label,3,[0],offset+11,1)
67
- elsif (data[offset+11] == 0x01)
68
- jfif[label] = IFD_Tag.new(exif.value[2],label,3,[0],offset+11,1)
69
- elsif (data[offset+11] == 0x02)
70
- jfif[label] = IFD_Tag.new(exif.value[3],label,3,[0],offset+11,1)
77
+ if (data[offset+11] == X00)
78
+ jfif[label] = IFD_Tag.new(exif.value[1],label,3,[0],offset+11,1)
79
+ elsif (data[offset+11] == X01)
80
+ jfif[label] = IFD_Tag.new(exif.value[2],label,3,[0],offset+11,1)
81
+ elsif (data[offset+11] == X02)
82
+ jfif[label] = IFD_Tag.new(exif.value[3],label,3,[0],offset+11,1)
71
83
  else
72
- jfif[label] = IFD_Tag.new("Unknown",label,3,[0],offset+11,1)
84
+ puts "Unknown jfif tag: #{data[offset+11]}"
85
+ jfif[label] = IFD_Tag.new("Unknown",label,3,[0],offset+11,1)
73
86
  end
74
87
  xres= data[offset+12,2].unpack('n')[0]
75
88
  yres= data[offset+14,2].unpack('n')[0]
76
- exif = EXIF_TAGS[0x011a]
89
+ exif = EXIF_TAGS[0x011a] # x resolution
77
90
  label = 'Image ' + EXIF_TAGS[0x011a].name
78
91
  jfif[label] = IFD_Tag.new(xres.to_s,label,5,[xres],offset+12,2)
79
- label = 'Image ' + EXIF_TAGS[0x011b].name
92
+ label = 'Image ' + EXIF_TAGS[0x011b].name # y resolution
80
93
  jfif[label] = IFD_Tag.new(yres.to_s,label,5,[yres],offset+13,2)
81
94
  else
82
95
  if(data.length < fptr + 2)
83
96
  break
84
97
  end
85
98
  # scan for the next APP header, /\xFF(^[\x00])/
86
- _next = data.index("\xff",fptr + 2)
99
+ ff = [0xff].pack('C')
100
+ _next = data.index(ff,fptr + 2)
87
101
  _next = nil if !_next.nil? and _next + 2 >= data.length
88
- while (!_next.nil? and data[_next + 1] == 0x00)
89
- _next = data.index("\xff",_next + 2)
102
+ while (!_next.nil? and data[_next + 1] == X00)
103
+ _next = data.index(ff,_next + 2)
90
104
  _next = nil if !_next.nil? and _next + 2 >= data.length
91
105
  end
92
106
  unless (_next.nil?)
@@ -97,7 +111,7 @@ def self.process_file(f, opts={})
97
111
  end
98
112
  end
99
113
  f.seek(base+12)
100
- if data[2+base] == 0xFF and data[6+base, 4] == 'Exif'
114
+ if data[2+base] == XFF and data[6+base, 4] == 'Exif'
101
115
  # detected EXIF header
102
116
  offset = base+12 # f.tell()
103
117
  endian = f.read(1)
@@ -35,6 +35,16 @@ xml
35
35
  @src.rewind
36
36
  @ng_xml = BASE_XML.clone
37
37
  end
38
+ # this is a hack to deal with Ruby 1.9 shenanigans
39
+ def ord_value(val)
40
+ if val.is_a? Fixnum
41
+ return val
42
+ elsif val.is_a? String
43
+ return val.unpack('C')[0]
44
+ else
45
+ return val.to_i
46
+ end
47
+ end
38
48
  def nodeset
39
49
  @ng_xml.root.element_children
40
50
  end
@@ -84,13 +94,21 @@ xml
84
94
  def extent=(value)
85
95
  add_dt_prop("dcmi", "extent", value)
86
96
  end
97
+
98
+ def hex_inspect(str)
99
+ result = []
100
+ (0...str.length).each {|ix| result << str[ix].to_s(16)}
101
+ result.inspect
102
+ end
87
103
  end
88
104
 
89
105
  class Bmp < Base
90
106
  def initialize(srcfile=nil)
91
107
  super
92
108
  header_bytes = @src.read(18)
93
- raise "Source file is not a bitmap" unless header_bytes[0...2] == Cul::Image::Magic::BMP
109
+ unless header_bytes[0...2].unpack('C*') == Cul::Image::Magic::BMP
110
+ raise "Source file is not a bitmap: #{hex_inspect(header_bytes[0...2])}"
111
+ end
94
112
  size = header_bytes[-4,4].unpack('V')[0]
95
113
  header_bytes = header_bytes + @src.read(size)
96
114
  dims = header_bytes[0x12...0x1a].unpack('VV')
@@ -108,7 +126,9 @@ class Gif < Base
108
126
  def initialize(srcfile=nil)
109
127
  super
110
128
  header_bytes = @src.read(13)
111
- raise "Source file is not a gif" unless header_bytes[0...4] == Cul::Image::Magic::GIF
129
+ unless header_bytes[0...4].unpack('C*') == Cul::Image::Magic::GIF
130
+ raise "Source file is not a gif: #{hex_inspect(header_bytes[0...4])}"
131
+ end
112
132
  self.width= header_bytes[6,2].unpack('v')[0]
113
133
  self.length= header_bytes[8,2].unpack('v')[0]
114
134
  self.extent= srcfile.stat.size unless srcfile.nil?
@@ -119,14 +139,16 @@ class Jpeg < Base
119
139
  def initialize(srcfile=nil)
120
140
  super
121
141
  header_bytes = @src.read(2)
122
- raise "Source file is not a jpeg" unless header_bytes[0...2] == Cul::Image::Magic::JPEG
142
+ unless header_bytes[0...2].unpack('C*') == Cul::Image::Magic::JPEG
143
+ raise "Source file is not a jpeg: #{hex_inspect(header_bytes[0...2])}"
144
+ end
123
145
  xpix = 0
124
146
  ypix = 0
125
147
  while (!@src.eof?)
126
- if "\xFF" == @src.read(1)
127
- mrkr = "\xFF" + @src.read(1)
148
+ if 0xff == ord_value(@src.read(1))
149
+ mrkr = [0xff, ord_value(@src.read(1))]
128
150
  blen = @src.read(2).unpack('n')[0]
129
- if Cul::Image::Magic::JPEG_FRAME_MARKERS.include? mrkr # SOFn, Start of frame for scans
151
+ if Cul::Image::Magic::JFM_BYTES.include? mrkr # SOFn, Start of frame for scans
130
152
  @src.read(1) #skip bits per sample
131
153
  self.length= @src.read(2).unpack('n')[0]
132
154
  self.width= @src.read(2).unpack('n')[0]
@@ -170,7 +192,9 @@ class Png < Base
170
192
  def initialize(srcfile=nil)
171
193
  super
172
194
  header_bytes = @src.read(8)
173
- raise "Source file is not a png" unless header_bytes[0...8] == Cul::Image::Magic::PNG
195
+ unless header_bytes[0...8].unpack('C*') == Cul::Image::Magic::PNG
196
+ raise "Source file is not a png #{hex_inspect(header_bytes[0...8])}"
197
+ end
174
198
  until @src.eof?
175
199
  clen = @src.read(4).unpack('N')[0]
176
200
  ctype = @src.read(4)
@@ -193,7 +217,7 @@ class Png < Base
193
217
  val = @src.read(9)
194
218
  xres = val[0,4].unpack('N')[0]
195
219
  yres = val[4,4].unpack('N')[0]
196
- unit = val[8]
220
+ unit = ord_value(val[8])
197
221
  if unit == 1 # resolution unit is METER
198
222
  xres = (xres / 100).ceil
199
223
  yres = (yres / 100).ceil
@@ -277,13 +301,13 @@ class Tiff < Base
277
301
  def initialize(srcfile=nil)
278
302
  super
279
303
  header_bytes = @src.read(14)
280
- case header_bytes[0...4]
304
+ case header_bytes[0...4].unpack('C*')
281
305
  when Cul::Image::Magic::TIFF_INTEL_LE
282
306
  @endian = header_bytes[12]
283
307
  when Cul::Image::Magic::TIFF_MOTOROLA_BE
284
308
  @endian = header_bytes[12]
285
309
  else
286
- raise "Source file is not a tiff"
310
+ raise "Source file is not a tiff #{hex_inspect(header_bytes[0...4])}"
287
311
  end
288
312
  @src.rewind
289
313
  tags = Cul::Image::Properties::Exif.process_file(srcfile)
@@ -1,7 +1,7 @@
1
1
  module Cul
2
2
  module Image
3
3
  module Properties
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
@@ -16,7 +16,7 @@ module Properties
16
16
  buf = ''
17
17
  src.read(2,buf)
18
18
  magic_bytes << buf
19
- case magic_bytes
19
+ case magic_bytes.unpack('C*')
20
20
  when Cul::Image::Magic::BMP
21
21
  result = Cul::Image::Properties::Bmp.new(src)
22
22
  when Cul::Image::Magic::JPEG
@@ -26,7 +26,7 @@ module Properties
26
26
  if result.nil?
27
27
  src.read(2,buf)
28
28
  magic_bytes << buf
29
- case magic_bytes
29
+ case magic_bytes.unpack('C*')
30
30
  when Cul::Image::Magic::TIFF_MOTOROLA_BE
31
31
  result = Cul::Image::Properties::Tiff.new(src)
32
32
  when Cul::Image::Magic::TIFF_INTEL_LE
@@ -38,10 +38,10 @@ module Properties
38
38
  if result.nil?
39
39
  src.read(4,buf)
40
40
  magic_bytes << buf
41
- if magic_bytes == Cul::Image::Magic::PNG
41
+ if magic_bytes.unpack('C*') == Cul::Image::Magic::PNG
42
42
  result = Cul::Image::Properties::Png.new(src)
43
43
  else
44
- puts magic_bytes.unpack('H2H2H2H2H2H2H2H2').inspect
44
+ puts "Unknown magic bytes: " + magic_bytes.unpack('H2H2H2H2H2H2H2H2').inspect
45
45
  end
46
46
  end
47
47
  return result
metadata CHANGED
@@ -1,77 +1,86 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: cul_image_props
3
- version: !ruby/object:Gem::Version
4
- hash: 31
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 2
10
- version: 0.1.2
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Benjamin Armintor
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-06-18 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: rspec
12
+ date: 2012-06-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
22
23
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
24
25
  none: false
25
- requirements:
26
- - - <
27
- - !ruby/object:Gem::Version
28
- hash: 15
29
- segments:
30
- - 2
31
- - 0
32
- - 0
33
- version: 2.0.0
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
34
38
  type: :development
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: mocha
38
39
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
40
41
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 43
45
- segments:
46
- - 0
47
- - 9
48
- - 8
49
- version: 0.9.8
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
50
54
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: ruby-debug
54
55
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
56
57
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 3
61
- segments:
62
- - 0
63
- version: "0"
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mocha
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.8
64
70
  type: :development
65
- version_requirements: *id003
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.8
66
78
  description: Library for extracting basic image properties
67
79
  email: armintor@gmail.com
68
80
  executables: []
69
-
70
81
  extensions: []
71
-
72
82
  extra_rdoc_files: []
73
-
74
- files:
83
+ files:
75
84
  - lib/cul_image_props/image/magic.rb
76
85
  - lib/cul_image_props/image/properties/exif/constants.rb
77
86
  - lib/cul_image_props/image/properties/exif/types.rb
@@ -83,36 +92,26 @@ files:
83
92
  - lib/cul_image_props.rb
84
93
  homepage:
85
94
  licenses: []
86
-
87
95
  post_install_message:
88
96
  rdoc_options: []
89
-
90
- require_paths:
97
+ require_paths:
91
98
  - lib
92
- required_ruby_version: !ruby/object:Gem::Requirement
99
+ required_ruby_version: !ruby/object:Gem::Requirement
93
100
  none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
101
- required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
106
  none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- hash: 3
107
- segments:
108
- - 0
109
- version: "0"
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
110
111
  requirements: []
111
-
112
112
  rubyforge_project:
113
- rubygems_version: 1.8.13
113
+ rubygems_version: 1.8.24
114
114
  signing_key:
115
115
  specification_version: 3
116
116
  summary: Library for extracting basic image properties
117
117
  test_files: []
118
-