Class: FileData::ExifStream

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
BinaryExtensions
Defined in:
lib/file_data/formats/exif/exif_stream.rb

Overview

Wraps a stream with exif specific logic

Constant Summary collapse

MOTOROLLA_BYTES =
'MM'.bytes.to_a.freeze
INTEL_BYTES =
'II'.bytes.to_a.freeze
TYPE_BYTE =
1
TYPE_ASCII =
2
TYPE_SHORT =
3
TYPE_LONG =
4
TYPE_RATIONAL =
5
TYPE_UNDEFINED =
7
TYPE_SLONG =
9
TYPE_SRATIONAL =
10
HIGH_BIT_MASK =
2**31
VALUE_OFFSET_SIZE =
4

Instance Method Summary collapse

Methods included from BinaryExtensions

#read_ascii, #read_value

Constructor Details

#initialize(stream) ⇒ ExifStream

Returns a new instance of ExifStream.



28
29
30
31
# File 'lib/file_data/formats/exif/exif_stream.rb', line 28

def initialize(stream)
  @stream = stream
  @section_offset = stream.pos
end

Instance Method Details

#read_headerObject



33
34
35
36
37
38
39
40
41
42
# File 'lib/file_data/formats/exif/exif_stream.rb', line 33

def read_header
  @is_little_endian =
    case @stream.each_byte.take(2)
    when INTEL_BYTES then true
    when MOTOROLLA_BYTES then false
    else raise 'the byte order bytes did not match any expected value'
    end

  raise 'the tiff constant 42 is missing' unless read_value(2) == 42
end

#read_large_val(type) ⇒ Object



77
78
79
80
# File 'lib/file_data/formats/exif/exif_stream.rb', line 77

def read_large_val(type)
  seek_to_large_val
  read_rational(type == TYPE_SRATIONAL)
end

#read_rational(is_srational) ⇒ Object



91
92
93
94
95
96
# File 'lib/file_data/formats/exif/exif_stream.rb', line 91

def read_rational(is_srational)
  Array.new(2) do
    piece = read_value(4)
    is_srational ? to_slong(piece) : piece
  end.join('/')
end

#read_raw_val(size) ⇒ Object



72
73
74
75
# File 'lib/file_data/formats/exif/exif_stream.rb', line 72

def read_raw_val(size)
  seek_to_large_val if size > VALUE_OFFSET_SIZE
  @stream.each_byte.take([size, VALUE_OFFSET_SIZE].max)
end

#read_small_val(type) ⇒ Object



82
83
84
85
# File 'lib/file_data/formats/exif/exif_stream.rb', line 82

def read_small_val(type)
  value = read_value(4)
  type == TYPE_SLONG ? to_slong(value) : value
end

#read_tag_valueObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/file_data/formats/exif/exif_stream.rb', line 48

def read_tag_value
  type = read_value(2)
  size = read_value(4)

  case type
  when TYPE_RATIONAL, TYPE_SRATIONAL
    read_large_val(type)
  when TYPE_BYTE, TYPE_SHORT, TYPE_LONG, TYPE_SLONG
    read_small_val(type)
  when TYPE_ASCII
    read_text(size)
  when TYPE_UNDEFINED
    read_undefined(size)
  end
end

#read_text(size) ⇒ Object



64
65
66
# File 'lib/file_data/formats/exif/exif_stream.rb', line 64

def read_text(size)
  read_raw_val(size).pack('c*').chomp("\x00")
end

#read_undefined(size) ⇒ Object



68
69
70
# File 'lib/file_data/formats/exif/exif_stream.rb', line 68

def read_undefined(size)
  [read_raw_val(size), @is_little_endian]
end

#seek_exif(offset) ⇒ Object



44
45
46
# File 'lib/file_data/formats/exif/exif_stream.rb', line 44

def seek_exif(offset)
  @stream.seek(@section_offset + offset)
end

#seek_to_large_valObject



87
88
89
# File 'lib/file_data/formats/exif/exif_stream.rb', line 87

def seek_to_large_val
  seek_exif(read_value(4))
end

#to_slong(raw_value) ⇒ Object



98
99
100
# File 'lib/file_data/formats/exif/exif_stream.rb', line 98

def to_slong(raw_value)
  -(raw_value & HIGH_BIT_MASK) + (raw_value & ~HIGH_BIT_MASK)
end