/// <summary> /// Init. /// </summary> public CssParser(RAdapter adapter) { ArgChecker.AssertArgNotNull(adapter, "global"); _valueParser = new CssValueParser(adapter); _adapter = adapter; }
static void CreateSvgEllipse(SvgElement parentNode, HtmlElement elem) { SvgEllipseSpec spec = new SvgEllipseSpec(); SvgEllipse shape = new SvgEllipse(spec, elem); parentNode.AddChild(shape); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WebDom.WellknownName.Svg_Cx: { spec.X = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Cy: { spec.Y = UserMapUtil.ParseGenericLength(attr.Value); } break; case WellknownName.Svg_Rx: { spec.RadiusX = UserMapUtil.ParseGenericLength(attr.Value); } break; case WellknownName.Svg_Ry: { spec.RadiusY = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Fill: { spec.ActualColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke: { spec.StrokeColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke_Width: { spec.StrokeWidth = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Transform: { //TODO: parse svg transform function } break; default: { //other attrs } break; } } }
private void CalcTableSize(RGraphics g) { if (Display != CssConstants.None) { RectanglesReset(); MeasureWordsSize(g); } double width = ContainingBlock.Size.Width - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth; double height = ContainingBlock.Size.Height - ContainingBlock.ActualPaddingTop - ContainingBlock.ActualPaddingBottom - ContainingBlock.ActualBorderTopWidth - ContainingBlock.ActualBorderBottomWidth; bool _widthSpecified = false; bool _heightSpecified = false; if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width)) { width = CssValueParser.ParseLength(Width, width, this); _widthSpecified = width > 0 ? true : false; } if (Height != CssConstants.Auto && !string.IsNullOrEmpty(Height)) { height = CssValueParser.ParseLength(Height, height, this); _heightSpecified = height > 0 ? true : false; } Size = new RSize(_widthSpecified ? width : 0, _heightSpecified ? height : 0); }
/// <summary> /// Handle mouse up to handle selection and link click. /// </summary> /// <param name="parent">the control hosting the html to invalidate</param> /// <param name="e">the mouse event args</param> public void HandleMouseUp(Control parent, MouseEventArgs e) { ArgChecker.AssertArgNotNull(parent, "parent"); ArgChecker.AssertArgNotNull(e, "e"); if (_selectionHandler != null) { var inSelection = _selectionHandler.HandleMouseUp(parent, e.Button); if (!inSelection && (e.Button & MouseButtons.Left) != 0) { var loc = OffsetByScroll(e.Location); var link = DomUtils.GetLinkBox(_root, loc); if (link != null) { if (LinkClicked != null) { var args = new HtmlLinkClickedEventArgs(link.GetAttribute("href")); LinkClicked(this, args); if (args.Handled) { return; } } CssValueParser.GoLink(link.GetAttribute("href", string.Empty), Bridge); } } } }
/// <summary> /// Assigns words its width and height /// </summary> /// <param name="g"></param> internal void MeasureWordsSize(Graphics g) { // Check if measure white space if not yet done to measure once if (!_wordsSizeMeasured) { MeasureWordSpacing(g); if (HtmlTag != null && HtmlTag.Name == "img") { var image = CssValueParser.GetImage(GetAttribute("src"), HtmlContainer.Bridge); var word = new CssBoxWord(this, image); Words.Clear(); Words.Add(word); } else if (Words.Count > 0) { foreach (var boxWord in Words) { var sf = new StringFormat(); sf.SetMeasurableCharacterRanges(new[] { new CharacterRange(0, boxWord.Text.Length) }); var regions = g.MeasureCharacterRanges(boxWord.Text, ActualFont, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); SizeF s = regions[0].GetBounds(g).Size; PointF p = regions[0].GetBounds(g).Location; boxWord.LastMeasureOffset = new PointF(p.X, p.Y); boxWord.Width = s.Width + ActualWordSpacing; boxWord.Height = s.Height; } } _wordsSizeMeasured = true; } }
/// <summary> /// Measures the bounds of box and children, recursively.<br/> /// Performs layout of the DOM structure creating lines by set bounds restrictions. /// </summary> /// <param name="g">Device context to use</param> protected override void PerformLayoutImp(IGraphics g) { if (Display == CssConstants.None) { return; } RectanglesReset(); CssBox prevSibling = DomUtils.GetPreviousSibling(this); float left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth; float top = (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0) + MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0); Location = new PointF(left, top); ActualBottom = top; //width at 100% (or auto) float minwidth = GetMinimumWidth(); float width = ContainingBlock.Size.Width - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth - ActualMarginLeft - ActualMarginRight - ActualBorderLeftWidth - ActualBorderRightWidth; //Check width if not auto if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width)) { width = CssValueParser.ParseLength(Width, width, this); } if (width < minwidth || width >= 9999) { width = minwidth; } float height = ActualHeight; if (height < 1) { height = Size.Height + ActualBorderTopWidth + ActualBorderBottomWidth; } if (height < 1) { height = 2; } if (height <= 2 && ActualBorderTopWidth < 1 && ActualBorderBottomWidth < 1) { BorderTopStyle = BorderBottomStyle = CssConstants.Solid; BorderTopWidth = "1px"; BorderBottomWidth = "1px"; } Size = new SizeF(width, height); ActualBottom = Location.Y + ActualPaddingTop + ActualPaddingBottom + height; }
public CssValue Parse(string cssValue) { var parser = new CssValueParser(); parser.SetContext(cssValue); var val = parser.DoParse(); return(val); }
/// <summary> /// Gets the white space width of the specified box /// </summary> /// <param name="g"></param> /// <param name="box"></param> /// <returns></returns> public static float WhiteSpace(Graphics g, CssBoxProperties box) { float w = MeasureWhitespace(g, box.ActualFont); if (!(String.IsNullOrEmpty(box.WordSpacing) || box.WordSpacing == CssConstants.Normal)) { w += CssValueParser.ParseLength(box.WordSpacing, 0, box, true); } return(w); }
/// <summary> /// Gets the white space width of the specified box /// </summary> /// <param name="g"></param> /// <param name="box"></param> /// <returns></returns> public static double WhiteSpace(RGraphics g, CssBoxProperties box) { double w = box.ActualFont.GetWhitespaceWidth(g); if (!(String.IsNullOrEmpty(box.WordSpacing) || box.WordSpacing == CssConstants.Normal)) { w += CssValueParser.ParseLength(box.WordSpacing, 0, box, true); } return(w); }
/// <summary> /// Measures the bounds of box and children, recursively.<br/> /// Performs layout of the DOM structure creating lines by set bounds restrictions. /// </summary> /// <param name="g">Device context to use</param> protected override void PerformLayoutImp(RGraphics g) { if (this.Display == CssConstants.None) { return; } this.RectanglesReset(); var prevSibling = DomUtils.GetPreviousSibling(this); double left = this.ContainingBlock.Location.X + this.ContainingBlock.ActualPaddingLeft + this.ActualMarginLeft + this.ContainingBlock.ActualBorderLeftWidth; double top = (prevSibling == null && this.ParentBox != null ? this.ParentBox.ClientTop : this.ParentBox == null ? this.Location.Y : 0) + this.MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0); this.Location = new RPoint(left, top); this.ActualBottom = top; //width at 100% (or auto) double minwidth = this.GetMinimumWidth(); double width = this.ContainingBlock.Size.Width - this.ContainingBlock.ActualPaddingLeft - this.ContainingBlock.ActualPaddingRight - this.ContainingBlock.ActualBorderLeftWidth - this.ContainingBlock.ActualBorderRightWidth - this.ActualMarginLeft - this.ActualMarginRight - this.ActualBorderLeftWidth - this.ActualBorderRightWidth; //Check width if not auto if (this.Width != CssConstants.Auto && !string.IsNullOrEmpty(this.Width)) { width = CssValueParser.ParseLength(this.Width, width, this); } if (width < minwidth || width >= 9999) { width = minwidth; } double height = this.ActualHeight; if (height < 1) { height = this.Size.Height + this.ActualBorderTopWidth + this.ActualBorderBottomWidth; } if (height < 1) { height = 2; } if (height <= 2 && this.ActualBorderTopWidth < 1 && this.ActualBorderBottomWidth < 1) { this.BorderTopStyle = this.BorderBottomStyle = CssConstants.Solid; this.BorderTopWidth = "1px"; this.BorderBottomWidth = "1px"; } this.Size = new RSize(width, height); this.ActualBottom = this.Location.Y + this.ActualPaddingTop + this.ActualPaddingBottom + height; }
/// <summary> /// Gets the available width for the whole table. /// It also sets the value of WidthSpecified /// </summary> /// <returns></returns> /// <remarks> /// The table's width can be larger than the result of this method, because of the minimum /// size that individual boxes. /// </remarks> private double GetMaxTableWidth() { var tblen = new CssLength(_tableBox.MaxWidth); if (tblen.Number > 0) { _widthSpecified = true; return(CssValueParser.ParseLength(_tableBox.MaxWidth, _tableBox.ParentBox.AvailableWidth, _tableBox)); } return(9999f); }
static CssLength SetLineHeight(this CssBox box, CssLength len) { //2014,2015, //from www.w3c.org/wiki/Css/Properties/line-height //line height in <percentage> : //The computed value if the property is percentage multiplied by the //element's computed font size. return(CssLength.MakePixelLength( CssValueParser.ConvertToPx(len, box.GetEmHeight(), box))); }
/// <summary> /// Measures the width of whitespace between words (set <see cref="ActualWordSpacing"/>). /// </summary> protected void MeasureWordSpacing(RGraphics g) { if (double.IsNaN(ActualWordSpacing)) { _actualWordSpacing = CssUtils.WhiteSpace(g, this); if (WordSpacing != CssConstants.Normal) { string len = RegexParserUtils.Search(RegexParserUtils.CssLength, WordSpacing); _actualWordSpacing += CssValueParser.ParseLength(len, 1, this); } } }
public void SubArg() { var parser = new CssValueParser(); parser.SetContext("fn(#fff)"); var arg = parser.DoParse(); Assert.IsTrue(parser.End); Assert.AreEqual(0, parser.Errors.Count); Assert.IsTrue(arg.IsValid); Assert.IsInstanceOfType(arg, typeof(CssSimpleValue)); Assert.AreEqual("fn(#fff)", arg.ToString()); }
public void WithParenthesis() { var parser = new CssValueParser(); parser.SetContext("(30%+10%)*3"); var arg = parser.DoParse(); Assert.IsTrue(parser.End); Assert.AreEqual(0, parser.Errors.Count); Assert.IsTrue(arg.IsValid); Assert.IsInstanceOfType(arg, typeof(CssArithmeticOperation)); Assert.AreEqual("(30%+10%)*3", arg.ToString()); }
/// <summary> /// Gets the available width for the whole table. /// It also sets the value of WidthSpecified /// </summary> /// <returns></returns> /// <remarks> /// The table's width can be larger than the result of this method, because of the minimum /// size that individual boxes. /// </remarks> private float GetMaxTableWidth() { var tblen = new CssLength(_tableBox.MaxWidth); if (tblen.Number > 0) { _widthSpecified = true; return(tblen.IsPercentage ? CssValueParser.ParseNumber(tblen.Length, _tableBox.ParentBox.AvailableWidth) : tblen.Number); } else { return(9999f); } }
/// <summary> /// Gets the available width for the whole table. /// It also sets the value of WidthSpecified /// </summary> /// <returns></returns> /// <remarks> /// The table's width can be larger than the result of this method, because of the minimum /// size that individual boxes. /// </remarks> private double GetAvailableTableWidth() { CssLength tblen = new CssLength(_tableBox.Width); if (tblen.Number > 0) { _widthSpecified = true; return(CssValueParser.ParseLength(_tableBox.Width, _tableBox.ParentBox.AvailableWidth, _tableBox)); } else { return(_tableBox.ParentBox.AvailableWidth); } }
void CreateSvgPolygon(SvgElement parentNode, HtmlElement elem) { SvgPolygonSpec spec = new SvgPolygonSpec(); SvgPolygon shape = new SvgPolygon(spec, elem); parentNode.AddChild(shape); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WebDom.WellknownName.Svg_Points: { //parse points spec.Points = ParsePointList(attr.Value); } break; case WebDom.WellknownName.Svg_Fill: { spec.FillColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke: { spec.StrokeColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke_Width: { spec.StrokeWidth = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Transform: { //TODO: parse svg transform function } break; default: { //other attrs } break; } } }
public void ParseCssBoxWidth(CssBox box) { if (!(_parentLength == 0 && box.Width.Trim().EndsWith("%"))) { value = CssValueParser.ParseLength(box.Width, _parentLength, box); } if (!(_parentLength == 0 && box.MaxWidth.Trim().EndsWith("%"))) { max = CssValueParser.ParseLength(box.MaxWidth, _parentLength, box); } if (!(_parentLength == 0 && box.MinWidth.Trim().EndsWith("%"))) { min = CssValueParser.ParseLength(box.MinWidth, _parentLength, box); } }
public void ParseCssBoxHeight(CssBox box) { if (!(_parentLength == 0 && box.Height.Trim().EndsWith("%"))) { value = CssValueParser.ParseLength(box.Height, _parentLength, box); } if (!(_parentLength == 0 && box.MaxHeight.Trim().EndsWith("%"))) { max = CssValueParser.ParseLength(box.MaxHeight, _parentLength, box); } if (!(_parentLength == 0 && box.MinHeight.Trim().EndsWith("%"))) { min = CssValueParser.ParseLength(box.MinHeight, _parentLength, box); } }
public void Priority() { var parser = new CssValueParser(); parser.SetContext("30%/fn(2,3)+10%*1.4+2*3"); var arg = parser.DoParse(); Assert.IsTrue(parser.End); Assert.AreEqual(0, parser.Errors.Count); Assert.IsTrue(arg.IsValid); Assert.IsInstanceOfType(arg, typeof(CssArithmeticOperation)); Assert.AreEqual("30%/fn(2,3)+10%*1.4+2*3", arg.ToString()); Assert.AreEqual("30%/fn(2,3)", ((CssArithmeticOperation)arg).Left.ToString()); Assert.AreEqual("10%*1.4+2*3", ((CssArithmeticOperation)arg).Right.ToString()); var o = ((CssArithmeticOperation)arg).Right; Assert.IsInstanceOfType(o, typeof(CssArithmeticOperation)); Assert.AreEqual("10%*1.4", ((CssArithmeticOperation)o).Left.ToString()); Assert.AreEqual("2*3", ((CssArithmeticOperation)o).Right.ToString()); }
/// <summary> /// Gets the available width for the whole table. /// It also sets the value of <see cref="WidthSpecified"/> /// </summary> /// <returns></returns> /// <remarks> /// The table's width can be larger than the result of this method, because of the minimum /// size that individual boxes. /// </remarks> private float GetAvailableWidth() { CssLength tblen = new CssLength(TableBox.Width); if (tblen.Number > 0) { _widthSpecified = true; if (tblen.IsPercentage) { return(CssValueParser.ParseNumber(tblen.Length, TableBox.ParentBox.AvailableWidth)); } else { return(tblen.Number); } } else { return(TableBox.ParentBox.AvailableWidth); } }
static void CreateSvgGroupElement(SvgElement parentNode, HtmlElement elem) { SvgVisualSpec spec = new SvgVisualSpec(); SvgGroupElement svgGroupElement = new SvgGroupElement(spec, elem); parentNode.AddChild(svgGroupElement); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WebDom.WellknownName.Svg_Fill: { spec.ActualColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke: { spec.StrokeColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke_Width: { spec.StrokeWidth = UserMapUtil.ParseGenericLength(attr.Value); } break; default: { //other attrs } break; } } CreateSvgBoxContent(svgGroupElement, elem); }
/// <summary> /// Determine Row and Column Count, and ColumnWidths /// </summary> /// <returns></returns> private double CalculateCountAndWidth() { //Columns if (_columns.Count > 0) { _columnCount = _columns.Count; } else { foreach (CssBox b in _allRows) { _columnCount = Math.Max(_columnCount, b.Boxes.Count); } } //Initialize column widths array with NaNs _columnWidths = new double[_columnCount]; for (int i = 0; i < _columnWidths.Length; i++) { _columnWidths[i] = double.NaN; } double availCellSpace = GetAvailableCellWidth(); if (_columns.Count > 0) { // Fill ColumnWidths array by scanning column widths for (int i = 0; i < _columns.Count; i++) { CssLength len = new CssLength(_columns[i].Width); //Get specified width if (len.Number > 0) //If some width specified { if (len.IsPercentage) //Get width as a percentage { _columnWidths[i] = CssValueParser.ParseNumber(_columns[i].Width, availCellSpace); } else if (len.Unit == CssUnit.Pixels || len.Unit == CssUnit.None) { _columnWidths[i] = len.Number; //Get width as an absolute-pixel value } } } } else { // Fill ColumnWidths array by scanning width in table-cell definitions foreach (CssBox row in _allRows) { //Check for column width in table-cell definitions for (int i = 0; i < _columnCount; i++) { if (i < 20 || double.IsNaN(_columnWidths[i])) // limit column width check { if (i < row.Boxes.Count && row.Boxes[i].Display == CssConstants.TableCell) { double len = CssValueParser.ParseLength(row.Boxes[i].Width, availCellSpace, row.Boxes[i]); if (len > 0) //If some width specified { int colspan = GetColSpan(row.Boxes[i]); len /= Convert.ToSingle(colspan); for (int j = i; j < i + colspan; j++) { _columnWidths[j] = double.IsNaN(_columnWidths[j]) ? len : Math.Max(_columnWidths[j], len); } } } } } } } return(availCellSpace); }
/// <summary> /// Gets the span attribute of the tag of the specified box /// </summary> /// <param name="b"></param> private static int GetSpan(CssBox b) { double f = CssValueParser.ParseNumber(b.GetAttribute("span"), 1); return(Math.Max(1, Convert.ToInt32(f))); }
/// <summary> /// Write the given html tag with all its attributes and styles. /// </summary> /// <param name="sb">the string builder to write html into</param> /// <param name="box">the css box with the html tag to write</param> /// <param name="indent">the indent to use for nice formating</param> /// <param name="styleGen">Controls the way styles are generated when html is generated</param> private static void WriteHtmlTag(StringBuilder sb, CssBox box, int indent, HtmlGenerationStyle styleGen) { sb.Append(new string(' ', indent * 4)); sb.AppendFormat("<{0}", box.HtmlTag.Name); // collect all element style properties incliding from stylesheet var tagStyles = new Dictionary <string, string>(); var tagCssBlock = box.HtmlContainer.CssData.GetCssBlock(box.HtmlTag.Name); if (tagCssBlock != null) { // atodo: handle selectors foreach (var cssBlock in tagCssBlock) { foreach (var prop in cssBlock.Properties) { tagStyles[prop.Key] = prop.Value; } } } if (box.HtmlTag.Attributes.Count > 0) { sb.Append(" "); foreach (var att in box.HtmlTag.Attributes) { // handle image tags by inserting the image using base64 data if (box.HtmlTag.Name == "img" && att.Key == "src" && (att.Value.StartsWith("property") || att.Value.StartsWith("method"))) { var img = CssValueParser.GetImage(att.Value, box.HtmlContainer.Bridge); if (img != null) { using (var buffer = new MemoryStream()) { img.Save(buffer, ImageFormat.Png); var base64 = Convert.ToBase64String(buffer.ToArray()); sb.AppendFormat("{0}=\"data:image/png;base64, {1}\" ", att.Key, base64); } } } else if (styleGen == HtmlGenerationStyle.Inline && att.Key == HtmlConstants.Style) { // if inline style add the styles to the collection var block = CssParser.ParseCssBlockImp(box.HtmlTag.Name, box.HtmlTag.Attributes["style"]); foreach (var prop in block.Properties) { tagStyles[prop.Key] = prop.Value; } } else if (styleGen == HtmlGenerationStyle.Inline && att.Key == HtmlConstants.Class) { // if inline style convert the style class to actual properties and add to collection var cssBlocks = box.HtmlContainer.CssData.GetCssBlock("." + att.Value); if (cssBlocks != null) { // atodo: handle selectors foreach (var cssBlock in cssBlocks) { foreach (var prop in cssBlock.Properties) { tagStyles[prop.Key] = prop.Value; } } } } else { sb.AppendFormat("{0}=\"{1}\" ", att.Key, att.Value); } } sb.Remove(sb.Length - 1, 1); } // if inline style insert the style tag with all collected style properties if (styleGen == HtmlGenerationStyle.Inline && tagStyles.Count > 0) { sb.Append(" style=\""); foreach (var style in tagStyles) { sb.AppendFormat("{0}: {1}; ", style.Key, style.Value); } sb.Remove(sb.Length - 1, 1); sb.Append("\""); } sb.AppendFormat("{0}>", box.HtmlTag.IsSingle ? "/" : ""); sb.AppendLine(); }
/// <summary> /// Analyzes the Table and assigns values to this CssTable object. /// To be called from the constructor /// </summary> private void Analyze(Graphics g) { #region Assign box kinds foreach (CssBox box in TableBox.Boxes) { switch (box.Display) { case CssConstants.TableCaption: _caption = box; break; case CssConstants.TableColumn: for (int i = 0; i < GetSpan(box); i++) { Columns.Add(CreateColumn(box)); } break; case CssConstants.TableColumnGroup: if (box.Boxes.Count == 0) { int gspan = GetSpan(box); for (int i = 0; i < gspan; i++) { Columns.Add(CreateColumn(box)); } } else { foreach (CssBox bb in box.Boxes) { int bbspan = GetSpan(bb); for (int i = 0; i < bbspan; i++) { Columns.Add(CreateColumn(bb)); } } } break; case CssConstants.TableFooterGroup: if (FooterBox != null) { BodyRows.Add(box); } else { _footerBox = box; } break; case CssConstants.TableHeaderGroup: if (HeaderBox != null) { BodyRows.Add(box); } else { _headerBox = box; } break; case CssConstants.TableRow: BodyRows.Add(box); break; case CssConstants.TableRowGroup: foreach (CssBox childBox in box.Boxes) { if (childBox.Display == CssConstants.TableRow) { BodyRows.Add(childBox); } } break; } } #endregion #region Gather AllRows if (HeaderBox != null) { _allRows.AddRange(HeaderBox.Boxes); } _allRows.AddRange(BodyRows); if (FooterBox != null) { _allRows.AddRange(FooterBox.Boxes); } #endregion #region Insert EmptyBoxes for vertical cell spanning if (!TableBox._tableFixed) { int currow = 0; int curcol = 0; List <CssBox> rows = BodyRows; foreach (CssBox row in rows) { curcol = 0; for (int k = 0; k < row.Boxes.Count; k++) { CssBox cell = row.Boxes[k]; int rowspan = GetRowSpan(cell); int realcol = GetCellRealColumnIndex(row, cell); //Real column of the cell for (int i = currow + 1; i < currow + rowspan; i++) { int colcount = 0; for (int j = 0; j <= rows[i].Boxes.Count; j++) { if (colcount == realcol) { rows[i].Boxes.Insert(colcount, new CssSpacingBox(TableBox, ref cell, currow)); break; } colcount++; realcol -= GetColSpan(rows[i].Boxes[j]) - 1; } } curcol++; } currow++; } TableBox._tableFixed = true; } #endregion #region Determine Row and Column Count, and ColumnWidths //Rows _rowCount = BodyRows.Count + (HeaderBox != null ? HeaderBox.Boxes.Count : 0) + (FooterBox != null ? FooterBox.Boxes.Count : 0); //Columns if (Columns.Count > 0) { _columnCount = Columns.Count; } else { foreach (CssBox b in AllRows) //Check trhough rows { _columnCount = Math.Max(_columnCount, b.Boxes.Count); } } //Initialize column widths array _columnWidths = new float[_columnCount]; //Fill them with NaNs for (int i = 0; i < _columnWidths.Length; i++) { _columnWidths[i] = float.NaN; } float availCellSpace = GetAvailableCellWidth(); if (Columns.Count > 0) { #region Fill ColumnWidths array by scanning column widths for (int i = 0; i < Columns.Count; i++) { CssLength len = new CssLength(Columns[i].Width); //Get specified width if (len.Number > 0) //If some width specified { if (len.IsPercentage) //Get width as a percentage { ColumnWidths[i] = CssValueParser.ParseNumber(Columns[i].Width, availCellSpace); } else if (len.Unit == CssUnit.Pixels || len.Unit == CssUnit.None) { ColumnWidths[i] = len.Number; //Get width as an absolute-pixel value } } } #endregion } else { #region Fill ColumnWidths array by scanning width in table-cell definitions foreach (CssBox row in AllRows) { //Check for column width in table-cell definitions for (int i = 0; i < _columnCount; i++) { if (float.IsNaN(ColumnWidths[i]) && //Check if no width specified for column i < row.Boxes.Count && //And there's a box to check row.Boxes[i].Display == CssConstants.TableCell) //And the box is a table-cell { CssLength len = new CssLength(row.Boxes[i].Width); //Get specified width if (len.Number > 0) //If some width specified { int colspan = GetColSpan(row.Boxes[i]); float flen = 0f; if (len.IsPercentage)//Get width as a percentage { flen = CssValueParser.ParseNumber(row.Boxes[i].Width, availCellSpace); } else if (len.Unit == CssUnit.Pixels || len.Unit == CssUnit.None) { flen = len.Number; //Get width as an absolute-pixel value } flen /= Convert.ToSingle(colspan); for (int j = i; j < i + colspan; j++) { ColumnWidths[j] = flen; } } } } } #endregion } #endregion #region Determine missing Column widths if (WidthSpecified) //If a width was specified, { //Assign NaNs equally with space left after gathering not-NaNs int numberOfNans = 0; float occupedSpace = 0f; //Calculate number of NaNs and occuped space foreach (float t in ColumnWidths) { if (float.IsNaN(t)) { numberOfNans++; } else { occupedSpace += t; } } //Determine width that will be assigned to un asigned widths float nanWidth = (availCellSpace - occupedSpace) / Convert.ToSingle(numberOfNans); for (int i = 0; i < ColumnWidths.Length; i++) { if (float.IsNaN(ColumnWidths[i])) { ColumnWidths[i] = nanWidth; } } } else { //Assign NaNs using full width float[] maxFullWidths = new float[ColumnWidths.Length]; //Get the maximum full length of NaN boxes foreach (CssBox row in AllRows) { for (int i = 0; i < row.Boxes.Count; i++) { int col = GetCellRealColumnIndex(row, row.Boxes[i]); if (float.IsNaN(ColumnWidths[col]) && i < row.Boxes.Count && GetColSpan(row.Boxes[i]) == 1) { maxFullWidths[col] = Math.Max(maxFullWidths[col], row.Boxes[i].GetFullWidth(g)); } } } for (int i = 0; i < ColumnWidths.Length; i++) { if (float.IsNaN(ColumnWidths[i])) { ColumnWidths[i] = maxFullWidths[i]; } } } #endregion #region Reduce widths if necessary int curCol = 0; //While table width is larger than it should, and width is reductable while (GetWidthSum() > GetAvailableWidth() && CanReduceWidth()) { while (!CanReduceWidth(curCol)) { curCol++; } ColumnWidths[curCol] -= 1f; curCol++; if (curCol >= ColumnWidths.Length) { curCol = 0; } } #endregion #region Check for minimum sizes (increment widths if necessary) foreach (CssBox row in AllRows) { foreach (CssBox cell in row.Boxes) { int colspan = GetColSpan(cell); int col = GetCellRealColumnIndex(row, cell); int affectcol = col + colspan - 1; if (ColumnWidths[col] < ColumnMinWidths[col]) { float diff = ColumnMinWidths[col] - ColumnWidths[col]; ColumnWidths[affectcol] = ColumnMinWidths[affectcol]; if (col < ColumnWidths.Length - 1) { ColumnWidths[col + 1] -= diff; } } } } #endregion #region Set table padding //Ensure there's no padding TableBox.PaddingLeft = TableBox.PaddingTop = TableBox.PaddingRight = TableBox.PaddingBottom = "0"; #endregion #region Layout cells //Actually layout cells! float startx = TableBox.ClientLeft + HorizontalSpacing; float starty = TableBox.ClientTop + VerticalSpacing; float curx = startx; float cury = starty; float maxRight = startx; float maxBottom = 0f; int currentrow = 0; for (int i = 0; i < AllRows.Count; i++) { var row = AllRows[i]; curx = startx; curCol = 0; for (int j = 0; j < row.Boxes.Count; j++) { CssBox cell = row.Boxes[j]; if (curCol >= ColumnWidths.Length) { break; } int rowspan = GetRowSpan(cell); var columnIndex = GetCellRealColumnIndex(row, cell); float width = GetCellWidth(columnIndex, cell); cell.Location = new PointF(curx, cury); cell.Size = new SizeF(width, 0f); cell.MeasureBounds(g); //That will automatically set the bottom of the cell //Alter max bottom only if row is cell's row + cell's rowspan - 1 CssSpacingBox sb = cell as CssSpacingBox; if (sb != null) { if (sb.EndRow == currentrow) { maxBottom = Math.Max(maxBottom, sb.ExtendedBox.ActualBottom); } } else if (rowspan == 1) { maxBottom = Math.Max(maxBottom, cell.ActualBottom); } maxRight = Math.Max(maxRight, cell.ActualRight); curCol++; curx = cell.ActualRight + HorizontalSpacing; } foreach (CssBox cell in row.Boxes) { CssSpacingBox spacer = cell as CssSpacingBox; if (spacer == null && GetRowSpan(cell) == 1) { cell.ActualBottom = maxBottom; CssLayoutEngine.ApplyCellVerticalAlignment(g, cell); } else if (spacer != null && spacer.EndRow == currentrow) { spacer.ExtendedBox.ActualBottom = maxBottom; CssLayoutEngine.ApplyCellVerticalAlignment(g, spacer.ExtendedBox); } } cury = maxBottom + VerticalSpacing; currentrow++; } TableBox.ActualRight = maxRight + HorizontalSpacing + TableBox.ActualBorderRightWidth; TableBox.ActualBottom = maxBottom + VerticalSpacing + TableBox.ActualBorderBottomWidth; #endregion }
/// <summary> /// Creates a new CssLength from a length specified on a CSS style sheet or fragment /// </summary> /// <param name="length">Length as specified in the Style Sheet or style fragment</param> public CssLength(string length) { _length = length; _number = 0f; _unit = CssUnit.None; _isPercentage = false; //Return zero if no length specified, zero specified if (string.IsNullOrEmpty(length) || length == "0") { return; } //If percentage, use ParseNumber if (length.EndsWith("%")) { _number = CssValueParser.ParseNumber(length, 1); _isPercentage = true; return; } //If no units, has error if (length.Length < 3) { double.TryParse(length, out _number); _hasError = true; return; } //Get units of the length string u = length.Substring(length.Length - 2, 2); //Number of the length string number = length.Substring(0, length.Length - 2); //TODO: Units behave different in paper and in screen! switch (u) { case CssConstants.Em: _unit = CssUnit.Ems; _isRelative = true; break; case CssConstants.Ex: _unit = CssUnit.Ex; _isRelative = true; break; case CssConstants.Px: _unit = CssUnit.Pixels; _isRelative = true; break; case CssConstants.Mm: _unit = CssUnit.Milimeters; break; case CssConstants.Cm: _unit = CssUnit.Centimeters; break; case CssConstants.In: _unit = CssUnit.Inches; break; case CssConstants.Pt: _unit = CssUnit.Points; break; case CssConstants.Pc: _unit = CssUnit.Picas; break; default: _hasError = true; return; } if (!double.TryParse(number, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out _number)) { _hasError = true; } }
static void CreateSvgLinearGradient(SvgElement parentNode, HtmlElement elem) { //linear gradient definition SvgLinearGradient linearGradient = new SvgLinearGradient(elem); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WellknownName.Svg_X1: { linearGradient.X1 = UserMapUtil.ParseGenericLength(attr.Value); } break; case WellknownName.Svg_X2: { linearGradient.X2 = UserMapUtil.ParseGenericLength(attr.Value); } break; case WellknownName.Svg_Y1: { linearGradient.Y1 = UserMapUtil.ParseGenericLength(attr.Value); } break; case WellknownName.Svg_Y2: { linearGradient.Y2 = UserMapUtil.ParseGenericLength(attr.Value); } break; } } //------------------------------------------------------------ int j = elem.ChildrenCount; List <StopColorPoint> stopColorPoints = new List <StopColorPoint>(j); for (int i = 0; i < j; ++i) { HtmlElement node = elem.GetChildNode(i) as HtmlElement; if (node == null) { continue; } switch (node.WellknownElementName) { case WellKnownDomNodeName.svg_stop: { //stop point StopColorPoint stopPoint = new StopColorPoint(); foreach (WebDom.DomAttribute attr in node.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WellknownName.Svg_StopColor: { stopPoint.StopColor = CssValueParser.GetActualColor(attr.Value); } break; case WellknownName.Svg_Offset: { stopPoint.Offset = UserMapUtil.ParseGenericLength(attr.Value); } break; } } stopColorPoints.Add(stopPoint); } break; } } }
static void CreateSvgPath(SvgElement parentNode, HtmlElement elem) { SvgPathSpec spec = new SvgPathSpec(); SvgPath svgPath = new SvgPath(spec, elem); parentNode.AddChild(svgPath); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WebDom.WellknownName.Svg_X: { spec.X = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Y: { spec.Y = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Width: { spec.Width = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Height: { spec.Height = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Fill: { spec.ActualColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke: { spec.StrokeColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke_Width: { spec.StrokeWidth = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Transform: { //TODO: parse svg transform function } break; default: { //other attrs switch (attr.Name) { case "d": { //parse vertex commands Svg.Pathing.SvgPathDataParser parser = new Svg.Pathing.SvgPathDataParser(); svgPath.Segments = parser.Parse(attr.Value.ToCharArray()); } break; } } break; } } }
static void CreateSvgImage(SvgElement parentNode, HtmlElement elem) { SvgImageSpec spec = new SvgImageSpec(); SvgImage svgImage = new SvgImage(spec, elem); parentNode.AddChild(svgImage); foreach (WebDom.DomAttribute attr in elem.GetAttributeIterForward()) { WebDom.WellknownName wellknownName = (WebDom.WellknownName)attr.LocalNameIndex; switch (wellknownName) { case WebDom.WellknownName.Svg_X: { spec.X = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Y: { spec.Y = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Width: { spec.Width = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Height: { spec.Height = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Fill: { spec.ActualColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke: { spec.StrokeColor = CssValueParser.GetActualColor(attr.Value); } break; case WebDom.WellknownName.Svg_Stroke_Width: { spec.StrokeWidth = UserMapUtil.ParseGenericLength(attr.Value); } break; case WebDom.WellknownName.Svg_Transform: { //TODO: parse svg transform function } break; case WellknownName.Href: { //image src*** spec.ImageSrc = attr.Value; } break; default: { } break; } } }