I spent today working on adding support to render text using the VML
renderer. I used the VML textpath and for the most part it works
really nicely. I think with a little work it can be enhanced to match
the SVG renderer. Currently, the main issue in the text size in VML
isn't matching the SVG rendering... The main changes in this patch
are in vml.js Here's the svn diff inline and to get the original
patch file: http://severna.homeip.net/vml-render-text.patch
Index: samples/js/test.js
===================================================================
--- samples/js/test.js (revision 77)
+++ samples/js/test.js (working copy)
@@ -257,7 +257,8 @@
shape.setTextValue("Hello World!");
shape.setFill(getFill(index, nbShapes));
shape.setStroke(getStroke(index, nbShapes, 1));
- shape.setBounds(0, 30, 15, 10);
+ shape.setBounds(0, 30, 20, 20);
+ shape.setFont("20px","Arial","normal");
shape.translate(130, 600);
shape.rotate(angle, 0, 0);
@@ -295,4 +296,4 @@
renderer.draw();
var end = new Date().getTime();
return end - start;
-}
\ No newline at end of file
+}
Index: src/shape/text.js
===================================================================
--- src/shape/text.js (revision 77)
+++ src/shape/text.js (working copy)
@@ -24,11 +24,11 @@
},
getSize: function() {
- return {fontSize: this.attributes['font-size'], fontWeight:
this.attributes['font-weight']};
+ return {width: this.attributes.width, height:
this.attributes.height};
},
- setSize: function(fontSize, fontWeight) {
- this._setAttributes({'font-size': fontSize, 'font-weight':
fontWeight});
+ setSize: function(width, height) {
+ this._setAttributes({'width': width, 'height': height});
return this;
},
@@ -42,7 +42,9 @@
},
getFont: function() {
- return {fontSize: this.attributes['font-size'], fontFamily:
this.attributes['font-family'], fontWeight: this.attributes['font-
weight']};
+ return {fontSize: this.attributes['font-size'],
+ fontFamily: this.attributes['font-family'],
+ fontWeight: this.attributes['font-weight']};
},
setFont: function(size, family, weight) {
Index: src/renderer/vml.js
===================================================================
--- src/renderer/vml.js (revision 77)
+++ src/renderer/vml.js (working copy)
@@ -12,7 +12,7 @@
*/
//
-// Graphic.VMLRenderer render class.
+// Graphic.VMLRenderer render class.
//
Graphic.VMLRenderer = Class.create();
@@ -21,13 +21,13 @@
//
Object.extend(Graphic.VMLRenderer, {
init: false,
-
+
createNode: function(nodeName) {
return document.createElement(nodeName);;
},
-
- initBrowser: function () {
- if (!Graphic.VMLRenderer.init && document.readyState ==
"complete") {
+
+ initBrowser: function () {
+ if (!Graphic.VMLRenderer.init && document.readyState ==
"complete") {
// create xmlns
if (!document.namespaces["v"]) {
document.namespaces.add("v", "urn:schemas-microsoft-
com:vml");
@@ -42,26 +42,29 @@
})
Object.extend(Graphic.VMLRenderer.prototype,
Graphic.AbstractRender.prototype);
-Graphic.VMLRenderer.prototype._parentInitialize =
Graphic.AbstractRender.prototype.initialize;
-Graphic.VMLRenderer.prototype._parentSetSize =
Graphic.AbstractRender.prototype.setSize;
+Graphic.VMLRenderer.prototype._parentInitialize =
Graphic.AbstractRender.prototype.initialize;
+Graphic.VMLRenderer.prototype._parentSetSize =
Graphic.AbstractRender.prototype.setSize;
Object.extend(Graphic.VMLRenderer.prototype, {
initialize: function(element) {
Graphic.VMLRenderer.initBrowser();
-
+
this._parentInitialize(element);
-
+
this.element = Graphic.VMLRenderer.createNode("v:group");
- this.element.style.height = this.bounds.h + "px";
- this.element.style.width = this.bounds.w + "px";
+ this.element.style.height = this.bounds.h + "px";
+ this.element.style.width = this.bounds.w + "px";
+ this.element.style.position = "relative";
+ this.element.style.top = "0px";
+ this.element.style.left = "0px";
this._setViewing();
this.element.graphicNode = this;
- $(element).appendChild(this.element);
+ $(element).appendChild(this.element);
},
-
- destroy: function() {
+
+ destroy: function() {
$A(this.element.childNodes).each(function(node) {
if (node.shape) {
node.shape.destroy();
@@ -71,39 +74,47 @@
})
this.element.parentNode.removeChild(this.element);
},
-
- setSize: function(width, height) {
+
+ setSize: function(width, height) {
this._parentSetSize(width, height);
- this.element.style.height = this.bounds.h + "px";
- this.element.style.width = this.bounds.w + "px";
+ this.element.style.height = this.bounds.h + "px";
+ this.element.style.width = this.bounds.w + "px";
this.zoom(this.viewing.sx, this.viewing.sy)
},
- createShape: function(shape){
- var node = null;
+ createShape: function(shape){
+ var node = null;
switch (shape.nodeName) {
case "rect":
- node = Graphic.VMLRenderer.createNode("v:roundrect");
+ node = Graphic.VMLRenderer.createNode("v:roundrect");
node.arcsize = 0;
- break;
+ break;
case "ellipse":
case "circle":
node = Graphic.VMLRenderer.createNode("v:oval");
- break;
+ break;
case "polygon":
case "polyline":
- node = Graphic.VMLRenderer.createNode("v:shape");
- node.style.height = this.bounds.h + "px";
- node.style.width = this.bounds.w + "px";
+ node = Graphic.VMLRenderer.createNode("v:shape");
+ node.style.height = this.bounds.h + "px";
+ node.style.width = this.bounds.w + "px";
node.coordsize = this.bounds.w + ", " + this.bounds.h;
- node.coordorigin = "0, 0";
- break;
+ node.coordorigin = "0, 0";
+ break;
case "line":
- node = Graphic.VMLRenderer.createNode("v:line");
- break;
- // case "text":
- // node = Graphic.VMLRenderer.createNode("v:text");
- // break;
+ node = Graphic.VMLRenderer.createNode("v:line");
+ break;
+ case "text":
+ node = Graphic.VMLRenderer.createNode("v:shape");
+ node.style.height = this.bounds.h + "px";
+ node.style.width = this.bounds.w + "px";
+
+ var path = document.createElement("v:path");
+
+ path.textpathok=true;
+
+ node.appendChild(path);
+ break;
// case "image":
// node = document.createElement("div");
// node.style.position = "relative";
@@ -112,7 +123,7 @@
// node.style.filter =
"progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1,
Dx=100, Dy=0)";
// var img = document.createElement("img");
// node.appendChild(img);
- // break;
+ // break;
// case "image":
// node = document.createElement("div");
// node.style.position = "relative";
@@ -121,16 +132,16 @@
// node.style.filter =
"progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1,
Dx=100, Dy=0)";
// var img = document.createElement("img");
// node.appendChild(img);
- // break;
+ // break;
case "g":
- node = Graphic.VMLRenderer.createNode("v:group");
+ node = Graphic.VMLRenderer.createNode("v:group");
node.stroked = "false";
node.filled = "false";
- node.style.height = this.bounds.w + "px";
- node.style.width = this.bounds.h + "px";
+ node.style.height = this.bounds.w + "px";
+ node.style.width = this.bounds.h + "px";
node.coordsize = this.bounds.w + ", " + this.bounds.h;
- node.coordorigin = "0, 0";
- break;
+ node.coordorigin = "0, 0";
+ break;
default:
// console.log("shape " + shape.nodeName + " not yet
implemented for VML renderer");
break;
@@ -139,29 +150,29 @@
return null;
if (shape.nodeName != "g" && shape.nodeName != "image") {
// Create a fill node
- var fill = Graphic.VMLRenderer.createNode("v:fill");
+ var fill = Graphic.VMLRenderer.createNode("v:fill");
fill.on = "false";
node.appendChild(fill);
node.fill = fill;
-
+
// Create a stroke node
- var stroke = Graphic.VMLRenderer.createNode("v:stroke");
+ var stroke = Graphic.VMLRenderer.createNode("v:stroke");
stroke.on = "false";
node.appendChild(stroke);
- node.stroke = stroke;
+ node.stroke = stroke;
// Create a skew node
- var skew = Graphic.VMLRenderer.createNode("v:skew");
+ var skew = Graphic.VMLRenderer.createNode("v:skew");
skew.on = "true";
node.appendChild(skew);
- node.skew = skew;
+ node.skew = skew;
}
-
+
return node;
},
-
+
add: function(shape) {
- if (shape.parent) {
+ if (shape.parent) {
shape.parent.getRendererObject().appendChild(shape.getRendererObject());
// Why IE lost node attributes!!!
this.updateAttributes(shape, shape.attributes);
@@ -171,9 +182,9 @@
this.element.appendChild(shape.getRendererObject());
}
},
-
+
remove:function(shape) {
- if (shape.parent)
+ if (shape.parent)
shape.parent.getRendererObject().removeChild(shape.getRendererObject());
else
this.element.removeChild(shape.getRendererObject());
@@ -183,11 +194,11 @@
var element = $(id)
return element && element.shape ? element.shape : null;
},
-
+
shapes: function() {
return $A(this.element.childNodes).collect(function(element)
{return element.shape});
},
-
+
clear: function() {
$A(this.element.childNodes).each(function(element) {
element.shape.destroy();
@@ -198,47 +209,50 @@
var element = shape.element;
if (!element)
return;
-
- $H(attributes).keys().each(function(key) {
+
+ $H(attributes).keys().each(function(key) {
//console.log(element.nodeName + " " + key + " "+
attributes[key])
switch (key) {
case "width":
case "height":
element.style[key] = attributes[key] + "px";
- break;
+ this._updateTextPath(shape);
+ break;
case "cx":
case "cy":
case "rx":
- case "ry":
- case "r":
+ case "ry":
+ case "r":
if (element.nodeName != "roundrect") {
var rx = typeof shape.attributes.rx != "undefined" ?
shape.attributes.rx : shape.attributes.r;
var ry = typeof shape.attributes.ry != "undefined" ?
shape.attributes.ry : shape.attributes.r;
- element.style.left = (shape.attributes.cx -
rx).toFixed();
+ element.style.left = (shape.attributes.cx -
rx).toFixed();
element.style.top = (shape.attributes.cy -
ry).toFixed();
element.style.width = (rx * 2).toFixed();
element.style.height = (ry * 2).toFixed();
}
- break;
+ break;
case "x":
- element.style["left"] = attributes[key] + "px";
+ element.style["left"] = attributes[key] + "px";
+ this._updateTextPath(shape);
break;
case "y":
element.style["top"] = attributes[key] + "px";
+ this._updateTextPath(shape);
break;
case "fill":
if (element.fill) {
element.fill.color = attributes[key].parseColor();
element.fill.on = "true";
}
- break;
+ break;
case "fill-opacity":
if (element.fill) {
if (attributes[key] == "none") {
- element.fill.on = "false";
+ element.fill.on = "false";
}
- else {
- element.fill.opacity = attributes[key];
+ else {
+ element.fill.opacity = attributes[key];
element.fill.on = "true";
}
}
@@ -246,28 +260,28 @@
case "stroke":
if (element.stroke) {
if (attributes[key] == "none") {
- element.stroke.on = "false";
+ element.stroke.on = "false";
}
else {
element.stroke.color = attributes[key].parseColor();
element.stroke.on = "true";
}
}
- break;
+ break;
case "stroke-opacity":
if (element.stroke) {
- element.stroke.opacity = attributes[key];
+ element.stroke.opacity = attributes[key];
element.stroke.on = "true";
}
break;
case "stroke-width":
if (element.stroke) {
- element.stroke.weight = attributes[key] + "px";
+ element.stroke.weight = attributes[key] + "px";
element.stroke.on = "true";
}
- break;
- case "points":
- var p = shape.getPoints();
+ break;
+ case "points":
+ var p = shape.getPoints();
var attr = [];
if (p.length > 0) {
attr.push("m");
@@ -280,22 +294,29 @@
attr.push(p[i][1].toFixed());
}
}
- }
- if (shape.getType() == "polygon")
- attr.push("x");
- else
- attr.push("e");
- element.path = attr.join(" ");
- break;
- case "x1": //x2, y1, y2
+ }
+ if (shape.getType() == "polygon")
+ attr.push("x");
+ else
+ attr.push("e");
+ element.path = attr.join(" ");
+ break;
+ case "x1": //x2, y1, y2
element.from = shape.attributes.x1.toFixed() + " " +
shape.attributes.y1.toFixed();
element.to = shape.attributes.x2.toFixed() + " " +
shape.attributes.y2.toFixed();
break
+ case 'font-family':
+ case 'size':
+ case 'weight':
+ if( element.textpath ){
+ element.textpath.style.fontFamily = attributes[key];
+ }
+ break;
case "id":
element.id = attributes[key]
break;
case "class":
- // Nothing to do
+ // Nothing to do
element.id = attributes[key]
break;
case "href":
@@ -305,20 +326,20 @@
//console.log("updateAttributes: key " + key + "/" +
attributes[key] + " not supported")
break;
}
- });
+ }.bind(this));
},
-
+
updateTransform:function(shape) {
- if (!shape.element)
+ if (!shape.element)
return;
- var matrix = shape.getMatrix();
+ var matrix = shape.getMatrix();
if (shape instanceof Graphic.Group && shape.children) {
shape.children.each(function(s) {
var m = s.getMatrix()
this._updateSkew(s, Matrix.multiply(matrix, m))
}.bind(this))
}
- else if (shape instanceof Graphic.Image) {
+ else if (shape instanceof Graphic.Image) {
// if (shape.element.filters.length >0)
// with
(shape.element.filters["DXImageTransform.Microsoft.Matrix"]) {
// M11 = matrix.xx;
@@ -327,79 +348,107 @@
// M22 = matrix.yy;
// Dx = matrix.dx;
// Dy = matrix.dy;
- // }
+ // }
}
else {
this._updateSkew(shape, matrix);
}
},
-
- _updateSkew:function(shape, matrix) {
+
+ _updateSkew:function(shape, matrix) {
if (!shape.element.skew)
return;
-
+
var bounds = shape.getBounds();
shape.element.skew.on = "false";
- shape.element.skew.matrix = matrix.xx.toFixed(8) + " " +
matrix.xy.toFixed(8) + " " + matrix.yx.toFixed(8) + " " +
matrix.yy.toFixed(8) + " 0 0";
+ shape.element.skew.matrix = matrix.xx.toFixed(8) + " " +
matrix.xy.toFixed(8) + " " + matrix.yx.toFixed(8) + " " +
matrix.yy.toFixed(8) + " 0 0";
// TODO: Fix it with renderer viewing
//var pt =
Matrix.invert(this.viewingMatrix).multiplyPoint(matrix.dx, matrix.dy)
pt = {x:matrix.dx, y:matrix.dy}
- shape.element.skew.offset = Math.floor(pt.x).toFixed() + "px " +
Math.floor(pt.y).toFixed() + "px";
- shape.element.skew.origin = ((bounds.w != 0 ? -bounds.x /
bounds.w : 1) - 0.5).toFixed(8) + " " + ((bounds.h != 0 ? -bounds.y /
bounds.h : 1) - 0.5).toFixed(8);
- shape.element.skew.on = "true";
+ shape.element.skew.offset = Math.floor(pt.x).toFixed() + "px " +
Math.floor(pt.y).toFixed() + "px";
+ shape.element.skew.origin = ((bounds.w != 0 ? -bounds.x /
bounds.w : 1) - 0.5).toFixed(8) + " " + ((bounds.h != 0 ? -bounds.y /
bounds.h : 1) - 0.5).toFixed(8);
+ shape.element.skew.on = "true";
},
-
+
nbShapes: function() {
return this.element.childNodes.length;
},
-
+
moveToFront: function(node) {
if (this.nbShapes() > 0) {
//this.element.removeChild(node.element);
this.element.appendChild(node.element);
}
},
-
+
moveToBack: function(node) {
if (this.nbShapes() > 0) {
- this.element.insertBefore(node.element,
this.element.firstChild);
+ this.element.insertBefore(node.element,
this.element.firstChild);
}
},
show:function(shape) {
shape.element.style.display = "block";
},
-
+
hide:function(shape) {
- shape.element.style.display = "none";
+ shape.element.style.display = "none";
},
-
+
draw: function() {
// Nothing to do in VML
},
-
+
pick: function(event) {
- var element = Event.element(event);
+ var element = Event.element(event);
return element == this.element.parent ? null : element.shape;
},
-
+
position: function() {
if (this.offset == null)
this.offset =
Position.cumulativeOffset(this.element.parentNode);
return this.offset;
},
-
+
addComment: function(shape, text) {
shape.element.appendChild(document.createComment(text));
- },
-
+ },
+
addText: function(shape, text) {
-// shape.element.appendChild(document.createTextNode(text));
- },
-
- _setViewing: function() {
+ // try to find the textpath first
+ var childNodes = shape.element.childNodes;
+ var textpath = null;
+ for( var i = 0, len = childNodes.length; i < len; ++i ){
+ if( childNodes[i].tagName == "textpath" ){
+ textpath = childNodes[i];
+ break;
+ }
+ }
+ if( !textpath ){
+ var textpath = document.createElement("v:textpath");
+ textpath.on=true;
+ textpath.fitpath=false
+ textpath.fitshape=false
+ textpath.style.fontFamily = shape.getFont().fontFamily;;
+ textpath.style.fontSize = shape.getFont().fontSize;
+ textpath.style.fontWeight = shape.getFont().fontWeight;
+ shape.element.appendChild(textpath);
+ }
+ textpath.string = text;
+ this._updateTextPath(shape);
+ },
+ _updateTextPath: function(shape)
+ {
+ var location = shape.getLocation();
+ var size = shape.getSize();
+ if( location.x != undefined && location.y != undefined &&
size.width ){
+ shape.element.path = "m " + location.x + "," + location.y +
+ " l " + size.width + "," + location.y;
+ }
+ },
+ _setViewing: function() {
var bounds = this.viewingMatrix.multiplyBounds(this.bounds);
this.element.coordsize = bounds.w + ", " + bounds.h;
this.element.coordorigin = bounds.x + ", " + bounds.y;
}
-});
\ No newline at end of file
+});
In the samples/shape.html now the only difference is text doesn't work
with drag.
-Todd
Not sure where the best place to carry on this thought... but in
trying to further understand the issues with font differences between
SVG and VML. I think the main issue I've run into now is the
coordinate system between the two renderers appears to be different.
VML appears to set the zero zero position at the top left corner of a
group or container, while SVG appears to set the zero zero position in
the center.
My first thought to fix this was position:relative the containers with
a position absolute child... works in DHTML world... but I don't think
it carries over so well in the SVG/VML world...
-Todd
> ...
>
> read more »
> ...
>
> read more »
http://updates.simosoftware.com/pkg/heap-sort/prototype-graphic/vml-render-text.patch
as well my test app is here:
http://updates.simosoftware.com/pkg/heap-sort/heap-sort.html
You'll notice that on Firefox Mac svg text doesn't work however :-(