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(); } } } }
unsafe void GetGlyphPlacementsForControlCharacters( IntPtr pTextString, uint textLength, Font font, TextFormattingMode textFormattingMode, double fontEmSize, double scalingFactor, bool isSideways, float pixelsPerDip, uint glyphCount, ushort *pGlyphIndices, /* [in] glyphCount */ int *glyphAdvances, /* glyphCount */ [Out] out GlyphOffset[] glyphOffsets ) { if (glyphCount != textLength) { throw new ArgumentException("glyphCount must equal textLength"); } glyphOffsets = new GlyphOffset[textLength]; FontFace fontFace = font.GetFontFace(); try { int hyphenAdvanceWidth = -1; for (uint i = 0; i < textLength; ++i) { // LS will sometimes replace soft hyphens (which are invisible) with hyphens (which are visible). // So if we are in this code then LS actually did this replacement and we need to display the hyphen (x002D) if (Marshal.ReadInt16(pTextString, (int)i * 2) == CharHyphen) { if (hyphenAdvanceWidth == -1) { GlyphMetrics glyphMetrics; if (textFormattingMode == TextFormattingMode.Ideal) { fontFace.GetDesignGlyphMetrics(pGlyphIndices + i, 1, &glyphMetrics); } else { fontFace.GetDisplayGlyphMetrics( pGlyphIndices + i, 1, &glyphMetrics, (float)fontEmSize, textFormattingMode != TextFormattingMode.Display, isSideways, pixelsPerDip); } double approximatedHyphenAW = Math.Round(glyphMetrics.AdvanceWidth * fontEmSize / font.Metrics.DesignUnitsPerEm * pixelsPerDip) / pixelsPerDip; hyphenAdvanceWidth = (int)Math.Round(approximatedHyphenAW * scalingFactor); } glyphAdvances[i] = hyphenAdvanceWidth; } else { glyphAdvances[i] = 0; } glyphOffsets[i].du = 0; glyphOffsets[i].dv = 0; } } finally { fontFace.Release(); } }