Skip to content

Commit 72a6fb6

Browse files
author
KB Bot
committed
Added new kb article extracting-point-data-from-signature-signaturepad-dotnet-maui
1 parent e132702 commit 72a6fb6

File tree

1 file changed

+197
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)