max resolution for blog post displays
This commit is contained in:
parent
7f483af9cf
commit
3ea78620cd
@ -24,3 +24,4 @@ It should be used from the templates as well
|
|||||||
pub mod export_format;
|
pub mod export_format;
|
||||||
pub mod image_generator;
|
pub mod image_generator;
|
||||||
pub mod picture_markup_generator;
|
pub mod picture_markup_generator;
|
||||||
|
pub mod resolutions;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@ -7,7 +6,10 @@ use std::{
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use image::{image_dimensions, ImageReader};
|
use image::{image_dimensions, ImageReader};
|
||||||
|
|
||||||
use super::{export_format::ExportFormat, image_generator::generate_images};
|
use super::{
|
||||||
|
export_format::ExportFormat, image_generator::generate_images,
|
||||||
|
resolutions::get_max_resolution_with_crop,
|
||||||
|
};
|
||||||
|
|
||||||
pub const PIXEL_DENSITIES: [f32; 5] = [1., 1.5, 2., 3., 4.];
|
pub const PIXEL_DENSITIES: [f32; 5] = [1., 1.5, 2., 3., 4.];
|
||||||
|
|
||||||
@ -18,15 +20,6 @@ pub fn generate_picture_markup(
|
|||||||
alt_text: &str,
|
alt_text: &str,
|
||||||
generate_image: bool,
|
generate_image: bool,
|
||||||
) -> Result<String, anyhow::Error> {
|
) -> Result<String, anyhow::Error> {
|
||||||
if !orig_img_path.starts_with("/") {
|
|
||||||
return Ok(format!(
|
|
||||||
r#"<img
|
|
||||||
alt="{alt_text}"
|
|
||||||
src="{orig_img_path}"
|
|
||||||
/>"#
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -123,8 +116,8 @@ fn get_resolutions(
|
|||||||
let mut resolutions: Vec<(u32, u32, f32)> = vec![];
|
let mut resolutions: Vec<(u32, u32, f32)> = vec![];
|
||||||
for pixel_density in PIXEL_DENSITIES {
|
for pixel_density in PIXEL_DENSITIES {
|
||||||
let (density_width, density_height) = (
|
let (density_width, density_height) = (
|
||||||
(pixel_density * width as f32).floor() as u32,
|
(pixel_density * width as f32) as u32,
|
||||||
(pixel_density * height as f32).floor() as u32,
|
(pixel_density * height as f32) as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
// The equal sign `=` was added just to prevent next occurence of the loop
|
// The equal sign `=` was added just to prevent next occurence of the loop
|
||||||
@ -132,7 +125,7 @@ fn get_resolutions(
|
|||||||
// See test case #1
|
// See test case #1
|
||||||
if density_width >= orig_width || density_height >= orig_height {
|
if density_width >= orig_width || density_height >= orig_height {
|
||||||
let (max_width, max_height) =
|
let (max_width, max_height) =
|
||||||
get_max_resolution((orig_width, orig_height), width, height);
|
get_max_resolution_with_crop((orig_width, orig_height), width, height);
|
||||||
resolutions.push((max_width, max_height, pixel_density));
|
resolutions.push((max_width, max_height, pixel_density));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -171,51 +164,6 @@ fn test_get_resolutions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_resolution(
|
|
||||||
(orig_width, orig_height): (u32, u32),
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
) -> (u32, u32) {
|
|
||||||
let width_scale = orig_width as f32 / width as f32;
|
|
||||||
let height_scale = orig_height as f32 / height as f32;
|
|
||||||
|
|
||||||
let scale = match width_scale.partial_cmp(&height_scale) {
|
|
||||||
Some(Ordering::Less) => width_scale,
|
|
||||||
Some(Ordering::Greater) => height_scale,
|
|
||||||
_ => width_scale,
|
|
||||||
};
|
|
||||||
(
|
|
||||||
(width as f32 * scale) as u32,
|
|
||||||
(height as f32 * scale) as u32,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_max_resolution() {
|
|
||||||
assert_eq!(
|
|
||||||
get_max_resolution((320, 200), 320, 200),
|
|
||||||
(320, 200),
|
|
||||||
"Original size fits"
|
|
||||||
);
|
|
||||||
// THINK: Real curious if this is what I want to do. Rather than use CSS to `object-cover` original image size
|
|
||||||
assert_eq!(
|
|
||||||
get_max_resolution((200, 200), 300, 200),
|
|
||||||
(200, 133),
|
|
||||||
"Image has to be smaller"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
get_max_resolution((1000, 1000), 200, 100),
|
|
||||||
(1000, 500),
|
|
||||||
"width is maxed"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
get_max_resolution((1000, 1000), 100, 200),
|
|
||||||
(500, 1000),
|
|
||||||
"height is maxed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn strip_prefixes(path: &Path) -> &Path {
|
fn strip_prefixes(path: &Path) -> &Path {
|
||||||
// Loop to remove all leading "../" components
|
// Loop to remove all leading "../" components
|
||||||
let mut parent_path = path
|
let mut parent_path = path
|
||||||
|
91
axum_server/src/picture_generator/resolutions.rs
Normal file
91
axum_server/src/picture_generator/resolutions.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
pub fn get_max_resolution_with_crop(
|
||||||
|
(orig_width, orig_height): (u32, u32),
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> (u32, u32) {
|
||||||
|
let width_scale = orig_width as f32 / width as f32;
|
||||||
|
let height_scale = orig_height as f32 / height as f32;
|
||||||
|
|
||||||
|
let scale = width_scale.min(height_scale);
|
||||||
|
(
|
||||||
|
(width as f32 * scale) as u32,
|
||||||
|
(height as f32 * scale) as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_max_resolution_with_crop() {
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution_with_crop((320, 200), 320, 200),
|
||||||
|
(320, 200),
|
||||||
|
"Original size fits"
|
||||||
|
);
|
||||||
|
// THINK: Real curious if this is what I want to do. Rather than use CSS to `object-cover` original image size
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution_with_crop((200, 200), 300, 200),
|
||||||
|
(200, 133),
|
||||||
|
"Image has to be smaller"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution_with_crop((1000, 1000), 200, 100),
|
||||||
|
(1000, 500),
|
||||||
|
"width is maxed"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution_with_crop((1000, 1000), 100, 200),
|
||||||
|
(500, 1000),
|
||||||
|
"height is maxed"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution_with_crop((300, 200), 600, 500),
|
||||||
|
(240, 200),
|
||||||
|
"image has to be scaled down"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_resolution(
|
||||||
|
(orig_width, orig_height): (u32, u32),
|
||||||
|
max_width: u32,
|
||||||
|
max_height: u32,
|
||||||
|
) -> (u32, u32) {
|
||||||
|
// If the original dimensions are within the max dimensions, return them as is
|
||||||
|
if orig_width <= max_width && orig_height <= max_height {
|
||||||
|
return (orig_width, orig_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width_scale = max_width as f32 / orig_width as f32;
|
||||||
|
let height_scale = max_height as f32 / orig_height as f32;
|
||||||
|
|
||||||
|
// Determine the scaling factor to ensure the image fits within the bounds
|
||||||
|
let scale = width_scale.min(height_scale);
|
||||||
|
|
||||||
|
(
|
||||||
|
(orig_width as f32 * scale).round() as u32,
|
||||||
|
(orig_height as f32 * scale).round() as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_max_resolution() {
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution((999, 675), 1000, 800),
|
||||||
|
(999, 675),
|
||||||
|
"Original size fits"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution((1100, 400), 1000, 800),
|
||||||
|
(1000, 364),
|
||||||
|
"Image should be resized to fit within max dimensions"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution((1100, 1200), 1000, 800),
|
||||||
|
(733, 800),
|
||||||
|
"Image should be resized to fit within max dimensions"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_max_resolution((1100, 800), 1000, 800),
|
||||||
|
(1000, 727),
|
||||||
|
"Image should be resized to fit within max dimensions"
|
||||||
|
);
|
||||||
|
}
|
@ -3,12 +3,17 @@ use std::path::Path;
|
|||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use gray_matter::{engine::YAML, Matter};
|
use gray_matter::{engine::YAML, Matter};
|
||||||
|
use image::image_dimensions;
|
||||||
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd};
|
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer};
|
use serde::{de::DeserializeOwned, Deserialize, Deserializer};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::picture_generator::picture_markup_generator::generate_picture_markup;
|
use crate::picture_generator::{
|
||||||
|
picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MAX_BLOG_IMAGE_RESOLUTION: (u32, u32) = (1000, 800);
|
||||||
|
|
||||||
pub fn deserialize_date<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
pub fn deserialize_date<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||||
where
|
where
|
||||||
@ -84,12 +89,32 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
|
|||||||
title,
|
title,
|
||||||
id,
|
id,
|
||||||
}) => {
|
}) => {
|
||||||
// TODO Get image resolution
|
if !dest_url.starts_with("/") {
|
||||||
|
return Event::Html(
|
||||||
|
format!(
|
||||||
|
r#"<img
|
||||||
|
alt="{title}"
|
||||||
|
src="{dest_url}"
|
||||||
|
/>"#
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dev_only_img_path =
|
||||||
|
Path::new("../static/").join(dest_url.strip_prefix("/").unwrap_or(&dest_url));
|
||||||
|
let img_dimensions = image_dimensions(&dev_only_img_path).unwrap();
|
||||||
|
|
||||||
|
let (max_width, max_height) = get_max_resolution(
|
||||||
|
img_dimensions,
|
||||||
|
MAX_BLOG_IMAGE_RESOLUTION.0,
|
||||||
|
MAX_BLOG_IMAGE_RESOLUTION.1,
|
||||||
|
);
|
||||||
|
|
||||||
// Place image into the content with scaled reso to a boundary
|
// Place image into the content with scaled reso to a boundary
|
||||||
let picture_markup =
|
let picture_markup =
|
||||||
generate_picture_markup(&dest_url, 500, 500, &title, generate_images).unwrap_or(
|
generate_picture_markup(&dest_url, max_width, max_height, &title, generate_images)
|
||||||
format!(
|
.unwrap_or(format!(
|
||||||
r#"
|
r#"
|
||||||
<img
|
<img
|
||||||
alt="{alt}"
|
alt="{alt}"
|
||||||
@ -97,8 +122,7 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
|
|||||||
/>"#,
|
/>"#,
|
||||||
alt = title,
|
alt = title,
|
||||||
src = dest_url,
|
src = dest_url,
|
||||||
),
|
));
|
||||||
);
|
|
||||||
// let picture_markup = format!(
|
// let picture_markup = format!(
|
||||||
// r#"
|
// r#"
|
||||||
// <img
|
// <img
|
||||||
|
Loading…
Reference in New Issue
Block a user