mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-31 23:59:12 +00:00
PDF: count recompress decodes in the oversized-image test (#1586)
The test for a raster past the RGBA budget showed that recompress skips the decode only through a one-second limit, which a slow CI runner could fail and a fast machine could pass without the fix. recompress now counts its decodes in a test-only per-thread counter, and a unit test checks that the 16,385 px PNG doesn't reach the decode while a 9 px one does. The export test keeps its output checks and drops the time limit.
This commit is contained in:
1 parent
d5f07d30eb
commit
033fc2e17e
2 files changed
+30
-6
No files matched your search
@@ -161,12 +161,22 @@ pub(super) fn cmyk_tiff(data: &[u8], mode: RasterMode) -> Option<Image> {
|
||||
Image::from_custom(CmykImage(Arc::new(r)), mode.interpolate).ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// Rasters `recompress` has decoded on this thread.
|
||||
static RECOMPRESS_DECODES: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
/// Opaque raster → JPEG (quality 90). `None` when the image has transparency, is too large or
|
||||
/// can't be decoded. With `tag_rgb`, the JPEG keeps the source's RGB profile, or gets sRGB.
|
||||
pub(super) fn recompress(data: &[u8], mode: RasterMode) -> Option<Image> {
|
||||
if designcraft_images::too_large_to_decode(data) {
|
||||
return None;
|
||||
}
|
||||
#[cfg(test)]
|
||||
{
|
||||
RECOMPRESS_DECODES.with(|n| n.set(n.get() + 1));
|
||||
}
|
||||
let img = image::load_from_memory(data).ok()?;
|
||||
if img.color().has_alpha() && img.to_rgba8().pixels().any(|p| p[3] < 255) {
|
||||
return None;
|
||||
@@ -539,3 +549,20 @@ mod eps_tests {
|
||||
assert_eq!(civil(i64::MIN), (-292277022657, 1, 27, 8, 29, 52));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod recompress_tests {
|
||||
use super::*;
|
||||
|
||||
/// A raster past the RGBA budget is refused before `recompress` decodes it (#1586).
|
||||
#[test]
|
||||
fn a_raster_past_the_budget_is_not_decoded() {
|
||||
let mode = RasterMode { interpolate: false, tag_rgb: false };
|
||||
let decodes = || RECOMPRESS_DECODES.with(std::cell::Cell::get);
|
||||
let start = decodes();
|
||||
assert!(recompress(&crate::tests::blank_png(16_385), mode).is_none());
|
||||
assert_eq!(decodes(), start);
|
||||
assert!(recompress(&crate::tests::blank_png(9), mode).is_some());
|
||||
assert_eq!(decodes(), start + 1);
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ fn cmyk_tiff() -> (Vec<u8>, Vec<u8>) {
|
||||
}
|
||||
|
||||
/// A black `side`² 1-bit grey PNG: well under 1 MB, it decodes to `side`² bytes.
|
||||
fn blank_png(side: u32) -> Vec<u8> {
|
||||
pub(crate) fn blank_png(side: u32) -> Vec<u8> {
|
||||
use std::io::Write;
|
||||
let chunk = |out: &mut Vec<u8>, kind: &[u8; 4], data: &[u8]| {
|
||||
out.extend((data.len() as u32).to_be_bytes());
|
||||
@@ -354,17 +354,14 @@ fn a_cut_off_image_is_skipped_not_fatal() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A raster past the RGBA budget is skipped undecoded, also when images are recompressed (#1586).
|
||||
/// A raster past the RGBA budget is skipped with a warning, also when images are recompressed (#1586).
|
||||
#[test]
|
||||
fn an_image_past_the_decode_budget_is_skipped_undecoded() {
|
||||
fn an_image_past_the_decode_budget_is_skipped_with_a_warning() {
|
||||
let side = 16_385;
|
||||
let mut d = doc_with_text("still here");
|
||||
place(&mut d, blank_png(side), (side, side));
|
||||
for compress_images in [false, true] {
|
||||
let started = std::time::Instant::now();
|
||||
let r = export_pdf_with_report(&d, &Cache::new(), &PdfOptions { compress_images, ..Default::default() }).unwrap();
|
||||
let took = started.elapsed();
|
||||
assert!(took < std::time::Duration::from_secs(1), "compress_images {compress_images}: took {took:?}");
|
||||
assert!(r.warnings.iter().any(|w| w.contains("could not be decoded and was skipped")), "compress_images {compress_images}: {:?}", r.warnings);
|
||||
assert!(image_xobjects(&r.bytes).is_empty(), "compress_images {compress_images}");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user