@meonode/canvas 1.0.0-beta.1

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.
Files changed (65) hide show
  1. package/CONTRIBUTING.md +75 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +382 -0
  4. package/dist/cjs/canvas/canvas.helper.d.ts +57 -0
  5. package/dist/cjs/canvas/canvas.helper.d.ts.map +1 -0
  6. package/dist/cjs/canvas/canvas.helper.js +239 -0
  7. package/dist/cjs/canvas/canvas.helper.js.map +1 -0
  8. package/dist/cjs/canvas/canvas.type.d.ts +657 -0
  9. package/dist/cjs/canvas/canvas.type.d.ts.map +1 -0
  10. package/dist/cjs/canvas/grid.canvas.util.d.ts +39 -0
  11. package/dist/cjs/canvas/grid.canvas.util.d.ts.map +1 -0
  12. package/dist/cjs/canvas/grid.canvas.util.js +263 -0
  13. package/dist/cjs/canvas/grid.canvas.util.js.map +1 -0
  14. package/dist/cjs/canvas/image.canvas.util.d.ts +34 -0
  15. package/dist/cjs/canvas/image.canvas.util.d.ts.map +1 -0
  16. package/dist/cjs/canvas/image.canvas.util.js +310 -0
  17. package/dist/cjs/canvas/image.canvas.util.js.map +1 -0
  18. package/dist/cjs/canvas/layout.canvas.util.d.ts +123 -0
  19. package/dist/cjs/canvas/layout.canvas.util.d.ts.map +1 -0
  20. package/dist/cjs/canvas/layout.canvas.util.js +785 -0
  21. package/dist/cjs/canvas/layout.canvas.util.js.map +1 -0
  22. package/dist/cjs/canvas/root.canvas.util.d.ts +42 -0
  23. package/dist/cjs/canvas/root.canvas.util.d.ts.map +1 -0
  24. package/dist/cjs/canvas/root.canvas.util.js +140 -0
  25. package/dist/cjs/canvas/root.canvas.util.js.map +1 -0
  26. package/dist/cjs/canvas/text.canvas.util.d.ts +148 -0
  27. package/dist/cjs/canvas/text.canvas.util.d.ts.map +1 -0
  28. package/dist/cjs/canvas/text.canvas.util.js +1112 -0
  29. package/dist/cjs/canvas/text.canvas.util.js.map +1 -0
  30. package/dist/cjs/constant/common.const.d.ts +37 -0
  31. package/dist/cjs/constant/common.const.d.ts.map +1 -0
  32. package/dist/cjs/constant/common.const.js +51 -0
  33. package/dist/cjs/constant/common.const.js.map +1 -0
  34. package/dist/cjs/index.d.ts +7 -0
  35. package/dist/cjs/index.d.ts.map +1 -0
  36. package/dist/cjs/index.js +31 -0
  37. package/dist/cjs/index.js.map +1 -0
  38. package/dist/esm/canvas/canvas.helper.d.ts +57 -0
  39. package/dist/esm/canvas/canvas.helper.d.ts.map +1 -0
  40. package/dist/esm/canvas/canvas.helper.js +214 -0
  41. package/dist/esm/canvas/canvas.type.d.ts +657 -0
  42. package/dist/esm/canvas/canvas.type.d.ts.map +1 -0
  43. package/dist/esm/canvas/grid.canvas.util.d.ts +39 -0
  44. package/dist/esm/canvas/grid.canvas.util.d.ts.map +1 -0
  45. package/dist/esm/canvas/grid.canvas.util.js +259 -0
  46. package/dist/esm/canvas/image.canvas.util.d.ts +34 -0
  47. package/dist/esm/canvas/image.canvas.util.d.ts.map +1 -0
  48. package/dist/esm/canvas/image.canvas.util.js +306 -0
  49. package/dist/esm/canvas/layout.canvas.util.d.ts +123 -0
  50. package/dist/esm/canvas/layout.canvas.util.d.ts.map +1 -0
  51. package/dist/esm/canvas/layout.canvas.util.js +777 -0
  52. package/dist/esm/canvas/root.canvas.util.d.ts +42 -0
  53. package/dist/esm/canvas/root.canvas.util.d.ts.map +1 -0
  54. package/dist/esm/canvas/root.canvas.util.js +116 -0
  55. package/dist/esm/canvas/text.canvas.util.d.ts +148 -0
  56. package/dist/esm/canvas/text.canvas.util.d.ts.map +1 -0
  57. package/dist/esm/canvas/text.canvas.util.js +1108 -0
  58. package/dist/esm/constant/common.const.d.ts +37 -0
  59. package/dist/esm/constant/common.const.d.ts.map +1 -0
  60. package/dist/esm/constant/common.const.js +23 -0
  61. package/dist/esm/index.d.ts +7 -0
  62. package/dist/esm/index.d.ts.map +1 -0
  63. package/dist/esm/index.js +7 -0
  64. package/dist/meonode-canvas-1.0.0-beta.1.tgz +0 -0
  65. package/package.json +79 -0
@@ -0,0 +1,42 @@
1
+ import { Canvas } from 'skia-canvas';
2
+ import { ColumnNode } from '../canvas/layout.canvas.util.js';
3
+ import type { BaseProps, RootProps } from '../canvas/canvas.type.js';
4
+ export declare const _clearRegisteredFonts: () => void;
5
+ /**
6
+ * Root node that manages the canvas rendering context and coordinates overall layout and drawing.
7
+ * Inherits from ColumnNode to provide vertical layout capabilities.
8
+ */
9
+ export declare class RootNode extends ColumnNode {
10
+ /** The canvas instance used for rendering */
11
+ private canvas;
12
+ /** The 2D rendering context for the canvas */
13
+ private ctx;
14
+ /** Target width for the canvas in pixels */
15
+ private readonly targetWidth;
16
+ /** Scale factor for rendering (e.g. 2 for 2x resolution) */
17
+ private readonly scale;
18
+ /**
19
+ * Creates a new root node for canvas rendering
20
+ * @param props - Configuration properties for the root node
21
+ * @throws Error if width property is not provided
22
+ */
23
+ constructor(props: RootProps & BaseProps);
24
+ /**
25
+ * Traverses the node tree to find all ImageNode instances using breadth-first search
26
+ * @returns Array of all ImageNode instances found in the tree
27
+ */
28
+ private findAllImageNodes;
29
+ /**
30
+ * Renders the entire node tree to a canvas, handling image loading, layout calculation,
31
+ * and final drawing
32
+ * @returns Promise resolving to the rendered Canvas instance
33
+ */
34
+ render(): Promise<Canvas>;
35
+ }
36
+ /**
37
+ * Creates and renders a new root node with the given properties
38
+ * @param props - Configuration properties for the root node
39
+ * @returns Promise resolving to the rendered Canvas instance
40
+ */
41
+ export declare const Root: (props: RootProps) => Promise<Canvas>;
42
+ //# sourceMappingURL=root.canvas.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA8C,MAAM,aAAa,CAAA;AAChF,OAAO,EAAE,UAAU,EAAW,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAUnE,eAAO,MAAM,qBAAqB,YAEjC,CAAA;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU;IACtC,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAoB;IAClC,8CAA8C;IAC9C,OAAO,CAAC,GAAG,CAAwC;IACnD,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAE9B;;;;OAIG;gBACS,KAAK,EAAE,SAAS,GAAG,SAAS;IAsCxC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;CAqChC;AAED;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAU,OAAO,SAAS,oBAAuC,CAAA"}
@@ -0,0 +1,116 @@
1
+ import { FontLibrary, Canvas } from 'skia-canvas';
2
+ import { ColumnNode } from './layout.canvas.util.js';
3
+ import { ImageNode } from './image.canvas.util.js';
4
+ import { Style } from '../constant/common.const.js';
5
+ import * as path from 'node:path';
6
+ import * as fs from 'node:fs';
7
+
8
+ /** Registry to track fonts that have already been loaded */
9
+ const registeredFonts = new Map();
10
+ /**
11
+ * Root node that manages the canvas rendering context and coordinates overall layout and drawing.
12
+ * Inherits from ColumnNode to provide vertical layout capabilities.
13
+ */
14
+ class RootNode extends ColumnNode {
15
+ /** The canvas instance used for rendering */
16
+ canvas;
17
+ /** The 2D rendering context for the canvas */
18
+ ctx = null;
19
+ /** Target width for the canvas in pixels */
20
+ targetWidth;
21
+ /** Scale factor for rendering (e.g. 2 for 2x resolution) */
22
+ scale;
23
+ /**
24
+ * Creates a new root node for canvas rendering
25
+ * @param props - Configuration properties for the root node
26
+ * @throws Error if width property is not provided
27
+ */
28
+ constructor(props) {
29
+ // Call the parent constructor with root name and props
30
+ super({ name: 'Root', ...props });
31
+ // Validate the required width property
32
+ if (!props.width) {
33
+ throw new Error('Width and height are required for Root');
34
+ }
35
+ // Register provided fonts with caching
36
+ if (props.fonts?.length) {
37
+ for (const font of props.fonts) {
38
+ const family = font.family;
39
+ const paths = font.paths.map(p => path.resolve(p));
40
+ if (!registeredFonts.has(family)) {
41
+ registeredFonts.set(family, new Set());
42
+ }
43
+ const cachedPaths = registeredFonts.get(family);
44
+ const newPaths = paths.filter(p => !cachedPaths.has(p) && fs.existsSync(p));
45
+ if (newPaths.length > 0) {
46
+ FontLibrary.use({ [family]: newPaths });
47
+ newPaths.forEach(p => cachedPaths.add(p));
48
+ }
49
+ }
50
+ }
51
+ // Set up scale and width
52
+ this.scale = props.scale || 1;
53
+ this.targetWidth = props.width;
54
+ this.node.setWidth(this.targetWidth);
55
+ // Initialize children nodes
56
+ this.processInitialChildren();
57
+ }
58
+ /**
59
+ * Traverses the node tree to find all ImageNode instances using breadth-first search
60
+ * @returns Array of all ImageNode instances found in the tree
61
+ */
62
+ findAllImageNodes() {
63
+ const imageNodes = [];
64
+ const queue = [this];
65
+ while (queue.length > 0) {
66
+ const node = queue.shift();
67
+ if (node instanceof ImageNode) {
68
+ imageNodes.push(node);
69
+ }
70
+ queue.push(...node.children);
71
+ }
72
+ return imageNodes;
73
+ }
74
+ /**
75
+ * Renders the entire node tree to a canvas, handling image loading, layout calculation,
76
+ * and final drawing
77
+ * @returns Promise resolving to the rendered Canvas instance
78
+ */
79
+ async render() {
80
+ // Step 1: Load all images
81
+ const imageNodes = this.findAllImageNodes();
82
+ const loadingPromises = imageNodes.map(node => node.getLoadingPromise());
83
+ if (loadingPromises.length > 0) {
84
+ await Promise.allSettled(loadingPromises);
85
+ }
86
+ // Step 2: Calculate initial layout
87
+ this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR);
88
+ // Step 3: Allow nodes to finalize their layout
89
+ const needRecalculate = this.finalizeLayout();
90
+ if (needRecalculate) {
91
+ this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR);
92
+ }
93
+ // Step 4: Create a canvas with calculated dimensions
94
+ const calculatedContentHeight = this.node.getComputedHeight();
95
+ const finalCanvasWidth = Math.ceil(this.targetWidth * this.scale);
96
+ const finalCanvasHeight = Math.max(1, Math.ceil(calculatedContentHeight * this.scale));
97
+ // Step 5: Set up canvas context
98
+ this.canvas = new Canvas(finalCanvasWidth, finalCanvasHeight);
99
+ this.ctx = this.canvas.getContext('2d');
100
+ this.ctx.scale(this.scale, this.scale);
101
+ // Step 6: Render content
102
+ super.render(this.ctx, 0, 0);
103
+ if (!this.canvas) {
104
+ throw new Error('Canvas not initialized');
105
+ }
106
+ return this.canvas;
107
+ }
108
+ }
109
+ /**
110
+ * Creates and renders a new root node with the given properties
111
+ * @param props - Configuration properties for the root node
112
+ * @returns Promise resolving to the rendered Canvas instance
113
+ */
114
+ const Root = async (props) => await new RootNode(props).render();
115
+
116
+ export { Root, RootNode };
@@ -0,0 +1,148 @@
1
+ import type { TextProps } from '../canvas/canvas.type.js';
2
+ import { type CanvasRenderingContext2D } from 'skia-canvas';
3
+ import { BoxNode } from '../canvas/layout.canvas.util.js';
4
+ /**
5
+ * Node for rendering text content with rich text styling support
6
+ * Supports color and weight variations through HTML-like tags
7
+ */
8
+ export declare class TextNode extends BoxNode {
9
+ private readonly segments;
10
+ private lines;
11
+ private static measurementContext;
12
+ private readonly metricsString;
13
+ private lineHeights;
14
+ private lineAscents;
15
+ private lineContentHeights;
16
+ props: TextProps & {
17
+ lineGap: number;
18
+ };
19
+ constructor(text?: number | string, props?: TextProps);
20
+ protected applyDefaults(): void;
21
+ /**
22
+ * Processes Unix-like escape sequences in text strings.
23
+ * Converts escaped characters into their actual representations.
24
+ *
25
+ * Supported escape sequences:
26
+ * - \n - Newline (line feed)
27
+ * - \t - Tab (converted to 4 spaces)
28
+ * - \r - Carriage return (treated as newline)
29
+ * - \\ - Literal backslash
30
+ * - \' - Single quote
31
+ * - \" - Double quote
32
+ * - \0 - Null character (removed)
33
+ * - \b - Backspace (removed)
34
+ * - \f - Form feed (treated as newline)
35
+ * - \v - Vertical tab (treated as newline)
36
+ *
37
+ * @param input - Raw text string potentially containing escape sequences
38
+ * @returns Processed string with escape sequences converted
39
+ */
40
+ private processEscapeSequences;
41
+ /**
42
+ * Parses input text with HTML-style markup into styled text segments.
43
+ *
44
+ * Supported tags:
45
+ * - <color="value"> - Sets text color (hex code or CSS color name)
46
+ * - <weight="value"> - Sets font weight (100-900 or keywords like "bold")
47
+ * - <size="value"> - Sets font size in pixels
48
+ * - <b> - Makes text bold (shorthand for weight="bold")
49
+ * - <i> - Makes text italic
50
+ *
51
+ * Tag values can use double quotes, single quotes, or no quotes:
52
+ * <color="red">, <color='red'>, <color=red>
53
+ *
54
+ * Tags can be nested and must be properly closed with </tag>
55
+ *
56
+ * @param input - Text string containing markup tags
57
+ * @param baseStyle - Default style properties to apply to all segments
58
+ * @returns Array of styled text segments with consistent style properties
59
+ */
60
+ private parseRichText;
61
+ private formatSpacing;
62
+ private parseSpacingToPx;
63
+ /**
64
+ * Generates a CSS font string by combining base TextProps with optional TextSegment styling.
65
+ * Follows browser font string format: "font-style font-weight font-size font-family"
66
+ *
67
+ * Priority for style properties:
68
+ * - Weight: segment <weight> tag > segment <b> tag > base fontWeight prop
69
+ * - Style: segment <i> > base fontStyle
70
+ * - Size: segment size > base fontSize
71
+ * - Family: base fontFamily
72
+ *
73
+ * @param segmentStyle - Optional TextSegment styling to override base props
74
+ * @returns Formatted CSS font string for canvas context
75
+ */
76
+ private getFontString;
77
+ /**
78
+ * Gets lines to process respecting maxLines constraint
79
+ */
80
+ private getLinesToMeasureOrRender;
81
+ /**
82
+ * Measures text dimensions and calculates layout metrics for the YogaLayout engine.
83
+ * Handles text wrapping, line height calculations, and dynamic leading.
84
+ *
85
+ * Line heights are determined by:
86
+ * 1. Using props.lineHeight as fixed pixel value if provided
87
+ * 2. Otherwise calculating dynamic height based on largest font size per line
88
+ * 3. Adding leading space above/below text content
89
+ * 4. Including specified line gaps between lines
90
+ *
91
+ * @param widthConstraint - Maximum allowed width in pixels for text layout
92
+ * @param widthMode - YogaLayout mode determining how width constraint is applied
93
+ * @returns Calculated minimum dimensions required to render text content
94
+ * - width: Total width needed for text layout
95
+ * - height: Total height including line heights and gaps
96
+ */
97
+ private measureText;
98
+ /**
99
+ * Wraps text segments into multiple lines while respecting width constraints and preserving styling.
100
+ * Handles rich text attributes (color, weight, size, bold, italic) and proper word wrapping.
101
+ * Also respects explicit newline characters (\n) for forced line breaks.
102
+ *
103
+ * @param ctx - Canvas rendering context used for text measurements
104
+ * @param segments - Array of text segments with styling information
105
+ * @param maxWidth - Maximum allowed width for each line in pixels
106
+ * @param parsedWordSpacingPx - Additional spacing to add between words in pixels
107
+ * @returns Array of lines, where each line contains styled text segments
108
+ */
109
+ private wrapTextRich;
110
+ /**
111
+ * Breaks a word segment into multiple segments that each fit within the specified width constraint.
112
+ * Maintains all styling properties (color, weight, size, bold, italic) across broken segments.
113
+ *
114
+ * @param ctx - Canvas rendering context used for text measurements
115
+ * @param segmentToBreak - Original text segment to split
116
+ * @param maxWidth - Maximum width allowed for each resulting segment
117
+ * @returns Array of TextSegments, each fitting maxWidth, or original segment if no breaking needed
118
+ */
119
+ private breakWordRich;
120
+ /**
121
+ * Measures width of space character using base font
122
+ */
123
+ private measureSpaceWidth;
124
+ /**
125
+ * Renders multi-line text content with rich text styling and layout features
126
+ *
127
+ * Core features:
128
+ * - Dynamic line heights with leading/spacing controls
129
+ * - Vertical text alignment (top/middle/bottom)
130
+ * - Horizontal text alignment (left/center/right/justify)
131
+ * - Text wrapping within bounds
132
+ * - Ellipsis truncation
133
+ * - Rich text styling per segment (color, weight, size, etc)
134
+ * - Performance optimizations (clipping, visibility checks)
135
+ *
136
+ * @param ctx - Canvas rendering context
137
+ * @param x - Content box left position in pixels
138
+ * @param y - Content box top position in pixels
139
+ * @param width - Content box total width including padding
140
+ * @param height - Content box total height including padding
141
+ */
142
+ protected _renderContent(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): void;
143
+ }
144
+ /**
145
+ * Creates a new TextNode instance with rich text support
146
+ */
147
+ export declare const Text: (text: number | string, props?: TextProps) => TextNode;
148
+ //# sourceMappingURL=text.canvas.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGxD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;cAuB1C,aAAa,IAAI,IAAI;IA+DxC;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,WAAW;IA+OnB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY;IAkLpB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;;OAiBG;cACgB,cAAc,CAC/B,GAAG,EAAE,wBAAwB,EAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM;CAiWjB;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,aAA8B,CAAA"}