forked from 1b-agent/opencadstudio-upstream
web: harden plot preview after adversarial review
Three reviewers (security, correctness, performance) converged on:
- Download PDF died on the second click: Closure::once_into_js throws
when invoked twice. The handler is now a plain FnMut kept for the
overlay's lifetime.
- Rasterisation was bounded by page count, not pixels: 12 A0 sheets at
150 DPI peaked near 1.8 GB of RGBA plus ~1.7 GB of decoded images,
and a drawing declaring absurd paper sizes made the rasteriser
saturate its u16 dimensions into a ~17 GB allocation that aborts the
tab. Preview now computes a uniform DPI under a 64-Mpx total budget
(floor 24 DPI) from the page tree before allocating, with a per-page
4096-px edge clamp.
- Overlays stacked when previewing twice and their blob URLs were only
revoked via Close. The overlay is now single-instance (id-tagged);
a replacement or Close revokes every URL through a shared registry.
- emit_image resampled before probing the XObject cache, so a title
block placed on 12 layouts ran 12 Lanczos passes to embed once.
The cache is now probed with the target dimensions first.
- Preview downloads are named {stem}_preview.pdf instead of a constant
plot_preview.pdf, and the page label says 'first N of M' when the
preview is capped while the PDF keeps every page.
(cherry picked from commit 2a05479d69)
This commit is contained in:
1 parent
8cce362009
commit
112060e370
5 files changed
+215
-25
No files matched your search
+13
-1
@@ -195,7 +195,16 @@ fn emit_image(
|
||||
// One shared factor from the denser axis preserves the pixel aspect ratio.
|
||||
let (w, h) = downsample_target(width, height, dpi_x.max(dpi_y), target);
|
||||
// Skip token reductions (< 5%): a resampling pass costs more than it saves.
|
||||
if (w != width || h != height) && (w as f64) / (width as f64) < 0.95 {
|
||||
let needs_resample = (w != width || h != height) && (w as f64) / (width as f64) < 0.95;
|
||||
// The same image placed repeatedly (a title block on every layout)
|
||||
// resamples once: an existing XObject at the target dims means
|
||||
// only the draw transform below is new work.
|
||||
let already_embedded = resources.contains_key(&(
|
||||
std::sync::Arc::as_ptr(&image.pixels) as usize | usize::from(!image.use_alpha),
|
||||
w,
|
||||
h,
|
||||
));
|
||||
if needs_resample && !already_embedded {
|
||||
if let Some(buffer) =
|
||||
image::RgbaImage::from_raw(width, height, image.pixels.as_ref().clone())
|
||||
{
|
||||
@@ -205,6 +214,9 @@ fn emit_image(
|
||||
height = h;
|
||||
pixels = std::borrow::Cow::Owned(resized.into_raw());
|
||||
}
|
||||
} else if already_embedded {
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user