From a51fc053e39fffc5fd32a8b99ca9c6a03960e51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 17 May 2017 15:18:15 -0400 Subject: [PATCH] fix toSVG for marker gradient - style="fill: url("#...")" break the XML seriliazer, - we need to swap those inner \" before seriliazing and bring those back in the SVG after, similar to how 'font-family' properties are handled. --- src/snapshot/tosvg.js | 20 +++++++++-- test/jasmine/tests/snapshot_test.js | 54 +++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/snapshot/tosvg.js b/src/snapshot/tosvg.js index 92188e7b3ff..e5db4f6cd0e 100644 --- a/src/snapshot/tosvg.js +++ b/src/snapshot/tosvg.js @@ -16,6 +16,9 @@ var Drawing = require('../components/drawing'); var Color = require('../components/color'); var xmlnsNamespaces = require('../constants/xmlns_namespaces'); +var DOUBLEQUOTE_REGEX = /"/g; +var DUMMY_SUB = 'TOBESTRIPPED'; +var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g'); module.exports = function toSVG(gd, format) { @@ -90,10 +93,21 @@ module.exports = function toSVG(gd, format) { // to a string (browsers convert singles back) var ff = txt.style('font-family'); if(ff && ff.indexOf('"') !== -1) { - txt.style('font-family', ff.replace(/"/g, 'TOBESTRIPPED')); + txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB)); } }); + svg.selectAll('.point').each(function() { + var pt = d3.select(this); + var fill = pt.style('fill'); + + // similar to font family styles above, + // we must remove " after the SVG DOM has been serialized + if(fill && fill.indexOf('url(') !== -1) { + pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB)); + } + }); + if(format === 'pdf' || format === 'eps') { // these formats make the extra line MathJax adds around symbols look super thick in some cases // it looks better if this is removed entirely. @@ -110,8 +124,8 @@ module.exports = function toSVG(gd, format) { s = svgTextUtils.html_entity_decode(s); s = svgTextUtils.xml_entity_encode(s); - // Fix quotations around font strings - s = s.replace(/("TOBESTRIPPED)|(TOBESTRIPPED")/g, '\''); + // Fix quotations around font strings and gradient URLs + s = s.replace(DUMMY_REGEX, '\''); return s; }; diff --git a/test/jasmine/tests/snapshot_test.js b/test/jasmine/tests/snapshot_test.js index cd712b5a1b0..16caa318a70 100644 --- a/test/jasmine/tests/snapshot_test.js +++ b/test/jasmine/tests/snapshot_test.js @@ -3,6 +3,7 @@ var Plotly = require('@lib/index'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var subplotMock = require('../../image/mocks/multiple_subplots.json'); var annotationMock = require('../../image/mocks/annotations.json'); @@ -191,8 +192,8 @@ describe('Plotly.Snapshot', function() { }); describe('toSVG', function() { - var parser = new DOMParser(), - gd; + var parser = new DOMParser(); + var gd; beforeEach(function() { gd = createGraphDiv(); @@ -200,7 +201,6 @@ describe('Plotly.Snapshot', function() { afterEach(destroyGraphDiv); - it('should not return any nested svg tags of plots', function(done) { Plotly.plot(gd, subplotMock.data, subplotMock.layout).then(function() { return Plotly.Snapshot.toSVG(gd); @@ -245,5 +245,53 @@ describe('Plotly.Snapshot', function() { done(); }); }); + + it('should handle quoted style properties', function(done) { + Plotly.plot(gd, [{ + y: [1, 2, 1], + marker: { + gradient: { + type: 'radial', + color: '#fff' + }, + color: ['red', 'blue', 'green'] + } + }], { + font: { family: 'Times New Roman' } + }) + .then(function() { + d3.selectAll('text').each(function() { + var tx = d3.select(this); + expect(tx.style('font-family')).toEqual('\"Times New Roman\"'); + }); + + d3.selectAll('.point').each(function() { + var pt = d3.select(this); + expect(pt.style('fill').substr(0, 6)).toEqual('url(\"#'); + }); + + return Plotly.Snapshot.toSVG(gd); + }) + .then(function(svg) { + var svgDOM = parser.parseFromString(svg, 'image/svg+xml'); + var i; + + var textElements = svgDOM.getElementsByTagName('text'); + expect(textElements.length).toEqual(11); + + for(i = 0; i < textElements.length; i++) { + expect(textElements[i].style['font-family']).toEqual('\"Times New Roman\"'); + } + + var pointElements = svgDOM.getElementsByClassName('point'); + expect(pointElements.length).toEqual(3); + + for(i = 0; i < pointElements.length; i++) { + expect(pointElements[i].style.fill.substr(0, 6)).toEqual('url(\"#'); + } + }) + .catch(fail) + .then(done); + }); }); });