// Token: 0x06006866 RID: 26726 RVA: 0x001D6D10 File Offset: 0x001D4F10 internal void Format(Line.FormattingContext ctx, int dcp, int width, int trackWidth, TextParagraphProperties lineProps, TextLineBreak textLineBreak) { this._formattingContext = ctx; this._dcp = dcp; this._host.Context = this; this._wrappingWidth = TextDpi.FromTextDpi(width); this._trackWidth = TextDpi.FromTextDpi(trackWidth); this._mirror = (lineProps.FlowDirection == FlowDirection.RightToLeft); this._indent = lineProps.Indent; try { if (ctx.LineFormatLengthTarget == -1) { this._line = this._host.TextFormatter.FormatLine(this._host, dcp, this._wrappingWidth, lineProps, textLineBreak, ctx.TextRunCache); } else { this._line = this._host.TextFormatter.RecreateLine(this._host, dcp, ctx.LineFormatLengthTarget, this._wrappingWidth, lineProps, textLineBreak, ctx.TextRunCache); } this._runs = this._line.GetTextRunSpans(); Invariant.Assert(this._runs != null, "Cannot retrieve runs collection."); if (this._formattingContext.MeasureMode) { List <InlineObject> list = new List <InlineObject>(1); int num = this._dcp; foreach (TextSpan <TextRun> textSpan in this._runs) { TextRun value = textSpan.Value; if (value is InlineObjectRun) { list.Add(new InlineObject(num, ((InlineObjectRun)value).UIElementIsland, (TextParagraph)this._paraClient.Paragraph)); } else if (value is FloatingRun) { if (((FloatingRun)value).Figure) { this._hasFigures = true; } else { this._hasFloaters = true; } } num += textSpan.Length; } if (list.Count == 0) { list = null; } this.TextParagraph.SubmitInlineObjects(dcp, dcp + this.ActualLength, list); } } finally { this._host.Context = null; } }
// Token: 0x06006B3A RID: 27450 RVA: 0x001EF850 File Offset: 0x001EDA50 internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, int trackWidth, bool firstLine, int dcpLine) { TextDpi.EnsureValidLineWidth(ref width); this._currentLine = line; TextLineBreak textLineBreak = null; if (pbrLineIn != IntPtr.Zero) { LineBreakRecord lineBreakRecord = base.PtsContext.HandleToObject(pbrLineIn) as LineBreakRecord; PTS.ValidateHandle(lineBreakRecord); textLineBreak = lineBreakRecord.TextLineBreak; } try { line.Format(ctx, dcp, width, trackWidth, this.GetLineProperties(firstLine, dcpLine), textLineBreak); } finally { this._currentLine = null; } }
// Token: 0x06006B29 RID: 27433 RVA: 0x001EF038 File Offset: 0x001ED238 internal void FormatLine(TextParaClient paraClient, int iArea, int dcp, IntPtr pbrlineIn, uint fswdir, int urStartLine, int durLine, int urStartTrack, int durTrack, int urPageLeftMargin, bool fAllowHyphenation, bool fClearOnLeft, bool fClearOnRight, bool fTreatAsFirstInPara, bool fTreatAsLastInPara, bool fSuppressTopSpace, out IntPtr lineHandle, out int dcpLine, out IntPtr ppbrlineOut, out int fForcedBroken, out PTS.FSFLRES fsflres, out int dvrAscent, out int dvrDescent, out int urBBox, out int durBBox, out int dcpDepend, out int fReformatNeighborsAsLastLine) { Invariant.Assert(iArea == 0); base.StructuralCache.CurrentFormatContext.OnFormatLine(); Line line = new Line(base.StructuralCache.TextFormatterHost, paraClient, base.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(true, fClearOnLeft, fClearOnRight, this._textRunCache); this.FormatLineCore(line, pbrlineIn, ctx, dcp, durLine, durTrack, fTreatAsFirstInPara, dcp); lineHandle = line.Handle; dcpLine = line.SafeLength; TextLineBreak textLineBreak = line.GetTextLineBreak(); if (textLineBreak != null) { LineBreakRecord lineBreakRecord = new LineBreakRecord(base.PtsContext, textLineBreak); ppbrlineOut = lineBreakRecord.Handle; } else { ppbrlineOut = IntPtr.Zero; } fForcedBroken = PTS.FromBoolean(line.IsTruncated); fsflres = line.FormattingResult; dvrAscent = line.Baseline; dvrDescent = line.Height - line.Baseline; urBBox = urStartLine + line.Start; durBBox = line.Width; dcpDepend = line.DependantLength; fReformatNeighborsAsLastLine = 0; this.CalcLineAscentDescent(dcp, ref dvrAscent, ref dvrDescent); int num = base.ParagraphStartCharacterPosition + dcp + line.ActualLength + dcpDepend; int symbolCount = base.StructuralCache.TextContainer.SymbolCount; if (num > symbolCount) { num = symbolCount; } base.StructuralCache.CurrentFormatContext.DependentMax = base.StructuralCache.TextContainer.CreatePointerAtOffset(num, LogicalDirection.Backward); }
private IInputElement InputHitTestCompositeLines( PTS.FSPOINT pt, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); IInputElement ie = null; int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; // Get list of complex lines. PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Find affected composite line by looking at vertical offset for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) continue; if (lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent > pt.v) { // Affected composite line has been found. // Get list of line elements. PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Create and format line Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } using (line) { TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); if (element.urStart + line.CalculateUOffsetShift() <= pt.u && pt.u <= (element.urStart + line.CalculateUOffsetShift() + element.dur)) { int distance = pt.u - element.urStart; // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); if ((line.Start <= distance) && (distance <= (line.Start + line.Width))) { ie = line.InputHitTest(distance); break; } } } } break; } } return ie; }
// ------------------------------------------------------------------ // Returns ArrayList of rectangles (at msot one) for the given ContentElement // e if it spans all or part of the specified line // ----------------------------------------------------------------- private List<Rect> GetRectanglesInSingleLine( PTS.FSLINEDESCRIPTIONSINGLE lineDesc, ContentElement e, int start, int length) { // Calculate end of element relative to TextParagraph by adding length to start position int end = start + length; List<Rect> rectangles = new List<Rect>(); // If the element does not lie in the line at all, return empty list if (start >= lineDesc.dcpLim) { // Element starts after line ends return rectangles; } if (end <= lineDesc.dcpFirst) { // Element ends before line starts return rectangles; } // Establish start and end points of element span within the line so that // we can get rectangle between them int localStart = (start < lineDesc.dcpFirst) ? lineDesc.dcpFirst : start; int localEnd = (end < lineDesc.dcpLim) ? end : lineDesc.dcpLim; Debug.Assert(localEnd > localStart); // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); // Get rectangles from start and end positions of range rectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); // Rectangles must have at least one element Invariant.Assert(rectangles.Count > 0); // Dispose the line line.Dispose(); return rectangles; }
private void RenderCompositeLines( ContainerVisual visual, ref PTS.FSTEXTDETAILSFULL textDetails, bool ignoreUpdateInfo) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); VisualCollection visualChildren = visual.Children; int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; // Get list of composite lines. PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Create lines and render them if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo) { // There is no update information, hence need to recreate // visuals for all lines. visualChildren.Clear(); for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; int visualIndex; VisualCollection lineVisuals; if (lineDesc.cElements == 1) { visualIndex = index; lineVisuals = visualChildren; } else { visualIndex = 0; ParagraphElementVisual lineVisual = new ParagraphElementVisual(); visualChildren.Add(lineVisual); lineVisuals = lineVisual.Children; } // Get list of line elements PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Create and format line Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Create and validate line's visual ContainerVisual lineVisual = line.CreateVisual(); lineVisuals.Insert(visualIndex + elIndex, lineVisual); lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); // Dispose the line line.Dispose(); } } } else { // Shift lines before change if(textDetails.dvrShiftBeforeChange != 0) { for (int index = 0; index < textDetails.cLinesBeforeChange; index++) { ContainerVisual lineVisual = (ContainerVisual) visualChildren[index]; Vector offset = lineVisual.Offset; offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange); lineVisual.Offset = offset; } } // Skip not changed lines // Remove changed lines visualChildren.RemoveRange(textDetails.cLinesBeforeChange, textDetails.cLinesChanged - textDetails.dcLinesChanged); // Add new lines for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; int visualIndex; VisualCollection lineVisuals; if (lineDesc.cElements == 1) { visualIndex = index; lineVisuals = visualChildren; } else { visualIndex = 0; ParagraphElementVisual lineVisual = new ParagraphElementVisual(); visualChildren.Add(lineVisual); lineVisuals = lineVisual.Children; } // Get list of line elements. PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Create and format line Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Create and validate line's visual ContainerVisual lineVisual = line.CreateVisual(); lineVisuals.Insert(visualIndex + elIndex, lineVisual); lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); // Dispose the line line.Dispose(); } } // Shift remaining lines for (int index = textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index < arrayLineDesc.Length; index++) { ContainerVisual lineVisual = (ContainerVisual)visualChildren[index]; Vector offset = lineVisual.Offset; offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange); lineVisual.Offset = offset; } } }
// ----------------------------------------------------------------- // From a line desc and the cp for the para start, formats a line and creates a visual // ----------------------------------------------------------------- private ContainerVisual CreateLineVisual(ref PTS.FSLINEDESCRIPTIONSINGLE lineDesc, int cpTextParaStart) { Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); // Create and validate line's visual ContainerVisual lineVisual = line.CreateVisual(); // Dispose the line line.Dispose(); return lineVisual; }
private void GetGlyphRunsFromCompositeLines( List<GlyphRun> glyphRuns, int dcpStart, int dcpEnd, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Range (dcpStart...dcpEnd) needs to insersect with line's range. if (dcpStart < element.dcpLim && dcpEnd > element.dcpFirst) { // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Retrieve glyphs from this line line.GetGlyphRuns(glyphRuns, Math.Max(dcpStart, element.dcpFirst), Math.Min(dcpEnd, element.dcpLim)); // Dispose the line line.Dispose(); } // No need to continue, if dcpEnd has been reached. if (dcpEnd < element.dcpLim) break; } } }
private ITextPointer BackspaceCaretUnitPositionFromDcpCompositeLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Declare backspace position and set it to initial position ITextPointer backspaceCaretPosition = position; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) { if (dcp == element.dcpFirst) { // Beginning of element. if (dcp == 0) { // Assert that this is first elment on first line Debug.Assert(index == 0); Debug.Assert(elIndex == 0); return position; } else { if (elIndex > 0) { // Beginning of element, but not of line --elIndex; element = arrayLineElement[elIndex]; } else { // There must be at least one line above this Debug.Assert(index > 0); // Go to previous line --index; lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { // Stay in same position return position; } else { // Get list of line elements. PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); element = arrayLineElement[arrayLineElement.Length - 1]; } } } } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Create CharacterHit from dcp, and get backspace CharacterHit charHit = new CharacterHit(dcp, 0); CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(charHit); LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == element.dcpFirst) { // Going forward brought us to the start of a line, context must be backward for previous line if (index == 0) { // First line, so we will stay forward logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); // Dispose the line line.Dispose(); return backspaceCaretPosition; } } } return backspaceCaretPosition; }
// Token: 0x06006B39 RID: 27449 RVA: 0x001EF830 File Offset: 0x001EDA30 internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, bool firstLine, int dcpLine) { this.FormatLineCore(line, pbrLineIn, ctx, dcp, width, -1, firstLine, dcpLine); }
private bool IsAtCaretUnitBoundaryFromDcpCompositeLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); bool isAtCaretUnitBoundary = false; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) { CharacterHit charHit = new CharacterHit(); if (dcp >= element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) { // Special case: at the end of the last line there is a special character that // does not belong to the line. Return true for this case return true; } if (position.LogicalDirection == LogicalDirection.Backward) { // Beginning of element. if (dcp == element.dcpFirst) { if (elIndex > 0) { // Beginning of element, but not of line. Create char hit at last dcp of previous element, trailing edge. --elIndex; element = arrayLineElement[elIndex]; charHit = new CharacterHit(dcp - 1, 1); } else { // Beginning of line if (index == 0) { // Backward context at start position of first line is not considered a unit boundary return false; } else { // Go to previous line --index; lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { return false; } else { // Get list of line elements. PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); element = arrayLineElement[arrayLineElement.Length - 1]; charHit = new CharacterHit(dcp - 1, 1); } } } } else { // Get trailing edge of previous dcp Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else if (position.LogicalDirection == LogicalDirection.Forward) { // Create character hit at leading edge charHit = new CharacterHit(dcp, 0); } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); // Dispose the line line.Dispose(); return isAtCaretUnitBoundary; } } } return isAtCaretUnitBoundary; }
private bool IsAtCaretUnitBoundaryFromDcpSimpleLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); bool isAtCaretUnitBoundary = false; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) { CharacterHit charHit = new CharacterHit(); if (dcp >= lineDesc.dcpLim - 1 && index == arrayLineDesc.Length - 1) { // Special case: last line has additional character to mark the end of paragraph. // We should not try and check for next source character index // But just return true in this case return true; } if (position.LogicalDirection == LogicalDirection.Backward) { if (lineDesc.dcpFirst == dcp) { if (index == 0) { // First position of first line does not have a trailing edge. Return false. return false; } else { // Get the trailing edge of the last character on the previous line, at dcp - 1 index--; lineDesc = arrayLineDesc[index]; Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else { // Get CharacterHit at trailing edge of previous position Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else if (position.LogicalDirection == LogicalDirection.Forward) { // Get character hit at leading edge charHit = new CharacterHit(dcp, 0); } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); // Dispose the line line.Dispose(); break; } } return isAtCaretUnitBoundary; }
// ------------------------------------------------------------------ // Returns rectangles for a single composite line correcsponding to the // given dcp range. Includes trailing whitespaces. // Params: // dcpRangeStart - range's cp start position. Adjusted for // line's cp range. // cchRange - nuber of cps in the range. // lineTopSpace - the value that line's height should // be extended to at the top. // lineRightSpace - the value that line's width should // be extended to at the right. // lineDesc - line description. // lineIndex - line index. // elemDesc - element description. // visibleRect - visibility rectangle. It is Ok to return // null if the line is not visible. // hasAttachedObjects- Attached objects are present // Returns: // null - if line is not visible // rectangles - otherwise. // ----------------------------------------------------------------- private List<Rect> RectanglesFromDcpRangeOfCompositeLineElement( int dcpRangeStart, int cchRange, double lineTopSpace, double lineRightSpace, ref PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc, int lineIndex, ref PTS.FSLINEELEMENT elemDesc, int elemIndex, Rect visibleRect) { List<Rect> rectangles = null; Rect elementRect = new PTS.FSRECT(elemDesc.urBBox, lineDesc.vrStart, elemDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); // width has to be adjusted to include trailing whitespaces... LineVisual lineVisual = FetchLineVisualComposite(lineIndex, elemIndex); if (lineVisual != null) { elementRect.Width = Math.Max(lineVisual.WidthIncludingTrailingWhitespace, 0); } elementRect.Y = elementRect.Y - lineTopSpace; elementRect.Height = elementRect.Height + lineTopSpace; elementRect.Width = elementRect.Width + lineRightSpace; // Ignore horizontal offset because TextBox page width != extent width. // It's ok to include content that doesn't strictly intersect -- this // is a perf optimization and the edge cases won't significantly hurt us. Rect testRect = elementRect; testRect.X = visibleRect.X; if (testRect.IntersectsWith(visibleRect)) { // Check whether the line is fully selected - we don't need to reformat it in this case if (dcpRangeStart == elemDesc.dcpFirst && elemDesc.dcpLim <= (dcpRangeStart + cchRange)) { rectangles = new List<Rect>(1); rectangles.Add(elementRect); } else { // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(elemDesc.fClearOnLeft), PTS.ToBoolean(elemDesc.fClearOnRight), TextParagraph.TextRunCache); if (IsOptimalParagraph) { ctx.LineFormatLengthTarget = elemDesc.dcpLim - elemDesc.dcpFirst; } TextParagraph.FormatLineCore(line, elemDesc.pfsbreakreclineclient, ctx, elemDesc.dcpFirst, elemDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), elemDesc.dcpFirst); Invariant.Assert(line.SafeLength == elemDesc.dcpLim - elemDesc.dcpFirst, "Line length is out of [....]"); double duOffset = TextDpi.FromTextDpi(elemDesc.urStart); double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart); rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset); if (!DoubleUtil.IsZero(lineTopSpace)) { for (int i = 0, count = rectangles.Count; i < count; ++i) { Rect r = rectangles[i]; r.Y = r.Y - lineTopSpace; r.Height = r.Height + lineTopSpace; rectangles[i] = r; } } if (!DoubleUtil.IsZero(lineRightSpace)) { // add the rect representing end-of-line / end-of-para rectangles.Add( new Rect( duOffset + TextDpi.FromTextDpi(line.Start + line.Width), dvOffset - lineTopSpace, lineRightSpace, TextDpi.FromTextDpi(line.Height) + lineTopSpace ) ); } // dispose of the line line.Dispose(); } } return (rectangles); }
private void RectFromDcpCompositeLines( int dcp, int originalDcp, LogicalDirection orientation, TextPointerContext context, ref PTS.FSTEXTDETAILSFULL textDetails, ref Rect rect, ref int vrBaseline) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. // But if this is the last line (EOP character), get rectangle form the last // character of the line. if ( ((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) { // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Get rect from cp FlowDirection flowDirection; rect = line.GetBoundsFromTextPosition(dcp, out flowDirection); rect.X += TextDpi.FromTextDpi(element.urStart); rect.Y += TextDpi.FromTextDpi(lineDesc.vrStart); // Return only TopLeft and Height. // Adjust rect.Left by taking into account flow direction of the // content and orientation of input position. if (ThisFlowDirection != flowDirection) { if (orientation == LogicalDirection.Forward) { rect.X = rect.Right; } } else { // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning // content with Backward orientation. This should not be a valid position. // Remove it later if (orientation == LogicalDirection.Backward && originalDcp > 0 && (context == TextPointerContext.Text || context == TextPointerContext.EmbeddedElement)) { rect.X = rect.Right; } } rect.Width = 0; vrBaseline = line.Baseline + lineDesc.vrStart; // Dispose the line line.Dispose(); break; } } } }
internal ITextPointer GetTextPositionFromDistance(int dcpLine, double distance) { // Query paragraph details int urDistance = TextDpi.ToTextDpi(distance); PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); if(ThisFlowDirection != PageFlowDirection) { urDistance = _pageContext.PageRect.du - urDistance; } int lineWidth = 0; bool firstLine = (dcpLine == 0); int dcpLim = 0; IntPtr breakRecLine = IntPtr.Zero; // There are 3 different types of text paragraphs: // (a) full with simple lines // (b) full with composite lines - when figures/floaters are present // (c) cached - when using ParaChache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { if (textDetails.u.full.cLines > 0) { if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); // Get lines information int index; for (index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; if (dcpLine == lineDesc.dcpFirst) { lineWidth = lineDesc.dur; urDistance -= lineDesc.urStart; // Store dcpLim to check if line lengths are in [....] dcpLim = lineDesc.dcpLim; breakRecLine = lineDesc.pfsbreakreclineclient; break; } } } else { // (b) full with composite lines - when figures/floaters are present PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); // Get lines information int index; for (index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) continue; // Get list of line elements. PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); int elIndex; for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; if (element.dcpFirst == dcpLine) { lineWidth = element.dur; urDistance -= element.urStart; // Store dcpLim to check if line lengths are in [....] dcpLim = element.dcpLim; breakRecLine = element.pfsbreakreclineclient; break; } } if (elIndex < arrayLineElement.Length) { firstLine = (index == 0); break; } } } } } else { // (c) cached - when using ParaChache Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Debug.Assert(false, "Should not get here. ParaCache is not currently used."); } // Recreate text line Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = dcpLim - dcpLine; } TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]"); CharacterHit charHit = line.GetTextPositionFromDistance(urDistance); int cpPosition = charHit.FirstCharacterIndex + charHit.TrailingLength; int dcpLastAttachedObject = TextParagraph.GetLastDcpAttachedObjectBeforeLine(dcpLine); if(cpPosition < dcpLastAttachedObject) { cpPosition = dcpLastAttachedObject; } StaticTextPointer pos = TextContainerHelper.GetStaticTextPointerFromCP(Paragraph.StructuralCache.TextContainer, cpPosition + Paragraph.ParagraphStartCharacterPosition); LogicalDirection logicalDirection = (charHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; line.Dispose(); return pos.CreateDynamicTextPointer(logicalDirection); }
private List<Rect> GetRectanglesInCompositeLine( PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc, ContentElement e, int start, int length) { List<Rect> rectangles = new List<Rect>(); int end = start + length; // If the element does not lie in the line at all, return empty list //if (start > lineDesc.dcpLim) //{ // // Element starts after line ends // return rectangles; //} //if (end < lineDesc.dcpFirst) //{ // Element ends before line starts // return rectangles; //} // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Check if element we are looking for does not span the current element at all if (start >= element.dcpLim) { // Element starts after other element ends continue; } if (end <= element.dcpFirst) { // Element ends before line starts continue; } // Establish start and end points of element span within the line so that // we can get rectangle between them int localStart = (start < element.dcpFirst) ? element.dcpFirst : start; int localEnd = (end < element.dcpLim) ? end : element.dcpLim; Debug.Assert(localEnd > localStart); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Get rectangles from start and end positions of range for this element List<Rect> elementRectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); // Rectangles must have at least one element Invariant.Assert(elementRectangles.Count > 0); // Add rectangles from this element to rectangles from whole line rectangles.AddRange(elementRectangles); // Dispose the line line.Dispose(); } return rectangles; }
internal void GetLineDetails(int dcpLine, out int cchContent, out int cchEllipses) { // Query paragraph details PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); int lineWidth = 0; bool firstLine = (dcpLine == 0); int dcpLim = 0; IntPtr breakRecLine = IntPtr.Zero; // There are 3 different types of text paragraphs: // (a) full with simple lines // (b) full with composite lines - when figures/floaters are present // (c) cached - when using ParaChache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { if (textDetails.u.full.cLines > 0) { if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); // Get lines information int index; for (index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; if (dcpLine == lineDesc.dcpFirst) { lineWidth = lineDesc.dur; // Store dcpLim to check that line lengths are in [....] dcpLim = lineDesc.dcpLim; breakRecLine = lineDesc.pfsbreakreclineclient; break; } } } else { // (b) full with composite lines - when figures/floaters are present PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); // Get lines information int index; for (index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) continue; // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); int elIndex; for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; if (element.dcpFirst == dcpLine) { lineWidth = element.dur; // Store dcpLim to check that line lengths are in [....] dcpLim = element.dcpLim; breakRecLine = element.pfsbreakreclineclient; break; } } if (elIndex < arrayLineElement.Length) { firstLine = (index == 0); break; } } } } } else { // (c) cached - when using ParaChache Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Invariant.Assert(false, "Should not get here. ParaCache is not currently used."); } // Recreate text line Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache); Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = dcpLim - dcpLine; } TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]"); cchContent = line.ContentLength; cchEllipses = line.GetEllipsesLength(); line.Dispose(); }
private ITextPointer BackspaceCaretUnitPositionFromDcpSimpleLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Declare backspace position and set it to initial position ITextPointer backspaceCaretPosition = position; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) { if (dcp == lineDesc.dcpFirst) { // Go to previous line if (index == 0) { return position; } else { // Update dcp, lineDesc Debug.Assert(index > 0); --index; lineDesc = arrayLineDesc[index]; } } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); // Create CharacterHit and get backspace index from line API CharacterHit textSourceCharacterIndex = new CharacterHit(dcp, 0); CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex); LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == lineDesc.dcpFirst) { // Going forward brought us to the start of a line, context must be backward for previous line if (index == 0) { // First line, so we will stay forward logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); // Dispose the line line.Dispose(); break; } } Debug.Assert(backspaceCaretPosition != null); return backspaceCaretPosition; }