//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.UI { using System; using System.Collections; using System.Globalization; using System.IO; using System.Text; using System.Web.Util; /// /// Derived TextWriter that provides CSS rendering API. /// internal sealed class CssTextWriter : TextWriter { private TextWriter _writer; private static Hashtable attrKeyLookupTable; private static AttributeInformation[] attrNameLookupArray; static CssTextWriter() { // register known style attributes, HtmlTextWriterStyle.Length attrKeyLookupTable = new Hashtable((int)HtmlTextWriterStyle.ZIndex + 1); attrNameLookupArray = new AttributeInformation[(int)HtmlTextWriterStyle.ZIndex + 1]; RegisterAttribute("background-color", HtmlTextWriterStyle.BackgroundColor); RegisterAttribute("background-image", HtmlTextWriterStyle.BackgroundImage, true, true); RegisterAttribute("border-collapse", HtmlTextWriterStyle.BorderCollapse); RegisterAttribute("border-color", HtmlTextWriterStyle.BorderColor); RegisterAttribute("border-style", HtmlTextWriterStyle.BorderStyle); RegisterAttribute("border-width", HtmlTextWriterStyle.BorderWidth); RegisterAttribute("color", HtmlTextWriterStyle.Color); RegisterAttribute("cursor", HtmlTextWriterStyle.Cursor); RegisterAttribute("direction", HtmlTextWriterStyle.Direction); RegisterAttribute("display", HtmlTextWriterStyle.Display); RegisterAttribute("filter", HtmlTextWriterStyle.Filter); RegisterAttribute("font-family", HtmlTextWriterStyle.FontFamily, true); RegisterAttribute("font-size", HtmlTextWriterStyle.FontSize); RegisterAttribute("font-style", HtmlTextWriterStyle.FontStyle); RegisterAttribute("font-variant", HtmlTextWriterStyle.FontVariant); RegisterAttribute("font-weight", HtmlTextWriterStyle.FontWeight); RegisterAttribute("height", HtmlTextWriterStyle.Height); RegisterAttribute("left", HtmlTextWriterStyle.Left); RegisterAttribute("list-style-image", HtmlTextWriterStyle.ListStyleImage, true, true); RegisterAttribute("list-style-type", HtmlTextWriterStyle.ListStyleType); RegisterAttribute("margin", HtmlTextWriterStyle.Margin); RegisterAttribute("margin-bottom", HtmlTextWriterStyle.MarginBottom); RegisterAttribute("margin-left", HtmlTextWriterStyle.MarginLeft); RegisterAttribute("margin-right", HtmlTextWriterStyle.MarginRight); RegisterAttribute("margin-top", HtmlTextWriterStyle.MarginTop); RegisterAttribute("overflow-x", HtmlTextWriterStyle.OverflowX); RegisterAttribute("overflow-y", HtmlTextWriterStyle.OverflowY); RegisterAttribute("overflow", HtmlTextWriterStyle.Overflow); RegisterAttribute("padding", HtmlTextWriterStyle.Padding); RegisterAttribute("padding-bottom", HtmlTextWriterStyle.PaddingBottom); RegisterAttribute("padding-left", HtmlTextWriterStyle.PaddingLeft); RegisterAttribute("padding-right", HtmlTextWriterStyle.PaddingRight); RegisterAttribute("padding-top", HtmlTextWriterStyle.PaddingTop); RegisterAttribute("position", HtmlTextWriterStyle.Position); RegisterAttribute("text-align", HtmlTextWriterStyle.TextAlign); RegisterAttribute("text-decoration", HtmlTextWriterStyle.TextDecoration); RegisterAttribute("text-overflow", HtmlTextWriterStyle.TextOverflow); RegisterAttribute("top", HtmlTextWriterStyle.Top); RegisterAttribute("vertical-align", HtmlTextWriterStyle.VerticalAlign); RegisterAttribute("visibility", HtmlTextWriterStyle.Visibility); RegisterAttribute("width", HtmlTextWriterStyle.Width); RegisterAttribute("white-space", HtmlTextWriterStyle.WhiteSpace); RegisterAttribute("z-index", HtmlTextWriterStyle.ZIndex); } /// /// Initializes an instance of a CssTextWriter with its underlying TextWriter. /// public CssTextWriter(TextWriter writer) { _writer = writer; } /// public override Encoding Encoding { get { return _writer.Encoding; } } /// public override string NewLine { get { return _writer.NewLine; } set { _writer.NewLine = value; } } /// public override void Close() { _writer.Close(); } /// public override void Flush() { _writer.Flush(); } /// /// Returns the HtmlTextWriterStyle value for known style attributes. /// public static HtmlTextWriterStyle GetStyleKey(string styleName) { if (!String.IsNullOrEmpty(styleName)) { object key = attrKeyLookupTable[styleName.ToLower(CultureInfo.InvariantCulture)]; if (key != null) { return (HtmlTextWriterStyle)key; } } return (HtmlTextWriterStyle)(-1); } /// /// Returns the name of the attribute corresponding to the specified HtmlTextWriterStyle value. /// public static string GetStyleName(HtmlTextWriterStyle styleKey) { if ((int)styleKey >= 0 && (int)styleKey < attrNameLookupArray.Length) { return attrNameLookupArray[(int)styleKey].name; } return String.Empty; } /// /// Does the specified style key require attribute value encoding if the value is being /// rendered in a style attribute. /// public static bool IsStyleEncoded(HtmlTextWriterStyle styleKey) { if ((int)styleKey >= 0 && (int)styleKey < attrNameLookupArray.Length) { return attrNameLookupArray[(int)styleKey].encode; } return true; } /// /// /// Registers the specified style attribute to create a mapping between a string representation /// and the corresponding HtmlTextWriterStyle value. /// internal static void RegisterAttribute(string name, HtmlTextWriterStyle key) { RegisterAttribute(name, key, false, false); } /// /// /// Registers the specified style attribute to create a mapping between a string representation /// and the corresponding HtmlTextWriterStyle value. /// internal static void RegisterAttribute(string name, HtmlTextWriterStyle key, bool encode) { RegisterAttribute(name, key, encode, false); } /// /// /// Registers the specified style attribute to create a mapping between a string representation /// and the corresponding HtmlTextWriterStyle value. /// In addition, the mapping can include additional information about the attribute type /// such as whether it is a URL. /// internal static void RegisterAttribute(string name, HtmlTextWriterStyle key, bool encode, bool isUrl) { string nameLCase = name.ToLower(CultureInfo.InvariantCulture); attrKeyLookupTable.Add(nameLCase, key); if ((int)key < attrNameLookupArray.Length) { attrNameLookupArray[(int)key] = new AttributeInformation(name, encode, isUrl); } } /// public override void Write(string s) { _writer.Write(s); } /// public override void Write(bool value) { _writer.Write(value); } /// public override void Write(char value) { _writer.Write(value); } /// public override void Write(char[] buffer) { _writer.Write(buffer); } /// public override void Write(char[] buffer, int index, int count) { _writer.Write(buffer, index, count); } /// public override void Write(double value) { _writer.Write(value); } /// public override void Write(float value) { _writer.Write(value); } /// public override void Write(int value) { _writer.Write(value); } /// public override void Write(long value) { _writer.Write(value); } /// public override void Write(object value) { _writer.Write(value); } /// public override void Write(string format, object arg0) { _writer.Write(format, arg0); } /// public override void Write(string format, object arg0, object arg1) { _writer.Write(format, arg0, arg1); } /// public override void Write(string format, params object[] arg) { _writer.Write(format, arg); } /// /// Render out the specified style attribute and value. /// public void WriteAttribute(string name, string value) { WriteAttribute(_writer, GetStyleKey(name), name, value); } /// /// Render out the specified style attribute and value. /// public void WriteAttribute(HtmlTextWriterStyle key, string value) { WriteAttribute(_writer, key, GetStyleName(key), value); } /// /// Render the specified style attribute into the specified TextWriter. /// This method contains all the logic for rendering a CSS name/value pair. /// private static void WriteAttribute(TextWriter writer, HtmlTextWriterStyle key, string name, string value) { writer.Write(name); writer.Write(':'); bool isUrl = false; if (key != (HtmlTextWriterStyle)(-1)) { isUrl = attrNameLookupArray[(int)key].isUrl; } if (isUrl == false) { writer.Write(value); } else { WriteUrlAttribute(writer, value); } writer.Write(';'); } /// /// Render the specified style attributes. This is used by HtmlTextWriter to render out all /// its collected style attributes. /// internal static void WriteAttributes(TextWriter writer, RenderStyle[] styles, int count) { for (int i = 0; i < count; i++) { RenderStyle style = styles[i]; WriteAttribute(writer, style.key, style.name, style.value); } } /// /// Start rendering a new CSS rule with the given selector. /// public void WriteBeginCssRule(string selector) { _writer.Write(selector); _writer.Write(" { "); } /// /// End the current CSS rule that is being rendered. /// public void WriteEndCssRule() { _writer.WriteLine(" }"); } /// public override void WriteLine(string s) { _writer.WriteLine(s); } /// public override void WriteLine() { _writer.WriteLine(); } /// public override void WriteLine(bool value) { _writer.WriteLine(value); } /// public override void WriteLine(char value) { _writer.WriteLine(value); } /// public override void WriteLine(char[] buffer) { _writer.WriteLine(buffer); } /// public override void WriteLine(char[] buffer, int index, int count) { _writer.WriteLine(buffer, index, count); } /// public override void WriteLine(double value) { _writer.WriteLine(value); } /// public override void WriteLine(float value) { _writer.WriteLine(value); } /// public override void WriteLine(int value) { _writer.WriteLine(value); } /// public override void WriteLine(long value) { _writer.WriteLine(value); } /// public override void WriteLine(object value) { _writer.WriteLine(value); } /// public override void WriteLine(string format, object arg0) { _writer.WriteLine(format, arg0); } /// public override void WriteLine(string format, object arg0, object arg1) { _writer.WriteLine(format, arg0, arg1); } /// public override void WriteLine(string format, params object[] arg) { _writer.WriteLine(format, arg); } /// public override void WriteLine(UInt32 value) { _writer.WriteLine(value); } /// /// Writes out the specified URL value with the appropriate encoding /// and url() syntax. /// internal for unit testing. /// internal static void WriteUrlAttribute(TextWriter writer, string url) { string urlValue = url; char[] quotes = new char[] { '\'', '"'}; char? surroundingQuote = null; if (StringUtil.StringStartsWith(url, "url(")) { int urlIndex = 4; int urlLength = url.Length - 4; if (StringUtil.StringEndsWith(url, ')')) { urlLength--; } // extract out the actual URL value urlValue = url.Substring(urlIndex, urlLength).Trim(); } // The CSS specification http://www.w3.org/TR/CSS2/syndata.html#uri says the ' and " characters are // optional for specifying the url values. // And we do not want to pass them to UrlPathEncode if they are present. foreach (char quote in quotes) { if (StringUtil.StringStartsWith(urlValue, quote) && StringUtil.StringEndsWith(urlValue, quote)) { urlValue = urlValue.Trim(quote); surroundingQuote = quote; break; } } // write out the "url(" prefix writer.Write("url("); if (surroundingQuote != null) { writer.Write(surroundingQuote); } writer.Write(HttpUtility.UrlPathEncode(urlValue)); if (surroundingQuote != null) { writer.Write(surroundingQuote); } // write out the end of the "url()" syntax writer.Write(")"); } /// /// Holds information about each registered style attribute. /// private struct AttributeInformation { public string name; public bool isUrl; public bool encode; public AttributeInformation(string name, bool encode, bool isUrl) { this.name = name; this.encode = encode; this.isUrl = isUrl; } } } /// /// Holds information about each style attribute that needs to be rendered. /// This is used by the tag rendering API of HtmlTextWriter. /// internal struct RenderStyle { public string name; public string value; public HtmlTextWriterStyle key; } }