DevExtreme v24.1 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
<div class="chart_environment"> <div id="custom_markup_container"> <svg version="1.1" 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; font-family: 'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana, sans-serif; font-size: 36px; font-weight: 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; font-family: 'Segoe UI', 'Helvetica Neue', 'Trebuchet MS', Verdana, sans-serif; font-size: 14px; " > <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" stroke-width="1" stroke-linecap="butt" fill="none" stroke-linejoin="miter" ></path> </svg> </div> <dx-chart id="chart" [dataSource]="oilProductionData" palette="Violet"> <dxi-series valueField="year1990" name="1990"></dxi-series> <dxi-series valueField="year2000" name="2000"></dxi-series> <dxi-series valueField="year2010" name="2010"></dxi-series> <dxi-series valueField="year2016" name="2016"></dxi-series> <dxi-series valueField="year2017" name="2017"></dxi-series> <dxo-common-series-settings argumentField="state" type="bar" [barPadding]="0.3" > </dxo-common-series-settings> <dxo-legend verticalAlignment="bottom" horizontalAlignment="center"> </dxo-legend> <dxo-title text="Oil Production"> <dxo-subtitle text="(in millions tonnes)"></dxo-subtitle> </dxo-title> </dx-chart> </div> <div class="controls-pane"> <dx-button icon="export" text="Export" type="default" [width]="145" (onClick)="export()" > </dx-button> </div>
import { NgModule, Component, ViewChild, enableProdMode, } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxChartModule, DxChartComponent, DxButtonModule } from 'devextreme-angular'; import { exportFromMarkup } from 'devextreme/viz/export'; // import canvg from 'canvg'; import { Service, OilProductionDataItem } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', providers: [Service], templateUrl: `app/app.component.html`, styleUrls: [`app/app.component.css`], }) export class AppComponent { @ViewChild(DxChartComponent, { static: false }) chart: DxChartComponent; oilProductionData: OilProductionDataItem[]; constructor(service: Service) { this.oilProductionData = service.getOilProductionData(); } export() { exportFromMarkup(this.prepareMarkup(), { width: 820, height: 420, margin: 0, format: 'png', svgToCanvas(svg, canvas) { return new Promise((resolve) => { // canvg(canvas, new XMLSerializer().serializeToString(svg), { // ignoreDimensions: true, // ignoreClear: true, // renderCallback: resolve, // }); }); }, }); } prepareMarkup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); svg.setAttribute('version', '1.1'); svg.setAttribute('width', '820px'); svg.setAttribute('height', '420px'); svg.innerHTML = document.getElementById('custom_markup_container').innerHTML; const group = document.createElementNS('http://www.w3.org/2000/svg', 'g'); group.setAttribute('transform', 'translate(305,12)'); group.innerHTML = this.chart.instance.svg(); svg.appendChild(group); return svg; } } @NgModule({ imports: [ BrowserModule, DxChartModule, DxButtonModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep #chart-demo { height: 460px; } ::ng-deep #chart { position: absolute; top: 12px; right: 35px; width: 480px; height: 347px; } ::ng-deep .chart_environment { width: 820px; position: relative; margin: 0 auto; } ::ng-deep .controls-pane { text-align: center; }
import { Injectable } from '@angular/core'; export class OilProductionDataItem { state: string; year1990: number; year2000: number; year2010: number; year2016: number; year2017: number; } const oilProductionData: OilProductionDataItem[] = [{ 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, }]; @Injectable() export class Service { getOilProductionData(): OilProductionDataItem[] { return oilProductionData; } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ const componentNames = [ 'accordion', 'action-sheet', 'autocomplete', 'bar-gauge', 'box', 'bullet', 'button-group', 'button', 'calendar', 'chart', 'check-box', 'circular-gauge', 'color-box', 'context-menu', 'data-grid', 'date-box', 'date-range-box', 'defer-rendering', 'diagram', 'draggable', 'drawer', 'drop-down-box', 'drop-down-button', 'file-manager', 'file-uploader', 'filter-builder', 'form', 'funnel', 'gallery', 'gantt', 'html-editor', 'linear-gauge', 'list', 'load-indicator', 'load-panel', 'lookup', 'map', 'menu', 'multi-view', 'nested', 'number-box', 'pie-chart', 'pivot-grid-field-chooser', 'pivot-grid', 'polar-chart', 'popover', 'popup', 'progress-bar', 'radio-group', 'range-selector', 'range-slider', 'recurrence-editor', 'resizable', 'responsive-box', 'sankey', 'scheduler', 'scroll-view', 'select-box', 'slider', 'sortable', 'sparkline', 'speed-dial-action', 'splitter', 'switch', 'tab-panel', 'tabs', 'tag-box', 'text-area', 'text-box', 'tile-view', 'toast', 'toolbar', 'tooltip', 'tree-list', 'tree-map', 'tree-view', 'validation-group', 'validation-summary', 'validator', 'vector-map', ]; window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, '@angular/platform-browser-dynamic': { 'esModule': true, }, '@angular/platform-browser': { 'esModule': true, }, '@angular/core': { 'esModule': true, }, '@angular/common': { 'esModule': true, }, '@angular/common/http': { 'esModule': true, }, '@angular/compiler': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', /* @angular */ '@angular/compiler': 'bundles:@angular/compiler.umd.js', '@angular/platform-browser-dynamic': 'bundles:@angular/platform-browser-dynamic.umd.js', '@angular/core': 'bundles:@angular/core.umd.js', '@angular/core/primitives/signals': 'bundles:@angular/core.primitives.signals.umd.js', '@angular/common': 'bundles:@angular/common.umd.js', '@angular/common/http': 'bundles:@angular/common-http.umd.js', '@angular/platform-browser': 'bundles:@angular/platform-browser.umd.js', '@angular/platform-browser/animations': 'bundles:@angular/platform-browser.umd.js', '@angular/forms': 'bundles:@angular/forms.umd.js', /* devextreme */ 'devextreme': 'npm:devextreme@24.1.4/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.4/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.10', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; return acc; }, {}), 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'tslib': 'npm:tslib@2.6.1/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.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', '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', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.13/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.10/package.json', 'npm:devexpress-gantt@4.1.56/package.json', ], }; System.config(window.config); // System.import('@angular/compiler').catch(console.error.bind(console));
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.1.4/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.13.0/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>