lets generate some images

This commit is contained in:
Michal Vanko 2024-09-04 15:43:47 +02:00
parent f1b3400e63
commit 7734c03ba5
5 changed files with 57 additions and 48 deletions

View File

@ -0,0 +1,27 @@
#[derive(Debug, PartialEq)]
pub enum ExportFormat {
JPG,
AVIF,
SVG,
PNG,
}
impl ExportFormat {
pub fn get_extension(&self) -> &str {
match self {
ExportFormat::JPG => "jpg",
ExportFormat::AVIF => "avif",
ExportFormat::SVG => "svg",
ExportFormat::PNG => "png",
}
}
pub fn get_type(&self) -> &str {
match self {
ExportFormat::JPG => "image/jpeg",
ExportFormat::AVIF => "image/avif",
ExportFormat::SVG => "image/svg+xml",
ExportFormat::PNG => "image/png",
}
}
}

View File

@ -21,4 +21,5 @@ It can be used from the rust code
It should be used from the templates as well It should be used from the templates as well
*/ */
pub mod export_format;
pub mod picture_markup_generator; pub mod picture_markup_generator;

View File

@ -1,41 +1,16 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
path::{Path, PathBuf}, path::{Path, PathBuf},
str::FromStr, str::FromStr as _,
}; };
use axum::handler::HandlerWithoutStateExt; use anyhow::Context;
use image::{GenericImageView, ImageReader}; use image::{GenericImageView, ImageReader};
use super::export_format::ExportFormat;
pub const PIXEL_DENSITIES: [f32; 5] = [1., 1.5, 2., 3., 4.]; pub const PIXEL_DENSITIES: [f32; 5] = [1., 1.5, 2., 3., 4.];
#[derive(Debug, PartialEq)]
pub enum ExportFormat {
JPG,
AVIF,
SVG,
PNG,
}
impl ExportFormat {
pub fn get_extension(&self) -> &str {
match self {
ExportFormat::JPG => "jpg",
ExportFormat::AVIF => "avif",
ExportFormat::SVG => "svg",
ExportFormat::PNG => "png",
}
}
pub fn get_type(&self) -> &str {
match self {
ExportFormat::JPG => "image/jpeg",
ExportFormat::AVIF => "image/avif",
ExportFormat::SVG => "image/svg+xml",
ExportFormat::PNG => "image/png",
}
}
}
pub fn generate_picture_markup( pub fn generate_picture_markup(
orig_img_path: &str, orig_img_path: &str,
width: u32, width: u32,
@ -44,7 +19,13 @@ pub fn generate_picture_markup(
) -> Result<String, anyhow::Error> { ) -> Result<String, anyhow::Error> {
let exported_formats = get_export_formats(orig_img_path); let exported_formats = get_export_formats(orig_img_path);
let path_to_generated = get_generated_file_name(orig_img_path); let path_to_generated = get_generated_file_name(orig_img_path);
let orig_img = ImageReader::open(orig_img_path)?.decode()?;
// TODO This should get removed when we move the project structure #move
let dev_only_img_path = Path::new("../static/").join(orig_img_path.strip_prefix("/").unwrap());
let orig_img = ImageReader::open(&dev_only_img_path)
.with_context(|| format!("Failed to read instrs from {:?}", &dev_only_img_path))?
.decode()?;
let orig_img_dimensions = orig_img.dimensions(); let orig_img_dimensions = orig_img.dimensions();
let resolutions = get_resolutions(orig_img_dimensions, width, height); let resolutions = get_resolutions(orig_img_dimensions, width, height);
@ -221,20 +202,20 @@ fn get_generated_file_name(orig_img_path: &str) -> PathBuf {
let file_name = path let file_name = path
.file_stem() .file_stem()
.expect("There should be a name for every img"); .expect("There should be a name for every img");
let result = Path::new("./generated_images/") let result = Path::new("/generated_images/")
.join(parent) .join(parent.strip_prefix("/").unwrap())
.join(file_name); .join(file_name);
result result
} }
#[test] #[test]
fn test_get_generated_paths() { fn test_get_generated_paths() {
let orig_img_path = "./images/uploads/img_name.jpg"; let orig_img_path = "/images/uploads/img_name.jpg";
assert_eq!( assert_eq!(
get_generated_file_name(orig_img_path) get_generated_file_name(orig_img_path)
.to_str() .to_str()
.unwrap_or(""), .unwrap_or(""),
"./generated_images/images/uploads/img_name" "/generated_images/images/uploads/img_name"
); );
} }
@ -271,13 +252,13 @@ fn get_export_formats(orig_img_path: &str) -> Vec<ExportFormat> {
#[test] #[test]
fn test_get_export_formats() { fn test_get_export_formats() {
assert_eq!( assert_eq!(
get_export_formats("./images/uploads/img_name.jpg"), get_export_formats("/images/uploads/img_name.jpg"),
vec![ExportFormat::AVIF, ExportFormat::JPG] vec![ExportFormat::AVIF, ExportFormat::JPG]
) )
} }
#[test] #[test]
fn test_generate_srcset() { fn test_generate_srcset() {
let orig_img_path = PathBuf::from_str("./generated_images/images/uploads/img_name").unwrap(); let orig_img_path = PathBuf::from_str("/generated_images/images/uploads/img_name").unwrap();
let export_format = ExportFormat::AVIF; let export_format = ExportFormat::AVIF;
let resolutions = vec![ let resolutions = vec![
(320, 200, 1.), (320, 200, 1.),
@ -286,7 +267,7 @@ fn test_generate_srcset() {
(960, 600, 3.), (960, 600, 3.),
(1200, 750, 4.), (1200, 750, 4.),
]; ];
let result = "./generated_images/images/uploads/img_name_320x200.avif 1x, ./generated_images/images/uploads/img_name_480x300.avif 1.5x, ./generated_images/images/uploads/img_name_640x400.avif 2x, ./generated_images/images/uploads/img_name_960x600.avif 3x, ./generated_images/images/uploads/img_name_1200x750.avif 4x"; let result = "/generated_images/images/uploads/img_name_320x200.avif 1x, /generated_images/images/uploads/img_name_480x300.avif 1.5x, /generated_images/images/uploads/img_name_640x400.avif 2x, /generated_images/images/uploads/img_name_960x600.avif 3x, /generated_images/images/uploads/img_name_1200x750.avif 4x";
assert_eq!( assert_eq!(
generate_srcset(&orig_img_path, &export_format, &resolutions), generate_srcset(&orig_img_path, &export_format, &resolutions),
result result
@ -297,18 +278,18 @@ fn test_generate_srcset() {
fn test_generate_picture_markup() { fn test_generate_picture_markup() {
let width = 300; let width = 300;
let height = 200; let height = 200;
let orig_img_path = "../static/images/uploads/2020-03-23_20-24-06_393.jpg"; let orig_img_path = "/images/uploads/2020-03-23_20-24-06_393.jpg";
let result = r#"<picture> let result = r#"<picture>
<source <source
srcset="./generated_images/static/images/uploads/2020-03-23_20-24-06_393_300x200.avif 1x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_450x300.avif 1.5x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_600x400.avif 2x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_900x600.avif 3x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_1200x800.avif 4x" srcset="/generated_images/images/uploads/2020-03-23_20-24-06_393_300x200.avif 1x, /generated_images/images/uploads/2020-03-23_20-24-06_393_450x300.avif 1.5x, /generated_images/images/uploads/2020-03-23_20-24-06_393_600x400.avif 2x, /generated_images/images/uploads/2020-03-23_20-24-06_393_900x600.avif 3x, /generated_images/images/uploads/2020-03-23_20-24-06_393_1200x800.avif 4x"
type="image/avif" type="image/avif"
> >
<source <source
srcset="./generated_images/static/images/uploads/2020-03-23_20-24-06_393_300x200.jpg 1x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_450x300.jpg 1.5x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_600x400.jpg 2x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_900x600.jpg 3x, ./generated_images/static/images/uploads/2020-03-23_20-24-06_393_1200x800.jpg 4x" srcset="/generated_images/images/uploads/2020-03-23_20-24-06_393_300x200.jpg 1x, /generated_images/images/uploads/2020-03-23_20-24-06_393_450x300.jpg 1.5x, /generated_images/images/uploads/2020-03-23_20-24-06_393_600x400.jpg 2x, /generated_images/images/uploads/2020-03-23_20-24-06_393_900x600.jpg 3x, /generated_images/images/uploads/2020-03-23_20-24-06_393_1200x800.jpg 4x"
type="image/jpeg" type="image/jpeg"
> >
<img <img
src="./generated_images/static/images/uploads/2020-03-23_20-24-06_393_300x200.jpg" src="/generated_images/images/uploads/2020-03-23_20-24-06_393_300x200.jpg"
width="300" width="300"
height="200" height="200"
alt="Testing image alt" alt="Testing image alt"

View File

@ -1,5 +1,5 @@
/* /*
! tailwindcss v3.4.9 | MIT License | https://tailwindcss.com ! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com
*/ */
/* /*

View File

@ -1,11 +1,11 @@
<article class="grid grid-cols-[1fr_2fr] grid-flow-col gap-4"> <article class="grid grid-cols-[1fr_2fr] grid-flow-col gap-4">
<aside class="row-span-3"> <aside class="row-span-3">
<!-- TODO <figure> --> {% match post.metadata.thumbnail %}
<!-- TODO Thumbnail --> {% when Some with (orig_path) %}
<!-- <svg aria-hidden="true" class="h-12 w-12 fill-blue-950"> --> {{ crate::picture_generator::picture_markup_generator::generate_picture_markup(orig_path, 180, 240, "Article thumbnail").unwrap()|safe }}
<!-- <use xlink:href="/svg/icons-sprite.svg#mail" /> --> {% when None %}
<!-- </svg> --> <div> TODO default obrazok </div>
<figure> {% endmatch %}
</aside> </aside>
<header> <header>
<h3 class="text-lg font-bold mb-1">{{post.metadata.title}}</h3> <h3 class="text-lg font-bold mb-1">{{post.metadata.title}}</h3>