add image sizes to emoji

This commit is contained in:
liquidex 2024-07-19 19:15:24 +02:00
parent 9bf3409197
commit e44d5babca
2 changed files with 44 additions and 22 deletions

View file

@ -1,7 +1,8 @@
use std::{collections::HashMap, ffi::OsStr, fs::File, io::BufReader, path::Path}; use std::{collections::HashMap, ffi::OsStr, fs::File, io::BufReader, path::Path};
use anyhow::Context; use anyhow::Context;
use log::debug; use image::ImageError;
use log::{debug, error, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use walkdir::WalkDir; use walkdir::WalkDir;
@ -180,32 +181,43 @@ impl Config {
/// Data derived from the config. /// Data derived from the config.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ConfigDerivedData { pub struct ConfigDerivedData {
pub pic_sizes: HashMap<String, Option<PicSize>>, pub image_sizes: HashMap<String, Option<ImageSize>>,
} }
/// Picture size. This is useful for emitting <img> elements with a specific size to eliminate layout shifting. /// Image size. This is useful for emitting <img> elements with a specific size to eliminate
/// layout shifting.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PicSize { pub struct ImageSize {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,
} }
impl ConfigDerivedData { impl ConfigDerivedData {
fn read_pic_size(config: &Config, pic_id: &str) -> Option<PicSize> { fn read_image_size(filename: &str) -> Option<ImageSize> {
let pic_filename = config.pics.get(pic_id)?; let (width, height) = image::io::Reader::new(BufReader::new(File::open(filename).ok()?))
let (width, height) = image::io::Reader::new(BufReader::new( .with_guessed_format()
File::open(format!("static/pic/{pic_filename}")).ok()?, .map_err(ImageError::from)
)) .and_then(|i| i.into_dimensions())
.into_dimensions() // NOTE: Not being able to determine the image size is not the end of the world,
// so just warn the user if we couldn't do it.
// For example, currently SVG is not supported at all, which causes this to fail.
.inspect_err(|e| warn!("cannot read image size of {filename}: {e}"))
.ok()?; .ok()?;
Some(PicSize { width, height }) Some(ImageSize { width, height })
} }
pub fn pic_size(&mut self, config: &Config, pic_id: &str) -> Option<PicSize> { pub fn image_size(&mut self, filename: &str) -> Option<ImageSize> {
if !self.pic_sizes.contains_key(pic_id) { if !self.image_sizes.contains_key(filename) {
self.pic_sizes self.image_sizes
.insert(pic_id.to_owned(), Self::read_pic_size(config, pic_id)); .insert(filename.to_owned(), Self::read_image_size(filename));
} }
self.pic_sizes.get(pic_id).copied().flatten() self.image_sizes.get(filename).copied().flatten()
}
pub fn pic_size(&mut self, config: &Config, pic_id: &str) -> Option<ImageSize> {
config
.pics
.get(pic_id)
.and_then(|pic_filename| self.image_size(&format!("static/pic/{pic_filename}")))
} }
} }

View file

@ -30,7 +30,7 @@ use pulldown_cmark::escape::{escape_href, escape_html, StrWrite};
use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Tag}; use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Tag};
use pulldown_cmark::{CowStr, Event::*}; use pulldown_cmark::{CowStr, Event::*};
use crate::config::{Config, ConfigDerivedData, PicSize}; use crate::config::{Config, ConfigDerivedData, ImageSize};
use crate::html::highlight::highlight; use crate::html::highlight::highlight;
use crate::state::Treehouse; use crate::state::Treehouse;
@ -281,15 +281,13 @@ where
if let LiterateCodeKind::Output { placeholder_pic_id } = kind { if let LiterateCodeKind::Output { placeholder_pic_id } = kind {
if !placeholder_pic_id.is_empty() { if !placeholder_pic_id.is_empty() {
self.write( self.write("<img class=\"placeholder-image\" loading=\"lazy\" src=\"")?;
"<img class=\"placeholder-image\" loading=\"lazy\" src=\"",
)?;
escape_html( escape_html(
&mut self.writer, &mut self.writer,
&self.config.pic_url(placeholder_pic_id), &self.config.pic_url(placeholder_pic_id),
)?; )?;
self.write("\"")?; self.write("\"")?;
if let Some(PicSize { width, height }) = self if let Some(ImageSize { width, height }) = self
.config_derived_data .config_derived_data
.pic_size(self.config, placeholder_pic_id) .pic_size(self.config, placeholder_pic_id)
{ {
@ -552,6 +550,7 @@ where
escape_html(&mut self.writer, &branch.attributes.id)?; escape_html(&mut self.writer, &branch.attributes.id)?;
self.writer.write_str("\">")?; self.writer.write_str("\">")?;
} }
self.writer self.writer
.write_str("<img data-cast=\"emoji\" title=\":")?; .write_str("<img data-cast=\"emoji\" title=\":")?;
escape_html(&mut self.writer, name)?; escape_html(&mut self.writer, name)?;
@ -561,7 +560,18 @@ where
escape_html(&mut self.writer, filename)?; escape_html(&mut self.writer, filename)?;
self.writer.write_str("\" alt=\"")?; self.writer.write_str("\" alt=\"")?;
escape_html(&mut self.writer, name)?; escape_html(&mut self.writer, name)?;
if let Some(image_size) = self
.config_derived_data
.image_size(&format!("static/emoji/{filename}"))
{
write!(
self.writer,
"\" width=\"{}\" height=\"{}",
image_size.width, image_size.height
)?;
}
self.writer.write_str("\">")?; self.writer.write_str("\">")?;
if branch_id.is_some() { if branch_id.is_some() {
self.writer.write_str("</a>")?; self.writer.write_str("</a>")?;
} }