提取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();

posted @ 2025-09-07 21:59  JohnYang819  阅读(13)  评论(0)    收藏  举报