@vyaz/renderer 0.0.2 → 0.0.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/dist/index.js +820 -220
- package/dist/src/CanvasRenderer.d.ts +105 -0
- package/dist/src/SVGRenderer.d.ts +96 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/interactive.d.ts +63 -0
- package/dist/src/types.d.ts +61 -0
- package/dist/src/utils.d.ts +21 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +6 -6
- package/src/CanvasRenderer.ts +0 -249
- package/src/SVGRenderer.ts +0 -582
- package/src/index.ts +0 -13
- package/src/types.ts +0 -14
- package/src/utils.ts +0 -15
package/src/CanvasRenderer.ts
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CanvasRenderer.ts — render Line[] → Canvas.
|
|
3
|
-
*
|
|
4
|
-
* Takes ready Line[] with absolute coordinates.
|
|
5
|
-
* Does not compute anything — only draws (dumb drawer principle).
|
|
6
|
-
*
|
|
7
|
-
* Options:
|
|
8
|
-
* - sizing: 'frame' | 'content' — auto canvas size
|
|
9
|
-
* - preserveSpaces: boolean — draw spaces (instead of skip)
|
|
10
|
-
* - backgroundColor: string — background color
|
|
11
|
-
* - debug: DebugFlags — debug overlays
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { Line, Span } from '@vyaz/core';
|
|
15
|
-
import type { DebugFlags } from './types.js';
|
|
16
|
-
import { computeBBox } from './utils.js';
|
|
17
|
-
|
|
18
|
-
export interface CanvasRenderOptions {
|
|
19
|
-
/**
|
|
20
|
-
* How the canvas size is determined:
|
|
21
|
-
* 'frame' — use current ctx.canvas.width/height (default)
|
|
22
|
-
* 'content' — compute bounding box from lines, resize canvas to fit
|
|
23
|
-
*/
|
|
24
|
-
sizing?: 'frame' | 'content';
|
|
25
|
-
/**
|
|
26
|
-
* When true: render space spans with a space character.
|
|
27
|
-
* When false (default): skip space spans (position is already accounted for in x).
|
|
28
|
-
*/
|
|
29
|
-
preserveSpaces?: boolean;
|
|
30
|
-
/** Background color for clearing. If omitted, canvas is cleared transparent. */
|
|
31
|
-
backgroundColor?: string;
|
|
32
|
-
/** Debug overlay flags. */
|
|
33
|
-
debug?: DebugFlags;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Font weight to CSS value */
|
|
37
|
-
function fontWeightCSS(weight: string | number): string {
|
|
38
|
-
if (weight === 'bold') return 'bold';
|
|
39
|
-
if (weight === 'normal') return 'normal';
|
|
40
|
-
if (typeof weight === 'number') return String(weight);
|
|
41
|
-
return 'normal';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Font style to CSS value */
|
|
45
|
-
function fontStyleCSS(style: string): string {
|
|
46
|
-
return style === 'italic' ? 'italic' : 'normal';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Render Line[] array to Canvas.
|
|
51
|
-
*
|
|
52
|
-
* @param ctx — Canvas 2D rendering context
|
|
53
|
-
* @param lines — ready Line[] with absolute coordinates
|
|
54
|
-
* @param options — rendering options
|
|
55
|
-
*/
|
|
56
|
-
export function renderToCanvas(
|
|
57
|
-
ctx: CanvasRenderingContext2D | any,
|
|
58
|
-
lines: Line[],
|
|
59
|
-
options: CanvasRenderOptions = {},
|
|
60
|
-
): void {
|
|
61
|
-
const sizing = options.sizing ?? 'frame';
|
|
62
|
-
const preserveSpaces = options.preserveSpaces ?? false;
|
|
63
|
-
|
|
64
|
-
// ── Sizing: content mode resizes canvas ──────────────────────────
|
|
65
|
-
let canvasWidth: number;
|
|
66
|
-
let canvasHeight: number;
|
|
67
|
-
|
|
68
|
-
if (sizing === 'content') {
|
|
69
|
-
const bbox = computeBBox(lines);
|
|
70
|
-
canvasWidth = bbox.width;
|
|
71
|
-
canvasHeight = bbox.height;
|
|
72
|
-
ctx.canvas.width = canvasWidth;
|
|
73
|
-
ctx.canvas.height = canvasHeight;
|
|
74
|
-
} else {
|
|
75
|
-
canvasWidth = ctx.canvas.width;
|
|
76
|
-
canvasHeight = ctx.canvas.height;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── Background ───────────────────────────────────────────────────
|
|
80
|
-
if (options.backgroundColor) {
|
|
81
|
-
ctx.fillStyle = options.backgroundColor;
|
|
82
|
-
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
83
|
-
} else {
|
|
84
|
-
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ── Render lines ─────────────────────────────────────────────────
|
|
88
|
-
for (const line of lines) {
|
|
89
|
-
const baselineY = line.y + line.baseline;
|
|
90
|
-
|
|
91
|
-
for (const span of line.spans) {
|
|
92
|
-
const x = line.x + span.x;
|
|
93
|
-
|
|
94
|
-
// Font setting
|
|
95
|
-
const style = fontStyleCSS(span.style.fontStyle);
|
|
96
|
-
const weight = fontWeightCSS(span.style.fontWeight);
|
|
97
|
-
const size = span.fontMetrics.fontSize;
|
|
98
|
-
const family = span.style.fontFamily;
|
|
99
|
-
ctx.font = `${style} ${weight} ${size}px ${family}`;
|
|
100
|
-
ctx.fillStyle = span.style.color || '#000000';
|
|
101
|
-
ctx.textBaseline = 'alphabetic';
|
|
102
|
-
|
|
103
|
-
// Draw text
|
|
104
|
-
if (preserveSpaces || span.type !== 'space') {
|
|
105
|
-
ctx.fillText(span.text, x, baselineY);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Underline
|
|
109
|
-
if (span.style.underline) {
|
|
110
|
-
const ulY = baselineY + 2;
|
|
111
|
-
ctx.strokeStyle = span.style.color || '#000000';
|
|
112
|
-
ctx.lineWidth = 1;
|
|
113
|
-
ctx.beginPath();
|
|
114
|
-
ctx.moveTo(x, ulY);
|
|
115
|
-
ctx.lineTo(x + span.width, ulY);
|
|
116
|
-
ctx.stroke();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Strikethrough
|
|
120
|
-
if (span.style.strikethrough) {
|
|
121
|
-
const stY = baselineY - span.fontMetrics.ascent * 0.4;
|
|
122
|
-
ctx.strokeStyle = span.style.color || '#000000';
|
|
123
|
-
ctx.lineWidth = 1;
|
|
124
|
-
ctx.beginPath();
|
|
125
|
-
ctx.moveTo(x, stY);
|
|
126
|
-
ctx.lineTo(x + span.width, stY);
|
|
127
|
-
ctx.stroke();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// InlineWidget (simple rectangle)
|
|
131
|
-
if (span.inlineWidget) {
|
|
132
|
-
const iw = span.inlineWidget;
|
|
133
|
-
const iwY = baselineY - (iw.height || span.fontMetrics.ascent) + (iw.baselineOffset || 0);
|
|
134
|
-
ctx.fillStyle = '#cccccc';
|
|
135
|
-
ctx.fillRect(x, iwY, iw.width, iw.height);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Debug overlay
|
|
141
|
-
if (options.debug) {
|
|
142
|
-
renderDebugToCanvas(ctx, lines, canvasWidth, canvasHeight, options.debug);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ── Debug overlay ────────────────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
/** Draw debug overlays on canvas */
|
|
149
|
-
export function renderDebugToCanvas(
|
|
150
|
-
ctx: CanvasRenderingContext2D,
|
|
151
|
-
lines: Line[],
|
|
152
|
-
_width: number,
|
|
153
|
-
_height: number,
|
|
154
|
-
flags: DebugFlags,
|
|
155
|
-
): void {
|
|
156
|
-
ctx.save();
|
|
157
|
-
|
|
158
|
-
// Text frame — outer bounding box of all lines
|
|
159
|
-
if (flags.frame && lines.length > 0) {
|
|
160
|
-
const first = lines[0];
|
|
161
|
-
const last = lines[lines.length - 1];
|
|
162
|
-
const maxW = Math.max(...lines.map(l => l.x + l.width));
|
|
163
|
-
const frameX = first.x;
|
|
164
|
-
const frameY = first.y;
|
|
165
|
-
const frameW = maxW - frameX;
|
|
166
|
-
const frameH = last.y + last.height - first.y;
|
|
167
|
-
ctx.strokeStyle = 'rgba(255,200,0,0.6)';
|
|
168
|
-
ctx.lineWidth = 1;
|
|
169
|
-
ctx.setLineDash([6, 2]);
|
|
170
|
-
ctx.strokeRect(frameX, frameY, frameW, frameH);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
ctx.setLineDash([]);
|
|
174
|
-
|
|
175
|
-
for (const line of lines) {
|
|
176
|
-
const bx = line.x;
|
|
177
|
-
const by = line.y;
|
|
178
|
-
const bw = line.width;
|
|
179
|
-
const bh = line.height;
|
|
180
|
-
const baselineY = line.y + line.baseline;
|
|
181
|
-
|
|
182
|
-
// Line gap — blue filled rect for lineHeight visualization
|
|
183
|
-
if (flags.lineGap) {
|
|
184
|
-
ctx.fillStyle = 'rgba(0,150,255,0.10)';
|
|
185
|
-
ctx.fillRect(bx, by, bw, bh);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Bounding box — red rect
|
|
189
|
-
if (flags.box) {
|
|
190
|
-
ctx.strokeStyle = 'rgba(255,100,100,0.5)';
|
|
191
|
-
ctx.lineWidth = 1;
|
|
192
|
-
ctx.strokeRect(bx, by, bw, bh);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Baseline — blue line
|
|
196
|
-
if (flags.baseline) {
|
|
197
|
-
ctx.strokeStyle = 'rgba(100,100,255,0.5)';
|
|
198
|
-
ctx.lineWidth = 1;
|
|
199
|
-
ctx.beginPath();
|
|
200
|
-
ctx.moveTo(bx, baselineY);
|
|
201
|
-
ctx.lineTo(bx + bw, baselineY);
|
|
202
|
-
ctx.stroke();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Ascent / Descent — green dashed lines
|
|
206
|
-
if (flags.ascentDescent) {
|
|
207
|
-
const ascentY = baselineY - line.ascent;
|
|
208
|
-
const descentY = baselineY + line.descent;
|
|
209
|
-
ctx.strokeStyle = 'rgba(100,255,100,0.4)';
|
|
210
|
-
ctx.lineWidth = 0.5;
|
|
211
|
-
ctx.setLineDash([3, 2]);
|
|
212
|
-
ctx.beginPath();
|
|
213
|
-
ctx.moveTo(bx, ascentY);
|
|
214
|
-
ctx.lineTo(bx + bw, ascentY);
|
|
215
|
-
ctx.stroke();
|
|
216
|
-
ctx.beginPath();
|
|
217
|
-
ctx.moveTo(bx, descentY);
|
|
218
|
-
ctx.lineTo(bx + bw, descentY);
|
|
219
|
-
ctx.stroke();
|
|
220
|
-
ctx.setLineDash([]);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Labels — small monospace text
|
|
224
|
-
if (flags.labels) {
|
|
225
|
-
const labelY = by - 2;
|
|
226
|
-
const label = `y=${by.toFixed(1)} x=${bx.toFixed(1)} w=${bw.toFixed(1)} h=${bh.toFixed(1)} bl=${baselineY.toFixed(1)}`;
|
|
227
|
-
ctx.font = '9px monospace';
|
|
228
|
-
ctx.fillStyle = 'rgba(0,0,0,0.55)';
|
|
229
|
-
ctx.textBaseline = 'bottom';
|
|
230
|
-
ctx.fillText(label, bx, labelY);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Run boxes — purple rects around Spans
|
|
234
|
-
if (flags.runs) {
|
|
235
|
-
for (const span of line.spans) {
|
|
236
|
-
if (span.width <= 0) continue;
|
|
237
|
-
const rx = line.x + span.x;
|
|
238
|
-
const ry = baselineY - span.fontMetrics.ascent;
|
|
239
|
-
const rw = span.width;
|
|
240
|
-
const rh = span.fontMetrics.ascent + span.fontMetrics.descent;
|
|
241
|
-
ctx.strokeStyle = 'rgba(200,100,255,0.4)';
|
|
242
|
-
ctx.lineWidth = 0.5;
|
|
243
|
-
ctx.strokeRect(rx, ry, rw, rh);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
ctx.restore();
|
|
249
|
-
}
|