Christoph Burgmer
@cburgmer, cburgmer@thoughtworks.com
JSConf 2014
Provide the missing (i.e. a "prollyfill")
var ctx = canvas.getContext("2d");
ctx.drawHTML(htmlString, 0, 0);
Sounds easy?
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext("2d");
ctx.drawWindow(content, 0, 0, w, h, "rgb(0,0,0)");
That was easy
<foreignObject>
allows to embed foreign content, including (X)HTML
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="100%" height="100%">
<html>
<strong>
This is an image containing HTML
</strong>
</html>
</foreignObject>
</svg>
Can we build on that?
<foreignObject>
Firefox
var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = ourHTML;
doc.documentElement.setAttribute("xmlns",
doc.documentElement.namespaceURI);
var xhtml = (new XMLSerializer())
.serializeToString(doc.documentElement);
Need "shim" for Chrome & Safari :(
The future
var blob = new Blob(["<svg xmlns=...>"],
{"type": "image/svg+xml"});
return URL.createObjectURL(blob);
The present: data URIs
<img src="data:image/svg+xml;charset=utf-8,<svg xmlns='http://www..."/>
Now we can render this
var image = new Image();
image.src = "data:image/svg+xml;charset=utf-8," +
encodeURIComponent("<svg xmlns='http://www...'");
canvas.getContext("2d").drawImage(image, 0, 0);
<foreignObject>
into an SVG<img>
drawImage()
.All good?
SVGs are not allowed to include external references.
HTML allows
<img>
and <input type="image">
<link rel="stylesheet" href="...">
div { background-img: "url(...)"}
@import url(...)
@font face { src: url(...)}
We could try embed them.
<img>
and <input type="image">
are easy
var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = "<html><img src="..."/></html>";
var images = doc.getElementsByTagName("img");
images.forEach(function (image) {
loadAndEmbedImageSrc(image.src);
});
<img src="externalImage.png"/>
Replace image src with data URI
<img src="data:image/png;base64,iVBORw0KGgo...">
Get the image content via AJAX
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function () {
image.src = "data:image/png;base64,"
+ btoa(xhr.response); // base64
});
xhr.open('GET', "externalImage.png");
xhr.send();
Workaround:
xhr.addEventListener("load", function () {
var content = xhr.response,
binaryContent = "";
for (var i = 0; i < content.length; i++) {
binaryContent += String.fromCharCode(
content.charCodeAt(i) & 0xFF);
}
});
background-image
?Working on CSS resources needs parsing CSS
var rules = parseCss("span {" +
"background-image: url('externalImage.png');" +
"}");
We can access it through the CSSOM
function parseCss(content) {
var styleElement = document.createElement("style");
styleElement.textContent = content;
// the style will only be parsed once
// it is added to a document
document.implementation.createHTMLDocument("")
.body.appendChild(styleElement);
return styleElement.sheet.cssRules;
}
We now have our rules
var rules = parseCss("span {" +
"background-image: url('externalImage.png');" +
"}");
Then iterate over rules
rules.forEach(function (rule) {
var backgroundImage = rule.style.getPropertyValue(
'background-image');
if (backgroundImage) {
doInlineImageUrl(backgroundImage);
}
});
Finally same stuff as for our <img>
before
<link rel="stylesheet" href="sheetUrl">
sheetUrl
via AJAX<link>
with <style>PUT_CONTENT_HERE</style>
Example
../images/sprite.png
in assets/style/default.css
becomes assets/images/sprite.png
after embedding
Using the URL parser from Node.js
fontFaceRules.forEach(function (rule) {
var url = getFontFaceSrc(rule),
adaptedUrl = url.resolve(sheetUrl, url);
updateBackgroundImageSrc(rule, adaptedUrl);
});
<foreignObject>
<foreignObject>
to make it into IE (in development)Oh, and go make something cool with it.
This presentation was made with