10
10
using System . Linq ;
11
11
using System . Reflection ;
12
12
using System . Drawing . Drawing2D ;
13
+ using System . Threading . Tasks ;
14
+ using System . Threading ;
13
15
14
16
namespace SvgFileTypePlugin
15
17
{
@@ -56,15 +58,9 @@ public static Document Get(Stream input)
56
58
using ( var wrapper = new SvgStreamWrapper ( input ) )
57
59
doc = SvgDocument . Open < SvgDocument > ( wrapper . SvgStream ) ;
58
60
59
- bool keepAspectRatio ;
60
- int resolution ;
61
- int canvasw ;
62
- int canvash ;
63
61
var vpw = 0 ;
64
62
var vph = 0 ;
65
63
var ppi = doc . Ppi ;
66
-
67
- var layersMode = LayersMode . All ;
68
64
if ( ! doc . Width . IsNone && ! doc . Width . IsEmpty )
69
65
{
70
66
vpw = ConvertToPixels ( doc . Width . Type , doc . Width . Value , doc . Ppi ) ;
@@ -79,130 +75,175 @@ public static Document Get(Stream input)
79
75
var vby = ( int ) doc . ViewBox . MinY ;
80
76
var vbw = ( int ) doc . ViewBox . Width ;
81
77
var vbh = ( int ) doc . ViewBox . Height ;
82
-
78
+
83
79
// Store opacity as layer options.
84
80
var setOpacityForLayer = true ;
85
81
var importHiddenLayers = true ;
86
82
var importGroupBoundariesAsLayers = false ;
87
- DialogResult dr = DialogResult . Cancel ;
83
+ var dr = DialogResult . Cancel ;
84
+ Document results = null ;
88
85
using ( var dialog = new UiDialog ( ) )
89
86
{
90
- Form mainForm = GetMainForm ( ) ;
91
- if ( mainForm != null )
92
- {
93
- mainForm . Invoke ( ( MethodInvoker ) ( ( ) =>
94
- {
95
- dialog . SetSvgInfo ( vpw , vph , vbx , vby , vbw , vbh , ppi ) ;
96
- dr = dialog . ShowDialog ( mainForm ) ;
97
- } ) ) ;
98
- }
99
- else
87
+ var tokenSource = new CancellationTokenSource ( ) ;
88
+ var token = tokenSource . Token ;
89
+
90
+ dialog . FormClosing += ( o , e ) =>
100
91
{
101
- dialog . SetSvgInfo ( vpw , vph , vbx , vby , vbw , vbh , ppi ) ;
102
- dr = dialog . ShowDialog ( ) ;
103
- }
104
- if ( dr != DialogResult . OK )
105
- throw new OperationCanceledException ( "Cancelled by user" ) ;
106
- canvasw = dialog . CanvasW ;
107
- canvash = dialog . CanvasH ;
108
- resolution = dialog . Dpi ;
109
- layersMode = dialog . LayerMode ;
110
- keepAspectRatio = dialog . KeepAspectRatio ;
111
- setOpacityForLayer = dialog . ImportOpacity ;
112
- importHiddenLayers = dialog . ImportHiddenLayers ;
113
- importGroupBoundariesAsLayers = dialog . ImportGroupBoundariesAsLayers ;
114
- }
92
+ tokenSource . Cancel ( ) ;
93
+ } ;
115
94
116
- doc . Ppi = resolution ;
117
- doc . Width = new SvgUnit ( SvgUnitType . Pixel , canvasw ) ;
118
- doc . Height = new SvgUnit ( SvgUnitType . Pixel , canvash ) ;
119
- doc . AspectRatio = keepAspectRatio
120
- ? new SvgAspectRatio ( SvgPreserveAspectRatio . xMinYMin )
121
- : new SvgAspectRatio ( SvgPreserveAspectRatio . none ) ;
95
+ dialog . OkClick += ( o , e ) =>
96
+ {
97
+ var canvasw = dialog . CanvasW ;
98
+ var canvash = dialog . CanvasH ;
99
+ var resolution = dialog . Dpi ;
100
+ var layersMode = dialog . LayerMode ;
101
+ var keepAspectRatio = dialog . KeepAspectRatio ;
102
+ setOpacityForLayer = dialog . ImportOpacity ;
103
+ importHiddenLayers = dialog . ImportHiddenLayers ;
104
+ importGroupBoundariesAsLayers = dialog . ImportGroupBoundariesAsLayers ;
105
+
106
+ doc . Ppi = resolution ;
107
+ doc . Width = new SvgUnit ( SvgUnitType . Pixel , canvasw ) ;
108
+ doc . Height = new SvgUnit ( SvgUnitType . Pixel , canvash ) ;
109
+ doc . AspectRatio = keepAspectRatio
110
+ ? new SvgAspectRatio ( SvgPreserveAspectRatio . xMinYMin )
111
+ : new SvgAspectRatio ( SvgPreserveAspectRatio . none ) ;
112
+
113
+ var progressCallback = new System . Action < int > ( p => dialog . ReportProgress ( p ) ) ;
114
+ // Run in another thread and unblock the UI.
115
+ // Cannot run .AsParallel().AsOrdered() to render each element in async thread while gdi+ svg renderer failing with errors...
116
+ Task . Run ( ( Action ) ( ( ) =>
117
+ {
118
+ if ( layersMode == LayersMode . Flat )
119
+ {
120
+ // Render one flat image and quit.
121
+ var bmp = RenderImage ( doc , canvasw , canvash ) ;
122
+ results = Document . FromImage ( bmp ) ;
123
+ }
124
+ else
125
+ {
126
+ List < SvgVisualElement > allElements = null ;
122
127
123
128
124
- if ( layersMode == LayersMode . Flat )
125
- {
126
- // Render one flat image and quit.
127
- var bmp = RenderImage ( doc , canvasw , canvash ) ;
128
- return Document . FromImage ( bmp ) ;
129
- }
130
- else
131
- {
132
- var allElements = PrepareFlatElements ( doc . Children ) . Where ( p => p is SvgVisualElement ) . Cast < SvgVisualElement > ( ) . ToList ( ) ;
129
+ allElements = PrepareFlatElements ( doc . Children ) . Where ( p => p is SvgVisualElement ) . Cast < SvgVisualElement > ( ) . ToList ( ) ;
133
130
134
- Document outputDocument = new Document ( canvasw , canvash ) ;
135
- if ( layersMode == LayersMode . All )
136
- {
137
- // Dont render groups and boundaries if defined
138
- allElements = allElements . Where ( p => ! ( p is SvgGroup ) ) . ToList ( ) ;
131
+ Document outputDocument = new Document ( canvasw , canvash ) ;
132
+ if ( layersMode == LayersMode . All )
133
+ {
134
+ // Dont render groups and boundaries if defined
135
+ allElements = allElements . Where ( p => ! ( p is SvgGroup ) ) . ToList ( ) ;
139
136
140
- // Filter out group boundaries if not set.
141
- if ( ! importGroupBoundariesAsLayers )
142
- {
143
- allElements = allElements . Where ( p => ! ( p is PaintGroupBoundaries ) ) . ToList ( ) ;
144
- }
137
+ // Filter out group boundaries if not set.
138
+ if ( ! importGroupBoundariesAsLayers )
139
+ {
140
+ allElements = allElements . Where ( p => ! ( p is PaintGroupBoundaries ) ) . ToList ( ) ;
141
+ }
145
142
146
- RenderElements ( allElements , outputDocument , setOpacityForLayer , importHiddenLayers ) ;
147
- }
148
- else if ( layersMode == LayersMode . Groups )
149
- {
150
- // Get only parent groups and single elements
151
- var groupsAndElementsWithoutGroup = new List < SvgVisualElement > ( ) ;
152
-
153
- foreach ( var element in allElements )
154
- {
155
- if ( element is PaintGroupBoundaries )
156
- continue ;
143
+ // Thread safe
144
+ dialog . SetMaxProgress ( allElements . Count + 10 ) ;
145
+ dialog . ReportProgress ( 10 ) ;
157
146
158
- if ( element . ContainsAttribute ( groupAttribute ) )
159
- {
160
- // Get only root level
161
- SvgGroup lastGroup = null ;
162
- if ( element is SvgGroup )
163
- {
164
- lastGroup = ( SvgGroup ) element ;
147
+ RenderElements ( allElements , outputDocument , setOpacityForLayer , importHiddenLayers , progressCallback , token ) ;
165
148
}
166
-
167
- SvgElement toCheck = element ;
168
- while ( toCheck != null )
149
+ else if ( layersMode == LayersMode . Groups )
169
150
{
170
- toCheck = toCheck . Parent ;
171
- if ( toCheck is SvgGroup )
151
+ // Get only parent groups and single elements
152
+ var groupsAndElementsWithoutGroup = new List < SvgVisualElement > ( ) ;
153
+
154
+ foreach ( var element in allElements )
172
155
{
173
- // TODO: render more groups. In most cases svg has only few root groups.
174
- var groupToCheck = ( SvgGroup ) toCheck ;
175
- var title = GetLayerTitle ( groupToCheck ) ;
156
+ if ( element is PaintGroupBoundaries )
157
+ continue ;
176
158
177
- if ( ! string . IsNullOrEmpty ( title ) )
159
+ if ( element . ContainsAttribute ( groupAttribute ) )
160
+ {
161
+ // Get only root level
162
+ SvgGroup lastGroup = null ;
163
+ if ( element is SvgGroup )
164
+ {
165
+ lastGroup = ( SvgGroup ) element ;
166
+ }
167
+
168
+ SvgElement toCheck = element ;
169
+ while ( toCheck != null )
170
+ {
171
+ toCheck = toCheck . Parent ;
172
+ if ( toCheck is SvgGroup )
173
+ {
174
+ // TODO: render more groups. In most cases svg has only few root groups.
175
+ var groupToCheck = ( SvgGroup ) toCheck ;
176
+ var title = GetLayerTitle ( groupToCheck ) ;
177
+
178
+ if ( ! string . IsNullOrEmpty ( title ) )
179
+ {
180
+ lastGroup = groupToCheck ;
181
+ }
182
+ }
183
+ }
184
+
185
+ if ( ! groupsAndElementsWithoutGroup . Contains ( lastGroup ) )
186
+ {
187
+ groupsAndElementsWithoutGroup . Add ( lastGroup ) ;
188
+ }
189
+ }
190
+ else
178
191
{
179
- lastGroup = groupToCheck ;
192
+ groupsAndElementsWithoutGroup . Add ( element ) ;
180
193
}
181
194
}
195
+
196
+ // Thread safe
197
+ dialog . SetMaxProgress ( groupsAndElementsWithoutGroup . Count + 10 ) ;
198
+ dialog . ReportProgress ( 10 ) ;
199
+
200
+ RenderElements ( groupsAndElementsWithoutGroup , outputDocument , setOpacityForLayer , importHiddenLayers , progressCallback , token ) ;
182
201
}
183
202
184
- if ( ! groupsAndElementsWithoutGroup . Contains ( lastGroup ) )
203
+ // Fallback. Nothing is added. Render one default layer.
204
+ if ( outputDocument . Layers . Count == 0 )
185
205
{
186
- groupsAndElementsWithoutGroup . Add ( lastGroup ) ;
206
+ var bmp = RenderImage ( doc , canvasw , canvash ) ;
207
+ outputDocument = Document . FromImage ( bmp ) ;
187
208
}
209
+
210
+ results = outputDocument ;
211
+ }
212
+
213
+ } ) , token )
214
+ . ContinueWith ( ( p ) =>
215
+ {
216
+ if ( p . Exception != null && ! p . IsCanceled )
217
+ {
218
+ MessageBox . Show ( p . Exception . Message ) ;
188
219
}
189
220
else
190
221
{
191
- groupsAndElementsWithoutGroup . Add ( element ) ;
222
+ if ( dialog . DialogResult == DialogResult . None )
223
+ dialog . DialogResult = DialogResult . OK ;
192
224
}
193
- }
225
+ } ) ;
226
+ } ;
194
227
195
- RenderElements ( groupsAndElementsWithoutGroup , outputDocument , setOpacityForLayer , importHiddenLayers ) ;
228
+ Form mainForm = GetMainForm ( ) ;
229
+ if ( mainForm != null )
230
+ {
231
+ mainForm . Invoke ( ( MethodInvoker ) ( ( ) =>
232
+ {
233
+ dialog . SetSvgInfo ( vpw , vph , vbx , vby , vbw , vbh , ppi ) ;
234
+ dr = dialog . ShowDialog ( mainForm ) ;
235
+ } ) ) ;
196
236
}
197
-
198
- // Fallback. Nothing is added. Render one default layer.
199
- if ( outputDocument . Layers . Count == 0 )
237
+ else
200
238
{
201
- var bmp = RenderImage ( doc , canvasw , canvash ) ;
202
- return Document . FromImage ( bmp ) ;
239
+ dialog . SetSvgInfo ( vpw , vph , vbx , vby , vbw , vbh , ppi ) ;
240
+ dr = dialog . ShowDialog ( ) ;
203
241
}
204
242
205
- return outputDocument ;
243
+ if ( dr != DialogResult . OK )
244
+ throw new OperationCanceledException ( "Cancelled by user" ) ;
245
+
246
+ return results ;
206
247
}
207
248
}
208
249
@@ -238,14 +279,20 @@ private static int ConvertToPixels(SvgUnitType type, float value, float ppi)
238
279
}
239
280
}
240
281
241
- private static void RenderElements ( List < SvgVisualElement > elements , Document outputDocument , bool setOpacityForLayer , bool importHiddenLayers )
282
+ private static void RenderElements ( List < SvgVisualElement > elements , Document outputDocument , bool setOpacityForLayer , bool importHiddenLayers , Action < int > progress , CancellationToken token )
242
283
{
243
284
// I had problems to render each element directly while parent transformation can affect child.
244
285
// But we can do a trick and render full document each time with only required nodes set as visible.
245
286
246
287
// Render all visual elements that are passed here.
288
+ int layer = 0 ;
247
289
foreach ( var element in elements )
248
290
{
291
+ if ( token != null )
292
+ {
293
+ token . ThrowIfCancellationRequested ( ) ;
294
+ }
295
+
249
296
if ( element is PaintGroupBoundaries )
250
297
{
251
298
// Render empty group boundary and continue
@@ -261,7 +308,9 @@ private static void RenderElements( List<SvgVisualElement> elements, Document ou
261
308
}
262
309
263
310
outputDocument . Layers . Add ( pdnLayer ) ;
264
-
311
+ layer ++ ;
312
+ if ( progress != null )
313
+ progress ( layer ) ;
265
314
continue ;
266
315
}
267
316
@@ -296,12 +345,20 @@ private static void RenderElements( List<SvgVisualElement> elements, Document ou
296
345
toCheck = toCheck . Parent ;
297
346
}
298
347
299
- if ( itemShouldBeIgnored )
348
+ if ( itemShouldBeIgnored )
300
349
{
350
+ layer ++ ;
351
+ if ( progress != null )
352
+ progress ( layer ) ;
353
+
301
354
continue ;
302
355
}
303
356
304
357
RenderElement ( element , outputDocument , setOpacityForLayer , importHiddenLayers ) ;
358
+
359
+ layer ++ ;
360
+ if ( progress != null )
361
+ progress ( layer ) ;
305
362
}
306
363
}
307
364
0 commit comments