@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.
@@ -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
- }