         if @height == 0
           @viewBox[3] = zeroSizeReplacement
       ## Reset viewBox attribute in case either absent (and computed via
-      ## svgBBox) or changed to avoid zeroes.
+      ## `svgBBox`) or changed to avoid zeroes.
       @dom.documentElement.setAttribute 'viewBox', @viewBox.join ' '
+    else
+      @width = @height = null
     @overflowBox = extractOverflowBox @dom
-    if wrapper == 'symbol'
-      @width = forceWidth if (forceWidth = @getSetting 'forceWidth')?
-      @height = forceHeight if (forceHeight = @getSetting 'forceHeight')?
-      warnings = []
-      unless @width?
-        warnings.push 'width'
-        @width = 0
-      unless @height?
-        warnings.push 'height'
-        @height = 0
-      if warnings.length > 0
-        console.warn "Failed to detect #{warnings.join ' and '} of SVG for tile '#{@key}'"
+    @zIndex = extractZIndex @dom.documentElement
+    @dom
+
+class SVGSVG extends SVGTopLevel
+  ###
+  SVG content wrapped in <svg>, used for `afterRender` content.
+  ###
+  wrapper: 'svg'
+
+class SVGSymbol extends SVGTopLevel
+  ###
+  SVG content wrapped in `<symbol>`, with special width/height handling,
+  used for tiles.  An alternative title for this class would be `Tile`,
+  but often the same symbol is re-used by many tiles.
+  ###
+  wrapper: 'symbol'
+  makeDOM: ->
+    super()
+    ## `SVGTop` sets @width and @height according to viewBox.
+    ## Check for overrides and missing width/height needed for symbols.
+    @width = forceWidth if (forceWidth = @getSetting 'forceWidth')?
+    @height = forceHeight if (forceHeight = @getSetting 'forceHeight')?
+    warnings = []
+    unless @width?
+      warnings.push 'width'
+      @width = 0
+    unless @height?
+      warnings.push 'height'
+      @height = 0
+    if warnings.length > 0
+      console.warn "Failed to detect #{warnings.join ' and '} of SVG for #{@name}"
     ## Detect special `width="auto"` and/or `height="auto"` fields for future
     ## processing, and remove them to ensure valid SVG.
     @autoWidth = isAuto @dom, 'width'
     @autoHeight = isAuto @dom, 'height'
     @dom.documentElement.removeAttribute 'width' if @autoWidth
     @dom.documentElement.removeAttribute 'height' if @autoHeight
-    @zIndex = extractZIndex @dom.documentElement
     ## Optionally extract <text> nodes for LaTeX output
     if @getSetting 'texText'
       @text = []
       domRecurse @dom.documentElement, (node, parent) =>
         if node.nodeName == 'text'
           @text.push node
-          parent.removeChild node
+          node.parentNode.removeChild node
           false # don't recurse into <text>'s children
         else
           true

    @key? and @key.match ...args
  row: (di = 0) ->
    i = @i + di
    for tile, j in @drawing.keys[i] ? []
      new Context @render, i, j
  column: (dj = 0) ->
    j = @j + dj

      height = node.getAttribute 'data-height'
      node.removeAttribute 'data-height'
      # Transfer x/y/width/height to <use> element, for more re-usability.
      node.parentNode.replaceChild (use = @dom.createElementNS SVGNS, 'use'),
        node
      for attr in ['x', 'y', 'width', 'height']
        use.setAttribute attr, node.getAttribute attr if node.hasAttribute attr
        node.removeAttribute attr
