提取windows字体轮廓线
class PointCollectSink : CallbackBase,SimplifiedGeometrySink
{
public class GlyphContour
{
public List<RawVector2> Points = new List<RawVector2>();
public bool IsHole;
public GlyphContour Parent;
public List<GlyphContour> Holes = new List<GlyphContour>();
}
public List<GlyphContour> Contours { get; } = new List<GlyphContour>();
GlyphContour _currentContour;
public List<RawVector2> Points
{
get
{
return _points;
}
}
List<RawVector2> _points;
public PointCollectSink(List<RawVector2> points)
{
_points = points;
}
public void AddBeziers(BezierSegment[] beziers)
{
if(_currentContour!=null && beziers != null)
{
foreach(var b in beziers)
{
_currentContour.Points.Add(b.Point1);
_currentContour.Points.Add(b.Point2);
_currentContour.Points.Add(b.Point3);
}
}
}
public void AddLines(RawVector2[] ointsRef)
{
_points.AddRange(ointsRef);
if(_currentContour!=null && ointsRef != null)
{
_currentContour.Points.AddRange(ointsRef);
}
}
public void BeginFigure(RawVector2 startPoint, FigureBegin figureBegin)
{
_points.Add(startPoint);
_currentContour = new GlyphContour();
_currentContour.Points.Add(startPoint);
}
public void Close()
{
}
public void EndFigure(FigureEnd figureEnd)
{
if(figureEnd==FigureEnd.Closed && _points.Count > 0)
{
_points.Add(_points[0]);
}
if (_currentContour != null && figureEnd==FigureEnd.Closed && _currentContour.Points.Count > 0)
{
_currentContour.Points.Add(_currentContour.Points[0]);
}
float area = SignedArea(_currentContour.Points);
_currentContour.IsHole = area < 0;
Contours.Add(_currentContour);
_currentContour = null;
}
private float SignedArea(List<RawVector2> points)
{
float a = 0;
for(int i = 0; i < points.Count; i++)
{
var p1= points[i];
var p2 = points[(i + 1) % points.Count];
a += (p1.X*p2.Y-p1.Y*p2.X);
}
return a;
}
public void SetFillMode(FillMode fillMode)
{
}
public void SetSegmentFlags(PathSegment vertexFlags)
{
}
}
static void Main(string[] args)
{
//var a1 = new TestCrossEle();
//List<TestCrossEle> aa = new List<TestCrossEle>();
//aa.Add(a1);
//a1 = null;
char ch = 'A'; // 要测试的汉字
float emSize = 100.0f; // 字号 (DIP)
float flattenTolerance = 1f; // 展平精度(越小越精细)
using (var dwFactory = new SharpDX.DirectWrite.Factory())
using (var d2dFactory = new SharpDX.Direct2D1.Factory())
{
var fontCollection = dwFactory.GetSystemFontCollection(false);
// 候选的常见中文字体,按需增减
string[] candidates = new[]
{
"Microsoft YaHei", "SimSun", "NSimSun", "Microsoft JhengHei",
"PMingLiU", "MS Gothic", "Arial Unicode MS", "Noto Sans CJK SC"
};
FontFace chosenFace = null;
string chosenName = null;
// 逐个候选字体查找是否包含所需 glyph
foreach (var name in candidates)
{
int familyIndex;
if (!fontCollection.FindFamilyName(name, out familyIndex))
continue;
var family = fontCollection.GetFontFamily(familyIndex);
var font = family.GetFirstMatchingFont(FontWeight.Normal, FontStretch.Normal, FontStyle.Normal);
// 临时创建 FontFace 测试 glyph 是否存在
using (var testFace = new FontFace(font))
{
short[] glyphs = testFace.GetGlyphIndices(new int[] { ch });
if (glyphs != null && glyphs.Length > 0 && glyphs[0] != 0) // 注意:0 通常表示缺字
{
// 重新用该 Font 创建一个不在 using 中立即释放的 FontFace
chosenFace = new FontFace(font);
chosenName = name;
break;
}
}
}
// 如果候选列表没找到,退回系统默认第一个字体(仍然可能缺字)
if (chosenFace == null)
{
Console.WriteLine("候选字体中未找到 glyph,尝试使用系统第一个字体(可能仍缺字)。");
var fam = fontCollection.GetFontFamily(0);
var font = fam.GetFirstMatchingFont(FontWeight.Normal, FontStretch.Normal, FontStyle.Normal);
chosenFace = new FontFace(font);
chosenName = "(system fallback)";
}
Console.WriteLine("使用字体: " + chosenName);
// 获得 glyph indices
short[] glyphIndices = chosenFace.GetGlyphIndices(new int[] { ch });
Console.WriteLine("glyph index: " + (glyphIndices != null && glyphIndices.Length > 0 ? glyphIndices[0].ToString() : "null"));
// 如果 glyph index == 0 很可能缺字,需换字体
if (glyphIndices == null || glyphIndices.Length == 0 || glyphIndices[0] == 0)
{
Console.WriteLine("警告:选定字体不包含该字符的字形(glyph index == 0)。请改用支持中文的字体。");
}
else
{
// 写入 PathGeometry(务必 Close writer)
using (var path = new PathGeometry(d2dFactory))
{
using (var writer = path.Open())
{
chosenFace.GetGlyphRunOutline(
emSize, // em size
glyphIndices,
null, // advanceWidths
null, // offsets
glyphIndices.Length,
false, // isSideways
false, // isRightToLeft
writer
);
writer.Close();
}
// 展平并收集点(实现见下)
var collect = new PointCollectSink(new List<RawVector2>());
path.Simplify(GeometrySimplificationOption.Lines, Matrix3x2.Identity, flattenTolerance, collect);
Console.WriteLine($"展平后点数: {collect.Points.Count}");
// 注意:Direct2D 坐标系 Y 向下(如果你要用于 3D,可能要把 Y 翻转)
for (int i = 0; i < collect.Points.Count; i++)
{
var p = collect.Points[i];
Console.WriteLine($"{i}: {p.X:F2}, {p.Y:F2}");
}
foreach(var con in collect.Contours)
{
List<DbPt> ptss = (from n in con.Points select new DbPt(n.X,-n.Y)).ToList();
var liness = GMath.GetOutlines(ptss);
GuiFunction.GFunc.DebugSend(liness);
}
}
}
chosenFace.Dispose();
}
Console.WriteLine("完成,按任意键退出");
Console.ReadKey();
#####
愿你一寸一寸地攻城略地,一点一点地焕然一新
#####