Skip to content

Commit

Permalink
- Added icc_transform to the pipeline to preserve original image colors
Browse files Browse the repository at this point in the history
- Fixed caching for very large files
  • Loading branch information
lamka02sk committed Nov 26, 2024
1 parent 8cb25eb commit 55ede45
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "picturium"
version = "0.1.3"
version = "0.1.4"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
2 changes: 1 addition & 1 deletion src/pipeline/finalize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use libvips::{ops, VipsImage};
use libvips::ops::{ForeignHeifCompression, ForeignHeifEncoder, ForeignKeep, ForeignSubsample, ForeignWebpPreset, HeifsaveOptions, JpegsaveOptions, PngsaveOptions, WebpsaveOptions};
use libvips::{ops, VipsImage};
use log::{debug, error};

use crate::cache;
Expand Down
10 changes: 10 additions & 0 deletions src/pipeline/icc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::pipeline::{PipelineError, PipelineResult};
use crate::services::vips::get_error_message;
use libvips::{ops, VipsImage};

pub(crate) async fn transform(image: VipsImage) -> PipelineResult<VipsImage> {
match ops::icc_transform(&image, "sRGB") {
Ok(image) => Ok(image),
Err(_) => Err(PipelineError(format!("Failed to transform image to sRGB: {}", get_error_message())))
}
}
39 changes: 26 additions & 13 deletions src/pipeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,50 +13,63 @@ mod crop;
mod finalize;
mod rasterize;
mod background;
mod icc;

pub type PipelineResult<T> = Result<T, PipelineError>;

#[derive(Debug)]
pub struct PipelineError(pub String);

pub async fn run(url_parameters: &UrlParameters<'_>, output_format: OutputFormat) -> PipelineResult<PathBuf> {

debug!("Running pipeline for {}", url_parameters.path.to_string_lossy());
pub enum PipelineOutput {
Image(PathBuf),
OutputFormat(OutputFormat)
}

pub async fn run(url_parameters: &UrlParameters<'_>, output_format: OutputFormat) -> PipelineResult<PipelineOutput> {
let mut image = thumbnail::run(url_parameters.path, url_parameters).await?;

if output_format == OutputFormat::Pdf {
return Ok(cache::get_document_path_from_url_parameters(url_parameters).into());
return Ok(PipelineOutput::Image(cache::get_document_path_from_url_parameters(url_parameters).into()));
}

if is_svg(url_parameters.path) {
image = rasterize::run(image, url_parameters).await?;
}


debug!("Performing autorotate");
image = rotate::autorotate(image).await?;

let valid_output_format = validate_output_format(&image, url_parameters, &output_format)?;

if valid_output_format != output_format {
return Ok(PipelineOutput::OutputFormat(valid_output_format));
}

let output_format = valid_output_format;

debug!("Performing ICC transform");
image = icc::transform(image).await?;

// if url_parameters.crop.is_some() {
// crop::run(&image, &url_parameters, &output_format).await?;
// }

let output_format = validate_output_format(&image, url_parameters, &output_format)?;

if url_parameters.width.is_some() || url_parameters.height.is_some() {
image = resize::run(image, url_parameters).await?;
}

debug!("Before rotate");

if url_parameters.rotate != Rotate::No {
debug!("Rotating image");
image = rotate::run(image, url_parameters).await?;
}

debug!("Checking if background is required");

if supports_transparency(url_parameters.path) && output_format != OutputFormat::Jpg {
debug!("Applying background");
image = background::run(image, url_parameters).await?;
}

finalize::run(image, url_parameters, &output_format).await

match finalize::run(image, url_parameters, &output_format).await {
Ok(result) => Ok(PipelineOutput::Image(result)),
Err(e) => Err(e)
}
}
64 changes: 52 additions & 12 deletions src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use log::{debug, error};
use crate::parameters::{RawUrlParameters, UrlParameters};
use crate::pipeline;
use crate::cache;
use crate::pipeline::PipelineOutput;
use crate::services::formats::is_generated;

pub mod formats;
Expand Down Expand Up @@ -62,21 +63,11 @@ pub async fn serve(req: HttpRequest, path: Path<String>, parameters: Query<HashM
let cache_path = cache::get_path_from_url_parameters(&url_parameters, &output_format);

// Return from cache
if cache_enable && cache::is_cached(&cache_path, &url_parameters) {
debug!("Using cache @{cache_path}");

let mut response = NamedFile::open(cache_path).unwrap().set_content_disposition(
ContentDisposition {
disposition: DispositionType::Inline,
parameters: vec![DispositionParam::Filename(url_parameters.path.file_name().unwrap().to_string_lossy().into())]
}
).into_response(&req);

response.headers_mut().insert(header::CACHE_CONTROL, header::HeaderValue::from_static("public, max-age=604800, must-revalidate"));
if let Some(response) = cache_response(cache_enable, &cache_path, &url_parameters, &req) {
return response;
}

debug!("Running pipeline for @{cache_path}");
debug!("Running pipeline for {} @ {cache_path}", url_parameters.path.to_string_lossy());

// Process image
let output = match pipeline::run(&url_parameters, output_format).await {
Expand All @@ -87,6 +78,37 @@ pub async fn serve(req: HttpRequest, path: Path<String>, parameters: Query<HashM
}
};

let output = match output {
PipelineOutput::Image(output) => output,
PipelineOutput::OutputFormat(output_format) => {
let cache_path = cache::get_path_from_url_parameters(&url_parameters, &output_format);

// Return from cache
if let Some(response) = cache_response(cache_enable, &cache_path, &url_parameters, &req) {
return response;
}

debug!("Running pipeline for {} @ {cache_path}", url_parameters.path.to_string_lossy());

// Process image
let output = match pipeline::run(&url_parameters, output_format).await {
Ok(output) => output,
Err(e) => {
error!("Failed to process image: {}", e.0);
return HttpResponse::InternalServerError().into();
}
};

match output {
PipelineOutput::Image(output) => output,
_ => {
error!("Failed to process image: detected output format resolution recursion");
return HttpResponse::InternalServerError().into();
}
}
}
};

match NamedFile::open(output) {
Ok(named_file) => {

Expand All @@ -109,4 +131,22 @@ pub async fn serve(req: HttpRequest, path: Path<String>, parameters: Query<HashM
Err(_) => HttpResponse::InternalServerError().into()
}

}

fn cache_response(enabled: bool, cache_path: &str, url_parameters: &UrlParameters, req: &HttpRequest) -> Option<HttpResponse> {
if !enabled || !cache::is_cached(&cache_path, &url_parameters) {
return None;
}

debug!("Using cache @{cache_path}");

let mut response = NamedFile::open(cache_path).unwrap().set_content_disposition(
ContentDisposition {
disposition: DispositionType::Inline,
parameters: vec![DispositionParam::Filename(url_parameters.path.file_name().unwrap().to_string_lossy().into())]
}
).into_response(req);

response.headers_mut().insert(header::CACHE_CONTROL, header::HeaderValue::from_static("public, max-age=604800, must-revalidate"));
Some(response)
}

0 comments on commit 55ede45

Please sign in to comment.