pelago / emogrifier
将CSS样式转换为HTML代码中的内联样式属性
Requires
- php: ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0
- ext-dom: *
- ext-libxml: *
- sabberworm/php-css-parser: ^8.4.0
- symfony/css-selector: ^4.4.23 || ^5.4.0 || ^6.0.0 || ^7.0.0
Requires (Dev)
- php-parallel-lint/php-parallel-lint: 1.3.2
- phpunit/phpunit: 9.6.11
- rawr/cross-data-providers: 2.4.0
- dev-main / 8.0.x-dev
- v7.2.0
- v7.1.0
- v7.0.0
- v6.0.0
- v5.0.1
- v5.0.0
- v4.0.0
- 3.1.x-dev
- v3.1.0
- v3.0.0
- v2.2.0
- v2.1.1
- v2.1.0
- v2.0.0
- v1.2.x-dev
- v1.2.2
- v1.2.1
- V1.2.0
- V1.1.0
- V1.0.0
- v0.1.1
- v0.1
- v0.0.1
- dev-bugfix/css-variable-case
- dev-test/css-parser-8
- dev-test/css-parser-latest
- dev-feature/php-84
- dev-feature/rector
- dev-feature/infection
- dev-test/transparent-content-model
- dev-dependency-injection
- dev-tests/encoded-attributes
This package is auto-updated.
Last update: 2024-09-22 18:20:12 UTC
README
n. e•mog•ri•fi•er [\ē-'mä-grƏ-,fī-Ər] - 一种用于完全改变HTML电子邮件性质或外观的实用工具,尤其是在特别奇妙或奇异的方式
Emogrifier将CSS样式转换为HTML代码中的内联样式属性。这确保了在不支持样式表的电子邮件和移动设备阅读器上正确显示。
此实用工具是作为Intervals的一部分开发的,用于解决某些电子邮件客户端(尤其是Outlook 2007和GoogleMail)处理HTML电子邮件中包含的样式时引起的问题。正如许多网页开发人员和设计师所知,某些电子邮件客户端因其缺乏CSS支持而臭名昭著。尽管正在努力制定电子邮件标准,但实施仍有一段距离。
不合作的电子邮件客户端的主要问题是,大多数只关注内联CSS,丢弃所有<style>
元素和<link>
元素中的样式表链接。Emogrifier通过将CSS样式转换为HTML代码中的内联样式属性来解决此问题。
工作原理
Emogrifier通过解析CSS并在HTML标签中根据CSS选择器插入CSS定义,自动将HTML转换。
安装
要安装emogrifier,请将pelago/emogrifier
添加到项目composer.json
中的require
部分,或者您可以使用以下方式使用composer
composer require pelago/emogrifier
有关更多信息和方法,请参阅https://getcomposer.org.cn/。
使用方法
内联CSS
使用CssInliner
类的最基本方法是创建一个实例,将原始HTML内联外部CSS,然后获取结果HTML
use Pelago\Emogrifier\CssInliner; … $visualHtml = CssInliner::fromHtml($html)->inlineCss($css)->render();
如果没有外部CSS文件,所有CSS都位于HTML中的<style>
元素内,则可以省略$css
参数
$visualHtml = CssInliner::fromHtml($html)->inlineCss()->render();
如果您想只获取<body>
元素的内容而不是整个HTML文档,请使用renderBodyContent
方法
$bodyContent = $visualHtml = CssInliner::fromHtml($html)->inlineCss() ->renderBodyContent();
如果您想使用任何可用的选项修改内联过程,您需要在内联CSS之前调用相应的方法。代码如下所示
$visualHtml = CssInliner::fromHtml($html)->disableStyleBlocksParsing() ->inlineCss($css)->render();
还有一些其他HTML处理类可供使用(所有这些都是AbstractHtmlProcessor
的子类),您可以使用这些类在CSS内联后进一步更改HTML。有关类别的更多详细信息,请参阅下面的部分。)CssInliner
和所有HTML处理类可以共享相同的DOMDocument
实例来工作。
use Pelago\Emogrifier\CssInliner; use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter; use Pelago\Emogrifier\HtmlProcessor\HtmlPruner; … $cssInliner = CssInliner::fromHtml($html)->inlineCss($css); $domDocument = $cssInliner->getDomDocument(); HtmlPruner::fromDomDocument($domDocument)->removeElementsWithDisplayNone() ->removeRedundantClassesAfterCssInlined($cssInliner); $finalHtml = CssToAttributeConverter::fromDomDocument($domDocument) ->convertCssToVisualAttributes()->render();
标准化和清理HTML
HtmlNormalizer
类按以下方式标准化给定的HTML:
- 如果缺失,添加文档类型(HTML5)
- 解开错误嵌套的标签
- 添加HEAD和BODY元素(如果缺失)
类可以这样使用:
use Pelago\Emogrifier\HtmlProcessor\HtmlNormalizer; … $cleanHtml = HtmlNormalizer::fromHtml($rawHtml)->render();
将CSS样式转换为视觉HTML属性
CssToAttributeConverter
将一些样式属性值转换为视觉HTML属性。这使得在不支持CSS的电子邮件客户端中至少能够获得一些视觉样式。例如,style="width: 100px"
将转换为width="100"
。
类可以这样使用:
use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter; … $visualHtml = CssToAttributeConverter::fromHtml($rawHtml) ->convertCssToVisualAttributes()->render();
您还可以让CssToAttributeConverter
在DOMDocument
上工作
$visualHtml = CssToAttributeConverter::fromDomDocument($domDocument) ->convertCssToVisualAttributes()->render();
从HTML中移除冗余内容和属性
HtmlPruner
类可以通过移除具有display: none
样式声明的元素来减小HTML的大小,以及从class
属性中移除不必要的类。
用法如下
use Pelago\Emogrifier\HtmlProcessor\HtmlPruner; … $prunedHtml = HtmlPruner::fromHtml($html)->removeElementsWithDisplayNone() ->removeRedundantClasses($classesToKeep)->render();
removeRedundantClasses
方法接受应保留的类名的白名单。如果在内联CSS后的后处理步骤中,您可以改用removeRedundantClassesAfterCssInlined
,传递内联CSS的CssInliner
实例(让HtmlPruner
在DOMDocument
上工作)。这将使用CssInliner
的信息来确定哪些类仍然是必需的(即那些用于已复制到<style>
元素的不可内联规则)
$prunedHtml = HtmlPruner::fromDomDocument($cssInliner->getDomDocument()) ->removeElementsWithDisplayNone() ->removeRedundantClassesAfterCssInlined($cssInliner)->render();
removeElementsWithDisplayNone
方法将不会移除具有类-emogrifier-keep
的任何元素。因此,例如,如果某些元素默认具有display: none
但通过@media
规则被显示出来,或者作为预头信息(preheader),您可以添加这个类到这些元素上。即使这个HTML片段中的段落有display: none
(可能是由CssInliner::inlineCss()
从CSS规则.preheader { display: none; }
应用),它也不会被移除
<p class="preheader -emogrifier-keep" style="display: none;"> Hello World! </p>
如果在removeElementsWithDisplayNone
之后调用,removeRedundantClassesAfterCssInlined
(或removeRedundantClasses
)方法将移除-emogrifier-keep
类。
选项
在调用inlineCss
方法之前,您可以在CssInliner
实例上设置几个选项
->disableStyleBlocksParsing()
- 默认情况下,CssInliner
将抓取HTML中的所有<style>
块并将CSS样式作为内联的“style”属性应用到HTML上。然后从HTML中移除这些<style>
块。如果您想禁用此功能,以便CssInliner
在HTML中保留这些<style>
块并解析它们,则应使用此选项。如果使用此选项,则<style>
块的内容将不会作为内联样式应用,并且您想让CssInliner
使用的任何CSS都必须按上面用法部分中所述传递。->disableInlineStyleAttributesParsing()
- 默认情况下,CssInliner
会保留传递给它的HTML标签上所有的“style”属性。但是,如果您想在应用CSS之前丢弃HTML中现有的所有内联样式,则应使用此选项。->addAllowedMediaType(string $mediaName)
- 默认情况下,CssInliner
将仅保留媒体类型all
、screen
和print
。如果您想保留其他媒体类型,可以使用此方法定义它们。->removeAllowedMediaType(string $mediaName)
- 您可以使用此方法移除Emogrifier保留的媒体类型。->addExcludedSelector(string $selector)
- 保持元素不受CSS内联的影响。请注意,只有匹配提供的选择器的元素才会从CSS内联中排除,而不一定是它们的后代。如果您希望排除整个子树,应提供将匹配子树中所有元素的选择器,例如使用通用选择器$cssInliner->addExcludedSelector('.message-preview'); $cssInliner->addExcludedSelector('.message-preview *');
->addExcludedCssSelector(string $selector)
- 与addExcludedSelector
相反,后者用于排除HTML节点,此方法用于排除内联的CSS选择器。例如,如果您不希望CSS重置规则在每个HTML节点上内联(例如,* { margin: 0; padding: 0; font-size: 100% }
),这非常有用。请注意,这些选择器必须与您希望排除的选择器完全匹配。这意味着排除.example
不会排除p .example
。$cssInliner->addExcludedCssSelector('*'); $cssInliner->addExcludedCssSelector('form');
->removeExcludedCssSelector(string $selector)
- 删除之前添加的排除选择器(如果有)。$cssInliner->removeExcludedCssSelector('form');
从已删除的 Emogrifier
类迁移到 CssInliner
类
最小示例
使用 Emogrifier
的旧代码
$emogrifier = new Emogrifier($html); $html = $emogrifier->emogrify();
使用 CssInliner
的新代码
$html = CssInliner::fromHtml($html)->inlineCss()->render();
注意:在此示例中,旧代码会删除具有 display: none;
的元素,而新代码不会删除,因为旧类和新类在此方面的默认行为不同。
更复杂的示例
使用 Emogrifier
的旧代码
$emogrifier = new Emogrifier($html, $css); $emogrifier->enableCssToHtmlMapping(); $html = $emogrifier->emogrify();
使用 CssInliner
和家族的新代码
$domDocument = CssInliner::fromHtml($html)->inlineCss($css)->getDomDocument(); HtmlPruner::fromDomDocument($domDocument)->removeElementsWithDisplayNone(); $html = CssToAttributeConverter::fromDomDocument($domDocument) ->convertCssToVisualAttributes()->render();
支持的CSS选择器
Emogrifier 当前支持以下 CSS选择器
- 类型
- 类
- ID
- 通用
- 属性:
- 存在
- 精确值匹配
- 带有
~
的值(空格分隔的单词列表中的一个单词) - 带有
|
的值(精确值匹配或前缀后跟连字符) - 带有
^
的值(前缀匹配) - 带有
$
的值(后缀匹配) - 带有
*
的值(子串匹配)
- 相邻
- 一般兄弟
- 子
- 后代
- 伪类:
- 空
- 第一个子
- :first-of-type(具有类型,例如
p:first-of-type
但不是*:first-of-type
) - 最后一个子
- :last-of-type(具有类型)
- :not()
- :nth-child()
- :nth-last-child()
- :nth-last-of-type()(具有类型)
- :nth-of-type()(具有类型)
- 唯一子
- :only-of-type(具有类型)
- 根
以下选择器尚未实现
- 不区分大小写的属性值
- 以下未列出的受支持的静态 伪类 - 涉及它们的规则将仍然保留并复制到HTML中的
<style>
元素中 - 包括但不限于以下内容
涉及以下选择器的规则无法作为内联样式应用。然而,它们将被保留并复制到HTML中的 <style>
元素中
注意事项
- Emogrifier 需要 HTML 和 CSS 使用 UTF-8 编码。ISO8859-1 或 ISO8859-15 等编码不受支持。
- Emogrifier 保留所有有价值的
@media
规则。媒体查询在响应式邮件设计中非常有用。请参阅 媒体查询支持。然而,为了使其有效,您可能需要在其中的一些声明中添加!important
,以便它们可以覆盖内联的 CSS 样式。例如,使用以下 CSS,@media
规则中的font-size
声明在 HTML 中内联为<p style="font-size: 16px;">
后,将不会覆盖p
元素的字体大小,除非有!important
指令(即使如果没有内联 CSS,也不需要!important
)p { font-size: 16px; } @media (max-width: 640px) { p { font-size: 14px !important; } }
- Emogrifier 无法内联涉及伪元素(如
::after
)或动态伪类(如:hover
)的选择器的 CSS 规则 - 这是不可能的。然而,这些规则将被保留并复制到<style>
元素中,就像@media
规则一样。关于可能需要!important
指令的相同警告也适用于伪类。 - Emogrifier 将捕获现有的内联样式属性 以及 从您的 HTML 中捕获
<style>
块,但它不会捕获在<link>
元素或@import
规则中引用的 CSS 文件(尽管它将保留它们以供支持它们的邮件客户端使用)。 - 即使在样式内联的情况下,某些 CSS 属性也会被某些邮件客户端忽略。有关更多信息,请参阅以下资源
- 适用于元素的所有 CSS 属性都将应用,即使它们是冗余的。例如,如果您定义了字体属性 以及 字体大小属性,这两个属性都将应用于该元素(换句话说,更具体的属性不会合并到更通用的属性中)。
- 如果您发现您的 HTML 不好或无效(DOMDocument 可能会报错),那么您可能会遇到问题。如果您遇到此类问题,请在将 HTML 传递给 Emogrifier 之前,考虑运行您的 HTML 通过 Tidy。
- Emogrifier 自动将提供的(X)HTML 转换为 HTML5,即,自闭合标签将丢失其斜杠。为了保持您的 HTML 有效性,建议使用 HTML5 而不是 XHTML 的一种变体。
API 和弃用策略
请参阅我们的 API 和弃用策略。
贡献
以错误报告、功能请求或拉取请求的形式的贡献是非常受欢迎的。🙏 请参阅我们的 贡献指南 以了解更多有关如何为 Emogrifier 贡献的信息。
发布新版本步骤
- 在 composer.json 中,更新
branch-alias
条目以指向即将发布的版本的发布 之后 的版本。 - 在 CHANGELOG.md 中,创建一个新节,用于更改 之后 即将发布的子标题,设置即将发布的版本号,并删除任何空节。
- 更新 Dependabot 配置中的目标里程碑。
- 创建一个“准备版本 x.y.z 的发布”的拉取请求,包含这些更改。
- 对拉取请求进行审查和合并。
- 标记新版本。
- 在 发布选项卡 中创建一个新版本,并将更改日志条目复制到新版本中。
- 在社交媒体上发布有关新版本的消息。