mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 13:17:19 -06:00
PDF: colorized images that can't be exported are cached as missing
colorized_image returned before its cache insertion when the image was missing, left out or undecodable, or when its color channel was larger than MAX_RASTER_ALLOC. Every placement of such an image then decoded it and computed its coverage again. Both cases now cache None for the key, as load_image does.
This commit is contained in:
@@ -57,10 +57,10 @@ impl Exporter<'_> {
|
||||
/// that ink (a DeviceN space of the one ink, which lists its Separation space), Registration in
|
||||
/// every process ink and every spot ink of the document, other colors in gray, RGB or CMYK as
|
||||
/// the Output settings write them. The coverage is resampled as the Compression settings ask
|
||||
/// ([`crate::images::coverage_sized`]). `None` when the paint isn't a solid color or the pixels
|
||||
/// can't be decoded or its color channel exceeds [`vectorcraft_doc::MAX_RASTER_ALLOC`]; cached
|
||||
/// like [`Self::load_image`]. With `interpolate` (and the export's own setting), viewers may
|
||||
/// smooth it.
|
||||
/// ([`crate::images::coverage_sized`]). `None` when the paint isn't a solid color, the image is
|
||||
/// missing or left out, its pixels can't be decoded, or its color channel exceeds
|
||||
/// [`vectorcraft_doc::MAX_RASTER_ALLOC`]; image rejections are cached like [`Self::load_image`].
|
||||
/// With `interpolate` (and the export's own setting), viewers may smooth it.
|
||||
pub(super) fn colorized_image(&mut self, im: &vectorcraft_doc::ImageObject, paint: &Paint, interpolate: bool) -> Option<Image> {
|
||||
let Paint::Solid { color, swatch, tint } = paint else { return None };
|
||||
let size = placed_size(im);
|
||||
@@ -73,11 +73,15 @@ impl Exporter<'_> {
|
||||
return i.clone();
|
||||
}
|
||||
let doc = self.doc;
|
||||
let (width, height, alpha) = doc
|
||||
let Some((width, height, alpha)) = doc
|
||||
.image_blob(im)
|
||||
.filter(|_| !self.left_out.contains(&im.key))
|
||||
.and_then(|b| b.coverage())
|
||||
.and_then(|(w, h, cover)| crate::images::coverage_sized(w, h, cover, size, self.compression))?;
|
||||
.and_then(|(w, h, cover)| crate::images::coverage_sized(w, h, cover, size, self.compression))
|
||||
else {
|
||||
self.images.insert(cache, None);
|
||||
return None;
|
||||
};
|
||||
let n = alpha.len();
|
||||
let registration = swatch.as_deref() == Some(vectorcraft_color::swatch::REGISTRATION);
|
||||
let inks: Vec<(String, [f32; 4])> = if registration {
|
||||
@@ -103,7 +107,10 @@ impl Exporter<'_> {
|
||||
other => (other.to_rgb().map(q).to_vec(), ImageColorspace::Rgb),
|
||||
},
|
||||
};
|
||||
tinted_color_len(samples.len(), n)?;
|
||||
if tinted_color_len(samples.len(), n).is_none() {
|
||||
self.images.insert(cache, None);
|
||||
return None;
|
||||
}
|
||||
let color = Arc::new(samples.repeat(n));
|
||||
let img = Image::from_custom(Tinted { color, alpha: Arc::new(alpha), width, height, space, device_n }, interpolate).ok();
|
||||
self.images.insert(cache, img.clone());
|
||||
@@ -256,7 +263,31 @@ pub(super) fn embed(blob: &vectorcraft_doc::ImageBlob, interpolate: bool, sixtee
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::tinted_color_len;
|
||||
use super::{Exporter, tinted_color_len};
|
||||
use vectorcraft_color::{Color, Paint};
|
||||
use vectorcraft_doc::{Document, ImageBlob, ImageObject};
|
||||
use vectorcraft_geom::Affine;
|
||||
|
||||
#[test]
|
||||
fn colorized_image_caches_rejected_images() {
|
||||
let mut doc = Document::new(10.0, 10.0);
|
||||
doc.images.insert("bad".into(), ImageBlob::png(vec![1, 2, 3]));
|
||||
doc.images.insert("left-out".into(), ImageBlob::png(vec![1, 2, 3]));
|
||||
let settings = crate::PdfSettings::default();
|
||||
let mut exporter = Exporter::new(&doc, &settings);
|
||||
exporter.left_out.insert("left-out".into());
|
||||
let paint = Paint::solid(Color::rgb(1.0, 0.0, 0.0));
|
||||
|
||||
for key in ["missing", "bad", "left-out"] {
|
||||
let image =
|
||||
ImageObject { key: key.into(), width: 1, height: 1, xf: Affine::IDENTITY, link: None, placement: Default::default(), pixels: None };
|
||||
assert!(exporter.colorized_image(&image, &paint, true).is_none(), "{key}");
|
||||
let cache = format!("{key}\u{0}{paint:?}\u{0}1.000x1.000");
|
||||
assert!(matches!(exporter.images.get(&cache), Some(None)), "rejection for {key} was not cached");
|
||||
assert!(exporter.colorized_image(&image, &paint, true).is_none(), "second call for {key}");
|
||||
}
|
||||
assert_eq!(exporter.images.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tinted_color_rejects_oversized_and_overflowing_buffers() {
|
||||
|
||||
Reference in New Issue
Block a user