Clipboard: refuse oversized pasted pictures before copying their pixels; document paste as desktop-only (#699)

This commit is contained in:
adifiedio committed 2026-11-03 14:25:14 +05:30
1 parent ac27137cd8
commit e56a722bb4
4 files changed
+27 -6

No files matched your search

+15 -4
View File
@@ -74,8 +74,13 @@ impl SheetApp {
/// Cmd/Ctrl+V with a picture and no text on the clipboard (egui's `Event::PasteImage`).
pub fn paste_color_image(&mut self, image: &egui::ColorImage) {
let [w, h] = image.size;
// A side too large for u32 is too large to paste; check before copying the pixels.
let (w, h) = (u32::try_from(w).unwrap_or(u32::MAX), u32::try_from(h).unwrap_or(u32::MAX));
if let Err(e) = check_picture_size(w, h) {
return self.alert(e);
}
let rgba = image.pixels.iter().flat_map(|c| c.to_srgba_unmultiplied()).collect();
match u32::try_from(w).ok().zip(u32::try_from(h).ok()).and_then(|(w, h)| image::RgbaImage::from_raw(w, h, rgba)) {
match image::RgbaImage::from_raw(w, h, rgba) {
Some(image) => self.paste_picture(image),
None => self.alert("The picture on the clipboard can't be read.".into()),
}
@@ -104,15 +109,21 @@ impl SheetApp {
const MAX_PICTURE_SIDE: u32 = 16_384;
const MAX_PICTURE_PIXELS: u64 = 64 * 1024 * 1024;
/// PNG bytes of a clipboard picture, refusing sizes that would exhaust memory.
fn encode_png(image: &image::RgbaImage) -> Result<Vec<u8>, String> {
let (w, h) = image.dimensions();
/// Refuse empty clipboard pictures and sizes that would exhaust memory.
fn check_picture_size(w: u32, h: u32) -> Result<(), String> {
if w == 0 || h == 0 {
return Err("The picture on the clipboard is empty.".into());
}
if w > MAX_PICTURE_SIDE || h > MAX_PICTURE_SIDE || u64::from(w) * u64::from(h) > MAX_PICTURE_PIXELS {
return Err("The picture on the clipboard is too large to paste.".into());
}
Ok(())
}
/// PNG bytes of a clipboard picture, refusing sizes that would exhaust memory.
fn encode_png(image: &image::RgbaImage) -> Result<Vec<u8>, String> {
let (w, h) = image.dimensions();
check_picture_size(w, h)?;
let mut png = Vec::new();
image
.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png)
+9
View File
@@ -522,6 +522,15 @@ fn a_malformed_clipboard_picture_is_refused_without_inserting() {
h.input_mut().events.push(Event::PasteImage(std::sync::Arc::new(egui::ColorImage::new([0, 0], Vec::new()))));
h.run_steps(2);
assert!(pictures(&h).is_empty());
// Wider than the 16384-pixel limit: refused before its pixels are copied.
dismiss(&mut h);
let wide = egui::ColorImage::new([20_000, 1], vec![egui::Color32::WHITE; 20_000]);
h.input_mut().events.push(Event::PasteImage(std::sync::Arc::new(wide)));
h.run_steps(2);
assert!(pictures(&h).is_empty());
let message = format!("{:?}", h.state().message);
assert!(message.contains("too large"), "{message}");
}
#[test]
+2 -1
View File
@@ -19,7 +19,8 @@ The command API accepts the same placement:
This initial feature handles static pictures. A picture on the operating system's clipboard (a
screenshot, an image copied in a browser) pastes with Cmd/Ctrl+V or Paste as a floating picture
at the active cell, as in Excel. Pasting it into a cell, converting between floating and cell
at the active cell, as in Excel (desktop builds only: the web build's paste receives clipboard
text alone, so a picture with no text pastes nothing there). Pasting it into a cell, converting between floating and cell
pictures, and the `IMAGE()` function remain separate work. Formula references and arithmetic do not carry pictures: the calculation
engine uses a `#VALUE!` fallback, including when a picture is referenced from another cell.
Text-only exports also use that fallback. The picture cell still counts as occupied and
+1 -1
View File
@@ -308,7 +308,7 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12.
| 2026-10-12 | minor | Ctrl+wheel zoom steps along a ladder of percentages about 20% apart that never skips 100%, gliding to each step with animations on, so a notch out undoes a notch in (it drifted: 100 → 122 → 101); Excel steps 15 points, see decisions.md |
| 2026-10-12 | minor | Page Layout view: left/right/top/bottom margins are dragged on the rulers (#792) |
| 2026-10-12 | minor | Alt+Down opens the active filter header's existing popup, with validation priority and mirrored anchoring on right-to-left sheets (part of #441) |
| 2026-11-03 | minor | A picture on the system clipboard (a screenshot, an image copied in a browser or an image editor) pastes with Cmd/Ctrl+V, the ribbon's Paste and the Paste menu items as a floating picture whose top-left corner is the active cell, as Excel does; text on the clipboard still wins. Oversized or malformed pictures are refused with a message (#699, #3018, #3182) |
| 2026-11-03 | minor | A picture on the system clipboard (a screenshot, an image copied in a browser or an image editor) pastes with Cmd/Ctrl+V, the ribbon's Paste and the Paste menu items as a floating picture whose top-left corner is the active cell, as Excel does; text on the clipboard still wins. Desktop only: the web build's paste receives clipboard text alone. Oversized or malformed pictures are refused with a message (#699, #3018, #3182) |
| 2026-10-12 | minor | Paste from the ribbon (button and options menu), the cell and header context menus and the macOS menu bar's Edit menu pastes what is on the system clipboard, as Cmd/Ctrl+V does, instead of only GridCraft's own copy (the web build, which can't read the clipboard on request, still pastes GridCraft's copy). Copy and Cut by keytip (H,C / H,X) publish to the system clipboard like the ribbon buttons. After a control-channel or MCP copy, which leaves the system clipboard alone, Paste pastes that copy until other text is copied. Once a cut has been pasted, Paste (ribbon, menus or Cmd/Ctrl+V) doesn't paste the cut's clipboard text again over the moved cells (Excel pastes a cut once), and the Paste keytip (H,V,P) pastes the system clipboard like the button. Regression test for the Paste button click freeze in 0.5.0 (#273, fixed by #272); the Paste button gets an accessible "Paste" name for screen readers |
| 2026-10-11 | minor | Ribbon: scroll arrows on a tab wider than the window, Formulas › AutoSum as a split button with its function menu, View › Show in two columns so Headings is no longer cut off |
| 2026-10-11 | minor | Theme tokens for icon, reference, on-accent and sheet-control colours; WCAG 2 contrast test over both themes (dark danger red and selected header text adjusted) |