/** * Copyright (c) 2006-2015, JGraph Ltd * Copyright (c) 2006-2015, Gaudenz Alder */ /** * * Class: mxVmlCanvas2D * * Implements a canvas to be used for rendering VML. Here is an example of implementing a * fallback for SVG images which are not supported in VML-based browsers. * * (code) * var mxVmlCanvas2DImage = mxVmlCanvas2D.prototype.image; * mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) * { * if (src.substring(src.length - 4, src.length) == '.svg') * { * src = 'http://www.jgraph.com/images/mxgraph.gif'; * } * * mxVmlCanvas2DImage.apply(this, arguments); * }; * (end) * * To disable anti-aliasing in the output, use the following code. * * (code) * document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{antialias:false;)}'; * (end) * * A description of the public API is available in . Note that * there is a known issue in VML where gradients are painted using the outer * bounding box of rotated shapes, not the actual bounds of the shape. See * also for plain text label restrictions in shapes for VML. */ var mxVmlCanvas2D = function(root) { mxAbstractCanvas2D.call(this); /** * Variable: root * * Reference to the container for the SVG content. */ this.root = root; }; /** * Extends mxAbstractCanvas2D */ mxUtils.extend(mxVmlCanvas2D, mxAbstractCanvas2D); /** * Variable: path * * Holds the current DOM node. */ mxVmlCanvas2D.prototype.node = null; /** * Variable: textEnabled * * Specifies if text output should be enabledetB. Default is true. */ mxVmlCanvas2D.prototype.textEnabled = true; /** * Variable: moveOp * * Contains the string used for moving in paths. Default is 'm'. */ mxVmlCanvas2D.prototype.moveOp = 'm'; /** * Variable: lineOp * * Contains the string used for moving in paths. Default is 'l'. */ mxVmlCanvas2D.prototype.lineOp = 'l'; /** * Variable: curveOp * * Contains the string used for bezier curves. Default is 'c'. */ mxVmlCanvas2D.prototype.curveOp = 'c'; /** * Variable: closeOp * * Holds the operator for closing curves. Default is 'x e'. */ mxVmlCanvas2D.prototype.closeOp = 'x'; /** * Variable: rotatedHtmlBackground * * Background color for rotated HTML. Default is ''. This can be set to eg. * white to improve rendering of rotated text in VML for IE9. */ mxVmlCanvas2D.prototype.rotatedHtmlBackground = ''; /** * Variable: vmlScale * * Specifies the scale used to draw VML shapes. */ mxVmlCanvas2D.prototype.vmlScale = 1; /** * Function: createElement * * Creates the given element using the document. */ mxVmlCanvas2D.prototype.createElement = function(name) { return document.createElement(name); }; /** * Function: createVmlElement * * Creates a new element using and prefixes the given name with * . */ mxVmlCanvas2D.prototype.createVmlElement = function(name) { return this.createElement(mxClient.VML_PREFIX + ':' + name); }; /** * Function: addNode * * Adds the current node to the . */ mxVmlCanvas2D.prototype.addNode = function(filled, stroked) { var node = this.node; var s = this.state; if (node != null) { if (node.nodeName == 'shape') { // Checks if the path is not empty if (this.path != null && this.path.length > 0) { node.path = this.path.join(' ') + ' e'; node.style.width = this.root.style.width; node.style.height = this.root.style.height; node.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); } else { return; } } node.strokeweight = this.format(Math.max(1, s.strokeWidth * s.scale / this.vmlScale)) + 'px'; if (s.shadow) { this.root.appendChild(this.createShadow(node, filled && s.fillColor != null, stroked && s.strokeColor != null)); } if (stroked && s.strokeColor != null) { node.stroked = 'true'; node.strokecolor = s.strokeColor; } else { node.stroked = 'false'; } node.appendChild(this.createStroke()); if (filled && s.fillColor != null) { node.appendChild(this.createFill()); } else if (this.pointerEvents && (node.nodeName != 'shape' || this.path[this.path.length - 1] == this.closeOp)) { node.appendChild(this.createTransparentFill()); } else { node.filled = 'false'; } // LATER: Update existing DOM for performance this.root.appendChild(node); } }; /** * Function: createTransparentFill * * Creates a transparent fill. */ mxVmlCanvas2D.prototype.createTransparentFill = function() { var fill = this.createVmlElement('fill'); fill.src = mxClient.imageBasePath + '/transparent.gif'; fill.type = 'tile'; return fill; }; /** * Function: createFill * * Creates a fill for the current state. */ mxVmlCanvas2D.prototype.createFill = function() { var s = this.state; // Gradients in foregrounds not supported because special gradients // with bounds must be created for each element in graphics-canvases var fill = this.createVmlElement('fill'); fill.color = s.fillColor; if (s.gradientColor != null) { fill.type = 'gradient'; fill.method = 'none'; fill.color2 = s.gradientColor; var angle = 180 - s.rotation; if (s.gradientDirection == mxConstants.DIRECTION_WEST) { angle -= 90 + ((this.root.style.flip == 'x') ? 180 : 0); } else if (s.gradientDirection == mxConstants.DIRECTION_EAST) { angle += 90 + ((this.root.style.flip == 'x') ? 180 : 0); } else if (s.gradientDirection == mxConstants.DIRECTION_NORTH) { angle -= 180 + ((this.root.style.flip == 'y') ? -180 : 0); } else { angle += ((this.root.style.flip == 'y') ? -180 : 0); } if (this.root.style.flip == 'x' || this.root.style.flip == 'y') { angle *= -1; } // LATER: Fix outer bounding box for rotated shapes used in VML. fill.angle = mxUtils.mod(angle, 360); fill.opacity = (s.alpha * s.gradientFillAlpha * 100) + '%'; fill.setAttribute(mxClient.OFFICE_PREFIX + ':opacity2', (s.alpha * s.gradientAlpha * 100) + '%'); } else if (s.alpha < 1 || s.fillAlpha < 1) { fill.opacity = (s.alpha * s.fillAlpha * 100) + '%'; } return fill; }; /** * Function: createStroke * * Creates a fill for the current state. */ mxVmlCanvas2D.prototype.createStroke = function() { var s = this.state; var stroke = this.createVmlElement('stroke'); stroke.endcap = s.lineCap || 'flat'; stroke.joinstyle = s.lineJoin || 'miter'; stroke.miterlimit = s.miterLimit || '10'; if (s.alpha < 1 || s.strokeAlpha < 1) { stroke.opacity = (s.alpha * s.strokeAlpha * 100) + '%'; } if (s.dashed) { stroke.dashstyle = this.getVmlDashStyle(); } return stroke; }; /** * Function: getVmlDashPattern * * Returns a VML dash pattern for the current dashPattern. * See http://msdn.microsoft.com/en-us/library/bb264085(v=vs.85).aspx */ mxVmlCanvas2D.prototype.getVmlDashStyle = function() { var result = 'dash'; if (typeof(this.state.dashPattern) === 'string') { var tok = this.state.dashPattern.split(' '); if (tok.length > 0 && tok[0] == 1) { result = '0 2'; } } return result; }; /** * Function: createShadow * * Creates a shadow for the given node. */ mxVmlCanvas2D.prototype.createShadow = function(node, filled, stroked) { var s = this.state; var rad = -s.rotation * (Math.PI / 180); var cos = Math.cos(rad); var sin = Math.sin(rad); var dx = s.shadowDx * s.scale; var dy = s.shadowDy * s.scale; if (this.root.style.flip == 'x') { dx *= -1; } else if (this.root.style.flip == 'y') { dy *= -1; } var shadow = node.cloneNode(true); shadow.style.marginLeft = Math.round(dx * cos - dy * sin) + 'px'; shadow.style.marginTop = Math.round(dx * sin + dy * cos) + 'px'; // Workaround for wrong cloning in IE8 standards mode if (document.documentMode == 8) { shadow.strokeweight = node.strokeweight; if (node.nodeName == 'shape') { shadow.path = this.path.join(' ') + ' e'; shadow.style.width = this.root.style.width; shadow.style.height = this.root.style.height; shadow.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); } } if (stroked) { shadow.strokecolor = s.shadowColor; shadow.appendChild(this.createShadowStroke()); } else { shadow.stroked = 'false'; } if (filled) { shadow.appendChild(this.createShadowFill()); } else { shadow.filled = 'false'; } return shadow; }; /** * Function: createShadowFill * * Creates the fill for the shadow. */ mxVmlCanvas2D.prototype.createShadowFill = function() { var fill = this.createVmlElement('fill'); fill.color = this.state.shadowColor; fill.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; return fill; }; /** * Function: createShadowStroke * * Creates the stroke for the shadow. */ mxVmlCanvas2D.prototype.createShadowStroke = function() { var stroke = this.createStroke(); stroke.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; return stroke; }; /** * Function: rotate * * Sets the rotation of the canvas. Note that rotation cannot be concatenated. */ mxVmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy) { if (flipH && flipV) { theta += 180; } else if (flipH) { this.root.style.flip = 'x'; } else if (flipV) { this.root.style.flip = 'y'; } if (flipH ? !flipV : flipV) { theta *= -1; } this.root.style.rotation = theta; this.state.rotation = this.state.rotation + theta; this.state.rotationCx = cx; this.state.rotationCy = cy; }; /** * Function: begin * * Extends superclass to create path. */ mxVmlCanvas2D.prototype.begin = function() { mxAbstractCanvas2D.prototype.begin.apply(this, arguments); this.node = this.createVmlElement('shape'); this.node.style.position = 'absolute'; }; /** * Function: quadTo * * Replaces quadratic curve with bezier curve in VML. */ mxVmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2) { var s = this.state; var cpx0 = (this.lastX + s.dx) * s.scale; var cpy0 = (this.lastY + s.dy) * s.scale; var qpx1 = (x1 + s.dx) * s.scale; var qpy1 = (y1 + s.dy) * s.scale; var cpx3 = (x2 + s.dx) * s.scale; var cpy3 = (y2 + s.dy) * s.scale; var cpx1 = cpx0 + 2/3 * (qpx1 - cpx0); var cpy1 = cpy0 + 2/3 * (qpy1 - cpy0); var cpx2 = cpx3 + 2/3 * (qpx1 - cpx3); var cpy2 = cpy3 + 2/3 * (qpy1 - cpy3); this.path.push('c ' + this.format(cpx1) + ' ' + this.format(cpy1) + ' ' + this.format(cpx2) + ' ' + this.format(cpy2) + ' ' + this.format(cpx3) + ' ' + this.format(cpy3)); this.lastX = (cpx3 / s.scale) - s.dx; this.lastY = (cpy3 / s.scale) - s.dy; }; /** * Function: createRect * * Sets the glass gradient. */ mxVmlCanvas2D.prototype.createRect = function(nodeName, x, y, w, h) { var s = this.state; var n = this.createVmlElement(nodeName); n.style.position = 'absolute'; n.style.left = this.format((x + s.dx) * s.scale) + 'px'; n.style.top = this.format((y + s.dy) * s.scale) + 'px'; n.style.width = this.format(w * s.scale) + 'px'; n.style.height = this.format(h * s.scale) + 'px'; return n; }; /** * Function: rect * * Sets the current path to a rectangle. */ mxVmlCanvas2D.prototype.rect = function(x, y, w, h) { this.node = this.createRect('rect', x, y, w, h); }; /** * Function: roundrect * * Sets the current path to a rounded rectangle. */ mxVmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy) { this.node = this.createRect('roundrect', x, y, w, h); // SetAttribute needed here for IE8 this.node.setAttribute('arcsize', Math.max(dx * 100 / w, dy * 100 / h) + '%'); }; /** * Function: ellipse * * Sets the current path to an ellipse. */ mxVmlCanvas2D.prototype.ellipse = function(x, y, w, h) { this.node = this.createRect('oval', x, y, w, h); }; /** * Function: image * * Paints an image. */ mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) { var node = null; if (!aspect) { node = this.createRect('image', x, y, w, h); node.src = src; } else { // Uses fill with aspect to avoid asynchronous update of size node = this.createRect('rect', x, y, w, h); node.stroked = 'false'; // Handles image aspect via fill var fill = this.createVmlElement('fill'); fill.aspect = (aspect) ? 'atmost' : 'ignore'; fill.rotate = 'true'; fill.type = 'frame'; fill.src = src; node.appendChild(fill); } if (flipH && flipV) { node.style.rotation = '180'; } else if (flipH) { node.style.flip = 'x'; } else if (flipV) { node.style.flip = 'y'; } if (this.state.alpha < 1 || this.state.fillAlpha < 1) { // KNOWN: Borders around transparent images in IE<9. Using fill.opacity // fixes this problem by adding a white background in all IE versions. node.style.filter += 'alpha(opacity=' + (this.state.alpha * this.state.fillAlpha * 100) + ')'; } this.root.appendChild(node); }; /** * Function: createText * * Creates the innermost element that contains the HTML text. */ mxVmlCanvas2D.prototype.createDiv = function(str, align, valign, overflow) { var div = this.createElement('div'); var state = this.state; var css = ''; if (state.fontBackgroundColor != null) { css += 'background-color:' + mxUtils.htmlEntities(state.fontBackgroundColor) + ';'; } if (state.fontBorderColor != null) { css += 'border:1px solid ' + mxUtils.htmlEntities(state.fontBorderColor) + ';'; } if (mxUtils.isNode(str)) { div.appendChild(str); } else { if (overflow != 'fill' && overflow != 'width') { var div2 = this.createElement('div'); div2.style.cssText = css; div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; div2.style.zoom = '1'; div2.style.textDecoration = 'inherit'; div2.innerHTML = str; div.appendChild(div2); } else { div.style.cssText = css; div.innerHTML = str; } } var style = div.style; style.fontSize = (state.fontSize / this.vmlScale) + 'px'; style.fontFamily = state.fontFamily; style.color = state.fontColor; style.verticalAlign = 'top'; style.textAlign = align || 'left'; style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (state.fontSize * mxConstants.LINE_HEIGHT / this.vmlScale) + 'px' : mxConstants.LINE_HEIGHT; if ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) { style.fontWeight = 'bold'; } if ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) { style.fontStyle = 'italic'; } if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) { style.textDecoration = 'underline'; } return div; }; /** * Function: text * * Paints the given text. Possible values for format are empty string for plain * text and html for HTML markup. Clipping, text background and border are not * supported for plain text in VML. */ mxVmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) { if (this.textEnabled && str != null) { var s = this.state; if (format == 'html') { if (s.rotation != null) { var pt = this.rotatePoint(x, y, s.rotation, s.rotationCx, s.rotationCy); x = pt.x; y = pt.y; } if (document.documentMode == 8 && !mxClient.IS_EM) { x += s.dx; y += s.dy; // Workaround for rendering offsets if (overflow != 'fill' && valign == mxConstants.ALIGN_TOP) { y -= 1; } } else { x *= s.scale; y *= s.scale; } // Adds event transparency in IE8 standards without the transparent background // filter which cannot be used due to bugs in the zoomed bounding box (too slow) // FIXME: No event transparency if inside v:rect (ie part of shape) // KNOWN: Offset wrong for rotated text with word that are longer than the wrapping // width in IE8 because real width of text cannot be determined here. // This should be fixed in mxText.updateBoundingBox by calling before this and // passing the real width to this method if not clipped and wrapped. var abs = (document.documentMode == 8 && !mxClient.IS_EM) ? this.createVmlElement('group') : this.createElement('div'); abs.style.position = 'absolute'; abs.style.display = 'inline'; abs.style.left = this.format(x) + 'px'; abs.style.top = this.format(y) + 'px'; abs.style.zoom = s.scale; var box = this.createElement('div'); box.style.position = 'relative'; box.style.display = 'inline'; var margin = mxUtils.getAlignmentAsPoint(align, valign); var dx = margin.x; var dy = margin.y; var div = this.createDiv(str, align, valign, overflow); var inner = this.createElement('div'); if (dir != null) { div.setAttribute('dir', dir); } if (wrap && w > 0) { if (!clip) { div.style.width = Math.round(w) + 'px'; } div.style.wordWrap = mxConstants.WORD_WRAP; div.style.whiteSpace = 'normal'; // LATER: Check if other cases need to be handled if (div.style.wordWrap == 'break-word') { var tmp = div; if (tmp.firstChild != null && tmp.firstChild.nodeName == 'DIV') { tmp.firstChild.style.width = '100%'; } } } else { div.style.whiteSpace = 'nowrap'; } var rot = s.rotation + (rotation || 0); if (this.rotateHtml && rot != 0) { inner.style.display = 'inline'; inner.style.zoom = '1'; inner.appendChild(div); // Box not needed for rendering in IE8 standards if (document.documentMode == 8 && !mxClient.IS_EM && this.root.nodeName != 'DIV') { box.appendChild(inner); abs.appendChild(box); } else { abs.appendChild(inner); } } else if (document.documentMode == 8 && !mxClient.IS_EM) { box.appendChild(div); abs.appendChild(box); } else { div.style.display = 'inline'; abs.appendChild(div); } // Inserts the node into the DOM if (this.root.nodeName != 'DIV') { // Rectangle to fix position in group var rect = this.createVmlElement('rect'); rect.stroked = 'false'; rect.filled = 'false'; rect.appendChild(abs); this.root.appendChild(rect); } else { this.root.appendChild(abs); } if (clip) { div.style.overflow = 'hidden'; div.style.width = Math.round(w) + 'px'; if (!mxClient.IS_QUIRKS) { div.style.maxHeight = Math.round(h) + 'px'; } } else if (overflow == 'fill') { // KNOWN: Affects horizontal alignment in quirks // but fill should only be used with align=left div.style.overflow = 'hidden'; div.style.width = (Math.max(0, w) + 1) + 'px'; div.style.height = (Math.max(0, h) + 1) + 'px'; } else if (overflow == 'width') { // KNOWN: Affects horizontal alignment in quirks // but fill should only be used with align=left div.style.overflow = 'hidden'; div.style.width = (Math.max(0, w) + 1) + 'px'; div.style.maxHeight = (Math.max(0, h) + 1) + 'px'; } if (this.rotateHtml && rot != 0) { var rad = rot * (Math.PI / 180); // Precalculate cos and sin for the rotation var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8)); var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8)); rad %= 2 * Math.PI; if (rad < 0) rad += 2 * Math.PI; rad %= Math.PI; if (rad > Math.PI / 2) rad = Math.PI - rad; var cos = Math.cos(rad); var sin = Math.sin(rad); // Adds div to document to measure size if (document.documentMode == 8 && !mxClient.IS_EM) { div.style.display = 'inline-block'; inner.style.display = 'inline-block'; box.style.display = 'inline-block'; } div.style.visibility = 'hidden'; div.style.position = 'absolute'; document.body.appendChild(div); var sizeDiv = div; if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV') { sizeDiv = sizeDiv.firstChild; } var tmp = sizeDiv.offsetWidth + 3; var oh = sizeDiv.offsetHeight; if (clip) { w = Math.min(w, tmp); oh = Math.min(oh, h); } else { w = tmp; } // Handles words that are longer than the given wrapping width if (wrap) { div.style.width = w + 'px'; } // Simulates max-height in quirks if (mxClient.IS_QUIRKS && (clip || overflow == 'width') && oh > h) { oh = h; // Quirks does not support maxHeight div.style.height = oh + 'px'; } h = oh; var top_fix = (h - h * cos + w * -sin) / 2 - real_sin * w * (dx + 0.5) + real_cos * h * (dy + 0.5); var left_fix = (w - w * cos + h * -sin) / 2 + real_cos * w * (dx + 0.5) + real_sin * h * (dy + 0.5); if (abs.nodeName == 'group' && this.root.nodeName == 'DIV') { // Workaround for bug where group gets moved away if left and top are non-zero in IE8 standards var pos = this.createElement('div'); pos.style.display = 'inline-block'; pos.style.position = 'absolute'; pos.style.left = this.format(x + (left_fix - w / 2) * s.scale) + 'px'; pos.style.top = this.format(y + (top_fix - h / 2) * s.scale) + 'px'; abs.parentNode.appendChild(pos); pos.appendChild(abs); } else { var sc = (document.documentMode == 8 && !mxClient.IS_EM) ? 1 : s.scale; abs.style.left = this.format(x + (left_fix - w / 2) * sc) + 'px'; abs.style.top = this.format(y + (top_fix - h / 2) * sc) + 'px'; } // KNOWN: Rotated text rendering quality is bad for IE9 quirks inner.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11="+real_cos+", M12="+ real_sin+", M21="+(-real_sin)+", M22="+real_cos+", sizingMethod='auto expand')"; inner.style.backgroundColor = this.rotatedHtmlBackground; if (this.state.alpha < 1) { inner.style.filter += 'alpha(opacity=' + (this.state.alpha * 100) + ')'; } // Restore parent node for DIV inner.appendChild(div); div.style.position = ''; div.style.visibility = ''; } else if (document.documentMode != 8 || mxClient.IS_EM) { div.style.verticalAlign = 'top'; if (this.state.alpha < 1) { abs.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; } // Adds div to document to measure size var divParent = div.parentNode; div.style.visibility = 'hidden'; document.body.appendChild(div); w = div.offsetWidth; var oh = div.offsetHeight; // Simulates max-height in quirks if (mxClient.IS_QUIRKS && clip && oh > h) { oh = h; // Quirks does not support maxHeight div.style.height = oh + 'px'; } h = oh; div.style.visibility = ''; divParent.appendChild(div); abs.style.left = this.format(x + w * dx * this.state.scale) + 'px'; abs.style.top = this.format(y + h * dy * this.state.scale) + 'px'; } else { if (this.state.alpha < 1) { div.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; } // Faster rendering in IE8 without offsetWidth/Height box.style.left = (dx * 100) + '%'; box.style.top = (dy * 100) + '%'; } } else { this.plainText(x, y, w, h, mxUtils.htmlEntities(str, false), align, valign, wrap, format, overflow, clip, rotation, dir); } } }; /** * Function: plainText * * Paints the outline of the current path. */ mxVmlCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) { // TextDirection is ignored since this code is not used (format is always HTML in the text function) var s = this.state; x = (x + s.dx) * s.scale; y = (y + s.dy) * s.scale; var node = this.createVmlElement('shape'); node.style.width = '1px'; node.style.height = '1px'; node.stroked = 'false'; var fill = this.createVmlElement('fill'); fill.color = s.fontColor; fill.opacity = (s.alpha * 100) + '%'; node.appendChild(fill); var path = this.createVmlElement('path'); path.textpathok = 'true'; path.v = 'm ' + this.format(0) + ' ' + this.format(0) + ' l ' + this.format(1) + ' ' + this.format(0); node.appendChild(path); // KNOWN: Font family and text decoration ignored var tp = this.createVmlElement('textpath'); tp.style.cssText = 'v-text-align:' + align; tp.style.align = align; tp.style.fontFamily = s.fontFamily; tp.string = str; tp.on = 'true'; // Scale via fontsize instead of node.style.zoom for correct offsets in IE8 var size = s.fontSize * s.scale / this.vmlScale; tp.style.fontSize = size + 'px'; // Bold if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) { tp.style.fontWeight = 'bold'; } // Italic if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) { tp.style.fontStyle = 'italic'; } // Underline if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) { tp.style.textDecoration = 'underline'; } var lines = str.split('\n'); var textHeight = size + (lines.length - 1) * size * mxConstants.LINE_HEIGHT; var dx = 0; var dy = 0; if (valign == mxConstants.ALIGN_BOTTOM) { dy = - textHeight / 2; } else if (valign != mxConstants.ALIGN_MIDDLE) // top { dy = textHeight / 2; } if (rotation != null) { node.style.rotation = rotation; var rad = rotation * (Math.PI / 180); dx = Math.sin(rad) * dy; dy = Math.cos(rad) * dy; } // FIXME: Clipping is relative to bounding box /*if (clip) { node.style.clip = 'rect(0px ' + this.format(w) + 'px ' + this.format(h) + 'px 0px)'; }*/ node.appendChild(tp); node.style.left = this.format(x - dx) + 'px'; node.style.top = this.format(y + dy) + 'px'; this.root.appendChild(node); }; /** * Function: stroke * * Paints the outline of the current path. */ mxVmlCanvas2D.prototype.stroke = function() { this.addNode(false, true); }; /** * Function: fill * * Fills the current path. */ mxVmlCanvas2D.prototype.fill = function() { this.addNode(true, false); }; /** * Function: fillAndStroke * * Fills and paints the outline of the current path. */ mxVmlCanvas2D.prototype.fillAndStroke = function() { this.addNode(true, true); };