|
<!DOCTYPE html> |
|
<style type="text/css"> |
|
* { background: #333; color: #FFF; font-size: 1.5em; text-align: center; } |
|
h1 { cursor: pointer; min-height: 200px; } |
|
h1, h1 * { background: #FFF; max-height: 200px; vertical-align: top; } |
|
</style> |
|
<h1 title="Click to open SVG image" onclick="location.href='data:image/svg+xml,'+escape(this.innerHTML)">140byt es</h1> |
|
<input onkeyup="refreshLogo(this.value)" value="140byt es"> |
|
<script type="text/javascript"> |
|
var svg = function(w, x, y, z) |
|
{ |
|
z = { //character map, each path must start and end on the baseline |
|
' ': 'm1 0', //move 1 forward on the baseline without drawing a line |
|
'!': 'm1 -3v1.99m0 1v.01', |
|
'"': 'm1 -3v1m1 -1v1m0 2', |
|
'#': 'm1 -2h3m-3 1h3m-2 -2v3m1 -3v3m1 0', |
|
"'": 'm1 -3v1m0 2', |
|
'+': 'm1 -1h2h-1v1v-2m1 2', |
|
',': 'm1 1v-1', //move 1 forward and 1 down, then draw a vertical line 1 up |
|
'-': 'm1 -1h1m0 1', //move 1 forward and 1 up, draw horizontal line, move 1 down to the baseline |
|
'.': 'm1 -.01v.01', |
|
'/': 'm1 0l1 -2m0 2', |
|
'0': 'm3 0h-2v-2h2z', //move 3 forward, draw 2 backward, 2 up, 2 forward and close the subpath |
|
'1': 'm1 -2h1v2', |
|
'2': 'm1 -2h2v1m-2 0v1h2', |
|
'3': 'm1 -2h2v1h-1h1v1h-2h2', |
|
'4': 'm1 -2v1h2v-1v2', |
|
'5': 'm3 -2h-2v1m2 0v1h-2h2', |
|
'6': 'm1 -1h2v1h-2v-2h2m0 2', |
|
'7': 'm1 -2h2v2', |
|
'8': 'm3 0h-2v-2h2v1h-2h2z', |
|
'9': 'm3 -1h-2v-1h2v2', |
|
':': 'm1 -2v.01m0 2v-.01', |
|
';': 'm1 -1.01v.01m0 2v-1', |
|
'<': 'm2 -2l-1 1l1 1', //move forward and up, draw two diagonal lines |
|
'>': 'm1 -2l1 1l-1 1m1 0', |
|
'@': 'm2 -2v1h2v-2h-3v3h3', |
|
'\\': 'm1 -2l1 2', |
|
'_': 'm1 0h1', |
|
'a': 'm1 -1v1h2v-2h-2m2 2', |
|
'b': 'm3 0h-2v-3v1h2z', |
|
'c': 'm3 -2h-2v2h2', |
|
'd': 'm3 0h-2v-2h2v-1z', |
|
'e': 'm3 -1v-1h-2v2h2', |
|
'f': 'm2 -3h-1v3v-2h1m0 2', |
|
'g': 'm3 -2h-2v2h2v-1v1', |
|
'h': 'm1 0v-3v1h2v2', |
|
'i': 'm1 -2v2', |
|
'j': 'm1 1h1v-3v2', |
|
'k': 'm1 -3v3l2 -2l-1 1l1 1', |
|
'l': 'm1 -3v3h1', |
|
'm': 'm1 0v-2h1.5v2v-2h1.5v2', |
|
'n': 'm1 0v-2h2v2', |
|
'o': 'm3 0h-2v-2h2z', |
|
'p': 'm3 0h-2v1v-3h2z', |
|
'q': 'm3 1v-3h-2v2h2', |
|
'r': 'm1 0v-2h1m0 2', |
|
's': 'm3 -2h-2v1m2 0v1h-2h2', |
|
't': 'm2 -2h-1v-1v3h1', |
|
'u': 'm1 -2v2h2v-2v2', |
|
'v': 'm1 -2l1 2l1 -2m0 2', |
|
'w': 'm1 -2v2h1.5v-2v2h1.5v-2v2', |
|
'x': 'm1 0l2 -2m-2 0l2 2', |
|
'y': 'm1 -2v2h1v1v-1h1v-2v2', |
|
'z': 'm1 -2h2l-2 2h2', |
|
'|': 'm1 -3v3' |
|
} |
|
y = [0, -4, 1, 6] //viewBox with x, y, width, height |
|
x = '><path d="'; //XML fragment |
|
w = [ //array of default styles with fixed positions |
|
'fill:none', //w[0] |
|
'stroke:#000', //w[1] |
|
'stroke-linecap:round', //w[2] |
|
'stroke-linejoin:round', //w[3] |
|
'stroke-width:.9' //w[4] |
|
] |
|
this.color = function(a) { return this.setStyle(1, a) } |
|
this.linecap = function(a) { return this.setStyle(2, a) } |
|
this.linejoin = function(a) { return this.setStyle(3, a) } |
|
this.width = function(a) { return this.setStyle(4, a) } |
|
this.setStyle = function(a, b) |
|
{ |
|
w[a] = w[a].replace(/:.*/, ':' + b); |
|
return this //make all method calls chainable |
|
} |
|
this.padding = function(a) //can not be called multiple times |
|
{ |
|
a = --a || -1; //default padding is 1 |
|
y = [y[0] - a, y[1] - a, y[2] + a * 2, y[3] + a * 2]; |
|
return this |
|
} |
|
// 127 bytes |
|
this.text = function(a, b, c, d) //can be called multiple times |
|
{ |
|
d = ''; //compile path for the current string |
|
for (b = 0; c = a.charAt(b++); ) //for each character in the string |
|
d += z[c] || //append path from the character map or |
|
z._; //use the underscore for undefined characters |
|
b = /[hlm]([--9]+)/g; //match first number from all horizontal/line/move commands |
|
for (; c = b.exec(d); ) //for each match in the compiled path |
|
y[2] += +c[1]; //expand viewBox by the matched width, converted to a number |
|
x += d; //append the compiled path to the XML fragment |
|
return this //make the method call chainable |
|
} |
|
this.background = function(a) //can not be called multiple times |
|
{ |
|
// This way of setting a background color (instead of adding a colored rect) |
|
// works in Firefox and Opera but is ignored when loading the SVG in Inkscape. |
|
x = ' style="background:' + (a || '#FFF') + '"' + x; |
|
return this |
|
} |
|
this.toString = function() |
|
{ |
|
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="' + y.join(' ') + '"' + |
|
x + '" style="' + w.join(';') + '"/></svg>' |
|
} |
|
} |
|
var container = document.getElementsByTagName('H1')[0]; |
|
var refreshLogo = function(a, b) |
|
{ |
|
container.innerHTML = new svg().color('#333').text(a.toLowerCase()); |
|
// A more complex example: |
|
//container.innerHTML = new svg().background('green').color('white').padding(3).width(.5).text(a); |
|
|
|
// Instead of embedding the SVG in HTML we can create an image object with a data URI. |
|
// This is a little more compatible but flickers when replacing the image. |
|
//b = new Image(); |
|
//b.src = 'data:image/svg+xml,' + escape(new svg().text(a)); |
|
//container.replaceChild(b, container.firstChild); |
|
} |
|
refreshLogo(container.firstChild.data); |
|
// Create optional favicon, because we can. ;-) |
|
var l = document.createElement('LINK'); |
|
l.rel = 'shortcut icon'; |
|
// Opera is able to render the background color but does not when using the SVG as favicon. |
|
// Firefox does not display the SVG favicon when reloading the page. |
|
l.href = 'data:image/svg+xml,' + escape(new svg().background('#FD2').padding(.7).width(.6).text('js')); |
|
document.getElementsByTagName('HEAD')[0].appendChild(l); |
|
</script> |
Thank you. It was not possible to do this trick without eval. I like SVG, too, but canvas (especially with shims like excanvas) is much better supported. The whole logo data is currently 763bytes long. but that's an unoptimized array that could probably be packed rather simple. Even moreso, I'd like to look for an algorythmic way to store that data (preferably in <=140bytes), but I haven't found one yet.