Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 4 additions & 14 deletions src/adapters/cli/convert.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
MediaType, RawArtifact, TransformRequest, WatermarkInput, sniff_artifact, transform_raster,
transform_svg,
};
use crate::{MediaType, RawArtifact, TransformRequest, WatermarkInput, sniff_artifact, transform};
use std::fs;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -150,16 +147,9 @@ where
None
};

let result = if input.media_type == MediaType::Svg {
let mut request = TransformRequest::new(input, options);
request.watermark = watermark;
transform_svg(request)
} else {
let mut request = TransformRequest::new(input, options);
request.watermark = watermark;
transform_raster(request)
}
.map_err(map_transform_error)?;
let mut request = TransformRequest::new(input, options);
request.watermark = watermark;
let result = transform(request).map_err(map_transform_error)?;

for warning in &result.warnings {
eprintln!("warning: {warning}");
Expand Down
22 changes: 6 additions & 16 deletions src/adapters/server/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use super::stderr_write;

use crate::{
CropRegion, Fit, MediaType, Position, RawArtifact, Rgba8, Rotation, TransformOptions,
TransformRequest, WatermarkInput, sniff_artifact, transform_raster, transform_svg,
TransformRequest, WatermarkInput, sniff_artifact, transform,
};
use std::str::FromStr;

Expand Down Expand Up @@ -1375,21 +1375,11 @@ fn transform_source_bytes_inner(
let transform_start = Instant::now();
let mut request_obj = TransformRequest::new(artifact, options);
request_obj.watermark = watermark;
let result = if is_svg {
match transform_svg(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
}
} else {
match transform_raster(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
let result = match transform(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
};
record_transform_duration(result.artifact.media_type, transform_start);
Expand Down
22 changes: 6 additions & 16 deletions src/adapters/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ use response::{

use crate::{
CropRegion, Fit, MediaType, Position, RawArtifact, Rgba8, Rotation, TransformOptions,
TransformRequest, WatermarkInput, sniff_artifact, transform_raster, transform_svg,
TransformRequest, WatermarkInput, sniff_artifact, transform,
};
use hmac::{Hmac, Mac};
use serde::Deserialize;
Expand Down Expand Up @@ -2465,21 +2465,11 @@ fn transform_source_bytes_inner(
let transform_start = Instant::now();
let mut request_obj = TransformRequest::new(artifact, options);
request_obj.watermark = watermark;
let result = if is_svg {
match transform_svg(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
}
} else {
match transform_raster(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
let result = match transform(request_obj) {
Ok(result) => result,
Err(error) => {
record_transform_error(&error);
return transform_error_response(error);
}
};
record_transform_duration(result.artifact.media_type, transform_start);
Expand Down
31 changes: 2 additions & 29 deletions src/adapters/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@

use crate::{
Artifact, CropRegion, MediaType, Position, RawArtifact, Rgba8, Rotation, TransformError,
TransformOptions, TransformRequest, TransformResult, WatermarkInput, sniff_artifact,
transform_raster,
TransformOptions, TransformRequest, TransformResult, WatermarkInput, sniff_artifact, transform,
};
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[cfg(feature = "svg")]
use crate::transform_svg;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -263,32 +259,9 @@ fn dispatch_browser_transform_with_watermark(
options: TransformOptions,
watermark: Option<WatermarkInput>,
) -> Result<TransformResult, TransformError> {
if artifact.media_type != MediaType::Svg && options.format == Some(MediaType::Svg) {
return Err(TransformError::UnsupportedOutputMediaType(MediaType::Svg));
}

if artifact.media_type == MediaType::Svg {
if watermark.is_some() {
return Err(TransformError::InvalidOptions(
"watermark is not supported for SVG inputs".to_string(),
));
}
#[cfg(feature = "svg")]
{
return transform_svg(TransformRequest::new(artifact, options));
}
#[cfg(not(feature = "svg"))]
{
let _ = options;
return Err(TransformError::CapabilityMissing(
"SVG processing is not enabled in this build".to_string(),
));
}
}

let mut request = TransformRequest::new(artifact, options);
request.watermark = watermark;
transform_raster(request)
transform(request)
}

fn artifact_info(artifact: &Artifact) -> WasmArtifactInfo {
Expand Down
36 changes: 36 additions & 0 deletions src/codecs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
//! Backend codec implementations.

use crate::core::{MediaType, TransformError, TransformRequest, TransformResult};

/// Raster image decoding and encoding support.
pub mod raster;

/// SVG sanitization and rasterization support.
#[cfg(feature = "svg")]
pub mod svg;

/// Dispatches a transform request to the appropriate codec based on the input media type.
///
/// This is the primary entry point for all image transformations. It routes SVG
/// inputs to [`svg::transform_svg`] and raster inputs to [`raster::transform_raster`],
/// and rejects unsupported conversions (e.g., raster-to-SVG output) with a clear error.
///
/// # Errors
///
/// Returns [`TransformError::UnsupportedOutputMediaType`] if a raster input requests
/// SVG output, [`TransformError::CapabilityMissing`] if an SVG input is provided but
/// the `svg` feature is not enabled, or any error propagated from the underlying codec.
#[must_use = "this function returns the transform result without side effects"]
pub fn transform(request: TransformRequest) -> Result<TransformResult, TransformError> {
if request.input.media_type == MediaType::Svg {
#[cfg(feature = "svg")]
{
return svg::transform_svg(request);
}
#[cfg(not(feature = "svg"))]
{
let _ = request;
return Err(TransformError::CapabilityMissing(
"SVG processing is not enabled in this build".to_string(),
));
}
}

if request.options.format == Some(MediaType::Svg) {
return Err(TransformError::UnsupportedOutputMediaType(MediaType::Svg));
}

raster::transform_raster(request)
}
1 change: 1 addition & 0 deletions src/codecs/raster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ macro_rules! check_deadline_if_set {
/// let mut decoder = JpegDecoder::new(Cursor::new(&output.artifact.bytes)).unwrap();
/// assert_eq!(decoder.icc_profile().unwrap(), Some(b"demo-icc-profile".to_vec()));
/// ```
#[must_use = "this function returns the transform result without side effects"]
pub fn transform_raster(request: TransformRequest) -> Result<TransformResult, TransformError> {
let normalized = request.normalize()?;
let deadline = normalized.options.deadline;
Expand Down
1 change: 1 addition & 0 deletions src/codecs/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ use std::time::Instant;
/// )).unwrap();
/// assert_eq!(result.artifact.media_type, MediaType::Png);
/// ```
#[must_use = "this function returns the transform result without side effects"]
pub fn transform_svg(request: TransformRequest) -> Result<TransformResult, TransformError> {
if request.options.blur.is_some() {
return Err(TransformError::InvalidOptions(
Expand Down
3 changes: 3 additions & 0 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub const MAX_WATERMARK_PIXELS: u64 = 4_000_000;
/// assert_eq!(d.pixel_count(), 1920 * 1080);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[must_use]
pub struct Dimensions {
pub width: u32,
pub height: u32,
Expand Down Expand Up @@ -119,6 +120,7 @@ impl RawArtifact {
/// assert_eq!(artifact.metadata.frame_count, 1);
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct Artifact {
/// The artifact bytes.
pub bytes: Vec<u8>,
Expand Down Expand Up @@ -1205,6 +1207,7 @@ pub struct TransformResult {
/// assert_eq!(artifact.metadata.height, Some(2));
/// assert_eq!(artifact.metadata.has_alpha, Some(true));
/// ```
#[must_use = "this function returns the detected artifact without side effects"]
pub fn sniff_artifact(input: RawArtifact) -> Result<Artifact, TransformError> {
let (media_type, metadata) = detect_artifact(&input.bytes)?;

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub use adapters::server::{
pub use codecs::raster::transform_raster;
#[cfg(feature = "svg")]
pub use codecs::svg::transform_svg;
pub use codecs::transform;
pub use core::{
Artifact, ArtifactMetadata, CropRegion, Dimensions, Fit, MAX_DECODED_PIXELS, MAX_OUTPUT_PIXELS,
MAX_WATERMARK_PIXELS, MediaType, MetadataKind, MetadataPolicy, NormalizedTransformOptions,
Expand Down
Loading