This commit is contained in:
GG Z
2026-02-12 21:28:44 +08:00
parent 785e5de36e
commit a9faf251be
14 changed files with 207 additions and 2 deletions

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
</configuration>

View File

@@ -1,16 +0,0 @@
<Application
StartupUri="MainWindow.xaml"
x:Class="DrfxFontFixer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:DrfxFontFixer"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Melskin;component/Themes/Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/Melskin;component/Themes/ColorPalette/LightBlue.xaml" />
<ResourceDictionary Source="pack://application:,,,/Melskin;component/Themes/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace DrfxFontFixer
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -1,179 +0,0 @@
<ms:MelWindow
Height="700"
Title="DaVinci Resolve drfx 字体替换"
Width="1000"
mc:Ignorable="d"
x:Class="DrfxFontFixer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ms="https://github.com/ShrlAlgo/Melskin"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="200" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="1.5*" />
</Grid.ColumnDefinitions>
<GroupBox Grid.Row="0" Header="添加需要处理的 drfx 文件">
<ListBox BorderThickness="1" x:Name="FileListBox" />
</GroupBox>
<ms:FlexibleRowPanel Grid.Column="0" Grid.Row="1">
<Button
Click="AddFilesButton_Click"
Content="添加drfx文件"
x:Name="AddFilesButton" />
<Button
Click="ClearListButton_Click"
Content="清空处理列表"
x:Name="ClearListButton" />
</ms:FlexibleRowPanel>
<ms:FlexibleRowPanel
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2">
<TextBox ms:FlexibleRowPanel.Span="2" x:Name="OutputPath" />
<Button
Click="BrowserOutputPathButton_Click"
Content="选择输出路径"
x:Name="BrowserOutputPathButton" />
<Button
Click="OpenOutputButton_Click"
Content="打开输出目录"
IsEnabled="False"
x:Name="OpenOutputButton" />
</ms:FlexibleRowPanel>
<GroupBox
FontWeight="Bold"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="3"
Header="处理日志">
<ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="LogScrollViewer">
<TextBlock
FontFamily="Consolas"
Margin="5"
TextWrapping="Wrap"
x:Name="LogTextBlock" />
</ScrollViewer>
</GroupBox>
<GroupBox
Grid.Column="1"
Grid.Row="0"
Header="扫描文件并定义字体/样式替换规则">
<DataGrid
AutoGenerateColumns="False"
CanUserAddRows="False"
x:Name="MappingDataGrid">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ToolTipService.ShowDuration" Value="30000" />
<Setter Property="ToolTip" Value="{Binding UsageInfo}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding OriginalFont}"
Header="原始字体"
IsReadOnly="True"
Width="*" />
<DataGridTextColumn
Binding="{Binding OriginalStyle}"
Header="原始样式"
IsReadOnly="True"
Width="*" />
<DataGridTemplateColumn Header="新字体" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!-- 注意我们仍然保留LostFocus因为它能最好地处理“手动输入”文本的场景 -->
<ComboBox
IsEditable="True"
ItemsSource="{Binding DataContext.SystemFonts, RelativeSource={RelativeSource AncestorType=Window}}"
LostFocus="NewFontComboBox_LostFocus"
Text="{Binding NewFont, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="新样式" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding DataContext.NewStyle, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsNewFontInSystem}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox
IsEditable="True"
ItemsSource="{Binding Path=DataContext.AvailableNewStyles, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}}"
Text="{Binding DataContext.NewStyle, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridCell}}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<StackPanel
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
VerticalAlignment="Bottom">
<Button
Click="ScanFilesButton_Click"
Content="扫描文件并更新映射表"
x:Name="ScanFilesButton" />
<Button
Click="LoadMapButton_Click"
Content="加载映射表"
x:Name="LoadMapButton" />
<Button
Click="SaveMapButton_Click"
Content="保存映射表"
x:Name="SaveMapButton" />
<Button
Click="ClearMapButton_Click"
Content="清空映射表"
x:Name="ClearMapButton" />
</StackPanel>
<Grid Grid.ColumnSpan="2" Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ProgressBar
Grid.Column="0"
Height="35"
x:Name="MainProgressBar" />
<Button
Click="StartButton_Click"
Content="开始处理"
Grid.Column="1"
x:Name="StartButton" />
</Grid>
</Grid>
</ms:MelWindow>

View File

@@ -1,595 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace DrfxFontFixer
{
public class FullMapping : INotifyPropertyChanged
{
private static Dictionary<string, FontFamily> _systemFontDict;
public static void Initialize(Dictionary<string, FontFamily> fontDict)
{
_systemFontDict = fontDict;
}
public string OriginalFont { get; init; }
public string OriginalStyle { get; init; }
private string newFont;
public string NewFont
{
get => newFont;
set
{
if (newFont != value)
{
newFont = value;
OnPropertyChanged(nameof(NewFont));
UpdateAvailableStyles();
}
}
}
private string newStyle;
public string NewStyle
{
get => newStyle;
set
{
newStyle = value;
OnPropertyChanged(nameof(NewStyle));
}
}
public ObservableCollection<string> AvailableNewStyles { get; } = [];
public bool IsNewFontInSystem { get; private set; }
[JsonIgnore] private string usageInfo;
[JsonIgnore]
public string UsageInfo
{
get => usageInfo;
set
{
usageInfo = value;
OnPropertyChanged(nameof(UsageInfo));
}
}
private void UpdateAvailableStyles()
{
IsNewFontInSystem = _systemFontDict.TryGetValue(NewFont, out var fontFamily);
Application.Current.Dispatcher.Invoke(() => AvailableNewStyles.Clear());
if (IsNewFontInSystem)
{
var styles = new List<string>();
foreach (var typeface in fontFamily.GetTypefaces())
{
styles.Add(typeface.FaceNames[System.Windows.Markup.XmlLanguage.GetLanguage("en-us")]);
}
styles.Distinct().OrderBy(s => s).ToList().ForEach(s => Application.Current.Dispatcher.Invoke(() => AvailableNewStyles.Add(s)));
if (!AvailableNewStyles.Contains(NewStyle))
{
NewStyle = AvailableNewStyles.Contains("Regular") ? "Regular" : AvailableNewStyles.FirstOrDefault();
}
}
OnPropertyChanged(nameof(IsNewFontInSystem));
OnPropertyChanged(nameof(AvailableNewStyles));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public partial class MainWindow
{
public ObservableCollection<FullMapping> FullMappings { get; set; } = new ObservableCollection<FullMapping>();
public List<string> SystemFonts { get; set; } = new List<string>();
private readonly string mappingFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mapping.json");
private bool isProcessingComplete = false;
private string lastOutputDirectory = "";
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
LoadSystemFonts();
OutputPath.Text = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Blackmagic Design",
"DaVinci Resolve",
"Support",
"Fusion",
"Templates"
);
LoadMapping(false);
MappingDataGrid.ItemsSource = FullMappings;
}
#region Initialization, Persistence and Font Management
private void LoadSystemFonts()
{
var fontDict = new Dictionary<string, FontFamily>();
foreach (var fontFamily in Fonts.SystemFontFamilies.OrderBy(f => f.Source))
{
if (!fontDict.ContainsKey(fontFamily.Source)) fontDict.Add(fontFamily.Source, fontFamily);
}
SystemFonts = fontDict.Keys.ToList();
FullMapping.Initialize(fontDict);
}
private void SaveMapping()
{
try
{
string json = JsonConvert.SerializeObject(FullMappings, Formatting.Indented);
File.WriteAllText(mappingFilePath, json);
Log("映射表已成功保存。", Brushes.Blue);
}
catch (Exception ex)
{
Log($"保存映射表失败: {ex.Message}", Brushes.Red);
}
}
private void LoadMapping(bool userInitiated = true)
{
if (!File.Exists(mappingFilePath))
{
if (userInitiated) MessageBox.Show("未找到 mapping.json 文件。", "提示");
return;
}
try
{
string json = File.ReadAllText(mappingFilePath);
var loaded = JsonConvert.DeserializeObject<ObservableCollection<FullMapping>>(json);
if (loaded != null)
{
FullMappings = loaded;
MappingDataGrid.ItemsSource = null;
MappingDataGrid.ItemsSource = FullMappings;
Log("已成功加载本地映射表。", Brushes.Blue);
}
}
catch (Exception ex)
{
Log($"加载映射表失败: {ex.Message}", Brushes.Red);
}
}
#endregion
#region UI Event Handlers
private void NewFontComboBox_LostFocus(object sender, RoutedEventArgs e)
{
var comboBox = sender as ComboBox;
var mapping = comboBox?.DataContext as FullMapping;
if (mapping != null && mapping.NewFont != comboBox.Text)
{
mapping.NewFont = comboBox.Text;
}
}
private void NewStyleComboBox_Loaded(object sender, RoutedEventArgs e)
{
var comboBox = sender as ComboBox;
var mapping = comboBox?.DataContext as FullMapping;
if (mapping != null)
{
comboBox.ItemsSource = mapping.AvailableNewStyles;
}
}
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
if (FileListBox.Items.Count == 0)
{
MessageBox.Show("请先添加 .drfx 文件。");
return;
}
if (FullMappings.Count == 0)
{
MessageBox.Show("映射表为空。");
return;
}
var files = FileListBox.Items.Cast<string>().ToList();
SetUiState(false);
LogTextBlock.Text = "";
MainProgressBar.Value = 0;
isProcessingComplete = false;
try
{
var progress = new Progress<ProgressReport>(r =>
{
if (!string.IsNullOrEmpty(r.Message)) Log(r.Message, r.Color);
MainProgressBar.Value = r.Percentage;
});
var outputPath = OutputPath.Text;
await Task.Run(() => ProcessFiles_Mapping(files, outputPath, FullMappings.ToList(), progress));
Log("====== 所有任务已完成! ======", Brushes.Green);
isProcessingComplete = true;
var result = MessageBox.Show("处理完成!是否打开输出目录", "完成", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result== MessageBoxResult.Yes)
{
Process.Start("explorer.exe", outputPath);
}
}
catch (Exception ex)
{
Log($"严重错误: {ex.Message}", Brushes.Red);
}
finally
{
SetUiState(true);
}
}
private async void ScanFilesButton_Click(object sender, RoutedEventArgs e)
{
if (FileListBox.Items.Count == 0)
{
MessageBox.Show("请先添加要扫描的 .drfx 文件。");
return;
}
SetUiState(false);
Log("开始扫描文件...");
var usageDict = new Dictionary<Tuple<string, string>, List<string>>();
try
{
await Task.Run(() =>
{
foreach (var drfxFile in FileListBox.Items.Cast<string>())
{
using (var archive = ZipFile.OpenRead(drfxFile))
{
foreach (var entry in archive.Entries.Where(en => en.Name.EndsWith(".setting", StringComparison.OrdinalIgnoreCase)))
{
using (var entryStream = entry.Open())
using (var memoryStream = new MemoryStream())
{
entryStream.CopyTo(memoryStream);
byte[] contentBytes = memoryStream.ToArray();
using (var readableStream = new MemoryStream(contentBytes))
{
var encoding = GetFileEncoding(readableStream);
string content = encoding.GetString(contentBytes);
var foundPairs = ExtractFontAndStylePairs(content);
foreach (var pair in foundPairs)
{
if (!usageDict.ContainsKey(pair))
{
usageDict[pair] = new List<string>();
}
usageDict[pair].Add(entry.Name);
}
}
}
}
}
}
});
int newItems = 0;
var existingMappings = FullMappings.ToDictionary(m => (m.OriginalFont, m.OriginalStyle));
foreach (var entry in usageDict.OrderBy(p => p.Key.Item1).ThenBy(p => p.Key.Item2))
{
var pair = (entry.Key.Item1, entry.Key.Item2);
string usageInfo = $"使用文件:\n{string.Join("\n", entry.Value.Distinct())}";
if (existingMappings.TryGetValue(pair, out var existingMapping))
{
existingMapping.UsageInfo = usageInfo;
}
else
{
FullMappings.Add(new FullMapping
{
OriginalFont = pair.Item1, OriginalStyle = pair.Item2, NewFont = pair.Item1, NewStyle = pair.Item2, UsageInfo = usageInfo
});
newItems++;
}
}
Log($"扫描完成,新增 {newItems} 个组合,并已更新所有文件引用信息。", Brushes.Blue);
}
catch (Exception ex)
{
Log($"扫描失败: {ex.Message}", Brushes.Red);
}
finally
{
SetUiState(true);
}
}
private void SaveMapButton_Click(object sender, RoutedEventArgs e) => SaveMapping();
private void LoadMapButton_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("加载将覆盖当前更改,确定吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
LoadMapping();
}
}
private void ClearMapButton_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("确定要清空映射表吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
FullMappings.Clear();
Log("映射表已清空。");
}
}
#endregion
#region Core Processing Logic
private void ProcessFiles_Mapping(List<string> files, string outputDir, List<FullMapping> mappings, IProgress<ProgressReport> progress)
{
int filesCompleted = 0;
var mappingDict = mappings.ToDictionary(m => (m.OriginalFont, m.OriginalStyle));
foreach (var drfxFile in files)
{
int totalReplacementsInFile = 0;
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
progress.Report(new ProgressReport { Message = $"---处理: {Path.GetFileName(drfxFile)} ---" });
ZipFile.ExtractToDirectory(drfxFile, tempDir);
var settingFiles = Directory.GetFiles(tempDir, "*.setting", SearchOption.AllDirectories);
foreach (var settingFile in settingFiles)
{
var (modifiedContent, replacementsCount) = ProcessSingleFile(settingFile, mappingDict, progress);
if (replacementsCount > 0)
{
Encoding encoding;
using (var stream = new FileStream(settingFile, FileMode.Open, FileAccess.Read))
{
encoding = GetFileEncoding(stream);
}
File.WriteAllText(settingFile, modifiedContent, encoding);
totalReplacementsInFile += replacementsCount;
}
}
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
lastOutputDirectory = outputDir;
string finalDrfxPath = Path.Combine(outputDir, Path.GetFileName(drfxFile));
if (File.Exists(finalDrfxPath)) File.Delete(finalDrfxPath);
ZipFile.CreateFromDirectory(tempDir, finalDrfxPath, CompressionLevel.Optimal, false);
progress.Report(totalReplacementsInFile > 0
? new ProgressReport { Message = $" - 总结: 共应用 {totalReplacementsInFile} 条替换规则。", Color = Brushes.Purple }
: new ProgressReport { Message = $" - 总结: 无需任何替换。", Color = Brushes.Gray });
progress.Report(new ProgressReport { Message = $" -> 成功! 已保存到 '{outputDir}' 文件夹。", Color = Brushes.Blue });
}
catch (Exception ex)
{
progress.Report(new ProgressReport { Message = $" -> 失败: {ex.Message}", Color = Brushes.Red });
}
finally
{
filesCompleted++;
progress.Report(new ProgressReport { Percentage = (int)(100.0 * filesCompleted / files.Count) });
if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true);
}
}
}
private (string ModifiedContent, int ReplacementsCount) ProcessSingleFile(string filePath,
Dictionary<(string, string), FullMapping> mappingDict, IProgress<ProgressReport> progress)
{
Encoding encoding;
// if (filePath.Contains("Title 03"))
// {
// }
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
encoding = GetFileEncoding(stream);
}
string originalContent = File.ReadAllText(filePath, encoding);
int replacementsCount = 0;
string toolBlockPattern =
@"\w+\s*=\s*(?:TextPlus)\s*(\{((?>[^{}]+|\{(?<open>)|\}(?<-open>))*(?(open)(?!)))\})";
string modifiedContent = Regex.Replace(originalContent, toolBlockPattern, (match) =>
{
string toolBlockContent = match.Value;
string font = ExtractValue(toolBlockContent, "Font");
string style = ExtractValue(toolBlockContent, "Style");
if (font != null && style != null && mappingDict.TryGetValue((font, style), out var rule))
{
string newBlock = toolBlockContent;
if (rule.NewFont != font)
{
newBlock = Regex.Replace(newBlock, GetPattern("Font"), $"$1{rule.NewFont}$2");
progress.Report(new ProgressReport
{ Message = $" - 字体映射: '{font}' -> '{rule.NewFont}' (文件: {Path.GetFileName(filePath)})", Color = Brushes.DarkGreen });
replacementsCount++;
}
if (rule.NewStyle != style)
{
newBlock = Regex.Replace(newBlock, GetPattern("Style"), $"$1{rule.NewStyle}$2");
progress.Report(new ProgressReport
{ Message = $" - 样式映射: '{style}' -> '{rule.NewStyle}' (文件: {Path.GetFileName(filePath)})", Color = Brushes.DarkCyan });
replacementsCount++;
}
return newBlock;
}
return toolBlockContent;
});
return (modifiedContent, replacementsCount);
}
#endregion
#region Helper Methods
// --- 核心BUG终极修复: 使用两个捕获组,确保替换的完整性 ---
private string GetPattern(string fieldName) => $@"({GetExtractionPattern(fieldName, true)})[^""]*({GetTrailingPattern()})";
// 这个辅助方法只用于 GetPattern
private string GetTrailingPattern() => @"""(,?)\s*\S*\s*\}";
// 这个辅助方法只用于 GetPattern
private string GetExtractionPattern(string fieldName, bool forCapture)
{
string pattern = $@"\b{fieldName}\s*=\s*Input\s*\{{[^{{}}]*?Value\s*=\s*""";
return forCapture ? pattern : pattern + @"(?<value>[^""]*)""";
}
private string ExtractValue(string content, string fieldName)
{
var match = Regex.Match(content, GetExtractionPattern(fieldName, false));
return match.Success ? match.Groups["value"].Value : null;
}
private List<Tuple<string, string>> ExtractFontAndStylePairs(string content)
{
var pairs = new List<Tuple<string, string>>();
string toolBlockPattern =
@"\w+\s*=\s*(?:TextPlus)\s*(\{((?>[^{}]+|\{(?<open>)|\}(?<-open>))*(?(open)(?!)))\})";
var matches = Regex.Matches(content, toolBlockPattern);
foreach (Match match in matches)
{
string font = ExtractValue(match.Value, "Font");
string style = ExtractValue(match.Value, "Style");
if (font != null && style != null)
{
pairs.Add(new Tuple<string, string>(font, style));
}
}
return pairs;
}
private Encoding GetFileEncoding(Stream stream)
{
var bom = new byte[4];
stream.Read(bom, 0, 4);
stream.Position = 0;
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode;
if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode;
return new UTF8Encoding(false);
}
private void SetUiState(bool isEnabled)
{
StartButton.IsEnabled = isEnabled;
AddFilesButton.IsEnabled = isEnabled;
ClearListButton.IsEnabled = isEnabled;
ScanFilesButton.IsEnabled = isEnabled;
SaveMapButton.IsEnabled = isEnabled;
ClearMapButton.IsEnabled = isEnabled;
LoadMapButton.IsEnabled = isEnabled;
MappingDataGrid.IsEnabled = isEnabled;
OpenOutputButton.IsEnabled = isProcessingComplete || isEnabled;
}
private void Log(string message, Brush color = null)
{
var run = new Run(DateTime.Now.ToString("HH:mm:ss") + " - " + message + Environment.NewLine) { Foreground = color ?? Brushes.Black };
LogTextBlock.Inlines.Add(run);
LogScrollViewer.ScrollToEnd();
}
private void AddFilesButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog { Filter = "DRFX Files (*.drfx)|*.drfx", Multiselect = true, Title = "选择 DRFX 文件" };
if (ofd.ShowDialog() == true)
foreach (string f in ofd.FileNames)
if (!FileListBox.Items.Contains(f))
FileListBox.Items.Add(f);
}
private void ClearListButton_Click(object sender, RoutedEventArgs e)
{
FileListBox.Items.Clear();
Log("文件列表已清空。");
}
private void OpenOutputButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(lastOutputDirectory) || !Directory.Exists(lastOutputDirectory))
{
MessageBox.Show("输出目录不存在。请先处理至少一个文件。", "提示");
return;
}
try
{
Process.Start("explorer.exe", lastOutputDirectory);
}
catch (Exception ex)
{
MessageBox.Show("无法打开目录: " + ex.Message, "错误");
}
}
public struct ProgressReport
{
public int Percentage { get; set; }
public string Message { get; set; }
public Brush Color { get; set; }
}
#endregion
private void BrowserOutputPathButton_Click(object sender, RoutedEventArgs e)
{
var dlg = new OpenFolderDialog()
{
Title = "选择输出目录",
};
if (dlg.ShowDialog()== true)
{
OutputPath.Text = dlg.FolderName;
}
}
}
}

View File

@@ -1,71 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace DrfxFontFixer.Properties
{
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DrfxFontFixer.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,30 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DrfxFontFixer.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>