Skip to content

Commit

Permalink
Fixed #5985, issue with allowHTML in offline exporting.
Browse files Browse the repository at this point in the history
  • Loading branch information
oysteinmoseng authored and TorsteinHonsi committed Nov 21, 2016
1 parent c38cbb3 commit 578c973
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 35 deletions.
51 changes: 22 additions & 29 deletions js/modules/exporting.src.js
Expand Up @@ -195,10 +195,25 @@ H.post = function (url, data, formAttributes) {
extend(Chart.prototype, {

/**
* A collection of regex fixes on the produces SVG to account for expando properties,
* A collection of fixes on the produced SVG to account for expando properties,
* browser bugs, VML problems and other. Returns a cleaned SVG.
*/
sanitizeSVG: function (svg) {
sanitizeSVG: function (svg, options) {
// Move HTML into a foreignObject
if (options && options.exporting && options.exporting.allowHTML) {
var html = svg.match(/<\/svg>(.*?$)/);
if (html) {
html = '<foreignObject x="0" y="0" ' +
'width="' + options.chart.width + '" ' +
'height="' + options.chart.height + '">' +
'<body xmlns="http://www.w3.org/1999/xhtml">' +
html[1] +
'</body>' +
'</foreignObject>';
svg = svg.replace('</svg>', html + '</svg>');
}
}

svg = svg
.replace(/zIndex="[^"]+"/g, '')
.replace(/isShadow="[^"]+"/g, '')
Expand Down Expand Up @@ -267,9 +282,7 @@ extend(Chart.prototype, {
sourceHeight,
cssWidth,
cssHeight,
html,
options = merge(chart.options, additionalOptions), // copy the options and add extra options
allowHTML = options.exporting.allowHTML;
options = merge(chart.options, additionalOptions); // copy the options and add extra options


// IE compatibility hack for generating SVG content that it doesn't really understand
Expand Down Expand Up @@ -352,36 +365,16 @@ extend(Chart.prototype, {
});
});

// get the SVG from the container's innerHTML
// Get the SVG from the container's innerHTML
svg = chartCopy.getChartHTML();

svg = chart.sanitizeSVG(svg, options);

// free up memory
options = null;
chartCopy.destroy();
discardElement(sandbox);

// Move HTML into a foreignObject
if (allowHTML) {
html = svg.match(/<\/svg>(.*?$)/);
if (html) {
html = '<foreignObject x="0" y="0" ' +
'width="' + sourceWidth + '" ' +
'height="' + sourceHeight + '">' +
'<body xmlns="http://www.w3.org/1999/xhtml">' +
html[1] +
'</body>' +
'</foreignObject>';
svg = svg.replace('</svg>', html + '</svg>');
}
}

// sanitize
svg = this.sanitizeSVG(svg);

// IE9 beta bugs with innerHTML. Test again with final IE9.
svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
.replace(/&quot;/g, '\'');


return svg;
},

Expand Down
31 changes: 25 additions & 6 deletions js/modules/offline-exporting.src.js
Expand Up @@ -310,9 +310,14 @@ Highcharts.Chart.prototype.getSVGForLocalExport = function (options, chartOption
images,
imagesEmbedded = 0,
chartCopyContainer,
chartCopyOptions,
el,
i,
l,
// After grabbing the SVG of the chart's copy container we need to do sanitation on the SVG
sanitize = function (svg) {
return chart.sanitizeSVG(svg, chartCopyOptions);
},
// Success handler, we converted image to base64!
embeddedSuccess = function (imageURL, imageType, callbackArgs) {
++imagesEmbedded;
Expand All @@ -322,7 +327,7 @@ Highcharts.Chart.prototype.getSVGForLocalExport = function (options, chartOption

// When done with last image we have our SVG
if (imagesEmbedded === images.length) {
successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML));
successCallback(sanitize(chartCopyContainer.innerHTML));
}
};

Expand All @@ -335,6 +340,7 @@ Highcharts.Chart.prototype.getSVGForLocalExport = function (options, chartOption
this,
Array.prototype.slice.call(arguments, 1)
);
chartCopyOptions = this.options;
chartCopyContainer = this.container.cloneNode(true);
return ret;
}
Expand All @@ -347,7 +353,7 @@ Highcharts.Chart.prototype.getSVGForLocalExport = function (options, chartOption
try {
// If there are no images to embed, the SVG is okay now.
if (!images.length) {
successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML)); // Use SVG of chart copy
successCallback(sanitize(chartCopyContainer.innerHTML)); // Use SVG of chart copy
return;
}

Expand Down Expand Up @@ -387,12 +393,25 @@ Highcharts.Chart.prototype.exportChartLocal = function (exportingOptions, chartO
}
},
svgSuccess = function (svg) {
Highcharts.downloadSVGLocal(svg, options, fallbackToExportServer);
// If SVG contains foreignObjects all exports except SVG will fail,
// as both CanVG and svg2pdf choke on this. Gracefully fall back.
if (
svg.indexOf('<foreignObject') > -1 &&
options.type !== 'image/svg+xml'
) {
fallbackToExportServer();
} else {
Highcharts.downloadSVGLocal(svg, options, fallbackToExportServer);
}
};

// If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back.
// Also fall back for embedded images with PDF.
if ((isMSBrowser && options.type !== 'image/svg+xml' || options.type === 'application/pdf') && chart.container.getElementsByTagName('image').length) {
// If we have embedded images and are exporting to JPEG/PNG, Microsoft
// browsers won't handle it, so fall back.
if (
(isMSBrowser && options.type !== 'image/svg+xml' ||
options.type === 'application/pdf') &&
chart.container.getElementsByTagName('image').length
) {
fallbackToExportServer();
return;
}
Expand Down
@@ -0,0 +1,5 @@
#container {
max-width: 800px;
height: 400px;
margin: 1em auto;
}
@@ -0,0 +1,6 @@
---
name: Highcharts Demo
authors:
- Øystein Moseng
requiresManualTesting: true
...
@@ -0,0 +1,5 @@
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/offline-exporting.js"></script>

<div id="container"></div>
44 changes: 44 additions & 0 deletions samples/highcharts/exporting/offline-download-usehtml/demo.js
@@ -0,0 +1,44 @@
$(function () {

Highcharts.chart('container', {

exporting: {
chartOptions: { // specific options for the exported image
plotOptions: {
series: {
dataLabels: {
useHTML: true,
enabled: true
}
}
}
},
scale: 3,
fallbackToExportServer: false,
allowHTML: true
},

title: {
text: 'Offline export'
},

subtitle: {
text: 'Should fail for all except SVG'
},

chart: {
type: 'area'
},

xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},

series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 126.0, 148.5, 216.4, 194.1, 95.6, 54.4]
}]

});

});

0 comments on commit 578c973

Please sign in to comment.