summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/visualize
diff options
context:
space:
mode:
authorBeiri22 <beier1@hs-mittweida.de>2023-08-05 13:58:28 +0200
committerGitHub <noreply@github.com>2023-08-05 13:58:28 +0200
commite3115336bfe26cfddcce41dc9177f3bbe6d7d88e (patch)
tree00d8a5bd1e77cdc6138e60f76931b98a90c7513f /crates/typst-library/src/visualize
parent49282626e98cb1fbbb06c774cc0ae470b2854af6 (diff)
Decode image (#1810)
Diffstat (limited to 'crates/typst-library/src/visualize')
-rw-r--r--crates/typst-library/src/visualize/image.rs109
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()),