v1.23.2

gildas-lormeau/SingleFilev1.23.2Aug 31, 2026by gildas-lormeau

AI Summary

This release focuses on fixing critical bugs in font handling, stylesheet preservation, and self-extracting archive extraction. It also improves error reporting for extension requests and resolves memory leaks related to shadow roots.

Key Highlights

  • Stylesheet attributes (id, class, data-*) are now preserved when replacing stylesheets, preventing data loss in plain saves.
  • Self-extracting archives now correctly decode legacy entry names and prevent duplicate stylesheets from causing missing images.
  • Inline SVG is no longer removed when images are blocked, and tainted canvas elements are now reported instead of dropped silently.
  • Chromium extensions now receive a reason when their capture request is refused, replacing the previous silent timeout.
  • Filename handling now preserves every character, preventing text like `C++` from being corrupted into `C+`.

Full Release Notes

### Fixes

Fonts and stylesheets

- Fonts declared inside a frame whose content cannot be read are no longer all removed, which left the text of those frames in a system font
- Replacing a stylesheet element no longer drops the attributes that identify it: `id`, `class`, `data-*` and `title` are kept. Plain saves lost them on every page with an external stylesheet
- A `font-family` built with `var()`, a multi-word family name, or a custom property naming another custom property is now resolved when removing unused fonts
- A `font` shorthand the parser cannot read, such as `font: bold var(--font)`, no longer counts the fonts it names as unused
- A font value that cannot be resolved no longer switches off font removal for the whole document
- Vendor-prefixed values the browser supports are kept
- Font faces already declared with the same sources are no longer embedded twice
- Circular `@import` rules no longer make the capture loop until it times out

Page content

- Inline SVG is no longer removed when images are blocked, which deleted charts, icons, gradients and filters
- The constructed stylesheets of a closed shadow root are no longer lost
- The canvas image of a page that was already saved is kept
- A canvas tainted by a cross-origin resource is now reported instead of being dropped silently
- Video posters are stored in a lossy format, and autoplay is dropped from media elements left without a source
- A listener added for adopted stylesheets was never removed and accumulated one per shadow root per save

Filenames

- Every character is kept when replacing the characters a filesystem rejects; a run of them was collapsed, so `C++` was saved as `C+`
- The ellipsis counts toward the filename length limit

Self-extracting archives

- Self-extracting pages can read the names of the entries they contain. They declare windows-1252, and a non-ASCII character in the inlined ZIP library made every legacy entry name decode to garbage
- Shared copies of duplicate stylesheets no longer keep the addresses of the original page, which left the images they reference missing from the archive
- A stylesheet shared by duplicate `<style>` elements is stored once instead of twice
- Saving an archive no longer asks the captured site for a ZIP worker three times
- Script resources are named with a `.js` extension, so a module script saved from a `.ts` URL is no longer refused when the archive is opened
- The compression of a resource is decided from its content type when there is one
- The SingleFile comment and the canonical link are withheld from a password-protected archive; both carried the page URL in the clear
- The `%PDF-` header stays within the range PDF readers scan whatever the length of the page doctype
- Extracting the ZIP data from the document is hardened: the data is addressed by an identifier instead of its position, the candidate nodes are checked against the payload, and a failed download falls back to the page data
- An embedded image or PDF the wrapper ladder cannot hide is left out of the file instead of being written unhidden
- Editing a self-extracting archive and saving it again keeps the options it was created with, read from the file itself

Extension API

- An extension whose capture request is refused is now told why on Chromium. It previously received no answer at all and could only time out