From 174078bca93e7926907890cb4bbd2be23ba3c7b9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 31 Mar 2011 11:58:28 +0000 Subject: [PATCH] adding support for line breaks to SVG, SVG2 and Canvas renderers. Also adds vertical alignment support to Canvas. p=vog,fredj,me r=me (closes #2193) git-svn-id: http://svn.openlayers.org/trunk/openlayers@11838 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/vector-features-with-text.html | 5 +- lib/OpenLayers/Renderer/Canvas.js | 76 ++++++++++++++++++------- lib/OpenLayers/Renderer/SVG.js | 67 ++++++++++++++++------ lib/OpenLayers/Renderer/SVG2.js | 61 +++++++++++++++----- 4 files changed, 152 insertions(+), 57 deletions(-) diff --git a/examples/vector-features-with-text.html b/examples/vector-features-with-text.html index e336b194cb..8a9d8579d5 100644 --- a/examples/vector-features-with-text.html +++ b/examples/vector-features-with-text.html @@ -29,7 +29,8 @@ fillOpacity: 0.5, pointRadius: 6, pointerEvents: "visiblePainted", - label : "name: ${name}, age: ${age}", + // label with \n linebreaks + label : "name: ${name}\n\nage: ${age}", fontColor: "${favColor}", fontSize: "12px", @@ -113,7 +114,7 @@ map.addLayer(vectorLayer); vectorLayer.drawFeature(multiFeature); - map.setCenter(new OpenLayers.LonLat(point.x, point.y), 3); + map.setCenter(new OpenLayers.LonLat(-109.370078125, 43.39484375), 4); vectorLayer.addFeatures([pointFeature, polygonFeature, multiFeature, labelOffsetFeature, nullFeature ]); } diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index f0efeee689..3a5de7ce56 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -9,7 +9,7 @@ /** * Class: OpenLayers.Renderer.Canvas - * A renderer based on the 2D 'canvas' drawing element.element + * A renderer based on the 2D 'canvas' drawing element. * * Inherits: * - @@ -329,42 +329,61 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { labelAlign: "cm" }, style); var pt = this.getLocalXY(location); - + this.setCanvasStyle("reset"); this.canvas.fillStyle = style.fontColor; this.canvas.globalAlpha = style.fontOpacity || 1.0; var fontStyle = [style.fontStyle ? style.fontStyle : "normal", "normal", // "font-variant" not supported style.fontWeight ? style.fontWeight : "normal", - style.fontSize ? style.fontSize : "10px", + style.fontSize ? style.fontSize : "1em", style.fontFamily ? style.fontFamily : "sans-serif"].join(" "); + var labelRows = style.label.split('\n'); + var numRows = labelRows.length; if (this.canvas.fillText) { // HTML5 - var labelAlign = + this.canvas.font = fontStyle; + this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || "center"; - this.canvas.font = fontStyle; - this.canvas.textAlign = labelAlign; - this.canvas.fillText(style.label, pt[0], pt[1]); + this.canvas.textBaseline = + OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || + "middle"; + var vfactor = + OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; + if (vfactor == null) { + vfactor = -.5; + } + var lineHeight = + this.canvas.measureText('Mg').height || + this.canvas.measureText('xx').width; + pt[1] += lineHeight*vfactor*(numRows-1); + for (var i = 0; i < numRows; i++) { + this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i)); + } } else if (this.canvas.mozDrawText) { // Mozilla pre-Gecko1.9.1 (} */ drawText: function(featureId, style, location) { var resolution = this.getResolution(); - + var x = (location.x / resolution + this.left); var y = (location.y / resolution - this.top); - + var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text"); - var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan"); label.setAttributeNS(null, "x", x); label.setAttributeNS(null, "y", -y); - + if (style.fontColor) { label.setAttributeNS(null, "fill", style.fontColor); } @@ -715,12 +714,9 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { if (style.fontStyle) { label.setAttributeNS(null, "font-style", style.fontStyle); } - if(style.labelSelect === true) { + if (style.labelSelect === true) { label.setAttributeNS(null, "pointer-events", "visible"); label._featureId = featureId; - tspan._featureId = featureId; - tspan._geometry = location; - tspan._geometryClass = location.CLASS_NAME; } else { label.setAttributeNS(null, "pointer-events", "none"); } @@ -731,17 +727,43 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { if (OpenLayers.IS_GECKO === true) { label.setAttributeNS(null, "dominant-baseline", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); - } else { - tspan.setAttributeNS(null, "baseline-shift", - OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); } - tspan.textContent = style.label; - - if(!label.parentNode) { - label.appendChild(tspan); + var labelRows = style.label.split('\n'); + var numRows = labelRows.length; + while (label.childNodes.length > numRows) { + label.removeChild(label.lastChild); + } + for (var i = 0; i < numRows; i++) { + var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan"); + if (style.labelSelect === true) { + tspan._featureId = featureId; + tspan._geometry = location; + tspan._geometryClass = location.CLASS_NAME; + } + if (OpenLayers.IS_GECKO === false) { + tspan.setAttributeNS(null, "baseline-shift", + OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); + } + tspan.setAttribute("x", x); + if (i == 0) { + var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; + if (vfactor == null) { + vfactor = -.5; + } + tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em"); + } else { + tspan.setAttribute("dy", "1em"); + } + tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; + if (!tspan.parentNode) { + label.appendChild(tspan); + } + } + + if (!label.parentNode) { this.textRoot.appendChild(label); - } + } }, /** @@ -988,6 +1010,15 @@ OpenLayers.Renderer.SVG.LABEL_VSHIFT = { "b": "0" }; +/** + * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR + * {Object} + */ +OpenLayers.Renderer.SVG.LABEL_VFACTOR = { + "t": 0, + "b": -1 +}; + /** * Function: OpenLayers.Renderer.SVG.preventDefault * Used to prevent default events (especially opening images in a new tab on diff --git a/lib/OpenLayers/Renderer/SVG2.js b/lib/OpenLayers/Renderer/SVG2.js index b304f25dc5..c4c7f1ec1a 100644 --- a/lib/OpenLayers/Renderer/SVG2.js +++ b/lib/OpenLayers/Renderer/SVG2.js @@ -570,8 +570,8 @@ OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, { * Method: drawText * Function for drawing text labels. * This method is only called by the renderer itself. - * - * Parameters: + * + * Parameters: * featureId - {String|DOMElement} * style - {Object} * location - {}, will be modified inline @@ -583,14 +583,12 @@ OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, { var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments); var text = g.firstChild || this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text"); - var tspan = text.firstChild || - this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan"); var res = this.getResolution(); text.setAttributeNS(null, "x", location.x / res); text.setAttributeNS(null, "y", - location.y / res); g.setAttributeNS(null, "transform", "scale(" + res + ")"); - + if (style.fontColor) { text.setAttributeNS(null, "fill", style.fontColor); } @@ -609,10 +607,9 @@ OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, { if (style.fontStyle) { text.setAttributeNS(null, "font-style", style.fontStyle); } - if(style.labelSelect === true) { + if (style.labelSelect === true) { text.setAttributeNS(null, "pointer-events", "visible"); text._featureId = featureId; - tspan._featureId = featureId; } else { text.setAttributeNS(null, "pointer-events", "none"); } @@ -623,18 +620,43 @@ OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, { if (OpenLayers.IS_GECKO === true) { text.setAttributeNS(null, "dominant-baseline", OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central"); - } else { - tspan.setAttributeNS(null, "baseline-shift", - OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%"); } - tspan.textContent = style.label; - - if(!text.parentNode) { - text.appendChild(tspan); + var labelRows = style.label.split('\n'); + var numRows = labelRows.length; + while (text.childNodes.length > numRows) { + text.removeChild(text.lastChild); + } + for (var i = 0; i < numRows; i++) { + var tspan = text.childNodes[i] || + this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan"); + if (style.labelSelect === true) { + tspan._featureId = featureId; + } + if (OpenLayers.IS_GECKO === false) { + tspan.setAttributeNS(null, "baseline-shift", + OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%"); + } + tspan.setAttribute("x", location.x / res); + if (i == 0) { + var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]]; + if (vfactor == null) { + vfactor = -.5; + } + tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em"); + } else { + tspan.setAttribute("dy", "1em"); + } + tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; + if (!tspan.parentNode) { + text.appendChild(tspan); + } + } + + if (!text.parentNode) { g.appendChild(text); } - + return g; }, @@ -786,6 +808,15 @@ OpenLayers.Renderer.SVG2.LABEL_VSHIFT = { "b": "0" }; +/** + * Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR + * {Object} + */ +OpenLayers.Renderer.SVG2.LABEL_VFACTOR = { + "t": 0, + "b": -1 +}; + /** * Function: OpenLayers.Renderer.SVG2.preventDefault * Used to prevent default events (especially opening images in a new tab on