Skip to content

Commit

Permalink
Support import/require of .svg files as Preact Virtual DOM (fix #73)
Browse files Browse the repository at this point in the history
  • Loading branch information
edemaine committed Jul 28, 2022
1 parent f37082f commit c92600c
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,10 @@ The object or function should map a symbol name to either
2. [Preact](https://preactjs.com/) (React-style) Virtual DOM elements, via
[JSX](https://reactjs.org/docs/introducing-jsx.html) syntax
(or its [CoffeeScript analog](https://coffeescript.org/#jsx))
or via `preact.h` calls;
see [the polyomino example](examples/polyomino).
Be careful not to modify Preact nodes, as they get re-used; instead use
or via
[`preact.h`](https://preactjs.com/guide/v10/api-reference/#h--createelement)
calls; see [the polyomino example](examples/polyomino).
Be careful not to modify Preact nodes, in case they get re-used; instead use
[`preact.cloneElement`](https://preactjs.com/guide/v10/api-reference/#cloneelement)
to make a modified copy (or before modification).
3. a filename with `.svg` extension containing SVG code,
Expand Down Expand Up @@ -204,12 +205,26 @@ In addition to the preloaded module `preact`, they have access to the
SVG Tiler API (not yet documented) via `svgtiler`.

You can also use `import ... from './filename'` or `require('./filename')`
to import local modules relative to the mapping file.
In particular, you can share .js/.coffee code among mapping files.
If you `import`/`require` a filename with `.png`, `.jpg`, `.jpeg`, or `.gif`
extension, you obtain an `image` object representing an `<image>` tag for
the file's inclusion. You can include `image` in a JSX template via `{image}`;
or if you want to inline/manipulate the SVG string, use `image.svg`.
to import local modules or files relative to the mapping file.

* In particular, you can share .js/.coffee code among mapping files.
* If you `import`/`require` a filename with `.svg` extension, you obtain an
Preact Virtual DOM object `svg` representing the SVG file, which you can
include in a JSX template via `{svg}`.
You can also easily manipulate the SVG before inclusion.
For example, `svg.props.children` strips off the outermost tag,
allowing you to rewrap as in `<symbol>{svg.props.children}</symbol>`.
Or [`preact.cloneElement`](https://preactjs.com/guide/v10/api-reference/#cloneelement)
lets you override certain attributes or add children; for example,
`preact.cloneElement(svg, {class: 'foo'}, <rect width="5" height="5"/>, svg.props.children)`
adds a `class` attribute and prepends a `<rect>` child.
Alternatively, use `svg.svg` (the `svg` attribute of the returned object)
to get the SVG string (with comments removed).
* If you `import`/`require` a filename with `.png`, `.jpg`, `.jpeg`, or `.gif`
extension, you obtain an Preact Virtual DOM object `image`
representing an `<image>` tag for the file's inclusion,
which you can include in a JSX template via `{image}`.
Or if you want to inline/manipulate the SVG string, use `image.svg`.

## Drawing Files: .asc, .ssv, .csv, .tsv, .xlsx, .xls, .ods

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"image-size": "1.0.1",
"pirates": "4.0.5",
"preact": "10.7.2",
"preact-html-converter": "0.4.2",
"preact-render-to-string": "5.2.0",
"prettify-xml": "1.2.0",
"stylus": "0.57.0",
Expand Down
36 changes: 25 additions & 11 deletions src/svgtiler.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,27 @@ contentType =
'.svg': 'image/svg+xml'

## Support for `require`/`import`ing images.
## SVG files get parsed into Preact Virtual DOM so you can manipulate them,
## while raster images get converted into <image> Preact Virtual DOM elements.
## In either case, DOM gets `svg` attribute with raw SVG string.
unless window?
pirates = require 'pirates'
pirates.settings = defaultSettings
pirates.addHook (code, filename) ->
href = hrefAttr pirates.settings
"""
module.exports = require('preact').h('image', #{JSON.stringify "#{href}": filename});
module.exports.svg = '<image #{href}="'+#{JSON.stringify filename.replace /"/g, '&quot;'}+'"/>';
"""
if '.svg' == extensionOf filename
code = removeSVGComments code
domCode = require('@babel/core').transform "module.exports = #{code}",
{...babelConfig, filename}
"""
#{domCode.code}
module.exports.svg = #{JSON.stringify code};
"""
else
href = hrefAttr pirates.settings
"""
module.exports = require('preact').h('image', #{JSON.stringify "#{href}": filename});
module.exports.svg = '<image #{href}="'+#{JSON.stringify filename.replace /"/g, '&quot;'}+'"/>';
"""
, exts: Object.keys contentType

renderPreact = (data) ->
Expand Down Expand Up @@ -395,20 +407,22 @@ escapeId = (key) ->

zeroSizeReplacement = 1

removeSVGComments = (svg) ->
## Remove SVG/XML comments such as <?xml...?> and <!DOCTYPE>
## (spec: https://www.w3.org/TR/2008/REC-xml-20081126/#NT-prolog)
svg.replace /<\?[^]*?\?>|<![^-][^]*?>|<!--[^]*?-->/g, ''

class StaticSymbol extends Symbol
constructor: (@key, options) ->
super()
for own key, value of options
@[key] = value
@svg = @svg
## Remove initial SVG/XML comments such as <?xml...?> and <!DOCTYPE>
## (spec: https://www.w3.org/TR/2008/REC-xml-20081126/#NT-prolog)
## for the next replace rule.
.replace /^\s*|^<\?[^]*?\?>\s*|<![^-][^]*?>|<!--[^]*?-->/g, ''
## Remove initial SVG/XML comments for the next replace rule.
@svg = removeSVGComments @svg
## Force SVG namespace when parsing, so nodes have correct namespaceURI.
## (This is especially important on the browser, so the results can be
## reparented into an HTML Document.)
.replace /^<(?:[^<>'"\/]|'[^']*'|"[^"]*")*\s*(\/?\s*>)/,
.replace /^\s*<(?:[^<>'"\/]|'[^']*'|"[^"]*")*\s*(\/?\s*>)/,
(match, end) ->
unless 'xmlns' in match
match = match[...match.length-end.length] +
Expand Down

0 comments on commit c92600c

Please sign in to comment.