mirror of
https://github.com/typst/typst
synced 2025-08-12 22:27:56 +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.
|
// Construct the image itself.
|
||||||
let kind = match format {
|
let kind = match format {
|
||||||
ImageFormat::Raster(format) => {
|
ImageFormat::Raster(format) => ImageKind::Raster(
|
||||||
ImageKind::Raster(RasterImage::new(data.clone(), format).at(span)?)
|
RasterImage::new(
|
||||||
}
|
data.clone(),
|
||||||
|
format,
|
||||||
|
elem.icc(styles).as_ref().map(|icc| icc.derived.clone()),
|
||||||
|
)
|
||||||
|
.at(span)?,
|
||||||
|
),
|
||||||
ImageFormat::Vector(VectorFormat::Svg) => ImageKind::Svg(
|
ImageFormat::Vector(VectorFormat::Svg) => ImageKind::Svg(
|
||||||
SvgImage::with_fonts(
|
SvgImage::with_fonts(
|
||||||
data.clone(),
|
data.clone(),
|
||||||
|
@ -105,8 +105,11 @@ fn draw_raster_glyph(
|
|||||||
raster_image: ttf_parser::RasterGlyphImage,
|
raster_image: ttf_parser::RasterGlyphImage,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let data = Bytes::new(raster_image.data.to_vec());
|
let data = Bytes::new(raster_image.data.to_vec());
|
||||||
let image =
|
let image = Image::new(
|
||||||
Image::new(RasterImage::new(data, ExchangeFormat::Png).ok()?, None, Smart::Auto);
|
RasterImage::new(data, ExchangeFormat::Png, Smart::Auto).ok()?,
|
||||||
|
None,
|
||||||
|
Smart::Auto,
|
||||||
|
);
|
||||||
|
|
||||||
// Apple Color emoji doesn't provide offset information (or at least
|
// Apple Color emoji doesn't provide offset information (or at least
|
||||||
// not in a way ttf-parser understands), so we artificially shift their
|
// 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.
|
/// _Note:_ The exact look may differ across PDF viewers.
|
||||||
pub scaling: Smart<ImageScaling>,
|
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
|
/// Whether text in SVG images should be converted into curves before
|
||||||
/// embedding. This will result in the text becoming unselectable in the
|
/// embedding. This will result in the text becoming unselectable in the
|
||||||
/// output.
|
/// output.
|
||||||
|
@ -12,7 +12,7 @@ use image::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::diag::{bail, StrResult};
|
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.
|
/// A decoded raster image.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
@ -23,31 +23,43 @@ struct Repr {
|
|||||||
data: Bytes,
|
data: Bytes,
|
||||||
format: RasterFormat,
|
format: RasterFormat,
|
||||||
dynamic: image::DynamicImage,
|
dynamic: image::DynamicImage,
|
||||||
icc: Option<Vec<u8>>,
|
icc: Option<Bytes>,
|
||||||
dpi: Option<f64>,
|
dpi: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RasterImage {
|
impl RasterImage {
|
||||||
/// Decode a raster image.
|
/// Decode a raster image.
|
||||||
pub fn new(data: Bytes, format: impl Into<RasterFormat>) -> StrResult<RasterImage> {
|
pub fn new(
|
||||||
Self::new_impl(data, format.into())
|
data: Bytes,
|
||||||
|
format: impl Into<RasterFormat>,
|
||||||
|
icc: Smart<Bytes>,
|
||||||
|
) -> StrResult<RasterImage> {
|
||||||
|
Self::new_impl(data, format.into(), icc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The internal, non-generic implementation.
|
/// The internal, non-generic implementation.
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
#[typst_macros::time(name = "load raster image")]
|
#[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 {
|
let (dynamic, icc, dpi) = match format {
|
||||||
RasterFormat::Exchange(format) => {
|
RasterFormat::Exchange(format) => {
|
||||||
fn decode_with<T: ImageDecoder>(
|
fn decode<T: ImageDecoder>(
|
||||||
decoder: ImageResult<T>,
|
decoder: ImageResult<T>,
|
||||||
) -> ImageResult<(image::DynamicImage, Option<Vec<u8>>)> {
|
icc: Smart<Bytes>,
|
||||||
|
) -> ImageResult<(image::DynamicImage, Option<Bytes>)> {
|
||||||
let mut decoder = decoder?;
|
let mut decoder = decoder?;
|
||||||
let icc = decoder
|
let icc = icc.custom().or_else(|| {
|
||||||
|
decoder
|
||||||
.icc_profile()
|
.icc_profile()
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|icc| !icc.is_empty());
|
.filter(|icc| !icc.is_empty())
|
||||||
|
.map(Bytes::new)
|
||||||
|
});
|
||||||
decoder.set_limits(Limits::default())?;
|
decoder.set_limits(Limits::default())?;
|
||||||
let dynamic = image::DynamicImage::from_decoder(decoder)?;
|
let dynamic = image::DynamicImage::from_decoder(decoder)?;
|
||||||
Ok((dynamic, icc))
|
Ok((dynamic, icc))
|
||||||
@ -55,9 +67,9 @@ impl RasterImage {
|
|||||||
|
|
||||||
let cursor = io::Cursor::new(&data);
|
let cursor = io::Cursor::new(&data);
|
||||||
let (mut dynamic, icc) = match format {
|
let (mut dynamic, icc) = match format {
|
||||||
ExchangeFormat::Jpg => decode_with(JpegDecoder::new(cursor)),
|
ExchangeFormat::Jpg => decode(JpegDecoder::new(cursor), icc),
|
||||||
ExchangeFormat::Png => decode_with(PngDecoder::new(cursor)),
|
ExchangeFormat::Png => decode(PngDecoder::new(cursor), icc),
|
||||||
ExchangeFormat::Gif => decode_with(GifDecoder::new(cursor)),
|
ExchangeFormat::Gif => decode(GifDecoder::new(cursor), icc),
|
||||||
}
|
}
|
||||||
.map_err(format_image_error)?;
|
.map_err(format_image_error)?;
|
||||||
|
|
||||||
@ -115,7 +127,7 @@ impl RasterImage {
|
|||||||
PixelEncoding::Lumaa8 => to::<image::LumaA<u8>>(&data, format).into(),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{ExchangeFormat, RasterImage};
|
use super::*;
|
||||||
use crate::foundations::Bytes;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_image_dpi() {
|
fn test_image_dpi() {
|
||||||
@ -414,7 +425,7 @@ mod tests {
|
|||||||
fn test(path: &str, format: ExchangeFormat, dpi: f64) {
|
fn test(path: &str, format: ExchangeFormat, dpi: f64) {
|
||||||
let data = typst_dev_assets::get(path).unwrap();
|
let data = typst_dev_assets::get(path).unwrap();
|
||||||
let bytes = Bytes::new(data);
|
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));
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
let image = Image::new(
|
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,
|
None,
|
||||||
Smart::Auto,
|
Smart::Auto,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user