@@ -20,25 +20,67 @@ class Exports {
20
20
}
21
21
22
22
getSvgString ( ) {
23
- const w = this . w
24
- const width = w . config . chart . toolbar . export . width
25
- let scale =
26
- w . config . chart . toolbar . export . scale || width / w . globals . svgWidth
23
+ return new Promise ( ( resolve ) => {
24
+ const w = this . w
25
+ const width = w . config . chart . toolbar . export . width
26
+ let scale =
27
+ w . config . chart . toolbar . export . scale || width / w . globals . svgWidth
28
+
29
+ if ( ! scale ) {
30
+ scale = 1 // if no scale is specified, don't scale...
31
+ }
32
+ let svgString = this . w . globals . dom . Paper . svg ( )
27
33
28
- if ( ! scale ) {
29
- scale = 1 // if no scale is specified, don't scale...
30
- }
31
- let svgString = this . w . globals . dom . Paper . svg ( )
32
- // in case the scale is different than 1, the svg needs to be rescaled
33
- if ( scale !== 1 ) {
34
34
// clone the svg node so it remains intact in the UI
35
35
const svgNode = this . w . globals . dom . Paper . node . cloneNode ( true )
36
- // scale the image
37
- this . scaleSvgNode ( svgNode , scale )
38
- // get the string representation of the svgNode
39
- svgString = new XMLSerializer ( ) . serializeToString ( svgNode )
40
- }
41
- return svgString . replace ( / & n b s p ; / g, ' ' )
36
+
37
+ // in case the scale is different than 1, the svg needs to be rescaled
38
+
39
+ if ( scale !== 1 ) {
40
+ // scale the image
41
+ this . scaleSvgNode ( svgNode , scale )
42
+ }
43
+ // Convert image URLs to base64
44
+ this . convertImagesToBase64 ( svgNode ) . then ( ( ) => {
45
+ svgString = new XMLSerializer ( ) . serializeToString ( svgNode )
46
+ resolve ( svgString . replace ( / & n b s p ; / g, ' ' ) )
47
+ } )
48
+ } )
49
+ }
50
+
51
+ convertImagesToBase64 ( svgNode ) {
52
+ const images = svgNode . getElementsByTagName ( 'image' )
53
+ const promises = Array . from ( images ) . map ( ( img ) => {
54
+ const href = img . getAttributeNS ( 'http://www.w3.org/1999/xlink' , 'href' )
55
+ if ( href && ! href . startsWith ( 'data:' ) ) {
56
+ return this . getBase64FromUrl ( href )
57
+ . then ( ( base64 ) => {
58
+ img . setAttributeNS ( 'http://www.w3.org/1999/xlink' , 'href' , base64 )
59
+ } )
60
+ . catch ( ( error ) => {
61
+ console . error ( 'Error converting image to base64:' , error )
62
+ } )
63
+ }
64
+ return Promise . resolve ( )
65
+ } )
66
+ return Promise . all ( promises )
67
+ }
68
+
69
+ getBase64FromUrl ( url ) {
70
+ return new Promise ( ( resolve , reject ) => {
71
+ const img = new Image ( )
72
+ img . crossOrigin = 'Anonymous'
73
+ img . onload = ( ) => {
74
+ const canvas = document . createElement ( 'canvas' )
75
+ canvas . width = img . width
76
+ canvas . height = img . height
77
+ const ctx = canvas . getContext ( '2d' )
78
+ ctx . drawImage ( img , 0 , 0 )
79
+ resolve ( canvas . toDataURL ( ) )
80
+ }
81
+ img . onerror = reject
82
+ img . src = url
83
+ } )
42
84
}
43
85
44
86
cleanup ( ) {
@@ -70,11 +112,15 @@ class Exports {
70
112
}
71
113
72
114
svgUrl ( ) {
73
- this . cleanup ( )
74
-
75
- const svgData = this . getSvgString ( )
76
- const svgBlob = new Blob ( [ svgData ] , { type : 'image/svg+xml;charset=utf-8' } )
77
- return URL . createObjectURL ( svgBlob )
115
+ return new Promise ( ( resolve ) => {
116
+ this . cleanup ( )
117
+ this . getSvgString ( ) . then ( ( svgData ) => {
118
+ const svgBlob = new Blob ( [ svgData ] , {
119
+ type : 'image/svg+xml;charset=utf-8' ,
120
+ } )
121
+ resolve ( URL . createObjectURL ( svgBlob ) )
122
+ } )
123
+ } )
78
124
}
79
125
80
126
dataURI ( options ) {
@@ -100,35 +146,37 @@ class Exports {
100
146
ctx . fillStyle = canvasBg
101
147
ctx . fillRect ( 0 , 0 , canvas . width * scale , canvas . height * scale )
102
148
103
- const svgData = this . getSvgString ( )
149
+ this . getSvgString ( ) . then ( ( svgData ) => {
150
+ const svgUrl = 'data:image/svg+xml,' + encodeURIComponent ( svgData )
151
+ let img = new Image ( )
152
+ img . crossOrigin = 'anonymous'
104
153
105
- const svgUrl = 'data:image/svg+xml,' + encodeURIComponent ( svgData )
106
- let img = new Image ( )
107
- img . crossOrigin = 'anonymous'
154
+ img . onload = ( ) => {
155
+ ctx . drawImage ( img , 0 , 0 )
108
156
109
- img . onload = ( ) => {
110
- ctx . drawImage ( img , 0 , 0 )
111
-
112
- if ( canvas . msToBlob ) {
113
- // Microsoft Edge can't navigate to data urls, so we return the blob instead
114
- let blob = canvas . msToBlob ( )
115
- resolve ( { blob } )
116
- } else {
117
- let imgURI = canvas . toDataURL ( 'image/png' )
118
- resolve ( { imgURI } )
157
+ if ( canvas . msToBlob ) {
158
+ // Microsoft Edge can't navigate to data urls, so we return the blob instead
159
+ let blob = canvas . msToBlob ( )
160
+ resolve ( { blob } )
161
+ } else {
162
+ let imgURI = canvas . toDataURL ( 'image/png' )
163
+ resolve ( { imgURI } )
164
+ }
119
165
}
120
- }
121
166
122
- img . src = svgUrl
167
+ img . src = svgUrl
168
+ } )
123
169
} )
124
170
}
125
171
126
172
exportToSVG ( ) {
127
- this . triggerDownload (
128
- this . svgUrl ( ) ,
129
- this . w . config . chart . toolbar . export . svg . filename ,
130
- '.svg'
131
- )
173
+ this . svgUrl ( ) . then ( ( url ) => {
174
+ this . triggerDownload (
175
+ url ,
176
+ this . w . config . chart . toolbar . export . svg . filename ,
177
+ '.svg'
178
+ )
179
+ } )
132
180
}
133
181
134
182
exportToPng ( ) {
0 commit comments