/// <summary> /// Конструктор. /// </summary> /// <param name="original">Оригинальное состояние.</param> public ReadonlyTextRenderAttributeState(ITextRenderAttributeState original) { var attr = original?.Attributes; if (attr != null) { foreach (var kv in attr) { Attributes[kv.Key] = kv.Value; } } }
/// <summary> /// Выполнить текстовую команду. /// </summary> /// <param name="attributes">Атрибуты.</param> /// <param name="text">Текст.</param> /// <returns>Остаток текста.</returns> protected virtual string ExectuteText(ITextRenderAttributeState attributes, string text) { var words = Splitter.Split(text); var run = CheckLengths(attributes, words); var isAdded = false; Action old = null; var sb = new StringBuilder(); foreach (var r in run) { if (!r.Item2) { bool isThis = false; if (!isAdded) { isAdded = true; var ta = old; if (ta != null) { ta(); } else { if (FirstInLine) { r.Item3?.Invoke(); isThis = true; } else { NewLine(attributes); } } } if (!isThis) { sb.Append(r.Item1); } } old = r.Item3; } if (!isAdded) { var ta = old; ta?.Invoke(); } return sb.ToString(); }
/// <summary> /// Установить высоту линии по умолчанию. /// </summary> /// <param name="attributes">Атрибуты.</param> protected virtual void SetDefaultLineHeight(ITextRenderAttributeState attributes) { var size = Factory.MeasureCommand(new TextRenderCommand(attributes, new TextRenderTextContent("A"))); if (LineHeight < size.Height) { LineHeight = size.Height; } }
/// <summary> /// Начать новую строку. /// </summary> /// <param name="attributes">Атрибуты.</param> protected virtual void NewLine(ITextRenderAttributeState attributes) { Lines++; if (LineHeight < 0.01) { SetDefaultLineHeight(attributes); } Top = Top + LineHeight; LineHeight = 0; LineWidth = 0; FirstInLine = true; SynCanvasHeight(); }
protected IEnumerable<Tuple<string, bool, Action, bool>> CheckLengths(ITextRenderAttributeState attributes, IEnumerable<string> words) { var wordsArr = words.ToArray(); var testCom = new TextRenderCommand(attributes, new TextRenderTextContent("a")); var charWidth = GetElementWidth(testCom); int idx0 = 0; string current = ""; for (int i = 0; i < wordsArr.Length; i++) { var word = wordsArr[i]; current += word; var len = current.Length * charWidth; if ((len + LineWidth) > RenderWidth) { idx0 = i; break; } } int idx1 = -1; for (int j = idx0; j >= 0; j--) { current = ""; for (int i = 0; i <= j; i++) { var word = wordsArr[i]; current += word; } var com = new TextRenderCommand(attributes, new TextRenderTextContent(current)); var elWidth = GetElementWidth(com); if (!((elWidth + LineWidth) > RenderWidth)) { idx1 = j; break; } } bool exceeded = false; current = ""; for (int i = 0; i < wordsArr.Length; i++) { var word = wordsArr[i]; current += word; if (exceeded) { yield return new Tuple<string, bool, Action, bool>(word, false, null, true); } else { var com = new TextRenderCommand(attributes, new TextRenderTextContent(current)); if (i > idx1) { var elWidth = GetElementWidth(com); if ((elWidth + LineWidth) > RenderWidth) { exceeded = true; } yield return new Tuple<string, bool, Action, bool>(word, !exceeded, () => AddElementToCanvas(com), false); } else { yield return new Tuple<string, bool, Action, bool>(word, true, () => AddElementToCanvas(com), false); } } } }
/// <summary> /// Конструктор. /// </summary> /// <param name="attributes">Атрибуты.</param> /// <param name="content">Контент.</param> public TextRenderCommand(ITextRenderAttributeState attributes, ITextRenderContent content) { Attributes = attributes; Content = content; }
/// <summary> /// Установить высоту линии по умолчанию. /// </summary> /// <param name="attributes">Атрибуты.</param> protected virtual void SetDefaultLineHeight(ITextRenderAttributeState attributes) { var el = Factory.Create(new TextRenderCommand(attributes, new TextRenderTextContent("A"))); if (LineHeight < el.Height) { LineHeight = el.Height; } }
protected IEnumerable<Tuple<string, bool, Func<FrameworkElement>, bool>> CheckLengths(ITextRenderAttributeState attributes, IEnumerable<string> words) { var wordsArr = GetRoughLengths(attributes, words).ToArray(); int idx0 = 0; string current = ""; double len = 0; for (int i = 0; i < wordsArr.Length; i++) { var word = wordsArr[i]; current += word.Item1; len = word.Item2; if ((len + LineWidth) > TextCanvas.ActualWidth) { idx0 = i; break; } } int idx1 = -1; for (int j = idx0; j >= 0; j--) { current = ""; for (int i = 0; i <= j; i++) { var word = wordsArr[i].Item1; current += word; } var com = new TextRenderCommand(attributes, new TextRenderTextContent(current)); var key = GetElementKey(com); var elWidth = GetElementWidth(key, com); if (!((elWidth + LineWidth) > TextCanvas.ActualWidth)) { idx1 = j; break; } } bool exceeded = false; current = ""; for (int i = 0; i < wordsArr.Length; i++) { var word = wordsArr[i].Item1; current += word; if (exceeded) { yield return new Tuple<string, bool, Func<FrameworkElement>, bool>(word, false, () => null, true); } else { var com = new TextRenderCommand(attributes, new TextRenderTextContent(current)); var key = GetElementKey(com); if (i > idx1) { var elWidth = GetElementWidth(key, com); if ((elWidth + LineWidth) > TextCanvas.ActualWidth) { exceeded = true; } yield return new Tuple<string, bool, Func<FrameworkElement>, bool>(word, !exceeded, ExtractElement(key, com), false); } else { yield return new Tuple<string, bool, Func<FrameworkElement>, bool>(word, true, ExtractElement(key, com), false); } } } }
protected IEnumerable<Tuple<string, double>> GetRoughLengths(ITextRenderAttributeState attributes, IEnumerable<string> words) { var wordsArr = words.ToArray(); var agg = wordsArr.Aggregate(new StringBuilder(), (sb, s) => sb.Append(s)).ToString(); var aggCom = new TextRenderCommand(attributes, new TextRenderTextContent(agg)); var aggKey = GetElementKey(aggCom); var totalWidth = GetElementWidth(aggKey, aggCom); var totalCount = agg.Length > 0 ? agg.Length : 1; int cnt = 0; foreach (var word in wordsArr) { cnt += word.Length; yield return new Tuple<string, double>(word, totalWidth * cnt / totalCount); } }
private double GetOWidth(ITextRenderAttributeState attributes, TextBlock r) { var key = GetCacheKey(new TextRenderCommand(attributes, new TextRenderTextContent("o"))); if (!OWidthCache.ContainsKey(key)) { var s2 = r.Text; r.Text = "o"; r.Measure(new Size(0, 0)); OWidthCache[key] = r.ActualWidth; r.Text = s2; } return OWidthCache[key]; }