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.62.1 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("...")
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.10.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f568df198a0e8ca523b1041fb3cffd2a8d7920de822ee79cc60420a8426cb78d |
|
MD5 | 7fd93e8227e52b53e0d4cbfe7284a415 |
|
BLAKE2b-256 | bc761ef0f87c538f76a2af97b7f0c10b00a88a72d5af2fcd16bdea8babbb4e00 |
Hashes for css_inline-0.10.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 44b22b8d297551422661b5381c42f1562d41273afb262d572052abe2c7a08445 |
|
MD5 | 425948b4489dd4cd5e63cd086ff95297 |
|
BLAKE2b-256 | 0d75d9c05a8a1b2785cb21670c19279eef8be595efb1a62abc3c655a597554b1 |
Hashes for css_inline-0.10.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | bde58c0d55e56a31debce44761776484dbc333dfaf7967cabc07121fe45596a8 |
|
MD5 | 6791a07384ff4e232fd6ae372c44dc55 |
|
BLAKE2b-256 | 04e0b8b03736d05f6f2d4095570aca4de05f5ebc52a108c83b2e1377ecae7b0e |
Hashes for css_inline-0.10.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | b5a5d63a40d497bcf825dd23d4501187c5e67e7b1849f577b7fc913830e38b3c |
|
MD5 | ee583070603f549a3caaca4b8d499289 |
|
BLAKE2b-256 | 502e130db6edb124b93cb8b581074b5d13c7b7472871ff0d2c73e039e4b5b50d |
Hashes for css_inline-0.10.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | b93b1b51a231575e45f6e5680bfdb5a831dbb5acc28412b7aa0d9ee1b41c5ff5 |
|
MD5 | 677da7fcd5252239865de00ffd9fc759 |
|
BLAKE2b-256 | d50912773a0c9ba7211be63220ceb6939f360f4b793fec28ee72c36f3c2669f7 |
Hashes for css_inline-0.10.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | fc5370b5aa1edbe8dade284561966d48cc84b6f3b23045e143fc90993b304513 |
|
MD5 | 64ebe224f65c1772fef823d809930f19 |
|
BLAKE2b-256 | fddf58f65b4308b8a1984c57bf480b0b89b6a7703f7d907fdda0328accc2ae85 |
Hashes for css_inline-0.10.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3da6cfd5cbaebe1cf94478f943e1cb7f988f260197a0ed1b6e8f117d5806e17a |
|
MD5 | 4ecc36cbcaae68260b910e5b32225f15 |
|
BLAKE2b-256 | 77f2db9f1f639a26a16bc951de3f01c64085afcc5788ee25a69a3a31ba2ad2e1 |
Hashes for css_inline-0.10.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6255930a9d876d5f457489892affa8f68edf570a90935240d02389d11622bd78 |
|
MD5 | 9f1aa2fd00c619f9662ad8d9af83e1ec |
|
BLAKE2b-256 | 2595eacc0ba799c55d21734738e9142b4d121997917cfb06c4db63f4949681a3 |
Hashes for css_inline-0.10.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3f43b320ff90a18aa43196065fd09909a491cff0c1506afd2b2df9509f00410b |
|
MD5 | bf7a9810ce5b6592b5c40b1fbdd39bb4 |
|
BLAKE2b-256 | 52e422e1c277ffcb769f7631165c87504ebb73d486149498a3c7fa0bd0ac4eff |
Hashes for css_inline-0.10.5-cp37-abi3-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 90f1e1039fa57b5e55d6173739f2596f772ffb2495448927375d622bdca24bac |
|
MD5 | e8475027c6556ef6de16af24d8e88111 |
|
BLAKE2b-256 | 6328b0525c937b82e41d3e531d1d5eb08bce7c4983508caee8083d60bdf8f5b8 |
Hashes for css_inline-0.10.5-cp37-abi3-win32.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 92ba60ac6592bd0b03b667e1b96e46824d46828a28699737d432034160c02b65 |
|
MD5 | cc1553134b0267a1acf215347a0b7b4c |
|
BLAKE2b-256 | 53238f7d227732b2cf3d561cd79e9f3d427d1cdc39d693b46b36d1d91713b55e |
Hashes for css_inline-0.10.5-cp37-abi3-musllinux_1_2_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e92f6b2cd863c275f9bd968d876fe92cb3296371403c3970f3c4e8d710eed36a |
|
MD5 | 9f3061d885eb1e60b09cf68e8aecb012 |
|
BLAKE2b-256 | 1990aee7c368aac51e4b02b87b319c74a2fcfa75397709245916dc042f1338fb |
Hashes for css_inline-0.10.5-cp37-abi3-musllinux_1_2_armv7l.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | cb272be588096e7ea4476e6cd196fd8a7e4aac375a8b48b70294d66b008760bd |
|
MD5 | e7a8b8202fa19c6dd8a47f61d2e30ddc |
|
BLAKE2b-256 | 74e9f5004a90d0d3a8c12cfd59b67fdde273a84ec0e45f4820ce9ad5ed1f8533 |
Hashes for css_inline-0.10.5-cp37-abi3-musllinux_1_2_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7dcd7d71c28314c95f24cb60f27b47903431d880285ff48811a8c8dee5ab4568 |
|
MD5 | 77533c07e856abd92577d3c5ccb96e37 |
|
BLAKE2b-256 | 2428078f46de046dda46849a5ac0b61cf1abeb5bda78d9dcd38dc85858ab75ca |
Hashes for css_inline-0.10.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a10456873483ea4cc47b07c5486a327d64affa074a1010ec8801d1bd5bcc7a8a |
|
MD5 | 857827089e2728c072a0df60e9567564 |
|
BLAKE2b-256 | 9f1fb12ed8fe2572d0d3eeba269f6c0fd76867b9f9481aee767e54d460e3d67f |
Hashes for css_inline-0.10.5-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 568e9816509a98000bda8cbf5f8c82c683c7a6ae096026ceb614339709b4ca68 |
|
MD5 | dc8e0e74f86a7d675ab3c6b132544920 |
|
BLAKE2b-256 | 2a5640fe7e4faa50d7393c193ffc26b9b6ea409540dcd25b19e06517cb8b9929 |
Hashes for css_inline-0.10.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e8b3caf3360df477bdf662e242549cd214c5201b6515b79e0e5ae007747eb87f |
|
MD5 | 73ec97ab17c75d5a0be7643fcf4186c7 |
|
BLAKE2b-256 | 0da067a68d450312140f51474fcb71eb42fc5673eb6e0f0a1b5e71a1380836c5 |
Hashes for css_inline-0.10.5-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | fd991c3f2f5ab9bb27bc1a7312305e1a19e7e6add173f1c1b40f8e686c6738a1 |
|
MD5 | 05faeafaef22b1497d01c0befb75b415 |
|
BLAKE2b-256 | 72770d0012aa6c4049d0190cd21c4dd74d0ba5bf4bc4f3d12c3d33c1f8e6f444 |
Hashes for css_inline-0.10.5-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3b83aab29e524947760f9696ebc57b1dc1cdfc1138d98dfb2ffeea437dc0d060 |
|
MD5 | c2095c06e67ee069e1fff230eb097632 |
|
BLAKE2b-256 | 6c05b620e95dfa45f34e77373de18bf4f4762c442abf184d4db477765f8f95ad |
Hashes for css_inline-0.10.5-cp37-abi3-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e1e95ec41dff79f7bc1db0589bd1c43bcf8dddbcf05b41231311c718b34fcf8f |
|
MD5 | 62f3e1a34391933490426edeb678753e |
|
BLAKE2b-256 | ee8dceaca4cd3f51390ba863c1da4f43abebabf780c79904d50c7509f1e82833 |