-
Notifications
You must be signed in to change notification settings - Fork 19
Added new kb article extracting-point-data-from-signature-signaturepad-dotnet-maui #1274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
didiyordanova
merged 2 commits into
master
from
new-kb-extracting-point-data-from-signature-signaturepad-dotnet-maui-3e416bccf9a54ab0afa71960b2a51ca9
Nov 20, 2025
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
197 changes: 197 additions & 0 deletions
197
knowledge-base/extracting-point-data-from-signature-signaturepad-dotnet-maui.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| 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. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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** | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Decode the image bytes into `SKBitmap`. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Create a grayscale bitmap for better contrast. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Convert the grayscale bitmap into a binary bitmap (black and white) using a threshold. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| **Stage 2: Scan Bitmap and Create Point Groups** | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Scan the binary bitmap for black pixels. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Group neighboring black pixels into strokes using an algorithm. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Store grouped strokes as `List<List<SKPoint>>`. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| **Stage 3: Format the Data** | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Serialize the grouped strokes into a desired format. | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Supported formats: | ||
| - Custom: `"1,2;3,4;5,6/7,8;9,10;11,12"` | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - SVG-like: `"M x y L x y L x y ..."` | ||
didiyordanova marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.