1use crate::node::{Node, NodeData};
2use color::{AlphaColor, Srgb};
3use std::borrow::Cow;
4use style::color::AbsoluteColor;
5
6pub type Color = AlphaColor<Srgb>;
7
8pub fn decode_font_bytes(bytes: &[u8]) -> Cow<'_, [u8]> {
12 if bytes.len() < 4 {
13 return Cow::Borrowed(bytes);
14 }
15 match &bytes[0..4] {
16 #[cfg(feature = "woff")]
17 b"wOFF" => wuff::decompress_woff1(bytes)
18 .map(Cow::Owned)
19 .unwrap_or_else(|_| {
20 #[cfg(feature = "tracing")]
21 tracing::warn!("Failed to decompress woff1 font");
22 Cow::Borrowed(bytes)
23 }),
24 #[cfg(feature = "woff")]
25 b"wOF2" => wuff::decompress_woff2(bytes)
26 .map(Cow::Owned)
27 .unwrap_or_else(|_| {
28 #[cfg(feature = "tracing")]
29 tracing::warn!("Failed to decompress woff2 font");
30 Cow::Borrowed(bytes)
31 }),
32 _ => Cow::Borrowed(bytes),
33 }
34}
35
36#[cfg(feature = "svg")]
37use std::sync::{Arc, LazyLock};
38#[cfg(feature = "svg")]
39use usvg::fontdb;
40#[cfg(feature = "svg")]
41pub(crate) static FONT_DB: LazyLock<Arc<fontdb::Database>> = LazyLock::new(|| {
42 let mut db = fontdb::Database::new();
43 db.load_system_fonts();
44 Arc::new(db)
45});
46
47#[derive(Clone, Copy, Debug)]
48pub enum ImageType {
49 Image,
50 Background(usize),
51}
52
53#[derive(Clone, Debug, Copy, Eq, PartialEq)]
55pub struct Point<T> {
56 pub x: T,
58 pub y: T,
60}
61
62impl Point<f64> {
63 pub const ZERO: Self = Point { x: 0.0, y: 0.0 };
64}
65
66pub fn walk_tree(indent: usize, node: &Node) {
68 if let NodeData::Text(data) = &node.data {
70 if data.content.chars().all(|c| c.is_ascii_whitespace()) {
71 return;
72 }
73 }
74
75 print!("{}", " ".repeat(indent));
76 let id = node.id;
77 match &node.data {
78 NodeData::Document => println!("#Document {id}"),
79
80 NodeData::Text(data) => {
81 if data.content.chars().all(|c| c.is_ascii_whitespace()) {
82 println!("{id} #text: <whitespace>");
83 } else {
84 let content = data.content.trim();
85 if content.len() > 10 {
86 println!(
87 "#text {id}: {}...",
88 content
89 .split_at(content.char_indices().take(10).last().unwrap().0)
90 .0
91 .escape_default()
92 )
93 } else {
94 println!("#text {id}: {}", data.content.trim().escape_default())
95 }
96 }
97 }
98
99 NodeData::Comment => println!("<!-- COMMENT {id} -->"),
100
101 NodeData::AnonymousBlock(_) => println!("{id} AnonymousBlock"),
102
103 NodeData::Element(data) => {
104 print!("<{} {id}", data.name.local);
105 for attr in data.attrs.iter() {
106 print!(" {}=\"{}\"", attr.name.local, attr.value);
107 }
108 if !node.children.is_empty() {
109 println!(">");
110 } else {
111 println!("/>");
112 }
113 } }
120
121 if !node.children.is_empty() {
122 for child_id in node.children.iter() {
123 walk_tree(indent + 2, node.with(*child_id));
124 }
125
126 if let NodeData::Element(data) = &node.data {
127 println!("{}</{}>", " ".repeat(indent), data.name.local);
128 }
129 }
130}
131
132#[cfg(feature = "svg")]
133pub(crate) fn parse_svg(source: &[u8]) -> Result<usvg::Tree, usvg::Error> {
134 let options = usvg::Options {
135 fontdb: Arc::clone(&*FONT_DB),
136 ..Default::default()
137 };
138
139 let tree = usvg::Tree::from_data(source, &options)?;
140 Ok(tree)
141}
142
143pub trait ToColorColor {
144 fn as_color_color(&self) -> Color;
146}
147impl ToColorColor for AbsoluteColor {
148 fn as_color_color(&self) -> Color {
149 Color::new(
150 *self
151 .to_color_space(style::color::ColorSpace::Srgb)
152 .raw_components(),
153 )
154 }
155}
156
157#[macro_export]
160macro_rules! qual_name {
161 ($local:tt $(, $ns:ident)?) => {
162 $crate::QualName {
163 prefix: None,
164 ns: $crate::ns!($($ns)?),
165 local: $crate::local_name!($local),
166 }
167 };
168}