mirror of
https://github.com/typst/typst
synced 2025-08-10 13:17:55 +08:00
Add icc
argument
This commit is contained in:
parent
e00508f22d
commit
b68971d01e
@ -51,9 +51,14 @@ pub fn layout_image(
|
||||
|
||||
// Construct the image itself.
|
||||
let kind = match format {
|
||||
ImageFormat::Raster(format) => {
|
||||
ImageKind::Raster(RasterImage::new(data.clone(), format).at(span)?)
|
||||
}
|
||||
ImageFormat::Raster(format) => ImageKind::Raster(
|
||||
RasterImage::new(
|
||||
data.clone(),
|
||||
format,
|
||||
elem.icc(styles).as_ref().map(|icc| icc.derived.clone()),
|
||||
)
|
||||
.at(span)?,
|
||||
),
|
||||
ImageFormat::Vector(VectorFormat::Svg) => ImageKind::Svg(
|
||||
SvgImage::with_fonts(
|
||||
data.clone(),
|
||||
|
@ -105,8 +105,11 @@ fn draw_raster_glyph(
|
||||
raster_image: ttf_parser::RasterGlyphImage,
|
||||
) -> Option<()> {
|
||||
let data = Bytes::new(raster_image.data.to_vec());
|
||||
let image =
|
||||
Image::new(RasterImage::new(data, ExchangeFormat::Png).ok()?, None, Smart::Auto);
|
||||
let image = Image::new(
|
||||
RasterImage::new(data, ExchangeFormat::Png, Smart::Auto).ok()?,
|
||||
None,
|
||||
Smart::Auto,
|
||||
);
|
||||
|
||||
// Apple Color emoji doesn't provide offset information (or at least
|
||||
// not in a way ttf-parser understands), so we artificially shift their
|
||||
|
@ -127,6 +127,21 @@ pub struct ImageElem {
|
||||
/// _Note:_ The exact look may differ across PDF viewers.
|
||||
pub scaling: Smart<ImageScaling>,
|
||||
|
||||
/// An ICC profile for the image.
|
||||
///
|
||||
/// ICC profiles define how to interpret the colors in an image. When set
|
||||
/// to `{auto}`, Typst will try to extract an ICC profile from the image.
|
||||
#[parse(match args.named::<Spanned<Smart<DataSource>>>("icc")? {
|
||||
Some(Spanned { v: Smart::Custom(source), span }) => Some(Smart::Custom({
|
||||
let data = Spanned::new(&source, span).load(engine.world)?;
|
||||
Derived::new(source, data)
|
||||
})),
|
||||
Some(Spanned { v: Smart::Auto, .. }) => Some(Smart::Auto),
|
||||
None => None,
|
||||
})]
|
||||
#[borrowed]
|
||||
pub icc: Smart<Derived<DataSource, Bytes>>,
|
||||
|
||||
/// Whether text in SVG images should be converted into curves before
|
||||
/// embedding. This will result in the text becoming unselectable in the
|
||||
/// output.
|
||||
|
@ -12,7 +12,7 @@ use image::{
|
||||
};
|
||||
|
||||
use crate::diag::{bail, StrResult};
|
||||
use crate::foundations::{cast, dict, Bytes, Cast, Dict, Value};
|
||||
use crate::foundations::{cast, dict, Bytes, Cast, Dict, Smart, Value};
|
||||
|
||||
/// A decoded raster image.
|
||||
#[derive(Clone, Hash)]
|
||||
@ -23,31 +23,43 @@ struct Repr {
|
||||
data: Bytes,
|
||||
format: RasterFormat,
|
||||
dynamic: image::DynamicImage,
|
||||
icc: Option<Vec<u8>>,
|
||||
icc: Option<Bytes>,
|
||||
dpi: Option<f64>,
|
||||
}
|
||||
|
||||
impl RasterImage {
|
||||
/// Decode a raster image.
|
||||
pub fn new(data: Bytes, format: impl Into<RasterFormat>) -> StrResult<RasterImage> {
|
||||
Self::new_impl(data, format.into())
|
||||
pub fn new(
|
||||
data: Bytes,
|
||||
format: impl Into<RasterFormat>,
|
||||
icc: Smart<Bytes>,
|
||||
) -> StrResult<RasterImage> {
|
||||
Self::new_impl(data, format.into(), icc)
|
||||
}
|
||||
|
||||
/// The internal, non-generic implementation.
|
||||
#[comemo::memoize]
|
||||
#[typst_macros::time(name = "load raster image")]
|
||||
fn new_impl(data: Bytes, format: RasterFormat) -> StrResult<RasterImage> {
|
||||
fn new_impl(
|
||||
data: Bytes,
|
||||
format: RasterFormat,
|
||||
icc: Smart<Bytes>,
|
||||
) -> StrResult<RasterImage> {
|
||||
let (dynamic, icc, dpi) = match format {
|
||||
RasterFormat::Exchange(format) => {
|
||||
fn decode_with<T: ImageDecoder>(
|
||||
fn decode<T: ImageDecoder>(
|
||||
decoder: ImageResult<T>,
|
||||
) -> ImageResult<(image::DynamicImage, Option<Vec<u8>>)> {
|
||||
icc: Smart<Bytes>,
|
||||
) -> ImageResult<(image::DynamicImage, Option<Bytes>)> {
|
||||
let mut decoder = decoder?;
|
||||
let icc = decoder
|
||||
.icc_profile()
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|icc| !icc.is_empty());
|
||||
let icc = icc.custom().or_else(|| {
|
||||
decoder
|
||||
.icc_profile()
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|icc| !icc.is_empty())
|
||||
.map(Bytes::new)
|
||||
});
|
||||
decoder.set_limits(Limits::default())?;
|
||||
let dynamic = image::DynamicImage::from_decoder(decoder)?;
|
||||
Ok((dynamic, icc))
|
||||
@ -55,9 +67,9 @@ impl RasterImage {
|
||||
|
||||
let cursor = io::Cursor::new(&data);
|
||||
let (mut dynamic, icc) = match format {
|
||||
ExchangeFormat::Jpg => decode_with(JpegDecoder::new(cursor)),
|
||||
ExchangeFormat::Png => decode_with(PngDecoder::new(cursor)),
|
||||
ExchangeFormat::Gif => decode_with(GifDecoder::new(cursor)),
|
||||
ExchangeFormat::Jpg => decode(JpegDecoder::new(cursor), icc),
|
||||
ExchangeFormat::Png => decode(PngDecoder::new(cursor), icc),
|
||||
ExchangeFormat::Gif => decode(GifDecoder::new(cursor), icc),
|
||||
}
|
||||
.map_err(format_image_error)?;
|
||||
|
||||
@ -115,7 +127,7 @@ impl RasterImage {
|
||||
PixelEncoding::Lumaa8 => to::<image::LumaA<u8>>(&data, format).into(),
|
||||
};
|
||||
|
||||
(dynamic, None, None)
|
||||
(dynamic, icc.custom(), None)
|
||||
}
|
||||
};
|
||||
|
||||
@ -405,8 +417,7 @@ fn format_image_error(error: image::ImageError) -> EcoString {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ExchangeFormat, RasterImage};
|
||||
use crate::foundations::Bytes;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_image_dpi() {
|
||||
@ -414,7 +425,7 @@ mod tests {
|
||||
fn test(path: &str, format: ExchangeFormat, dpi: f64) {
|
||||
let data = typst_dev_assets::get(path).unwrap();
|
||||
let bytes = Bytes::new(data);
|
||||
let image = RasterImage::new(bytes, format).unwrap();
|
||||
let image = RasterImage::new(bytes, format, Smart::Auto).unwrap();
|
||||
assert_eq!(image.dpi().map(f64::round), Some(dpi));
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,12 @@ fn convert_bitmap_glyph_to_image(font: &Font, id: GlyphId) -> Option<(Image, f64
|
||||
return None;
|
||||
}
|
||||
let image = Image::new(
|
||||
RasterImage::new(Bytes::new(raster.data.to_vec()), ExchangeFormat::Png).ok()?,
|
||||
RasterImage::new(
|
||||
Bytes::new(raster.data.to_vec()),
|
||||
ExchangeFormat::Png,
|
||||
Smart::Auto,
|
||||
)
|
||||
.ok()?,
|
||||
None,
|
||||
Smart::Auto,
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user