refactor: merge Image::new and Image::with_fonts

This commit is contained in:
frozolotl 2024-12-13 16:30:13 +01:00
parent 259a029723
commit 4993bae782
6 changed files with 109 additions and 111 deletions

View File

@ -10,8 +10,8 @@ use typst_library::layout::{
use typst_library::loading::Readable;
use typst_library::text::families;
use typst_library::visualize::{
Curve, Image, ImageElem, ImageFit, ImageFormat, ImageSource, RasterFormat,
VectorFormat,
Curve, Image, ImageElem, ImageFit, ImageFormat, ImageOptions, ImageSource,
RasterFormat, VectorFormat,
};
/// Layout the image.
@ -57,13 +57,15 @@ pub fn layout_image(
}
// Construct the image itself.
let image = Image::with_fonts(
let image = Image::new(
source.clone(),
format,
elem.alt(styles),
engine.world,
&families(styles).map(|f| f.as_str()).collect::<Vec<_>>(),
elem.flatten_text(styles),
&ImageOptions {
alt: elem.alt(styles),
world: Some(engine.world),
families: &families(styles).map(|f| f.as_str()).collect::<Vec<_>>(),
flatten_text: elem.flatten_text(styles),
},
)
.at(span)?;

View File

@ -105,8 +105,11 @@ fn draw_raster_glyph(
upem: Abs,
raster_image: ttf_parser::RasterGlyphImage,
) -> Option<()> {
let image =
Image::new(Bytes::from(raster_image.data).into(), RasterFormat::Png.into(), None)
let image = Image::new(
Bytes::from(raster_image.data).into(),
RasterFormat::Png.into(),
&Default::default(),
)
.ok()?;
// Apple Color emoji doesn't provide offset information (or at least
@ -180,8 +183,12 @@ fn draw_colr_glyph(
let data = svg.end_document().into_bytes();
let image =
Image::new(Bytes::from(data).into(), VectorFormat::Svg.into(), None).ok()?;
let image = Image::new(
Bytes::from(data).into(),
VectorFormat::Svg.into(),
&Default::default(),
)
.ok()?;
let y_shift = Abs::pt(upem.to_pt() - y_max);
let position = Point::new(Abs::pt(x_min), y_shift);
@ -257,7 +264,7 @@ fn draw_svg_glyph(
);
let source = ImageSource::Readable(Readable::Str(wrapper_svg.into()));
let image = Image::new(source, VectorFormat::Svg.into(), None).ok()?;
let image = Image::new(source, VectorFormat::Svg.into(), &Default::default()).ok()?;
let position = Point::new(Abs::pt(left), Abs::pt(top) + upem);
let size = Size::new(Abs::pt(width), Abs::pt(height));

View File

@ -8,6 +8,7 @@ pub use self::raster::{RasterFormat, RasterImage};
pub use self::svg::SvgImage;
use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
use std::sync::Arc;
use comemo::Tracked;
@ -209,17 +210,6 @@ struct Repr {
alt: Option<EcoString>,
}
/// A kind of image.
#[derive(Hash)]
pub enum ImageKind {
/// A raster image.
Raster(RasterImage),
/// An SVG image.
Svg(SvgImage),
/// An image constructed from a pixmap.
Pixmap(Pixmap),
}
impl Image {
/// When scaling an image to it's natural size, we default to this DPI
/// if the image doesn't contain DPI metadata.
@ -234,7 +224,7 @@ impl Image {
pub fn new(
source: ImageSource,
format: ImageFormat,
alt: Option<EcoString>,
options: &ImageOptions,
) -> StrResult<Image> {
let kind = match format {
ImageFormat::Raster(format) => {
@ -247,7 +237,7 @@ impl Image {
let ImageSource::Readable(readable) = source else {
bail!("expected readable source for the given format (str or bytes)");
};
ImageKind::Svg(SvgImage::new(readable.into())?)
ImageKind::Svg(SvgImage::new(readable.into(), options)?)
}
ImageFormat::Pixmap(format) => {
let ImageSource::Pixmap(source) = source else {
@ -257,47 +247,7 @@ impl Image {
}
};
Ok(Self(Arc::new(LazyHash::new(Repr { kind, alt }))))
}
/// Create a possibly font-dependent image from a buffer and a format.
#[comemo::memoize]
#[typst_macros::time(name = "load image")]
pub fn with_fonts(
source: ImageSource,
format: ImageFormat,
alt: Option<EcoString>,
world: Tracked<dyn World + '_>,
families: &[&str],
flatten_text: bool,
) -> StrResult<Image> {
let kind = match format {
ImageFormat::Raster(format) => {
let ImageSource::Readable(readable) = source else {
bail!("expected readable source for the given format (str or bytes)");
};
ImageKind::Raster(RasterImage::new(readable.into(), format)?)
}
ImageFormat::Vector(VectorFormat::Svg) => {
let ImageSource::Readable(readable) = source else {
bail!("expected readable source for the given format (str or bytes)");
};
ImageKind::Svg(SvgImage::with_fonts(
readable.into(),
world,
flatten_text,
families,
)?)
}
ImageFormat::Pixmap(format) => {
let ImageSource::Pixmap(source) = source else {
bail!("source must be pixmap");
};
ImageKind::Pixmap(Pixmap::new(source, format)?)
}
};
Ok(Self(Arc::new(LazyHash::new(Repr { kind, alt }))))
Ok(Self(Arc::new(LazyHash::new(Repr { kind, alt: options.alt.clone() }))))
}
/// The format of the image.
@ -433,3 +383,40 @@ cast! {
v: VectorFormat => Self::Vector(v),
v: PixmapFormat => Self::Pixmap(v),
}
/// A kind of image.
#[derive(Hash)]
pub enum ImageKind {
/// A raster image.
Raster(RasterImage),
/// An SVG image.
Svg(SvgImage),
/// An image constructed from a pixmap.
Pixmap(Pixmap),
}
pub struct ImageOptions<'a> {
pub alt: Option<EcoString>,
pub world: Option<Tracked<'a, dyn World + 'a>>,
pub families: &'a [&'a str],
pub flatten_text: bool,
}
impl Default for ImageOptions<'_> {
fn default() -> Self {
ImageOptions {
alt: None,
world: None,
families: &[],
flatten_text: false,
}
}
}
impl Hash for ImageOptions<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.alt.hash(state);
self.families.hash(state);
self.flatten_text.hash(state);
}
}

View File

@ -14,6 +14,8 @@ use crate::text::{
};
use crate::World;
use super::ImageOptions;
/// A decoded SVG.
#[derive(Clone, Hash)]
pub struct SvgImage(Arc<Repr>);
@ -28,30 +30,14 @@ struct Repr {
}
impl SvgImage {
/// Decode an SVG image without fonts.
/// Decode an SVG image.
#[comemo::memoize]
pub fn new(data: Bytes) -> StrResult<SvgImage> {
let tree =
usvg::Tree::from_data(&data, &base_options()).map_err(format_usvg_error)?;
Ok(Self(Arc::new(Repr {
data,
size: tree_size(&tree),
font_hash: 0,
flatten_text: false,
tree,
})))
}
/// Decode an SVG image with access to fonts.
#[comemo::memoize]
pub fn with_fonts(
data: Bytes,
world: Tracked<dyn World + '_>,
flatten_text: bool,
families: &[&str],
) -> StrResult<SvgImage> {
pub fn new(data: Bytes, options: &ImageOptions) -> StrResult<SvgImage> {
let (tree, font_hash) = match options.world {
Some(world) => {
let book = world.book();
let resolver = Mutex::new(FontResolver::new(world, book, families));
let resolver =
Mutex::new(FontResolver::new(world, book, options.families));
let tree = usvg::Tree::from_data(
&data,
&usvg::Options {
@ -60,7 +46,11 @@ impl SvgImage {
resolver.lock().unwrap().select_font(font, db)
}),
select_fallback: Box::new(|c, exclude_fonts, db| {
resolver.lock().unwrap().select_fallback(c, exclude_fonts, db)
resolver.lock().unwrap().select_fallback(
c,
exclude_fonts,
db,
)
}),
},
..base_options()
@ -68,11 +58,20 @@ impl SvgImage {
)
.map_err(format_usvg_error)?;
let font_hash = resolver.into_inner().unwrap().finish();
(tree, font_hash)
}
None => {
let tree = usvg::Tree::from_data(&data, &base_options())
.map_err(format_usvg_error)?;
let font_hash = 0;
(tree, font_hash)
}
};
Ok(Self(Arc::new(Repr {
data,
size: tree_size(&tree),
font_hash,
flatten_text,
flatten_text: options.flatten_text,
tree,
})))
}

View File

@ -244,8 +244,11 @@ fn convert_bitmap_glyph_to_image(font: &Font, id: GlyphId) -> Option<(Image, f64
if raster.format != ttf_parser::RasterImageFormat::PNG {
return None;
}
let image =
Image::new(Bytes::from(raster.data).into(), RasterFormat::Png.into(), None)
let image = Image::new(
Bytes::from(raster.data).into(),
RasterFormat::Png.into(),
&Default::default(),
)
.ok()?;
Some((image, raster.x as f64, raster.y as f64))
}

View File

@ -201,7 +201,7 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
)
--- image-png-but-pixmap-format ---
// Error: 1:2-4:2 source must be pixmap
// Error: 1:2-4:2 source must be a pixmap
#image.decode(
read("/assets/images/tiger.jpg", encoding: none),
format: "rgba8",