using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using Microsoft.Win32; namespace ShrlAlgoStudio { /// /// Iconfont2CSharpView.xaml 的交互逻辑 /// public partial class Iconfont2CSharpView { public ObservableCollection Icons { get; set; } = new ObservableCollection(); // 字体预览属性 public FontFamily PreviewFontFamily { get { return (FontFamily)GetValue(PreviewFontFamilyProperty); } set { SetValue(PreviewFontFamilyProperty, value); } } public static readonly DependencyProperty PreviewFontFamilyProperty = DependencyProperty.Register("PreviewFontFamily", typeof(FontFamily), typeof(MainWindow), new PropertyMetadata(null)); // 内部状态 private Dictionary _cssNameMap = new Dictionary(); // Unicode -> CSS Name private string _loadedFontName = "MyIcons"; private GlyphTypeface _currentGlyphTypeface; public Iconfont2CSharpView() { InitializeComponent(); GridIcons.DataContext = this; GridIcons.ItemsSource = Icons; } // --------------------------------------------------------- // 1. 加载字体逻辑 // --------------------------------------------------------- private void OnLoadFontClick(object sender, RoutedEventArgs e) { var dlg = new OpenFileDialog { Filter = "Font Files|*.ttf;*.otf" }; if (dlg.ShowDialog() == true) { LoadFont(dlg.FileName); } } private void LoadFont(string path) { try { var uri = new Uri(path); _currentGlyphTypeface = new GlyphTypeface(uri); // 获取字体名称 (优先取英文) _loadedFontName = _currentGlyphTypeface.FamilyNames.ContainsKey(CultureInfo.GetCultureInfo("en-US")) ? _currentGlyphTypeface.FamilyNames[CultureInfo.GetCultureInfo("en-US")] : _currentGlyphTypeface.FamilyNames.Values.FirstOrDefault(); // 设置预览字体 (WPF Hack: file:///path/#FontName) this.PreviewFontFamily = new FontFamily(uri, "./#" + _loadedFontName); TxtStatus.Text = $"已加载字体: {_loadedFontName} (含 {_currentGlyphTypeface.CharacterToGlyphMap.Count} 个字符)"; RefreshData(); } catch (Exception ex) { MessageBox.Show($"无法加载字体: {ex.Message}"); } } // --------------------------------------------------------- // 2. 加载 CSS 映射逻辑 (核心增强) // --------------------------------------------------------- private void OnLoadCssClick(object sender, RoutedEventArgs e) { if (_currentGlyphTypeface == null) { MessageBox.Show("请先加载字体文件!"); return; } var dlg = new OpenFileDialog { Filter = "CSS Files|*.css", Title = "选择对应的 CSS 文件 (如 fontawesome.css)" }; if (dlg.ShowDialog() == true) { ParseCss(dlg.FileName); RefreshData(); // 重新生成列表和代码 } } private void ParseCss(string path) { _cssNameMap.Clear(); string cssContent = File.ReadAllText(path); // 正则表达式匹配: .class-name:before { content: "\f001"; } // 兼容格式:单双引号,有无 content 文本 // 捕获组 1: class name (e.g. "fa-home") // 捕获组 2: hex code (e.g. "f015") var regex = new Regex(@"\.([a-zA-Z0-9_-]+)::?before\s*\{\s*content:\s*[""']\\([a-fA-F0-9]+)[""'];?\s*\}"); var matches = regex.Matches(cssContent); foreach (Match match in matches) { string className = match.Groups[1].Value; string hex = match.Groups[2].Value; if (int.TryParse(hex, NumberStyles.HexNumber, null, out int unicode)) { // 如果存在多个名字对应同一个 Unicode (alias),这里只取第一个,或者取最短的 if (!_cssNameMap.ContainsKey(unicode)) { _cssNameMap[unicode] = className; } } } TxtStatus.Text += $" | 已解析 CSS 映射: {_cssNameMap.Count} 个名称"; } // --------------------------------------------------------- // 3. 数据处理与代码生成 // --------------------------------------------------------- private void RefreshData() { if (_currentGlyphTypeface == null) return; Icons.Clear(); var sortedList = new List(); foreach (var kvp in _currentGlyphTypeface.CharacterToGlyphMap) { int unicode = kvp.Key; // 生成基础变量名 string varName; if (_cssNameMap.ContainsKey(unicode)) { // 将 css-kebab-case 转换为 PascalCase // 比如 fa-arrow-circle-down -> ArrowCircleDown varName = ConvertCssToPascal(_cssNameMap[unicode]); } else { // 只有 Unicode 时的默认回退 varName = $"Icon_{unicode:X4}"; } sortedList.Add(new IconItem { Unicode = unicode, HexDisplay = $"\\u{unicode:X4}", Character = char.ConvertFromUtf32(unicode), VarName = varName }); } // 按 Unicode 排序并添加到界面 foreach (var item in sortedList.OrderBy(i => i.Unicode)) { Icons.Add(item); } GenerateCSharpCode(sortedList); } private void GenerateCSharpCode(List items) { bool isEnum = ChkGenerateEnum.IsChecked == true; string cleanName = Regex.Replace(_loadedFontName, @"[^a-zA-Z0-9]", ""); // 清理类名 if (char.IsDigit(cleanName[0])) cleanName = "_" + cleanName; StringBuilder sb = new StringBuilder(); sb.AppendLine("// -----------------------------------------------------------------------------"); sb.AppendLine($"// "); sb.AppendLine($"// Font Name: {_loadedFontName}"); sb.AppendLine($"// Generated: {DateTime.Now}"); sb.AppendLine($"// "); sb.AppendLine("// -----------------------------------------------------------------------------"); sb.AppendLine("using System;"); sb.AppendLine(); if (isEnum) { sb.AppendLine($"public enum {cleanName}Enum"); sb.AppendLine("{"); // 检查重复名称并处理 var usedNames = new HashSet(); foreach (var item in items) { string finalName = item.VarName; // 如果名字冲突,追加 Hex 后缀 if (usedNames.Contains(finalName)) finalName += $"_{item.Unicode:X4}"; usedNames.Add(finalName); // 不能以数字开头 if (char.IsDigit(finalName[0])) finalName = "_" + finalName; // 只能是 int sb.AppendLine($" /// Hex: {item.Unicode:X4}"); sb.AppendLine($" {finalName} = 0x{item.Unicode:X4},"); } sb.AppendLine("}"); } else { sb.AppendLine($"public static class {cleanName}Icons"); sb.AppendLine("{"); var usedNames = new HashSet(); foreach (var item in items) { string finalName = item.VarName; if (usedNames.Contains(finalName)) finalName += $"_{item.Unicode:X4}"; usedNames.Add(finalName); sb.AppendLine($" public const string {finalName} = \"{item.HexDisplay}\";"); } sb.AppendLine("}"); } TxtOutput.Text = sb.ToString(); } // 辅助:将 css-name 转换为 PascalName private string ConvertCssToPascal(string cssName) { // 移除常见的 icon 前缀 (可选) if (cssName.StartsWith("fa-")) cssName = cssName.Substring(3); if (cssName.StartsWith("mdi-")) cssName = cssName.Substring(4); if (cssName.StartsWith("bi-")) cssName = cssName.Substring(3); // 分割并首字母大写 var parts = cssName.Split(new[] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < parts.Length; i++) { if (parts[i].Length > 0) { char[] a = parts[i].ToCharArray(); a[0] = char.ToUpper(a[0]); parts[i] = new string(a); } } string result = string.Join("", parts); // 确保不是数字开头 if (char.IsDigit(result[0])) result = "_" + result; return result; } private void OnCopyCodeClick(object sender, RoutedEventArgs e) { Clipboard.SetText(TxtOutput.Text); MessageBox.Show("代码已复制到剪贴板"); } } public class IconItem { public int Unicode { get; set; } public string HexDisplay { get; set; } // "\uF001" public string Character { get; set; } // 实际字符 public string VarName { get; set; } // C# 变量名 } }