Initial population
This commit is contained in:
844
JRCookbookBusiness/Converters/HtmlCSSParser.cs
Normal file
844
JRCookbookBusiness/Converters/HtmlCSSParser.cs
Normal file
@@ -0,0 +1,844 @@
|
||||
// // Copyright (c) Microsoft. All rights reserved.
|
||||
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
|
||||
namespace HtmlToXamlDemo
|
||||
{
|
||||
// DependencyProperty
|
||||
|
||||
// TextElement
|
||||
|
||||
internal static class HtmlCssParser
|
||||
{
|
||||
private static readonly string[] Colors =
|
||||
{
|
||||
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
|
||||
"blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral",
|
||||
"cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
|
||||
"darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred",
|
||||
"darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink",
|
||||
"deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro",
|
||||
"ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink", "indianred",
|
||||
"indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
|
||||
"lightcoral",
|
||||
"lightcyan", "lightgoldenrodyellow", "lightgreen", "lightgrey", "lightpink", "lightsalmon", "lightseagreen",
|
||||
"lightskyblue", "lightslategray", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
|
||||
"maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
|
||||
"mediumslateblue",
|
||||
"mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose",
|
||||
"moccasin",
|
||||
"navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod",
|
||||
"palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum",
|
||||
"powderblue",
|
||||
"purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell",
|
||||
"sienna", "silver", "skyblue", "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", "teal",
|
||||
"thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
|
||||
};
|
||||
|
||||
private static readonly string[] SystemColors =
|
||||
{
|
||||
"activeborder", "activecaption", "appworkspace", "background", "buttonface", "buttonhighlight",
|
||||
"buttonshadow",
|
||||
"buttontext", "captiontext", "graytext", "highlight", "highlighttext", "inactiveborder", "inactivecaption",
|
||||
"inactivecaptiontext", "infobackground", "infotext", "menu", "menutext", "scrollbar", "threeddarkshadow",
|
||||
"threedface", "threedhighlight", "threedlightshadow", "threedshadow", "window", "windowframe", "windowtext"
|
||||
};
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS font Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
// CSS has five font properties: font-family, font-style, font-variant, font-weight, font-size.
|
||||
// An aggregated "font" property lets you specify in one action all the five in combination
|
||||
// with additional line-height property.
|
||||
//
|
||||
// font-family: [<family-name>,]* [<family-name> | <generic-family>]
|
||||
// generic-family: serif | sans-serif | monospace | cursive | fantasy
|
||||
// The list of families sets priorities to choose fonts;
|
||||
// Quotes not allowed around generic-family names
|
||||
// font-style: normal | italic | oblique
|
||||
// font-variant: normal | small-caps
|
||||
// font-weight: normal | bold | bolder | lighter | 100 ... 900 |
|
||||
// Default is "normal", normal==400
|
||||
// font-size: <absolute-size> | <relative-size> | <length> | <percentage>
|
||||
// absolute-size: xx-small | x-small | small | medium | large | x-large | xx-large
|
||||
// relative-size: larger | smaller
|
||||
// length: <point> | <pica> | <ex> | <em> | <points> | <millimeters> | <centimeters> | <inches>
|
||||
// Default: medium
|
||||
// font: [ <font-style> || <font-variant> || <font-weight ]? <font-size> [ / <line-height> ]? <font-family>
|
||||
|
||||
private static readonly string[] FontGenericFamilies =
|
||||
{
|
||||
"serif", "sans-serif", "monospace", "cursive", "fantasy"
|
||||
};
|
||||
|
||||
private static readonly string[] FontStyles = {"normal", "italic", "oblique"};
|
||||
private static readonly string[] FontVariants = {"normal", "small-caps"};
|
||||
|
||||
private static readonly string[] FontWeights =
|
||||
{
|
||||
"normal", "bold", "bolder", "lighter", "100", "200", "300",
|
||||
"400", "500", "600", "700", "800", "900"
|
||||
};
|
||||
|
||||
private static readonly string[] FontAbsoluteSizes =
|
||||
{
|
||||
"xx-small", "x-small", "small", "medium", "large",
|
||||
"x-large", "xx-large"
|
||||
};
|
||||
|
||||
private static readonly string[] FontRelativeSizes = {"larger", "smaller"};
|
||||
|
||||
private static readonly string[] FontSizeUnits = {"px", "mm", "cm", "in", "pt", "pc", "em", "ex", "%"};
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS list-style Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
// list-style: [ <list-style-type> || <list-style-position> || <list-style-image> ]
|
||||
|
||||
private static readonly string[] ListStyleTypes =
|
||||
{
|
||||
"disc", "circle", "square", "decimal", "lower-roman",
|
||||
"upper-roman", "lower-alpha", "upper-alpha", "none"
|
||||
};
|
||||
|
||||
private static readonly string[] ListStylePositions = {"inside", "outside"};
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS text-decorations Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] TextDecorations = {"none", "underline", "overline", "line-through", "blink"};
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS text-transform Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] TextTransforms = {"none", "capitalize", "uppercase", "lowercase"};
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS text-align Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] TextAligns = {"left", "right", "center", "justify"};
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS vertical-align Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] VerticalAligns =
|
||||
{
|
||||
"baseline", "sub", "super", "top", "text-top", "middle",
|
||||
"bottom", "text-bottom"
|
||||
};
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS float Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] Floats = {"left", "right", "none"};
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS clear Property
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] Clears = {"none", "left", "right", "both"};
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS border-style Propertie
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static readonly string[] BorderStyles =
|
||||
{
|
||||
"none", "dotted", "dashed", "solid", "double", "groove",
|
||||
"ridge", "inset", "outset"
|
||||
};
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// What are these definitions doing here:
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static string[] _blocks = {"block", "inline", "list-item", "none"};
|
||||
// .................................................................
|
||||
//
|
||||
// Processing CSS Attributes
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
internal static void GetElementPropertiesFromCssAttributes(XmlElement htmlElement, string elementName,
|
||||
CssStylesheet stylesheet, Hashtable localProperties, List<XmlElement> sourceContext)
|
||||
{
|
||||
var styleFromStylesheet = stylesheet.GetStyle(elementName, sourceContext);
|
||||
|
||||
var styleInline = HtmlToXamlConverter.GetAttribute(htmlElement, "style");
|
||||
|
||||
// Combine styles from stylesheet and from inline attribute.
|
||||
// The order is important - the latter styles will override the former.
|
||||
var style = styleFromStylesheet ?? null;
|
||||
if (styleInline != null)
|
||||
{
|
||||
style = style == null ? styleInline : (style + ";" + styleInline);
|
||||
}
|
||||
|
||||
// Apply local style to current formatting properties
|
||||
if (style != null)
|
||||
{
|
||||
var styleValues = style.Split(';');
|
||||
foreach (string t in styleValues)
|
||||
{
|
||||
string[] styleNameValue;
|
||||
|
||||
styleNameValue = t.Split(':');
|
||||
if (styleNameValue.Length == 2)
|
||||
{
|
||||
var styleName = styleNameValue[0].Trim().ToLower();
|
||||
var styleValue = HtmlToXamlConverter.UnQuote(styleNameValue[1].Trim()).ToLower();
|
||||
var nextIndex = 0;
|
||||
|
||||
switch (styleName)
|
||||
{
|
||||
case "font":
|
||||
ParseCssFont(styleValue, localProperties);
|
||||
break;
|
||||
case "font-family":
|
||||
ParseCssFontFamily(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "font-size":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, "font-size",
|
||||
/*mustBeNonNegative:*/true);
|
||||
break;
|
||||
case "font-style":
|
||||
ParseCssFontStyle(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "font-weight":
|
||||
ParseCssFontWeight(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "font-variant":
|
||||
ParseCssFontVariant(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "line-height":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, "line-height",
|
||||
/*mustBeNonNegative:*/true);
|
||||
break;
|
||||
case "word-spacing":
|
||||
// Implement word-spacing conversion
|
||||
break;
|
||||
case "letter-spacing":
|
||||
// Implement letter-spacing conversion
|
||||
break;
|
||||
case "color":
|
||||
ParseCssColor(styleValue, ref nextIndex, localProperties, "color");
|
||||
break;
|
||||
|
||||
case "text-decoration":
|
||||
ParseCssTextDecoration(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
|
||||
case "text-transform":
|
||||
ParseCssTextTransform(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
|
||||
case "background-color":
|
||||
ParseCssColor(styleValue, ref nextIndex, localProperties, "background-color");
|
||||
break;
|
||||
case "background":
|
||||
// TODO: need to parse composite background property
|
||||
ParseCssBackground(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
|
||||
case "text-align":
|
||||
ParseCssTextAlign(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "vertical-align":
|
||||
ParseCssVerticalAlign(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "text-indent":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, "text-indent",
|
||||
/*mustBeNonNegative:*/false);
|
||||
break;
|
||||
|
||||
case "width":
|
||||
case "height":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, styleName,
|
||||
/*mustBeNonNegative:*/true);
|
||||
break;
|
||||
|
||||
case "margin": // top/right/bottom/left
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
|
||||
break;
|
||||
case "margin-top":
|
||||
case "margin-right":
|
||||
case "margin-bottom":
|
||||
case "margin-left":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, styleName,
|
||||
/*mustBeNonNegative:*/true);
|
||||
break;
|
||||
|
||||
case "padding":
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
|
||||
break;
|
||||
case "padding-top":
|
||||
case "padding-right":
|
||||
case "padding-bottom":
|
||||
case "padding-left":
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, styleName,
|
||||
/*mustBeNonNegative:*/true);
|
||||
break;
|
||||
|
||||
case "border":
|
||||
ParseCssBorder(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "border-style":
|
||||
case "border-width":
|
||||
case "border-color":
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
|
||||
break;
|
||||
case "border-top":
|
||||
case "border-right":
|
||||
case "border-left":
|
||||
case "border-bottom":
|
||||
// Parse css border style
|
||||
break;
|
||||
|
||||
// NOTE: css names for elementary border styles have side indications in the middle (top/bottom/left/right)
|
||||
// In our internal notation we intentionally put them at the end - to unify processing in ParseCssRectangleProperty method
|
||||
case "border-top-style":
|
||||
case "border-right-style":
|
||||
case "border-left-style":
|
||||
case "border-bottom-style":
|
||||
case "border-top-color":
|
||||
case "border-right-color":
|
||||
case "border-left-color":
|
||||
case "border-bottom-color":
|
||||
case "border-top-width":
|
||||
case "border-right-width":
|
||||
case "border-left-width":
|
||||
case "border-bottom-width":
|
||||
// Parse css border style
|
||||
break;
|
||||
|
||||
case "display":
|
||||
// Implement display style conversion
|
||||
break;
|
||||
|
||||
case "float":
|
||||
ParseCssFloat(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
case "clear":
|
||||
ParseCssClear(styleValue, ref nextIndex, localProperties);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Parsing CSS - Lexical Helpers
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
// Skips whitespaces in style values
|
||||
private static void ParseWhiteSpace(string styleValue, ref int nextIndex)
|
||||
{
|
||||
while (nextIndex < styleValue.Length && char.IsWhiteSpace(styleValue[nextIndex]))
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the following character matches to a given word and advances nextIndex
|
||||
// by the word's length in case of success.
|
||||
// Otherwise leaves nextIndex in place (except for possible whitespaces).
|
||||
// Returns true or false depending on success or failure of matching.
|
||||
private static bool ParseWord(string word, string styleValue, ref int nextIndex)
|
||||
{
|
||||
ParseWhiteSpace(styleValue, ref nextIndex);
|
||||
|
||||
for (var i = 0; i < word.Length; i++)
|
||||
{
|
||||
if (!(nextIndex + i < styleValue.Length && word[i] == styleValue[nextIndex + i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextIndex + word.Length < styleValue.Length && char.IsLetterOrDigit(styleValue[nextIndex + word.Length]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nextIndex += word.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// CHecks whether the following character sequence matches to one of the given words,
|
||||
// and advances the nextIndex to matched word length.
|
||||
// Returns null in case if there is no match or the word matched.
|
||||
private static string ParseWordEnumeration(string[] words, string styleValue, ref int nextIndex)
|
||||
{
|
||||
foreach (string t in words)
|
||||
{
|
||||
if (ParseWord(t, styleValue, ref nextIndex))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void ParseWordEnumeration(string[] words, string styleValue, ref int nextIndex,
|
||||
Hashtable localProperties, string attributeName)
|
||||
{
|
||||
var attributeValue = ParseWordEnumeration(words, styleValue, ref nextIndex);
|
||||
if (attributeValue != null)
|
||||
{
|
||||
localProperties[attributeName] = attributeValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseCssSize(string styleValue, ref int nextIndex, bool mustBeNonNegative)
|
||||
{
|
||||
ParseWhiteSpace(styleValue, ref nextIndex);
|
||||
|
||||
var startIndex = nextIndex;
|
||||
|
||||
// Parse optional munis sign
|
||||
if (nextIndex < styleValue.Length && styleValue[nextIndex] == '-')
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
if (nextIndex < styleValue.Length && char.IsDigit(styleValue[nextIndex]))
|
||||
{
|
||||
while (nextIndex < styleValue.Length &&
|
||||
(char.IsDigit(styleValue[nextIndex]) || styleValue[nextIndex] == '.'))
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
var number = styleValue.Substring(startIndex, nextIndex - startIndex);
|
||||
|
||||
var unit = ParseWordEnumeration(FontSizeUnits, styleValue, ref nextIndex) ?? "px";
|
||||
|
||||
if (mustBeNonNegative && styleValue[startIndex] == '-')
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
return number + unit;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void ParseCssSize(string styleValue, ref int nextIndex, Hashtable localValues,
|
||||
string propertyName,
|
||||
bool mustBeNonNegative)
|
||||
{
|
||||
var length = ParseCssSize(styleValue, ref nextIndex, mustBeNonNegative);
|
||||
if (length != null)
|
||||
{
|
||||
localValues[propertyName] = length;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseCssColor(string styleValue, ref int nextIndex)
|
||||
{
|
||||
// Implement color parsing
|
||||
// rgb(100%,53.5%,10%)
|
||||
// rgb(255,91,26)
|
||||
// #FF5B1A
|
||||
// black | silver | gray | ... | aqua
|
||||
// transparent - for background-color
|
||||
ParseWhiteSpace(styleValue, ref nextIndex);
|
||||
|
||||
string color = null;
|
||||
|
||||
if (nextIndex < styleValue.Length)
|
||||
{
|
||||
var startIndex = nextIndex;
|
||||
var character = styleValue[nextIndex];
|
||||
|
||||
if (character == '#')
|
||||
{
|
||||
nextIndex++;
|
||||
while (nextIndex < styleValue.Length)
|
||||
{
|
||||
character = char.ToUpper(styleValue[nextIndex]);
|
||||
if (!('0' <= character && character <= '9' || 'A' <= character && character <= 'F'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
nextIndex++;
|
||||
}
|
||||
if (nextIndex > startIndex + 1)
|
||||
{
|
||||
color = styleValue.Substring(startIndex, nextIndex - startIndex);
|
||||
}
|
||||
}
|
||||
else if (styleValue.Substring(nextIndex, 3).ToLower() == "rbg")
|
||||
{
|
||||
// Implement real rgb() color parsing
|
||||
while (nextIndex < styleValue.Length && styleValue[nextIndex] != ')')
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
if (nextIndex < styleValue.Length)
|
||||
{
|
||||
nextIndex++; // to skip ')'
|
||||
}
|
||||
color = "gray"; // return bogus color
|
||||
}
|
||||
else if (char.IsLetter(character))
|
||||
{
|
||||
color = ParseWordEnumeration(Colors, styleValue, ref nextIndex);
|
||||
if (color == null)
|
||||
{
|
||||
color = ParseWordEnumeration(SystemColors, styleValue, ref nextIndex);
|
||||
if (color != null)
|
||||
{
|
||||
// Implement smarter system color converions into real colors
|
||||
color = "black";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private static void ParseCssColor(string styleValue, ref int nextIndex, Hashtable localValues,
|
||||
string propertyName)
|
||||
{
|
||||
var color = ParseCssColor(styleValue, ref nextIndex);
|
||||
if (color != null)
|
||||
{
|
||||
localValues[propertyName] = color;
|
||||
}
|
||||
}
|
||||
|
||||
// Parses CSS string fontStyle representing a value for css font attribute
|
||||
private static void ParseCssFont(string styleValue, Hashtable localProperties)
|
||||
{
|
||||
var nextIndex = 0;
|
||||
|
||||
ParseCssFontStyle(styleValue, ref nextIndex, localProperties);
|
||||
ParseCssFontVariant(styleValue, ref nextIndex, localProperties);
|
||||
ParseCssFontWeight(styleValue, ref nextIndex, localProperties);
|
||||
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, "font-size", /*mustBeNonNegative:*/true);
|
||||
|
||||
ParseWhiteSpace(styleValue, ref nextIndex);
|
||||
if (nextIndex < styleValue.Length && styleValue[nextIndex] == '/')
|
||||
{
|
||||
nextIndex++;
|
||||
ParseCssSize(styleValue, ref nextIndex, localProperties, "line-height", /*mustBeNonNegative:*/true);
|
||||
}
|
||||
|
||||
ParseCssFontFamily(styleValue, ref nextIndex, localProperties);
|
||||
}
|
||||
|
||||
private static void ParseCssFontStyle(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(FontStyles, styleValue, ref nextIndex, localProperties, "font-style");
|
||||
}
|
||||
|
||||
private static void ParseCssFontVariant(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(FontVariants, styleValue, ref nextIndex, localProperties, "font-variant");
|
||||
}
|
||||
|
||||
private static void ParseCssFontWeight(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(FontWeights, styleValue, ref nextIndex, localProperties, "font-weight");
|
||||
}
|
||||
|
||||
private static void ParseCssFontFamily(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
string fontFamilyList = null;
|
||||
|
||||
while (nextIndex < styleValue.Length)
|
||||
{
|
||||
// Try generic-family
|
||||
var fontFamily = ParseWordEnumeration(FontGenericFamilies, styleValue, ref nextIndex);
|
||||
|
||||
if (fontFamily == null)
|
||||
{
|
||||
// Try quoted font family name
|
||||
if (nextIndex < styleValue.Length && (styleValue[nextIndex] == '"' || styleValue[nextIndex] == '\''))
|
||||
{
|
||||
var quote = styleValue[nextIndex];
|
||||
|
||||
nextIndex++;
|
||||
|
||||
var startIndex = nextIndex;
|
||||
|
||||
while (nextIndex < styleValue.Length && styleValue[nextIndex] != quote)
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
fontFamily = '"' + styleValue.Substring(startIndex, nextIndex - startIndex) + '"';
|
||||
}
|
||||
|
||||
if (fontFamily == null)
|
||||
{
|
||||
// Try unquoted font family name
|
||||
var startIndex = nextIndex;
|
||||
while (nextIndex < styleValue.Length && styleValue[nextIndex] != ',' &&
|
||||
styleValue[nextIndex] != ';')
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
if (nextIndex > startIndex)
|
||||
{
|
||||
fontFamily = styleValue.Substring(startIndex, nextIndex - startIndex).Trim();
|
||||
if (fontFamily.Length == 0)
|
||||
{
|
||||
fontFamily = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParseWhiteSpace(styleValue, ref nextIndex);
|
||||
if (nextIndex < styleValue.Length && styleValue[nextIndex] == ',')
|
||||
{
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
if (fontFamily != null)
|
||||
{
|
||||
// css font-family can contein a list of names. We only consider the first name from the list. Need a decision what to do with remaining names
|
||||
// fontFamilyList = (fontFamilyList == null) ? fontFamily : fontFamilyList + "," + fontFamily;
|
||||
if (fontFamilyList == null && fontFamily.Length > 0)
|
||||
{
|
||||
if (fontFamily[0] == '"' || fontFamily[0] == '\'')
|
||||
{
|
||||
// Unquote the font family name
|
||||
fontFamily = fontFamily.Substring(1, fontFamily.Length - 2);
|
||||
}
|
||||
fontFamilyList = fontFamily;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fontFamilyList != null)
|
||||
{
|
||||
localProperties["font-family"] = fontFamilyList;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseCssListStyle(string styleValue, Hashtable localProperties)
|
||||
{
|
||||
var nextIndex = 0;
|
||||
|
||||
while (nextIndex < styleValue.Length)
|
||||
{
|
||||
var listStyleType = ParseCssListStyleType(styleValue, ref nextIndex);
|
||||
if (listStyleType != null)
|
||||
{
|
||||
localProperties["list-style-type"] = listStyleType;
|
||||
}
|
||||
else
|
||||
{
|
||||
var listStylePosition = ParseCssListStylePosition(styleValue, ref nextIndex);
|
||||
if (listStylePosition != null)
|
||||
{
|
||||
localProperties["list-style-position"] = listStylePosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
var listStyleImage = ParseCssListStyleImage(styleValue, ref nextIndex);
|
||||
if (listStyleImage != null)
|
||||
{
|
||||
localProperties["list-style-image"] = listStyleImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Process unrecognized list style value
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseCssListStyleType(string styleValue, ref int nextIndex) => ParseWordEnumeration(ListStyleTypes, styleValue, ref nextIndex);
|
||||
|
||||
private static string ParseCssListStylePosition(string styleValue, ref int nextIndex) => ParseWordEnumeration(ListStylePositions, styleValue, ref nextIndex);
|
||||
|
||||
private static string ParseCssListStyleImage(string styleValue, ref int nextIndex) => null;
|
||||
|
||||
private static void ParseCssTextDecoration(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
// Set default text-decorations:none;
|
||||
for (var i = 1; i < TextDecorations.Length; i++)
|
||||
{
|
||||
localProperties["text-decoration-" + TextDecorations[i]] = "false";
|
||||
}
|
||||
|
||||
// Parse list of decorations values
|
||||
while (nextIndex < styleValue.Length)
|
||||
{
|
||||
var decoration = ParseWordEnumeration(TextDecorations, styleValue, ref nextIndex);
|
||||
if (decoration == null || decoration == "none")
|
||||
{
|
||||
break;
|
||||
}
|
||||
localProperties["text-decoration-" + decoration] = "true";
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseCssTextTransform(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(TextTransforms, styleValue, ref nextIndex, localProperties, "text-transform");
|
||||
}
|
||||
|
||||
private static void ParseCssTextAlign(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(TextAligns, styleValue, ref nextIndex, localProperties, "text-align");
|
||||
}
|
||||
|
||||
private static void ParseCssVerticalAlign(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
// Parse percentage value for vertical-align style
|
||||
ParseWordEnumeration(VerticalAligns, styleValue, ref nextIndex, localProperties, "vertical-align");
|
||||
}
|
||||
|
||||
private static void ParseCssFloat(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(Floats, styleValue, ref nextIndex, localProperties, "float");
|
||||
}
|
||||
|
||||
private static void ParseCssClear(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
ParseWordEnumeration(Clears, styleValue, ref nextIndex, localProperties, "clear");
|
||||
}
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS margin and padding Properties
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
// Generic method for parsing any of four-values properties, such as margin, padding, border-width, border-style, border-color
|
||||
private static bool ParseCssRectangleProperty(string styleValue, ref int nextIndex, Hashtable localProperties,
|
||||
string propertyName)
|
||||
{
|
||||
// CSS Spec:
|
||||
// If only one value is set, then the value applies to all four sides;
|
||||
// If two or three values are set, then missinng value(s) are taken fromm the opposite side(s).
|
||||
// The order they are applied is: top/right/bottom/left
|
||||
|
||||
Debug.Assert(propertyName == "margin" || propertyName == "padding" || propertyName == "border-width" ||
|
||||
propertyName == "border-style" || propertyName == "border-color");
|
||||
|
||||
var value = propertyName == "border-color"
|
||||
? ParseCssColor(styleValue, ref nextIndex)
|
||||
: propertyName == "border-style"
|
||||
? ParseCssBorderStyle(styleValue, ref nextIndex)
|
||||
: ParseCssSize(styleValue, ref nextIndex, /*mustBeNonNegative:*/true);
|
||||
if (value != null)
|
||||
{
|
||||
localProperties[propertyName + "-top"] = value;
|
||||
localProperties[propertyName + "-bottom"] = value;
|
||||
localProperties[propertyName + "-right"] = value;
|
||||
localProperties[propertyName + "-left"] = value;
|
||||
value = propertyName == "border-color"
|
||||
? ParseCssColor(styleValue, ref nextIndex)
|
||||
: propertyName == "border-style"
|
||||
? ParseCssBorderStyle(styleValue, ref nextIndex)
|
||||
: ParseCssSize(styleValue, ref nextIndex, /*mustBeNonNegative:*/true);
|
||||
if (value != null)
|
||||
{
|
||||
localProperties[propertyName + "-right"] = value;
|
||||
localProperties[propertyName + "-left"] = value;
|
||||
value = propertyName == "border-color"
|
||||
? ParseCssColor(styleValue, ref nextIndex)
|
||||
: propertyName == "border-style"
|
||||
? ParseCssBorderStyle(styleValue, ref nextIndex)
|
||||
: ParseCssSize(styleValue, ref nextIndex, /*mustBeNonNegative:*/true);
|
||||
if (value != null)
|
||||
{
|
||||
localProperties[propertyName + "-bottom"] = value;
|
||||
value = propertyName == "border-color"
|
||||
? ParseCssColor(styleValue, ref nextIndex)
|
||||
: propertyName == "border-style"
|
||||
? ParseCssBorderStyle(styleValue, ref nextIndex)
|
||||
: ParseCssSize(styleValue, ref nextIndex, /*mustBeNonNegative:*/true);
|
||||
if (value != null)
|
||||
{
|
||||
localProperties[propertyName + "-left"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS border Properties
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
// border: [ <border-width> || <border-style> || <border-color> ]
|
||||
|
||||
private static void ParseCssBorder(string styleValue, ref int nextIndex, Hashtable localProperties)
|
||||
{
|
||||
while (
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, "border-width") ||
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, "border-style") ||
|
||||
ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, "border-color"))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseCssBorderStyle(string styleValue, ref int nextIndex) => ParseWordEnumeration(BorderStyles, styleValue, ref nextIndex);
|
||||
|
||||
// .................................................................
|
||||
//
|
||||
// Pasring CSS Background Properties
|
||||
//
|
||||
// .................................................................
|
||||
|
||||
private static void ParseCssBackground(string styleValue, ref int nextIndex, Hashtable localValues)
|
||||
{
|
||||
// Implement parsing background attribute
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user