internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, double paragraphTopSpace, Rect visibleRect) { Geometry geometry = null; Geometry floatAndFigGeometry = null; int cpStartTextPointer = startPosition.Offset; int cpParagraphStart = Paragraph.ParagraphStartCharacterPosition; int dcpStart = Math.Max(cpStartTextPointer, cpParagraphStart) - cpParagraphStart; int cpEndTextPointer = endPosition.Offset; int cpParagraphEnd = Paragraph.ParagraphEndCharacterPosition; int dcpEnd = Math.Min(cpEndTextPointer, cpParagraphEnd) - cpParagraphStart; // apply first line top space only if selection starts before or exactly at this paragraph double firstLineTopSpace = (cpStartTextPointer < cpParagraphStart) ? paragraphTopSpace : 0.0; // handle end-of-para only if the range extends beyond this paragraph bool handleEndOfPara = cpEndTextPointer > cpParagraphEnd; // mirror transform - needed if flow direction changes Transform transform = null; if (ThisFlowDirection != PageFlowDirection) { transform = new MatrixTransform(-1.0, 0.0, 0.0, 1.0, TextDpi.FromTextDpi(2 * _pageContext.PageRect.u + _pageContext.PageRect.du), 0.0); // (and while we are at it) visibleRect should be mirrored too visibleRect = transform.TransformBounds(visibleRect); } // query paragraph details PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); // 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 ParaCache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { if (textDetails.u.full.cLines > 0) { if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines geometry = PathGeometryFromDcpRangeSimpleLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect); } else { // (b) full with composite lines - when figures/floaters are present geometry = PathGeometryFromDcpRangeCompositeLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect); } } // build highlight for floaters and figures in this paragraph if (textDetails.u.full.cAttachedObjects > 0) { floatAndFigGeometry = PathGeometryFromDcpRangeFloatersAndFigures(cpStartTextPointer, cpEndTextPointer, ref textDetails.u.full); } } else { // (c) cached - when using ParaCache Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Debug.Assert(false, "Should not get here. ParaCache is not currently used."); } // at this point geometry contains only the text content related geometry if (geometry != null && transform != null) { // mirror back to page flow direction CaretElement.AddTransformToGeometry(geometry, transform); } // rectangles from which floatAndFigGeometry is calculated are already mirrored. // this is why geometry and floatAndFigGeometry are combined after geometry is mirrored above if (floatAndFigGeometry != null) { CaretElement.AddGeometry(ref geometry, floatAndFigGeometry); } return (geometry); }
/// <summary> /// Transforms point to content's coordinate system. /// </summary> /// <param name="rect">Rect to which transform is applied.</param> private void TransformToContent(ref Rect rect) { // DocumentPage.Visual for printing scenarions needs to be always returned // in LeftToRight FlowDirection. Hence, if the document is RightToLeft, // mirroring transform need to be applied to the content of DocumentPage.Visual. FlowDirection flowDirection = (FlowDirection)_owner.StructuralCache.PropertyOwner.GetValue(FlowDocument.FlowDirectionProperty); if (flowDirection == FlowDirection.RightToLeft) { MatrixTransform transform = new MatrixTransform(-1.0, 0.0, 0.0, 1.0, _owner.Size.Width, 0.0); rect = transform.TransformBounds(rect); } }