using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
namespace MultiWheelC.TrajectoryPlanning.PathSmoothing.Visualization;
/// 按准确族名解析报告所需字体,并提供混合中英文文本的共同基线度量。
public sealed class SmoothingFontResolver
{
public bool TryResolve(
string chineseFamilyName,
string latinFamilyName,
out SmoothingFontResolution resolution,
out string reason)
{
resolution = null;
reason = string.Empty;
if (string.IsNullOrWhiteSpace(chineseFamilyName) || string.IsNullOrWhiteSpace(latinFamilyName))
{
reason = "中文和拉丁字体族名均不能为空。";
return false;
}
var collection = new InstalledFontCollection();
FontFamily chinese = FindExact(collection.Families, chineseFamilyName);
FontFamily latin = FindExact(collection.Families, latinFamilyName);
if (chinese == null || latin == null)
{
collection.Dispose();
reason = "未安装报告所需的精确字体族:" + (chinese == null ? chineseFamilyName : latinFamilyName) + "。";
return false;
}
resolution = new SmoothingFontResolution(collection, chinese, latin, chineseFamilyName, latinFamilyName);
return true;
}
public RectangleF MeasureMixedText(SmoothingFontResolution resolution, string text, float points)
{
if (resolution == null) throw new ArgumentNullException(nameof(resolution));
if (string.IsNullOrEmpty(text) || points <= 0f) return RectangleF.Empty;
using (var bitmap = new Bitmap(1, 1))
using (Graphics graphics = Graphics.FromImage(bitmap))
using (var format = (StringFormat)StringFormat.GenericTypographic.Clone())
{
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
float width = 0f, height = 0f;
foreach (TextRun run in SplitRuns(text))
{
using (Font font = resolution.CreateFont(run.IsChinese ? resolution.ChineseFamily : resolution.LatinFamily, points))
{
SizeF size = graphics.MeasureString(run.Text, font, PointF.Empty, format);
width += size.Width;
height = Math.Max(height, size.Height);
}
}
return new RectangleF(0f, 0f, width, height);
}
}
internal static IReadOnlyList SplitRuns(string text)
{
var runs = new List();
if (string.IsNullOrEmpty(text)) return runs;
int start = 0;
bool isChinese = IsChinese(text[0]);
for (int index = 1; index < text.Length; index++)
{
bool currentIsChinese = IsChinese(text[index]);
if (currentIsChinese == isChinese) continue;
runs.Add(new TextRun(text.Substring(start, index - start), isChinese));
start = index;
isChinese = currentIsChinese;
}
runs.Add(new TextRun(text.Substring(start), isChinese));
return runs;
}
private static FontFamily FindExact(IReadOnlyList families, string name)
{
for (int index = 0; index < families.Count; index++)
{
FontFamily family = families[index];
if (string.Equals(family.Name, name, StringComparison.Ordinal) ||
string.Equals(family.GetName(1033), name, StringComparison.Ordinal)) return family;
}
return null;
}
private static bool IsChinese(char value)
{
return (value >= 0x3400 && value <= 0x4dbf) || (value >= 0x4e00 && value <= 0x9fff) ||
(value >= 0xf900 && value <= 0xfaff) || value == 0x3002 || value == 0xff0c || value == 0xff1a;
}
internal sealed class TextRun
{
public TextRun(string text, bool isChinese) { Text = text; IsChinese = isChinese; }
public string Text { get; }
public bool IsChinese { get; }
}
}
/// 一次报告导出所使用的精确字体族;释放时同时释放字体集合。
public sealed class SmoothingFontResolution : IDisposable
{
private readonly InstalledFontCollection _collection;
private readonly string _chineseFamilyName;
private readonly string _latinFamilyName;
internal SmoothingFontResolution(
InstalledFontCollection collection,
FontFamily chineseFamily,
FontFamily latinFamily,
string chineseFamilyName,
string latinFamilyName)
{
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
ChineseFamily = chineseFamily ?? throw new ArgumentNullException(nameof(chineseFamily));
LatinFamily = latinFamily ?? throw new ArgumentNullException(nameof(latinFamily));
_chineseFamilyName = chineseFamilyName ?? throw new ArgumentNullException(nameof(chineseFamilyName));
_latinFamilyName = latinFamilyName ?? throw new ArgumentNullException(nameof(latinFamilyName));
}
public string ChineseFamilyName => _chineseFamilyName;
public string LatinFamilyName => _latinFamilyName;
internal FontFamily ChineseFamily { get; }
internal FontFamily LatinFamily { get; }
internal Font CreateFont(FontFamily family, float points)
{
return new Font(family, points, FontStyle.Regular, GraphicsUnit.Point);
}
public void Dispose()
{
_collection.Dispose();
}
}