/// <summary> /// Splits the text on words using rules of the specified box /// </summary> /// <returns></returns> public void SplitWords() { if (string.IsNullOrEmpty(Text)) return; _curword = new BoxWord(Box); var onspace = IsSpace(Text[0]); for (var i = 0; i < Text.Length; i++) { if (IsSpace(Text[i])) { if (!onspace) CutWord(); if (IsLineBreak(Text[i])) { _curword.AppendChar('\n'); CutWord(); } else if (IsTab(Text[i])) { _curword.AppendChar('\t'); CutWord(); } else { _curword.AppendChar(' '); } onspace = true; } else { if (onspace) CutWord(); _curword.AppendChar(Text[i]); onspace = false; } } CutWord(); }
/// <summary> /// Gets the longest word (in width) inside the box, deeply. /// </summary> /// <param name="b"></param> /// <param name="maxw"></param> /// <param name="word"></param> /// <returns></returns> private void GetMinimumWidth_LongestWord(Box b, ref float maxw, ref BoxWord word) { if (b.Words.Count > 0) { foreach (var w in b.Words) { if (!(w.FullWidth > maxw)) continue; maxw = w.FullWidth; word = w; } } else { foreach (var bb in b.Boxes) GetMinimumWidth_LongestWord(bb, ref maxw, ref word); } }
/// <summary> /// Assigns words its width and height /// </summary> /// <param name="g"></param> internal void MeasureWordsSize(Graphics g) { if (_wordsSizeMeasured) return; //Measure white space if not yet done if (float.IsNaN(_actualWordSpacing)) MeasureWordSpacing(g); if (HtmlTag != null && HtmlTag.TagName.Equals("img", StringComparison.CurrentCultureIgnoreCase)) { #region Measure image var word = new BoxWord(this, Value.GetImage(GetAttribute("src"))); Words.Clear(); Words.Add(word); #endregion } else { #region Measure text words var lastWasSpace = false; foreach (var b in Words) { var collapse = BoxWordSplitter.CollapsesWhiteSpaces(this); if (BoxWordSplitter.EliminatesLineBreaks(this)) b.ReplaceLineBreaksAndTabs(); if (b.IsSpaces) { b.Height = FontLineSpacing; if (b.IsTab) { b.Width = ActualWordSpacing*4; //TODO: Configure tab size } else if (b.IsLineBreak) { b.Width = 0; } else { if (!(lastWasSpace && collapse)) { b.Width = ActualWordSpacing*(collapse ? 1 : b.Text.Length); } } lastWasSpace = true; } else { var word = b.Text; CharacterRange[] measurable = {new CharacterRange(0, word.Length)}; var sf = new StringFormat(); sf.SetMeasurableCharacterRanges(measurable); var regions = g.MeasureCharacterRanges(word, ActualFont, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); var s = regions[0].GetBounds(g).Size; var p = regions[0].GetBounds(g).Location; b.LastMeasureOffset = new PointF(p.X, p.Y); b.Width = s.Width; // +p.X; b.Height = s.Height; // +p.Y; lastWasSpace = false; } } #endregion } _wordsSizeMeasured = true; }
/// <summary> /// Recursively flows the content of the box using the inline model /// </summary> /// <param name="g">Device Info</param> /// <param name="blockbox">Blockbox that contains the text flow</param> /// <param name="box">Current box to flow its content</param> /// <param name="maxright">Maximum reached right</param> /// <param name="linespacing">Space to use between rows of text</param> /// <param name="startx">x starting coordinate for when breaking lines of text</param> /// <param name="line">Current linebox being used</param> /// <param name="curx">Current x coordinate that will be the left of the next word</param> /// <param name="cury">Current y coordinate that will be the top of the next word</param> /// <param name="maxbottom">Maximum bottom reached so far</param> private static void FlowBox(Graphics g, Box blockbox, Box box, float maxright, float linespacing, float startx, ref LineBox line, ref float curx, ref float cury, ref float maxbottom) { box.FirstHostingLineBox = line; foreach (var b in box.Boxes) { var leftspacing = b.ActualMarginLeft + b.ActualBorderLeftWidth + b.ActualPaddingLeft; var rightspacing = b.ActualMarginRight + b.ActualBorderRightWidth + b.ActualPaddingRight; var topspacing = b.ActualBorderTopWidth + b.ActualPaddingTop; var bottomspacing = b.ActualBorderBottomWidth + b.ActualPaddingTop; b.RectanglesReset(); b.MeasureWordsSize(g); curx += leftspacing; if (b.Words.Count > 0) { #region Flow words foreach (var word in b.Words) { //curx += word.SpacesBeforeWidth; if ((b.WhiteSpace != Constants.Nowrap && curx + word.Width + rightspacing > maxright) || word.IsLineBreak) { #region Break line curx = startx; cury = maxbottom + linespacing; line = new LineBox(blockbox); if (word.IsImage || word.Equals(b.FirstWord)) { curx += leftspacing; } #endregion } line.ReportExistanceOf(word); word.Left = curx; // -word.LastMeasureOffset.X + 1; word.Top = cury; // - word.LastMeasureOffset.Y; curx = word.Right; // +word.SpacesAfterWidth; maxbottom = Math.Max(maxbottom, word.Bottom); //+ (word.IsImage ? topspacing + bottomspacing : 0)); _lastTreatedWord = word; } #endregion } else { FlowBox(g, blockbox, b, maxright, linespacing, startx, ref line, ref curx, ref cury, ref maxbottom); } curx += rightspacing; } box.LastHostingLineBox = line; }
/// <summary> /// Lets the linebox add the word an its box to their lists if necessary. /// </summary> /// <param name="word"></param> internal void ReportExistanceOf(BoxWord word) { if (!Words.Contains(word)) { Words.Add(word); } if (!RelatedBoxes.Contains(word.OwnerBox)) { RelatedBoxes.Add(word.OwnerBox); } }
private BoxWordSplitter() { Words = new List<BoxWord>(); _curword = null; }
private void CutWord() { if (_curword.Text.Length > 0) Words.Add(_curword); _curword = new BoxWord(Box); }