/// <summary> /// Creates a new LineBox /// </summary> public CssLineBox(CssBox ownerBox) { _rects = new Dictionary<CssBox, RectangleF>(); _relatedBoxes = new List<CssBox>(); _words = new List<CssBoxWord>(); _ownerBox = ownerBox; _ownerBox.LineBoxes.Add(this); }
public SpacingBox(CssBox tableBox, ref CssBox extendedBox, int startRow) : base(tableBox, new HtmlTag("<none colspan=" + extendedBox.GetAttribute("colspan", "1") + ">")) { ExtendedBox = extendedBox; Display = CssConstants.None; _startRow = startRow; _endRow = startRow + int.Parse(extendedBox.GetAttribute("rowspan", "1")) - 1; }
public CssAnonymousBlockBox(CssBox parent, CssBox insertBefore) : this(parent) { int index = parent.Boxes.IndexOf(insertBefore); if (index < 0) { throw new Exception("insertBefore box doesn't exist on parent"); } parent.Boxes.Remove(this); parent.Boxes.Insert(index, this); }
public CssTable(CssBox tableBox, Graphics g) : this() { if (!(tableBox.Display == CssConstants.Table || tableBox.Display == CssConstants.InlineTable)) throw new ArgumentException("Box is not a table", "tableBox"); _tableBox = tableBox; MeasureWords(tableBox, g); Analyze(g); }
/// <summary> /// Applies special vertical alignment for table-cells /// </summary> /// <param name="g"></param> /// <param name="cell"></param> public static void ApplyCellVerticalAlignment(Graphics g, CssBox cell) { if (cell.VerticalAlign == CssConstants.Top || cell.VerticalAlign == CssConstants.Baseline) return; float celltop = cell.ClientTop; float cellbot = cell.ClientBottom; float bottom = cell.GetMaximumBottom(cell, 0f); float dist = 0f; if (cell.VerticalAlign == CssConstants.Bottom) { dist = cellbot - bottom; } else if (cell.VerticalAlign == CssConstants.Middle) { dist = (cellbot - bottom) / 2; } foreach (CssBox b in cell.Boxes) { b.OffsetTop(dist); } //float top = cell.ClientTop; //float bottom = cell.ClientBottom; //bool middle = cell.VerticalAlign == CssConstants.Middle; //foreach (LineBox line in cell.LineBoxes) //{ // for (int i = 0; i < line.RelatedBoxes.Count; i++) // { // float diff = bottom - line.RelatedBoxes[i].Rectangles[line].Bottom; // if (middle) diff /= 2f; // RectangleF r = line.RelatedBoxes[i].Rectangles[line]; // line.RelatedBoxes[i].Rectangles[line] = new RectangleF(r.X, r.Y + diff, r.Width, r.Height); // } // foreach (BoxWord word in line.Words) // { // float gap = word.Top - top; // word.Top = bottom - gap - word.Height; // } //} }
/// <summary> /// Parses a border value in CSS style; e.g. 1px, 1, thin, thick, medium /// </summary> /// <param name="borderValue"></param> /// <returns></returns> public static float GetActualBorderWidth(string borderValue, CssBox b) { if (string.IsNullOrEmpty(borderValue)) { return GetActualBorderWidth(CssConstants.Medium, b); } switch (borderValue) { case CssConstants.Thin: return 1f; case CssConstants.Medium: return 2f; case CssConstants.Thick: return 4f; default: return Math.Abs(ParseLength(borderValue, 1, b)); } }
/// <summary> /// Creates line boxes for the specified blockbox /// </summary> /// <param name="g"></param> /// <param name="blockBox"></param> public static void CreateLineBoxes(Graphics g, CssBox blockBox) { blockBox.LineBoxes.Clear(); float maxRight = blockBox.ActualRight - blockBox.ActualPaddingRight - blockBox.ActualBorderRightWidth; //Get the start x and y of the blockBox float startx = blockBox.Location.X + blockBox.ActualPaddingLeft - 0 + blockBox.ActualBorderLeftWidth; //TODO: Check for floats float starty = blockBox.Location.Y + blockBox.ActualPaddingTop - 0 + blockBox.ActualBorderTopWidth; float curx = startx + blockBox.ActualTextIndent; float cury = starty; //Reminds the maximum bottom reached float maxBottom = starty; //Extra amount of spacing that should be applied to lines when breaking them. float lineSpacing = 0f; //First line box CssLineBox line = new CssLineBox(blockBox); //Flow words and boxes FlowBox(g, blockBox, blockBox, maxRight, lineSpacing, startx,ref line, ref curx, ref cury, ref maxBottom); //Gets the rectangles foreach linebox foreach (CssLineBox linebox in blockBox.LineBoxes) { BubbleRectangles(blockBox, linebox); linebox.AssignRectanglesToBoxes(); ApplyAlignment(g, linebox); if (blockBox.Direction == CssConstants.Rtl) ApplyRightToLeft(linebox); //linebox.DrawRectangles(g); } blockBox.ActualBottom = maxBottom + blockBox.ActualPaddingBottom + blockBox.ActualBorderBottomWidth; }
internal CssBox(CssBox parentBox, HtmlTag tag) : this(parentBox) { _htmltag = tag; }
/// <summary> /// Creates the <see cref="ListItemBox"/> /// </summary> /// <param name="g"></param> private void CreateListItemBox(Graphics g) { if (Display == CssConstants.ListItem) { if (_listItemBox == null) { _listItemBox = new CssBox(); _listItemBox.InheritStyle(this, false); _listItemBox.Display = CssConstants.Inline; _listItemBox.SetInitialContainer(InitialContainer); if (ParentBox != null && ListStyleType == CssConstants.Decimal) { _listItemBox.Text = GetIndexForList().ToString() + "."; } else { _listItemBox.Text = "�E"; } _listItemBox.MeasureBounds(g); _listItemBox.Size = new SizeF(_listItemBox.Words[0].Width, _listItemBox.Words[0].Height); } _listItemBox.Words[0].Left = Location.X - _listItemBox.Size.Width - 5; _listItemBox.Words[0].Top = Location.Y + ActualPaddingTop;// +FontAscent; } }
public CssBox(CssBox parentBox) : this() { ParentBox = parentBox; }
/// <summary> /// Inherits inheritable values from specified box. /// </summary> /// <param name="everything">Set to true to inherit all CSS properties instead of only the ineritables</param> /// <param name="godfather">Box to inherit the properties</param> internal void InheritStyle(CssBox godfather, bool everything) { if (godfather != null) { IEnumerable<PropertyInfo> pps = everything ? _cssproperties : _inheritables; foreach (PropertyInfo prop in pps) { prop.SetValue(this, prop.GetValue(godfather, null), null); } } }
/// <summary> /// Searches for the first word occourence inside the box, on the specified linebox /// </summary> /// <param name="b"></param> /// <returns></returns> internal CssBoxWord FirstWordOccourence(CssBox b, CssLineBox line) { if (b.Words.Count == 0 && b.Boxes.Count == 0) { return null; } if (b.Words.Count > 0) { foreach (CssBoxWord word in b.Words) { if (line.Words.Contains(word)) { return word; } } return null; } else { foreach (CssBox bb in b.Boxes) { CssBoxWord w = FirstWordOccourence(bb, line); if (w != null) { return w; } } return null; } }
/// <summary> /// Gets the maximum bottom of the boxes inside the startBox /// </summary> /// <param name="startBox"></param> /// <param name="currentMaxBottom"></param> /// <returns></returns> internal float GetMaximumBottom(CssBox startBox, float currentMaxBottom) { foreach (CssLineBox line in startBox.Rectangles.Keys) { currentMaxBottom = Math.Max(currentMaxBottom, startBox.Rectangles[line].Bottom); } foreach (CssBox b in startBox.Boxes) { currentMaxBottom = Math.Max(currentMaxBottom, b.ActualBottom); currentMaxBottom = Math.Max(currentMaxBottom, GetMaximumBottom(b, currentMaxBottom)); } return currentMaxBottom; }
/// <summary> /// Returns a bool indicating if line breaks at the source should be eliminated /// </summary> /// <param name="b"></param> /// <returns></returns> public static bool EliminatesLineBreaks(CssBox b) { return b.WhiteSpace == CssConstants.Normal || b.WhiteSpace == CssConstants.Nowrap; }
/// <summary> /// Cascades to the TD's the border spacified in the TABLE tag. /// </summary> /// <param name="table"></param> /// <param name="border"></param> private void ApplyTablePadding(CssBox table, string padding) { foreach (CssBox box in table.Boxes) { foreach (CssBox cell in box.Boxes) { cell.Padding = TranslateLength(padding); } } }
/// <summary> /// Gets the longest word (in width) inside the box, deeply. /// </summary> /// <param name="b"></param> /// <returns></returns> private void GetMinimumWidth_LongestWord(CssBox b, ref float maxw, ref CssBoxWord word) { if (b.Words.Count > 0) { foreach (CssBoxWord w in b.Words) { if (w.FullWidth > maxw) { maxw = w.FullWidth; word = w; } } } else { foreach(CssBox bb in b.Boxes) GetMinimumWidth_LongestWord(bb, ref maxw,ref word); } }
/// <summary> /// Gets the result of collapsing the vertical margins of the two boxes /// </summary> /// <param name="a">Superior box (checks for margin-bottom)</param> /// <param name="b">Inferior box (checks for margin-top)</param> /// <returns>Maximum of margins</returns> private float MarginCollapse(CssBox a, CssBox b) { return Math.Max( a == null ? 0 : a.ActualMarginBottom, b == null ? 0 : b.ActualMarginTop); }
/// <summary> /// Gets the white space width of the specified box /// </summary> /// <param name="g"></param> /// <param name="b"></param> /// <returns></returns> public static float WhiteSpace(Graphics g, CssBox b) { string space = " ."; float w = 0f; float onError = 5f; StringFormat sf = new StringFormat(); sf.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, 1) }); Region[] regs = g.MeasureCharacterRanges(space, b.ActualFont, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); if (regs == null || regs.Length == 0) return onError; w = regs[0].GetBounds(g).Width; if (!(string.IsNullOrEmpty(b.WordSpacing) || b.WordSpacing == CssConstants.Normal)) { w += CssValue.ParseLength(b.WordSpacing, 0, b); } return w; }
/// <summary> /// Recursively creates the rectangles of the blockBox, by bubbling from deep to outside of the boxes /// in the rectangle structure /// </summary> private static void BubbleRectangles(CssBox box, CssLineBox line) { if (box.Words.Count > 0) { float x = float.MaxValue, y = float.MaxValue, r = float.MinValue, b = float.MinValue; List<CssBoxWord> words = line.WordsOf(box); if (words.Count > 0) { foreach (CssBoxWord word in words) { x = Math.Min(x, word.Left);// - word.SpacesBeforeWidth); r = Math.Max(r, word.Right);// + word.SpacesAfterWidth); y = Math.Min(y, word.Top); b = Math.Max(b, word.Bottom); } line.UpdateRectangle(box, x, y, r, b); } } else { foreach (CssBox b in box.Boxes) { BubbleRectangles(b, line); } } }
/// <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, CssBox blockbox, CssBox box, float maxright, float linespacing, float startx,ref CssLineBox line, ref float curx, ref float cury, ref float maxbottom) { box.FirstHostingLineBox = line; foreach (CssBox b in box.Boxes) { float leftspacing = b.ActualMarginLeft + b.ActualBorderLeftWidth + b.ActualPaddingLeft; float rightspacing = b.ActualMarginRight + b.ActualBorderRightWidth + b.ActualPaddingRight; float topspacing = b.ActualBorderTopWidth + b.ActualPaddingTop; float bottomspacing = b.ActualBorderBottomWidth + b.ActualPaddingTop; b.RectanglesReset(); b.MeasureWordsSize(g); curx += leftspacing; if (b.Words.Count > 0) { #region Flow words foreach (CssBoxWord word in b.Words) { //curx += word.SpacesBeforeWidth; if ((b.WhiteSpace != CssConstants.Nowrap && curx + word.Width + rightspacing > maxright) || word.IsLineBreak) { #region Break line curx = startx; cury = maxbottom + linespacing; line = new CssLineBox(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> /// Gets the longest word (in width) inside the box, deeply. /// </summary> /// <param name="b"></param> /// <returns></returns> private void GetFullWidth_WordsWith(CssBox b, Graphics g, ref float sum, ref float paddingsum) { if (b.Display != CssConstants.Inline) { sum = 0; } paddingsum += b.ActualBorderLeftWidth + b.ActualBorderRightWidth + b.ActualPaddingRight + b.ActualPaddingLeft; if (b.Words.Count > 0) { foreach (CssBoxWord word in b.Words) sum += word.FullWidth; } else { foreach (CssBox bb in b.Boxes) { GetFullWidth_WordsWith(bb, g, ref sum, ref paddingsum); } } }
internal void TranslateAttributes(CssBox box) { string t = TagName.ToUpper(); foreach (string att in Attributes.Keys) { string value = Attributes[att]; switch (att) { case HtmlConstants.align: if (value == HtmlConstants.left || value == HtmlConstants.center || value == HtmlConstants.right || value == HtmlConstants.justify) box.TextAlign = value; else box.VerticalAlign = value; break; case HtmlConstants.background: box.BackgroundImage = value; break; case HtmlConstants.bgcolor: box.BackgroundColor = value; break; case HtmlConstants.border: box.BorderWidth = TranslateLength(value); if (t == HtmlConstants.TABLE) { ApplyTableBorder(box, value); } else { box.BorderStyle = CssConstants.Solid; } break; case HtmlConstants.bordercolor: box.BorderColor = value; break; case HtmlConstants.cellspacing: box.BorderSpacing = TranslateLength(value); break; case HtmlConstants.cellpadding: ApplyTablePadding(box, value); break; case HtmlConstants.color: box.Color = value; break; case HtmlConstants.dir: box.Direction = value; break; case HtmlConstants.face: box.FontFamily = value; break; case HtmlConstants.height: box.Height = TranslateLength(value); break; case HtmlConstants.hspace: box.MarginRight = box.MarginLeft = TranslateLength(value); break; case HtmlConstants.nowrap: box.WhiteSpace = CssConstants.Nowrap; break; case HtmlConstants.size: if (t == HtmlConstants.HR) box.Height = TranslateLength(value); break; case HtmlConstants.valign: box.VerticalAlign = value; break; case HtmlConstants.vspace: box.MarginTop = box.MarginBottom = TranslateLength(value); break; case HtmlConstants.width: box.Width = TranslateLength(value); break; } } }
/// <summary> /// Bubbles up the padding from the starting box /// </summary> /// <param name="box"></param> /// <returns></returns> private void GetMinimumWidth_BubblePadding(CssBox box, CssBox endbox, ref float sum) { //float padding = box.ActualMarginLeft + box.ActualBorderLeftWidth + box.ActualPaddingLeft + // box.ActualMarginRight + box.ActualBorderRightWidth + box.ActualPaddingRight; float padding = box.ActualBorderLeftWidth + box.ActualPaddingLeft + box.ActualBorderRightWidth + box.ActualPaddingRight; sum += padding; if (!box.Equals(endbox)) { GetMinimumWidth_BubblePadding(box.ParentBox, endbox, ref sum); } }
/// <summary> /// Cascades to the TD's the border spacified in the TABLE tag. /// </summary> /// <param name="table"></param> /// <param name="border"></param> private void ApplyTableBorder(CssBox table, string border) { foreach (CssBox box in table.Boxes) { foreach (CssBox cell in box.Boxes) { cell.BorderWidth = TranslateLength(border); } } }
/// <summary> /// Gets the previous sibling of this box. /// </summary> /// <returns>Box before this one on the tree. Null if its the first</returns> private CssBox GetPreviousSibling(CssBox b) { if (b.ParentBox == null) { return null; //This is initial containing block } int index = b.ParentBox.Boxes.IndexOf(this); if (index < 0) throw new Exception("Box doesn't exist on parent's Box list"); if (index == 0) return null; //This is the first sibling. int diff = 1; CssBox sib = b.ParentBox.Boxes[index - diff]; while ((sib.Display == CssConstants.None || sib.Position == CssConstants.Absolute) && index - diff - 1 >= 0) { sib = b.ParentBox.Boxes[index - ++diff]; } return sib.Display == CssConstants.None ? null : sib; }
/// <summary> /// Creates a new BoxWord which represents an image /// </summary> /// <param name="owner"></param> /// <param name="image"></param> public CssBoxWord(CssBox owner, Image image) : this(owner) { Image = image; }
/// <summary> /// Returns a bool indicating if the specified box white-space processing model specifies /// that sequences of white spaces should be collapsed on a single whitespace /// </summary> /// <param name="b"></param> /// <returns></returns> public static bool CollapsesWhiteSpaces(CssBox b) { return b.WhiteSpace == CssConstants.Normal || b.WhiteSpace == CssConstants.Nowrap || b.WhiteSpace == CssConstants.PreLine; }
internal CssBoxWord(CssBox owner) { _ownerBox = owner; _word = string.Empty; }
public CssBoxWordSplitter(CssBox box, string text) : this() { _box = box; _text = text.Replace("\r", string.Empty); ; }
/// <summary> /// Static constructor and initialization /// </summary> static CssBox() { #region Initialize _properties, _inheritables and _defaults Dictionaries _properties = new Dictionary<string, PropertyInfo>(); _defaults = new Dictionary<string, string>(); _inheritables = new List<PropertyInfo>(); _cssproperties = new List<PropertyInfo>(); PropertyInfo[] props = typeof(CssBox).GetProperties(); for (int i = 0; i < props.Length; i++) { CssPropertyAttribute att = Attribute.GetCustomAttribute(props[i], typeof(CssPropertyAttribute)) as CssPropertyAttribute; if (att != null) { _properties.Add(att.Name, props[i]); _defaults.Add(att.Name, GetDefaultValue(props[i])); _cssproperties.Add(props[i]); CssPropertyInheritedAttribute inh = Attribute.GetCustomAttribute(props[i], typeof(CssPropertyInheritedAttribute)) as CssPropertyInheritedAttribute; if (inh != null) { _inheritables.Add(props[i]); } } } #endregion Empty = new CssBox(); }