Monday, September 1st, 2008

foreignObject: Hey, you’ve got HTML in my SVG!

<p>The SVG foreignObject tag allows you to mix non-SVG content into your page. For example, you could drop some HTML in the middle of an SVG element.

Firefox 3 recently announced support for foreignObject; both Safari and Opera have supported this tag for awhile.

From Mark Finkle comes two examples (1, 2). The first embeds some HTML text into SVG shapes to take advantage of HTML’s nice reflowing and text abilities:

Screenshot of HTML embedded in SVG

Screenshot of HTML embedded in SVG

Here’s the code that accomplishes this:

  1. < ?xml version="1.0" standalone="yes"?>
  2. <svg xmlns = "http://www.w3.org/2000/svg">
  3.   <rect x="10" y="10" width="100" height="150" fill="gray"/>
  4.   <foreignobject x="10" y="10" width="100" height="150">
  5.     <body xmlns="http://www.w3.org/1999/xhtml">
  6.       <div>Here is a <strong>paragraph</strong> that requires <em>word wrap</em></div>
  7.     </body>
  8.  
  9.   </foreignobject>
  10.  
  11.   <circle cx="200" cy="200" r="100" fill="blue" stoke="red"/>
  12.   <foreignobject x="120" y="120" width="180" height="180">
  13.     <body xmlns="http://www.w3.org/1999/xhtml">
  14.       <div>
  15.         <ul>
  16.           <li><strong>First</strong> item</li>
  17.  
  18.           <li><em>Second</em> item</li>
  19.           <li>Thrid item</li>
  20.         </ul>
  21.       </div>
  22.     </body>
  23.   </foreignobject>
  24. </svg>

The second embeds a full iframe, and then rotates it using SVG:

Screenshot of HTML in SVG

Screenshot of HTML in SVG

The first example works fine on both Safari and Firefox 3. On Safari, the second example initially has a display glitch which fixes itself when you resize the browser; I’m sure the SVG source could be changed to accomodate this to work correctly initially, perhaps by changing the viewBox attribute.

From Ted Mielczarek comes an even more ambitious example. He embeds a Google Map into a foreignObject in an SVG document, and then uses an SVG transform to rotate the entire Google Map! Here is a short video showing this in action; I can even click and drag the map as it rotates!

Safari can display this as well, but the behavior is a bit different: the entire map rotates out of view quickly; again, I’m still learning cross-browser SVG but I believe that this could be fixed perhaps by changing the SVG and testing on Safari:

The code that achieves this:

  1. < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3.     <head>
  4.         <script type="text/javascript">
  5.             //< ![CDATA[
  6.             var angle = 0;
  7.             function rotate()
  8.             {
  9.               angle++;
  10.               if(angle > 360)
  11.                 angle = 0;
  12.  
  13.               document.getElementById("g").setAttribute("transform", "rotate(" + angle + ",1000,1000)");
  14.             }
  15.  
  16.             function doResize()
  17.             {
  18.               var w = window.innerWidth/2 - 1000;
  19.               var h = window.innerHeight/2 - 1000;
  20.               document.getElementById("g0").setAttribute("transform","translate("+w+","+h+")");
  21.             }
  22.  
  23.             function setup()
  24.             {
  25.               window.addEventListener("resize", doResize, false);
  26.               doResize();
  27.               setInterval(rotate,100);
  28.             }
  29.  
  30.             //]]>
  31.         </script>
  32.     </head>
  33.    
  34.     <body onload="setup()">
  35.         <svg :svg xmlns:svg="http://www.w3.org/2000/svg" version="1.1" width="100%" height="96%">
  36.             </svg><svg :g id="g0" transform="translate(-1000,-1000)">
  37.                 </svg><svg :g id="g">
  38.                     </svg><svg :foreignObject  width="2000" height="2000">
  39.                         <iframe style="border: none" src="fo.html" width="2000" height="2000">
  40.                         </iframe>
  41.                     </svg>
  42.                
  43.  
  44.            
  45.        
  46.     </body>
  47. </html>

Where fo.html is simply a normal HTML file that embeds a Google Map.

For the foreignObject support in Firefox 3, there are some provisos. From a comment by Robert Longson:

There are some caveats to using HTML text instead of SVG text.

a) SVG text does not scale independently of the SVG image so if you increase or decrease the text size in the browser it has no effect on your SVG text. If you use HTML text however this will change size, possibly obscuring parts of your drawing.

b) SVG text ignores the browser’s minimum font size. I.e. Tools->Options->Fonts & Colours(Advanced)->Minimum Font Size. If you have this set your HTML replacement text may come out bigger than you expect on your drawing.

To see the full new set of SVG tags in Firefox 3 (including SVG filters), see here.

Related Content:

Posted by Brad Neuberg at 5:45 am
7 Comments

+++--
3.5 rating from 18 votes

7 Comments »

Comments feed TrackBack URI

“The first example works fine on both Safari and Firefox 3.” Now that’s an overstatement. Safari has a rather serious display glitch that renders the element pretty unusable. Firefox 3 does it well. Everything works as it should, although it’s slow. But still, faster than the other browsers.

Opera does render it, however, it treats it as an image, so you can’t interact with it (Meaning no links, no scroll, etc).

Comment by Andy — September 1, 2008

alert(document.cookie)

And one more way to embed malicious code inside SVGs… working fine in Opera, FF2 (without really rendering the svg of course), FF3 and Safari.

Comment by x00mario — September 1, 2008

Here’s the stripped code: http://phpfi.com/349332

Comment by x00mario — September 1, 2008

x00mario: I don’t think you know what you’re talking about. SVG supports the script tag & ECMAScript naitively. (they call it ECMAScript on a technicality, its still JS)

Example:

alert(document.cookie);

Comment by rethinker — September 1, 2008

Oops, pardon me. I misused the code tag, I guess.
http://phpfi.com/349371

Comment by rethinker — September 1, 2008

rethinker: I think you don’t know what I was talking about. I am pretty aware of the multiple ways to execute JS from within a SVG file. I was just ranting about the addition of another and IMHO useless way in major browser which makes filtering and parsing way harder for security affine developers.

Why is it that you have access to the DOM of the embedding page when you execute the JS in the SVG? Why can’t there be a default sandbox model that is capable of making it more secure to deal with user uploaded SVG files etc.?

Comment by x00mario — September 2, 2008

And, one of the examples from my OSCON talk:

http://xdraw.org/CSS_slides/examples/foreignobject.svg

Comment by gavindoughtie — September 2, 2008

Leave a comment

You must be logged in to post a comment.