/// <summary> /// Applies style to all boxes in the tree.<br/> /// If the html tag has style defined for each apply that style to the css box of the tag.<br/> /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/> /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/> /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/> /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/> /// </summary> /// <param name="box"></param> /// <param name="bridge"> </param> /// <param name="cssData"> </param> /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param> private static void CascadeStyles(CssBox box, object bridge, ref CssData cssData, ref bool cssDataChanged) { box.InheritStyle(); if (box.HtmlTag != null) { // try assign style using the html element tag AssignCssBlocks(box, cssData, box.HtmlTag.Name); // try assign style using the "class" attribute of the html element if (box.HtmlTag.HasAttribute("class")) { AssignCssBlocks(box, cssData, "." + box.HtmlTag.Attributes["class"]); AssignCssBlocks(box, cssData, box.HtmlTag.Name + "." + box.HtmlTag.Attributes["class"]); } // try assign style using the "id" attribute of the html element if (box.HtmlTag.HasAttribute("id")) { AssignCssBlocks(box, cssData, "#" + box.HtmlTag.Attributes["id"]); } HtmlParser.TranslateAttributes(box.HtmlTag, box); // Check for the style="" attribute if (box.HtmlTag.HasAttribute("style")) { var block = CssParser.ParseCssBlockImp(box.HtmlTag.Name, box.HtmlTag.Attributes["style"]); AssignCssBlock(box, block); } // Check for the <style> tag if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1) { CloneCssData(ref cssData, ref cssDataChanged); CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text); } // Check for the <link rel=stylesheet> tag if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) && box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase)) { CloneCssData(ref cssData, ref cssDataChanged); var styleSheet = CssValueParser.GetStyleSheet(box.GetAttribute("href", string.Empty), bridge); CssParser.ParseStyleSheet(cssData, styleSheet); } } foreach (var childBox in box.Boxes) { CascadeStyles(childBox, bridge, ref cssData, ref cssDataChanged); } }
/// <summary> /// Applies style to all boxes in the tree.<br/> /// If the html tag has style defined for each apply that style to the css box of the tag.<br/> /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/> /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/> /// </summary> /// <param name="box">the box to apply the style to</param> /// <param name="cssData">the style data for the html</param> private void CascadeApplyStyles(CssBox box, CssData cssData) { box.InheritStyle(); if (box.HtmlTag != null) { // try assign style using all wildcard AssignCssBlocks(box, cssData, "*"); // try assign style using the html element tag AssignCssBlocks(box, cssData, box.HtmlTag.Name); // try assign style using the "class" attribute of the html element if (box.HtmlTag.HasAttribute("class")) { AssignClassCssBlocks(box, cssData); } // try assign style using the "id" attribute of the html element if (box.HtmlTag.HasAttribute("id")) { var id = box.HtmlTag.TryGetAttribute("id"); AssignCssBlocks(box, cssData, "#" + id); } TranslateAttributes(box.HtmlTag, box); // Check for the style="" attribute if (box.HtmlTag.HasAttribute("style")) { var block = _cssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style")); if (block != null) { AssignCssBlock(box, block); } } } // cascade text decoration only to boxes that actually have text so it will be handled correctly. if (box.TextDecoration != String.Empty && box.Text == null) { foreach (var childBox in box.Boxes) { childBox.TextDecoration = box.TextDecoration; } box.TextDecoration = string.Empty; } foreach (var childBox in box.Boxes) { CascadeApplyStyles(childBox, cssData); } }
/// <summary> /// Split bad box that has inline and block boxes into two parts, the left - before the block box /// and right - after the block box. /// </summary> /// <param name="parentBox">the parent box that has the problem</param> /// <param name="badBox">the box to split into different boxes</param> /// <param name="leftBlock">the left block box that is created for the split</param> private static void CorrectBlockSplitBadBox(CssBox parentBox, CssBox badBox, CssBox leftBlock) { var leftbox = CssBox.CreateBox(leftBlock, badBox.HtmlTag); leftbox.InheritStyle(badBox, true); while (badBox.Boxes[0].IsInline && ContainsInlinesOnlyDeep(badBox.Boxes[0])) { badBox.Boxes[0].ParentBox = leftbox; } var splitBox = badBox.Boxes[0]; if (!ContainsInlinesOnlyDeep(splitBox)) { CorrectBlockSplitBadBox(parentBox, splitBox, leftBlock); splitBox.ParentBox = null; } else { splitBox.ParentBox = parentBox; } if (badBox.Boxes.Count > 0) { CssBox rightBox = null; if (splitBox.ParentBox != null || parentBox.Boxes.Count < 2) { rightBox = CssBox.CreateBox(parentBox, badBox.HtmlTag); rightBox.InheritStyle(badBox, true); if (parentBox.Boxes.Count > 2) { rightBox.SetBeforeBox(parentBox.Boxes[1]); } splitBox.SetBeforeBox(rightBox); } else if (parentBox.Boxes.Count > 2) { rightBox = parentBox.Boxes[2]; } while (badBox.Boxes.Count > 0) { badBox.Boxes[0].ParentBox = rightBox; } } else if (splitBox.ParentBox != null && parentBox.Boxes.Count > 1) { splitBox.SetBeforeBox(parentBox.Boxes[1]); } }
/// <summary> /// Applies style to all boxes in the tree.<br/> /// If the html tag has style defined for each apply that style to the css box of the tag.<br/> /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/> /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/> /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/> /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/> /// </summary> /// <param name="box"></param> /// <param name="htmlContainer">the html container to use for reference resolve</param> /// <param name="cssData"> </param> /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param> private static void CascadeStyles(CssBox box, HtmlContainer htmlContainer, ref CssData cssData, ref bool cssDataChanged) { box.InheritStyle(); if (box.HtmlTag != null) { // try assign style using the html element tag AssignCssBlocks(box, cssData, box.HtmlTag.Name); // try assign style using the "class" attribute of the html element if (box.HtmlTag.HasAttribute("class")) { AssignClassCssBlocks(box, cssData); } // try assign style using the "id" attribute of the html element if (box.HtmlTag.HasAttribute("id")) { var id = box.HtmlTag.TryGetAttribute("id"); AssignCssBlocks(box, cssData, "#" + id); } TranslateAttributes(box.HtmlTag, box); // Check for the style="" attribute if (box.HtmlTag.HasAttribute("style")) { var block = CssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style")); AssignCssBlock(box, block); } // Check for the <style> tag if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1) { CloneCssData(ref cssData, ref cssDataChanged); CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text.CutSubstring()); } // Check for the <link rel=stylesheet> tag if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) && box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase)) { CloneCssData(ref cssData, ref cssDataChanged); var styleSheet = StylesheetLoadHelper.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes); CssParser.ParseStyleSheet(cssData, styleSheet); } } foreach (var childBox in box.Boxes) { CascadeStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged); } }
/// <summary> /// Split bad box that has inline and block boxes into two parts, the left - before the block box /// and right - after the block box. /// </summary> /// <param name="parentBox">the parent box that has the problem</param> /// <param name="badBox">the box to split into different boxes</param> /// <param name="leftBlock">the left block box that is created for the split</param> private static void CorrectBlockSplitBadBox(CssBox parentBox, CssBox badBox, CssBox leftBlock) { CssBox leftbox = null; while (badBox.Boxes[0].IsInline && ContainsInlinesOnlyDeep(badBox.Boxes[0])) { if (leftbox == null) { // if there is no elements in the left box there is no reason to keep it leftbox = CssBox.CreateBox(leftBlock, badBox.HtmlTag); leftbox.InheritStyle(badBox, true); } badBox.Boxes[0].ParentBox = leftbox; } var splitBox = badBox.Boxes[0]; if (!ContainsInlinesOnlyDeep(splitBox)) { CorrectBlockSplitBadBox(parentBox, splitBox, leftBlock); splitBox.ParentBox = null; } else { splitBox.ParentBox = parentBox; } if (badBox.Boxes.Count > 0) { CssBox rightBox; if (splitBox.ParentBox != null || parentBox.Boxes.Count < 3) { rightBox = CssBox.CreateBox(parentBox, badBox.HtmlTag); rightBox.InheritStyle(badBox, true); if (parentBox.Boxes.Count > 2) { rightBox.SetBeforeBox(parentBox.Boxes[1]); } if (splitBox.ParentBox != null) { splitBox.SetBeforeBox(rightBox); } } else { rightBox = parentBox.Boxes[2]; } rightBox.SetAllBoxes(badBox); } else if (splitBox.ParentBox != null && parentBox.Boxes.Count > 1) { splitBox.SetBeforeBox(parentBox.Boxes[1]); if (splitBox.HtmlTag != null && splitBox.HtmlTag.Name == "br" && (leftbox != null || leftBlock.Boxes.Count > 1)) { splitBox.Display = CssConstants.Inline; } } }
/// <summary> /// Split bad box that has inline and block boxes into two parts, the left - before the block box /// and right - after the block box. /// </summary> /// <param name="parentBox">the parent box that has the problem</param> /// <param name="badBox">the box to split into different boxes</param> /// <param name="leftBlock">the left block box that is created for the split</param> private static void CorrectBlockSplitBadBox(CssBox parentBox, CssBox badBox, CssBox leftBlock) { CssBox leftbox = null; //This checks if the first child of badbox isInline and if it has only inline elements while (badBox.Boxes[0].IsInline && ContainsInlinesOnlyDeep(badBox.Boxes[0])) { if (leftbox == null) { // if there is no elements in the left box there is no reason to keep it leftbox = CssBox.CreateBox(leftBlock, badBox.HtmlTag); leftbox.InheritStyle(badBox, true); } //puts the HTML of leftbox (which is a new box?) into the parent ofbadbox (it's previous parent) badBox.Boxes[0].ParentBox = leftbox; } var splitBox = badBox.Boxes[0]; if (!ContainsInlinesOnlyDeep(splitBox)) { CorrectBlockSplitBadBox(parentBox, splitBox, leftBlock); splitBox.ParentBox = null; } else { splitBox.ParentBox = parentBox; } if (badBox.Boxes.Count > 0) { CssBox rightBox; if (splitBox.ParentBox != null || parentBox.Boxes.Count < 3) { rightBox = CssBox.CreateBox(parentBox, badBox.HtmlTag); rightBox.InheritStyle(badBox, true); if (parentBox.Boxes.Count > 2) { rightBox.SetBeforeBox(parentBox.Boxes[1]); } if (splitBox.ParentBox != null) { splitBox.SetBeforeBox(rightBox); } } else { rightBox = parentBox.Boxes[2]; } rightBox.SetAllBoxes(badBox); } else if (splitBox.ParentBox != null && parentBox.Boxes.Count > 1) { splitBox.SetBeforeBox(parentBox.Boxes[1]); if (splitBox.HtmlTag != null && splitBox.HtmlTag.Name == "br" && (leftbox != null || leftBlock.Boxes.Count > 1)) { splitBox.Display = CssConstants.Inline; } } }
/// <summary> /// Split bad box that has inline and block boxes into two parts, the left - before the block box /// and right - after the block box. /// </summary> /// <param name="parentBox">the parent box that has the problem</param> /// <param name="badBox">the box to split into different boxes</param> /// <param name="leftBlock">the left block box that is created for the split</param> private static void CorrectBlockSplitBadBox(CssBox parentBox, CssBox badBox, CssBox leftBlock) { CssBox leftbox = CssBox.CreateBox(leftBlock, badBox.HtmlTag); leftbox.InheritStyle(badBox, true); bool hadLeft = false; while (badBox.Boxes[0].IsInline && ContainsInlinesOnlyDeep(badBox.Boxes[0])) { hadLeft = true; badBox.Boxes[0].ParentBox = leftbox; } CssBox splitBox = badBox.Boxes[0]; if (!ContainsInlinesOnlyDeep(splitBox)) { CorrectBlockSplitBadBox(parentBox, splitBox, leftBlock); splitBox.ParentBox = null; } else { splitBox.ParentBox = parentBox; } if (badBox.Boxes.Count > 0) { CssBox rightBox; if (splitBox.ParentBox != null || parentBox.Boxes.Count < 3) { rightBox = CssBox.CreateBox(parentBox, badBox.HtmlTag); rightBox.InheritStyle(badBox, true); if (parentBox.Boxes.Count > 2) { rightBox.SetBeforeBox(parentBox.Boxes[1]); } if (splitBox.ParentBox != null) { splitBox.SetBeforeBox(rightBox); } } else { rightBox = parentBox.Boxes[2]; } while (badBox.Boxes.Count > 0) { badBox.Boxes[0].ParentBox = rightBox; } } else if (splitBox.ParentBox != null && parentBox.Boxes.Count > 1) { splitBox.SetBeforeBox(parentBox.Boxes[1]); if (splitBox.HtmlTag != null && splitBox.HtmlTag.Name == "br" && (hadLeft || leftBlock.Boxes.Count > 1)) { splitBox.Display = CssConstants.Inline; } } }
/// <summary> /// Applies style to all boxes in the tree.<br/> /// If the html tag has style defined for each apply that style to the css box of the tag.<br/> /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/> /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/> /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/> /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/> /// </summary> /// <param name="box"></param> /// <param name="htmlContainer">the html container to use for reference resolve</param> /// <param name="cssData"> </param> /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param> private static void CascadeStyles(CssBox box, HtmlContainer htmlContainer, ref CssData cssData, ref bool cssDataChanged) { box.InheritStyle(); if (box.HtmlTag != null) { // try assign style using the html element tag AssignCssBlocks(box, cssData, box.HtmlTag.Name); // try assign style using the "class" attribute of the html element if (box.HtmlTag.HasAttribute("class")) { AssignClassCssBlocks(box, cssData); } // try assign style using the "id" attribute of the html element if (box.HtmlTag.HasAttribute("id")) { string id = box.HtmlTag.TryGetAttribute("id"); AssignCssBlocks(box, cssData, "#" + id); } TranslateAttributes(box.HtmlTag, box); // Check for the style="" attribute if (box.HtmlTag.HasAttribute("style")) { CssBlock block = CssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style")); AssignCssBlock(box, block); } // Check for the <style> tag if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1) { CloneCssData(ref cssData, ref cssDataChanged); CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text.CutSubstring()); } // Check for the <link rel=stylesheet> tag if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) && box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase)) { CloneCssData(ref cssData, ref cssDataChanged); string stylesheet; CssData stylesheetData; StylesheetLoadHandler.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes, out stylesheet, out stylesheetData); if (stylesheet != null) { CssParser.ParseStyleSheet(cssData, stylesheet); } else if (stylesheetData != null) { cssData.Combine(stylesheetData); } } } // cascade text decoration only to boxes that actually have text so it will be handled correctly. if (box.TextDecoration != String.Empty && box.Text == null) { foreach (CssBox childBox in box.Boxes) { childBox.TextDecoration = box.TextDecoration; } box.TextDecoration = string.Empty; } foreach (CssBox childBox in box.Boxes) { CascadeStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged); } }