The Mozilla layout code uses frame objects to lay out the DOM on the screen. (These are entirely different from <:frame> nodes in the DOM). All Mozilla frames inherit from a single C++ abstract class nsIFrame. As part of a project I’m working on to separate the frame classes from XPCOM, I used dehydra to generate a graph of all the frame types in Mozilla.
What I really want for Christmas is a web-based interactive graph viewer for this type of content. I’ve seen a couple closed-source things in Java, but nothing really exciting or hackable.
Note: Viewing this graph in Safari won’t work, because the image is much larger than a single screen, and Safari doesn’t provide scrollbars for SVG that overflows… feel free to download it and view in inkscape, though, or get Firefox!.
If you are able to ignore Internet Explorer, all the other major browser can render SVG content. Recently, while writing previous posts and webapps, I discovered several quirks that may cause browsers to fail to render SVG content consistently.
Inline SVG is an attractive option because it doesn’t require an external file. Unfortunately, inline SVG has one significant problem: authors are forced to use XHTML and the application/xhtml+xml MIME type, instead of standard HTML and the text/html MIME type. XHTML is not a very forgiving language, and one I would generally discourage. Depending on authoring tools and other content on the page, producing valid XML may be difficult. Even more problematic is that Internet Explorer will refuse to display the page at all; there is no graceful fallback for the majority browser. Finally, syndicating SVG in feeds will often cause the SVG to be stripped as it is syndicated.
I used inline SVG for my Mozilla compiler warnings front end, because I don’t care about Internet Explorer users in that application. But it has very limited usefulness in general.
Referencing external SVG content
At first glance, it might seem that you could reference an SVG document using the HTML <:img> element., but this is not the case. SVG images are complete sub-documents. They have their own script context and can script themselves. They can also load additional sub-documents such as images. Because of this, browsers force authors to embed SVG images using <object> or <iframe>.
Embedding SVG with <object>
The <object> element is the generic HTML mechanism for embedding external content. It can be used just like an <iframe> for external HTML document. It can be used to embed plugin-rendered content such as Flash, and it can be used to embed SVG:
Alternate markup here. If you see this, your browser may not support SVG, or a content aggregator may have stripped the object element.
The object element is the best choice in most situations. All browsers including Internet Explorer will display the fallback content if they don’t know how to display SVG or if the image won’t load. Using the object element, authors can even pass parameters to the SVG document.
Embedding SVG with <iframe>
It is also possible to include SVG content using the <iframe> element.
<iframe width="350" height="250"
Alternate markup here. If you see this, your browser might not support iframes, or a content aggregator might have stripped the iframe element.
There are minor but important differences using iframe rather than object to display SVG: Internet explorer will load the iframe but choke on the SVG content. The user won’t skip back to the fallback content within the <iframe>element, and in some cases the user may see a download prompt for the SVG document. But many content sanitizers such as those found in feed aggregators will allow <iframe> through while rejecting <object> And finally, iframes have a border by default. You can remove this border using CSS.
Use this MIME type: image/svg+xml
The correct MIME type for SVG content is image/svg+xml. Firefox will accept application/svg+xml but Safari will not!
Specify image dimensions
The author should know the image dimensions in advance. If you don’t specify the width and height in the <object> or <iframe> element, browsers will initially size the object at 300×150 pixels, and then their behavior will diverge:
Resize to the image intrinsic size, once loaded
Scroll overflow content
Crop overflow content
Don’t use rgba() colors in SVG
The CSS3 specification allows for any color to be specified with transparency using rgba syntax. Many web browsers support RGBA colors for HTML content, but only Firefox supports them for SVG content. Instead of using rgba colors, use the SVG properties fill-opacity and stroke-opacity for maximum portability.
If you want to go drawing complex graphics on the web, you have two basic options: SVG and the HTML canvas element.
My first attempt at the hgweb graphical browser used SVG. Actually, it used an unholy hybrid of SVG and HTML: the revisions themselves were drawn using absolutely positioned HTML. The arrows between the boxes were drawn using SVG. I did this for several reasons:
only Firefox supports drawing text into canvas elements
I could use DOM events to do hit-testing and navigation
you can select/copy/paste text in HTML
Unfortunately, the performance of the first version was not very good, and the code was very complex. I was maintaining several parallel data structures: a JS object graph, and the DOM objects for revisions and arrows. The bookkeeping code to keep data in sync was dwarfing the actual layout logic.
Instead I decided to try using canvas. Sucking out the bookkeeping code and replacing it with a custom drawing code turned out to be much easier than I expected. Now all of the data is kept in a single tree, and layout, rendering, and hit-testing are all blazingly fast.
After getting it basically working, I was able to add additional features in a matter of minutes:
Animation when navigating between revisions
Switched to a vertical layout which provides more information
Made arrows into curves
Highlight the “center” node
The disadvantages of this approach are unfortunate:
Only works in Firefox 3+ (needs the experimental mozDrawText API)
One of the things that makes mercurial useful and frustrating at the same time is that history is not linear. History can separate and join at multiple places. Tools such as hg log can’t show the outline of history clearly. There have been several attempts to remedy this situation: hg glog prints a revision graph using ASCII art, and hg view allows browing a repository history in a GUI app written in TK.
I think we can do better, so I started a new project: it is an extension to hgweb which allows browsing repository history graphically in a web browser. You can navigate graphically through revisions, and it will load new revisions on demand. Note that I’ve only tested it with Firefox, and it won’t work in IE (no SVG support):
There is certainly lots more that can and should be done:
The layout algorithm is really dumb
Revisions should link back to hgweb for viewing diffs, etc
Would be nice to zoom out to get a larger picture with less detail
Unfortunately I don’t have any more time to spend on this project. So I’m looking for volunteers to come forward and finish it.
The code is here. It is packaged as a single mercurial extension which must be installed in a global hgrc file, and a www/ directory with static files. I believe this extension will work correctly against Mercurial 1.0: I’ve been testing it against a mercurial-crew pull from a couple days ago.
In www/navigate.js configure BASEURL for your site
In www/index.xhtml populate select-repo with the repositories on your site.
The hgweb extension exists primarily to provide repository information in JSON format. I hope that it will be useful for other kinds of projects as well.
There is one major hurdle I haven’t figured out yet: I don’t know how to position the text. The positioning of Gregorian chant is very precise: the first neume (note) of each syllable is placed over the center of the vowel of the syllable. In addition, the spacing of neumes is arranged so that there are precise amounts of whitespace between each element and syllable, and hyphens between syllables are lengthened or shortened as necessary to fit. What is the best way to give a web application precise text measurement of individual letters within a syllable (including any kerning and ligatures)?
Ideally I’d like a way to do this with the user’s own fonts, but if I need to preprocess a font file to get metrics out of it, I guess that’s ok too.
Dear lazyweb, can somebody verify that Adobe InDesign/Quark/your favorite DTP program imports my sample SVG file correctly and usably? I don’t have a copy at the moment, and it’s rather expensive just for an import test…