-
Notifications
You must be signed in to change notification settings - Fork 6
fix: restore per-project vCard files #179
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
Open
alafleur-genetec
wants to merge
4
commits into
main
Choose a base branch
from
fix/restore-vcard-per-project
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
a226932
fix: restore per-project VCard files to fix broken main build
alafleur-genetec e173f4a
Potential fix for pull request finding
alafleur-genetec 090537c
Potential fix for pull request finding
alafleur-genetec 401ae2d
fix: address remaining Copilot findings on VCardReader
alafleur-genetec 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
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
File renamed without changes.
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
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,20 @@ | ||
| // Copyright 2025 Genetec Inc. | ||
| // Licensed under the Apache License, Version 2.0 | ||
|
|
||
| namespace Genetec.Dap.CodeSamples; | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Windows.Media; | ||
|
|
||
| public class VCard | ||
| { | ||
| public string FirstName { get; set; } | ||
|
|
||
| public string LastName { get; set; } | ||
|
|
||
| public List<string> Emails { get; } = new(); | ||
|
|
||
| public string Note { get; set; } | ||
|
|
||
| public ImageSource Picture { get; set; } | ||
| } | ||
188 changes: 188 additions & 0 deletions
188
Samples/Workspace SDK/ImageExtractorSample/VCardReader.cs
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,188 @@ | ||
| // Copyright 2025 Genetec Inc. | ||
| // Licensed under the Apache License, Version 2.0 | ||
|
|
||
| namespace Genetec.Dap.CodeSamples; | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Text.RegularExpressions; | ||
| using System.Windows.Media; | ||
| using System.Windows.Media.Imaging; | ||
|
|
||
| public class VCardReader | ||
| { | ||
| public static VCard ReadVCard(string filePath) | ||
| { | ||
| string vCardText = File.ReadAllText(filePath); | ||
|
|
||
| // Handle line folding (lines starting with space or tab are continuations) | ||
| vCardText = Regex.Replace(vCardText, @"\r?\n[ \t]", "", RegexOptions.Multiline); | ||
|
|
||
| var vcard = new VCard(); | ||
|
|
||
| // Extract name fields properly | ||
| string fullName = ExtractField(vCardText, "FN"); | ||
| string structuredName = ExtractField(vCardText, "N"); | ||
|
|
||
| vcard.FirstName = ExtractFirstName(fullName, structuredName); | ||
| vcard.LastName = ExtractLastName(structuredName, fullName); | ||
| vcard.Note = ExtractField(vCardText, "NOTE"); | ||
| vcard.Picture = ExtractPhoto(vCardText); | ||
|
|
||
| vcard.Emails.AddRange(ExtractEmails(vCardText)); | ||
| return vcard; | ||
| } | ||
|
|
||
| private static string ExtractField(string vCardText, string fieldName) | ||
| { | ||
| // Match the exact field name, optionally followed by vCard parameters (";..."), then the value separator | ||
| string pattern = $@"^{Regex.Escape(fieldName)}(?:;[^:]*)?:(.*)$"; | ||
| Match match = Regex.Match(vCardText, pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase); | ||
| return match.Success ? match.Groups[1].Value.Trim() : string.Empty; | ||
| } | ||
|
|
||
| private static string ExtractFirstName(string fullName, string structuredName) | ||
| { | ||
| if (!string.IsNullOrEmpty(structuredName)) | ||
| { | ||
| // N: field format is "LastName;FirstName;MiddleName;Prefix;Suffix" | ||
| string[] nameParts = structuredName.Split(';'); | ||
| if (nameParts.Length > 1 && !string.IsNullOrEmpty(nameParts[1])) | ||
| { | ||
| return nameParts[1].Trim(); | ||
| } | ||
| } | ||
|
|
||
| // Fallback to extracting first word from full name | ||
| if (!string.IsNullOrEmpty(fullName)) | ||
| { | ||
| return fullName.Split(' ').FirstOrDefault()?.Trim() ?? string.Empty; | ||
| } | ||
|
|
||
| return string.Empty; | ||
| } | ||
|
|
||
| private static string ExtractLastName(string structuredName, string fullName) | ||
| { | ||
| if (!string.IsNullOrEmpty(structuredName)) | ||
| { | ||
| // N: field format - last name is the first component | ||
| string[] nameParts = structuredName.Split(';'); | ||
| if (nameParts.Length > 0 && !string.IsNullOrEmpty(nameParts[0])) | ||
| { | ||
| return nameParts[0].Trim(); | ||
| } | ||
| } | ||
|
|
||
| // Fallback to extracting last word from full name | ||
| if (!string.IsNullOrEmpty(fullName)) | ||
| { | ||
| string[] words = fullName.Split(' '); | ||
| return words.Length > 1 ? words.Last().Trim() : string.Empty; | ||
| } | ||
|
|
||
| return string.Empty; | ||
| } | ||
|
|
||
| private static List<string> ExtractEmails(string vCardText) | ||
| { | ||
| var emails = new List<string>(); | ||
|
|
||
| // Match EMAIL fields with various parameter formats | ||
| string pattern = @"^EMAIL(?:[^:]*)?:(.+)$"; | ||
| MatchCollection matches = Regex.Matches(vCardText, pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase); | ||
|
|
||
| foreach (Match match in matches) | ||
| { | ||
| string email = match.Groups[1].Value.Trim(); | ||
| if (!string.IsNullOrEmpty(email) && !emails.Contains(email)) | ||
| { | ||
| emails.Add(email); | ||
| } | ||
| } | ||
|
|
||
| return emails; | ||
| } | ||
|
|
||
| private static ImageSource ExtractPhoto(string vCardText) | ||
| { | ||
| // Try different photo formats | ||
| var photoPatterns = new[] | ||
| { | ||
| @"PHOTO;ENCODING=b;TYPE=image/jpeg:(.+?)(?=\r?\n[A-Z]|\r?\n$|$)", | ||
| @"PHOTO;ENCODING=BASE64;TYPE=JPEG:(.+?)(?=\r?\n[A-Z]|\r?\n$|$)", | ||
| @"PHOTO;TYPE=JPEG;ENCODING=b:(.+?)(?=\r?\n[A-Z]|\r?\n$|$)", | ||
| @"PHOTO:data:image/jpeg;base64,(.+?)(?=\r?\n[A-Z]|\r?\n$|$)" | ||
| }; | ||
|
|
||
| foreach (string pattern in photoPatterns) | ||
| { | ||
| Match photoMatch = Regex.Match(vCardText, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase); | ||
| ImageSource image = TryCreateImageFromPhotoMatch(photoMatch); | ||
| if (image != null) | ||
| { | ||
| return image; | ||
| } | ||
| } | ||
|
|
||
| // Fallback for additional valid vCard photo formats: | ||
| // - PHOTO;ENCODING=b:... | ||
| // - PHOTO;TYPE=PNG;ENCODING=b:... | ||
| // - PHOTO:data:image/png;base64,... | ||
| // - PHOTO:data:image/*;base64,... | ||
| // - PHOTO:data:<any-media-type>;base64,... | ||
| var fallbackPatterns = new[] | ||
| { | ||
| @"^PHOTO(?:;[^:\r\n]*)?:(?:data:[^;,]+(?:;[^,]*)?,)?([A-Za-z0-9+/=\r\n\t ]+)$", | ||
| @"^PHOTO(?:;[^:\r\n]*ENCODING=b[^:\r\n]*)?:([A-Za-z0-9+/=\r\n\t ]+)$" | ||
| }; | ||
|
|
||
| foreach (string pattern in fallbackPatterns) | ||
| { | ||
| Match photoMatch = Regex.Match(vCardText, pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase); | ||
| ImageSource image = TryCreateImageFromPhotoMatch(photoMatch); | ||
| if (image != null) | ||
| { | ||
| return image; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private static ImageSource TryCreateImageFromPhotoMatch(Match photoMatch) | ||
| { | ||
| if (!photoMatch.Success || photoMatch.Groups.Count < 2) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| string base64Data = photoMatch.Groups[1].Value | ||
| .Replace("\n", "") | ||
| .Replace("\r", "") | ||
| .Replace(" ", "") | ||
| .Replace("\t", ""); | ||
|
|
||
| byte[] imageBytes = Convert.FromBase64String(base64Data); | ||
| using var stream = new MemoryStream(imageBytes); | ||
|
|
||
| var bitmap = new BitmapImage(); | ||
| bitmap.BeginInit(); | ||
| bitmap.StreamSource = stream; | ||
| bitmap.CacheOption = BitmapCacheOption.OnLoad; | ||
| bitmap.EndInit(); | ||
| bitmap.Freeze(); | ||
|
|
||
| return bitmap; | ||
| } | ||
| catch (Exception ex) when (ex is FormatException or IOException or NotSupportedException) | ||
| { | ||
| Console.Error.WriteLine($"Failed to decode photo from vCard: {ex.Message}"); | ||
| return null; | ||
| } | ||
| } | ||
| } |
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.