diff options
| author | Beiri22 <beier1@hs-mittweida.de> | 2023-08-05 13:58:28 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-05 13:58:28 +0200 |
| commit | e3115336bfe26cfddcce41dc9177f3bbe6d7d88e (patch) | |
| tree | 00d8a5bd1e77cdc6138e60f76931b98a90c7513f /crates/typst-library/src/visualize | |
| parent | 49282626e98cb1fbbb06c774cc0ae470b2854af6 (diff) | |
Decode image (#1810)
Diffstat (limited to 'crates/typst-library/src/visualize')
| -rw-r--r-- | crates/typst-library/src/visualize/image.rs | 109 |
1 files changed, 93 insertions, 16 deletions
diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs index 514861e5..7c42ef3c 100644 --- a/crates/typst-library/src/visualize/image.rs +++ b/crates/typst-library/src/visualize/image.rs @@ -1,9 +1,10 @@ use std::ffi::OsStr; use std::path::Path; -use typst::eval::Bytes; +use typst::geom::Smart; use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat}; +use crate::compute::Readable; use crate::meta::{Figurable, LocalName}; use crate::prelude::*; use crate::text::families; @@ -32,6 +33,10 @@ use crate::text::families; /// Display: Image /// Category: visualize #[element(Layout, LocalName, Figurable)] +#[scope( + scope.define("decode", image_decode_func()); + scope +)] pub struct ImageElem { /// Path to an image file. #[required] @@ -47,8 +52,11 @@ pub struct ImageElem { /// The raw file data. #[internal] #[required] - #[parse(data)] - pub data: Bytes, + #[parse(Readable::Bytes(data))] + pub data: Readable, + + /// The image's format. Detected automatically by default. + pub format: Smart<ImageFormat>, /// The width of the image. pub width: Smart<Rel<Length>>, @@ -64,6 +72,61 @@ pub struct ImageElem { pub fit: ImageFit, } +/// Decode a raster of vector graphic from bytes or a string. +/// +/// ## Example { #example } +/// ```example +/// #let original = read("diagram.svg") +/// #let changed = original.replace( +/// "#2B80FF", // blue +/// green.hex(), +/// ) +/// +/// #image.decode(original) +/// #image.decode(changed) +/// ``` +/// +/// Display: Decode Image +/// Category: visualize +#[func] +pub fn image_decode( + /// The data to decode as an image. Can be a string for SVGs. + data: Readable, + /// The image's format. Detected automatically by default. + #[named] + format: Option<Smart<ImageFormat>>, + /// The width of the image. + #[named] + width: Option<Smart<Rel<Length>>>, + /// The height of the image. + #[named] + height: Option<Smart<Rel<Length>>>, + /// A text describing the image. + #[named] + alt: Option<Option<EcoString>>, + /// How the image should adjust itself to a given area. + #[named] + fit: Option<ImageFit>, +) -> StrResult<Content> { + let mut elem = ImageElem::new(EcoString::new(), data); + if let Some(format) = format { + elem.push_format(format); + } + if let Some(width) = width { + elem.push_width(width); + } + if let Some(height) = height { + elem.push_height(height); + } + if let Some(alt) = alt { + elem.push_alt(alt); + } + if let Some(fit) = fit { + elem.push_fit(fit); + } + Ok(elem.pack()) +} + impl Layout for ImageElem { #[tracing::instrument(name = "ImageElem::layout", skip_all)] fn layout( @@ -72,22 +135,36 @@ impl Layout for ImageElem { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let ext = Path::new(self.path().as_str()) - .extension() - .and_then(OsStr::to_str) - .unwrap_or_default() - .to_lowercase(); - - let format = match ext.as_str() { - "png" => ImageFormat::Raster(RasterFormat::Png), - "jpg" | "jpeg" => ImageFormat::Raster(RasterFormat::Jpg), - "gif" => ImageFormat::Raster(RasterFormat::Gif), - "svg" | "svgz" => ImageFormat::Vector(VectorFormat::Svg), - _ => bail!(self.span(), "unknown image format"), + // Take the format that was explicitly defined, or parse the extention, + // or try to detect the format. + let data = self.data(); + let format = match self.format(styles) { + Smart::Custom(v) => v, + Smart::Auto => { + let ext = Path::new(self.path().as_str()) + .extension() + .and_then(OsStr::to_str) + .unwrap_or_default() + .to_lowercase(); + + match ext.as_str() { + "png" => ImageFormat::Raster(RasterFormat::Png), + "jpg" | "jpeg" => ImageFormat::Raster(RasterFormat::Jpg), + "gif" => ImageFormat::Raster(RasterFormat::Gif), + "svg" | "svgz" => ImageFormat::Vector(VectorFormat::Svg), + _ => match &data { + Readable::Str(_) => ImageFormat::Vector(VectorFormat::Svg), + Readable::Bytes(bytes) => match RasterFormat::detect(bytes) { + Some(f) => ImageFormat::Raster(f), + None => bail!(self.span(), "unknown image format"), + }, + }, + } + } }; let image = Image::with_fonts( - self.data(), + data.into(), format, vt.world, families(styles).next().as_ref().map(|f| f.as_str()), |
