Skip to content

Commit e618fc7

Browse files
committed
CanvasGeometry path parse error now more descriptive in the Sample App.
1 parent 7d756c5 commit e618fc7

File tree

7 files changed

+160
-34
lines changed

7 files changed

+160
-34
lines changed

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,14 @@
8585
HorizontalAlignment="Left"
8686
Click="{x:Bind OnClearCanvas}"
8787
Content="Clear" />
88-
<Button Grid.Row="3"
89-
Width="120"
90-
Height="36"
91-
Margin="10,3"
92-
HorizontalAlignment="Right"
93-
Click="{x:Bind OnParseData}"
94-
Content="Parse" />
88+
<TextBlock x:Name="ParseError"
89+
Grid.Row="3"
90+
Margin="150,3,10,3"
91+
HorizontalAlignment="Stretch"
92+
VerticalAlignment="Center"
93+
FontSize="16"
94+
Foreground="Red"
95+
TextAlignment="Left" />
9596
<Pivot x:Name="RootPivot"
9697
Grid.Row="4">
9798
<PivotItem Foreground="Black"

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
using System.Collections.Generic;
77
using System.Linq;
88
using System.Text;
9-
using Windows.System;
109
using Microsoft.Graphics.Canvas.Geometry;
1110
using Microsoft.Graphics.Canvas.UI.Xaml;
11+
using Microsoft.Toolkit.Uwp.UI.Extensions;
1212
using Microsoft.Toolkit.Uwp.UI.Media.Geometry;
13+
using Windows.System;
1314
using Windows.UI;
1415
using Windows.UI.Xaml;
1516
using Windows.UI.Xaml.Controls;
1617
using Windows.UI.Xaml.Controls.Primitives;
17-
using Microsoft.Toolkit.Uwp.UI.Extensions;
1818

1919
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
2020
{
@@ -78,6 +78,10 @@ public sealed partial class CanvasPathGeometryPage : Page
7878
"135.021,11.529 135.313,10.749 135.898,10.158 C 136.482,9.567 137.210,9.272 138.080,9.272 C 138.938,9.272 139.662,9.570 140.253,10.167 C 140.844,10.764 141.139,11.516 141.139,12.424 " +
7979
"C 141.139,12.611 141.108,13.021 141.046,13.655 Z";
8080

81+
private const string ParseError1 = "Parameter \"(pathData matches.Count == 0)\" must be false, was true: ";
82+
private const string ParseError2 = "Parameter \"(pathData matches.Count > 1)\" must be false, was true: ";
83+
private const string ParseError3 = "PATH_ERR003";
84+
8185
private DispatcherQueueTimer _typeTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
8286

8387
private List<Color> _colors;
@@ -158,7 +162,7 @@ public CanvasPathGeometryPage()
158162
_selectionChanged = false;
159163
}
160164

161-
private void OnParseData(object sender, RoutedEventArgs e)
165+
private void ParseData()
162166
{
163167
_data = InputData.Text;
164168
_isParsing = true;
@@ -170,6 +174,7 @@ private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
170174
{
171175
if (string.IsNullOrWhiteSpace(_data))
172176
{
177+
ParseError.Text = string.Empty;
173178
CommandsList.Text = string.Empty;
174179
return;
175180
}
@@ -178,6 +183,7 @@ private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
178183

179184
_logger?.Clear();
180185
CommandsList.Text = string.Empty;
186+
ParseError.Text = string.Empty;
181187

182188
try
183189
{
@@ -192,9 +198,38 @@ private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
192198
args.DrawingSession.FillGeometry(geometry, _fillColor);
193199
args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
194200
}
201+
catch (ArgumentException argEx)
202+
{
203+
var message = argEx.Message;
204+
var errorCode = message.Substring(0, 11);
205+
if (message.StartsWith(ParseError1))
206+
{
207+
ParseError.Text = "Parse Error: No matching data!";
208+
}
209+
else if (message.StartsWith(ParseError2))
210+
{
211+
ParseError.Text = "Parse Error: Multiple FillRule elements present in Path Data!";
212+
}
213+
else if (message.StartsWith(ParseError3))
214+
{
215+
var tokens = message.Split('\n', StringSplitOptions.RemoveEmptyEntries);
216+
if (tokens.Length == 3)
217+
{
218+
ParseError.Text = $"Parse Error at {tokens[1]}. Cannot parse '{tokens[2]}'.";
219+
}
220+
}
221+
else
222+
{
223+
ParseError.Text = "Parsing error! Invalid input data!";
224+
}
225+
226+
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
227+
CommandsList.Text = ParseError.Text;
228+
}
195229
catch (Exception)
196230
{
197231
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
232+
ParseError.Text = "Parsing error! Invalid input data!";
198233
CommandsList.Text = "Parsing error! Invalid input data!";
199234
}
200235
}
@@ -247,7 +282,7 @@ private void ShowSample(int index)
247282
private void OnClearCanvas(object sender, RoutedEventArgs e)
248283
{
249284
InputData.Text = string.Empty;
250-
OnParseData(this, null);
285+
ParseData();
251286
}
252287

253288
private void OnShowRoundedStarSample(object sender, RoutedEventArgs e)
@@ -277,12 +312,8 @@ private void OnShowGearSample(object sender, RoutedEventArgs e)
277312

278313
public void OnInputTextChanged(object sender, RoutedEventArgs e)
279314
{
280-
_typeTimer.Debounce(
281-
() =>
282-
{
283-
// Only executes this code after 0.3 seconds have elapsed since last trigger.
284-
this.OnParseData(null, null);
285-
}, TimeSpan.FromSeconds(0.3));
315+
// Call the ParseData method only after 0.3 seconds have elapsed since last trigger.
316+
_typeTimer.Debounce(ParseData, TimeSpan.FromSeconds(0.3));
286317
}
287318
}
288319
}

Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasBrushParser.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
56
using Microsoft.Graphics.Canvas;
67
using Microsoft.Graphics.Canvas.Brushes;
78
using Microsoft.Toolkit.Diagnostics;
@@ -25,10 +26,10 @@ internal static ICanvasBrushElement Parse(string brushData)
2526
var matches = RegexFactory.CanvasBrushRegex.Matches(brushData);
2627

2728
// If no match is found or no captures in the match, then it means that the brush data is invalid.
28-
Guard.IsFalse(matches.Count == 0, "(brushData matches.Count == 0)", $"Invalid Brush data! No matching brush data found!\nBrush Data: {brushData}");
29+
Guard.IsFalse(matches.Count == 0, "(brushData matches.Count == 0)", $"BRUSH_ERR001:Invalid Brush data! No matching brush data found!\nBrush Data: {brushData}");
2930

3031
// If the match contains more than one captures, it means that there are multiple brushes present in the brush data. There should be only one brush defined in the brush data.
31-
Guard.IsFalse(matches.Count > 1, "(brushData matches.Count > 1)", "Multiple Brushes defined in Brush Data! " +
32+
Guard.IsFalse(matches.Count > 1, "(brushData matches.Count > 1)", "BRUSH_ERR002:Multiple Brushes defined in Brush Data! " +
3233
"There should be only one Brush definition within the Brush Data. " +
3334
"You can either remove Brush definitions or split the Brush Data " +
3435
"into multiple Brush Data and call the CanvasPathGeometry.CreateBrush() method on each of them." +
@@ -66,7 +67,26 @@ internal static ICanvasBrushElement Parse(string brushData)
6667
// Perform validation to check if there are any invalid characters in the brush data that were not captured
6768
var preValidationCount = RegexFactory.ValidationRegex.Replace(brushData, string.Empty).Length;
6869
var postValidationCount = brushElement.ValidationCount;
69-
Guard.IsTrue(preValidationCount == postValidationCount, nameof(brushData), $"Brush data contains invalid characters!\nBrush Data: {brushData}");
70+
71+
// If there are invalid characters, extract them and add them to the ArgumentException message
72+
if (preValidationCount != postValidationCount)
73+
{
74+
var parseIndex = 0;
75+
if (!string.IsNullOrWhiteSpace(brushElement.Data))
76+
{
77+
parseIndex = brushData.IndexOf(brushElement.Data, parseIndex, StringComparison.Ordinal);
78+
}
79+
80+
var errorString = brushData.Substring(parseIndex);
81+
if (errorString.Length > 30)
82+
{
83+
errorString = $"{errorString.Substring(0, 30)}...";
84+
}
85+
86+
errorString = $"BRUSH_ERR003:Brush data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
87+
88+
ThrowHelper.ThrowArgumentException(errorString);
89+
}
7090

7191
return brushElement;
7292
}

Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasGeometryParser.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ internal static CanvasGeometry Parse(ICanvasResourceCreator resourceCreator, str
3333
var matches = RegexFactory.CanvasGeometryRegex.Matches(pathData);
3434

3535
// If no match is found or no captures in the match, then it means // that the path data is invalid.
36-
Guard.IsFalse(matches.Count == 0, "(pathData matches.Count == 0)", $"Invalid Path data! No matching path data found!\nPath Data: {pathData}");
36+
Guard.IsFalse(matches.Count == 0, "(pathData matches.Count == 0)", $"PATH_ERR000:Invalid Path data! No matching path data found!\nPath Data: {pathData}");
3737

3838
// If the match contains more than one captures, it means that there are multiple FillRuleElements present in the path data. There can be only one FillRuleElement in the path data (at the beginning).
39-
Guard.IsFalse(matches.Count > 1, "(pathData matches.Count > 1)", "Multiple FillRule elements present in Path Data! " +
39+
Guard.IsFalse(matches.Count > 1, "(pathData matches.Count > 1)", "PATH_ERR001:Multiple FillRule elements present in Path Data!\n" +
4040
"There should be only one FillRule within the Path Data. " +
4141
"You can either remove additional FillRule elements or split the Path Data " +
4242
"into multiple Path Data and call the CanvasPathGeometry.CreateGeometry() method on each of them." +
@@ -80,16 +80,33 @@ internal static CanvasGeometry Parse(ICanvasResourceCreator resourceCreator, str
8080
pathFigures.Insert(0, PathElementFactory.CreateDefaultPathElement(PathFigureType.FillRule));
8181
}
8282
}
83+
else
84+
{
85+
return null;
86+
}
8387

8488
// Perform validation to check if there are any invalid characters in the path data that were not captured
8589
var preValidationCount = RegexFactory.ValidationRegex.Replace(pathData, string.Empty).Length;
8690
var postValidationCount = pathFigures.Sum(x => x.ValidationCount);
8791

88-
Guard.IsTrue(preValidationCount == postValidationCount, nameof(pathData), $"Path data contains invalid characters!\nPath Data: {pathData}");
89-
90-
if (pathFigures.Count == 0)
92+
// If there are invalid characters, extract them and add them to the ArgumentException message
93+
if (preValidationCount != postValidationCount)
9194
{
92-
return null;
95+
var parseIndex = 0;
96+
foreach (var figure in pathFigures)
97+
{
98+
parseIndex = pathData.IndexOf(figure.Data, parseIndex, StringComparison.Ordinal) + figure.Data.Length;
99+
}
100+
101+
var errorString = pathData.Substring(parseIndex);
102+
if (errorString.Length > 30)
103+
{
104+
errorString = $"{errorString.Substring(0, 30)}...";
105+
}
106+
107+
errorString = $"PATH_ERR003:Path data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
108+
109+
ThrowHelper.ThrowArgumentException(errorString);
93110
}
94111

95112
ICanvasPathElement lastElement = null;

Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeParser.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
56
using Microsoft.Graphics.Canvas;
67
using Microsoft.Toolkit.Diagnostics;
78
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
@@ -25,12 +26,12 @@ internal static ICanvasStrokeElement Parse(string strokeData)
2526

2627
// If no match is found or no captures in the match, then it means
2728
// that the stroke data is invalid.
28-
Guard.IsFalse(matches.Count == 0, "(strokeData matches.Count == 0)", $"Invalid Stroke data! No matching CanvasStroke found!\nStroke Data: {strokeData}");
29+
Guard.IsFalse(matches.Count == 0, "(strokeData matches.Count == 0)", $"STROKE_ERR001:Invalid Stroke data! No matching CanvasStroke found!\nStroke Data: {strokeData}");
2930

3031
// If the match contains more than one captures, it means that there
3132
// are multiple CanvasStrokes present in the stroke data. There should
3233
// be only one CanvasStroke defined in the stroke data.
33-
Guard.IsFalse(matches.Count > 1, "(strokeData matches.Count > 1)", "Multiple CanvasStrokes defined in Stroke Data! " +
34+
Guard.IsFalse(matches.Count > 1, "(strokeData matches.Count > 1)", "STROKE_ERR002:Multiple CanvasStrokes defined in Stroke Data! " +
3435
"There should be only one CanvasStroke definition within the Stroke Data. " +
3536
"You can either remove CanvasStroke definitions or split the Stroke Data " +
3637
"into multiple Stroke Data and call the CanvasPathGeometry.CreateStroke() method on each of them." +
@@ -44,7 +45,25 @@ internal static ICanvasStrokeElement Parse(string strokeData)
4445
var preValidationCount = RegexFactory.ValidationRegex.Replace(strokeData, string.Empty).Length;
4546
var postValidationCount = strokeElement.ValidationCount;
4647

47-
Guard.IsTrue(preValidationCount == postValidationCount, nameof(strokeData), $"Stroke data contains invalid characters!\nStroke Data: {strokeData}");
48+
// If there are invalid characters, extract them and add them to the ArgumentException message
49+
if (preValidationCount != postValidationCount)
50+
{
51+
var parseIndex = 0;
52+
if (!string.IsNullOrWhiteSpace(strokeElement.Data))
53+
{
54+
parseIndex = strokeData.IndexOf(strokeElement.Data, parseIndex, StringComparison.Ordinal);
55+
}
56+
57+
var errorString = strokeData.Substring(parseIndex);
58+
if (errorString.Length > 30)
59+
{
60+
errorString = $"{errorString.Substring(0, 30)}...";
61+
}
62+
63+
errorString = $"STROKE_ERR003:Stroke data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
64+
65+
ThrowHelper.ThrowArgumentException(errorString);
66+
}
4867

4968
return strokeElement;
5069
}

Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeStyleParser.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
56
using System.Text.RegularExpressions;
67
using Microsoft.Graphics.Canvas.Geometry;
78
using Microsoft.Toolkit.Diagnostics;
@@ -25,12 +26,12 @@ internal static CanvasStrokeStyle Parse(string styleData)
2526
var matches = RegexFactory.CanvasStrokeStyleRegex.Matches(styleData);
2627

2728
// If no match is found or no captures in the match, then it means that the style data is invalid.
28-
Guard.IsFalse(matches.Count == 0, "(styleData matches.Count == 0)", $"Invalid CanvasStrokeStyle data! No matching CanvasStrokeStyle found!\nCanvasStrokeStyle Data: {styleData}");
29+
Guard.IsFalse(matches.Count == 0, "(styleData matches.Count == 0)", $"STYLE_ERR001:Invalid CanvasStrokeStyle data! No matching CanvasStrokeStyle found!\nCanvasStrokeStyle Data: {styleData}");
2930

3031
// If the match contains more than one captures, it means that there
3132
// are multiple CanvasStrokeStyles present in the CanvasStrokeStyle data. There should
3233
// be only one CanvasStrokeStyle defined in the CanvasStrokeStyle data.
33-
Guard.IsFalse(matches.Count > 1, "(styleData matches.Count > 1)", "Multiple CanvasStrokeStyles defined in CanvasStrokeStyle Data! " +
34+
Guard.IsFalse(matches.Count > 1, "(styleData matches.Count > 1)", "STYLE_ERR002:Multiple CanvasStrokeStyles defined in CanvasStrokeStyle Data! " +
3435
"There should be only one CanvasStrokeStyle definition within the CanvasStrokeStyle Data. " +
3536
"You can either remove CanvasStrokeStyle definitions or split the CanvasStrokeStyle Data " +
3637
"into multiple CanvasStrokeStyle Data and call the CanvasPathGeometry.CreateStrokeStyle() method on each of them." +
@@ -44,7 +45,25 @@ internal static CanvasStrokeStyle Parse(string styleData)
4445
var preValidationCount = RegexFactory.ValidationRegex.Replace(styleData, string.Empty).Length;
4546
var postValidationCount = styleElement.ValidationCount;
4647

47-
Guard.IsTrue(preValidationCount == postValidationCount, nameof(styleData), $"CanvasStrokeStyle data contains invalid characters!\nCanvasStrokeStyle Data: {styleData}");
48+
// If there are invalid characters, extract them and add them to the ArgumentException message
49+
if (preValidationCount != postValidationCount)
50+
{
51+
var parseIndex = 0;
52+
if (!string.IsNullOrWhiteSpace(styleElement.Data))
53+
{
54+
parseIndex = styleData.IndexOf(styleElement.Data, parseIndex, StringComparison.Ordinal);
55+
}
56+
57+
var errorString = styleData.Substring(parseIndex);
58+
if (errorString.Length > 30)
59+
{
60+
errorString = $"{errorString.Substring(0, 30)}...";
61+
}
62+
63+
errorString = $"STYLE_ERR003:Style data contains invalid characters!\nIndex: {parseIndex}\n{errorString}";
64+
65+
ThrowHelper.ThrowArgumentException(errorString);
66+
}
4867

4968
return styleElement.Style;
5069
}

0 commit comments

Comments
 (0)