diff --git a/package.json b/package.json index 95e01aa5..e0bd808f 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "react-addons-shallow-compare": "^0.14.7", "react-addons-update": "^0.14.7", "react-dom": "^0.14.7", - "save-svg-as-png": "^1.0.2", + "save-svg-as-png": "git://github.com/nsonnad/saveSvgAsPng.git#clean-svg-fonts", "sizeof": "^1.0.0", "sugar-date": "^1.5.1" }, diff --git a/src/js/components/ChartExport.jsx b/src/js/components/ChartExport.jsx index 1a43ba22..83baf2ba 100644 --- a/src/js/components/ChartExport.jsx +++ b/src/js/components/ChartExport.jsx @@ -35,38 +35,6 @@ var ChartExport = React.createClass({ }; }, - createSVGFile: function(chart) { - // Returns a valid svg string representing the chart that can be opened in graphics software - - // retrieve the current size - var size = chart.getBoundingClientRect(); - var scale_factor = 2; - - chart = this._addIDsForIllustrator(chart); - - // get a useable SVG string using SVG Crowbar - var svg_content = this.createSVGContent(chart); - - // set the width of the svg in pixels for output - var svg_string = svg_content.source[0] - .split('width="100%"').join('width="'+size.width*scale_factor+'"') - .split('height="100%"').join('height="'+size.height*scale_factor+'"'); - - return "data:text/svg," + svg_string; - }, - - embedCSS: function(type) { - if (type == "add") { - d3.selectAll("." + this.props.svgWrapperClassName) - .append("style") - .attr("type","text/css") - .html("\n\n"); - } - else { - d3.selectAll("." + this.props.svgWrapperClassName + " style").remove(); - } - }, - componentDidMount: function() { var enableSvgExport; var chartNode = null; @@ -78,17 +46,9 @@ var ChartExport = React.createClass({ .getElementsByClassName(this.props.svgWrapperClassName)[0] .getElementsByClassName("renderer-svg")[0]; - try { - var svgHref = this.createSVGFile(chart); - enableSvgExport = true; - chartNode = chart; - } catch (e) { - enableSvgExport = false; - } - this.setState({ chartNode: chart, - enableSvgExport: enableSvgExport + enableSvgExport: true }); }, @@ -100,55 +60,6 @@ var ChartExport = React.createClass({ this.setState({ chartNode: chart }); }, - styleStringify: function(styleSheets) { - var sheet, style_strings = [], rules, rule; - - for (var i = styleSheets.length - 1; i >= 0; i--) { - sheet = styleSheets[i]; - if (sheet) { - if (sheet.rules !== undefined) { - rules = sheet.rules; - } - else { - rules = sheet.cssRules; - } - - for (var j = rules.length - 1; j >= 0; j--) { - if (rules[j].style) { - rule = this._cleanRulesForIllustrator(rules[j]); - style_strings.unshift(rule.cssText); - } - } - } - } - - return style_strings.join("\n"); - }, - - _cleanRulesForIllustrator: function(rule) { - // Adobe Illustrator freaks out with certain style declarations this strips those out - var styleDec = rule.style; - var pd; - for (var i = styleDec.length - 1; i >= 0; i--) { - orig_prop = styleDec[i]; - orig_dec = styleDec[orig_prop]; - - pd = { - prop: orig_prop, - dec: orig_dec - }; - - pd = this._cleanRuleOfExtraFonts(pd); - - rule.style[pd.prop] = pd.dec; - - rule.cssText = rule.cssText.split(orig_prop + ":" + orig_dec + ";") - .join(pd.prop + ":" + pd.dec + ";"); - } - - return rule; - }, - _addIDsForIllustrator: function(node) { var chart = d3.select(node); var classAttr = ""; @@ -169,65 +80,6 @@ var ChartExport = React.createClass({ return chart[0][0]; }, - _cleanRuleOfExtraFonts: function(pd) { - // Adobe Illustrator freaks out with fallback fonts, remove fallback fonts - if(pd.prop == "font-family") { - pd.dec = pd.dec.split(",")[0]; - } - - return {"prop":pd.prop,"dec":pd.dec}; - }, - - createSVGContent: function(svg) { - /* - Copyright (c) 2013 The New York Times - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - //via https://github.com/NYTimes/svg-crowbar - - var prefix = { - xmlns: "http://www.w3.org/2000/xmlns/", - xlink: "http://www.w3.org/1999/xlink", - svg: "http://www.w3.org/2000/svg" - }; - - var doctype = ''; - - svg.setAttribute("version", "1.1"); - - var defsEl = document.createElement("defs"); - svg.insertBefore(defsEl, svg.firstChild); //TODO .insert("defs", ":first-child") - - var styleEl = document.createElement("style"); - defsEl.appendChild(styleEl); - styleEl.setAttribute("type", "text/css"); - - // removing attributes so they aren't doubled up - svg.removeAttribute("xmlns"); - svg.removeAttribute("xlink"); - - // These are needed for the svg - if (!svg.hasAttributeNS(prefix.xmlns, "xmlns")) { - svg.setAttributeNS(prefix.xmlns, "xmlns", prefix.svg); - } - - if (!svg.hasAttributeNS(prefix.xmlns, "xmlns:xlink")) { - svg.setAttributeNS(prefix.xmlns, "xmlns:xlink", prefix.xlink); - } - - //TODO atually get the styles - var styles = ""; - - var source = (new XMLSerializer()).serializeToString(svg).replace('', ''); - - return {svg: svg, source: [doctype + source]}; - }, - _makeFilename: function(extension) { var filename = this.props.data.reduce(function(a, b, i) { if (a.length === 0) { @@ -242,26 +94,15 @@ var ChartExport = React.createClass({ ].join("."); }, - createSVGOutput: function(callback) { - // updates the download links with the data-uris and download file names - var filename = this._makeFilename("svg"); - //clone the svg so that the image creation and svg creation don't conflict - return { - download: filename, - href: this.createSVGFile(this.state.chartNode) - }; - }, - downloadPNG: function() { filename = this._makeFilename("png"); saveSvgAsPng.saveSvgAsPng(this.state.chartNode, filename, { scale: 2.0 }); }, - downloadSVG: function() { - var output = this.createSVGOutput(); + _autoClickDownload: function(filename, href) { var a = document.createElement('a'); - a.download = output.download; - a.href = output.href; + a.download = filename; + a.href = href; document.body.appendChild(a); a.addEventListener("click", function(e) { a.parentNode.removeChild(a); @@ -269,6 +110,21 @@ var ChartExport = React.createClass({ a.click(); }, + downloadSVG: function() { + var filename = this._makeFilename("svg"); + var chart = this._addIDsForIllustrator(this.state.chartNode); + var autoClickDownload = this._autoClickDownload; + saveSvgAsPng.svgAsDataUri(chart, { + cleanFontDefs: true, + fontFamilyRemap: { + "Khula-Light": "Khula Light", + "Khula-Regular": "Khula", + } + }, function(uri) { + autoClickDownload(filename, uri); + }); + }, + downloadJSON: function() { json_string = JSON.stringify({ @@ -276,14 +132,9 @@ var ChartExport = React.createClass({ metadata: this.props.model.metadata }, null, "\t") - var a = document.createElement('a'); - a.download = this._makeFilename(".json") - a.href = "data:text/json;charset=utf-8," + encodeURIComponent(json_string); - document.body.appendChild(a); - a.addEventListener("click", function(e) { - a.parentNode.removeChild(a); - }); - a.click(); + var filename = this._makeFilename("json"); + var href = "data:text/json;charset=utf-8," + encodeURIComponent(json_string); + this._autoClickDownload(filename, href); }, setAdvancedOptionState: function() { diff --git a/src/js/index.js b/src/js/index.js index f669f95e..51a1e2f7 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -21,9 +21,4 @@ document.addEventListener("DOMContentLoaded", function() { />, container ); - //append the current timestamp to the end of the stylesheets on load so that the fonts will load - Array.prototype.slice.call(document.querySelectorAll('link[rel="stylesheet"]')) - .forEach(function(el){ - el.setAttribute("href",el.getAttribute("href").split("?")[0] + "?t=" + (new Date().getTime())); - }); }); diff --git a/src/styl/chart-renderer.styl b/src/styl/chart-renderer.styl index 5c9977a8..6ebcb091 100644 --- a/src/styl/chart-renderer.styl +++ b/src/styl/chart-renderer.styl @@ -33,6 +33,10 @@ svg .d4 font-size $em_size + .leftAxis path.domain + display none + .xAxis path.domain + display none .axis .tick font-family $font-sans-light