Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
---
title: Extracting Point Data from Signature in SignaturePad for UI for .NET MAUI
description: Learn how to extract point and line data from a signature in the SignaturePad component for UI for .NET MAUI.
type: how-to
page_title: How to Extract Point Data from SignaturePad in UI for .NET MAUI
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed.

meta_title: How to Extract Point Data from SignaturePad in UI for .NET MAUI
slug: extracting-point-data-from-signature-signaturepad-dotnet-maui
tags: signaturepad, ui-for-dotnet-maui, point-data, signature, vector-data
res_type: kb
ticketid: 1600847
---

## Environment

<table>
<tbody>
<tr>
<td>Product</td>
<td>UI for .NET MAUI SignaturePad</td>
</tr>
<tr>
<td>Version</td>
<td>Current</td>
</tr>
</tbody>
</table>

## Description

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
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.


This knowledge base article also answers the following questions:
- How to save signature data as points and strokes from SignaturePad UI for .NET MAUI?
- How to format SignaturePad data into custom or SVG-like string formats?
- How to process and scan SignaturePad image to extract point coordinates?

## Solution

To achieve this, process the signature image generated by the SignaturePad component and extract vector data representing the point coordinates. Follow these steps:

### 1. Save the Signature as an Image

Save the signature as a PNG image using the `SaveImageAsync` method:

```csharp
using var stream = new MemoryStream();

await SigPad1.SaveImageAsync(stream, new SaveImageSettings
{
ImageFormat = Telerik.Maui.Controls.SignaturePad.ImageFormat.Png,
BackgroundColor = Colors.AliceBlue,
StrokeColor = Colors.DarkBlue,
StrokeThickness = 2
});

var imageBytes = stream.ToArray();
GetVectorFromSignatureImage(imageBytes);
```

### 2. Process the Image to Extract Vector Data

Create a method to process the image and extract vector data. This involves three stages:

**Stage 1: Prepare Bitmaps**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Stage 1: Prepare Bitmaps**
1. Prepare the Bitmaps.

- Decode the image bytes into `SKBitmap`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Decode the image bytes into `SKBitmap`.
1. Decode the image bytes into `SKBitmap`.

- Create a grayscale bitmap for better contrast.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Create a grayscale bitmap for better contrast.
2. Create a grayscale bitmap for better contrast.

- Convert the grayscale bitmap into a binary bitmap (black and white) using a threshold.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Convert the grayscale bitmap into a binary bitmap (black and white) using a threshold.
3. Convert the grayscale bitmap into a binary bitmap (black and white) using a threshold.


**Stage 2: Scan Bitmap and Create Point Groups**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Stage 2: Scan Bitmap and Create Point Groups**
2. Scan the Bitmap and create point groups.

- Scan the binary bitmap for black pixels.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Scan the binary bitmap for black pixels.
1. Scan the binary bitmap for black pixels.

- Group neighboring black pixels into strokes using an algorithm.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Group neighboring black pixels into strokes using an algorithm.
2. Group neighboring black pixels into strokes using an algorithm.

- Store grouped strokes as `List<List<SKPoint>>`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Store grouped strokes as `List<List<SKPoint>>`.
3. Store the grouped strokes as `List<List<SKPoint>>`.


**Stage 3: Format the Data**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Stage 3: Format the Data**
3. Serialize the grouped strokes into your preferred format:
- Custom: `"1,2;3,4;5,6/7,8;9,10;11,12"`
- SVG-like: `"M x y L x y L x y ..."`

- Serialize the grouped strokes into a desired format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete.

- Supported formats:
- Custom: `"1,2;3,4;5,6/7,8;9,10;11,12"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete.

- SVG-like: `"M x y L x y L x y ..."`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete.


Below is the complete implementation:

```csharp
public static string GetVectorFromSignatureImage(byte[] imageBytes, string formatType = "custom")
{
using var bitmap = SKBitmap.Decode(imageBytes);
var grayscaleBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
var binaryBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

// Convert to grayscale
for (var y = 0; y < bitmap.Height; y++)
{
for (var x = 0; x < bitmap.Width; x++)
{
var color = bitmap.GetPixel(x, y);
var gray = (byte)(0.3 * color.Red + 0.59 * color.Green + 0.11 * color.Blue);
grayscaleBitmap.SetPixel(x, y, new SKColor(gray, gray, gray));
}
}

// Convert to binary (black/white)
for (var y = 0; y < grayscaleBitmap.Height; y++)
{
for (var x = 0; x < grayscaleBitmap.Width; x++)
{
var color = grayscaleBitmap.GetPixel(x, y);
var value = color.Red < 128 ? SKColors.Black : SKColors.White;
binaryBitmap.SetPixel(x, y, value);
}
}

var pxScanned = new bool[binaryBitmap.Width, binaryBitmap.Height];
var groupedStrokes = new List<List<SKPoint>>();

int[] dx = { -1, 0, 1, 0, -1, -1, 1, 1 };
int[] dy = { 0, -1, 0, 1, -1, 1, -1, 1 };

for (var y = 0; y < binaryBitmap.Height; y++)
{
for (var x = 0; x < binaryBitmap.Width; x++)
{
if (pxScanned[x, y] || binaryBitmap.GetPixel(x, y) != SKColors.Black)
continue;

pxScanned[x, y] = true;

var queue = new Queue<SKPoint>();
queue.Enqueue(new SKPoint(x, y));

var stroke = new List<SKPoint>();

while (queue.Count > 0)
{
var point = queue.Dequeue();
stroke.Add(point);

for (var i = 0; i < dx.Length; i++)
{
var nx = (int)point.X + dx[i];
var ny = (int)point.Y + dy[i];

if (nx >= 0 && ny >= 0 && nx < binaryBitmap.Width && ny < binaryBitmap.Height &&
!pxScanned[nx, ny] && binaryBitmap.GetPixel(nx, ny) == SKColors.Black)
{
pxScanned[nx, ny] = true;
queue.Enqueue(new SKPoint(nx, ny));
}
}
}

if (stroke.Count > 0)
groupedStrokes.Add(stroke);
}
}

var sb = new System.Text.StringBuilder();
var ic = System.Globalization.CultureInfo.InvariantCulture;
var fm = "N1";

switch (formatType.ToLower())
{
case "custom":
for (var i = 0; i < groupedStrokes.Count; i++)
{
for (var j = 0; j < groupedStrokes[i].Count; j++)
{
sb.Append(groupedStrokes[i][j].X.ToString(fm, ic));
sb.Append(",");
sb.Append(groupedStrokes[i][j].Y.ToString(fm, ic));
if (j < groupedStrokes[i].Count - 1) sb.Append(";");
}
if (i < groupedStrokes.Count - 1) sb.Append("/");
}
break;

case "svg":
for (var gs = 0; gs < groupedStrokes.Count; gs++)
{
sb.Append(gs == 0 ? "M " : " L ");
for (var p = 0; p < groupedStrokes[gs].Count; p++)
{
sb.Append(groupedStrokes[gs][p].X.ToString(fm, ic));
sb.Append(" ");
sb.Append(groupedStrokes[gs][p].Y.ToString(fm, ic));
}
}
break;
}

return sb.ToString();
}
```

## See Also

- [SignaturePad Overview](https://docs.telerik.com/devtools/maui/controls/signaturepad/overview)
- [SaveImageSettings Documentation](https://docs.telerik.com/devtools/maui/api/telerik.maui.controls.signaturepad.saveimagesettings)
- [SKBitmap Documentation](https://docs.microsoft.com/en-us/dotnet/api/skiasharp.skbitmap?view=skiasharp-2.80)
Loading