philiprehberger-image_size
Image dimension detection from file headers without full decode
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-image_size"
Or install directly:
gem install philiprehberger-image_size
Usage
require "philiprehberger/image_size"
info = Philiprehberger::ImageSize.of("photo.png")
info.width # => 1920
info.height # => 1080
info.format # => :png
Dimensions Only
width, height = Philiprehberger::ImageSize.dimensions("banner.jpg")
Format Detection
format = Philiprehberger::ImageSize.format("image.webp")
# => :webp
IO Objects
File.open("photo.gif", "rb") do |f|
info = Philiprehberger::ImageSize.of(f)
puts info.to_s # => "GIF 320x240"
end
Animation Detection
info = Philiprehberger::ImageSize.of("animation.gif")
info.animated? # => true
Alpha Channel Detection
info = Philiprehberger::ImageSize.of("transparent.png")
info.alpha? # => true
EXIF Orientation
info = Philiprehberger::ImageSize.of("rotated.jpg")
info.orientation # => 6
# Width and height reflect actual display dimensions (swapped for 90/270 rotation)
ImageInfo Value Object
info = Philiprehberger::ImageSize.of("photo.bmp")
info.to_a # => [640, 480]
info.to_h # => { width: 640, height: 480, format: :bmp, animated: false, alpha: false, orientation: nil }
Megapixels
info = Philiprehberger::ImageSize.of("photo.png")
info.megapixels # => 2.1
DPI Extraction
info = Philiprehberger::ImageSize.of("print-ready.jpg")
info.dpi # => { x: 300.0, y: 300.0 } or nil if not available
Supported sources: JPEG (JFIF APP0), PNG (pHYs chunk), TIFF (resolution tags), BMP (pixels per meter).
Color Depth
info = Philiprehberger::ImageSize.of("photo.png")
info.color_depth # => 24 (bits per pixel) or nil if not detectable
Supported formats: PNG (bit depth * channels), BMP (from header), JPEG (precision * components from SOF marker).
Interlace Detection
info = Philiprehberger::ImageSize.of("progressive.jpg")
info.interlaced? # => true (progressive JPEG)
info = Philiprehberger::ImageSize.of("interlaced.png")
info.interlaced? # => true (Adam7 interlacing)
Computed Properties
info = Philiprehberger::ImageSize.of("photo.png")
info.aspect_ratio # => 1.78
info.landscape? # => true
info.portrait? # => false
info.square? # => false
info.area # => 2073600
info.megapixels # => 2.1
info.rotated? # => false
Fit Within a Bounding Box
info = Philiprehberger::ImageSize.of("photo.png") # 1920x1080
info.fit_within(400, 400) # => [400, 225] # scaled down, aspect preserved
info.fit_within(3000, 3000) # => [1920, 1080] # no upscale
API
Philiprehberger::ImageSize
| Method | Description |
|---|---|
.of(path_or_io) |
Returns ImageInfo with width, height, format, and metadata |
.dimensions(path_or_io) |
Returns [width, height] array |
.format(path_or_io) |
Returns format symbol (:png, :jpeg, :gif, :bmp, :webp, :tiff, :ico, :cur, :svg, :avif) |
Philiprehberger::ImageSize::ImageInfo
| Method | Description |
|---|---|
#width |
Image width in pixels (display dimensions for rotated JPEG) |
#height |
Image height in pixels (display dimensions for rotated JPEG) |
#format |
Format symbol |
#animated? |
Whether the image is animated (GIF, WebP, APNG) |
#alpha? |
Whether the image has an alpha channel |
#interlaced? |
Whether the image uses interlaced (PNG Adam7) or progressive (JPEG) encoding |
#orientation |
EXIF orientation (1-8), nil if not applicable |
#aspect_ratio |
Width divided by height as Float |
#landscape? |
Whether width > height |
#portrait? |
Whether height > width |
#square? |
Whether width == height |
#area |
Total pixel count (width * height) |
#megapixels |
Area in megapixels, rounded to 1 decimal |
#dpi |
DPI as { x: Float, y: Float } hash, or nil |
#color_depth |
Bits per pixel (PNG, BMP, JPEG), or nil |
#rotated? |
Whether EXIF orientation indicates 90/270 rotation |
#fit_within(max_w, max_h) |
Returns [w, h] scaled to fit inside a bounding box (preserves aspect ratio, never upscales) |
#to_a |
Returns [width, height] |
#to_h |
Returns hash with all attributes |
#to_s |
Returns "FORMAT WxH" string |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: