1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use image::ImageEncoder;
use image::PixelWithColorType;
use once_cell::sync::Lazy;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use regex::Regex;
use std::borrow::Cow;
static WHITESPACES_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s+").unwrap());
trait SvgDataUriUtils: AsRef<str> {
fn trim_byte_order_mark(&self) -> &str {
let string = self.as_ref();
match string.chars().next() {
Some('\u{FEFF}') => &string[1..],
_ => string,
}
}
fn collapse_whitespace(&self) -> Cow<str> {
WHITESPACES_REGEX.replace_all(self.as_ref(), " ")
}
fn encode_uri_components(&self) -> Cow<str> {
let string = self.as_ref();
utf8_percent_encode(string, NON_ALPHANUMERIC).collect()
}
}
impl<T: AsRef<str>> SvgDataUriUtils for T {}
pub fn svg_str_to_data_uri(svg: impl AsRef<str>) -> String {
format!(
"data:image/svg+xml,{}",
svg.trim_byte_order_mark()
.trim()
.collapse_whitespace()
.encode_uri_components()
)
}
pub fn image_to_png_data_uri<T>(image: &T) -> image::ImageResult<String>
where
T: image::GenericImageView + image::EncodableLayout,
<T as image::GenericImageView>::Pixel: image::PixelWithColorType,
{
let mut buffer = Vec::new();
let encoder = image::codecs::png::PngEncoder::new(&mut buffer);
encoder.write_image(
image.as_bytes(),
image.width(),
image.height(),
<T as image::GenericImageView>::Pixel::COLOR_TYPE,
)?;
Ok([
"data:",
mime::IMAGE_PNG.as_ref(),
";base64,",
base64::encode(&buffer).as_ref(),
]
.iter()
.cloned()
.collect())
}
pub fn image_to_jpeg_data_uri<T>(image: &T, quality: u8) -> image::ImageResult<String>
where
T: image::GenericImageView,
<T as image::GenericImageView>::Pixel: image::PixelWithColorType,
{
let mut buffer = Vec::new();
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buffer, quality);
encoder.encode_image(image)?;
Ok([
"data:",
mime::IMAGE_JPEG.as_ref(),
";base64,",
base64::encode(&buffer).as_ref(),
]
.iter()
.cloned()
.collect())
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn full_test() {
let svg = r##"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M22 38V51L32 32l19-19v12C44 26 43 10 38 0 52 15 49 39 22 38z"/>
</svg>"##;
let expected = r#"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww%2Ew3%2Eorg%2F2000%2Fsvg%22%20viewBox%3D%220%200%2050%2050%22%3E%20%3Cpath%20d%3D%22M22%2038V51L32%2032l19%2D19v12C44%2026%2043%2010%2038%200%2052%2015%2049%2039%2022%2038z%22%2F%3E%20%3C%2Fsvg%3E"#;
let result = svg_str_to_data_uri(svg);
assert_eq!(result, expected);
}
}