Files
FontGlyphExporter/MainForm.cs
2025-04-04 19:26:10 -04:00

291 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace FontGlyphExporter
{
public partial class MainForm : Form
{
private string fontPath = "";
private string cssPath = "";
private List<IconInfo> allIcons = new();
private List<IconInfo> selectedIcons = new();
private PrivateFontCollection fontCollection = new();
private Font font;
private Dictionary<string, string> regexPatterns = new();
private List<int> selectedSizes = new();
public MainForm()
{
InitializeComponent();
InitializeRegexPatterns();
InitializeSizeCheckboxes();
cboRegexPattern.SelectedIndex = 0;
}
private void InitializeRegexPatterns()
{
regexPatterns.Add("Font Awesome 5/6", @"\.fa-([a-z0-9-]+)\s*{[^}]*--fa:\s*""\\(f[a-f0-9]{3,4})""");
regexPatterns.Add("Font Awesome 4", @"\.fa-([a-z0-9-]+):before\s*{[^}]*content:\s*""\\(f[a-f0-9]{3,4})""");
regexPatterns.Add("Material Icons", @"\.material-icons-([a-z0-9-]+):before\s*{[^}]*content:\s*""\\(e[a-f0-9]{3,4})""");
regexPatterns.Add("Custom", "");
}
private void InitializeSizeCheckboxes()
{
chkSize4.Tag = 4;
chkSize8.Tag = 8;
chkSize12.Tag = 12;
chkSize24.Tag = 24;
// Default selection
chkSize12.Checked = true;
chkSize24.Checked = true;
UpdateSelectedSizes();
}
private void UpdateSelectedSizes()
{
selectedSizes.Clear();
foreach (Control c in pnlSizes.Controls)
{
if (c is CheckBox chk && chk.Checked && chk.Tag is int size)
{
selectedSizes.Add(size);
}
}
// Update the selected sizes label
lblSelectedSizes.Text = selectedSizes.Count > 0
? $"Selected sizes: {string.Join(", ", selectedSizes)}px"
: "No sizes selected";
}
private void btnBrowseTTF_Click(object sender, EventArgs e)
{
using OpenFileDialog ofd = new() { Filter = "Font Files (*.ttf)|*.ttf" };
if (ofd.ShowDialog() == DialogResult.OK)
{
fontPath = ofd.FileName;
txtTTF.Text = fontPath;
}
}
private void btnBrowseCSS_Click(object sender, EventArgs e)
{
using OpenFileDialog ofd = new() { Filter = "CSS Files (*.css)|*.css" };
if (ofd.ShowDialog() == DialogResult.OK)
{
cssPath = ofd.FileName;
txtCSS.Text = cssPath;
}
}
private void btnLoad_Click(object sender, EventArgs e)
{
if (!File.Exists(fontPath) || !File.Exists(cssPath))
{
MessageBox.Show("Please select valid CSS and TTF files.");
return;
}
fontCollection = new PrivateFontCollection();
fontCollection.AddFontFile(fontPath);
font = new Font(fontCollection.Families[0], 16);
string css = File.ReadAllText(cssPath);
string patternToUse;
if (cboRegexPattern.SelectedItem.ToString() == "Custom")
{
patternToUse = txtCustomRegex.Text;
if (string.IsNullOrWhiteSpace(patternToUse))
{
MessageBox.Show("Please enter a valid regex pattern.");
return;
}
}
else
{
patternToUse = regexPatterns[cboRegexPattern.SelectedItem.ToString()];
}
try
{
var matches = Regex.Matches(css, patternToUse, RegexOptions.IgnoreCase);
allIcons.Clear();
foreach (Match match in matches)
{
if (match.Groups.Count < 3) continue;
string name = match.Groups[1].Value;
string unicode = match.Groups[2].Value;
allIcons.Add(new IconInfo { name = name, unicode = unicode });
}
if (allIcons.Count == 0)
{
MessageBox.Show("No icons found with the selected pattern. Try a different pattern or check your CSS file.");
return;
}
PopulateGrid();
}
catch (ArgumentException ex)
{
MessageBox.Show($"Invalid regex pattern: {ex.Message}");
}
}
private void PopulateGrid()
{
iconFlowPanel.Controls.Clear();
selectedIcons.Clear();
UpdateCounter();
foreach (var icon in allIcons)
{
var panel = new Panel
{
Width = 80,
Height = 100,
Margin = new Padding(5),
BorderStyle = BorderStyle.FixedSingle,
Tag = icon
};
var bmp = new Bitmap(48, 48);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
string glyph = char.ConvertFromUtf32(Convert.ToInt32(icon.unicode, 16));
g.DrawString(glyph, font, Brushes.Black, 0, 0);
}
var pic = new PictureBox
{
Image = bmp,
SizeMode = PictureBoxSizeMode.CenterImage,
Width = 48,
Height = 48,
Top = 5,
Left = 15
};
var lbl = new Label
{
Text = icon.name,
Width = 70,
Top = 60,
Left = 5,
TextAlign = ContentAlignment.MiddleCenter
};
panel.Controls.Add(pic);
panel.Controls.Add(lbl);
panel.Click += (s, e) =>
{
if (selectedIcons.Contains(icon))
{
selectedIcons.Remove(icon);
panel.BackColor = SystemColors.Control;
}
else
{
selectedIcons.Add(icon);
panel.BackColor = Color.LightBlue;
}
UpdateCounter();
};
iconFlowPanel.Controls.Add(panel);
}
}
private void UpdateCounter()
{
lblCounter.Text = $"Selected: {selectedIcons.Count} / {allIcons.Count}";
}
private void btnExport_Click(object sender, EventArgs e)
{
if (selectedIcons.Count == 0)
{
MessageBox.Show("Please select at least one icon to export.");
return;
}
if (selectedSizes.Count == 0)
{
MessageBox.Show("Please select at least one size for export.");
return;
}
var icons = new List<object>();
var outputDir = Path.Combine("output", "icons");
Directory.CreateDirectory(outputDir);
foreach (var icon in selectedIcons)
{
foreach (var size in selectedSizes)
{
string glyph = char.ConvertFromUtf32(Convert.ToInt32(icon.unicode, 16));
string file = $"icon_{icon.name}_{size}x{size}.bin";
string path = Path.Combine(outputDir, file);
using Bitmap bmp = new(size, size);
using Graphics g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
g.DrawString(glyph, new Font(font.FontFamily, size), Brushes.Black, 0, 0);
using FileStream fs = new(path, FileMode.Create);
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x += 8)
{
byte b = 0;
for (int bit = 0; bit < 8 && (x + bit) < bmp.Width; bit++)
{
var pixel = bmp.GetPixel(x + bit, y);
if (pixel.R < 128) b |= (byte)(1 << bit);
}
fs.WriteByte(b);
}
}
icons.Add(new { name = icon.name, file = file, width = size, height = size });
}
}
File.WriteAllText("output/icons_index.json", JsonSerializer.Serialize(icons, new JsonSerializerOptions { WriteIndented = true }));
MessageBox.Show("Export complete!");
}
private void cboRegexPattern_SelectedIndexChanged(object sender, EventArgs e)
{
bool isCustom = cboRegexPattern.SelectedItem.ToString() == "Custom";
txtCustomRegex.Visible = isCustom;
lblCustomRegex.Visible = isCustom;
}
private void sizeCheckbox_CheckedChanged(object sender, EventArgs e)
{
UpdateSelectedSizes();
}
public class IconInfo
{
public string name { get; set; }
public string unicode { get; set; }
}
}
}