Skip to content

Commit 73933f6

Browse files
Merge pull request #1274 from telerik/new-kb-extracting-point-data-from-signature-signaturepad-dotnet-maui-3e416bccf9a54ab0afa71960b2a51ca9
Added new kb article extracting-point-data-from-signature-signaturepad-dotnet-maui
2 parents cdca4af + eb644f8 commit 73933f6

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
title: Extracting Point Data from Signature in SignaturePad for UI for .NET MAUI
3+
description: Learn how to extract point and line data from a signature in the SignaturePad component for UI for .NET MAUI.
4+
type: how-to
5+
meta_title: How to Extract Point Data from SignaturePad in UI for .NET MAUI
6+
slug: extracting-point-data-from-signature-signaturepad-dotnet-maui
7+
tags: signaturepad, ui-for-dotnet-maui, point-data, signature, vector-data
8+
res_type: kb
9+
ticketid: 1600847
10+
---
11+
12+
## Environment
13+
14+
<table>
15+
<tbody>
16+
<tr>
17+
<td>Product</td>
18+
<td>UI for .NET MAUI SignaturePad</td>
19+
</tr>
20+
<tr>
21+
<td>Version</td>
22+
<td>Current</td>
23+
</tr>
24+
</tbody>
25+
</table>
26+
27+
## Description
28+
29+
I want to extract point and line data from the signature created using the [SignaturePad](https://docs.telerik.com/devtools/maui/controls/signaturepad/overview) control of UI for .NET MAUI. The goal is to format the signature data as a string that represents the x, y coordinates of the strokes in the signature. Different formats for the output data are required, such as a custom format or an SVG-like format.
30+
31+
This knowledge base article also answers the following questions:
32+
- How to save signature data as points and strokes from SignaturePad UI for .NET MAUI?
33+
- How to format SignaturePad data into custom or SVG-like string formats?
34+
- How to process and scan SignaturePad image to extract point coordinates?
35+
36+
## Solution
37+
38+
To achieve this, process the signature image generated by the SignaturePad component and extract vector data representing the point coordinates. Follow these steps:
39+
40+
### 1. Save the Signature as an Image
41+
42+
Save the signature as a PNG image using the `SaveImageAsync` method:
43+
44+
```csharp
45+
using var stream = new MemoryStream();
46+
47+
await SigPad1.SaveImageAsync(stream, new SaveImageSettings
48+
{
49+
ImageFormat = Telerik.Maui.Controls.SignaturePad.ImageFormat.Png,
50+
BackgroundColor = Colors.AliceBlue,
51+
StrokeColor = Colors.DarkBlue,
52+
StrokeThickness = 2
53+
});
54+
55+
var imageBytes = stream.ToArray();
56+
GetVectorFromSignatureImage(imageBytes);
57+
```
58+
59+
### 2. Process the Image to Extract Vector Data
60+
61+
Create a method to process the image and extract vector data. This involves three stages:
62+
63+
1. Prepare the Bitmaps.
64+
1. Decode the image bytes into `SKBitmap`.
65+
2. Create a grayscale bitmap for better contrast.
66+
3. Convert the grayscale bitmap into a binary bitmap (black and white) using a threshold.
67+
68+
2. Scan the Bitmap and create point groups.
69+
1. Scan the binary bitmap for black pixels.
70+
2. Group neighboring black pixels into strokes using an algorithm.
71+
3. Store the grouped strokes as `List<List<SKPoint>>`.
72+
73+
3. Serialize the grouped strokes into your preferred format:
74+
- Custom: `"1,2;3,4;5,6/7,8;9,10;11,12"`
75+
- SVG-like: `"M x y L x y L x y ..."`
76+
77+
Below is the complete implementation:
78+
79+
```csharp
80+
public static string GetVectorFromSignatureImage(byte[] imageBytes, string formatType = "custom")
81+
{
82+
using var bitmap = SKBitmap.Decode(imageBytes);
83+
var grayscaleBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
84+
var binaryBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
85+
86+
// Convert to grayscale
87+
for (var y = 0; y < bitmap.Height; y++)
88+
{
89+
for (var x = 0; x < bitmap.Width; x++)
90+
{
91+
var color = bitmap.GetPixel(x, y);
92+
var gray = (byte)(0.3 * color.Red + 0.59 * color.Green + 0.11 * color.Blue);
93+
grayscaleBitmap.SetPixel(x, y, new SKColor(gray, gray, gray));
94+
}
95+
}
96+
97+
// Convert to binary (black/white)
98+
for (var y = 0; y < grayscaleBitmap.Height; y++)
99+
{
100+
for (var x = 0; x < grayscaleBitmap.Width; x++)
101+
{
102+
var color = grayscaleBitmap.GetPixel(x, y);
103+
var value = color.Red < 128 ? SKColors.Black : SKColors.White;
104+
binaryBitmap.SetPixel(x, y, value);
105+
}
106+
}
107+
108+
var pxScanned = new bool[binaryBitmap.Width, binaryBitmap.Height];
109+
var groupedStrokes = new List<List<SKPoint>>();
110+
111+
int[] dx = { -1, 0, 1, 0, -1, -1, 1, 1 };
112+
int[] dy = { 0, -1, 0, 1, -1, 1, -1, 1 };
113+
114+
for (var y = 0; y < binaryBitmap.Height; y++)
115+
{
116+
for (var x = 0; x < binaryBitmap.Width; x++)
117+
{
118+
if (pxScanned[x, y] || binaryBitmap.GetPixel(x, y) != SKColors.Black)
119+
continue;
120+
121+
pxScanned[x, y] = true;
122+
123+
var queue = new Queue<SKPoint>();
124+
queue.Enqueue(new SKPoint(x, y));
125+
126+
var stroke = new List<SKPoint>();
127+
128+
while (queue.Count > 0)
129+
{
130+
var point = queue.Dequeue();
131+
stroke.Add(point);
132+
133+
for (var i = 0; i < dx.Length; i++)
134+
{
135+
var nx = (int)point.X + dx[i];
136+
var ny = (int)point.Y + dy[i];
137+
138+
if (nx >= 0 && ny >= 0 && nx < binaryBitmap.Width && ny < binaryBitmap.Height &&
139+
!pxScanned[nx, ny] && binaryBitmap.GetPixel(nx, ny) == SKColors.Black)
140+
{
141+
pxScanned[nx, ny] = true;
142+
queue.Enqueue(new SKPoint(nx, ny));
143+
}
144+
}
145+
}
146+
147+
if (stroke.Count > 0)
148+
groupedStrokes.Add(stroke);
149+
}
150+
}
151+
152+
var sb = new System.Text.StringBuilder();
153+
var ic = System.Globalization.CultureInfo.InvariantCulture;
154+
var fm = "N1";
155+
156+
switch (formatType.ToLower())
157+
{
158+
case "custom":
159+
for (var i = 0; i < groupedStrokes.Count; i++)
160+
{
161+
for (var j = 0; j < groupedStrokes[i].Count; j++)
162+
{
163+
sb.Append(groupedStrokes[i][j].X.ToString(fm, ic));
164+
sb.Append(",");
165+
sb.Append(groupedStrokes[i][j].Y.ToString(fm, ic));
166+
if (j < groupedStrokes[i].Count - 1) sb.Append(";");
167+
}
168+
if (i < groupedStrokes.Count - 1) sb.Append("/");
169+
}
170+
break;
171+
172+
case "svg":
173+
for (var gs = 0; gs < groupedStrokes.Count; gs++)
174+
{
175+
sb.Append(gs == 0 ? "M " : " L ");
176+
for (var p = 0; p < groupedStrokes[gs].Count; p++)
177+
{
178+
sb.Append(groupedStrokes[gs][p].X.ToString(fm, ic));
179+
sb.Append(" ");
180+
sb.Append(groupedStrokes[gs][p].Y.ToString(fm, ic));
181+
}
182+
}
183+
break;
184+
}
185+
186+
return sb.ToString();
187+
}
188+
```
189+
190+
## See Also
191+
192+
- [SignaturePad Overview](https://docs.telerik.com/devtools/maui/controls/signaturepad/overview)
193+
- [SaveImageSettings Documentation](https://docs.telerik.com/devtools/maui/api/telerik.maui.controls.signaturepad.saveimagesettings)
194+
- [SKBitmap Documentation](https://docs.microsoft.com/en-us/dotnet/api/skiasharp.skbitmap?view=skiasharp-2.80)

0 commit comments

Comments
 (0)