public bool ExistsOutline(string text, out uint glyphIndex) { 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 selectResult = NativeApi.SelectObject(hdc, 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) { glyphIndex = 0; return(false); } glyphIndex = (uint)characterPlacement.Glyphs[0]; int bufferSize = (int)NativeApi.GetGlyphOutline(hdc, glyphIndex, GGO_GLYPH_INDEX | GGO_NATIVE, out GLYPHMETRICS metrics, 0, IntPtr.Zero, ref matrix); return(bufferSize > 0); } }
public byte[] GetFontData() { using (var hdc = DeviceContextSafeHandle.CreateMesurementDeviceContext()) { var selectResult = NativeApi.SelectObject(hdc, Handle); if (selectResult == IntPtr.Zero) { throw new Win32Exception(); } var dataLength = GetFontData(hdc, 0, 0, null, 0); if (dataLength == GDI_ERROR) { throw new Win32Exception(); } var dataBuffer = new byte[dataLength]; var writeLength = GetFontData(hdc, 0, 0, dataBuffer, dataLength); if (writeLength == GDI_ERROR) { throw new Win32Exception(); } return(dataBuffer); } }
public static TEXTMETRIC GetTextMetrics(DeviceContextSafeHandle hdc) { if (GetTextMetrics(hdc, out TEXTMETRIC textmetric)) { return(textmetric); } throw new Win32Exception(Marshal.GetLastWin32Error()); }
public static void GetCharacterPlacement(DeviceContextSafeHandle hdc, string text, GCPFlags flags, out GcpResults gcpResults) { int len = text.Length; gcpResults = new GcpResults { Order = new int[len], Dx = new int[len], CaretPos = new int[len], Class = new byte[len], Glyphs = new short[len], }; GCHandle ordHnd = GCHandle.Alloc(gcpResults.Order, GCHandleType.Pinned); GCHandle dxHnd = GCHandle.Alloc(gcpResults.Dx, GCHandleType.Pinned); GCHandle carHnd = GCHandle.Alloc(gcpResults.CaretPos, GCHandleType.Pinned); GCHandle clsHnd = GCHandle.Alloc(gcpResults.Class, GCHandleType.Pinned); GCHandle glyHnd = GCHandle.Alloc(gcpResults.Glyphs, GCHandleType.Pinned); try { GCP_RESULTS rs = new GCP_RESULTS { StructSize = Marshal.SizeOf(typeof(GCP_RESULTS)), OutString = new String('\0', len + 2), Order = ordHnd.AddrOfPinnedObject(), Dx = dxHnd.AddrOfPinnedObject(), CaretPos = carHnd.AddrOfPinnedObject(), Class = clsHnd.AddrOfPinnedObject(), Glyphs = glyHnd.AddrOfPinnedObject(), GlyphCount = len, MaxFit = 0 }; uint r = GetCharacterPlacement(hdc, text, len, 0, ref rs, (uint)flags); if (r == 0 && Marshal.GetLastWin32Error() != 0) { throw new Win32Exception(); } gcpResults.OutString = rs.OutString; gcpResults.MaxFit = rs.MaxFit; return; } finally { ordHnd.Free(); dxHnd.Free(); carHnd.Free(); clsHnd.Free(); glyHnd.Free(); } }
public static Size GetTextExtentPoint(DeviceContextSafeHandle hdc, string text) { if (GetTextExtentPoint32(hdc, text, text.Length, out var size)) { return(size.ToSize()); } else { return(Size.Empty); } }
public bool TryGetGlyphMetrics(string text, out GlyphMetrics 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 selectResult = NativeApi.SelectObject(hdc, 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) { glyphMetrics = default; return(false); } uint glyphIndex = (uint)characterPlacement.Glyphs[0]; int bufferSize = (int)NativeApi.GetGlyphOutline(hdc, glyphIndex, GGO_GLYPH_INDEX | GGO_NATIVE, out GLYPHMETRICS metrics, 0, IntPtr.Zero, ref matrix); if (bufferSize <= 0) { glyphMetrics = default; return(false); } glyphMetrics = new GlyphMetrics(metrics.gmBlackBoxX, metrics.gmBlackBoxY, metrics.gmptGlyphOrigin.x, metrics.gmptGlyphOrigin.y, metrics.gmCellIncX, metrics.gmCellIncY); return(true); } }
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); } }
/// <summary> /// デバイスコンテキストの現在のフォントのOutlineTextMetricを取得する。不正なハンドルや対象のフォントが字形を含まない場合はnullを返却する。 /// </summary> /// <param name="hdc">デバイスコンテキスト</param> /// <param name="win32ErrorCode">エラーコード</param> /// <returns>OutlineTextMetric</returns> public static bool TryGetOutlineTextMetrics(DeviceContextSafeHandle hdc, out OutlineTextMetric outlineTextMetric, out int win32ErrorCode) { uint bufferSize = GetOutlineTextMetrics(hdc, 0, IntPtr.Zero); if (bufferSize == 0) { outlineTextMetric = default; win32ErrorCode = Marshal.GetLastWin32Error(); return(false); } IntPtr buffer = Marshal.AllocHGlobal((int)bufferSize); try { var getOutlineTextMetricsResult = GetOutlineTextMetrics(hdc, bufferSize, buffer); if (getOutlineTextMetricsResult == 0) { outlineTextMetric = default; win32ErrorCode = Marshal.GetLastWin32Error(); return(false); } OUTLINETEXTMETRIC nativeOutlineTextMetrics = (OUTLINETEXTMETRIC)Marshal.PtrToStructure(buffer, typeof(OUTLINETEXTMETRIC)); outlineTextMetric = new OutlineTextMetric { otmSize = nativeOutlineTextMetrics.otmSize, otmTextMetrics = nativeOutlineTextMetrics.otmTextMetrics, otmFiller = nativeOutlineTextMetrics.otmFiller, otmPanoseNumber = nativeOutlineTextMetrics.otmPanoseNumber, otmfsSelection = nativeOutlineTextMetrics.otmfsSelection, otmfsType = nativeOutlineTextMetrics.otmfsType, otmsCharSlopeRise = nativeOutlineTextMetrics.otmsCharSlopeRise, otmsCharSlopeRun = nativeOutlineTextMetrics.otmsCharSlopeRun, otmItalicAngle = nativeOutlineTextMetrics.otmItalicAngle, otmEMSquare = nativeOutlineTextMetrics.otmEMSquare, otmAscent = nativeOutlineTextMetrics.otmAscent, otmDescent = nativeOutlineTextMetrics.otmDescent, otmLineGap = nativeOutlineTextMetrics.otmLineGap, otmsCapEmHeight = nativeOutlineTextMetrics.otmsCapEmHeight, otmsXHeight = nativeOutlineTextMetrics.otmsXHeight, otmrcFontBox = nativeOutlineTextMetrics.otmrcFontBox, otmMacAscent = nativeOutlineTextMetrics.otmMacAscent, otmMacDescent = nativeOutlineTextMetrics.otmMacDescent, otmMacLineGap = nativeOutlineTextMetrics.otmMacLineGap, otmusMinimumPPEM = nativeOutlineTextMetrics.otmusMinimumPPEM, otmptSubscriptSize = nativeOutlineTextMetrics.otmptSubscriptSize.ToPoint(), otmptSubscriptOffset = nativeOutlineTextMetrics.otmptSubscriptOffset.ToPoint(), otmptSuperscriptSize = nativeOutlineTextMetrics.otmptSuperscriptSize.ToPoint(), otmptSuperscriptOffset = nativeOutlineTextMetrics.otmptSuperscriptOffset.ToPoint(), otmsStrikeoutSize = nativeOutlineTextMetrics.otmsStrikeoutSize, otmsStrikeoutPosition = nativeOutlineTextMetrics.otmsStrikeoutPosition, otmsUnderscoreSize = nativeOutlineTextMetrics.otmsUnderscoreSize, otmsUnderscorePosition = nativeOutlineTextMetrics.otmsUnderscorePosition, otmpFamilyName = Marshal.PtrToStringUni(new IntPtr(buffer.ToInt64() + nativeOutlineTextMetrics.otmpFamilyName.ToInt64())), otmpFaceName = Marshal.PtrToStringUni(new IntPtr(buffer.ToInt64() + nativeOutlineTextMetrics.otmpFaceName.ToInt64())), otmpStyleName = Marshal.PtrToStringUni(new IntPtr(buffer.ToInt64() + nativeOutlineTextMetrics.otmpStyleName.ToInt64())), otmpFullName = Marshal.PtrToStringUni(new IntPtr(buffer.ToInt64() + nativeOutlineTextMetrics.otmpFullName.ToInt64())), }; Debug.Assert(outlineTextMetric.otmSize == bufferSize); win32ErrorCode = 0; return(true); } finally { Marshal.FreeHGlobal(buffer); } }
static extern bool GetTextExtentPoint32(DeviceContextSafeHandle hdc, string lpString, int cbString, out SIZE lpSize);
static extern uint GetCharacterPlacement(DeviceContextSafeHandle hdc, [MarshalAs(UnmanagedType.LPWStr)] string lpString, int nCount, int nMaxExtent, ref GCP_RESULTS lpResults, uint dwFlags);
static extern bool GetTextMetrics(DeviceContextSafeHandle hdc, out TEXTMETRIC lptm);
static extern uint GetOutlineTextMetrics(DeviceContextSafeHandle hdc, uint cbData, IntPtr lpOTM);
public static extern uint GetGlyphOutline(DeviceContextSafeHandle hdc, uint uChar, uint uFormat, out GLYPHMETRICS lpgm, uint cbBuffer, IntPtr lpvBuffer, ref MAT2 lpmat2);
public static extern IntPtr SelectObject(DeviceContextSafeHandle hdc, FontSafeHandle obj);