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 anyhow::Context;
use log::debug;
use image::ImageError;
use log::{debug, error, warn};
use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
@ -180,32 +181,43 @@ impl Config {
/// Data derived from the config.
#[derive(Debug, Clone, Default)]
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)]
pub struct PicSize {
pub struct ImageSize {
pub width: u32,
pub height: u32,
}
impl ConfigDerivedData {
fn read_pic_size(config: &Config, pic_id: &str) -> Option<PicSize> {
let pic_filename = config.pics.get(pic_id)?;
let (width, height) = image::io::Reader::new(BufReader::new(
File::open(format!("static/pic/{pic_filename}")).ok()?,
))
.into_dimensions()
.ok()?;
Some(PicSize { width, height })
fn read_image_size(filename: &str) -> Option<ImageSize> {
let (width, height) = image::io::Reader::new(BufReader::new(File::open(filename).ok()?))
.with_guessed_format()
.map_err(ImageError::from)
.and_then(|i| i.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()?;
Some(ImageSize { width, height })
}
pub fn pic_size(&mut self, config: &Config, pic_id: &str) -> Option<PicSize> {
if !self.pic_sizes.contains_key(pic_id) {
self.pic_sizes
.insert(pic_id.to_owned(), Self::read_pic_size(config, pic_id));
pub fn image_size(&mut self, filename: &str) -> Option<ImageSize> {
if !self.image_sizes.contains_key(filename) {
self.image_sizes
.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::{CowStr, Event::*};
use crate::config::{Config, ConfigDerivedData, PicSize};
use crate::config::{Config, ConfigDerivedData, ImageSize};
use crate::html::highlight::highlight;
use crate::state::Treehouse;
@ -281,15 +281,13 @@ where
if let LiterateCodeKind::Output { placeholder_pic_id } = kind {
if !placeholder_pic_id.is_empty() {
self.write(
"<img class=\"placeholder-image\" loading=\"lazy\" src=\"",
)?;
self.write("<img class=\"placeholder-image\" loading=\"lazy\" src=\"")?;
escape_html(
&mut self.writer,
&self.config.pic_url(placeholder_pic_id),
)?;
self.write("\"")?;
if let Some(PicSize { width, height }) = self
if let Some(ImageSize { width, height }) = self
.config_derived_data
.pic_size(self.config, placeholder_pic_id)
{
@ -552,6 +550,7 @@ where
escape_html(&mut self.writer, &branch.attributes.id)?;
self.writer.write_str("\">")?;
}
self.writer
.write_str("<img data-cast=\"emoji\" title=\":")?;
escape_html(&mut self.writer, name)?;
@ -561,7 +560,18 @@ where
escape_html(&mut self.writer, filename)?;
self.writer.write_str("\" alt=\"")?;
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("\">")?;
if branch_id.is_some() {
self.writer.write_str("</a>")?;
}