public static bool TryGetGlyphMetrics(string fontFaceName, string text, FontSizeUnit fontSizeUnit, float size, out GlyphMetrics glyphMetrics) { return(Gdi32FontPool.GetPoolingFont( faceName: fontFaceName, fontSizeUnit: fontSizeUnit, size: size, weight: FW_NORMAL, italic: 0, underline: 0, strikeOut: 0, charSet: 1, fontQuality: Gdi32FontQuality.Default ).TryGetGlyphMetrics(text, out glyphMetrics)); }
public static FontOutline?GetOutline(string fontFaceName, string text, OutlineMode outlineMode) { return(Gdi32FontPool.GetPoolingFont( faceName: fontFaceName, fontSizeUnit: FontSizeUnit.Pixel, size: 12, weight: FW_NORMAL, italic: 0, underline: 0, strikeOut: 0, charSet: 1, fontQuality: Gdi32FontQuality.Default ).GetOutline(text, outlineMode)); }
public static bool ExistsOutline(string fontFaceName, string text, out uint glyphIndex) { return(Gdi32FontPool.GetPoolingFont( faceName: fontFaceName, fontSizeUnit: FontSizeUnit.Pixel, size: 12, weight: FW_NORMAL, italic: 0, underline: 0, strikeOut: 0, charSet: 1, fontQuality: Gdi32FontQuality.Default ).ExistsOutline(text, out glyphIndex)); }
public FontOutline?GetOutline(string text, OutlineMode outlineMode) { if (SharedFont.OutlineTextMetric is null) { return(null); } // 参考文献 // http://marupeke296.com/TIPS_Main.html // http://marupeke296.com/WINT_GetGlyphOutline.html // http://marupeke296.com/DXG_No67_NewFont.html // https://msdn.microsoft.com/ja-jp/library/cc410385.aspx // https://msdn.microsoft.com/ja-jp/library/cc428640.aspx // https://www11.atwiki.jp/slice/pages/78.html // http://dendrocopos.jp/tips/win32.html // http://www.geocities.co.jp/Playtown-Dice/9391/program/win04.html // http://misohena.jp/article/ggo_trap/index.html // https://oshiete.goo.ne.jp/qa/4743793.html // http://eternalwindows.jp/graphics/bitmap/bitmap12.html // https://social.msdn.microsoft.com/Forums/ja-JP/3442d813-823a-449a-993e-7fc073aea949/opentype?forum=vcgeneralja // http://phys.cool.coocan.jp/physjpn/htextmetric.htm // https://docs.microsoft.com/ja-jp/windows/desktop/api/wingdi/ns-wingdi-_outlinetextmetricw // // 参考資料 // https://support.microsoft.com/en-us/help/87115/how-to-getglyphoutline-native-buffer-format // http://kone.vis.ne.jp/diary/diaryb08.html // https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd144891(v=vs.85).aspx // try { GLYPHMETRICS metrics = new GLYPHMETRICS(); MAT2 matrix = new MAT2(); matrix.eM11.value = 1; matrix.eM12.value = 0; matrix.eM21.value = 0; matrix.eM22.value = 1; using (var hdc = DeviceContextSafeHandle.CreateMesurementDeviceContext()) { // アウトライン取得用のフォントを生成。この時フォントサイズは制御店のメッシュサイズと一致させる。 // 一致しない場合は取得されるアウトラインの精度が著しく低下する場合がある。 var outlineAccessFont = Gdi32FontPool.GetPoolingFont(Key.FaceName, FontSizeUnit.Pixel, SharedFont.OutlineTextMetric.Value.otmEMSquare, Key.Weight, Key.Italic, Key.Underline, Key.StrikeOut, Key.CharSet, Key.FontQuality); if (outlineAccessFont.SharedFont.OutlineTextMetric is null) { return(null); } var selectResult = NativeApi.SelectObject(hdc, outlineAccessFont.Handle); if (selectResult == IntPtr.Zero) { throw new Win32Exception(); } NativeApi.GetCharacterPlacement(hdc, text, GCPFlags.GCP_GLYPHSHAPE, out var characterPlacement); if (characterPlacement.Glyphs is null || characterPlacement.Glyphs.Length == 0) { return(null); } var size = NativeApi.GetTextExtentPoint(hdc, text); uint glyphIndex = (uint)characterPlacement.Glyphs[0]; uint format = GGO_GLYPH_INDEX | (outlineMode == OutlineMode.Bezier ? GGO_BEZIER : GGO_NATIVE); int bufferSize = (int)NativeApi.GetGlyphOutline(hdc, glyphIndex, format, out metrics, 0, IntPtr.Zero, ref matrix); if (bufferSize <= 0) { return(null); } IntPtr buffer = Marshal.AllocHGlobal(bufferSize); List <TtPolygon> ttPolygons = new List <TtPolygon>(); try { uint ret = NativeApi.GetGlyphOutline(hdc, glyphIndex, format, out metrics, (uint)bufferSize, buffer, ref matrix); if (ret == GDI_ERROR) { throw new Win32Exception(Marshal.GetLastWin32Error()); } int polygonHeaderSize = Marshal.SizeOf(typeof(TTPOLYGONHEADER)); int curveHeaderSize = Marshal.SizeOf(typeof(TTPOLYCURVEHEADER)); int pointFxSize = Marshal.SizeOf(typeof(POINTFX)); int index = 0; while (index < bufferSize) { TTPOLYGONHEADER header = (TTPOLYGONHEADER)Marshal.PtrToStructure(new IntPtr(buffer.ToInt64() + index), typeof(TTPOLYGONHEADER)); POINTFX startPoint = header.pfxStart; // サイズをfontEmSquareで指定して内部メッシュと一致させているので、正しくできている限り端数は生じない。 Debug.Assert(startPoint.x.fract == 0); Debug.Assert(startPoint.y.fract == 0); int endCurvesIndex = index + header.cb; index += polygonHeaderSize; List <TtPolygonCurve> ttPolygonCurves = new List <TtPolygonCurve>(); while (index < endCurvesIndex) { TTPOLYCURVEHEADER curveHeader = (TTPOLYCURVEHEADER)Marshal.PtrToStructure(new IntPtr(buffer.ToInt64() + index), typeof(TTPOLYCURVEHEADER)); index += curveHeaderSize; TtPolygonPoint[] curvePoints = new TtPolygonPoint[curveHeader.cpfx]; for (int i = 0; i < curveHeader.cpfx; i++) { var curvePoint = (POINTFX)Marshal.PtrToStructure(new IntPtr(buffer.ToInt64() + index), typeof(POINTFX)); // サイズをfontEmSquareで指定して内部メッシュと一致させているので、正しくできている限り端数は生じない。 // ただし、ベジェ曲線に変換している場合は制御店の創出により端数が生じる。 Debug.Assert(outlineMode != OutlineMode.Native || curvePoint.x.fract == 0); Debug.Assert(outlineMode != OutlineMode.Native || curvePoint.y.fract == 0); curvePoints[i] = curvePoint.ToTtPolygonPoint(outlineAccessFont.SharedFont.OutlineTextMetric.Value, metrics); index += pointFxSize; } TtPrimitiveTypes type; switch (curveHeader.wType) { case TT_PRIM_LINE: type = TtPrimitiveTypes.Line; break; case TT_PRIM_QSPLINE: Debug.Assert(outlineMode == OutlineMode.Native); type = TtPrimitiveTypes.QuadraticBezierSpline; break; case TT_PRIM_CSPLINE: Debug.Assert(outlineMode == OutlineMode.Bezier); type = TtPrimitiveTypes.CubicBezierSpline; break; default: throw new FormatException(); } ttPolygonCurves.Add(new TtPolygonCurve(type, curvePoints.ToImmutableArray())); } ttPolygons.Add(new TtPolygon(startPoint.ToTtPolygonPoint(outlineAccessFont.SharedFont.OutlineTextMetric.Value, metrics), ttPolygonCurves.ToImmutableArray())); } } finally { Marshal.FreeHGlobal(buffer); } Debug.Assert(metrics.gmCellIncX >= 0); Debug.Assert(metrics.gmCellIncY >= 0); var glyphMetrics = new GlyphMetrics(metrics.gmBlackBoxX, metrics.gmBlackBoxY, metrics.gmptGlyphOrigin.x, metrics.gmptGlyphOrigin.y, metrics.gmCellIncX, metrics.gmCellIncY); return(new FontOutline( outlineAccessFont.SharedFont.OutlineTextMetric.Value.otmEMSquare, outlineAccessFont.SharedFont.OutlineTextMetric.Value.otmMacAscent, glyphMetrics, ttPolygons.ToImmutableArray() )); } } catch (Win32Exception ex) { Debug.WriteLine(ex); return(null); } }