diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-10-31 15:52:16 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-10-31 15:52:35 +0100 |
| commit | 5b344b663a3d224134923eea0d67ebf44c069b07 (patch) | |
| tree | 34a5fb464a38b9d4cb11294379b3ddf351dfce21 /src/library/image.rs | |
| parent | feff013abb17f31bc5305fe77fe67cf615c19ff2 (diff) | |
Reorganize modules
Instead of separating functionality into layout and library, everything lives in the library now. This way, related things live side by side and there are no duplicate file names in the two directories.
Diffstat (limited to 'src/library/image.rs')
| -rw-r--r-- | src/library/image.rs | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/library/image.rs b/src/library/image.rs new file mode 100644 index 00000000..c2273502 --- /dev/null +++ b/src/library/image.rs @@ -0,0 +1,68 @@ +use std::io; + +use super::prelude::*; +use crate::diag::Error; +use crate::image::ImageId; + +/// `image`: An image. +pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + let path = args.expect::<Spanned<Str>>("path to image file")?; + let width = args.named("width")?; + let height = args.named("height")?; + + let full = ctx.make_path(&path.v); + let id = ctx.images.load(&full).map_err(|err| { + Error::boxed(path.span, match err.kind() { + io::ErrorKind::NotFound => "file not found".into(), + _ => format!("failed to load image ({})", err), + }) + })?; + + Ok(Value::Template(Template::from_inline(move |_| ImageNode { + id, + width, + height, + }))) +} + +/// An image node. +#[derive(Debug, Hash)] +pub struct ImageNode { + /// The id of the image file. + pub id: ImageId, + /// The fixed width, if any. + pub width: Option<Linear>, + /// The fixed height, if any. + pub height: Option<Linear>, +} + +impl InlineLevel for ImageNode { + fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { + let img = ctx.images.get(self.id); + let pixel_size = Spec::new(img.width() as f64, img.height() as f64); + let pixel_ratio = pixel_size.x / pixel_size.y; + + let width = self.width.map(|w| w.resolve(base.w)); + let height = self.height.map(|w| w.resolve(base.h)); + + let size = match (width, height) { + (Some(width), Some(height)) => Size::new(width, height), + (Some(width), None) => Size::new(width, width / pixel_ratio), + (None, Some(height)) => Size::new(height * pixel_ratio, height), + (None, None) => { + if space.is_finite() { + // Fit to width. + Size::new(space, space / pixel_ratio) + } else { + // Unbounded width, we have to make up something, + // so it is 1pt per pixel. + pixel_size.map(Length::pt).to_size() + } + } + }; + + let mut frame = Frame::new(size, size.h); + frame.push(Point::zero(), Element::Image(self.id, size)); + frame + } +} |
