Skip to content

Commit

Permalink
svgtiler.add in place of returning content from afterRender (fix #94)
Browse files Browse the repository at this point in the history
* Return value from `svgtiler.afterRender` content is now ignored.
* Instead, use `render.add` or `svgtiler.add` to add content explicitly.
* Can now add content during `svgtiler.beforeRender`.
* Suitable for adding metadata (#94), given that we no longer wrap
  • Loading branch information
edemaine committed Sep 29, 2022
1 parent 3d9ca19 commit b42d9bd
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 46 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,21 +337,24 @@ The top-level code of your .js or .coffee mapping file can also call:
which in particular has `drawing`, `mappings`, and `styles` attributes.
You can even modify the drawing's `keys` at this stage,
by modifying `render.drawing.keys`.
You can also add SVG content via `render.add` or `svgtiler.add`,
e.g., add metadata like `svgtiler.add(<title>My drawing</title>)`
(see below for more details).
* `svgtiler.afterRender(callback)` to schedule calling `callback(render)`
after rendering each drawing. In particular,
to render an overlay or underlay, return the content to render
as a string or Preact VDOM (ideally in an `<svg>` wrapper).
after rendering each drawing.
During the callback, `render` has properties about the rendering's
bounding box: `xMin`, `xMax`, `yMin`, `yMax`, `width`, `height`.
You can add SVG content (overlay/underlay) via `render.add` or
`svgtiler.add`, which you can pass a string or Preact Virtual DOM.
Specify a [`z-index`](#z-index-stacking-order-of-tiles)
to control the stacking order relative to other symbols
or overlays/underlays.
Specify [`overflowBox`](#overflow-and-bounding-box) to increase the
overall size of the rendered drawing.
During the callback, `render` has properties about the rendering's
bounding box: `xMin`, `xMax`, `yMin`, `yMax`, `width`, `height`.
* `svgtiler.background(fillColor)` to set the default background color
for the SVG drawing (implemented via a `<rect>` underneath the bounding box).
Equivalent to
`svgtiler.afterRender((render) => <rect fill="white" z-index="-99999" x={render.xMin} y={render.yMin} width={render.width} height={render.height}/>`.
`svgtiler.afterRender((render) => render.add(<rect z-index="-Infinity" fill={fillColor} x={render.xMin} y={render.yMin} width={render.width} height={render.height}/>))`.
You can also call `svgtiler.background` within a tile definition function or
a `beforeRender`/`afterRender` callback to set the background dynamically,
or set the global default via the `--bg`/`--background` command-line option.
Expand Down Expand Up @@ -470,6 +473,11 @@ polygon.purple
See the [animation example](examples/anim) for sample usage of a .css or
.styl file.

If you'd rather generate a `<style>` tag dynamically depending on the
drawing content, you can do so in a .js or .coffee mapping file by calling
`svgtiler.add` during an `svgtiler.beforeRender` or `svgtiler.afterRender`
callback.

## Layout Algorithm

Given one or more mapping files and a drawing file, SVG Tiler follows a fairly
Expand Down Expand Up @@ -508,6 +516,8 @@ will be rendered on top of (later than) all tiles without a
`z-index="..."` specification (which default to a z-index of 0).
You can use a `z-index="..."` property or an HTML-style
`style="z-index: ..."` property.
The special values `Infinity`, `+Infinity`, and `-Infinity` are allowed
(along with variants like `Inf` or `\infty`).

## Overflow and Bounding Box

Expand Down
1 change: 1 addition & 0 deletions examples/chess/board-immortal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/chess/board-init.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/chess/board-kasparov-immortal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions examples/chess/graph-board-immortal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/chess/graph-board-init.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions examples/chess/graph-board-kasparov-immortal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/chess/graph.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ svgtiler.afterRender (render) ->
stroke={graphColor} stroke-width="10" stroke-linecap="round"
/>

<svg>{edges}</svg>
render.add <g>{edges}</g>
7 changes: 5 additions & 2 deletions examples/chess/map.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ read = (filename) ->
# {dom.props.children}
#</symbol>

svgtiler.beforeRender ({drawing}) ->
svgtiler.add <title z-index="-Infinity">Chess diagram {drawing.filename}</title>

svgtiler.background 'white'
## Equivalent:
#svgtiler.afterRender (render) ->
# <rect fill="white" z-index="-2"
#svgtiler.afterRender (render) -> render.add \
# <rect fill="white" z-index="-Infinity"
# x={render.xMin} y={render.yMin} width={render.width} height={render.height}/>

[
Expand Down
9 changes: 6 additions & 3 deletions examples/chess/map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ function read(filename) {
return dom.props.children;
}

svgtiler.beforeRender(({drawing}) => svgtiler.add(
<title z-index="-Infinity">Chess diagram {drawing.filename}</title>));

svgtiler.background('white');
// Equivalent:
//svgtiler.afterRender((render) =>
// <rect fill="white" z-index="-2"
//svgtiler.afterRender((render) => render.add(
// <rect fill="white" z-index="-Infinity"
// x={render.xMin} y={render.yMin} width={render.width} height={render.height}/>
//);
//));

(key, context) => {
// Map blanks to empty string
Expand Down
80 changes: 50 additions & 30 deletions src/svgtiler.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ parseNum = (x) ->
else
parsed

## Allow Infinity, \infty, Inf with optional sign prefix
infinityRegExp = /^\s*([+-]?)\\?infi?n?i?t?y?\s*$/i
parseNumOrInfinity = (x) ->
if (match = infinityRegExp.exec x)?
if match[1] == '-'
-Infinity
else
Infinity
else
parseNum x

## Conversion from arbitrary unit to px (SVG units),
## from https://www.w3.org/TR/css-values-3/#absolute-lengths
units =
Expand Down Expand Up @@ -364,12 +375,13 @@ getHref = (node) ->

extractZIndex = (node) ->
## Check whether DOM node has a specified z-index, defaulting to zero.
## Special values of Infinity or -Infinity are allowed.
## Also remove z-index attribute, so output is valid SVG.
## Note that z-index must be an integer.
## 1. https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/z-index suggests
## a z-index="..." attribute. Check for this first.
## 2. Look for style="z-index:..." as in HTML.
z = parseNum attributeOrStyle node, 'z-index'
z = parseNumOrInfinity attributeOrStyle node, 'z-index'
removeAttributeOrStyle node, 'z-index'
z ? 0

Expand Down Expand Up @@ -545,7 +557,7 @@ getContextString = ->
else if currentRender?
"render of '#{currentRender.drawing.filename}'"
else
''
'unknown context'

getSettings = ->
###
Expand Down Expand Up @@ -1296,12 +1308,12 @@ class Mappings extends ArrayWrapper
value = @[i].lookup key, context
return value if value?
undefined
doBeforeRender: (render, callback) ->
doBeforeRender: (render, onResult) ->
for mapping in @
mapping.doBeforeRender render, callback
doAfterRender: (render, callback) ->
mapping.doBeforeRender render, onResult
doAfterRender: (render, onResult) ->
for mapping in @
mapping.doAfterRender render, callback
mapping.doAfterRender render, onResult

blankCells = new Set [
''
Expand Down Expand Up @@ -1555,6 +1567,9 @@ class Render extends HasSettings
@mappings = new Mappings @getSetting 'mappings'
@styles = new Styles @getSetting 'styles'
@defs = []
## Accumulated rendering, in case add() called before makeDOM():
@xMin = @yMin = @xMax = @yMax = 0
@layers = {}
hrefAttr: -> hrefAttr @settings
id: (key) -> #, noEscape) ->
## Generate unique ID starting with an escaped version of `key`.
Expand Down Expand Up @@ -1598,6 +1613,23 @@ class Render extends HasSettings
## Returns current background fill.
@backgroundFill = fill unless fill == undefined
@backgroundFill
add: (content, prepend) ->
unless content instanceof SVGContent
content = new SVGContent \
"svgtiler.add content from #{getContextString()}", content, @settings
dom = content.makeDOM()
return if content.isEmpty
if (box = content.overflowBox ? content.viewBox)?
@xMin = Math.min @xMin, box[0]
@yMin = Math.min @yMin, box[1]
@xMax = Math.max @xMax, box[0] + box[2]
@yMax = Math.max @yMax, box[1] + box[3]
@updateSize()
@layers[content.zIndex] ?= []
if prepend
@layers[content.zIndex].unshift dom.documentElement
else
@layers[content.zIndex].push dom.documentElement
updateSize: ->
@width = @xMax - @xMin
@height = @yMax - @yMin
Expand Down Expand Up @@ -1666,11 +1698,8 @@ class Render extends HasSettings
console.warn "Errors during tiles:", errored.join ', '

## Lay out the tiles in the drawing via SVG <use>.
@xMin = @yMin = @xMax = @yMax = 0
@layers = {}
y = 0
colWidths = {}
@coords = []
for row, i in @tiles
rowHeight = 0
for tiles in row
Expand Down Expand Up @@ -1740,28 +1769,13 @@ class Render extends HasSettings

## afterRender callbacks, which may use (and update) @width/@height
@updateSize()
@mappings.doAfterRender @, (out, mapping) =>
return unless out
overlay = new SVGContent "afterRender content from '#{mapping.filename}'",
out, @settings
dom = overlay.makeDOM()
return if overlay.isEmpty
if (box = overlay.overflowBox ? overlay.viewBox)?
@xMin = Math.min @xMin, box[0]
@yMin = Math.min @yMin, box[1]
@xMax = Math.max @xMax, box[0] + box[2]
@yMax = Math.max @yMax, box[1] + box[3]
@updateSize()
@layers[overlay.zIndex] ?= []
@layers[overlay.zIndex].push dom.documentElement
@mappings.doAfterRender @

## Background fill, now that size has settled
if (@backgroundFill ?= @getSetting 'background')?
@layers['-Infinity'] ?= []
@layers['-Infinity'].unshift (new SVGContent "background",
"""<rect x="#{@xMin}" y="#{@yMin}" width="#{@width}" height="#{@height}" fill="#{@backgroundFill}"/>""",
@settings
).makeDOM()
@add new SVGContent("background",
"""<rect z-index="-Infinity" x="#{@xMin}" y="#{@yMin}" width="#{@width}" height="#{@height}" fill="#{@backgroundFill}"/>""",
@settings), true # prepend

## Check for global <defs> used by the symbols so far.
usedIds = new Set
Expand Down Expand Up @@ -2047,6 +2061,12 @@ globalBackground = (fill) ->
else
throw new SVGTilerError "svgtiler.background called outside of render of mapping context"

globalAdd = (content, ...opts) ->
if currentRender?
currentRender.add content, ...opts
else
throw new SVGTilerError "svgtiler.add called outside of render context"

class Context
constructor: (@render, i, j) ->
@drawing = @render.drawing
Expand Down Expand Up @@ -2373,7 +2393,7 @@ main = (args = process.argv[2..]) ->
svgtiler = {
SVGContent, SVGWrapped, SVGSymbol, unrecognizedSymbol,
Mapping, Mappings, ASCIIMapping, JSMapping, CoffeeMapping,
getMapping, runWithMapping,
getMapping, runWithMapping, static: wrapStatic,
Drawing, AutoDrawing, ASCIIDrawing,
DSVDrawing, SSVDrawing, CSVDrawing, TSVDrawing,
Drawings, XLSXDrawings,
Expand All @@ -2382,7 +2402,7 @@ svgtiler = {
extensionMap, Input, DummyInput, ArrayWrapper,
Render, getRender, runWithRender, beforeRender, afterRender,
id: globalId, def: globalDef, background: globalBackground,
static: wrapStatic,
add: globalAdd,
Context, getContext, getContextString, runWithContext,
SVGTilerError, SVGNS, XLINKNS, escapeId,
main, renderDOM, convert, require: inputRequire,
Expand Down

0 comments on commit b42d9bd

Please sign in to comment.