DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Export Custom Markup

The DevExpress.viz.exportFromMarkup(markup, options) method allows you to export SVG content, including SVG-based DevExtreme components, to an image or document.

The markup parameter accepts valid SVG markup (see the prepareMarkup() function in the code). To get the markup of a component, call its svg() method. The options parameter accepts an object whose fields configure export properties. In this demo, we specify the width, height, format, and svgToCanvas fields. Refer to the method description for more information on these and other available fields.

Backend API
import React, { useCallback, useRef } from 'react'; import { Chart, CommonSeriesSettings, Series, Legend, Title, Subtitle, } from 'devextreme-react/chart'; import { Button } from 'devextreme-react/button'; import { exportFromMarkup } from 'devextreme/viz/export'; import toCanvas from 'canvg'; import { dataSource } from './data.ts'; import Form from './Form.tsx'; const barPadding = 0.3; function prepareMarkup(chartSvg, markup) { return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="820px" height="420px">' + `${markup}<g transform="translate(305,12)">${chartSvg}</g></svg>`; } function App() { const childRef = useRef(null); const chartRef = useRef(null); const onClick = useCallback(() => { exportFromMarkup( prepareMarkup(chartRef.current.instance.svg(), childRef.current.innerHTML), { width: 820, height: 420, margin: 0, format: 'png', svgToCanvas(svg: Node, canvas) { return new Promise((resolve) => { toCanvas(canvas, new XMLSerializer().serializeToString(svg), { ignoreDimensions: true, ignoreClear: true, renderCallback: resolve, }); }); }, }, ); }, []); return ( <div id="chart-demo"> <div className="chart_environment"> <Form ref={childRef} /> <Chart ref={chartRef} id="chart" dataSource={dataSource} palette="Violet" > <CommonSeriesSettings barPadding={barPadding} argumentField="state" type="bar" /> <Series valueField="year1990" name="1990" /> <Series valueField="year2000" name="2000" /> <Series valueField="year2010" name="2010" /> <Series valueField="year2016" name="2016" /> <Series valueField="year2017" name="2017" /> <Legend verticalAlignment="bottom" horizontalAlignment="center" /> <Title text="Oil Production"> <Subtitle text="(in millions tonnes)" /> </Title> </Chart> </div> <div className="controls-pane"> <Button id="export" width={145} icon="export" text="Export" type="default" onClick={onClick} /> </div> </div> ); } export default App;
import React, { useCallback, useRef } from 'react'; import { Chart, CommonSeriesSettings, Series, Legend, Title, Subtitle, } from 'devextreme-react/chart'; import { Button } from 'devextreme-react/button'; import { exportFromMarkup } from 'devextreme/viz/export'; import toCanvas from 'canvg'; import { dataSource } from './data.js'; import Form from './Form.js'; const barPadding = 0.3; function prepareMarkup(chartSvg, markup) { return ( '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="820px" height="420px">' + `${markup}<g transform="translate(305,12)">${chartSvg}</g></svg>` ); } function App() { const childRef = useRef(null); const chartRef = useRef(null); const onClick = useCallback(() => { exportFromMarkup(prepareMarkup(chartRef.current.instance.svg(), childRef.current.innerHTML), { width: 820, height: 420, margin: 0, format: 'png', svgToCanvas(svg, canvas) { return new Promise((resolve) => { toCanvas(canvas, new XMLSerializer().serializeToString(svg), { ignoreDimensions: true, ignoreClear: true, renderCallback: resolve, }); }); }, }); }, []); return ( <div id="chart-demo"> <div className="chart_environment"> <Form ref={childRef} /> <Chart ref={chartRef} id="chart" dataSource={dataSource} palette="Violet" > <CommonSeriesSettings barPadding={barPadding} argumentField="state" type="bar" /> <Series valueField="year1990" name="1990" /> <Series valueField="year2000" name="2000" /> <Series valueField="year2010" name="2010" /> <Series valueField="year2016" name="2016" /> <Series valueField="year2017" name="2017" /> <Legend verticalAlignment="bottom" horizontalAlignment="center" /> <Title text="Oil Production"> <Subtitle text="(in millions tonnes)" /> </Title> </Chart> </div> <div className="controls-pane"> <Button id="export" width={145} icon="export" text="Export" type="default" onClick={onClick} /> </div> </div> ); } export default App;
import React from 'react'; const fontFamily = "'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana, sans-serif"; const Form = React.forwardRef<HTMLDivElement, {}>((_, ref) => ( <div id="custom_markup_container" ref={ref}> <svg width="820px" height="420px"> <path d="M 13 407 L 128 407 L 232 39 L 13 39" fill="#6D39C3"></path> <path d="M 46 381 L 161 381 L 265 13 L 46 13" opacity="0.5" fill="#6D39C3"></path> <text transform="translate(30,89)" style={{ fill: '#FFFFFF', fontFamily, fontSize: 36, fontWeight: 'bold', }}> <tspan x="0" y="0">Export </tspan> <tspan x="0" y="38">Custom</tspan> <tspan x="0" y="76">Markup</tspan> </text> <text transform="translate(32,199)" style={{ opacity: 0.8, fill: '#FFFFFF', fontFamily, fontSize: 14, }}> <tspan x="0" y="0">Export a chart with</tspan> <tspan x="0" y="19.2">custom elements</tspan> </text> <path opacity="0.8" d="M 0 0 L 820 0 L 820 420 L 0 420 L 0 0" stroke="#999999" strokeWidth="1" strokeLinecap="butt" fill="none" strokeLinejoin="miter"></path> </svg> </div> )); Form.displayName = 'Form'; export default Form;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render( <App />, document.getElementById('app'), );
export const dataSource = [{ state: 'Saudi Arabia', year1990: 319.6, year2000: 408.4, year2010: 407, year2016: 523, year2017: 496.6, }, { state: 'USA', year1990: 362.8, year2000: 287.9, year2010: 270.2, year2016: 438, year2017: 461.4, }, { state: 'China', year1990: 138.3, year2000: 163, year2010: 203, year2016: 199.7, year2017: 191.5, }];
window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, jsx: 'react', }, meta: { 'react': { 'esModule': true, }, 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types@15.8.1/prop-types.js', 'stackblur-canvas': 'npm:stackblur-canvas@2.6.0/dist/stackblur.min.js', 'rgbcolor': 'npm:rgbcolor@1.0.1/index.js', 'canvg': 'npm:canvg@2.0.0/dist/browser/canvg.min.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme-react': 'npm:devextreme-react@23.2.5/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: '', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);
import React from 'react'; const fontFamily = "'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana, sans-serif"; const Form = React.forwardRef((_, ref) => ( <div id="custom_markup_container" ref={ref} > <svg width="820px" height="420px" > <path d="M 13 407 L 128 407 L 232 39 L 13 39" fill="#6D39C3" ></path> <path d="M 46 381 L 161 381 L 265 13 L 46 13" opacity="0.5" fill="#6D39C3" ></path> <text transform="translate(30,89)" style={{ fill: '#FFFFFF', fontFamily, fontSize: 36, fontWeight: 'bold', }} > <tspan x="0" y="0" > Export{' '} </tspan> <tspan x="0" y="38" > Custom </tspan> <tspan x="0" y="76" > Markup </tspan> </text> <text transform="translate(32,199)" style={{ opacity: 0.8, fill: '#FFFFFF', fontFamily, fontSize: 14, }} > <tspan x="0" y="0" > Export a chart with </tspan> <tspan x="0" y="19.2" > custom elements </tspan> </text> <path opacity="0.8" d="M 0 0 L 820 0 L 820 420 L 0 420 L 0 0" stroke="#999999" strokeWidth="1" strokeLinecap="butt" fill="none" strokeLinejoin="miter" ></path> </svg> </div> )); Form.displayName = 'Form'; export default Form;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
export const dataSource = [ { state: 'Saudi Arabia', year1990: 319.6, year2000: 408.4, year2010: 407, year2016: 523, year2017: 496.6, }, { state: 'USA', year1990: 362.8, year2000: 287.9, year2010: 270.2, year2016: 438, year2017: 461.4, }, { state: 'China', year1990: 138.3, year2000: 163, year2010: 203, year2016: 199.7, year2017: 191.5, }, ];
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#chart-demo { height: 460px; } #chart { position: absolute; top: 12px; right: 35px; width: 480px; height: 347px; } .chart_environment { width: 820px; position: relative; margin: 0 auto; } .controls-pane { text-align: center; }