Friday 13 May 2005
Rendering Web Page To Images
For a long time now, people have been asking for ways to use Gecko to render a Web page to an image. Creating thumbnails of a Web page is one common desire, but there are lots of potential uses, especially if the feature is available to scripts. I have implemented a new DOM API in 1.8/FF 1.1 that makes this possible. It builds on the canvas element that has recently been implemented in Gecko 1.8 and will be enabled by default soon. (My patch hasn't been checked in yet either, so you can't try this at home just yet.)
To demo this API I've implemented a very simple extension that displays a "thumbnail view" of the currently loaded page in your sidebar. Here's a screenshot. Below is the core source code for the extension. The extension itself is no big deal, and I'm hoping the wonderfully imaginative extension developer community will take this and run with it.
thumbviewSidebar.xul
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window
windowtype="global:thumbviewSidebar"
onclick="update()"
id="win"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="chrome://thumbview/content/thumbview.js"/>
<vbox flex="1" id="before"/>
<html:canvas id="canvas"/>
<vbox flex="1" id="after"/>
</window>
thumbview.js
function update() {
var w = content.innerWidth + content.scrollMaxX;
var h = content.innerHeight + content.scrollMaxY;
if (w > 10000) w = 10000;
if (h > 10000) h = 10000;
var container = document.getElementById("win");
var canvasW = container.boxObject.width;
var scale = canvasW/w;
var canvasH = Math.round(h*scale);
var canvas = document.getElementById("canvas");
canvas.style.width = canvasW+"px";
canvas.style.height = canvasH+"px";
canvas.width = canvasW;
canvas.height = canvasH;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.save();
ctx.scale(canvasW/w, canvasH/h);
ctx.drawWindow(content, 0, 0, w, h, "rgb(0,0,0)");
ctx.restore();
}
var NavLoadObserver = {
observe: function(aWindow)
{
update();
}
};
function start() {
var obs = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces["nsIObserverService"]);
obs.addObserver(NavLoadObserver, "EndDocumentLoad", false);
}
window.addEventListener("load", start, false);
Currently the drawWindow function can only be used by "chrome privileged" content, because untrusted Web content could abuse it in various ways. So extension authors and XUL application developers can use it, but normal Web pages cannot.
Update! I overhauled this entry significantly since we may not be adding a method to 'window' after all. The drawWindow method will be there though.
Comments
On a remotely related issue: Any chance you can fix https://bugzilla.mozilla.org/show_bug.cgi?id=204278 (XUL elements cannot be stacked on top of browser element) for Gecko 1.9, too? IMO, that's another bug that, when fixed, might have great impact on extensions... For example, Mouse Gestures might be able to stack a transparent canvas element on top of the browser, and display actual drawn mouse trails instead of inserting hundreds of tiny HTML elements into the web page (which is the only way to do it right now).
* Showing thumbnails of pages on the back & forward dropdown list
* Generating thumbnails of prefetched pages on google and showing them inline (greasmonkey style extension)
* A bugzilla helper extension, that would let you take a "screenshot" of the place in the page you feel is being mis-rendered.
* Bookmark gallery. When hitting ctrl+b the sidebar would not only show the bookmarks, but images of the pages too.
Another thing that I'd like to see is a binary application or an easy to use library that would generate images for you.
This could be a nice addition to gaim for example, which could show a thumbnail of a persons site in the "more information" dialog. Also any website could generate screenshots for their sites (see: http://dev.upian.com/hotlinks/)
That would be a great feature ... it is easier to remember a visited page by seeing the thumbnail than only the title ... extension idea (I would make it myself if i could) ?
Once the core is checked in, I strongly recommend making your extension an official extension to Firefox released by the Mozilla Foundation. This is awesome.
mvir, glazman: we should be able to do what you want once we have the ability to save the contents of a canvas. The plan is to have a .toDataURL() method that returns a data: URL containing a base64 encoded PNG. We should be able to get that into 1.8.
One thing that's not obvious here is that windows can be transparent, and drawWindow respects/captures the transparency. (Use "rgba(0,0,0,0)" as the background color.)
Another use of drawWindow is that it extends the power of the canvas element by giving you a way to conveniently draw certain kinds of content into a canvas --- e.g., text, or even SVG!
Aooouuuuuuuuhhhhhh !
http://apple.slashdot.org/apple/04/03/31/0513245.shtml?tid=126&tid=185&tid=95
http://www.acm.uiuc.edu/macwarriors/projects/trailblazer/
Stick a spider and thumbnailer onto one of the graph layout packages and much fun can be had
I had a rough one going with KHTML and graphviz once, but gave up because at the time KHTML didn't render well enough. But Gecko doing it would rock!
- Colin
This looks like it has so much potential for tracking layout regressions.
Awesome work roc!
michael: someone could write a remote-controllable XUL app for this
biesi: me? no. But I'm sure you could do this yourself :-). (BTW the GECKO_FORCE_PAINT_ONLOAD environment variable forces onload to be blocked by CSS background image loads.)
Nelson: It needs X. At some point we'll land a version of tetron's null-widget code so that you can run apps without X.
Somebody needs to get onto this and fast!
>variable forces onload to be blocked by CSS
>background image loads.)
last I checked, that environment variable did two things: not only did it it make images block onload, it also dumped each page to a file when done loading... (http://lxr.mozilla.org/seamonkey/source/layout/base/nsDocumentViewer.cpp#1024)
Or how about audio browsers for the visually impaired using the generated image and knowledge of the underlying HTML to let users navigate the page "visually"? That should work even on websites that were not designed for accessibility.
I was thinking about taking your code and do something interesting with it...
Thanks
the relevant bug
Is there a reason why this only works from chrome? Wouldn't a same origin check be sufficient?
In many cases you could just clone the content into its own hidden IFRAME and render that.
"
The plan is to have a .toDataURL() method that returns a data: URL containing a base64 encoded PNG.
"
In Moz/FF, is there a practical limit to the length of data: URL? I know spec says something like 2KB, but I've seen larger, and it's pertinent for Greasemonkey.
Thanks!
Thanks
I have a project that can really use this feature. Thank you in advance!
luping: You just need one of the newest "Deer Park" Firefox builds.