mirror of
https://github.com/typst/typst
synced 2025-08-13 22:57:56 +08:00
refactor: merge Image::new and Image::with_fonts
This commit is contained in:
parent
259a029723
commit
4993bae782
@ -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)?;
|
||||
|
||||
|
@ -105,9 +105,12 @@ 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)
|
||||
.ok()?;
|
||||
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
|
||||
// not in a way ttf-parser understands), so we artificially shift their
|
||||
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,51 +30,48 @@ 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> {
|
||||
let book = world.book();
|
||||
let resolver = Mutex::new(FontResolver::new(world, book, families));
|
||||
let tree = usvg::Tree::from_data(
|
||||
&data,
|
||||
&usvg::Options {
|
||||
font_resolver: usvg::FontResolver {
|
||||
select_font: Box::new(|font, db| {
|
||||
resolver.lock().unwrap().select_font(font, db)
|
||||
}),
|
||||
select_fallback: Box::new(|c, exclude_fonts, db| {
|
||||
resolver.lock().unwrap().select_fallback(c, exclude_fonts, db)
|
||||
}),
|
||||
},
|
||||
..base_options()
|
||||
},
|
||||
)
|
||||
.map_err(format_usvg_error)?;
|
||||
let font_hash = resolver.into_inner().unwrap().finish();
|
||||
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, options.families));
|
||||
let tree = usvg::Tree::from_data(
|
||||
&data,
|
||||
&usvg::Options {
|
||||
font_resolver: usvg::FontResolver {
|
||||
select_font: Box::new(|font, db| {
|
||||
resolver.lock().unwrap().select_font(font, db)
|
||||
}),
|
||||
select_fallback: Box::new(|c, exclude_fonts, db| {
|
||||
resolver.lock().unwrap().select_fallback(
|
||||
c,
|
||||
exclude_fonts,
|
||||
db,
|
||||
)
|
||||
}),
|
||||
},
|
||||
..base_options()
|
||||
},
|
||||
)
|
||||
.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,
|
||||
})))
|
||||
}
|
||||
|
@ -244,9 +244,12 @@ 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)
|
||||
.ok()?;
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user