Мне нужно встроить CSS из таблицы стилей в C#.
Например, как это работает.
http://www.mailchimp.com/labs/inlinecss.php
Css прост, просто классы, никаких причудливых селекторов.
Я подумывал об использовании регулярного выражения (?(?[^{}]+){(?[^{}]+)})+, чтобы удалить правила из CSS, а затем попытаться выполнить простую замену строк там, где вызываются классы, но некоторые элементы html уже имеют тег стиля, поэтому Мне тоже придется это учитывать.
Есть ли более простой подход? Или что-то уже написанное на C#?
ОБНОВЛЕНИЕ – 16 сентября 2010 г.
Мне удалось прийти используйте простой встроенный CSS, при условии, что ваш HTML также действителен xml. Он использует регулярное выражение для получения всех стилей в вашем элементе . Затем преобразует селекторы CSS в выражения XPath и добавляет встроенный стиль к соответствующим элементам перед любым ранее существовавшим встроенным стилем.
Обратите внимание, что CssToXpath не полностью реализовано, есть некоторые вещи, которые он просто не может сделать... пока.
CssInliner.cs
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
namespace CssInliner
{
public class CssInliner
{
private static Regex _matchStyles = new Regex("\\s*(?(?[^{}]+){(?[^{}]+)})",
RegexOptions.IgnoreCase
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
public List Styles { get; private set; }
public string InlinedXhtml { get; private set; }
private XElement XhtmlDocument { get; set; }
public CssInliner(string xhtml)
{
XhtmlDocument = ParseXhtml(xhtml);
Styles = GetStyleMatches();
foreach (var style in Styles)
{
if (!style.Success)
return;
var cssSelector = style.Groups["selector"].Value.Trim();
var xpathSelector = CssToXpath.Transform(cssSelector);
var cssStyle = style.Groups["style"].Value.Trim();
foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
{
var inlineStyle = element.Attribute("style");
var newInlineStyle = cssStyle + ";";
if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
{
newInlineStyle += inlineStyle.Value;
}
element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
}
}
XhtmlDocument.Descendants("style").Remove();
InlinedXhtml = XhtmlDocument.ToString();
}
private List GetStyleMatches()
{
var styles = new List();
var styleElements = XhtmlDocument.Descendants("style");
foreach (var styleElement in styleElements)
{
var matches = _matchStyles.Matches(styleElement.Value);
foreach (Match match in matches)
{
styles.Add(match);
}
}
return styles;
}
private static XElement ParseXhtml(string xhtml)
{
return XElement.Parse(xhtml);
}
}
}
CssToXpath.cs
using System.Text.RegularExpressions;
namespace CssInliner
{
public static class CssToXpath
{
public static string Transform(string css)
{
#region Translation Rules
// References: http://ejohn.org/blog/xpath-css-selectors/
// http://code.google.com/p/css2xpath/sour ... s2xpath.js
var regexReplaces = new[] {
// add @ for attribs
new RegexReplace {
Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
Replace = @"[@$1$2]"
},
// multiple queries
new RegexReplace {
Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
Replace = @"|"
},
// , + ~ >
new RegexReplace {
Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
Replace = @"$1"
},
//* ~ + >
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::*[1]/self::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/$2"
},
// all unescaped stuff escaped
new RegexReplace {
Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
Replace = @"[$1='$2']"
},
// all descendant or self to //
new RegexReplace {
Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"$1*$2$3"
},
new RegexReplace {
Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
Replace = @"$1//$2"
},
new RegexReplace {
Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
Replace = @"//"
},
// :first-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
Replace = @"*[1]/self::$1"
},
// :last-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
Replace = @"$1[not(following-sibling::*)]"
},
// :only-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
Replace = @"*[last()=1]/self::$1"
},
// :empty
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
Replace = @"$1[not(*) and not(normalize-space())]"
},
// |= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
},
// *= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(@$1,$2)]"
},
// ~= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
},
// ^= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[starts-with(@$1,$2)]"
},
// != attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[not(@$1) or @$1!=$2]"
},
// ids
new RegexReplace {
Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[@id='$1']"
},
// classes
new RegexReplace {
Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
},
// normalize multiple filters
new RegexReplace {
Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
Replace = @" and ($1)"
},
};
#endregion
foreach (var regexReplace in regexReplaces)
{
css = regexReplace.Regex.Replace(css, regexReplace.Replace);
}
return "//" + css;
}
}
struct RegexReplace
{
public Regex Regex;
public string Replace;
}
}
И немного тестов
[TestMethod]
public void TestCssToXpathRules()
{
var translations = new Dictionary
{
{ "*", "//*" },
{ "p", "//p" },
{ "p > *", "//p/*" },
{ "#foo", "//*[@id='foo']" },
{ "*[title]", "//*[@title]" },
{ ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" },
{ "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
};
foreach (var translation in translations)
{
var expected = translation.Value;
var result = CssInliner.CssToXpath.Transform(translation.Key);
Assert.AreEqual(expected, result);
}
}
[TestMethod]
public void HtmlWithMultiLineClassStyleReturnsInline()
{
#region var html = ...
var html = XElement.Parse(@"
Hello, World Page!
.redClass {
background: red;
color: purple;
}
Hello, World!
").ToString();
#endregion
#region const string expected ...
var expected = XElement.Parse(@"
Hello, World Page!
Hello, World!
").ToString();
#endregion
var result = new CssInliner.CssInliner(html);
Assert.AreEqual(expected, result.InlinedXhtml);
}
Есть и другие тесты, но они импортируют html-файлы для ввода и ожидаемого вывода, а я все это не публикую!
Но мне нужно опубликовать методы расширения Normalize!
private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
return NormalizeSpaceRegex.Replace(data, @" ");
}
public static string NormalizeCharacter(this string data, char character)
{
var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
return normalizeCharacterRegex.Replace(data, character.ToString());
}
Подробнее здесь: https://stackoverflow.com/questions/367 ... in-c-sharp
Встраивание CSS в C# ⇐ C#
Место общения программистов C#
1 сообщение
• Страница 1 из 1
Anonymous
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...