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.
105 106 107 |
# File 'lib/parsers/exif_parser.rb', line 105 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)
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/parsers/exif_parser.rb', line 156 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
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/parsers/exif_parser.rb', line 121 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 return orientation_value if !orientation_value.nil? && orientation_value != 0 end 0 # If none were found - the orientation is unknown end |
#orientation_sym ⇒ Object
113 114 115 |
# File 'lib/parsers/exif_parser.rb', line 113 def orientation_sym ORIENTATIONS.fetch(orientation) end |
#rotated? ⇒ Boolean
117 118 119 |
# File 'lib/parsers/exif_parser.rb', line 117 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
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/parsers/exif_parser.rb', line 135 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
109 110 111 |
# File 'lib/parsers/exif_parser.rb', line 109 def to_json(*maybe_coder) to_hash.to_json(*maybe_coder) end |