UNPKG

highcharts

Version:
733 lines (700 loc) 27.8 kB
/** * @license Highcharts JS v12.3.0 (2025-06-21) * @module highcharts/modules/offline-exporting * @requires highcharts * @requires highcharts/modules/exporting * * Client side exporting module * * (c) 2015-2025 Torstein Honsi / Oystein Moseng * * License: www.highcharts.com/license */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(root["_Highcharts"], root["_Highcharts"]["AST"], root["_Highcharts"]["Chart"]); else if(typeof define === 'function' && define.amd) define("highcharts/modules/offline-exporting", ["highcharts/highcharts"], function (amd1) {return factory(amd1,amd1["AST"],amd1["Chart"]);}); else if(typeof exports === 'object') exports["highcharts/modules/offline-exporting"] = factory(root["_Highcharts"], root["_Highcharts"]["AST"], root["_Highcharts"]["Chart"]); else root["Highcharts"] = factory(root["Highcharts"], root["Highcharts"]["AST"], root["Highcharts"]["Chart"]); })(typeof window === 'undefined' ? this : window, (__WEBPACK_EXTERNAL_MODULE__944__, __WEBPACK_EXTERNAL_MODULE__660__, __WEBPACK_EXTERNAL_MODULE__960__) => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 660: /***/ ((module) => { module.exports = __WEBPACK_EXTERNAL_MODULE__660__; /***/ }), /***/ 944: /***/ ((module) => { module.exports = __WEBPACK_EXTERNAL_MODULE__944__; /***/ }), /***/ 960: /***/ ((module) => { module.exports = __WEBPACK_EXTERNAL_MODULE__960__; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = (module) => { /******/ var getter = module && module.__esModule ? /******/ () => (module['default']) : /******/ () => (module); /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // EXPORTS __webpack_require__.d(__webpack_exports__, { "default": () => (/* binding */ offline_exporting_src) }); // EXTERNAL MODULE: external {"amd":["highcharts/highcharts"],"commonjs":["highcharts"],"commonjs2":["highcharts"],"root":["Highcharts"]} var highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_ = __webpack_require__(944); var highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default = /*#__PURE__*/__webpack_require__.n(highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_); ;// ./code/es-modules/Extensions/DownloadURL.js /* * * * (c) 2015-2025 Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Mixin for downloading content in the browser * * */ /* * * * Imports * * */ const { isSafari, win, win: { document: doc } } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); const { error } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); /* * * * Constants * * */ const domurl = win.URL || win.webkitURL || win; /* * * * Functions * * */ /** * Convert base64 dataURL to Blob if supported, otherwise returns undefined. * * @private * @function Highcharts.dataURLtoBlob * * @param {string} dataURL * URL to convert. * * @return {string | undefined} * Blob. */ function dataURLtoBlob(dataURL) { const parts = dataURL .replace(/filename=.*;/, '') .match(/data:([^;]*)(;base64)?,([A-Z+\d\/]+)/i); if (parts && parts.length > 3 && (win.atob) && win.ArrayBuffer && win.Uint8Array && win.Blob && (domurl.createObjectURL)) { // Try to convert data URL to Blob const binStr = win.atob(parts[3]), buf = new win.ArrayBuffer(binStr.length), binary = new win.Uint8Array(buf); for (let i = 0; i < binary.length; ++i) { binary[i] = binStr.charCodeAt(i); } return domurl .createObjectURL(new win.Blob([binary], { 'type': parts[1] })); } } /** * Download a data URL in the browser. Can also take a blob as first param. * * @private * @function Highcharts.downloadURL * * @param {string | global.URL} dataURL * The dataURL/Blob to download. * @param {string} filename * The name of the resulting file (w/extension). */ function downloadURL(dataURL, filename) { const nav = win.navigator, a = doc.createElement('a'); // IE specific blob implementation // Don't use for normal dataURLs if (typeof dataURL !== 'string' && !(dataURL instanceof String) && nav.msSaveOrOpenBlob) { nav.msSaveOrOpenBlob(dataURL, filename); return; } dataURL = '' + dataURL; if (nav.userAgent.length > 1000 /* RegexLimits.shortLimit */) { throw new Error('Input too long'); } const // Some browsers have limitations for data URL lengths. Try to convert // to Blob or fall back. Edge always needs that blob. isOldEdgeBrowser = /Edge\/\d+/.test(nav.userAgent), // Safari on iOS needs Blob in order to download PDF safariBlob = (isSafari && typeof dataURL === 'string' && dataURL.indexOf('data:application/pdf') === 0); if (safariBlob || isOldEdgeBrowser || dataURL.length > 2000000) { dataURL = dataURLtoBlob(dataURL) || ''; if (!dataURL) { throw new Error('Failed to convert to blob'); } } // Try HTML5 download attr if supported if (typeof a.download !== 'undefined') { a.href = dataURL; a.download = filename; // HTML5 download attribute doc.body.appendChild(a); a.click(); doc.body.removeChild(a); } else { // No download attr, just opening data URI try { if (!win.open(dataURL, 'chart')) { throw new Error('Failed to open window'); } } catch { // If window.open failed, try location.href win.location.href = dataURL; } } } /** * Asynchronously downloads a script from a provided location. * * @private * @function Highcharts.getScript * * @param {string} scriptLocation * The location for the script to fetch. */ function getScript(scriptLocation) { return new Promise((resolve, reject) => { const head = doc.getElementsByTagName('head')[0], script = doc.createElement('script'); // Set type and location for the script script.type = 'text/javascript'; script.src = scriptLocation; // Resolve in case of a succesful script fetching script.onload = () => { resolve(); }; // Reject in case of fail script.onerror = () => { reject(error(`Error loading script ${scriptLocation}`)); }; // Append the newly created script head.appendChild(script); }); } /* * * * Default Export * * */ const DownloadURL = { dataURLtoBlob, downloadURL, getScript }; /* harmony default export */ const Extensions_DownloadURL = (DownloadURL); // EXTERNAL MODULE: external {"amd":["highcharts/highcharts","AST"],"commonjs":["highcharts","AST"],"commonjs2":["highcharts","AST"],"root":["Highcharts","AST"]} var highcharts_AST_commonjs_highcharts_AST_commonjs2_highcharts_AST_root_Highcharts_AST_ = __webpack_require__(660); var highcharts_AST_commonjs_highcharts_AST_commonjs2_highcharts_AST_root_Highcharts_AST_default = /*#__PURE__*/__webpack_require__.n(highcharts_AST_commonjs_highcharts_AST_commonjs2_highcharts_AST_root_Highcharts_AST_); // EXTERNAL MODULE: external {"amd":["highcharts/highcharts","Chart"],"commonjs":["highcharts","Chart"],"commonjs2":["highcharts","Chart"],"root":["Highcharts","Chart"]} var highcharts_Chart_commonjs_highcharts_Chart_commonjs2_highcharts_Chart_root_Highcharts_Chart_ = __webpack_require__(960); var highcharts_Chart_commonjs_highcharts_Chart_commonjs2_highcharts_Chart_root_Highcharts_Chart_default = /*#__PURE__*/__webpack_require__.n(highcharts_Chart_commonjs_highcharts_Chart_commonjs2_highcharts_Chart_root_Highcharts_Chart_); ;// ./code/es-modules/Extensions/OfflineExporting/OfflineExportingDefaults.js /* * * * (c) 2010-2025 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ /* * * * API Options * * */ /** * @optionparent exporting * @private */ const exporting = {}; /* * * * Default Export * * */ const OfflineExportingDefaults = { exporting }; /* harmony default export */ const OfflineExporting_OfflineExportingDefaults = (OfflineExportingDefaults); ;// ./code/es-modules/Extensions/OfflineExporting/OfflineExporting.js /* * * * Client side exporting module * * (c) 2015 Torstein Honsi / Oystein Moseng * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ const { getOptions, setOptions } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); const { downloadURL: OfflineExporting_downloadURL, getScript: OfflineExporting_getScript } = Extensions_DownloadURL; const { composed, doc: OfflineExporting_doc, win: OfflineExporting_win } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); const { addEvent, extend, pushUnique } = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); /* * * * Composition * * */ var OfflineExporting; (function (OfflineExporting) { /* * * * Functions * * */ /** * Composition function. * * @private * @function compose * * @param {ExportingClass} ExportingClass * Exporting class. * * @requires modules/exporting * @requires modules/offline-exporting */ function compose(ExportingClass) { // Add the downloadSVG event to the Exporting class for local PDF export addEvent(ExportingClass, 'downloadSVG', async function (e) { const { svg, exportingOptions, exporting, preventDefault } = e; // Check if PDF export is requested if (exportingOptions?.type === 'application/pdf') { // Prevent the default export behavior preventDefault?.(); // Run the PDF local export try { // Get the final image options const { type, filename, scale, libURL } = highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default().Exporting.prepareImageOptions(exportingOptions); // Local PDF download if (type === 'application/pdf') { // Must load pdf libraries first if not found. Don't // destroy the object URL yet since we are doing // things asynchronously if (!OfflineExporting_win.jspdf?.jsPDF) { // Get jspdf await OfflineExporting_getScript(`${libURL}jspdf.js`); // Get svg2pdf await OfflineExporting_getScript(`${libURL}svg2pdf.js`); } // Call the PDF download if SVG element found await downloadPDF(svg, scale, filename, exportingOptions?.pdfFont); } } catch (error) { // Try to fallback to the server await exporting?.fallbackToServer(exportingOptions, error); } } }); // Check the composition registry for the OfflineExporting if (!pushUnique(composed, 'OfflineExporting')) { return; } // Adding wrappers for the deprecated functions extend((highcharts_Chart_commonjs_highcharts_Chart_commonjs2_highcharts_Chart_root_Highcharts_Chart_default()).prototype, { exportChartLocal: async function (exportingOptions, chartOptions) { await this.exporting?.exportChart(exportingOptions, chartOptions); return; } }); // Update with defaults of the offline exporting module setOptions(OfflineExporting_OfflineExportingDefaults); // Additionaly, extend the menuItems with the offline exporting variants const menuItems = getOptions().exporting?.buttons?.contextButton?.menuItems; menuItems && menuItems.push('downloadPDF'); } OfflineExporting.compose = compose; /** * Get data URL to an image of an SVG and call download on it options * object: * - **filename:** Name of resulting downloaded file without extension. * Default is `chart`. * * - **type:** File type of resulting download. Default is `image/png`. * * - **scale:** Scaling factor of downloaded image compared to source. * Default is `1`. * - **libURL:** URL pointing to location of dependency scripts to download * on demand. Default is the exporting.libURL option of the global * Highcharts options pointing to our server. * * @function Highcharts.downloadSVGLocal * @deprecated * * @param {string} svg * The generated SVG * * @param {Highcharts.ExportingOptions} options * The exporting options * */ async function downloadSVGLocal(svg, options) { await highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default().Exporting.prototype.downloadSVG.call(void 0, svg, options); } OfflineExporting.downloadSVGLocal = downloadSVGLocal; /** * Converts an SVG string into a PDF file and triggers its download. This * function processes the SVG, applies necessary font adjustments, converts * it to a PDF, and initiates the file download. * * @private * @async * @function downloadPDF * * @param {string} svg * A string representation of the SVG markup to be converted into a PDF. * @param {number} scale * The scaling factor for the PDF output. * @param {string} filename * The name of the downloaded PDF file. * @param {Highcharts.PdfFontOptions} [pdfFont] * An optional object specifying URLs for different font variants (normal, * bold, italic, bolditalic). * * @return {Promise<void>} * A promise that resolves when the PDF has been successfully generated and * downloaded. * * @requires modules/exporting * @requires modules/offline-exporting */ async function downloadPDF(svg, scale, filename, pdfFont) { const svgNode = preparePDF(svg, pdfFont); if (svgNode) { // Loads all required fonts await loadPdfFonts(svgNode, pdfFont); // Transform SVG to PDF const pdfData = await svgToPdf(svgNode, 0, scale); // Download the PDF OfflineExporting_downloadURL(pdfData, filename); } } /** * Loads and registers custom fonts for PDF export if non-ASCII characters * are detected in the given SVG element. This function ensures that text * content with special characters is properly rendered in the exported PDF. * * It fetches font files (if provided in `pdfFont`), converts them to * base64, and registers them with jsPDF. * * @private * @function loadPdfFonts * * @param {SVGElement} svgElement * The generated SVG element containing the text content to be exported. * @param {Highcharts.PdfFontOptions} [pdfFont] * An optional object specifying URLs for different font variants (normal, * bold, italic, bolditalic). If non-ASCII characters are not detected, * fonts are not loaded. * * @requires modules/exporting * @requires modules/offline-exporting */ async function loadPdfFonts(svgElement, pdfFont) { const hasNonASCII = (s) => ( // eslint-disable-next-line no-control-regex /[^\u0000-\u007F\u200B]+/.test(s)); // Register an event in order to add the font once jsPDF is initialized const addFont = (variant, base64) => { OfflineExporting_win.jspdf.jsPDF.API.events.push([ 'initialized', function () { this.addFileToVFS(variant, base64); this.addFont(variant, 'HighchartsFont', variant); if (!this.getFontList()?.HighchartsFont) { this.setFont('HighchartsFont'); } } ]); }; // If there are no non-ASCII characters in the SVG, do not use bother // downloading the font files if (pdfFont && !hasNonASCII(svgElement.textContent || '')) { pdfFont = void 0; } // Add new font if the URL is declared, #6417 const variants = ['normal', 'italic', 'bold', 'bolditalic']; // Shift the first element off the variants and add as a font. // Then asynchronously trigger the next variant until variants are empty let normalBase64; for (const variant of variants) { const url = pdfFont?.[variant]; if (url) { try { const response = await OfflineExporting_win.fetch(url); if (!response.ok) { throw new Error(`Failed to fetch font: ${url}`); } const blob = await response.blob(), reader = new FileReader(); const base64 = await new Promise((resolve, reject) => { reader.onloadend = () => { if (typeof reader.result === 'string') { resolve(reader.result.split(',')[1]); } else { reject(new Error('Failed to read font as base64')); } }; reader.onerror = reject; reader.readAsDataURL(blob); }); addFont(variant, base64); if (variant === 'normal') { normalBase64 = base64; } } catch (e) { // If fetch or reading fails, fallback to next variant } } else { // For other variants, fall back to normal text weight/style if (normalBase64) { addFont(variant, normalBase64); } } } } /** * Prepares an SVG for PDF export by ensuring proper text styling and * removing unnecessary elements. This function extracts an SVG element from * a given SVG string, applies font styles inherited from parent elements, * and removes text outlines and title elements to improve PDF rendering. * * @private * @function preparePDF * * @param {string} svg * A string representation of the SVG markup. * @param {Highcharts.PdfFontOptions} [pdfFont] * An optional object specifying URLs for different font variants (normal, * bold, italic, bolditalic). If provided, the text elements are assigned a * custom PDF font. * * @return {SVGSVGElement | null} * Returns the parsed SVG element from the container or `null` if the SVG is * not found. * * @requires modules/exporting * @requires modules/offline-exporting */ function preparePDF(svg, pdfFont) { const dummySVGContainer = OfflineExporting_doc.createElement('div'); highcharts_AST_commonjs_highcharts_AST_commonjs2_highcharts_AST_root_Highcharts_AST_default().setElementHTML(dummySVGContainer, svg); const textElements = dummySVGContainer.getElementsByTagName('text'), // Copy style property to element from parents if it's not there. // Searches up hierarchy until it finds prop, or hits the chart // container setStylePropertyFromParents = function (el, propName) { let curParent = el; while (curParent && curParent !== dummySVGContainer) { if (curParent.style[propName]) { let value = curParent.style[propName]; if (propName === 'fontSize' && /em$/.test(value)) { value = Math.round(parseFloat(value) * 16) + 'px'; } el.style[propName] = value; break; } curParent = curParent.parentNode; } }; let titleElements, outlineElements; // Workaround for the text styling. Making sure it does pick up // settings for parent elements. [].forEach.call(textElements, function (el) { // Workaround for the text styling. making sure it does pick up // the root element ['fontFamily', 'fontSize'] .forEach((property) => { setStylePropertyFromParents(el, property); }); el.style.fontFamily = pdfFont?.normal ? // Custom PDF font 'HighchartsFont' : // Generic font (serif, sans-serif etc) String(el.style.fontFamily && el.style.fontFamily.split(' ').splice(-1)); // Workaround for plotband with width, removing title from text // nodes titleElements = el.getElementsByTagName('title'); [].forEach.call(titleElements, function (titleElement) { el.removeChild(titleElement); }); // Remove all .highcharts-text-outline elements, #17170 outlineElements = el.getElementsByClassName('highcharts-text-outline'); while (outlineElements.length > 0) { const outline = outlineElements[0]; if (outline.parentNode) { outline.parentNode.removeChild(outline); } } }); return dummySVGContainer.querySelector('svg'); } /** * Transform from PDF to SVG. * * @async * @private * @function svgToPdf * * @param {Highcharts.SVGElement} svgElement * The SVG element to convert. * @param {number} margin * The margin to apply. * @param {number} scale * The scale of the SVG. * * @requires modules/exporting * @requires modules/offline-exporting */ async function svgToPdf(svgElement, margin, scale) { const width = (Number(svgElement.getAttribute('width')) + 2 * margin) * scale, height = (Number(svgElement.getAttribute('height')) + 2 * margin) * scale, pdfDoc = new OfflineExporting_win.jspdf.jsPDF(// eslint-disable-line new-cap // Setting orientation to portrait if height exceeds width height > width ? 'p' : 'l', 'pt', [width, height]); // Workaround for #7090, hidden elements were drawn anyway. It comes // down to https://github.com/yWorks/svg2pdf.js/issues/28. Check this // later. [].forEach.call(svgElement.querySelectorAll('*[visibility="hidden"]'), function (node) { node.parentNode.removeChild(node); }); // Workaround for #13948, multiple stops in linear gradient set to 0 // causing error in Acrobat const gradients = svgElement.querySelectorAll('linearGradient'); for (let index = 0; index < gradients.length; index++) { const gradient = gradients[index]; const stops = gradient.querySelectorAll('stop'); let i = 0; while (i < stops.length && stops[i].getAttribute('offset') === '0' && stops[i + 1].getAttribute('offset') === '0') { stops[i].remove(); i++; } } // Workaround for #15135, zero width spaces, which Highcharts uses // to break lines, are not correctly rendered in PDF. Replace it // with a regular space and offset by some pixels to compensate. [].forEach.call(svgElement.querySelectorAll('tspan'), (tspan) => { if (tspan.textContent === '\u200B') { tspan.textContent = ' '; tspan.setAttribute('dx', -5); } }); // Transform from PDF to SVG await pdfDoc.svg(svgElement, { x: 0, y: 0, width, height, removeInvalid: true }); // Return the output return pdfDoc.output('datauristring'); } })(OfflineExporting || (OfflineExporting = {})); /* * * * Default Export * * */ /* harmony default export */ const OfflineExporting_OfflineExporting = (OfflineExporting); ;// ./code/es-modules/masters/modules/offline-exporting.src.js const G = (highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default()); // Compatibility G.dataURLtoBlob = G.dataURLtoBlob || Extensions_DownloadURL.dataURLtoBlob; G.downloadSVGLocal = OfflineExporting_OfflineExporting.downloadSVGLocal; G.downloadURL = G.downloadURL || Extensions_DownloadURL.downloadURL; // Compose OfflineExporting_OfflineExporting.compose(G.Exporting); /* harmony default export */ const offline_exporting_src = ((highcharts_commonjs_highcharts_commonjs2_highcharts_root_Highcharts_default())); __webpack_exports__ = __webpack_exports__["default"]; /******/ return __webpack_exports__; /******/ })() ; });