@tooooools/html-to-svg
v1.8.0
Published
Render HTML to SVG
Downloads
74
Readme
tooooools/html-to-svg
Render in the browser an HTML element to SVG with vectorised fonts.
This module uses the Range API for robust and precise text layout computation.
https://github.com/tooooools/html-to-svg/assets/2837959/9bf213c6-620d-4e9a-b9a7-b399c2a93430
Features
Supported elements
The renderer pipeline is structured such as every type of HTML element can have its own renderer, making contribution and testing much more simple.
renderers/div
Render HTML <div>
with its background color and its border-radius.
Also acts as the fallback renderer for all HTML elements.
renderers/text
Render text node, using computed style and loaded fonts (will throw an error if no matching fonts declaration is found).
All fonts are outlined using Opentype.js.
renderers/image
Render HTML <img>
as SVG <image>
element.
renderers/canvas
Render a HTMLCanvas element as a base64 png in a SVG <image>
element.
renderers/svg
Render inline <svg>
element as a base64 in a SVG <image>
element.
Roadmap
- Support for HTML
<hr>
element - Support for CSS
text-decoration
property - Support for CSS
background-image
property - Support for CSS
border
property - Support for CSS
opacity
property - Support for CSS
transform: rotate()
property - Support for CSS
transform: skew()
property
Limitations
This project primarily aims at rendering printable SVG files, in which case font rendering is far more robust when outlining every texts.
Opentype.js does not support (yet) loading local fonts: as a result, every font used in in the rendering process should be explicitly declared in the render constructor (see Usage below).
At the time of writing, an an experimental Local Font Access API is being tested, which could circumvent this issue. Contributions on implementing this API, or using native opt-in (or fallback) SVG <text>
will be really appreciated.
Installation
npm install @tooooools/html-to-svg
Usage
import HtmlToSvg from '@tooooools/html-to-svg'
// Instanciate a new renderer
const renderer = new HtmlToSvg({
debug: false,
ignore: '.html-only, video', // CSS selector
fonts: [
{ family: 'Roboto', url: '/fonts/roboto-regular.otf' },
{ family: 'Roboto', url: '/fonts/roboto-bold.otf', weight: '600' },
{ family: 'Roboto', url: '/fonts/roboto-regular-italic.otf', style: 'italic' }
]
})
// Preload the fonts inside the renderer
await renderer.preload()
// Render a DOMElement
const options = {
rasterizeNestedSVG: true, // Convert <svg> into <image>
splitText: false // Force text fragments to be renderered letter by letter
}
const transform = async (from, to) => to
const svg = await renderer.render(document.querySelector('main'), options, transform)
// Do whatever you want with the returned shadow SVGElement
document.body.appendChild(svg)
download(svg.outerHTML)
renderer.destroy()
Contributing
Support for various CSS properties and HTML element will be implemented based on personal usage.
Any contribution are much appreciated: if you have a suggestion that would make this better, please fork the repo and create a pull request.
Implementing a new renderer
To implement a new renderer for a specifc HTML element, simply export the renderer in the src/renderers.js
, with the export name matching a HTMLElement.tagName.
The renderer is a curried function with the following signature, returning either a nullish value or a valid SVG HTMLElement:
renderers/example.js
import $ from '../utils/dom-render-svg'
export default ({ debug, fonts }) => async (element, props) => {
// The coordinates system origin is the top-left corner of the rendered container
const { x, y, width, height, style } = props
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('x', x)
rect.setAttribute('y', y)
rect.setAttribute('width', width)
rect.setAttribute('height', height)
return rect
// or with utils/dom-render-svg:
return $('rect', { x, y, width, height })
}
Development
$ yarn build
$ yarn example # build and live-serve example/
$ yarn version # build module, example, and publish to npm
Readings
- Deep dive CSS: font metrics, line-height and vertical-align, Vincent De Oliveira
- Detecting Rendered Line Breaks In A Text Node In JavaScript, Ben Nadel
- CSS Inline Layout Module Level 3