// Token: 0x06006644 RID: 26180 RVA: 0x001CBE1C File Offset: 0x001CA01C protected TextCollapsingProperties GetCollapsingProps(double wrappingWidth, LineProperties paraProperties) { TextCollapsingProperties result; if (paraProperties.TextTrimming == TextTrimming.CharacterEllipsis) { result = new TextTrailingCharacterEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } else { result = new TextTrailingWordEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } return(result); }
// ------------------------------------------------------------------ // Get collapsing properties. // // wrappingWidth - wrapping width for collapsed line. // paraProperties - paragraph properties. // // Returns: Line collapsing properties. // ------------------------------------------------------------------ protected TextCollapsingProperties GetCollapsingProps(double wrappingWidth, LineProperties paraProperties) { Debug.Assert(paraProperties.TextTrimming != TextTrimming.None, "Text trimming must be enabled."); TextCollapsingProperties collapsingProps; if (paraProperties.TextTrimming == TextTrimming.CharacterEllipsis) { collapsingProps = new TextTrailingCharacterEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } else { collapsingProps = new TextTrailingWordEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } return(collapsingProps); }
// ------------------------------------------------------------------ // Get collapsing properties. // // wrappingWidth - wrapping width for collapsed line. // paraProperties - paragraph properties. // // Returns: Line collapsing properties. // ------------------------------------------------------------------ protected TextCollapsingProperties GetCollapsingProps(double wrappingWidth, LineProperties paraProperties) { Debug.Assert(paraProperties.TextTrimming != TextTrimming.None, "Text trimming must be enabled."); TextCollapsingProperties collapsingProps; if (paraProperties.TextTrimming == TextTrimming.CharacterEllipsis) { collapsingProps = new TextTrailingCharacterEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } else { collapsingProps = new TextTrailingWordEllipsis(wrappingWidth, paraProperties.DefaultTextRunProperties); } return collapsingProps; }
// ------------------------------------------------------------------ // Constructor. // ------------------------------------------------------------------ internal FirstLineProperties(LineProperties lp) { _lp = lp; // properly set the local copy of hyphenator accordingly. Hyphenator = lp.Hyphenator; }
//------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------- #region Internal Methods /// <summary> /// Create and format text line. /// </summary> /// <param name="dcp">First character position for the line.</param> /// <param name="formatWidth">Width to pass to LS formatter.</param> /// <param name="paragraphWidth">Line wrapping width.</param> /// <param name="lineProperties">Line's properties.</param> /// <param name="textRunCache">Run cache.</param> /// <param name="formatter">Text formatter.</param> /// <remarks> /// formatWidth/paragraphWidth is an attempt to work around bug 114719. /// Unfortunately, Line Services cannot guarantee that once a line /// has been measured, measuring the same content with the actual line /// width will produce the same line. /// /// For example, suppose we format dcp 0 with paragraphWidth = 100. /// Suppose this results in a line from dcp 0 - 10, with width = 95. /// /// We would expect that a call to FormatLine with dcp = 0, /// paragraphWidth = 95 would result in the same 10 char line. /// But in practice it might return a 9 char line. /// /// The workaround is to pass in an explicit formatting width across /// multiple calls, even if the paragraphWidth changes. /// </remarks> internal void Format(int dcp, double formatWidth, double paragraphWidth, LineProperties lineProperties, TextRunCache textRunCache, TextFormatter formatter) { _lineProperties = lineProperties; _dcp = dcp; _paragraphWidth = paragraphWidth; // We must ignore TextAlignment here since formatWidth does not // necessarilly equal paragraphWidth. We'll adjust on later calls. lineProperties.IgnoreTextAlignment = (lineProperties.TextAlignment != TextAlignment.Justify); try { _line = formatter.FormatLine(this, dcp, formatWidth, lineProperties, null, textRunCache); } finally { lineProperties.IgnoreTextAlignment = false; } }
// Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete. // Formats line until we hit a synchronization point, a position where we know // following lines could not be affected by the change. private void SyncLineMetrics(DirtyTextRange range, double constraintWidth, LineProperties lineProperties, TextBoxLine line, bool endOfParagraph, int lineIndex, int lineOffset) { bool offsetSyncOk = (range.PositionsAdded == 0 || range.PositionsRemoved == 0); int lastCoveredCharOffset = range.StartIndex + Math.Max(range.PositionsAdded, range.PositionsRemoved); // Keep updating lines until we find a synchronized position. while (!endOfParagraph && (lineIndex == _lineMetrics.Count || !offsetSyncOk || lineOffset != _lineMetrics[lineIndex].Offset)) { if (lineIndex < _lineMetrics.Count && lineOffset >= _lineMetrics[lineIndex].EndOffset) { // If the current line offset starts past the current line metric offset, // remove the metric. This happens when the previous line // frees up enough space to completely consume the following line. // We can't simply replace the record without potentially missing our // [....] position. _lineMetrics.RemoveAt(lineIndex); // RemoveLineVisualRange(lineIndex, 1); } else { using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); LineRecord record = new LineRecord(lineOffset, line); if (lineIndex == _lineMetrics.Count || lineOffset + line.Length <= _lineMetrics[lineIndex].Offset) { // The new line preceeds the old line, insert a new record. // _lineMetrics.Insert(lineIndex, record); AddLineVisualPlaceholder(lineIndex); } else { // We expect to be colliding with the old line directly. // If we extend past it, we're in danger of needlessly // re-formatting the entire doc (ie, we miss the real // [....] position and don't stop until EndOfParagraph). Invariant.Assert(lineOffset < _lineMetrics[lineIndex].EndOffset); _lineMetrics[lineIndex] = record; ClearLineVisual(lineIndex); // If this line ends past the invalidated region, and it // has a hard line break, it's safe to synchronize on the next // line metric with a matching start offset. offsetSyncOk |= lastCoveredCharOffset <= record.EndOffset && line.HasLineBreak; } lineIndex++; lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } } } // Remove any trailing lines that got absorbed into the new last line. if (endOfParagraph && lineIndex < _lineMetrics.Count) { int count = _lineMetrics.Count - lineIndex; _lineMetrics.RemoveRange(lineIndex, count); RemoveLineVisualRange(lineIndex, count); } }
//------------------------------------------------------------------- // Calculate line advance distance. This functionality will go away // when TextFormatter will be able to handle line height/stacking. // // lineHeight - calculated line height // // Returns: Line advance distance.. //------------------------------------------------------------------- private double CalcLineAdvance(double lineHeight, LineProperties lineProperties) { return lineProperties.CalcLineAdvance(lineHeight); }
//------------------------------------------------------------------- // Get line properties // // firstLine - is it for the first line? // // Returns: Line properties for first/following lines. //------------------------------------------------------------------- private TextParagraphProperties GetLineProperties(bool firstLine, LineProperties lineProperties) { return GetLineProperties(firstLine, false, lineProperties); }
// Measures content invalidated due to a TextContainer change (rather than // a constraint change). // // Returns the full content size, omitting any unanalyzed content // at the document end (due to pending background layout). private Size IncrementalMeasure(double constraintWidth, LineProperties lineProperties) { Invariant.Assert(_dirtyList != null); Invariant.Assert(_dirtyList.Length > 0); // We only allocate _dirtyList when it has content. Size desiredSize = _contentSize; DirtyTextRange range = _dirtyList[0]; // Background layout may be running, in which case we need to // "clip" the scope of this incremental edit. We want to ignore // changes that extend past the area of the document we're already // tracking. if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); return desiredSize; } // Merge the dirty list into a single superset DirtyTextRange. // int previousOffset = range.StartIndex; int positionsAdded = range.PositionsAdded; int positionsRemoved = range.PositionsRemoved; for (int i = 1; i < _dirtyList.Length; i++) { range = _dirtyList[i]; if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); break; } int rangeDistance = range.StartIndex - previousOffset; positionsAdded += rangeDistance + range.PositionsAdded; positionsRemoved += rangeDistance + range.PositionsRemoved; previousOffset = range.StartIndex; } range = new DirtyTextRange(_dirtyList[0].StartIndex, positionsAdded, positionsRemoved); if (range.PositionsAdded >= range.PositionsRemoved) { IncrementalMeasureLinesAfterInsert(constraintWidth, lineProperties, range, ref desiredSize); } else if (range.PositionsAdded < range.PositionsRemoved) { IncrementalMeasureLinesAfterDelete(constraintWidth, lineProperties, range, ref desiredSize); } return desiredSize; }
// Performs one iteration of background measure. // Background measure always works at the end of the current // line metrics array -- invalidations to prevoiusly examined // content is handled by incremental layout, synchronously. // // Returns the full content size, omitting any unanalyzed content // at the document end. private Size FullMeasureTick(double constraintWidth, LineProperties lineProperties) { Size desiredSize; TextBoxLine line = new TextBoxLine(this); int lineOffset; bool endOfParagraph; // Find the next position for this iteration. if (_lineMetrics.Count == 0) { desiredSize = new Size(); lineOffset = 0; } else { desiredSize = _contentSize; lineOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; } // Calculate a stop time. // We limit work to just a few milliseconds per iteration // to avoid blocking the thread. DateTime stopTime; if ((ScrollBarVisibility)((Control)_host).GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty) == ScrollBarVisibility.Auto) { // Workaround for bug 1766924. // When VerticalScrollBarVisiblity == Auto, there's a problem with // our interaction with ScrollViewer. Disable background layout to // mitigate the problem until we can take a real fix in v.next. // stopTime = DateTime.MaxValue; } else { stopTime = DateTime.Now.AddMilliseconds(_maxMeasureTimeMs); } // Format lines until we hit the end of document or run out of time. do { using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); // This is a loop invariant, but has negligable cost. // _lineHeight = lineProperties.CalcLineAdvance(line.Height); _lineMetrics.Add(new LineRecord(lineOffset, line)); // Desired width is always max of calculated line widths. // Desired height is sum of all line heights. desiredSize.Width = Math.Max(desiredSize.Width, line.Width); desiredSize.Height += _lineHeight; lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } } while (!endOfParagraph && DateTime.Now < stopTime); if (!endOfParagraph) { // Ran out of time. Defer to background layout. SetFlags(true, Flags.BackgroundLayoutPending); this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnBackgroundMeasure), null); } else { // Finished the entire document. Stop background layout. SetFlags(false, Flags.BackgroundLayoutPending); } return desiredSize; }
// Returns a formatted TextBoxLine at the specified index. // Caller must Dispose the TextBoxLine. // This method is expensive. private TextBoxLine GetFormattedLine(int lineIndex, out LineProperties lineProperties) { TextBoxLine line = new TextBoxLine(this); LineRecord metrics = _lineMetrics[lineIndex]; lineProperties = GetLineProperties(); Control hostControl = (Control)_host; TextFormattingMode textFormattingMode = TextOptions.GetTextFormattingMode(hostControl); TextFormatter formatter = TextFormatter.FromCurrentDispatcher(textFormattingMode); double width = GetWrappingWidth(this.RenderSize.Width); double formatWidth = GetWrappingWidth(_previousConstraint.Width); line.Format(metrics.Offset, formatWidth, width, lineProperties, new TextRunCache(), formatter); Invariant.Assert(metrics.Length == line.Length, "Line is out of [....] with metrics!"); return line; }
internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) { // Query paragraph details PTS.FSSUBTRACKDETAILS subtrackDetails; PTS.Validate(PTS.FsQuerySubtrackDetails(PtsContext.Context, _paraHandle.Value, out subtrackDetails)); // Draw border and background info. MbpInfo mbp = MbpInfo.FromElement(Paragraph.Element); if (ThisFlowDirection != PageFlowDirection) { mbp.MirrorBP(); } uint fswdir = PTS.FlowDirectionToFswdir((FlowDirection)Paragraph.Element.GetValue(FrameworkElement.FlowDirectionProperty)); Brush backgroundBrush = (Brush)Paragraph.Element.GetValue(TextElement.BackgroundProperty); TextProperties textProperties = new TextProperties(Paragraph.Element, StaticTextPointer.Null, false /* inline objects */, false /* get background */); // There might be possibility to get empty sub-track, skip the sub-track in such case. if (subtrackDetails.cParas != 0) { PTS.FSPARADESCRIPTION [] arrayParaDesc; PtsHelper.ParaListFromSubtrack(PtsContext, _paraHandle.Value, ref subtrackDetails, out arrayParaDesc); using(DrawingContext ctx = _visual.RenderOpen()) { _visual.DrawBackgroundAndBorderIntoContext(ctx, backgroundBrush, mbp.BorderBrush, mbp.Border, _rect.FromTextDpi(), IsFirstChunk, IsLastChunk); // Get list of paragraphs ListMarkerLine listMarkerLine = new ListMarkerLine(Paragraph.StructuralCache.TextFormatterHost, this); int indexFirstParaInSubtrack = 0; for(int index = 0; index < subtrackDetails.cParas; index++) { List list = Paragraph.Element as List; BaseParaClient listItemParaClient = PtsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient; PTS.ValidateHandle(listItemParaClient); if(index == 0) { indexFirstParaInSubtrack = list.GetListItemIndex(listItemParaClient.Paragraph.Element as ListItem); } if(listItemParaClient.IsFirstChunk) { int dvBaseline = listItemParaClient.GetFirstTextLineBaseline(); if(PageFlowDirection != ThisFlowDirection) { ctx.PushTransform(new MatrixTransform(-1.0, 0.0, 0.0, 1.0, TextDpi.FromTextDpi(2 * listItemParaClient.Rect.u + listItemParaClient.Rect.du), 0.0)); } int adjustedIndex; if (int.MaxValue - index < indexFirstParaInSubtrack) { adjustedIndex = int.MaxValue; } else { adjustedIndex = indexFirstParaInSubtrack + index; } LineProperties lineProps = new LineProperties(Paragraph.Element, Paragraph.StructuralCache.FormattingOwner, textProperties, new MarkerProperties(list, adjustedIndex)); listMarkerLine.FormatAndDrawVisual(ctx, lineProps, listItemParaClient.Rect.u, dvBaseline); if(PageFlowDirection != ThisFlowDirection) { ctx.Pop(); } } } listMarkerLine.Dispose(); } // Render list of paragraphs PtsHelper.UpdateParaListVisuals(PtsContext, _visual.Children, fskupdInherited, arrayParaDesc); } else { _visual.Children.Clear(); } }
/// <summary> /// Create and format text line. /// </summary> /// <param name="ctx"> /// DrawingContext for text line. /// </param> /// <param name="lineProps"> /// LineProperties of text line /// </param> /// <param name="ur"> /// Horizontal draw location /// </param> /// <param name="vrBaseline"> /// Vertical baseline draw location /// </param> internal void FormatAndDrawVisual(DrawingContext ctx, LineProperties lineProps, int ur, int vrBaseline) { System.Windows.Media.TextFormatting.TextLine line; bool mirror = (lineProps.FlowDirection == FlowDirection.RightToLeft); _host.Context = this; try { // Create line object line = _host.TextFormatter.FormatLine(_host, 0, 0, lineProps.FirstLineProps, null, new TextRunCache()); Point drawLocation = new Point(TextDpi.FromTextDpi(ur), TextDpi.FromTextDpi(vrBaseline) - line.Baseline); line.Draw(ctx, drawLocation, (mirror ? InvertAxes.Horizontal : InvertAxes.None)); line.Dispose(); } finally { // clear the context _host.Context = null; } }
// ------------------------------------------------------------------ // Create appropriate line object. // a) SimpleLine, if the content is represented by a string. // b) ComplexLine, if the content is represented by a TextContainer. // ------------------------------------------------------------------ private Line CreateLine(LineProperties lineProperties) { Line line; if (_complexContent == null) line = new SimpleLine(this, Text, lineProperties.DefaultTextRunProperties); else line = new ComplexLine(this); return line; }
// ------------------------------------------------------------------ // Refetch and cache line properties, if needed. // ------------------------------------------------------------------ private LineProperties GetLineProperties() { // For default text properties always set background to null. // REASON: If element associated with the text run is TextBlock element, ignore background // brush, because it is handled outside as FrameworkElement's background. TextProperties defaultTextProperties = new TextProperties(this, this.IsTypographyDefaultValue); // Do not allow hyphenation for plain Text so always pass null for IHyphenate. // Pass page width and height as double.MaxValue when creating LineProperties, since TextBlock does not restrict // TextIndent or LineHeight LineProperties lineProperties = new LineProperties(this, this, defaultTextProperties, null); bool isHyphenationEnabled = (bool) this.GetValue(IsHyphenationEnabledProperty); if(isHyphenationEnabled) { lineProperties.Hyphenator = EnsureHyphenator(); } return lineProperties; }
// Measures content invalidated due to a TextContainer change. private void IncrementalMeasureLinesAfterInsert(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize) { int delta = range.PositionsAdded - range.PositionsRemoved; Invariant.Assert(delta >= 0); int lineIndex = GetLineIndexFromOffset(range.StartIndex, LogicalDirection.Forward); if (delta > 0) { // Increment of the offsets of all following lines. // for (int i = lineIndex + 1; i < _lineMetrics.Count; i++) { _lineMetrics[i].Offset += delta; } } TextBoxLine line = new TextBoxLine(this); int lineOffset; bool endOfParagraph = false; // We need to re-format the previous line, because if someone inserted // a hard break, the first directly affected line might now be shorter // and mergeable with its predecessor. if (lineIndex > 0) // { FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); } else { lineOffset = _lineMetrics[lineIndex].Offset; } // Format the line directly affected by the change. // If endOfParagraph == true, then the line was absorbed into its // predessor (because its new content is thinner, or because the // TextWrapping property changed). if (!endOfParagraph) { using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } ClearLineVisual(lineIndex); lineIndex++; } // Recalc the following lines not directly affected as needed. SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); desiredSize = BruteForceCalculateDesiredSize(); }
private TextParagraphProperties GetLineProperties(bool firstLine, bool showParagraphEllipsis, LineProperties lineProperties) { GetLineProperties(); firstLine = firstLine && lineProperties.HasFirstLineProperties; if (!showParagraphEllipsis) { return firstLine ? lineProperties.FirstLineProps : lineProperties; } else { return lineProperties.GetParaEllipsisLineProps(firstLine); } }
// Measures content invalidated due to a TextContainer change. private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize) { int delta = range.PositionsAdded - range.PositionsRemoved; Invariant.Assert(delta < 0); int firstLineIndex = GetLineIndexFromOffset(range.StartIndex); // Clip the scope of the affected lines to the region of the document // we've already inspected. Clipping happens when background layout // has not yet completed but an incremental update happens. int endOffset = range.StartIndex + -delta - 1; if (endOffset > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); endOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; if (range.StartIndex == endOffset) { // Nothing left to do until background layout runs. return; } } int lastLineIndex = GetLineIndexFromOffset(endOffset); // Increment the offsets of all following lines. // for (int i = lastLineIndex + 1; i < _lineMetrics.Count; i++) { _lineMetrics[i].Offset += delta; } TextBoxLine line = new TextBoxLine(this); int lineIndex = firstLineIndex; int lineOffset; bool endOfParagraph; // We need to re-format the previous line, because if someone inserted // a hard break, the first directly affected line might now be shorter // and mergeable with its predecessor. if (lineIndex > 0) // { FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); } else { lineOffset = _lineMetrics[lineIndex].Offset; endOfParagraph = false; } // // Update the first affected line. If it's completely covered, remove it entirely below. if (!endOfParagraph && (range.StartIndex > lineOffset || range.StartIndex + -delta < _lineMetrics[lineIndex].EndOffset)) { // Only part of the line is covered, reformat it. using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } ClearLineVisual(lineIndex); lineIndex++; } // Remove all the following lines that are completely covered. // _lineMetrics.RemoveRange(lineIndex, lastLineIndex - lineIndex + 1); RemoveLineVisualRange(lineIndex, lastLineIndex - lineIndex + 1); // Recalc the following lines not directly affected as needed. SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); desiredSize = BruteForceCalculateDesiredSize(); }
// Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete. // Formats the line preceding the first directly affected line after a TextContainer change. // In general this line might grow as content in the following line is absorbed. private void FormatFirstIncrementalLine(int lineIndex, double constraintWidth, LineProperties lineProperties, TextBoxLine line, out int lineOffset, out bool endOfParagraph) { int originalEndOffset = _lineMetrics[lineIndex].EndOffset; lineOffset = _lineMetrics[lineIndex].Offset; using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } // Don't clear the cached Visual unless something changed. if (originalEndOffset != _lineMetrics[lineIndex].EndOffset) { ClearLineVisual(lineIndex); } }
// Token: 0x06008A92 RID: 35474 RVA: 0x00257536 File Offset: 0x00255736 internal FirstLineProperties(LineProperties lp) { this._lp = lp; this.Hyphenator = lp.Hyphenator; }