High-performance library for inlining CSS into HTML 'style' attributes
Project description
css_inline
css_inline
is a high-performance library for inlining CSS into HTML 'style' attributes.
This library is designed for scenarios such as preparing HTML emails or embedding HTML into third-party web pages.
For instance, the library transforms HTML like this:
<html>
<head>
<style>h1 { color:blue; }</style>
</head>
<body>
<h1>Big Text</h1>
</body>
</html>
into:
<html>
<head></head>
<body>
<h1 style="color:blue;">Big Text</h1>
</body>
</html>
- Uses reliable components from Mozilla's Servo project
- 10-400x faster than alternatives
- Inlines CSS from
style
andlink
tags - Removes
style
andlink
tags - Resolves external stylesheets (including local files)
- Can process multiple documents in parallel
- Works on Linux, Windows, and macOS
- Supports HTML5 & CSS3
Installation
Install with pip
:
pip install css_inline
Pre-compiled wheels are available for most popular platforms. If not available for your platform, a Rust compiler will be needed to build this package from source. Rust version 1.63 or higher is required.
Usage
import css_inline
HTML = """<html>
<head>
<style>h1 { color:blue; }</style>
</head>
<body>
<h1>Big Text</h1>
</body>
</html>"""
inlined = css_inline.inline(HTML)
# HTML becomes this:
#
# <html>
# <head>
# <style>h1 { color:blue; }</style>
# </head>
# <body>
# <h1 style="color:blue;">Big Text</h1>
# </body>
# </html>
When there is a need to inline multiple HTML documents simultaneously, css_inline
offers the inline_many
function.
This feature allows for concurrent processing of several inputs, significantly improving performance when dealing with a large number of documents.
import css_inline
css_inline.inline_many(["<...>", "<...>"])
Under the hood, inline_many
, spawns threads at the Rust layer to handle the parallel processing of inputs.
This results in faster execution times compared to employing parallel processing techniques at the Python level.
Note: To fully benefit from inline_many
, you should run your application on a multicore machine.
Configuration
For configuration options use the CSSInliner
class:
import css_inline
inliner = css_inline.CSSInliner(keep_style_tags=True)
inliner.inline("...")
inline_style_tags
. Specifies whether to inline CSS from "style" tags. Default:True
keep_style_tags
. Specifies whether to keep "style" tags after inlining. Default:False
keep_link_tags
. Specifies whether to keep "link" tags after inlining. Default:False
base_url
. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use thefile://
scheme. Default:None
load_remote_stylesheets
. Specifies whether remote stylesheets should be loaded. Default:True
extra_css
. Extra CSS to be inlined. Default:None
preallocate_node_capacity
. Advanced. Preallocates capacity for HTML nodes during parsing. This can improve performance when you have an estimate of the number of nodes in your HTML document. Default:32
You can also skip CSS inlining for an HTML tag by adding the data-css-inline="ignore"
attribute to it:
<head>
<style>h1 { color:blue; }</style>
</head>
<body>
<!-- The tag below won't receive additional styles -->
<h1 data-css-inline="ignore">Big Text</h1>
</body>
The data-css-inline="ignore"
attribute also allows you to skip link
and style
tags:
<head>
<!-- Styles below are ignored -->
<style data-css-inline="ignore">h1 { color:blue; }</style>
</head>
<body>
<h1>Big Text</h1>
</body>
If you'd like to load stylesheets from your filesystem, use the file://
scheme:
import css_inline
# styles/email is relative to the current directory
inliner = css_inline.CSSInliner(base_url="file://styles/email/")
inliner.inline("...")
XHTML compatibility
If you'd like to work around some XHTML compatibility issues like closing empty tags (<hr>
vs. <hr/>
), you can use the following snippet that involves lxml
:
import css_inline
from lxml import html, etree
document = "..." # Your HTML document
inlined = css_inline.inline(document)
tree = html.fromstring(inlined)
inlined = etree.tostring(tree).decode(encoding="utf-8")
Performance
css-inline
is powered by efficient tooling from Mozilla's Servo project and significantly outperforms other Python alternatives in terms of speed.
Most of the time it achieves over a 10x speed advantage compared to the next fastest alternative.
Here is the performance comparison:
Size | css_inline 0.10.4 |
premailer 3.10.0 |
toronado 0.1.0 |
inlinestyler 0.2.5 |
pynliner 0.8.0 |
|
---|---|---|---|---|---|---|
Basic | 230 B | 6.58 µs | 130.45 µs (19.82x) | 671.87 µs (102.06) | 1.05 ms (161.00) | 1.23 ms (187.51x) |
Realistic-1 | 8.58 KB | 146.20 µs | 1.42 ms (9.71x) | 16.56 ms (113.29x) | 27.45 ms (187.78x) | 51.85 ms (354.66x) |
Realistic-2 | 4.3 KB | 87.91 µs | 2.71 ms (30.90x) | ERROR | 18.07 ms (205.64x) | ERROR |
GitHub page | 1.81 MB | 262.74 ms | 25.38 s (96.63x) | ERROR | ERROR | ERROR |
The above data was obtained from benchmarking the inlining of CSS in HTML, as described in the Usage section.
Note that the toronado
, inlinestyler
and pynliner
libraries both encountered errors when used to inline CSS in the last scenario.
The benchmarking code is available in the benches/bench.py
file. The benchmarks were conducted using the stable rustc 1.71.1
on Python 3.11.4
.
Comparison with other libraries
Besides performance, css-inline
differs from other Python libraries for CSS inlining.
- Generally supports more CSS features than other libraries (for example,
toronado
andpynliner
do not support pseudo-elements); - It has fewer configuration options and not as flexible as
premailer
; - Works on fewer platforms than LXML-based libraries (
premailer
,inlinestyler
,toronado
, and optionallypynliner
); - Does not have debug logs yet;
- Supports only HTML 5.
Python support
css_inline
supports CPython 3.7, 3.8, 3.9, 3.10, 3.11 and PyPy 3.7, 3.8, 3.9.
Further reading
If you want to know how this library was created & how it works internally, you could take a look at these articles:
License
This project is licensed under the terms of the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Hashes for css_inline-0.11.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a7146ef691a7e7931d3033364c2e7001afc51311f6675b3d3da399d42ee8da1d |
|
MD5 | 7520b2d0659f74f7cf46564050e3d9c7 |
|
BLAKE2b-256 | 525a6126f1b163a24a8bec389d135265063802ec771d52554accc4fea96766d7 |
Hashes for css_inline-0.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8181437f963e3a65c0e6abe0c7303326307d5fafb410785669327e21aed36732 |
|
MD5 | 1360103e6a4dc9b3fddbe77851b4e948 |
|
BLAKE2b-256 | fe2dc6f1dff9be47e7784baa5192e4b062906e606379a84860d5393f8e6693a6 |
Hashes for css_inline-0.11.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a5741f2667aae5d2155050aa22a8c3af90241586e0c02533b758e851554c6786 |
|
MD5 | d212b8fb5fa611b79a0eeaf5bd67f786 |
|
BLAKE2b-256 | acf87a689b808e0d4afe4a27062b5427c522f8b149a424dcd621e0959280cad1 |
Hashes for css_inline-0.11.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 09ed9e3c40cf6f3f36ef641db915d6c4eb72b5c3651b38a6df0dc25785c939b0 |
|
MD5 | ea55cceca65e6671f125f02c12926a45 |
|
BLAKE2b-256 | d98ed019a1f27fc10b1455ae95636ff4e709e1a852eca075f3ec71b989f183c7 |
Hashes for css_inline-0.11.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 06e217b860cec28104afbe47f4fd2123208cb33d75071aa1ac52062eb883d61d |
|
MD5 | 9035f694e022c889a897f1c33023c0fe |
|
BLAKE2b-256 | dadf31e04a6db713e3d5d0d25b55cc4f726730dcf8b1ef11b62d5d290a5f3d09 |
Hashes for css_inline-0.11.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2ddb6a401e488851cff9f17a6d0958f56cd5a7f55f1da255bf0f97f8e81ce869 |
|
MD5 | 8f1526dc815cd5387b55365056bee3d9 |
|
BLAKE2b-256 | 9786a7223b178017ccd8051d5d305e9cb1c547bd95df206147c22123236d1bb6 |
Hashes for css_inline-0.11.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9406a160854109030301ecad8f591db6563a7272f3936bb78bb8ea9601ac8ff6 |
|
MD5 | 59e956861a38b494fa570c1f6ba21ed2 |
|
BLAKE2b-256 | 0fb242ad548b975999edef9a8a35a1d0315f7733afa0aca91559afa9b4c9c8c7 |
Hashes for css_inline-0.11.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | fe0b4810f7c735cc224cf44bf86582689ab4a78e49d86470bc05d7c5743428df |
|
MD5 | 88f5dfa8d93eaf0926e9fa9e0f676ef4 |
|
BLAKE2b-256 | dc02a4c00368a3f1acc06a8db0a50fd2ac75885c8cdebc30f1a54af867735521 |
Hashes for css_inline-0.11.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0b2e2edaaed8365449451fc3029acda4f38dee5d1ed032d3299b85ee006b4138 |
|
MD5 | 030d2a8131511d7f6beda72e36fb70c4 |
|
BLAKE2b-256 | 9109cef597d013245d52f8fcccbe4b7fd18d7434c039b80cdd6c58c8b31a2b1d |
Hashes for css_inline-0.11.0-cp37-abi3-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e666b5a834eb9f04c3e5774504513eafaf9709cebb72f1cd98b775f8fe3627ba |
|
MD5 | 3d2ad1197136cad8b31c93e435c1aec4 |
|
BLAKE2b-256 | 320d1fa2e855bbd0e816f3b74d45c2bbcb7bd5e44aabcf94bbbcb46e4e8ecd6c |
Hashes for css_inline-0.11.0-cp37-abi3-win32.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 78f8cc9c7400fd8255cbd310ef099af331273c29411c188f88560a31bcd51c6d |
|
MD5 | 8960503479698f9e1eb81be5fc780b11 |
|
BLAKE2b-256 | 01dbee5acf74129ba0fc73c8c92ecdd5fae4023702710f521e4095f956f850e2 |
Hashes for css_inline-0.11.0-cp37-abi3-musllinux_1_2_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d88db41b5a57ad8fb2b7c169aa6837c35bff80b6b21ed35a0b6e372a7032c802 |
|
MD5 | 0cc01be5d51b322b18dea2729e58705a |
|
BLAKE2b-256 | 61aca6efcf5d7c77f2cbcfda91a4ae7afd3965cf5f39a83764550b501dd7f6b9 |
Hashes for css_inline-0.11.0-cp37-abi3-musllinux_1_2_armv7l.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c4c9b0465bf1b248a6fd09379c8b6bd1850048ca7546eace7b2e6dc3b1f11858 |
|
MD5 | a9b8806ec51d646f6e724084b84e1bc0 |
|
BLAKE2b-256 | 61b1f3738ef341f2a6c8dc53e0ec6eabc83d8d6b11a05c8470fd1fa735e15b99 |
Hashes for css_inline-0.11.0-cp37-abi3-musllinux_1_2_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a60a66936eafb43b705ba80d791df5798b5490c613b91df76799c1f636cb8fd1 |
|
MD5 | e077c1a74dc6876a8f10ac189570c0ba |
|
BLAKE2b-256 | 629e6dbb04aa1f46236d67dfb5f5bc4ea4c284e0aa31c1c03ecc928dc3bd1c8a |
Hashes for css_inline-0.11.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7b00e63d9e6ba0294a1365d9a60ad9d3712c37218db991b05f7b0b5d26314526 |
|
MD5 | 14c3d5668e8cf8a5e7da2e716e06630d |
|
BLAKE2b-256 | 0d715c880a6401f6f0767287d9742630ea80069889a2dbac906d6679a674de4b |
Hashes for css_inline-0.11.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6676c5a2a6b5c50a4e124b92d85fc59d965a719638892e949c09154d778038b8 |
|
MD5 | dfaa556c0aa697ec0e1acd7ae3c42a17 |
|
BLAKE2b-256 | 4cf9fa1d2a996f59e282d9b4dca75931403992f6be1d427c2c36c947277e1f2e |
Hashes for css_inline-0.11.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 86e7f5c714683804127059bc0932b8f059187b4d26c439d7f23fabad91aedd9c |
|
MD5 | ec4139d148f9058917a817c4faf17e87 |
|
BLAKE2b-256 | adad9dd5f5d002ba2e95c9c64ae722e5311bed7cd27edb764536141def488279 |
Hashes for css_inline-0.11.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 4cf71b6b36d59c51bcf24dfeada617715dd4b56ea07ed9fd88fc5b395396296f |
|
MD5 | 092e3f381594a8c501f00d68a08c4fc2 |
|
BLAKE2b-256 | 4f5a9e2823a859bf0f32d2239df63f3a00c01e96dcd9c463d6315058fddbd7ee |
Hashes for css_inline-0.11.0-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 74dc95580678fce27dd006b34dbd3af58916bb66e247c11f2f016f4ad262d682 |
|
MD5 | 546e302b3b6eb99b9549ae27fa81d076 |
|
BLAKE2b-256 | 5f9b3896772d28209405f5e92ec7baf71d2f00783f3bc16161a00e10d92e5a8f |
Hashes for css_inline-0.11.0-cp37-abi3-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1d2dc3e9d438c30c34e2177fc1bcb12ee0fcc4850f764b2e28db299722522a67 |
|
MD5 | a45bc65b20e1e8ea936cd60f7039a944 |
|
BLAKE2b-256 | 624dbc24b2026db5adfec05caef7cfa7bad7fba3df588a8e4248e80f3c626d02 |