pub fn transform(
request: TransformRequest,
) -> Result<TransformResult, TransformError>Expand description
Dispatches a transform request to the appropriate codec based on the input media type.
This is the entry point for every image transformation, and the only one: it routes an SVG input to the SVG codec and everything else to the raster codec, and rejects an unsupported conversion such as raster-to-SVG output with a clear error. The two codecs are not exported, because choosing between them is what this function does and naming the wrong one for an input is only a way to get an error.
GIF is an input-only format here. A single-frame GIF decodes and transforms like any
other raster input; an animated one is refused rather than silently reduced to its
first frame, because a caller that sends an animation and receives a still image back
with exit code 0 has no way to notice the animation was discarded. truss inspect
still reads animated GIFs and reports isAnimated, so a caller can branch on that
before converting.
§Errors
Returns TransformError::UnsupportedOutputMediaType if a raster input requests
SVG or GIF output, TransformError::UnsupportedInputMediaType for an animated GIF
input, TransformError::CapabilityMissing if an SVG input is provided but the svg
feature is not enabled, or any error propagated from the underlying codec.
§Examples
use image::codecs::png::PngEncoder;
use image::{ColorType, ImageEncoder, Rgba, RgbaImage};
use truss::{sniff_artifact, transform, MediaType, RawArtifact, TransformOptions, TransformRequest};
let image = RgbaImage::from_pixel(2, 2, Rgba([10, 20, 30, 255]));
let mut bytes = Vec::new();
PngEncoder::new(&mut bytes)
.write_image(&image, 2, 2, ColorType::Rgba8.into())
.unwrap();
let input = sniff_artifact(RawArtifact::new(bytes, Some(MediaType::Png))).unwrap();
let mut options = TransformOptions::default();
options.format = Some(MediaType::Jpeg);
let output = transform(TransformRequest::new(
input,
options,
))
.unwrap();
assert_eq!(output.artifact.media_type, MediaType::Jpeg);
assert_eq!(output.artifact.metadata.width, Some(2));
assert_eq!(output.artifact.metadata.height, Some(2));use image::codecs::png::PngEncoder;
use image::{ColorType, ImageEncoder, Rgba, RgbaImage};
use truss::{sniff_artifact, transform, MediaType, RawArtifact, TransformOptions, TransformRequest};
let image = RgbaImage::from_pixel(2, 2, Rgba([10, 20, 30, 255]));
let mut bytes = Vec::new();
PngEncoder::new(&mut bytes)
.write_image(&image, 2, 2, ColorType::Rgba8.into())
.unwrap();
let input = sniff_artifact(RawArtifact::new(bytes, Some(MediaType::Png))).unwrap();
let mut options = TransformOptions::default();
options.format = Some(MediaType::Avif);
options.quality = Some(70);
let output = transform(TransformRequest::new(
input,
options,
))
.unwrap();
let sniffed = sniff_artifact(RawArtifact::new(output.artifact.bytes.clone(), None)).unwrap();
assert_eq!(output.artifact.media_type, MediaType::Avif);
assert_eq!(sniffed.media_type, MediaType::Avif);use image::codecs::jpeg::JpegDecoder;
use image::codecs::jpeg::JpegEncoder;
use image::metadata::Orientation;
use image::{ColorType, ImageDecoder, ImageEncoder, Rgb, RgbImage};
use std::io::Cursor;
use truss::{sniff_artifact, transform, MediaType, RawArtifact, TransformOptions, TransformRequest};
let image = RgbImage::from_pixel(2, 1, Rgb([10, 20, 30]));
let exif = vec![
0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x12, 0x01, 0x03, 0x00, 0x01, 0x00,
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let mut bytes = Vec::new();
let mut encoder = JpegEncoder::new_with_quality(&mut bytes, 80);
encoder.set_exif_metadata(exif).unwrap();
encoder
.write_image(&image, 2, 1, ColorType::Rgb8.into())
.unwrap();
let input = sniff_artifact(RawArtifact::new(bytes, Some(MediaType::Jpeg))).unwrap();
let mut options = TransformOptions::default();
options.format = Some(MediaType::Jpeg);
options.strip_metadata = false;
options.preserve_exif = true;
let output = transform(TransformRequest::new(
input,
options,
))
.unwrap();
let mut decoder = JpegDecoder::new(Cursor::new(&output.artifact.bytes)).unwrap();
let exif = decoder.exif_metadata().unwrap().unwrap();
assert_eq!(output.artifact.metadata.width, Some(1));
assert_eq!(output.artifact.metadata.height, Some(2));
assert_eq!(Orientation::from_exif_chunk(&exif), Some(Orientation::NoTransforms));use image::codecs::jpeg::JpegDecoder;
use image::codecs::jpeg::JpegEncoder;
use image::{ColorType, ImageDecoder, ImageEncoder, Rgb, RgbImage};
use std::io::Cursor;
use truss::{sniff_artifact, transform, MediaType, RawArtifact, TransformOptions, TransformRequest};
let image = RgbImage::from_pixel(2, 1, Rgb([10, 20, 30]));
let mut bytes = Vec::new();
let mut encoder = JpegEncoder::new_with_quality(&mut bytes, 80);
encoder.set_icc_profile(b"demo-icc-profile".to_vec()).unwrap();
encoder
.write_image(&image, 2, 1, ColorType::Rgb8.into())
.unwrap();
let input = sniff_artifact(RawArtifact::new(bytes, Some(MediaType::Jpeg))).unwrap();
let mut options = TransformOptions::default();
options.format = Some(MediaType::Jpeg);
options.strip_metadata = false;
let output = transform(TransformRequest::new(
input,
options,
))
.unwrap();
let mut decoder = JpegDecoder::new(Cursor::new(&output.artifact.bytes)).unwrap();
assert_eq!(decoder.icc_profile().unwrap(), Some(b"demo-icc-profile".to_vec()));use truss::{sniff_artifact, RawArtifact, TransformRequest, TransformOptions, MediaType};
use truss::transform;
let svg_bytes = b"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\"><rect width=\"10\" height=\"10\" fill=\"red\"/></svg>";
let input = sniff_artifact(RawArtifact::new(svg_bytes.to_vec(), None)).unwrap();
let mut options = TransformOptions::default();
options.format = Some(MediaType::Png);
options.width = Some(10);
options.height = Some(10);
let result = transform(TransformRequest::new(input, options)).unwrap();
assert_eq!(result.artifact.media_type, MediaType::Png);