offline-exporting.src.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. /**
  2. * @license Highcharts JS v9.0.1 (2021-02-16)
  3. *
  4. * Client side exporting module
  5. *
  6. * (c) 2015-2021 Torstein Honsi / Oystein Moseng
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/offline-exporting', ['highcharts', 'highcharts/modules/exporting'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Extensions/DownloadURL.js', [_modules['Core/Globals.js']], function (Highcharts) {
  32. /* *
  33. *
  34. * (c) 2015-2021 Oystein Moseng
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * Mixin for downloading content in the browser
  41. *
  42. * */
  43. var win = Highcharts.win,
  44. doc = win.document,
  45. domurl = win.URL || win.webkitURL || win;
  46. /**
  47. * Convert base64 dataURL to Blob if supported, otherwise returns undefined.
  48. * @private
  49. * @function Highcharts.dataURLtoBlob
  50. * @param {string} dataURL
  51. * URL to convert
  52. * @return {string|undefined}
  53. * Blob
  54. */
  55. var dataURLtoBlob = Highcharts.dataURLtoBlob = function (dataURL) {
  56. var parts = dataURL
  57. .replace(/filename=.*;/, '')
  58. .match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
  59. if (parts &&
  60. parts.length > 3 &&
  61. win.atob &&
  62. win.ArrayBuffer &&
  63. win.Uint8Array &&
  64. win.Blob &&
  65. domurl.createObjectURL) {
  66. // Try to convert data URL to Blob
  67. var binStr = win.atob(parts[3]),
  68. buf = new win.ArrayBuffer(binStr.length),
  69. binary = new win.Uint8Array(buf);
  70. for (var i = 0; i < binary.length; ++i) {
  71. binary[i] = binStr.charCodeAt(i);
  72. }
  73. var blob = new win.Blob([binary], { 'type': parts[1] });
  74. return domurl.createObjectURL(blob);
  75. }
  76. };
  77. /**
  78. * Download a data URL in the browser. Can also take a blob as first param.
  79. *
  80. * @private
  81. * @function Highcharts.downloadURL
  82. * @param {string|global.URL} dataURL
  83. * The dataURL/Blob to download
  84. * @param {string} filename
  85. * The name of the resulting file (w/extension)
  86. * @return {void}
  87. */
  88. var downloadURL = Highcharts.downloadURL = function (dataURL,
  89. filename) {
  90. var nav = win.navigator;
  91. var a = doc.createElement('a'),
  92. windowRef;
  93. // IE specific blob implementation
  94. // Don't use for normal dataURLs
  95. if (typeof dataURL !== 'string' &&
  96. !(dataURL instanceof String) &&
  97. nav.msSaveOrOpenBlob) {
  98. nav.msSaveOrOpenBlob(dataURL, filename);
  99. return;
  100. }
  101. dataURL = "" + dataURL;
  102. // Some browsers have limitations for data URL lengths. Try to convert to
  103. // Blob or fall back. Edge always needs that blob.
  104. var isEdgeBrowser = /Edge\/\d+/.test(nav.userAgent);
  105. if (isEdgeBrowser || dataURL.length > 2000000) {
  106. dataURL = dataURLtoBlob(dataURL) || '';
  107. if (!dataURL) {
  108. throw new Error('Failed to convert to blob');
  109. }
  110. }
  111. // Try HTML5 download attr if supported
  112. if (typeof a.download !== 'undefined') {
  113. a.href = dataURL;
  114. a.download = filename; // HTML5 download attribute
  115. doc.body.appendChild(a);
  116. a.click();
  117. doc.body.removeChild(a);
  118. }
  119. else {
  120. // No download attr, just opening data URI
  121. try {
  122. windowRef = win.open(dataURL, 'chart');
  123. if (typeof windowRef === 'undefined' || windowRef === null) {
  124. throw new Error('Failed to open window');
  125. }
  126. }
  127. catch (e) {
  128. // window.open failed, trying location.href
  129. win.location.href = dataURL;
  130. }
  131. }
  132. };
  133. var exports = {
  134. dataURLtoBlob: dataURLtoBlob,
  135. downloadURL: downloadURL
  136. };
  137. return exports;
  138. });
  139. _registerModule(_modules, 'Extensions/OfflineExporting.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js'], _modules['Extensions/DownloadURL.js']], function (Chart, H, SVGRenderer, U, DownloadURL) {
  140. /* *
  141. *
  142. * Client side exporting module
  143. *
  144. * (c) 2015 Torstein Honsi / Oystein Moseng
  145. *
  146. * License: www.highcharts.com/license
  147. *
  148. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  149. *
  150. * */
  151. var win = H.win,
  152. doc = H.doc;
  153. var addEvent = U.addEvent,
  154. error = U.error,
  155. extend = U.extend,
  156. fireEvent = U.fireEvent,
  157. getOptions = U.getOptions,
  158. merge = U.merge;
  159. var downloadURL = DownloadURL.downloadURL;
  160. var domurl = win.URL || win.webkitURL || win,
  161. // Milliseconds to defer image load event handlers to offset IE bug
  162. loadEventDeferDelay = H.isMS ? 150 : 0;
  163. // Dummy object so we can reuse our canvas-tools.js without errors
  164. H.CanVGRenderer = {};
  165. /* eslint-disable valid-jsdoc */
  166. /**
  167. * Downloads a script and executes a callback when done.
  168. *
  169. * @private
  170. * @function getScript
  171. * @param {string} scriptLocation
  172. * @param {Function} callback
  173. * @return {void}
  174. */
  175. function getScript(scriptLocation, callback) {
  176. var head = doc.getElementsByTagName('head')[0], script = doc.createElement('script');
  177. script.type = 'text/javascript';
  178. script.src = scriptLocation;
  179. script.onload = callback;
  180. script.onerror = function () {
  181. error('Error loading script ' + scriptLocation);
  182. };
  183. head.appendChild(script);
  184. }
  185. /**
  186. * Get blob URL from SVG code. Falls back to normal data URI.
  187. *
  188. * @private
  189. * @function Highcharts.svgToDataURL
  190. * @param {string} svg
  191. * @return {string}
  192. */
  193. function svgToDataUrl(svg) {
  194. // Webkit and not chrome
  195. var userAgent = win.navigator.userAgent;
  196. var webKit = (userAgent.indexOf('WebKit') > -1 &&
  197. userAgent.indexOf('Chrome') < 0);
  198. try {
  199. // Safari requires data URI since it doesn't allow navigation to blob
  200. // URLs. Firefox has an issue with Blobs and internal references,
  201. // leading to gradients not working using Blobs (#4550)
  202. if (!webKit && !H.isFirefox) {
  203. return domurl.createObjectURL(new win.Blob([svg], {
  204. type: 'image/svg+xml;charset-utf-16'
  205. }));
  206. }
  207. }
  208. catch (e) {
  209. // Ignore
  210. }
  211. return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
  212. }
  213. /**
  214. * Get data:URL from image URL. Pass in callbacks to handle results.
  215. *
  216. * @private
  217. * @function Highcharts.imageToDataUrl
  218. *
  219. * @param {string} imageURL
  220. *
  221. * @param {string} imageType
  222. *
  223. * @param {*} callbackArgs
  224. * callbackArgs is used only by callbacks.
  225. *
  226. * @param {number} scale
  227. *
  228. * @param {Function} successCallback
  229. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  230. *
  231. * @param {Function} taintedCallback
  232. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  233. *
  234. * @param {Function} noCanvasSupportCallback
  235. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  236. *
  237. * @param {Function} failedLoadCallback
  238. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  239. *
  240. * @param {Function} [finallyCallback]
  241. * finallyCallback is always called at the end of the process. All
  242. * callbacks receive four arguments: imageURL, imageType, callbackArgs,
  243. * and scale.
  244. *
  245. * @return {void}
  246. */
  247. function imageToDataUrl(imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
  248. var img = new win.Image(), taintedHandler, loadHandler = function () {
  249. setTimeout(function () {
  250. var canvas = doc.createElement('canvas'), ctx = canvas.getContext && canvas.getContext('2d'), dataURL;
  251. try {
  252. if (!ctx) {
  253. noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
  254. }
  255. else {
  256. canvas.height = img.height * scale;
  257. canvas.width = img.width * scale;
  258. ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  259. // Now we try to get the contents of the canvas.
  260. try {
  261. dataURL = canvas.toDataURL(imageType);
  262. successCallback(dataURL, imageType, callbackArgs, scale);
  263. }
  264. catch (e) {
  265. taintedHandler(imageURL, imageType, callbackArgs, scale);
  266. }
  267. }
  268. }
  269. finally {
  270. if (finallyCallback) {
  271. finallyCallback(imageURL, imageType, callbackArgs, scale);
  272. }
  273. }
  274. // IE bug where image is not always ready despite calling load
  275. // event.
  276. }, loadEventDeferDelay);
  277. },
  278. // Image load failed (e.g. invalid URL)
  279. errorHandler = function () {
  280. failedLoadCallback(imageURL, imageType, callbackArgs, scale);
  281. if (finallyCallback) {
  282. finallyCallback(imageURL, imageType, callbackArgs, scale);
  283. }
  284. };
  285. // This is called on load if the image drawing to canvas failed with a
  286. // security error. We retry the drawing with crossOrigin set to Anonymous.
  287. taintedHandler = function () {
  288. img = new win.Image();
  289. taintedHandler = taintedCallback;
  290. // Must be set prior to loading image source
  291. img.crossOrigin = 'Anonymous';
  292. img.onload = loadHandler;
  293. img.onerror = errorHandler;
  294. img.src = imageURL;
  295. };
  296. img.onload = loadHandler;
  297. img.onerror = errorHandler;
  298. img.src = imageURL;
  299. }
  300. /* eslint-enable valid-jsdoc */
  301. /**
  302. * Get data URL to an image of an SVG and call download on it options object:
  303. *
  304. * - **filename:** Name of resulting downloaded file without extension. Default
  305. * is `chart`.
  306. *
  307. * - **type:** File type of resulting download. Default is `image/png`.
  308. *
  309. * - **scale:** Scaling factor of downloaded image compared to source. Default
  310. * is `1`.
  311. *
  312. * - **libURL:** URL pointing to location of dependency scripts to download on
  313. * demand. Default is the exporting.libURL option of the global Highcharts
  314. * options pointing to our server.
  315. *
  316. * @function Highcharts.downloadSVGLocal
  317. *
  318. * @param {string} svg
  319. * The generated SVG
  320. *
  321. * @param {Highcharts.ExportingOptions} options
  322. * The exporting options
  323. *
  324. * @param {Function} failCallback
  325. * The callback function in case of errors
  326. *
  327. * @param {Function} [successCallback]
  328. * The callback function in case of success
  329. *
  330. * @return {void}
  331. */
  332. function downloadSVGLocal(svg, options, failCallback, successCallback) {
  333. var svgurl, blob, objectURLRevoke = true, finallyHandler, libURL = (options.libURL || getOptions().exporting.libURL), dummySVGContainer = doc.createElement('div'), imageType = options.type || 'image/png', filename = ((options.filename || 'chart') +
  334. '.' +
  335. (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1])), scale = options.scale || 1;
  336. // Allow libURL to end with or without fordward slash
  337. libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL;
  338. /* eslint-disable valid-jsdoc */
  339. /**
  340. * @private
  341. */
  342. function svgToPdf(svgElement, margin) {
  343. var width = svgElement.width.baseVal.value + 2 * margin,
  344. height = svgElement.height.baseVal.value + 2 * margin,
  345. pdf = new win.jsPDF(// eslint-disable-line new-cap
  346. height > width ? 'p' : 'l', // setting orientation to portrait if height exceeds width
  347. 'pt',
  348. [width,
  349. height]);
  350. // Workaround for #7090, hidden elements were drawn anyway. It comes
  351. // down to https://github.com/yWorks/svg2pdf.js/issues/28. Check this
  352. // later.
  353. [].forEach.call(svgElement.querySelectorAll('*[visibility="hidden"]'), function (node) {
  354. node.parentNode.removeChild(node);
  355. });
  356. // Workaround for #13948, multiple stops in linear gradient set to 0
  357. // causing error in Acrobat
  358. var gradients = svgElement.querySelectorAll('linearGradient');
  359. for (var index = 0; index < gradients.length; index++) {
  360. var gradient = gradients[index];
  361. var stops = gradient.querySelectorAll('stop');
  362. var i = 0;
  363. while (i < stops.length &&
  364. stops[i].getAttribute('offset') === '0' &&
  365. stops[i + 1].getAttribute('offset') === '0') {
  366. stops[i].remove();
  367. i++;
  368. }
  369. }
  370. // Workaround for #15135, zero width spaces, which Highcharts uses to
  371. // break lines, are not correctly rendered in PDF. Replace it with a
  372. // regular space and offset by some pixels to compensate.
  373. [].forEach.call(svgElement.querySelectorAll('tspan'), function (tspan) {
  374. if (tspan.textContent === '\u200B') {
  375. tspan.textContent = ' ';
  376. tspan.setAttribute('dx', -5);
  377. }
  378. });
  379. win.svg2pdf(svgElement, pdf, { removeInvalid: true });
  380. return pdf.output('datauristring');
  381. }
  382. /**
  383. * @private
  384. * @return {void}
  385. */
  386. function downloadPDF() {
  387. dummySVGContainer.innerHTML = svg;
  388. var textElements = dummySVGContainer.getElementsByTagName('text'),
  389. titleElements,
  390. svgData,
  391. // Copy style property to element from parents if it's not there.
  392. // Searches up hierarchy until it finds prop, or hits the chart
  393. // container.
  394. setStylePropertyFromParents = function (el,
  395. propName) {
  396. var curParent = el;
  397. while (curParent && curParent !== dummySVGContainer) {
  398. if (curParent.style[propName]) {
  399. el.style[propName] =
  400. curParent.style[propName];
  401. break;
  402. }
  403. curParent = curParent.parentNode;
  404. }
  405. };
  406. // Workaround for the text styling. Making sure it does pick up settings
  407. // for parent elements.
  408. [].forEach.call(textElements, function (el) {
  409. // Workaround for the text styling. making sure it does pick up the
  410. // root element
  411. ['font-family', 'font-size'].forEach(function (property) {
  412. setStylePropertyFromParents(el, property);
  413. });
  414. el.style['font-family'] = (el.style['font-family'] &&
  415. el.style['font-family'].split(' ').splice(-1));
  416. // Workaround for plotband with width, removing title from text
  417. // nodes
  418. titleElements = el.getElementsByTagName('title');
  419. [].forEach.call(titleElements, function (titleElement) {
  420. el.removeChild(titleElement);
  421. });
  422. });
  423. svgData = svgToPdf(dummySVGContainer.firstChild, 0);
  424. try {
  425. downloadURL(svgData, filename);
  426. if (successCallback) {
  427. successCallback();
  428. }
  429. }
  430. catch (e) {
  431. failCallback(e);
  432. }
  433. }
  434. /* eslint-enable valid-jsdoc */
  435. // Initiate download depending on file type
  436. if (imageType === 'image/svg+xml') {
  437. // SVG download. In this case, we want to use Microsoft specific Blob if
  438. // available
  439. try {
  440. if (typeof win.navigator.msSaveOrOpenBlob !== 'undefined') {
  441. blob = new MSBlobBuilder();
  442. blob.append(svg);
  443. svgurl = blob.getBlob('image/svg+xml');
  444. }
  445. else {
  446. svgurl = svgToDataUrl(svg);
  447. }
  448. downloadURL(svgurl, filename);
  449. if (successCallback) {
  450. successCallback();
  451. }
  452. }
  453. catch (e) {
  454. failCallback(e);
  455. }
  456. }
  457. else if (imageType === 'application/pdf') {
  458. if (win.jsPDF && win.svg2pdf) {
  459. downloadPDF();
  460. }
  461. else {
  462. // Must load pdf libraries first. // Don't destroy the object URL
  463. // yet since we are doing things asynchronously. A cleaner solution
  464. // would be nice, but this will do for now.
  465. objectURLRevoke = true;
  466. getScript(libURL + 'jspdf.js', function () {
  467. getScript(libURL + 'svg2pdf.js', function () {
  468. downloadPDF();
  469. });
  470. });
  471. }
  472. }
  473. else {
  474. // PNG/JPEG download - create bitmap from SVG
  475. svgurl = svgToDataUrl(svg);
  476. finallyHandler = function () {
  477. try {
  478. domurl.revokeObjectURL(svgurl);
  479. }
  480. catch (e) {
  481. // Ignore
  482. }
  483. };
  484. // First, try to get PNG by rendering on canvas
  485. imageToDataUrl(svgurl, imageType, {}, scale, function (imageURL) {
  486. // Success
  487. try {
  488. downloadURL(imageURL, filename);
  489. if (successCallback) {
  490. successCallback();
  491. }
  492. }
  493. catch (e) {
  494. failCallback(e);
  495. }
  496. }, function () {
  497. // Failed due to tainted canvas
  498. // Create new and untainted canvas
  499. var canvas = doc.createElement('canvas'), ctx = canvas.getContext('2d'), imageWidth = svg.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale, imageHeight = svg.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale, downloadWithCanVG = function () {
  500. ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
  501. try {
  502. downloadURL(win.navigator.msSaveOrOpenBlob ?
  503. canvas.msToBlob() :
  504. canvas.toDataURL(imageType), filename);
  505. if (successCallback) {
  506. successCallback();
  507. }
  508. }
  509. catch (e) {
  510. failCallback(e);
  511. }
  512. finally {
  513. finallyHandler();
  514. }
  515. };
  516. canvas.width = imageWidth;
  517. canvas.height = imageHeight;
  518. if (win.canvg) {
  519. // Use preloaded canvg
  520. downloadWithCanVG();
  521. }
  522. else {
  523. // Must load canVG first. // Don't destroy the object URL
  524. // yet since we are doing things asynchronously. A cleaner
  525. // solution would be nice, but this will do for now.
  526. objectURLRevoke = true;
  527. // Get RGBColor.js first, then canvg
  528. getScript(libURL + 'rgbcolor.js', function () {
  529. getScript(libURL + 'canvg.js', function () {
  530. downloadWithCanVG();
  531. });
  532. });
  533. }
  534. },
  535. // No canvas support
  536. failCallback,
  537. // Failed to load image
  538. failCallback,
  539. // Finally
  540. function () {
  541. if (objectURLRevoke) {
  542. finallyHandler();
  543. }
  544. });
  545. }
  546. }
  547. /* eslint-disable valid-jsdoc */
  548. /**
  549. * Get SVG of chart prepared for client side export. This converts embedded
  550. * images in the SVG to data URIs. It requires the regular exporting module. The
  551. * options and chartOptions arguments are passed to the getSVGForExport
  552. * function.
  553. *
  554. * @private
  555. * @function Highcharts.Chart#getSVGForLocalExport
  556. * @param {Highcharts.ExportingOptions} options
  557. * @param {Highcharts.Options} chartOptions
  558. * @param {Function} failCallback
  559. * @param {Function} successCallback
  560. * @return {void}
  561. */
  562. Chart.prototype.getSVGForLocalExport = function (options, chartOptions, failCallback, successCallback) {
  563. var chart = this,
  564. images,
  565. imagesEmbedded = 0,
  566. chartCopyContainer,
  567. chartCopyOptions,
  568. el,
  569. i,
  570. l,
  571. href,
  572. // After grabbing the SVG of the chart's copy container we need to do
  573. // sanitation on the SVG
  574. sanitize = function (svg) {
  575. return chart.sanitizeSVG(svg,
  576. chartCopyOptions);
  577. },
  578. // When done with last image we have our SVG
  579. checkDone = function () {
  580. if (imagesEmbedded === images.length) {
  581. successCallback(sanitize(chartCopyContainer.innerHTML));
  582. }
  583. },
  584. // Success handler, we converted image to base64!
  585. embeddedSuccess = function (imageURL, imageType, callbackArgs) {
  586. ++imagesEmbedded;
  587. // Change image href in chart copy
  588. callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
  589. checkDone();
  590. };
  591. // Hook into getSVG to get a copy of the chart copy's container (#8273)
  592. chart.unbindGetSVG = addEvent(chart, 'getSVG', function (e) {
  593. chartCopyOptions = e.chartCopy.options;
  594. chartCopyContainer = e.chartCopy.container.cloneNode(true);
  595. });
  596. // Trigger hook to get chart copy
  597. chart.getSVGForExport(options, chartOptions);
  598. images = chartCopyContainer.getElementsByTagName('image');
  599. try {
  600. // If there are no images to embed, the SVG is okay now.
  601. if (!images.length) {
  602. // Use SVG of chart copy
  603. successCallback(sanitize(chartCopyContainer.innerHTML));
  604. return;
  605. }
  606. // Go through the images we want to embed
  607. for (i = 0, l = images.length; i < l; ++i) {
  608. el = images[i];
  609. href = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
  610. if (href) {
  611. imageToDataUrl(href, 'image/png', { imageElement: el }, options.scale, embeddedSuccess,
  612. // Tainted canvas
  613. failCallback,
  614. // No canvas support
  615. failCallback,
  616. // Failed to load source
  617. failCallback);
  618. // Hidden, boosted series have blank href (#10243)
  619. }
  620. else {
  621. ++imagesEmbedded;
  622. el.parentNode.removeChild(el);
  623. checkDone();
  624. }
  625. }
  626. }
  627. catch (e) {
  628. failCallback(e);
  629. }
  630. // Clean up
  631. chart.unbindGetSVG();
  632. };
  633. /* eslint-enable valid-jsdoc */
  634. /**
  635. * Exporting and offline-exporting modules required. Export a chart to an image
  636. * locally in the user's browser.
  637. *
  638. * @function Highcharts.Chart#exportChartLocal
  639. *
  640. * @param {Highcharts.ExportingOptions} [exportingOptions]
  641. * Exporting options, the same as in
  642. * {@link Highcharts.Chart#exportChart}.
  643. *
  644. * @param {Highcharts.Options} [chartOptions]
  645. * Additional chart options for the exported chart. For example a
  646. * different background color can be added here, or `dataLabels`
  647. * for export only.
  648. *
  649. * @return {void}
  650. *
  651. * @requires modules/exporting
  652. */
  653. Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
  654. var chart = this,
  655. options = merge(chart.options.exporting,
  656. exportingOptions),
  657. fallbackToExportServer = function (err) {
  658. if (options.fallbackToExportServer === false) {
  659. if (options.error) {
  660. options.error(options,
  661. err);
  662. }
  663. else {
  664. error(28, true); // Fallback disabled
  665. }
  666. }
  667. else {
  668. chart.exportChart(options);
  669. }
  670. }, svgSuccess = function (svg) {
  671. // If SVG contains foreignObjects all exports except SVG will fail,
  672. // as both CanVG and svg2pdf choke on this. Gracefully fall back.
  673. if (svg.indexOf('<foreignObject') > -1 &&
  674. options.type !== 'image/svg+xml') {
  675. fallbackToExportServer('Image type not supported' +
  676. 'for charts with embedded HTML');
  677. }
  678. else {
  679. downloadSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer, function () { return fireEvent(chart, 'exportChartLocalSuccess'); });
  680. }
  681. },
  682. // Return true if the SVG contains images with external data. With the
  683. // boost module there are `image` elements with encoded PNGs, these are
  684. // supported by svg2pdf and should pass (#10243).
  685. hasExternalImages = function () {
  686. return [].some.call(chart.container.getElementsByTagName('image'), function (image) {
  687. var href = image.getAttribute('href');
  688. return href !== '' && href.indexOf('data:') !== 0;
  689. });
  690. };
  691. // If we are on IE and in styled mode, add a whitelist to the renderer for
  692. // inline styles that we want to pass through. There are so many styles by
  693. // default in IE that we don't want to blacklist them all.
  694. if (H.isMS && chart.styledMode) {
  695. SVGRenderer.prototype.inlineWhitelist = [
  696. /^blockSize/,
  697. /^border/,
  698. /^caretColor/,
  699. /^color/,
  700. /^columnRule/,
  701. /^columnRuleColor/,
  702. /^cssFloat/,
  703. /^cursor/,
  704. /^fill$/,
  705. /^fillOpacity/,
  706. /^font/,
  707. /^inlineSize/,
  708. /^length/,
  709. /^lineHeight/,
  710. /^opacity/,
  711. /^outline/,
  712. /^parentRule/,
  713. /^rx$/,
  714. /^ry$/,
  715. /^stroke/,
  716. /^textAlign/,
  717. /^textAnchor/,
  718. /^textDecoration/,
  719. /^transform/,
  720. /^vectorEffect/,
  721. /^visibility/,
  722. /^x$/,
  723. /^y$/
  724. ];
  725. }
  726. // Always fall back on:
  727. // - MS browsers: Embedded images JPEG/PNG, or any PDF
  728. // - Embedded images and PDF
  729. if ((H.isMS &&
  730. (options.type === 'application/pdf' ||
  731. chart.container.getElementsByTagName('image').length &&
  732. options.type !== 'image/svg+xml')) || (options.type === 'application/pdf' &&
  733. hasExternalImages())) {
  734. fallbackToExportServer('Image type not supported for this chart/browser.');
  735. return;
  736. }
  737. chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
  738. };
  739. // Extend the default options to use the local exporter logic
  740. merge(true, getOptions().exporting, {
  741. libURL: 'https://code.highcharts.com/9.0.1/lib/',
  742. // When offline-exporting is loaded, redefine the menu item definitions
  743. // related to download.
  744. menuItemDefinitions: {
  745. downloadPNG: {
  746. textKey: 'downloadPNG',
  747. onclick: function () {
  748. this.exportChartLocal();
  749. }
  750. },
  751. downloadJPEG: {
  752. textKey: 'downloadJPEG',
  753. onclick: function () {
  754. this.exportChartLocal({
  755. type: 'image/jpeg'
  756. });
  757. }
  758. },
  759. downloadSVG: {
  760. textKey: 'downloadSVG',
  761. onclick: function () {
  762. this.exportChartLocal({
  763. type: 'image/svg+xml'
  764. });
  765. }
  766. },
  767. downloadPDF: {
  768. textKey: 'downloadPDF',
  769. onclick: function () {
  770. this.exportChartLocal({
  771. type: 'application/pdf'
  772. });
  773. }
  774. }
  775. }
  776. });
  777. // Compatibility
  778. H.downloadSVGLocal = downloadSVGLocal;
  779. });
  780. _registerModule(_modules, 'masters/modules/offline-exporting.src.js', [], function () {
  781. });
  782. }));