render-tag 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -21
- package/lib/css-resolver.d.ts +10 -0
- package/lib/css-resolver.d.ts.map +1 -0
- package/lib/css-resolver.js +1318 -0
- package/lib/css-resolver.js.map +1 -0
- package/lib/index.d.ts +2 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -29
- package/lib/index.js.map +1 -1
- package/lib/layout.d.ts +5 -5
- package/lib/layout.d.ts.map +1 -1
- package/lib/layout.js +382 -294
- package/lib/layout.js.map +1 -1
- package/lib/render-tag.umd.js +6 -6
- package/lib/render-tag.umd.js.map +1 -1
- package/lib/render.d.ts.map +1 -1
- package/lib/render.js +32 -67
- package/lib/render.js.map +1 -1
- package/lib/style-resolver.d.ts.map +1 -1
- package/lib/style-resolver.js +22 -3
- package/lib/style-resolver.js.map +1 -1
- package/lib/types.d.ts +23 -19
- package/lib/types.d.ts.map +1 -1
- package/package.json +25 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Render HTML rich text onto canvas with the 2D API. No SVG, no `foreignObject`
|
|
|
6
6
|
|
|
7
7
|
## Why
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
When you need rich text as part of a canvas — design editors, image export, canvas-based apps — the standard SVG `foreignObject` approach is slow. `render-tag` parses your HTML, resolves styles with a built-in CSS parser, then lays out and draws everything with pure canvas 2D calls. It's significantly faster than SVG-based approaches.
|
|
10
10
|
|
|
11
11
|
By design, render-tag focuses on **rich text only** — paragraphs, headings, lists, tables, inline formatting. It is not designed for interactive elements (buttons, inputs, iframes) or complex HTML layouts. This focus is what makes it fast.
|
|
12
12
|
|
|
@@ -109,7 +109,7 @@ All-in-one: compute layout and draw in a single call.
|
|
|
109
109
|
| `ctx` | `CanvasRenderingContext2D` | — | Existing context to draw onto (no resizing/scaling) |
|
|
110
110
|
| `canvas` | `HTMLCanvasElement \| OffscreenCanvas` | created | Target canvas element (mutually exclusive with `ctx`) |
|
|
111
111
|
| `pixelRatio` | `number` | `devicePixelRatio` | Device pixel ratio for sharp rendering |
|
|
112
|
-
| `accuracy` | `'balanced' \| 'performance'` | `'
|
|
112
|
+
| `accuracy` | `'balanced' \| 'performance'` | `'performance'` | `'balanced'` uses DOM probes for cross-browser line height accuracy. `'performance'` uses pure canvas API only. |
|
|
113
113
|
|
|
114
114
|
Returns `{ canvas, height, layoutRoot, lines }`.
|
|
115
115
|
|
|
@@ -124,7 +124,7 @@ Compute layout without rendering. Use when you need to measure content or render
|
|
|
124
124
|
| `html` | `string` | *required* | HTML string (include `<style>` tags for CSS) |
|
|
125
125
|
| `width` | `number` | *required* | Layout width in CSS pixels |
|
|
126
126
|
| `height` | `number` | auto | Fixed height (auto-sized from content if omitted) |
|
|
127
|
-
| `accuracy` | `'balanced' \| 'performance'` | `'
|
|
127
|
+
| `accuracy` | `'balanced' \| 'performance'` | `'performance'` | Measurement accuracy mode |
|
|
128
128
|
|
|
129
129
|
Returns `{ layoutRoot, height, lines }`.
|
|
130
130
|
|
|
@@ -176,11 +176,16 @@ drawLayout({ layout: result, width: 400, ctx: offscreenCtx });
|
|
|
176
176
|
- `overflow-wrap: break-word`
|
|
177
177
|
- Soft hyphens (`­`)
|
|
178
178
|
|
|
179
|
-
##
|
|
179
|
+
## Recommended CSS reset
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
For best consistency between DOM and canvas rendering, add these CSS rules to your input HTML:
|
|
182
182
|
|
|
183
183
|
```css
|
|
184
|
+
/* Normalize monospace font size.
|
|
185
|
+
Chrome reduces <code>/<pre> font-size via a UA quirk that canvas can't replicate.
|
|
186
|
+
This makes DOM and canvas render code at the same size. */
|
|
187
|
+
code, pre, kbd, samp { font-size: inherit; }
|
|
188
|
+
|
|
184
189
|
/* Suppress Firefox's ::marker extra line height (~1.5px per list item).
|
|
185
190
|
render-tag draws list markers itself, so this loses nothing visually. */
|
|
186
191
|
li::marker { content: none; font-size: 0; line-height: 0; }
|
|
@@ -192,29 +197,28 @@ li::marker { content: none; font-size: 0; line-height: 0; }
|
|
|
192
197
|
.has-emoji { font-kerning: none; }
|
|
193
198
|
```
|
|
194
199
|
|
|
195
|
-
|
|
200
|
+
The default `accuracy: 'performance'` uses pure canvas API measurements with no DOM touches, producing consistent canvas output across browsers. Use `accuracy: 'balanced'` if you need each browser's canvas output to match its own native DOM rendering more closely (at the cost of cross-browser canvas consistency).
|
|
196
201
|
|
|
197
|
-
##
|
|
202
|
+
## Design decisions
|
|
198
203
|
|
|
199
|
-
|
|
200
|
-
2. **Resolve styles** via hidden DOM + `getComputedStyle` (CSS cascade for free)
|
|
201
|
-
3. **Layout** with canvas `measureText` (block flow, inline wrapping, margin collapsing)
|
|
202
|
-
4. **Render** with canvas 2D API (`fillText`, `fillRect`, `strokeText`, etc.)
|
|
204
|
+
### Chrome-first rendering
|
|
203
205
|
|
|
204
|
-
|
|
206
|
+
Chrome is the primary target browser. When a rendering choice must favor one browser over another, Chrome wins. All development and CI testing defaults to Chromium.
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
### Cross-browser consistency over per-browser accuracy
|
|
207
209
|
|
|
208
|
-
|
|
209
|
-
npm install
|
|
210
|
-
npx playwright install chromium
|
|
210
|
+
The library prioritizes producing **the same canvas output in every browser** over matching each browser's native DOM rendering pixel-for-pixel. If Chrome and Firefox render a `<p>` slightly differently in DOM, our canvas output should match Chrome's version in both browsers — not adapt to each browser's quirks.
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
npm run dev
|
|
212
|
+
In other words: identical canvas output everywhere > perfect DOM fidelity per browser. Users expect the same visual result regardless of which browser their audience uses.
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
## How it works
|
|
215
|
+
|
|
216
|
+
1. **Parse** HTML with `DOMParser`
|
|
217
|
+
2. **Resolve styles** via built-in CSS resolver (selector matching, cascade, inheritance — no DOM insertion)
|
|
218
|
+
3. **Layout** with canvas `measureText` (block flow, inline wrapping, margin collapsing)
|
|
219
|
+
4. **Render** with canvas 2D API (`fillText`, `fillRect`, `strokeText`, etc.)
|
|
220
|
+
|
|
221
|
+
Style resolution uses a built-in CSS parser and resolver that handles selectors, specificity, cascade, and inheritance without inserting HTML into the document. Layout and rendering are done entirely with the canvas 2D API.
|
|
218
222
|
|
|
219
223
|
## License
|
|
220
224
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StyledNode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve styles for a DOM tree without inserting into the document.
|
|
4
|
+
* Parses CSS rules, matches selectors, resolves cascade + inheritance.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveStylesFromCSS(fragment: DocumentFragment, css: string, containerWidth: number): {
|
|
7
|
+
tree: StyledNode;
|
|
8
|
+
cleanup: () => void;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=css-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-resolver.d.ts","sourceRoot":"","sources":["../src/css-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,YAAY,CAAC;AAm4B5D;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAiZ3C"}
|