diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-02-14 15:12:28 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-14 14:12:28 +0000 |
| commit | b89348b92a9c1ae67a4bb69a6db69d14f881384d (patch) | |
| tree | c48f067d9dce5eae08e95f48b47222791b79f442 /crates | |
| parent | fcf64d0ee0c2c48580f5d3576f8d53df4922c5e9 (diff) | |
Read EXIF data and apply image rotation (#3413)
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/typst/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/typst/src/visualize/image/raster.rs | 38 |
2 files changed, 37 insertions, 2 deletions
diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml index 650f53c3..0f33dbac 100644 --- a/crates/typst/Cargo.toml +++ b/crates/typst/Cargo.toml @@ -36,6 +36,7 @@ icu_provider_blob = { workspace = true } icu_segmenter = { workspace = true } image = { workspace = true } indexmap = { workspace = true } +kamadak-exif = { workspace = true } kurbo = { workspace = true } lipsum = { workspace = true } log = { workspace = true } diff --git a/crates/typst/src/visualize/image/raster.rs b/crates/typst/src/visualize/image/raster.rs index 98ba8fc0..3c9afe43 100644 --- a/crates/typst/src/visualize/image/raster.rs +++ b/crates/typst/src/visualize/image/raster.rs @@ -7,7 +7,7 @@ use image::codecs::gif::GifDecoder; use image::codecs::jpeg::JpegDecoder; use image::codecs::png::PngDecoder; use image::io::Limits; -use image::{guess_format, ImageDecoder, ImageResult}; +use image::{guess_format, DynamicImage, ImageDecoder, ImageResult}; use crate::diag::{bail, StrResult}; use crate::foundations::{Bytes, Cast}; @@ -39,13 +39,17 @@ impl RasterImage { } let cursor = io::Cursor::new(&data); - let (dynamic, icc) = match format { + let (mut dynamic, icc) = match format { RasterFormat::Jpg => decode_with(JpegDecoder::new(cursor)), RasterFormat::Png => decode_with(PngDecoder::new(cursor)), RasterFormat::Gif => decode_with(GifDecoder::new(cursor)), } .map_err(format_image_error)?; + if let Some(rotation) = exif_rotation(&data) { + apply_rotation(&mut dynamic, rotation); + } + Ok(Self(Arc::new(Repr { data, format, dynamic, icc }))) } @@ -129,6 +133,36 @@ impl TryFrom<image::ImageFormat> for RasterFormat { } } +/// Get rotation from EXIF metadata. +fn exif_rotation(data: &[u8]) -> Option<u32> { + let reader = exif::Reader::new(); + let mut cursor = std::io::Cursor::new(data); + let exif = reader.read_from_container(&mut cursor).ok()?; + let orient = exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY)?; + orient.value.get_uint(0) +} + +/// Apply an EXIF rotation to a dynamic image. +fn apply_rotation(image: &mut DynamicImage, rotation: u32) { + use image::imageops as ops; + match rotation { + 2 => ops::flip_horizontal_in_place(image), + 3 => ops::rotate180_in_place(image), + 4 => ops::flip_vertical_in_place(image), + 5 => { + ops::flip_horizontal_in_place(image); + *image = image.rotate270(); + } + 6 => *image = image.rotate90(), + 7 => { + ops::flip_horizontal_in_place(image); + *image = image.rotate90(); + } + 8 => *image = image.rotate270(), + _ => {} + } +} + /// Format the user-facing raster graphic decoding error message. fn format_image_error(error: image::ImageError) -> EcoString { match error { |
