typography-canvas-renderer
Version:
A lightweight npm package for rendering typographic content (text and images) on HTML5 Canvas with full CSS styling support including borders, border-radius, multiple border styles, inline text rendering, auto height calculation, and image support
588 lines (505 loc) • 17.6 kB
Markdown
A lightweight npm package for rendering typographic content (text and images) on HTML5 Canvas in a browser environment. The package is designed to generate images in high resolution (up to 3000x4244 pixels) with minimal dependencies and full support for modern CSS styling including borders, border-radius, and various border styles.
## Features
- 🎨 **High Resolution Support**: Generate images up to 3000x4244 pixels
- 📝 **Text Rendering**: Support for custom fonts, colors, positioning, and styling with multi-line text support, automatic text wrapping, custom padding, vertical alignment, and inline text rendering
- 🖼️ **Image Rendering**: Load and render images from URLs or base64 data
- 🎯 **Absolute Positioning**: All elements use absolute positioning for precise control
- 📐 **Z-Index Layering**: Proper element layering with z-index support
- 🔄 **Automatic Scaling**: Smart scaling for high-resolution canvases
- 🎨 **Advanced Border Support**: Complete border system with rounded corners and multiple styles
- 🔲 **Border Radius**: Full support for rounded corners on both text and images
- 📏 **Border Styles**: Support for solid, dashed, dotted, and double borders
- 📐 **Text Fitting**: Proper text positioning and padding within borders
- 🔄 **Auto Height Calculation**: Automatic height calculation based on content and padding
- 📏 **Inline Text Rendering**: Text renders inline when width is not specified
- 🖼️ **Image Support**: Full image rendering with borders, padding, and styling
- ⚡ **Minimal Dependencies**: Only one external dependency (`csstree-validator`)
- 🌐 **Browser-First**: Optimized for Chrome 100+, Firefox 100+, Safari 15+
## Installation
```bash
npm install typography-canvas-renderer
```
### Minified Versions
For production use, you can import minified versions for smaller bundle sizes:
```javascript
// Regular versions
import { renderCanvas } from 'typography-canvas-renderer';
// Minified versions (65% smaller)
import { renderCanvas } from 'typography-canvas-renderer/min';
```
**File Sizes:**
- `index.js`: 34KB (regular)
- `index.min.js`: 12KB (minified) - **65% smaller**
- `index.esm.js`: 34KB (regular ESM)
- `index.esm.min.js`: 12KB (minified ESM) - **65% smaller**
- `index.umd.js`: 38KB (regular UMD)
- `index.umd.min.js`: 12KB (minified UMD) - **68% smaller**
```javascript
import { renderCanvas, renderCanvasAsDataURL } from 'typography-canvas-renderer';
const input = {
canvas: {
width: 1000,
height: 1000,
background: 'white',
format: 'png'
},
texts: [
{
text: 'Hello World',
css: {
'font-size': '48px',
'color': 'black',
'left': '10px',
'top': '10px',
'background-color': '#f0f0f0',
'border': '2px solid #333',
'border-radius': '10px',
'z-index': '1'
}
}
],
images: [
{
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
css: {
'width': '200px',
'height': '200px',
'left': '50px',
'top': '50px',
'border': '3px dashed blue',
'border-radius': '15px',
'z-index': '2'
}
}
]
};
// Render as Blob
const blob = await renderCanvas(input);
// Render as Data URL
const dataURL = await renderCanvasAsDataURL(input);
```
Renders typography content on canvas and returns a Blob.
**Parameters:**
- `input` (Input): Input object containing canvas configuration and elements
- `canvasContext?` (CanvasRenderingContext2D): Optional existing canvas context
**Returns:** `Promise<Blob>`
Renders typography content on canvas and returns a data URL string.
**Parameters:**
- `input` (Input): Input object containing canvas configuration and elements
- `canvasContext?` (CanvasRenderingContext2D): Optional existing canvas context
**Returns:** `Promise<string>`
Clears the internal image cache.
```typescript
interface Input {
canvas: {
width: number; // Width in pixels (1-3000)
height: number; // Height in pixels (1-4244)
background?: string; // CSS background color (default 'white')
format?: 'png' | 'jpeg'; // Export format (default 'png')
quality?: number; // For JPEG (0-1, default 0.9)
scaleFactor?: number; // Scaling factor (default 1)
};
texts: Array<{
text: string; // Text string
css: Record<string, string>; // CSS properties
}>;
images: Array<{
src: string; // URL or base64 image string
css: Record<string, string>; // CSS properties
}>;
}
```
Here you can see full css properties list. [Open link](./STYLES.md)
| Property | Type | Description | Example |
|----------|------|-------------|---------|
| `position` | string | Only `absolute` supported | `'absolute'` |
| `left` | string | X position in pixels | `'50px'` |
| `top` | string | Y position in pixels | `'100px'` |
| `width` | string | Element width in pixels | `'200px'` |
| `height` | string | Element height in pixels | `'80px'` |
| `font-size` | string | Font size in pixels | `'24px'` |
| `font-family` | string | Font family | `'Arial, sans-serif'` |
| `color` | string | Text color | `'#333333'` or `'red'` |
| `background-color` | string | Background color | `'#f0f0f0'` or `'transparent'` |
| `opacity` | string | Opacity (0-1) | `'0.8'` |
| `text-align` | string | Text alignment | `'left'`, `'center'`, `'right'` |
| `vertical-align` | string | Vertical text alignment | `'top'`, `'middle'`, `'center'`, `'bottom'` |
| `line-height` | string | Line height in pixels | `'32px'` |
| `padding` | string | Custom padding in pixels | `'10px'` |
| `border` | string | Border style | `'2px solid red'` |
| `border-radius` | string | Border radius in pixels | `'10px'` |
| `z-index` | string | Layer order (number) | `'1'` |
## Text Rendering Modes
The package supports two text rendering modes based on whether the `width` property is specified:
### Inline Text Rendering (No Width)
When `width` is not specified, text renders inline with automatic sizing:
```javascript
{
text: 'Inline Text with Auto Sizing',
css: {
'font-size': '20px',
'color': '#2c3e50',
'left': '50px',
'top': '50px',
'background-color': '#e8f4f8',
'border': '2px solid #3498db',
'border-radius': '8px',
'padding': '15px'
// No width specified - renders inline
}
}
```
**Features:**
- ✅ Automatic width calculation based on text content
- ✅ Automatic height calculation with padding
- ✅ Proper text alignment (left, center, right)
- ✅ Multiline support with automatic height adjustment
- ✅ Background and border sizing based on content
### Block Text Rendering (With Width)
When `width` is specified, text renders as a block with text wrapping:
```javascript
{
text: 'Block Text with Width Constraint and Automatic Wrapping',
css: {
'font-size': '18px',
'color': '#ffffff',
'left': '50px',
'top': '50px',
'width': '300px', // Width specified - renders as block
'background-color': '#e74c3c',
'border': '2px solid #c0392b',
'border-radius': '10px',
'padding': '20px',
'text-align': 'center'
// Height can be auto-calculated or specified
}
}
```
**Features:**
- ✅ Text wrapping within specified width
- ✅ Automatic height calculation based on wrapped lines
- ✅ Padding reduces available width for text
- ✅ Vertical alignment support
- ✅ Proper background and border sizing
| Property | Type | Description | Example |
|----------|------|-------------|---------|
| `position` | string | Only `absolute` supported | `'absolute'` |
| `left` | string | X position in pixels | `'50px'` |
| `top` | string | Y position in pixels | `'100px'` |
| `width` | string | Image width in pixels | `'300px'` |
| `height` | string | Image height in pixels | `'200px'` |
| `opacity` | string | Opacity (0-1) | `'0.9'` |
| `background-color` | string | Background color | `'#f0f0f0'` |
| `border` | string | Border style | `'3px dashed blue'` |
| `border-radius` | string | Border radius in pixels | `'15px'` |
| `z-index` | string | Layer order (number) | `'2'` |
## Border Styles
The package supports comprehensive border styling with proper rendering:
### Border Style Syntax
```css
border: [width] [style] [color]
```
### Supported Border Styles
| Style | Description | Example | Visual Effect |
|-------|-------------|---------|---------------|
| `solid` | Solid line border | `'2px solid red'` | Continuous line |
| `dashed` | Dashed line border | `'3px dashed blue'` | Dashed line pattern |
| `dotted` | Dotted line border | `'2px dotted green'` | Dotted line pattern |
| `double` | Double line border | `'4px double purple'` | Two parallel lines |
### Border Radius Support
- **Text Elements**: Rounded backgrounds and borders with proper text fitting
- **Image Elements**: Rounded corners with proper image clipping
- **Border Compatibility**: All border styles work with border-radius
- **Padding**: Automatic padding ensures text fits properly within borders
## Examples
### Text with Padding and Vertical Alignment
```javascript
const input = {
canvas: { width: 800, height: 600 },
texts: [
{
text: 'Top Aligned\nwith Custom Padding\n20px padding',
css: {
'font-size': '20px',
'font-family': 'Arial, sans-serif',
'color': '#2c3e50',
'left': '50px',
'top': '50px',
'width': '200px',
'height': '120px',
'background-color': '#e8f4f8',
'text-align': 'center',
'vertical-align': 'top',
'padding': '20px',
'border': '3px solid #3498db',
'border-radius': '10px',
'line-height': '28px',
'z-index': '1'
}
},
{
text: 'Middle Aligned\nwith Custom Padding\n15px padding',
css: {
'font-size': '18px',
'color': '#ffffff',
'left': '300px',
'top': '50px',
'width': '200px',
'height': '120px',
'background-color': '#e74c3c',
'text-align': 'center',
'vertical-align': 'middle',
'padding': '15px',
'border': '2px solid #c0392b',
'border-radius': '12px',
'line-height': '26px',
'z-index': '2'
}
},
{
text: 'Bottom Aligned\nwith Custom Padding\n10px padding',
css: {
'font-size': '16px',
'color': '#34495e',
'left': '550px',
'top': '50px',
'width': '200px',
'height': '120px',
'background-color': '#ffffff',
'text-align': 'center',
'vertical-align': 'bottom',
'padding': '10px',
'border': '2px dashed #7f8c8d',
'border-radius': '8px',
'line-height': '24px',
'z-index': '3'
}
}
],
images: []
};
```
The package supports full image rendering with borders, padding, and styling:
```javascript
const input = {
canvas: { width: 600, height: 400 },
texts: [],
images: [
{
src: 'https://example.com/image.jpg',
css: {
'width': '200px',
'height': '150px',
'left': '100px',
'top': '75px',
'border': '3px dashed blue',
'border-radius': '15px',
'z-index': '1'
}
},
{
src: 'data:image/png;base64,...',
css: {
'width': '150px',
'height': '100px',
'left': '350px',
'top': '100px',
'border': '2px dotted red',
'border-radius': '20px',
'background-color': '#f0f0f0', // Background behind image
'opacity': '0.9', // Image opacity
'z-index': '2'
}
}
]
};
```
**Image Features:**
- ✅ Support for URLs and base64 data
- ✅ All border styles (solid, dashed, dotted, double)
- ✅ Border radius with proper clipping
- ✅ Background colors behind images
- ✅ Opacity control
- ✅ Z-index layering
- ✅ Automatic error handling with fallback
### Auto Height Calculation with Padding
The package automatically calculates height when not specified, properly accounting for padding:
```javascript
const input = {
canvas: { width: 800, height: 600 },
texts: [
{
text: 'This text will automatically calculate height based on content and padding. The height will adjust to fit all wrapped lines plus the specified padding.',
css: {
'font-size': '20px',
'color': '#2c3e50',
'left': '50px',
'top': '50px',
'width': '300px',
// No height specified - auto-calculated
'background-color': '#e8f4f8',
'border': '2px solid #3498db',
'border-radius': '5px',
'padding': '20px', // Padding reduces available width, creating more lines
'text-align': 'center'
}
},
{
text: 'Inline Text\nWith Multiline\nAuto Height',
css: {
'font-size': '18px',
'color': '#ffffff',
'left': '400px',
'top': '50px',
// No width or height specified - inline rendering with auto sizing
'background-color': '#e74c3c',
'border': '2px solid #c0392b',
'border-radius': '8px',
'padding': '15px',
'text-align': 'center'
}
}
],
images: []
};
```
```javascript
const input = {
canvas: { width: 1000, height: 800 },
texts: [
{
text: 'Solid Border',
css: {
'font-size': '24px',
'color': '#ffffff',
'left': '50px',
'top': '50px',
'width': '200px',
'height': '60px',
'background-color': '#e74c3c',
'text-align': 'center',
'border': '3px solid #c0392b',
'border-radius': '10px',
'z-index': '1'
}
},
{
text: 'Double Border',
css: {
'font-size': '22px',
'color': '#2c3e50',
'left': '300px',
'top': '50px',
'width': '200px',
'height': '60px',
'background-color': '#f39c12',
'text-align': 'center',
'border': '4px double #e67e22',
'border-radius': '5px',
'z-index': '2'
}
}
],
images: [
{
src: 'data:image/png;base64,...',
css: {
'width': '150px',
'height': '100px',
'left': '550px',
'top': '50px',
'border': '2px dotted #8e44ad',
'border-radius': '25px',
'z-index': '3'
}
}
]
};
```
```javascript
const input = {
canvas: {
width: 2500,
height: 2500,
scaleFactor: 1.5 // Automatically applied for canvases > 2000px
},
texts: [
{
text: 'High Resolution',
css: {
'font-size': '72px',
'left': '100px',
'top': '100px',
'border': '4px solid #333',
'border-radius': '20px',
'background-color': '#f8f9fa'
}
}
],
images: []
};
```
A comprehensive example file (`complete-example.html`) is included that demonstrates all features:
- **Interactive Presets**: 10+ different preset configurations
- **Live Rendering**: Real-time canvas updates
- **JSON Editor**: Edit configurations directly
- **All Features**: Inline text, block text, images, borders, padding, alignment
- **Auto Height**: Demonstrates automatic height calculation with padding
- **Mixed Content**: Shows text and images working together
To use the complete example:
1. Open `complete-example.html` in a web browser
2. Select different presets from the dropdown
3. Edit the JSON configuration
4. Click "Render" to see results
5. Download the canvas as PNG
## Browser Compatibility
- Chrome 100+
- Firefox 100+
- Safari 15+
## Error Handling
The package throws custom errors for better debugging:
- `InvalidInputError`: Invalid input data
- `CssValidationError`: CSS validation errors
- `ImageLoadError`: Image loading failures
```javascript
import { InvalidInputError, CssValidationError, ImageLoadError } from 'typography-canvas-renderer';
try {
const blob = await renderCanvas(input);
} catch (error) {
if (error instanceof InvalidInputError) {
console.error('Invalid input:', error.message);
} else if (error instanceof CssValidationError) {
console.error('CSS error:', error.message);
} else if (error instanceof ImageLoadError) {
console.error('Image error:', error.message);
}
}
```
1. **Image Caching**: Images are automatically cached during rendering
2. **Clear Cache**: Use `clearCache()` to free memory when done
3. **High Resolution**: Use `scaleFactor` for better performance on large canvases
4. **Minimize Elements**: Fewer elements = faster rendering
5. **Border Optimization**: Simple borders render faster than complex double borders
You can test the package functionality using the included `example-standalone.html` file. Simply open it in your browser to see various examples of text and image rendering with different border styles and border-radius effects.
The package is browser-oriented. Support for Node.js may be added in the future via a separate module (`typography-canvas-renderer-node`).
## License
MIT
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.