cul_image_props 0.1.2 → 0.2.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.
@@ -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
-