Class: FormatParser::EXIFParser::EXIFStack
- Inherits:
-
Object
- Object
- FormatParser::EXIFParser::EXIFStack
- Defined in:
- lib/parsers/exif_parser.rb
Overview
With some formats, multiple EXIF tag frames can be included in a single file. For example, JPEGs might have multiple APP1 markers which each contain EXIF data. The EXIF data in them, however, is not necessarily “complete” - it seems most applications assume that these blocks “overwrite” each other with the properties they specify. Probably this is done for more efficient saving - instead of overwriting the EXIF data with a modified version - which would also potentially disturb any digital signing that this data might include - the applications are supposed to follow the order in which these tags appear in the file:
Take a resized image for example:
APP1 {author: 'John', pixel_width: 1024}
APP1 {pixel_width: 10}
That image would get a combined EXIF of:
APP1 {author: 'John', pixel_width: 10}
since the frame that comes later in the file overwrites a property. From what I see exiftools do this is the way it works.
This class acts as a wrapper for this “layering” of chunks of EXIF properties, and will follow the following conventions:
-
When merging data for JSON conversion, it will merge it top-down. It will overwrite any specified properties. An exception is made for orientation (see below)
-
When retrieving a property, it will look “from the end to the beginning” of the EXIF dataframe stack, looking for the first dataframe which has this property with a non-nil value
-
When retrieving orientation, it will pick the first orientation value which is not nil but also not 0 (“unknown orientation”). Even files in our test suite contain these.
Instance Method Summary collapse
-
#initialize(multiple_exif_results) ⇒ EXIFStack
constructor
A new instance of EXIFStack.
- #orientation ⇒ Object
- #orientation_sym ⇒ Object
- #rotated? ⇒ Boolean
-
#to_hash ⇒ Object
ActiveSupport will attempt to call #to_hash first, and #to_hash is a decent default implementation to have.
- #to_json(*maybe_coder) ⇒ Object
Constructor Details
#initialize(multiple_exif_results) ⇒ EXIFStack
Returns a new instance of EXIFStack.
98 99 100 |
# File 'lib/parsers/exif_parser.rb', line 98 def initialize(multiple_exif_results) @multiple_exif_results = Array(multiple_exif_results) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(*a) ⇒ Object (private)
151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/parsers/exif_parser.rb', line 151 def method_missing(*a) return super unless @multiple_exif_results.any? # The EXIF tags get appended to the file, so the ones coming _later_ # are more specific and potentially overwrite the earlier ones. Walk # through the frames in reverse (starting with one that comes last) # and if it contans the requisite EXIF property, return the value # from that tag. @multiple_exif_results.reverse_each do |exif_tag_frame| value_of = exif_tag_frame.public_send(*a) return value_of if value_of end nil end |
Instance Method Details
#orientation ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/parsers/exif_parser.rb', line 114 def orientation # Retrieving an orientation "through" the sequence of EXIF tags # is trickier than the method_missing case, because the value # of the orientation can be 0, meaning "unknown". We need to skip through # those and return the _last_ non-0 orientation, or 0 otherwise @multiple_exif_results.reverse_each do |exif_tag_frame| orientation_value = exif_tag_frame.orientation if !orientation_value.nil? && orientation_value != 0 return orientation_value end end 0 # If none were found - the orientation is unknown end |
#orientation_sym ⇒ Object
106 107 108 |
# File 'lib/parsers/exif_parser.rb', line 106 def orientation_sym ORIENTATIONS.fetch(orientation) end |
#rotated? ⇒ Boolean
110 111 112 |
# File 'lib/parsers/exif_parser.rb', line 110 def rotated? orientation > 4 end |
#to_hash ⇒ Object
ActiveSupport will attempt to call #to_hash first, and #to_hash is a decent default implementation to have
130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/parsers/exif_parser.rb', line 130 def to_hash # Let EXIF tags that come later overwrite the properties from the tags # that come earlier = @multiple_exif_results.each_with_object({}) do |one_exif_frame, h| h.merge!(one_exif_frame.to_hash) end # Overwrite the orientation with our custom method implementation, because # it does reject 0-values. [:orientation] = orientation FormatParser::AttributesJSON._sanitize_json_value() end |
#to_json(*maybe_coder) ⇒ Object
102 103 104 |
# File 'lib/parsers/exif_parser.rb', line 102 def to_json(*maybe_coder) to_hash.to_json(*maybe_coder) end |