public void DrawGlyphRun(float baselineOriginX, float baselineOriginY, Graphics.Direct2D.MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, ClientDrawingEffect clientDrawingEffect) { using (PathGeometry pathGeometry = _factory.CreatePathGeometry()) { using (GeometrySink sink = pathGeometry.Open()) { glyphRun.FontFace.GetGlyphRunOutline( glyphRun.EmSize, glyphRun.GlyphIndices, glyphRun.GlyphAdvances, glyphRun.GlyphOffsets, glyphRun.IsSideways, glyphRun.BidiLevel != 0, sink); sink.Close(); } CustomGeometrySink customSink = new CustomGeometrySink(); pathGeometry.Stream(customSink); customSink.Close(); System.Diagnostics.Debug.WriteLine(customSink.ToString()); Matrix3x2 matrix = new Matrix3x2(1, 0, 0, 1, baselineOriginX, baselineOriginY); using (TransformedGeometry transformedGeometry = _factory.CreateTransformedGeometry(pathGeometry, matrix)) { _renderTarget.DrawGeometry(_outlineBrush, 5, transformedGeometry); _renderTarget.FillGeometry(_fillBrush, transformedGeometry); } } }
public void DrawGlyphRun(float baselineOriginX, float baselineOriginY, Graphics.Direct2D.MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, ClientDrawingEffect clientDrawingEffect) { using (PathGeometry pathGeometry = _factory.CreatePathGeometry()) { using (GeometrySink sink = pathGeometry.Open()) { glyphRun.FontFace.GetGlyphRunOutline( glyphRun.EmSize, glyphRun.GlyphIndices, glyphRun.GlyphAdvances, glyphRun.GlyphOffsets, glyphRun.IsSideways, glyphRun.BidiLevel != 0, sink); sink.Close(); } CustomGeometrySink customSink = new CustomGeometrySink(); pathGeometry.Stream(customSink); customSink.Close(); System.Diagnostics.Debug.WriteLine(customSink.ToString()); SolidColorBrush brush = null; if (clientDrawingEffect != null) { ColorDrawingEffect drawingEffect = clientDrawingEffect as ColorDrawingEffect; if (drawingEffect != null) { brush = _renderTarget.CreateSolidColorBrush(drawingEffect.Color); } } Matrix3x2 matrix = new Matrix3x2(1, 0, 0, 1, baselineOriginX, baselineOriginY); using (TransformedGeometry transformedGeometry = _factory.CreateTransformedGeometry(pathGeometry, matrix)) { _renderTarget.FillGeometry(brush == null ? _defaultBrush : brush, transformedGeometry); } if (brush != null) brush.Dispose(); } }
private void RedrawText(DrawingContext dc) { var glyphChars = new List <ushort>(this.horizontalCells); var advanceWidths = new List <double>(this.horizontalCells); // Render line by line, attempting to render as long as properties remain the same or we reach the end of the line. for (var y = 0; y < this.verticalCells; ++y) { var runStart = 0; var x = 0; var allNull = true; var allEmpty = true; while (x < this.horizontalCells) { var currentGlyph = this.characters[x, y].Glyph; allNull &= currentGlyph == 0x0; allEmpty &= (currentGlyph == 0x0 || currentGlyph == 0x20); ++x; // Properties that can change and cause a run stop + render: // - Any of the basic character properties (bright/inverse/colors/etc) // - Hitting a visible cursor (as we may need to invert it explicitly -- we have to stop both at the cursor and immediately before it.) // - Hitting a "null" character (this may be a terminator for certain properties such as underlining/inverse/etc) if (x == this.horizontalCells || !this.characters[runStart, y].PropertiesEqual(this.characters[x, y]) || (this.RenderCursor && ((runStart, y) == this.Buffer.CursorPosition) || (x, y) == this.Buffer.CursorPosition) || (this.characters[x, y].Glyph == 0x0 && this.characters[runStart, y].Glyph != 0x0)) { var charCount = x - runStart; var startChar = this.characters[runStart, y]; var invert = startChar.Inverse && startChar.Glyph != 0x0; // this isn't super intuitive from the above but we'll stop a run if the cursor is visible so we can render it specially, // here that means potentially inverting it depending on our own 'blink' state. if (this.RenderCursor && charCount == 1 && (runStart, y) == this.Buffer.CursorPosition && this.cursorInverted) { invert = !invert; } Character.ColorInfo fg, bg; if (!invert) { (fg, bg) = this.GetCharacterColors(startChar); } else { (bg, fg) = this.GetCharacterColors(startChar); } var backgroundBrush = this.brushCache.GetBrush(bg.R, bg.G, bg.B); var foregroundBrush = this.brushCache.GetBrush(fg.R, fg.G, fg.B); dc.DrawRectangle(backgroundBrush, null, new Rect(runStart * this.CellWidth, y * this.CellHeight, charCount * this.CellWidth, this.CellHeight)); // if all characters are null and we're at EOL stop after rendering only the background // XXX: this feels REALLY hacky to me if (x == this.horizontalCells && allNull) { continue; } var glyphOrigin = new Point((runStart * this.CellWidth) + this.baselineOrigin.X, (y * this.CellHeight) + this.baselineOrigin.Y); if (startChar.Underline) { var underlineRectangle = new Rect(glyphOrigin.X, y * this.CellHeight + this.underlineY, charCount * this.CellWidth, this.underlineHeight); dc.DrawRectangle(foregroundBrush, null, underlineRectangle); } glyphChars.Clear(); advanceWidths.Clear(); for (var c = runStart; c < x; ++c) { if (!this.glyphTypeface.CharacterToGlyphMap.TryGetValue((char)this.characters[c, y].Glyph, out var glyphIndex)) { glyphIndex = 0; } glyphChars.Add(glyphIndex); advanceWidths.Add(this.glyphTypeface.AdvanceWidths[glyphIndex] * this.fontSizeScaled); } if (!allEmpty) { var gr = new GlyphRun(this.glyphTypeface, 0, false, this.fontSizeScaled, (float)this.dpiInfo.PixelsPerDip, new List <ushort>(glyphChars), glyphOrigin, new List <double>(advanceWidths), null, null, null, null, null, null); dc.DrawGlyphRun(foregroundBrush, gr); } runStart = x; allNull = allEmpty = true; } } } }
private static void GetKerningInfo(CCRawList <char> charset) { _abcValues.Clear(); var fontFace = new FontFace(_currentFont); var value = new ABCFloat[1]; var glyphRun = new GlyphRun(); glyphRun.FontFace = fontFace; glyphRun.FontSize = _currentDIP; var BrushColor = SharpDX.Color.White; /* * SharpDX.DirectWrite.Matrix mtrx = new SharpDX.DirectWrite.Matrix(); * mtrx.M11 = 1F; * mtrx.M12 = 0; * mtrx.M21 = 0; * mtrx.M22 = 1F; * mtrx.Dx = 0; * mtrx.Dy = 0; */ //GlyphMetrics[] metrics = fontFace.GetGdiCompatibleGlyphMetrics(23, 1, mtrx, false, glyphIndices, false); //FontMetrics metr = fontFace.GetGdiCompatibleMetrics(23, 1, new SharpDX.DirectWrite.Matrix()); //_pRenderTarget.DrawGlyphRun(new SharpDX.DrawingPointF(left, top), glyphRun, new SharpDX.Direct2D1.SolidColorBrush(_pRenderTarget, BrushColor), MeasuringMode.GdiClassic); int[] codePoints = new int[1]; var unitsPerEm = fontFace.Metrics.DesignUnitsPerEm; var familyName = _currentFont.ToString(); for (int i = 0; i < charset.Count; i++) { var ch = charset[i]; if (!_abcValues.ContainsKey(ch)) { var textLayout = new TextLayout(FactoryDWrite, ch.ToString(), textFormat, unitsPerEm, unitsPerEm); var tlMetrics = textLayout.Metrics; var tlmWidth = tlMetrics.Width; var tllWidth = tlMetrics.LayoutWidth; codePoints[0] = (int)ch; short[] glyphIndices = fontFace.GetGlyphIndices(codePoints); glyphRun.Indices = glyphIndices; var metrics = fontFace.GetDesignGlyphMetrics(glyphIndices, false); //var width = metrics[0].AdvanceWidth + metrics[0].LeftSideBearing + metrics[0].RightSideBearing; //var glyphWidth = _currentFontSizeEm * (float)metrics[0].AdvanceWidth / unitsPerEm; //var abcWidth = _currentDIP * (float)width / unitsPerEm; //value[0].abcfA = _currentFontSizeEm * (float)metrics[0].LeftSideBearing / unitsPerEm; //value[0].abcfB = _currentFontSizeEm * (float)metrics[0].AdvanceWidth / unitsPerEm; //value[0].abcfC = _currentFontSizeEm * (float)metrics[0].RightSideBearing / unitsPerEm; // The A and C values are throwing the spacing off //value[0].abcfA = _currentDIP * (float)metrics[0].LeftSideBearing / unitsPerEm; value[0].abcfB = _currentDIP * (float)metrics[0].AdvanceWidth / unitsPerEm; //value[0].abcfC = _currentDIP * (float)metrics[0].RightSideBearing / unitsPerEm; _abcValues.Add( ch, new KerningInfo() { A = value[0].abcfA, B = value[0].abcfB, C = value[0].abcfC }); } } }
//Processes the Glyphs element, create one or more text runs out of it, add to containing text line and text box private void _ProcessGlyphsElement(Glyphs glyphs, FixedNode node) { Debug.Assert(glyphs != null); string s = glyphs.UnicodeString; if (s.Length == 0 || glyphs.FontRenderingEmSize <= 0) { return; } //Multiple table cells separated by wide spaces should be identified GlyphRun glyphRun = glyphs.ToGlyphRun(); if (glyphRun == null) { //Could not create a GlyphRun out of this Glyphs element //Some key properties might be missing/invalid return; } Rect alignmentBox = glyphRun.ComputeAlignmentBox(); alignmentBox.Offset(glyphs.OriginX, glyphs.OriginY); GlyphTypeface typeFace = glyphRun.GlyphTypeface; GeneralTransform trans = glyphs.TransformToAncestor(_fixedPage); int charIndex = -1; double advWidth = 0; double cumulativeAdvWidth = 0; int lastAdvWidthIndex = 0; int textRunStartIndex = 0; double lastX = alignmentBox.Left; int glyphIndex = charIndex; do { charIndex = s.IndexOf(" ", charIndex + 1, s.Length - charIndex - 1, StringComparison.Ordinal); if (charIndex >= 0) { if (glyphRun.ClusterMap != null && glyphRun.ClusterMap.Count > 0) { glyphIndex = glyphRun.ClusterMap[charIndex]; } else { glyphIndex = charIndex; } //Advance width of the space character in the font double advFont = typeFace.AdvanceWidths[glyphRun.GlyphIndices[glyphIndex]] * glyphRun.FontRenderingEmSize; double advSpecified = glyphRun.AdvanceWidths[glyphIndex]; if ((advSpecified / advFont) > 2) { //Are these seperated by a vertical line? advWidth = 0; for (int i = lastAdvWidthIndex; i < glyphIndex; i++) { advWidth += glyphRun.AdvanceWidths[i]; } cumulativeAdvWidth += advWidth; lastAdvWidthIndex = glyphIndex + 1; if (_lines.IsVerticallySeparated(glyphRun.BaselineOrigin.X + cumulativeAdvWidth, alignmentBox.Top, glyphRun.BaselineOrigin.X + cumulativeAdvWidth + advSpecified, alignmentBox.Bottom)) { //Create a new FixedTextRun Rect boundingRect = new Rect(lastX, alignmentBox.Top, advWidth + advFont, alignmentBox.Height); int endIndex = charIndex; if ((charIndex == 0 || s[charIndex - 1] == ' ') && (charIndex != s.Length - 1)) { endIndex = charIndex + 1; } _CreateTextRun(boundingRect, trans, glyphs, node, textRunStartIndex, endIndex); lastX = lastX + advWidth + advSpecified; textRunStartIndex = charIndex + 1; } cumulativeAdvWidth += advSpecified; } } } while (charIndex >= 0 && charIndex < s.Length - 1); if (textRunStartIndex < s.Length) { //Last text run //For non-partitioned elements this will be the whole Glyphs element Rect boundingRect = new Rect(lastX, alignmentBox.Top, alignmentBox.Right - lastX, alignmentBox.Height); _CreateTextRun(boundingRect, trans, glyphs, node, textRunStartIndex, s.Length); } }
/// <summary> /// Draw glyphrun /// </summary> /// <param name="drawingContext">The drawing context to draw into </param> /// <param name="foregroundBrush"> /// The foreground brush of the glyphrun. Pass in "null" to draw the /// glyph run with the foreground in TextRunProperties. /// </param> /// <param name="glyphRun">The GlyphRun to be drawn </param> /// <returns>bounding rectangle of drawn glyphrun</returns> /// <Remarks> /// TextEffect drawing code may use a different foreground brush for the text. /// </Remarks> internal Rect DrawGlyphRun( DrawingContext drawingContext, Brush foregroundBrush, GlyphRun glyphRun ) { Debug.Assert(_shapeable != null); Rect inkBoundingBox = glyphRun.ComputeInkBoundingBox(); if (!inkBoundingBox.IsEmpty) { // glyph run's ink bounding box is relative to its origin inkBoundingBox.X += glyphRun.BaselineOrigin.X; inkBoundingBox.Y += glyphRun.BaselineOrigin.Y; } if (drawingContext != null) { int pushCount = 0; // the number of push we do try { if (_textEffects != null) { // we need to push in the same order as they are set for (int i = 0; i < _textEffects.Count; i++) { // get the text effect by its index TextEffect textEffect = _textEffects[i]; if (textEffect.Transform != null && textEffect.Transform != Transform.Identity) { drawingContext.PushTransform(textEffect.Transform); pushCount++; } if (textEffect.Clip != null) { drawingContext.PushClip(textEffect.Clip); pushCount++; } if (textEffect.Foreground != null) { // remember the out-most non-null brush // this brush will be used to draw the glyph run foregroundBrush = textEffect.Foreground; } } } _shapeable.Draw(drawingContext, foregroundBrush, glyphRun); } finally { for (int i = 0; i < pushCount; i++) { drawingContext.Pop(); } } } return(inkBoundingBox); }
/// <summary> /// Splits the <see cref="TextRun"/> at specified length. /// </summary> /// <param name="length">The length.</param> /// <returns>The split result.</returns> public SplitTextCharactersResult Split(int length) { var glyphCount = GlyphRun.IsLeftToRight ? GlyphRun.FindGlyphIndex(GlyphRun.Characters.Start + length) : GlyphRun.FindGlyphIndex(GlyphRun.Characters.End - length); if (GlyphRun.Characters.Length == length) { return(new SplitTextCharactersResult(this, null)); } if (GlyphRun.GlyphIndices.Length == glyphCount) { return(new SplitTextCharactersResult(this, null)); } if (GlyphRun.IsLeftToRight) { var firstGlyphRun = new GlyphRun( Properties.Typeface.GlyphTypeface, Properties.FontRenderingEmSize, GlyphRun.GlyphIndices.Take(glyphCount), GlyphRun.GlyphAdvances.Take(glyphCount), GlyphRun.GlyphOffsets.Take(glyphCount), GlyphRun.Characters.Take(length), GlyphRun.GlyphClusters.Take(glyphCount), GlyphRun.BiDiLevel); var firstTextRun = new ShapedTextCharacters(firstGlyphRun, Properties); var secondGlyphRun = new GlyphRun( Properties.Typeface.GlyphTypeface, Properties.FontRenderingEmSize, GlyphRun.GlyphIndices.Skip(glyphCount), GlyphRun.GlyphAdvances.Skip(glyphCount), GlyphRun.GlyphOffsets.Skip(glyphCount), GlyphRun.Characters.Skip(length), GlyphRun.GlyphClusters.Skip(glyphCount), GlyphRun.BiDiLevel); var secondTextRun = new ShapedTextCharacters(secondGlyphRun, Properties); return(new SplitTextCharactersResult(firstTextRun, secondTextRun)); } else { var take = GlyphRun.GlyphIndices.Length - glyphCount; var firstGlyphRun = new GlyphRun( Properties.Typeface.GlyphTypeface, Properties.FontRenderingEmSize, GlyphRun.GlyphIndices.Take(take), GlyphRun.GlyphAdvances.Take(take), GlyphRun.GlyphOffsets.Take(take), GlyphRun.Characters.Skip(length), GlyphRun.GlyphClusters.Take(take), GlyphRun.BiDiLevel); var firstTextRun = new ShapedTextCharacters(firstGlyphRun, Properties); var secondGlyphRun = new GlyphRun( Properties.Typeface.GlyphTypeface, Properties.FontRenderingEmSize, GlyphRun.GlyphIndices.Skip(take), GlyphRun.GlyphAdvances.Skip(take), GlyphRun.GlyphOffsets.Skip(take), GlyphRun.Characters.Take(length), GlyphRun.GlyphClusters.Skip(take), GlyphRun.BiDiLevel); var secondTextRun = new ShapedTextCharacters(secondGlyphRun, Properties); return(new SplitTextCharactersResult(secondTextRun, firstTextRun)); } }
void IMetroDrawingContext.DrawGlyphRun(Brush foreground, GlyphRun glyphRun) { m_Flattener.DrawGlyphRun(foreground, glyphRun); }
public override IEnumerable <Drawing> GenerateDrawings(DrawingContext drawingContext, RenderContext renderContext) { if (_glyphTypeface == null) { return(new List <Drawing>()); } _drawings.Clear(); _totalWidth = 0.0; GlyphRunParams param = null; GlyphRun gr = null; Point origin = renderContext.Offset; // GlyphDrawing的起始点在左下角 origin.Y += _fontSize; Int32 idx = 0; // 起始点的偏移是0 do { Char ch = _content[idx]; if (_glyphTypeface.CharacterToGlyphMap.ContainsKey(ch)) { if (param != null && param.ForGlobal) { gr = new GlyphRun(_globalGlyphTypeface, 0, false, _fontSize, param.Indices, origin, param.AdvanceWidths, null, null, null, null, null, XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)); _drawings.Add(new GlyphRunDrawing(_fgBrush, gr)); origin.X += param.TotalWidth; param = null; } if (param == null) { param = new GlyphRunParams(false); } UInt16 indice = _glyphTypeface.CharacterToGlyphMap[ch]; Double width = _glyphTypeface.AdvanceWidths[indice] * _fontSize; param.Indices.Add(indice); param.AdvanceWidths.Add(width); param.TotalWidth += width; _totalWidth += width; } else { if (param != null && !param.ForGlobal) { gr = new GlyphRun(_glyphTypeface, 0, false, _fontSize, param.Indices, origin, param.AdvanceWidths, null, null, null, null, null, XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)); _drawings.Add(new GlyphRunDrawing(_fgBrush, gr)); origin.X += param.TotalWidth; param = null; } if (param == null) { param = new GlyphRunParams(true); } UInt16 indice = _globalGlyphTypeface.CharacterToGlyphMap[ch]; Double width = _globalGlyphTypeface.AdvanceWidths[indice] * _fontSize; param.Indices.Add(indice); param.AdvanceWidths.Add(width); param.TotalWidth += width; _totalWidth += width; } ++idx; } while (idx < _content.Length); if (param != null) { gr = new GlyphRun(param.ForGlobal ? _globalGlyphTypeface : _glyphTypeface, 0, false, _fontSize, param.Indices, origin, param.AdvanceWidths, null, null, null, null, null, XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)); _drawings.Add(new GlyphRunDrawing(_fgBrush, gr)); } renderContext.PushTranslation(_totalWidth, 0.0); return(_drawings); }
public void CaculateText(string text) { double StartXPosition = 0; _Text = text; Typeface typeface = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal); GlyphTypeface glyphTypeface; if (!typeface.TryGetGlyphTypeface(out glyphTypeface)) { throw new InvalidOperationException("No glyphtypeface found"); } double size = _FontSize; glyphIndexes = new ushort[text.Length]; advanceWidths = new double[text.Length]; charList = new IndividualLetterData[text.Length]; _TotalTextWidth = 0; for (int n = 0; n < text.Length; n++) { charList[n] = new IndividualLetterData(text[n], _TotalTextWidth, size, glyphTypeface); glyphIndexes[n] = charList[n].GlyphIndex; advanceWidths[n] = charList[n].CharWidth; _TotalTextWidth += charList[n].CharWidth; } switch (charList.Length) { case 4: charPositionUsedForMiddle = 1; StartXPosition = 165 - charList[1].GetMiddlePointOfCharInRelationToHoleOfString(); break; default: // find the char that falls in the 40% range for (int eachChar = 0; eachChar < charList.Length; eachChar++) { if (charList[eachChar].IsCharWithinPercentage(_TotalTextWidth, PercentageWhereToLookForCharInText)) { charPositionUsedForMiddle = eachChar; StartXPosition = 165 - charList[eachChar].GetMiddlePointOfCharInRelationToHoleOfString(); break; } } break; } origin = new Point(StartXPosition, 37); glyphRun = new GlyphRun(glyphTypeface, 0, false, size, glyphIndexes, origin, advanceWidths, null, null, null, null, null, null); }
/// <summary> /// Draw glyph run to the drawing surface /// </summary> /// <param name="drawingContext">drawing surface</param> /// <param name="foregroundBrush"> /// Foreground brush of the glyphrun. Passing in null brush will mean the GlyphRun /// is to be drawn with the Foreground of the TextRun. /// </param> /// <param name="glyphRun">glyph run object to be drawn</param> internal abstract void Draw( DrawingContext drawingContext, Brush foregroundBrush, GlyphRun glyphRun );
internal void RenderFixedNode(DrawingContext dc) { // //Iterate through fix node to draw red dotted line // CultureInfo EnglishCulture = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; int lineCount = _lineResults.Length; if (lineCount == 0) { return; } FixedNode fixedStartPage = _lineResults[0].Start; FixedNode fixedEndPage = _lineResults[lineCount - 1].End; FixedNode[] fixedNodes = _fixedTextBuilder.FixedFlowMap.FixedOrderGetRangeNodes(fixedStartPage, fixedEndPage); FixedPage fp = _fixedTextBuilder.FixedTextContainer.FixedDocument.GetFixedPage(PageIndex); FormattedText ft; Point prevTextPoint = new Point(0, 0); DpiScale dpi = fp.GetDpi(); foreach (FixedNode currentFixedNode in fixedNodes) { if (currentFixedNode.Page == FixedFlowMap.FixedOrderStartPage) { prevTextPoint.X = prevTextPoint.Y = 0; ft = new FormattedText("FixedOrderStartPage", EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } if (currentFixedNode.Page == FixedFlowMap.FixedOrderEndPage) { prevTextPoint.X = fp.Width - 100; prevTextPoint.Y = fp.Height - 10; ft = new FormattedText("FixedOrderEndPage", EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } if (currentFixedNode[1] == FixedFlowMap.FixedOrderStartVisual || currentFixedNode[1] == FixedFlowMap.FixedOrderEndVisual) { prevTextPoint.X = 2; prevTextPoint.Y = prevTextPoint.Y + 10; String outputString = currentFixedNode[1] == FixedFlowMap.FixedOrderStartVisual ? "FixedOrderStartVisual" : "FixedOrderEndVisual"; ft = new FormattedText(outputString, EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } DependencyObject dependencyObject = fp.GetElement(currentFixedNode); Image image = dependencyObject as Image; if (image != null) { GeneralTransform transform = image.TransformToAncestor(fp); // You can't use GetContentBounds inside OnRender Rect boundingRect = new Rect(0, 0, image.Width, image.Height); Rect imageRect = transform.TransformBounds(boundingRect); if (!imageRect.IsEmpty) { // Image might overlap, inflate the box. imageRect.Inflate(1, 1); Pen pen = new Pen(Brushes.DarkMagenta, 1.5); DrawRectOutline(dc, pen, imageRect); prevTextPoint.X = imageRect.Right; prevTextPoint.Y = imageRect.Bottom - 10; } else { prevTextPoint.X = 2; prevTextPoint.Y = prevTextPoint.Y + 10; } ft = new FormattedText(currentFixedNode.ToString(), EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } Path path = dependencyObject as Path; if (path != null) { GeneralTransform transform = path.TransformToAncestor(fp); // You can't use GetContentBounds inside OnRender Rect boundingRect = path.Data.Bounds; Rect imageRect = transform.TransformBounds(boundingRect); if (!imageRect.IsEmpty) { // Image might overlap, inflate the box. imageRect.Inflate(1, 1); Pen pen = new Pen(Brushes.DarkMagenta, 1.5); DrawRectOutline(dc, pen, imageRect); prevTextPoint.X = imageRect.Right; prevTextPoint.Y = imageRect.Bottom - 10; } else { prevTextPoint.X = 2; prevTextPoint.Y = prevTextPoint.Y + 10; } ft = new FormattedText(currentFixedNode.ToString(), EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } Glyphs glyphs = dependencyObject as Glyphs; if (glyphs != null) { GlyphRun run = glyphs.ToGlyphRun(); if (run != null) { Rect glyphBox = run.ComputeAlignmentBox(); glyphBox.Offset(glyphs.OriginX, glyphs.OriginY); GeneralTransform transform = glyphs.TransformToAncestor(fp); // // Draw it using the dotted red line // Pen pen = new Pen(Brushes.Red, 0.5); Transform t = transform.AffineTransform; if (t != null) { dc.PushTransform(t); } else { dc.PushTransform(Transform.Identity); } DrawRectOutline(dc, pen, glyphBox); prevTextPoint.X = glyphBox.Right; prevTextPoint.Y = glyphBox.Bottom; transform.TryTransform(prevTextPoint, out prevTextPoint); dc.Pop(); // transform } else { prevTextPoint.X = 2; prevTextPoint.Y = prevTextPoint.Y + 10; } ft = new FormattedText(currentFixedNode.ToString(), EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); continue; } // // For anything else, there is this code to draw ... // prevTextPoint.X = 2; prevTextPoint.Y = prevTextPoint.Y + 10; ft = new FormattedText(currentFixedNode.ToString(), EnglishCulture, FlowDirection.LeftToRight, new Typeface("Courier New"), 8, Brushes.DarkViolet, dpi.PixelsPerDip); dc.DrawText(ft, prevTextPoint); } }