From f06701055de4862f7976586619c5fed8cddb151a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Sun, 10 Feb 2013 10:23:49 -0800 Subject: [PATCH] Implement exporting of Symbols to SVG. --- examples/SVG Export/Symbols.html | 26 ++++++++ src/svg/SvgExport.js | 104 +++++++++++++++++++++++++------ src/svg/SvgImport.js | 12 ++-- 3 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 examples/SVG Export/Symbols.html diff --git a/examples/SVG Export/Symbols.html b/examples/SVG Export/Symbols.html new file mode 100644 index 00000000..46a554b5 --- /dev/null +++ b/examples/SVG Export/Symbols.html @@ -0,0 +1,26 @@ + + + + + Symbols + + + + + + + + + \ No newline at end of file diff --git a/src/svg/SvgExport.js b/src/svg/SvgExport.js index 359b4f80..bc7229c7 100644 --- a/src/svg/SvgExport.js +++ b/src/svg/SvgExport.js @@ -22,12 +22,20 @@ new function() { return formatFloat(point.x) + ',' + formatFloat(point.y); } + function formatRectangle(rect) { + return formatFloat(rect.x) + ',' + formatFloat(rect.y) + + ',' + formatFloat(rect.width) + ',' + formatFloat(rect.height); + } + function setAttributes(svg, attrs) { for (var key in attrs) { var val = attrs[key]; if (typeof val === 'number') val = formatFloat(val); - svg.setAttribute(key, val); + if (key === 'href') + svg.setAttributeNS('http://www.w3.org/1999/xlink','href', val); + else + svg.setAttribute(key, val); } return svg; } @@ -208,7 +216,7 @@ new function() { attrs.fill = 'none'; var svg = createElement('g', attrs); for (var i = 0, l = children.length; i < l; i++) { - var child = children[i].exportSvg(); + var child = exportSvg(children[i]); if (child) svg.appendChild(child); } @@ -218,13 +226,12 @@ new function() { function exportRaster(item) { var attrs = getTransform(item, true), size = item.getSize(); - attrs.width = size.width; - attrs.height = size.height; attrs.x -= size.width / 2; attrs.y -= size.height / 2; - var svg = createElement('image', attrs); - svg.setAttributeNS('http://www.w3.org/1999/xlink','href', item.toDataURL()); - return svg; + attrs.width = size.width; + attrs.height = size.height; + attrs.href = item.toDataURL(); + return createElement('image', attrs); } function exportText(item) { @@ -336,14 +343,35 @@ new function() { return createElement(type, attrs); } - function exportCompoundPath(path) { - var children = path._children, + function exportCompoundPath(item) { + var attrs = getTransform(item, true), + children = item._children, paths = []; for (var i = 0, l = children.length; i < l; i++) paths.push(getPath(children[i])); - return createElement('path', { - d: paths.join(' ') - }); + attrs.d = paths.join(' '); + return createElement('path', attrs); + } + + function exportPlacedSymbol(item) { + var attrs = getTransform(item, true), + symbol = item.getSymbol(), + symbolSvg = getDefinition(symbol); + definition = symbol.getDefinition(), + bounds = definition.getBounds(); + if (!symbolSvg) { + symbolSvg = createElement('symbol', { + viewBox: formatRectangle(bounds) + }); + symbolSvg.appendChild(exportSvg(definition)); + setDefinition(symbol, symbolSvg, 'symbol'); + } + attrs.href = '#' + symbolSvg.id; + attrs.x += bounds.x; + attrs.y += bounds.y; + attrs.width = formatFloat(bounds.width); + attrs.height = formatFloat(bounds.height); + return createElement('use', attrs); } var exporters = { @@ -352,9 +380,9 @@ new function() { raster: exportRaster, pointtext: exportText, path: exportPath, - compoundpath: exportCompoundPath + compoundpath: exportCompoundPath, + placedsymbol: exportPlacedSymbol // TODO: - // placedsymbol: // gradients }; @@ -398,6 +426,45 @@ new function() { return setAttributes(svg, attrs); } + var definitions; + function getDefinition(item) { + if (!definitions) + definitions = { ids: {}, svgs: {} }; + return definitions.svgs[item._id]; + } + + function setDefinition(item, svg, type) { + var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1; + svg.id = type + '-' + id; + definitions.svgs[item._id] = svg; + } + + function exportDefinitions(svg) { + if (!definitions) + return svg; + // We can only use svg nodes as defintion containers. Have the loop + // produce one if it's a single item of another type (when calling + // #exportSvg() on an item rather than a whole project) + var container = svg.nodeName.toLowerCase() == 'svg' && svg, + firstChild = container ? container.firstChild : svg; + for (var i in definitions.svgs) { + if (!container) { + container = createElement('svg'); + container.appendChild(svg); + } + container.insertBefore(definitions.svgs[i], firstChild); + } + // Clear definitions at the end of export + definitions = null; + return container; + } + + function exportSvg(item) { + var exporter = exporters[item._type], + svg = exporter && exporter(item, item._type); + return svg && applyStyle(item, svg); + } + Item.inject(/** @lends Item# */{ /** * {@grouptitle SVG Conversion} @@ -408,9 +475,8 @@ new function() { * @return {SVGSVGElement} the item converted to an SVG node */ exportSvg: function() { - var exporter = exporters[this._type], - svg = exporter && exporter(this, this._type); - return svg && applyStyle(this, svg); + var svg = exportSvg(this); + return exportDefinitions(svg); } }); @@ -427,8 +493,8 @@ new function() { var svg = createElement('svg'), layers = this.layers; for (var i = 0, l = layers.length; i < l; i++) - svg.appendChild(layers[i].exportSvg()); - return svg; + svg.appendChild(exportSvg(layers[i])); + return exportDefinitions(svg); } }); }; diff --git a/src/svg/SvgImport.js b/src/svg/SvgImport.js index b067d9dd..df209a84 100644 --- a/src/svg/SvgImport.js +++ b/src/svg/SvgImport.js @@ -240,12 +240,6 @@ new function() { return null; } - var definitions = {}; - function getDefinition(value) { - var match = value.match(/\(#([^)']+)/); - return match && definitions[match[1]]; - } - var importers = { // http://www.w3.org/TR/SVG/struct.html#Groups g: importGroup, @@ -502,6 +496,12 @@ new function() { } } + var definitions = {}; + function getDefinition(value) { + var match = value.match(/\(#([^)']+)/); + return match && definitions[match[1]]; + } + function importSvg(svg, clearDefs) { var type = svg.nodeName.toLowerCase(), importer = importers[type],