private unsafe GlyphRun ComputeUnshapedGlyphRun( LSRun lsrun, // LSrun used to shape the GlyphRun LsTFlow textFlow, // flow direction TextFormatterImp textFormatterImp, // The TextFormatter Implementation bool originProvided, // flag indicate whether the origin of the run is provided LSPOINT lsrunOrigin, // physical start of the run int dupRun, // width of the run int cchText, // character count char *pwchText, // characters for display int *piCharAdvances, // character advance widths, bool justify ) { GlyphRun glyphRun = null; if (lsrun.Type == Plsrun.Text) { Debug.Assert(lsrun.Shapeable != null); Point runOrigin = new Point(); int nominalX = 0; int nominalY = 0; if (originProvided) { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; if (textFlow == LsTFlow.lstflowWS) { lsrunOrigin.x -= dupRun; } if (currentLine.RightToLeft) { lsrunOrigin.x = -lsrunOrigin.x; } if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify) { LSRun.UVToNominalXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine, out nominalX, out nominalY ); } else { runOrigin = LSRun.UVToXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine ); } } char[] charString = new char[cchText]; IList<double> charWidths; bool isRightToLeft = (lsrun.BidiLevel & 1) != 0; if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal) { charWidths = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), cchText); for (int i = 0; i < cchText; i++) { charString[i] = pwchText[i]; charWidths[i] = textFormatterImp.IdealToReal(piCharAdvances[i]); } } else { if (justify) { AdjustMetricsForDisplayModeJustifiedText( pwchText, piCharAdvances, cchText, isRightToLeft, nominalX, nominalY, out runOrigin, out charWidths ); } else { charWidths = new List<double>(cchText); for (int i = 0; i < cchText; i++) { charWidths.Add(textFormatterImp.IdealToReal(piCharAdvances[i])); } } for (int i = 0; i < cchText; i++) { charString[i] = pwchText[i]; } } glyphRun = lsrun.Shapeable.ComputeUnshapedGlyphRun( runOrigin, charString, charWidths ); } return glyphRun; }
public override IEnumerable<IndexedGlyphRun> GetIndexedGlyphRuns() { List<IndexedGlyphRun> indexedGlyphRuns = new List<IndexedGlyphRun>(_runs.Length); // create each GlyphRun at Point(0, 0) Point start = new Point(0, 0); int currentCp = _cpFirst; foreach(SimpleRun run in _runs) { if (run.Length > 0 && !run.Ghost) { IList<double> displayGlyphAdvances; if (_settings.TextFormattingMode == TextFormattingMode.Ideal) { displayGlyphAdvances = new ThousandthOfEmRealDoubles(run.EmSize, run.NominalAdvances.Length); for (int i = 0; i < displayGlyphAdvances.Count; i++) { // convert ideal glyph advance width to real width for displaying displayGlyphAdvances[i] = _settings.Formatter.IdealToReal(run.NominalAdvances[i]); } } else { displayGlyphAdvances = new List<double>(run.NominalAdvances.Length); for (int i = 0; i < run.NominalAdvances.Length; i++) { // convert ideal glyph advance width to real width for displaying displayGlyphAdvances.Add(_settings.Formatter.IdealToReal(run.NominalAdvances[i])); } } GlyphTypeface glyphTypeface = run.Typeface.TryGetGlyphTypeface(); Invariant.Assert(glyphTypeface != null); // this simple run has GlyphRun GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun( start, new CharacterBufferRange(run.CharBufferReference, run.Length), displayGlyphAdvances, run.EmSize, run.TextRun.Properties.FontHintingEmSize, run.Typeface.NullFont, CultureMapper.GetSpecificCulture(run.TextRun.Properties.CultureInfo), null, // device font name _settings.TextFormattingMode ); if (glyphRun != null) { indexedGlyphRuns.Add( new IndexedGlyphRun( currentCp, run.Length, glyphRun ) ); } } currentCp += run.Length; } return indexedGlyphRuns; }
private unsafe GlyphRun ComputeShapedGlyphRun( LSRun lsrun, // ls run TextFormatterImp textFormatterImp, // The TextFormatter Implementation bool originProvided, // flag indicate whether the origin of the run is provided LSPOINT lsrunOrigin, // physical start of the run int charCount, // characters count char *pwchText, // characters for the GlyphRun ushort *puClusterMap, // cluster map int glyphCount, // glyph count ushort *puGlyphs, // glyph indices int *piJustifiedGlyphAdvances, // glyph advances GlyphOffset *piiGlyphOffsets, // glyph offsets bool justify ) { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; Point runOrigin = new Point(); int nominalX = 0; int nominalY = 0; if (originProvided) { if (currentLine.RightToLeft) { lsrunOrigin.x = -lsrunOrigin.x; } if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify) { LSRun.UVToNominalXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine, out nominalX, out nominalY ); } else { runOrigin = LSRun.UVToXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine ); } } char[] charString = new char[charCount]; ushort[] clusterMap = new ushort[charCount]; for (int i = 0; i < charCount; i++) { charString[i] = pwchText[i]; clusterMap[i] = puClusterMap[i]; } ushort[] glyphIndices = new ushort[glyphCount]; IList<double> glyphAdvances; IList<Point> glyphOffsets; bool isRightToLeft = (lsrun.BidiLevel & 1) != 0; if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal) { glyphAdvances = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount); glyphOffsets = new ThousandthOfEmRealPoints(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount); for (int i = 0; i < glyphCount; i++) { glyphIndices[i] = puGlyphs[i]; glyphAdvances[i] = textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]); glyphOffsets[i] = new Point( textFormatterImp.IdealToReal(piiGlyphOffsets[i].du), textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv) ); } } else { if (justify) { AdjustMetricsForDisplayModeJustifiedText( pwchText, piJustifiedGlyphAdvances, glyphCount, isRightToLeft, nominalX, nominalY, out runOrigin, out glyphAdvances ); } else { glyphAdvances = new List<double>(glyphCount); for (int i = 0; i < glyphCount; i++) { glyphAdvances.Add(textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i])); } } glyphOffsets = new List<Point>(glyphCount); for (int i = 0; i < glyphCount; i++) { glyphIndices[i] = puGlyphs[i]; glyphOffsets.Add(new Point( textFormatterImp.IdealToReal(piiGlyphOffsets[i].du), textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv) )); } } #if CHECK_GLYPHS if ( lsrun._glyphs != null && glyphCount <= lsrun._glyphs.Length) { for (int i = 0; i < glyphCount; i++) { Debug.Assert(glyphIndices[i] == lsrun._glyphs[i], "Corrupted glyphs"); } } #endif GlyphRun glyphRun = lsrun.Shapeable.ComputeShapedGlyphRun( runOrigin, charString, clusterMap, glyphIndices, glyphAdvances, glyphOffsets, isRightToLeft, false // no sideway support yet ); return glyphRun; }
internal Rect Draw( DrawingContext drawingContext, double x, double y, bool visiCodePath ) { if (Length <= 0 || this.Ghost) { return Rect.Empty; // nothing to draw } Brush foregroundBrush = TextRun.Properties.ForegroundBrush; if(visiCodePath && foregroundBrush is SolidColorBrush) { Color color = ((SolidColorBrush)foregroundBrush).Color; foregroundBrush = new SolidColorBrush(Color.FromArgb( (byte)(color.A>>2), // * 0.25 color.R, color.G, color.B )); } Rect inkBoundingBox; IList<double> displayGlyphAdvances; if (_textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal) { displayGlyphAdvances = new ThousandthOfEmRealDoubles(EmSize, NominalAdvances.Length); for (int i = 0; i < displayGlyphAdvances.Count; i++) { // convert ideal glyph advance width to real width for displaying. displayGlyphAdvances[i] = _textFormatterImp.IdealToReal(NominalAdvances[i]); } } else { displayGlyphAdvances = new List<double>(NominalAdvances.Length); for (int i = 0; i < NominalAdvances.Length; i++) { // convert ideal glyph advance width to real width for displaying. displayGlyphAdvances.Add(_textFormatterImp.IdealToReal(NominalAdvances[i])); } } CharacterBufferRange charBufferRange = new CharacterBufferRange(CharBufferReference, Length); GlyphTypeface glyphTypeface = Typeface.TryGetGlyphTypeface(); Invariant.Assert(glyphTypeface != null); GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun( new Point(x, y), charBufferRange, displayGlyphAdvances, EmSize, TextRun.Properties.FontHintingEmSize, Typeface.NullFont, CultureMapper.GetSpecificCulture(TextRun.Properties.CultureInfo), null, // device font name _textFormatterImp.TextFormattingMode ); if (glyphRun != null) { inkBoundingBox = glyphRun.ComputeInkBoundingBox(); } else { inkBoundingBox = Rect.Empty; } 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) { if (glyphRun != null) { glyphRun.EmitBackground(drawingContext, TextRun.Properties.BackgroundBrush); drawingContext.DrawGlyphRun(foregroundBrush, glyphRun); } // draw underline here if (Underline != null) { // Determine number of characters to underline. We don't underline trailing spaces // if the TrimTrailingUnderline flag is set. int underlineLength = Length; if (TrimTrailingUnderline) { while (underlineLength > 0 && IsSpace(charBufferRange[underlineLength - 1])) { --underlineLength; } } // Determine the width of the underline. double dxUnderline = 0; for (int i = 0; i < underlineLength; ++i) { dxUnderline += _textFormatterImp.IdealToReal(NominalAdvances[i]); } // We know only TextDecoration.Underline will be handled in Simple Path. double offset = -Typeface.UnderlinePosition * EmSize; double penThickness = Typeface.UnderlineThickness * EmSize; Point lineOrigin = new Point(x, y + offset); Rect underlineRect = new Rect( lineOrigin.X, lineOrigin.Y - penThickness * 0.5, dxUnderline, penThickness ); // Apply the pair of guidelines: one for baseline and another // for top edge of undelining line. Both will be snapped to pixel grid. // Guideline pairing algorithm detects the case when these two // guidelines happen to be close to one another and provides // synchronous snapping, so that the gap between baseline and // undelining line does not depend on the position of text line. drawingContext.PushGuidelineY2(y, lineOrigin.Y - penThickness * 0.5 - y); try { drawingContext.DrawRectangle( foregroundBrush, null, // pen underlineRect ); } finally { drawingContext.Pop(); } // underline pen thickness is always positive in fast path inkBoundingBox.Union( underlineRect ); } } return inkBoundingBox; }