Skip to content

Commit 2feb462

Browse files
committed
update README and add FEATURES
1 parent 17e58a8 commit 2feb462

4 files changed

Lines changed: 339 additions & 146 deletions

File tree

FEATURES.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# SnapDOM — Features
2+
3+
A complete technical overview of what **SnapDOM** captures, embeds and exports. SnapDOM serializes a DOM subtree into a self-contained SVG (via `<foreignObject>`) and rasterizes it to your target format — ultra-fast, dependency-free, and 100% based on standard Web APIs.
4+
5+
> 📖 Full API, options and guides: **[snapdom.dev/docs](https://snapdom.dev/docs/)**
6+
>
7+
> 🌐 简体中文: **[FEATURES_CN.md](FEATURES_CN.md)**
8+
9+
## Table of Contents
10+
11+
- [Capture & clone](#capture--clone)
12+
- [Styles](#styles)
13+
- [Images & backgrounds](#images--backgrounds)
14+
- [Fonts & icon fonts](#fonts--icon-fonts)
15+
- [Export formats](#export-formats)
16+
- [Options](#options)
17+
- [Plugin system](#plugin-system)
18+
- [Caching & preCache](#caching--precache)
19+
- [Cross-browser handling](#cross-browser-handling)
20+
- [Node-level control attributes](#node-level-control-attributes)
21+
22+
## Capture & clone
23+
24+
Deep node-by-node clone that snapshots the computed style of every node, preserving what the browser actually renders.
25+
26+
- **Shadow DOM** — traverses `shadowRoot`, extracts and scopes its CSS, seeds the required CSS custom properties, and resolves `<slot>` content via `assignedNodes({ flatten: true })` (slotted subtrees are marked to avoid double-cloning).
27+
- **Same-origin iframes** — rasterized inline (fonts read from the iframe's own document). Cross-origin iframes can't be read, so they render as a striped placeholder (or a hidden spacer when `placeholders` is off).
28+
- **`<canvas>`** — snapshotted to a PNG `<img>` (with Safari-safe retries), preserving intrinsic and CSS box size.
29+
- **`<video>`** — current frame drawn to an image; falls back to the `poster`; honors `object-fit: contain`.
30+
- **`<audio controls>`** — replaced with a drawn player mock sized to the element.
31+
- **Form control state**`<input>` `value` / `checked` / `indeterminate`, `<textarea>` value, and `<select>` selection are preserved. State attributes (`disabled`, `required`, `readonly`, `min`, `max`, `pattern`, `aria-invalid`) are copied so `:disabled`, `:required`, `:read-only`, `:invalid` and `:in-range` styles render. `::placeholder` color is preserved. Firefox checkboxes/radios get a drawn replacement.
32+
- **`<img>`**`srcset` is frozen, pre-transform dimensions recorded, px sizes frozen when the author used `%`/`auto`, and `object-fit` / `object-position` preserved.
33+
- **SVG** — paint properties (fill, stroke and its longhands, opacity variants, fill/clip rule, markers, visibility, display) copied as inline styles; external `<defs>` / `<symbol>` referenced by `<use>` are inlined so `var()` resolves at the use site.
34+
- **Scroll position** — scrolled containers are reproduced by translating their inner content and clipping overflow; fixed/absolute descendants are adjusted, and sticky headers/footers are frozen in place.
35+
- **Skipped by design**`meta`, `script`, `noscript`, `title`, `link`, `template`, the SnapDOM sandbox, and nested `<foreignObject>`.
36+
37+
Non-renderable content is handled gracefully: invalid XML control characters are stripped, `content-visibility` is forced visible so off-screen content is captured, and root margins are neutralized.
38+
39+
## Styles
40+
41+
- **Computed-style inlining** — every node's full computed style is snapshotted and deduplicated into generated CSS classes to keep output compact. Authored inline styles are replaced with computed values so stylesheet `!important` still wins.
42+
- **Preserved details** — text-decoration longhands (line/color/style/thickness, underline-offset, skip-ink), `-webkit-text-stroke` + `paint-order`, and (when embedding fonts) font-feature/variation/kerning/variant/optical-sizing settings.
43+
- **`counter()` / `counters()`** — a full CSS counter resolver (counter-reset with nesting, counter-increment, counter-set, and counter-style formatting), used in pseudo-element `content`.
44+
- **`-webkit-line-clamp` & `text-overflow: ellipsis`** — baked into real text (with ``) because Firefox and Safari don't honor them inside `<foreignObject>`.
45+
- **Transforms** — base and individual `translate`/`rotate`/`scale` are read into a total matrix with origin-aware bounding-box math (see the `outerTransforms` option).
46+
- **Shadows, blur & outline bleed** — box-shadow, filter blur, drop-shadow and outline expand the viewBox when `outerShadows` is on; otherwise root shadows are visually stripped (see `outerShadows`).
47+
- **Masks, backgrounds & border-image** — see [Images & backgrounds](#images--backgrounds).
48+
- **Custom scrollbars**`::-webkit-scrollbar` rules are injected so custom scrollbar styling appears.
49+
- **`excludeStyleProps`** — skip properties from the snapshot by RegExp or predicate (e.g. drop all CSS variables).
50+
51+
## Images & backgrounds
52+
53+
- **`<img>` inlining** — resolves `currentSrc`/`src`, fetches to a data URL, caches, and ensures dimensions (batched to respect HTTP/1.1 connection limits). SVG `<image href>` is inlined too.
54+
- **Backgrounds & masks** — inlines `url()` layers in `background-image` (and the `background` shorthand), `mask` / `-webkit-mask*`, and `border-image`, preserving multi-layer values and layout longhands (position, size, repeat, origin, clip, blend-mode, composite…). Supports `background-clip: text`.
55+
- **`<picture>` & lazy images** — resolves `<picture>` sources and common lazy attributes (`data-src`, `data-lazy-src`, `data-original`, `data-hi-res-src`, `data-srcset`, …) to real URLs before cloning.
56+
- **CORS / proxy** — a non-throwing fetch layer with in-flight deduplication, an error cache, timeouts, and inferred credentials. `useProxy` accepts flexible templates (`{url}`, `{urlRaw}`, `?url=` suffix, and more); already-proxied and `data:`/`blob:` URLs are skipped.
57+
- **Failure fallbacks** — a configurable `fallbackURL` (string or callback), then a placeholder box, then a hidden spacer.
58+
- **`compress`** — perceptual downsampling of inlined rasters to their visible resolution (display box × scale × dpr), preserving the source codec and never upscaling. On by default; set `compress: false` to embed verbatim.
59+
- **Decode-size guard** — SVG raster size is clamped to safe limits (max 16384px per side, ~268M px area) and downscaled with a warning if exceeded.
60+
61+
## Fonts & icon fonts
62+
63+
- **`@font-face` embedding** (`embedFonts`) — scans document (and iframe) stylesheets and embeds only the `@font-face` rules for the families/weights/styles/stretch **actually used**, intersected with the **used unicode codepoints**, keeping payloads small. Supports near-weight matching and synthetic-italic fallback.
64+
- **Icon fonts** — auto-detects Font Awesome, Material Icons / Symbols, Ionicons, Glyphicons, Feather, Bootstrap Icons, Remix, Heroicons, Layui and Lucide (plus a heuristic), and renders glyphs (including ligature icons) to images. Extend detection via the `iconFonts` option or `window.__SNAPDOM_ICON_FONTS__`.
65+
- **`localFonts`** — supply your own fonts as `{ family, src, weight?, style?, stretchPct? }` to fetch and embed.
66+
- **`excludeFonts`** — exclude by `{ families?, domains?, subsets? }`.
67+
- **Cross-origin stylesheets** — gated by `fontStylesheetDomains` (plus known math libraries like KaTeX/MathJax).
68+
- **`preCache`** — preloads images, background images and fonts before capture; defaults to `embedFonts: true` and `cache: 'full'`.
69+
70+
## Export formats
71+
72+
A `snapdom(el)` call returns a reusable result object; capture once, export many times.
73+
74+
| Method | Returns |
75+
|---|---|
76+
| `toRaw()` | Raw `data:image/svg+xml` URL |
77+
| `toSvg()` / `toImg()` | SVG `HTMLImageElement` |
78+
| `toCanvas()` | `HTMLCanvasElement` |
79+
| `toBlob()` | `Blob` (SVG text blob or rasterized) |
80+
| `toPng()` | PNG image |
81+
| `toJpg()` | JPG image (white background) |
82+
| `toWebp()` | WebP image |
83+
| `download()` | Triggers a file download |
84+
85+
The same methods exist as one-shot static shortcuts (`snapdom.toPng(el)`, `snapdom.download(el)`, …). Lossy formats (JPEG/WebP) auto-flatten transparency to white. Downloads use the Web Share API on iOS. Exports run through a serial per-session queue with `beforeExport` / `afterExport` / `afterSnap` hooks.
86+
87+
## Options
88+
89+
Defaults as normalized in `src/core/context.js`.
90+
91+
| Option | Default | Behavior |
92+
|---|---|---|
93+
| `debug` | `false` | Debug warnings |
94+
| `fast` | `true` | Skip idle delay for speed |
95+
| `scale` | `1` | Output scale multiplier |
96+
| `exclude` | `[]` | CSS selectors to exclude |
97+
| `excludeMode` | `'hide'` | `'hide'` (spacer) or `'remove'` |
98+
| `filter` | `null` | Node predicate `(node) => boolean` |
99+
| `filterMode` | `'hide'` | `'hide'` or `'remove'` |
100+
| `placeholders` | `true` | Show placeholders for failed images / cross-origin iframes |
101+
| `embedFonts` | `false` | Embed matched `@font-face` |
102+
| `iconFonts` | `[]` | Extra icon-font names/regexes |
103+
| `localFonts` | `[]` | User font descriptors |
104+
| `excludeFonts` | `undefined` | `{ families, domains, subsets }` |
105+
| `fontStylesheetDomains` | `[]` | Extra cross-origin CSS domains |
106+
| `fallbackURL` | `undefined` | Fallback image URL or callback |
107+
| `cache` | `'soft'` | `disabled` / `soft` / `auto` / `full` |
108+
| `useProxy` | `''` | CORS proxy template/base |
109+
| `width` | `null` | Output width (aspect-preserving) |
110+
| `height` | `null` | Output height |
111+
| `format` | `'png'` | `png` / `jpg``jpeg` / `webp` / `svg` |
112+
| `type` | `'svg'` | Output type `svg` / `img` / `canvas` / `blob` |
113+
| `quality` | `0.92` | Lossy encode quality |
114+
| `dpr` | `devicePixelRatio \|\| 1` | Device pixel ratio |
115+
| `backgroundColor` | `null` (`#ffffff` for jpeg/webp) | Flatten background |
116+
| `filename` | `'snapDOM'` | Download filename base |
117+
| `outerTransforms` | `true` | Normalize root translate/rotate vs. expand bbox for transforms |
118+
| `outerShadows` | `false` | Strip root shadows vs. expand bleed for shadows/blur/outline |
119+
| `compress` | `true` | Perceptual raster downsampling |
120+
| `safariWarmupAttempts` | `3` | Safari warmup iterations (1–3) |
121+
| `excludeStyleProps` | `null` | RegExp/predicate to skip style props |
122+
| `resolvePicturePlaceholders` | `true` | Built-in `<picture>` / lazy resolver |
123+
| `pictureResolver` | `{}` | `{ timeout, concurrency, resolveLazySrc, silent }` |
124+
| `plugins` || Per-capture plugin list (local-first) |
125+
126+
## Plugin system
127+
128+
Plugins are plain objects with lifecycle hooks, registered globally (`snapdom.plugins(...)`, deduped by `name`) or per-capture (`{ plugins: [...] }`, where locals override globals by name).
129+
130+
- **Hooks** (in order): `beforeSnap → beforeClone → afterClone → beforeRender → afterRender → beforeExport → afterExport → afterSnap`.
131+
- **Custom exporters** — a plugin's `defineExports` can add or override export formats; each becomes a `to<Name>()` helper on the result object and gets the same export pipeline as core formats.
132+
- **Accepted forms** — plain object, `[factory, options]`, `{ plugin, options }`, or a factory function.
133+
134+
See [`PLUGIN_SPEC.md`](PLUGIN_SPEC.md) and [`CONTRIBUTING_PLUGINS.md`](CONTRIBUTING_PLUGINS.md).
135+
136+
## Caching & preCache
137+
138+
- **Buckets** — FIFO evicting maps for `image`, `background`, `resource`, `baseStyle` and `defaultStyle`; `WeakMap`s for computed styles and layout measurement hints; a `Set` for fonts; and a per-session bucket.
139+
- **Policies** (`cache` option):
140+
- `disabled` — clear all caches every capture.
141+
- `soft` (default) — reset session style/node maps, keep persistent caches.
142+
- `auto` — reset style/node maps only, keep the style cache too.
143+
- `full` — keep everything.
144+
- **Invalidation** — a MutationObserver on the DOM and `<head>` plus font `loadingdone`/`ready` events bump a style-snapshot epoch, so stale snapshots are dropped automatically.
145+
- **`preCache`** — warm the caches ahead of time (defaults to `cache: 'full'`).
146+
147+
## Cross-browser handling
148+
149+
- **Safari warmup** — works around [WebKit #219770](https://bugs.webkit.org/show_bug.cgi?id=219770) (the first canvas draw with an embedded-font SVG is blank) by running small pre-captures + `drawImage` to prime the pipeline when fonts are embedded or the element has background/mask/canvas content. Configurable via `safariWarmupAttempts`.
150+
- **Safari canvas** — box-shadow is rewritten to an SVG drop-shadow, and image compositing is awaited before drawing.
151+
- **Firefox** — checkboxes and radios get drawn replacements.
152+
- **iOS**`download()` falls back to the Web Share API.
153+
154+
## Node-level control attributes
155+
156+
Fine-grained control directly in your markup:
157+
158+
- `data-capture="exclude"` — drop this node (per `excludeMode`).
159+
- `data-capture="placeholder"` + `data-placeholder-text` — render a placeholder box instead of the node.
160+
- `data-snapdom-sandbox` / `#snapdom-sandbox` — skipped entirely.
161+
162+
---
163+
164+
Want the full API and every option with examples? → **[snapdom.dev/docs](https://snapdom.dev/docs/)**

0 commit comments

Comments
 (0)