Re: Finding SVG Coordinates of Embedded HTML Content

On Nov 07, 2011, at 09:03 PM, Gavin Kistner <phrogz@me.com> wrote:
My question: is there a better way to accomplish my goal of getting SVG elements coordinated with embedded HTML elements?
 
While writing a workaround for the Webkit bug[1] (transforming points from screen to foreignObject space) I realized that there's no point in round-tripping through screen space for compliant browsers. It's easier to just use foreignObject.getTransformToElement( otherSVGElement ).

I now have a solution and test page[2] that works in Firefox and mostly works in Webkit (with the caveat that rotated/skewed foreignObjects don't give the right results, since getBoundingClientRect() gives the screen-space bounding box that no longer touches the corners of the objects).

[1] https://bugs.webkit.org/show_bug.cgi?id=71819
[2] http://phrogz.net/SVG/html_location_in_svg_2.svg

For archival posterity, here's my code (including Webkit workaround):

// Find the four corners (nw/ne/sw/se) of any HTML element embedded in
// an SVG document, transformed into the coordinates of an arbitrary SVG
// element in the document.
// 
// Usage:
// var coords = svgCoordsForHTMLElement( htmlEl, svgEl );
// svgEl may be null to use the root SVG documentElement
var svgCoordsForHTMLElement = (function(svg){
  var svgNS= svg.namespaceURI, xhtmlNS = "http://www.w3.org/1999/xhtml";
  var doc  = svg.ownerDocument;
  var wrap = svg.appendChild(doc.createElementNS(svgNS,'foreignObject'));
  var body = wrap.appendChild(doc.createElementNS(xhtmlNS,'body'));
  if (typeof body.getBoundingClientRect != 'function') return;
  wrap.setAttribute('x',100);
  var rectIsInForeignObject = body.getBoundingClientRect().left == 0;
  svg.removeChild(wrap);
  var pt = svg.createSVGPoint();
  return function svgCoordsForHTMLElement(htmlEl,svgEl){
    if (!svgEl) svgEl = htmlEl.ownerDocument.documentElement;
    var coords = {};
    for (var o=htmlEl;o&&o.tagName!='foreignObject';o=o.parentNode){}

    var transform = wrap.getTransformToElement(svgEl);
    if (!rectIsInForeignObject){
      // Workaround https://bugs.webkit.org/show_bug.cgi?id=71819
      transform = wrap.getScreenCTM().inverse().multiply(transform);
    }

    var rect = htmlEl.getBoundingClientRect();
    pt.x = rect.left; pt.y = rect.top;
    coords.nw = pt.matrixTransform(transform);
    pt.y = rect.bottom;
    coords.sw = pt.matrixTransform(transform);
    pt.x = rect.right;
    coords.se = pt.matrixTransform(transform);
    pt.y = rect.top;
    coords.ne = pt.matrixTransform(transform);

    return coords;
  };
})(document.documentElement);



Received on Wednesday, 9 November 2011 17:20:27 UTC