ruby
41 lines · 7 steps
Correcting EXIF orientation in Ruby
A class maps each EXIF orientation code to the flip/rotate that makes an image appear upright.
Explained by
highlit
1class ImageNormalizer
2 ORIENTATION_TRANSFORMS = {
3 1 => ->(img) {},
4 2 => ->(img) { img.flop },
5 3 => ->(img) { img.rotate(180) },
6 4 => ->(img) { img.flip },
7 5 => ->(img) { img.rotate(90); img.flop },
8 6 => ->(img) { img.rotate(90) },
9 7 => ->(img) { img.rotate(-90); img.flop },
10 8 => ->(img) { img.rotate(-90) }
11 }.freeze
12
13 def initialize(path)
14 @image = MiniMagick::Image.open(path)
15 end
16
17 def normalize!
18 orientation = raw_orientation
19 return @image if orientation.nil? || orientation == 1
20
21 transform = ORIENTATION_TRANSFORMS.fetch(orientation, ->(img) {})
22 transform.call(@image)
23
24 @image.combine_options do |cmd|
25 cmd.orient "top-left"
26 cmd.strip
27 end
28
29 @image.write(@image.path)
30 @image
31 end
32
33 private
34
35 def raw_orientation
36 value = @image.exif["Orientation"] || @image.data.dig("properties", "exif:Orientation")
37 Integer(value)
38 rescue ArgumentError, TypeError
39 nil
40 end
41end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A frozen hash of lambdas turns a branching problem into a clean table lookup keyed by an enum-like value.
- 2Correcting pixels isn't enough — you must also reset or strip the metadata so viewers don't re-apply the rotation.
- 3Wrapping parsing in a rescue that returns nil lets callers treat missing or malformed data as a no-op case.
Related explainers
ruby
class Product < ApplicationRecord scope :search, ->(query) { return all if query.blank?
Building a multi-term search scope in Rails
arel
scopes
full-text search
Advanced
7 steps
rust
use std::time::Duration; #[derive(Debug, PartialEq)] pub enum ParseDurationError {
Parsing duration strings safely in Rust
parsing
error-handling
checked-arithmetic
Intermediate
8 steps
ruby
module Rack class MaintenanceMode RETRY_AFTER = 3600
A Rack maintenance-mode middleware in Rails
middleware
rack
http-status
Intermediate
8 steps
javascript
const express = require('express'); const router = express.Router(); router.get('/articles/:slug', async (req, res, next) => {
Conditional GET caching in Express
http-caching
conditional-get
routing
Intermediate
8 steps
go
package handlers import ( "net/http"
Custom validators and binding in Gin
validation
struct-tags
error-handling
Intermediate
8 steps
ruby
module RequestTagging class Middleware def initialize(app) @app = app
Per-request context with CurrentAttributes in Rails
middleware
thread-safety
logging
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/correcting-exif-orientation-in-ruby-explained-ruby-1ee3/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.