unsafe public void GetGlyphPlacements( IntPtr textString, ushort *clusterMap, /* textLength */ ushort *textProps, /* textLength */ uint textLength, ushort *glyphIndices, /* glyphCount */ ushort *glyphProps, /* glyphCount */ uint glyphCount, Font font, double fontEmSize, double scalingFactor, bool isSideways, bool isRightToLeft, CultureInfo cultureInfo, DWriteFontFeature[][] features, uint[] featureRangeLengths, TextFormattingMode textFormattingMode, ItemProps itemProps, float pixelsPerDip, int *glyphAdvances, /* glyphCount */ [Out] out GlyphOffset[] glyphOffsets ) { // Shaping should not have taken place if ScriptAnalysis is NULL! Debug.Assert(itemProps.ScriptAnalysis != null); // These are control characters and WPF will not display control characters. if (GetScriptShapes(itemProps) != DWriteScriptShapes.Default) { GetGlyphPlacementsForControlCharacters( textString, textLength, font, textFormattingMode, fontEmSize, scalingFactor, isSideways, pixelsPerDip, glyphCount, glyphIndices, glyphAdvances, out glyphOffsets ); } else { float[] dwriteGlyphAdvances = new float[glyphCount]; DWriteGlyphOffset[] dwriteGlyphOffsets = null; var featureRangeLengthsNonNull = featureRangeLengths != null ? featureRangeLengths : new uint[0]; fixed(uint *pFeatureRangeLengthsPinned = featureRangeLengthsNonNull) { GCHandle[] dwriteFontFeaturesGCHandles = null; uint featureRanges = 0; IntPtr[] dwriteTypographicFeatures = null; IntPtr dwriteTypographicFeaturesMemory = IntPtr.Zero; uint * pFeatureRangeLengths = null; if (features != null) { pFeatureRangeLengths = pFeatureRangeLengthsPinned; featureRanges = (uint)featureRangeLengths.Length; dwriteTypographicFeatures = new IntPtr[featureRanges]; dwriteFontFeaturesGCHandles = new GCHandle[featureRanges]; dwriteTypographicFeatures = new IntPtr[featureRanges]; dwriteTypographicFeaturesMemory = Marshal.AllocCoTaskMem(Marshal.SizeOf <DWriteTypographicFeatures>() * (int)featureRanges); } FontFace fontFace = font.GetFontFace(); IntPtr scriptAnalysis = itemProps.ScriptAnalysisCoTaskMem(); try { string localeName = cultureInfo.IetfLanguageTag; DWriteMatrix transform = Factory.GetIdentityTransform(); if (features != null) { for (uint i = 0; i < featureRanges; ++i) { dwriteFontFeaturesGCHandles[i] = GCHandle.Alloc(features[i], GCHandleType.Pinned); var new_feature = new DWriteTypographicFeatures(); new_feature.features = dwriteFontFeaturesGCHandles[i].AddrOfPinnedObject(); new_feature.featureCount = features[i].Length; dwriteTypographicFeatures[i] = dwriteTypographicFeaturesMemory + (int)i * Marshal.SizeOf <DWriteTypographicFeatures>(); Marshal.StructureToPtr <DWriteTypographicFeatures>(new_feature, dwriteTypographicFeatures[i], false); } } float fontEmSizeFloat = (float)fontEmSize; dwriteGlyphOffsets = new DWriteGlyphOffset[glyphCount]; if (textFormattingMode == TextFormattingMode.Ideal) { try { _textAnalyzer.GetGlyphPlacements( textString, clusterMap, textProps, textLength, glyphIndices, glyphProps, glyphCount, fontFace.DWriteFontFaceNoAddRef, fontEmSizeFloat, isSideways, isRightToLeft, scriptAnalysis, localeName, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, dwriteGlyphAdvances, dwriteGlyphOffsets ); } catch (ArgumentException) { // If pLocaleName is unsupported (e.g. "prs-af"), DWrite returns E_INVALIDARG. // Try again with the default mapping. _textAnalyzer.GetGlyphPlacements( textString, clusterMap, textProps, textLength, glyphIndices, glyphProps, glyphCount, fontFace.DWriteFontFaceNoAddRef, fontEmSizeFloat, isSideways, isRightToLeft, scriptAnalysis, null, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, dwriteGlyphAdvances, dwriteGlyphOffsets ); } } else { Debug.Assert(textFormattingMode == TextFormattingMode.Display); try { _textAnalyzer.GetGdiCompatibleGlyphPlacements( textString, clusterMap, textProps, textLength, glyphIndices, glyphProps, glyphCount, fontFace.DWriteFontFaceNoAddRef, fontEmSizeFloat, pixelsPerDip, ref transform, false, isSideways, isRightToLeft, scriptAnalysis, localeName, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, dwriteGlyphAdvances, dwriteGlyphOffsets ); } catch (ArgumentException) { // If pLocaleName is unsupported (e.g. "prs-af"), DWrite returns E_INVALIDARG. // Try again with the default mapping. _textAnalyzer.GetGdiCompatibleGlyphPlacements( textString, clusterMap, textProps, textLength, glyphIndices, glyphProps, glyphCount, fontFace.DWriteFontFaceNoAddRef, fontEmSizeFloat, pixelsPerDip, ref transform, false, isSideways, isRightToLeft, scriptAnalysis, null, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, dwriteGlyphAdvances, dwriteGlyphOffsets ); } } glyphOffsets = new GlyphOffset[glyphCount]; if (textFormattingMode == TextFormattingMode.Ideal) { for (uint i = 0; i < glyphCount; ++i) { glyphAdvances[i] = (int)Math.Round(dwriteGlyphAdvances[i] * fontEmSize * scalingFactor / fontEmSizeFloat); glyphOffsets[i].du = (int)(dwriteGlyphOffsets[i].AdvanceOffset * scalingFactor); glyphOffsets[i].dv = (int)(dwriteGlyphOffsets[i].AscenderOffset * scalingFactor); } } else { for (uint i = 0; i < glyphCount; ++i) { glyphAdvances[i] = (int)Math.Round(dwriteGlyphAdvances[i] * scalingFactor); glyphOffsets[i].du = (int)(dwriteGlyphOffsets[i].AdvanceOffset * scalingFactor); glyphOffsets[i].dv = (int)(dwriteGlyphOffsets[i].AscenderOffset * scalingFactor); } } } finally { Marshal.FreeCoTaskMem(scriptAnalysis); if (features != null) { for (uint i = 0; i < featureRanges; ++i) { dwriteFontFeaturesGCHandles[i].Free(); } Marshal.FreeCoTaskMem(dwriteTypographicFeaturesMemory); } fontFace.Release(); } } } }
public unsafe void GetGlyphs( IntPtr textString, uint textLength, Font font, ushort blankGlyphIndex, bool isSideways, bool isRightToLeft, CultureInfo cultureInfo, DWriteFontFeature[][] features, uint[] featureRangeLengths, uint maxGlyphCount, TextFormattingMode textFormattingMode, ItemProps itemProps, ushort *clusterMap, /* textLength */ ushort *textProps, /* textLength */ ushort *glyphIndices, /* maxGlyphCount */ ushort *glyphProps, /* maxGlyphCount */ int *pfCanGlyphAlone, /* textLength */ [Out] out uint actualGlyphCount ) { // Shaping should not have taken place if ScriptAnalysis is NULL! Debug.Assert(itemProps.ScriptAnalysis != null); // These are control characters and WPF will not display control characters. if (GetScriptShapes(itemProps) != DWriteScriptShapes.Default) { FontFace fontFace = font.GetFontFace(); try { GetBlankGlyphsForControlCharacters( textString, textLength, fontFace, blankGlyphIndex, maxGlyphCount, clusterMap, glyphIndices, pfCanGlyphAlone, out actualGlyphCount ); } finally { fontFace.Release(); } } else { string localeName = cultureInfo.IetfLanguageTag; var featureRangeLengthsNonNull = (featureRangeLengths != null) ? featureRangeLengths : new uint[0]; fixed(uint *pFeatureRangeLengthsPinned = featureRangeLengthsNonNull) { uint *pFeatureRangeLengths = null; uint featureRanges = 0; GCHandle[] dwriteFontFeaturesGCHandles = null; IntPtr[] dwriteTypographicFeatures = null; IntPtr dwriteTypographicFeaturesMemory = IntPtr.Zero; if (features != null) { pFeatureRangeLengths = pFeatureRangeLengthsPinned; featureRanges = (uint)featureRangeLengths.Length; dwriteFontFeaturesGCHandles = new GCHandle[featureRanges]; dwriteTypographicFeatures = new IntPtr[featureRanges]; dwriteTypographicFeaturesMemory = Marshal.AllocCoTaskMem(Marshal.SizeOf <DWriteTypographicFeatures>() * (int)featureRanges); } FontFace fontFace = font.GetFontFace(); try { if (features != null) { for (uint i = 0; i < featureRanges; ++i) { dwriteFontFeaturesGCHandles[i] = GCHandle.Alloc(features[i], GCHandleType.Pinned); var new_feature = new DWriteTypographicFeatures(); new_feature.features = dwriteFontFeaturesGCHandles[i].AddrOfPinnedObject(); new_feature.featureCount = features[i].Length; dwriteTypographicFeatures[i] = dwriteTypographicFeaturesMemory + (int)i * Marshal.SizeOf <DWriteTypographicFeatures>(); Marshal.StructureToPtr <DWriteTypographicFeatures>(new_feature, dwriteTypographicFeatures[i], false); } } uint glyphCount = 0; IntPtr dwriteScriptAnalysis = itemProps.ScriptAnalysisCoTaskMem(); int hr = _textAnalyzer.GetGlyphs( textString, /*checked*/ ((uint)textLength), fontFace.DWriteFontFaceNoAddRef, isSideways, isRightToLeft, dwriteScriptAnalysis, localeName, itemProps.NumberSubstitution, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, /*checked*/ ((uint)maxGlyphCount), clusterMap, textProps, glyphIndices, glyphProps, out glyphCount ); if (unchecked ((int)0x80070057) == hr) // E_INVALIDARG { // If pLocaleName is unsupported (e.g. "prs-af"), DWrite returns E_INVALIDARG. // Try again with the default mapping. hr = _textAnalyzer.GetGlyphs( textString, /*checked*/ ((uint)textLength), fontFace.DWriteFontFaceNoAddRef, isSideways, isRightToLeft, dwriteScriptAnalysis, null, itemProps.NumberSubstitution, dwriteTypographicFeatures, pFeatureRangeLengths, featureRanges, /*checked*/ ((uint)maxGlyphCount), clusterMap, textProps, glyphIndices, glyphProps, out glyphCount ); } Marshal.FreeCoTaskMem(dwriteScriptAnalysis); if (features != null) { for (uint i = 0; i < featureRanges; ++i) { dwriteFontFeaturesGCHandles[i].Free(); } Marshal.FreeCoTaskMem(dwriteTypographicFeaturesMemory); } if (hr == unchecked ((int)0x8007007a)) // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) { // Actual glyph count is not returned by DWrite unless the call tp GetGlyphs succeeds. // It must be re-estimated in case the first estimate was not adequate. // The following calculation is a refactoring of DWrite's logic ( 3*stringLength/2 + 16) after 3 retries. // We'd rather go directly to the maximum buffer size we are willing to allocate than pay the cost of continuously retrying. actualGlyphCount = 27 * maxGlyphCount / 8 + 76; } else { Marshal.ThrowExceptionForHR(hr); if (pfCanGlyphAlone != null) { for (uint i = 0; i < textLength; ++i) { pfCanGlyphAlone[i] = (DWriteBitfieldUtils.ShapingText_IsShapedAlone(textProps[i])) ? 1 : 0; } } actualGlyphCount = glyphCount; } } finally { fontFace.Release(); } } } }