/// <summary> /// Collecting glyph runs /// </summary> static private void AddRun( ArrayList runs, SimpleRun run, ref int nonHiddenLength ) { if(run.Length > 0) { // dont add 0-length run runs.Add(run); if (!run.Ghost) { nonHiddenLength += run.Length; } } }
internal bool IsUnderlineCompatible(SimpleRun nextRun) { return Typeface.Equals(nextRun.Typeface) && EmSize == nextRun.EmSize && Baseline == nextRun.Baseline; }
/// <summary> /// Create simple run of text, /// returning null if the specified text run cannot be correctly formatted as simple run /// </summary> static internal SimpleRun CreateSimpleTextRun( CharacterBufferRange charBufferRange, TextRun textRun, TextFormatterImp formatter, int widthLeft, bool emergencyWrap, bool breakOnTabs ) { Invariant.Assert(textRun is TextCharacters); SimpleRun run = new SimpleRun(formatter); run.CharBufferReference = charBufferRange.CharacterBufferReference; run.TextRun = textRun; if (!run.TextRun.Properties.Typeface.CheckFastPathNominalGlyphs( charBufferRange, run.EmSize, 1.0, formatter.IdealToReal(widthLeft), !emergencyWrap, false, CultureMapper.GetSpecificCulture(run.TextRun.Properties.CultureInfo), formatter.TextFormattingMode, false, //No support for isSideways breakOnTabs, out run.Length )) { // Getting nominal glyphs is not supported by the font, // or it is but it results in low typographic quality text // e.g. OpenType support is not utilized. return null; } run.TextRun.Properties.Typeface.GetCharacterNominalWidthsAndIdealWidth( new CharacterBufferRange(run.CharBufferReference, run.Length), run.EmSize, TextFormatterImp.ToIdeal, formatter.TextFormattingMode, false, out run.NominalAdvances, out run.IdealWidth ); return run; }
/// <summary> /// Returns a simple text run that represents a Tab. /// </summary> /// <param name="settings">text formatting settings</param> /// <param name="textRun">text run</param> /// <param name="idealRunOffsetUnRounded">run's offset from the beginning of the line</param> static private SimpleRun CreateSimpleRunForTab( FormatSettings settings, TextRun textRun, int idealRunOffsetUnRounded ) { if (settings == null || textRun == null || textRun.Properties == null || textRun.Properties.Typeface == null) { return null; } GlyphTypeface glyphTypeface = textRun.Properties.Typeface.TryGetGlyphTypeface(); // Check whether the font has the space character. If not then we have to go through // font fallback. // We are not calling CreateSimpleTextRun() because CheckFastPathNominalGlyphs() // can fail if a font has TypographicAvailabilities. We are simply rendering a space // so we don't realy care about TypographicFeatures. This is a perf optimization. if (glyphTypeface == null || !glyphTypeface.HasCharacter(' ')) { return null; } // The full shaping path converts tabs to spaces. // Note: In order to get exactly the same metrics as we did in FullTextLine (specifically ink bounding box) // we need to "Draw" a space in place of a Tab (previously we were just ignoring the Tab and rendering nothing) // which turned out to give different overhang and extent values than those returned using the full shaping path. // So in order to avoid vertical jiggling when a line is changed from SimpleTextLine to FullTextLine by adding/removing // a complex character, we need to do the same thing as the full shaping path and draw a space for each tab. TextRun modifedTextRun = new TextCharacters(" ", textRun.Properties); CharacterBufferRange characterBufferRange = new CharacterBufferRange(modifedTextRun); SimpleRun run = new SimpleRun(1, modifedTextRun, Flags.Tab, settings.Formatter); run.CharBufferReference = characterBufferRange.CharacterBufferReference; run.TextRun.Properties.Typeface.GetCharacterNominalWidthsAndIdealWidth( characterBufferRange, run.EmSize, TextFormatterImp.ToIdeal, settings.Formatter.TextFormattingMode, false, out run.NominalAdvances ); int idealIncrementalTab = TextFormatterImp.RealToIdeal(settings.Pap.DefaultIncrementalTab); // Here we get the next tab stop without snapping the metrics to pixels. // We do the pixel snapping on the final position of the tab stop (and not on the IncrementalTab) // to achieve the same results as those in full shaping. int idealNextTabStopUnRounded = ((idealRunOffsetUnRounded / idealIncrementalTab) + 1) * idealIncrementalTab; run.IdealWidth = run.NominalAdvances[0] = idealNextTabStopUnRounded - idealRunOffsetUnRounded; return run; }
/// <summary> /// Creating a simple text run /// </summary> /// <param name="settings">text formatting settings</param> /// <param name="charString">character string associated to textrun</param> /// <param name="textRun">text run</param> /// <param name="cp">first cp of the run</param> /// <param name="cpFirst">first cp of the line</param> /// <param name="runLength">run length</param> /// <param name="widthLeft">maximum run width</param> /// <param name="idealRunOffsetUnRounded">run's offset from the beginning of the line</param> /// <returns>a SimpleRun object</returns> static public SimpleRun Create( FormatSettings settings, CharacterBufferRange charString, TextRun textRun, int cp, int cpFirst, int runLength, int widthLeft, int idealRunOffsetUnRounded ) { SimpleRun run = null; if (textRun is TextCharacters) { if ( textRun.Properties.BaselineAlignment != BaselineAlignment.Baseline || (textRun.Properties.TextEffects != null && textRun.Properties.TextEffects.Count != 0) ) { // fast path does not handle the following conditions // o non-default baseline alignment // o text drawing effect ( return null; } TextDecorationCollection textDecorations = textRun.Properties.TextDecorations; if ( textDecorations != null && textDecorations.Count != 0 && !textDecorations.ValueEquals(TextDecorations.Underline)) { // we only support a single underline return null; } settings.DigitState.SetTextRunProperties(textRun.Properties); if (settings.DigitState.RequiresNumberSubstitution) { // don't support number substitution in fast path return null; } bool canProcessTabsInSimpleShapingPath = CanProcessTabsInSimpleShapingPath( settings.Pap, settings.Formatter.TextFormattingMode ); if (charString[0] == TextStore.CharCarriageReturn) { // CR in the middle of text stream treated as explicit paragraph break // simple hard line break runLength = 1; if (charString.Length > 1 && charString[1] == TextStore.CharLineFeed) { runLength = 2; } // This path handles the case where the backing store breaks the text run in between // a Carriage Return and a Line Feed. So we fetch the next run to check whether the next // character is a line feed. else if (charString.Length == 1) { // Prefetch to check for line feed. TextRun newRun; int newRunLength; CharacterBufferRange newBufferRange = settings.FetchTextRun( cp + 1, cpFirst, out newRun, out newRunLength ); if (newBufferRange.Length > 0 && newBufferRange[0] == TextStore.CharLineFeed) { // Merge the 2 runs. int lengthOfRun = 2; char[] characterArray = new char[lengthOfRun]; characterArray[0] = TextStore.CharCarriageReturn; characterArray[1] = TextStore.CharLineFeed; TextRun mergedTextRun = new TextCharacters(characterArray, 0, lengthOfRun, textRun.Properties); return new SimpleRun(lengthOfRun, mergedTextRun, (Flags.EOT | Flags.Ghost), settings.Formatter); } } return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter); } else if (charString[0] == TextStore.CharLineFeed) { // LF in the middle of text stream treated as explicit paragraph break // simple hard line break runLength = 1; return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter); } else if (canProcessTabsInSimpleShapingPath && charString[0] == TextStore.CharTab) { return CreateSimpleRunForTab(settings, textRun, idealRunOffsetUnRounded); } // attempt to create a simple run for text run = CreateSimpleTextRun( charString, textRun, settings.Formatter, widthLeft, settings.Pap.EmergencyWrap, canProcessTabsInSimpleShapingPath ); if (run == null) { // fail to create simple text run, the run content is too complex return null; } // Check for underline condition if (textDecorations != null && textDecorations.Count == 1 ) { run.Underline = textDecorations[0]; } } else if (textRun is TextEndOfLine) { run = new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter); } else if (textRun is TextHidden) { // hidden run run = new SimpleRun(runLength, textRun, Flags.Ghost, settings.Formatter); } return run; }
/// <summary> /// Collecting glyph runs /// </summary> static private void AddRun( ArrayList runs, SimpleRun run, SimpleRun prev ) { Invariant.Assert( prev == null || (runs.Count > 0 && prev == runs[runs.Count - 1]), "Trailing space run is not after the last existing run!" ); if(run.Length > 0) { // dont add 0-length run runs.Add(run); } }