blitz-pdf 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/docs/layout-regressions.md +65 -0
- package/docs/pagination-regressions.md +7 -6
- package/package.json +1 -1
- package/vendor/README.md +35 -0
- package/vendor/blitz-dom/Cargo.toml +113 -0
- package/vendor/blitz-dom/LICENSE-APACHE +176 -0
- package/vendor/blitz-dom/LICENSE-MIT +23 -0
- package/vendor/blitz-dom/assets/default.css +1142 -0
- package/vendor/blitz-dom/assets/moz-bullet-font.otf +0 -0
- package/vendor/blitz-dom/src/accessibility.rs +255 -0
- package/vendor/blitz-dom/src/config.rs +97 -0
- package/vendor/blitz-dom/src/cssom.rs +971 -0
- package/vendor/blitz-dom/src/debug.rs +206 -0
- package/vendor/blitz-dom/src/document.rs +3395 -0
- package/vendor/blitz-dom/src/events/driver.rs +427 -0
- package/vendor/blitz-dom/src/events/focus.rs +40 -0
- package/vendor/blitz-dom/src/events/ime.rs +31 -0
- package/vendor/blitz-dom/src/events/keyboard.rs +174 -0
- package/vendor/blitz-dom/src/events/mod.rs +315 -0
- package/vendor/blitz-dom/src/events/pointer.rs +849 -0
- package/vendor/blitz-dom/src/font_metrics.rs +177 -0
- package/vendor/blitz-dom/src/form.rs +463 -0
- package/vendor/blitz-dom/src/html.rs +38 -0
- package/vendor/blitz-dom/src/iframe.rs +186 -0
- package/vendor/blitz-dom/src/layout/construct.rs +1328 -0
- package/vendor/blitz-dom/src/layout/damage.rs +642 -0
- package/vendor/blitz-dom/src/layout/inline.rs +1160 -0
- package/vendor/blitz-dom/src/layout/list.rs +237 -0
- package/vendor/blitz-dom/src/layout/mod.rs +969 -0
- package/vendor/blitz-dom/src/layout/paint_tree.rs +462 -0
- package/vendor/blitz-dom/src/layout/replaced.rs +439 -0
- package/vendor/blitz-dom/src/layout/table.rs +967 -0
- package/vendor/blitz-dom/src/layout/text_transform.rs +979 -0
- package/vendor/blitz-dom/src/layout/writing_mode.rs +437 -0
- package/vendor/blitz-dom/src/lib.rs +160 -0
- package/vendor/blitz-dom/src/mutator.rs +1818 -0
- package/vendor/blitz-dom/src/net.rs +649 -0
- package/vendor/blitz-dom/src/node/attributes.rs +60 -0
- package/vendor/blitz-dom/src/node/custom_widget.rs +178 -0
- package/vendor/blitz-dom/src/node/element.rs +1106 -0
- package/vendor/blitz-dom/src/node/mod.rs +30 -0
- package/vendor/blitz-dom/src/node/node.rs +2036 -0
- package/vendor/blitz-dom/src/node/scrollbar.rs +237 -0
- package/vendor/blitz-dom/src/node/serialize.rs +225 -0
- package/vendor/blitz-dom/src/node/stylo_data.rs +142 -0
- package/vendor/blitz-dom/src/node/svg.rs +265 -0
- package/vendor/blitz-dom/src/node/text.rs +804 -0
- package/vendor/blitz-dom/src/query_selector.rs +275 -0
- package/vendor/blitz-dom/src/resolve.rs +474 -0
- package/vendor/blitz-dom/src/resolved_style.rs +797 -0
- package/vendor/blitz-dom/src/scrolling.rs +780 -0
- package/vendor/blitz-dom/src/selection.rs +122 -0
- package/vendor/blitz-dom/src/stylo.rs +1518 -0
- package/vendor/blitz-dom/src/stylo_device.rs +88 -0
- package/vendor/blitz-dom/src/stylo_to_cursor_icon.rs +49 -0
- package/vendor/blitz-dom/src/stylo_to_kurbo.rs +129 -0
- package/vendor/blitz-dom/src/stylo_to_parley.rs +655 -0
- package/vendor/blitz-dom/src/traversal.rs +600 -0
- package/vendor/blitz-dom/src/tree.rs +136 -0
- package/vendor/blitz-dom/src/url.rs +63 -0
- package/vendor/blitz-dom/src/util.rs +256 -0
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
//! Versioned storage for the nodes of the DOM tree.
|
|
2
|
+
|
|
3
|
+
use std::ops::{Index, IndexMut};
|
|
4
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
5
|
+
|
|
6
|
+
use blitz_traits::node_id::NodeId;
|
|
7
|
+
use slotmap::{Key as _, KeyData, SlotMap};
|
|
8
|
+
|
|
9
|
+
use crate::Node;
|
|
10
|
+
|
|
11
|
+
slotmap::new_key_type! {
|
|
12
|
+
/// The internal [`slotmap`] key for node storage. Only used at the
|
|
13
|
+
/// storage boundary: all public APIs use [`NodeId`].
|
|
14
|
+
struct NodeKey;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[inline(always)]
|
|
18
|
+
fn to_key(id: NodeId) -> NodeKey {
|
|
19
|
+
NodeKey::from(KeyData::from_ffi(id.as_u64()))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[inline(always)]
|
|
23
|
+
fn to_id(key: NodeKey) -> NodeId {
|
|
24
|
+
NodeId::from_u64(key.data().as_ffi())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// The versioned map in which the nodes of the DOM tree are stored, backed by
|
|
28
|
+
/// a [`slotmap::SlotMap`].
|
|
29
|
+
///
|
|
30
|
+
/// Nodes are addressed by [`NodeId`], which carries the slot's version in
|
|
31
|
+
/// addition to its index: when a node is dropped and its slot reused, ids
|
|
32
|
+
/// referring to the dropped node no longer resolve ([`NodeTree::get`] returns
|
|
33
|
+
/// `None`, and indexing panics) instead of aliasing the new occupant.
|
|
34
|
+
pub struct NodeTree {
|
|
35
|
+
map: SlotMap<NodeKey, Node>,
|
|
36
|
+
/// Bumped whenever box geometry may have moved (layout ran or a scroll
|
|
37
|
+
/// offset changed). Values derived from `final_layout()` (e.g. hoisted
|
|
38
|
+
/// paint child offsets) are memoised against it. Starts at 1 so that a
|
|
39
|
+
/// stamp of 0 is never current.
|
|
40
|
+
geometry_generation: AtomicU64,
|
|
41
|
+
/// Scroll offset of the viewport (the root element scrolls the viewport,
|
|
42
|
+
/// so its offset lives here rather than on the node).
|
|
43
|
+
viewport_scroll: crate::Point<f64>,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
impl NodeTree {
|
|
47
|
+
pub(crate) fn new() -> Self {
|
|
48
|
+
Self {
|
|
49
|
+
map: SlotMap::with_key(),
|
|
50
|
+
geometry_generation: AtomicU64::new(1),
|
|
51
|
+
viewport_scroll: crate::Point::ZERO,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn viewport_scroll(&self) -> crate::Point<f64> {
|
|
56
|
+
self.viewport_scroll
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Memoised paint geometry may depend on the viewport scroll, so this
|
|
60
|
+
/// bumps the geometry generation like any other scroll write.
|
|
61
|
+
pub(crate) fn set_viewport_scroll(&mut self, scroll: crate::Point<f64>) {
|
|
62
|
+
self.viewport_scroll = scroll;
|
|
63
|
+
self.bump_geometry_generation();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// See the `geometry_generation` field.
|
|
67
|
+
pub fn geometry_generation(&self) -> u64 {
|
|
68
|
+
self.geometry_generation.load(Ordering::Relaxed)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub(crate) fn bump_geometry_generation(&self) {
|
|
72
|
+
self.geometry_generation.fetch_add(1, Ordering::Relaxed);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// The number of live nodes in the map.
|
|
76
|
+
pub fn len(&self) -> usize {
|
|
77
|
+
self.map.len()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn is_empty(&self) -> bool {
|
|
81
|
+
self.map.is_empty()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Whether `id` resolves to a live node.
|
|
85
|
+
pub fn contains_key(&self, id: NodeId) -> bool {
|
|
86
|
+
self.map.contains_key(to_key(id))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Get a reference to the node with the given id, if it is still live.
|
|
90
|
+
pub fn get(&self, id: NodeId) -> Option<&Node> {
|
|
91
|
+
self.map.get(to_key(id))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Get a mutable reference to the node with the given id, if it is still live.
|
|
95
|
+
pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
|
|
96
|
+
self.map.get_mut(to_key(id))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Insert a node constructed with knowledge of its own id.
|
|
100
|
+
pub(crate) fn insert_with_key(&mut self, f: impl FnOnce(NodeId) -> Node) -> NodeId {
|
|
101
|
+
to_id(self.map.insert_with_key(|key| f(to_id(key))))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Remove the node with the given id, returning it if it was still live.
|
|
105
|
+
pub(crate) fn remove(&mut self, id: NodeId) -> Option<Node> {
|
|
106
|
+
self.map.remove(to_key(id))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Iterate over all live `(NodeId, &Node)` pairs.
|
|
110
|
+
pub fn iter(&self) -> impl Iterator<Item = (NodeId, &Node)> {
|
|
111
|
+
self.map.iter().map(|(key, node)| (to_id(key), node))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Iterate over all live `(NodeId, &mut Node)` pairs.
|
|
115
|
+
pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut Node)> {
|
|
116
|
+
self.map.iter_mut().map(|(key, node)| (to_id(key), node))
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
impl Index<NodeId> for NodeTree {
|
|
121
|
+
type Output = Node;
|
|
122
|
+
|
|
123
|
+
#[track_caller]
|
|
124
|
+
#[inline]
|
|
125
|
+
fn index(&self, id: NodeId) -> &Node {
|
|
126
|
+
&self.map[to_key(id)]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
impl IndexMut<NodeId> for NodeTree {
|
|
131
|
+
#[track_caller]
|
|
132
|
+
#[inline]
|
|
133
|
+
fn index_mut(&mut self, id: NodeId) -> &mut Node {
|
|
134
|
+
&mut self.map[to_key(id)]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
use std::ops::Deref;
|
|
2
|
+
use std::str::FromStr;
|
|
3
|
+
|
|
4
|
+
use style::servo_arc::Arc as ServoArc;
|
|
5
|
+
use style::stylesheets::UrlExtraData;
|
|
6
|
+
use url::{Position, Url};
|
|
7
|
+
|
|
8
|
+
#[derive(Clone)]
|
|
9
|
+
pub(crate) struct DocumentUrl {
|
|
10
|
+
base_url: ServoArc<Url>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl DocumentUrl {
|
|
14
|
+
/// Create a stylo `UrlExtraData` from the URL
|
|
15
|
+
pub(crate) fn url_extra_data(&self) -> UrlExtraData {
|
|
16
|
+
UrlExtraData(ServoArc::clone(&self.base_url))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pub(crate) fn resolve_relative(&self, raw: &str) -> Option<url::Url> {
|
|
20
|
+
self.base_url.join(raw).ok()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Returns `true` if `other` refers to the same document as this URL, i.e. it is
|
|
24
|
+
/// identical to this URL except (possibly) for the fragment (`#...`) component.
|
|
25
|
+
///
|
|
26
|
+
/// This is used to decide whether following a link should perform in-page fragment
|
|
27
|
+
/// navigation (scrolling) rather than a full navigation.
|
|
28
|
+
pub(crate) fn is_same_document(&self, other: &Url) -> bool {
|
|
29
|
+
let this: &Url = &self.base_url;
|
|
30
|
+
this[..Position::AfterQuery] == other[..Position::AfterQuery]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl Default for DocumentUrl {
|
|
35
|
+
fn default() -> Self {
|
|
36
|
+
Self::from_str("data:text/css;charset=utf-8;base64,").unwrap()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
impl FromStr for DocumentUrl {
|
|
40
|
+
type Err = <Url as FromStr>::Err;
|
|
41
|
+
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
42
|
+
let base_url = ServoArc::new(Url::parse(s)?);
|
|
43
|
+
Ok(Self { base_url })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
impl From<Url> for DocumentUrl {
|
|
47
|
+
fn from(base_url: Url) -> Self {
|
|
48
|
+
Self {
|
|
49
|
+
base_url: ServoArc::new(base_url),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
impl From<ServoArc<Url>> for DocumentUrl {
|
|
54
|
+
fn from(base_url: ServoArc<Url>) -> Self {
|
|
55
|
+
Self { base_url }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
impl Deref for DocumentUrl {
|
|
59
|
+
type Target = Url;
|
|
60
|
+
fn deref(&self) -> &Self::Target {
|
|
61
|
+
&self.base_url
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
use crate::node::{Node, NodeData};
|
|
2
|
+
use color::{AlphaColor, Srgb};
|
|
3
|
+
use keyboard_types::Modifiers;
|
|
4
|
+
use std::borrow::Cow;
|
|
5
|
+
use style::color::AbsoluteColor;
|
|
6
|
+
|
|
7
|
+
#[cfg(target_os = "macos")]
|
|
8
|
+
pub(crate) const ACTION_MOD: Modifiers = Modifiers::SUPER;
|
|
9
|
+
#[cfg(not(target_os = "macos"))]
|
|
10
|
+
pub(crate) const ACTION_MOD: Modifiers = Modifiers::CONTROL;
|
|
11
|
+
|
|
12
|
+
pub type Color = AlphaColor<Srgb>;
|
|
13
|
+
|
|
14
|
+
/// Decode raw font bytes, decompressing WOFF/WOFF2 if the `woff` feature is enabled.
|
|
15
|
+
/// Returns the original slice unchanged for TTF/OTF input, and also on decompression
|
|
16
|
+
/// failure. With the `woff` feature disabled, all input passes through unchanged.
|
|
17
|
+
pub fn decode_font_bytes(bytes: &[u8]) -> Cow<'_, [u8]> {
|
|
18
|
+
if bytes.len() < 4 {
|
|
19
|
+
return Cow::Borrowed(bytes);
|
|
20
|
+
}
|
|
21
|
+
match &bytes[0..4] {
|
|
22
|
+
#[cfg(feature = "woff")]
|
|
23
|
+
b"wOFF" => wuff::decompress_woff1(bytes)
|
|
24
|
+
.map(Cow::Owned)
|
|
25
|
+
.unwrap_or_else(|_| {
|
|
26
|
+
#[cfg(feature = "tracing")]
|
|
27
|
+
tracing::warn!("Failed to decompress woff1 font");
|
|
28
|
+
Cow::Borrowed(bytes)
|
|
29
|
+
}),
|
|
30
|
+
#[cfg(feature = "woff")]
|
|
31
|
+
b"wOF2" => wuff::decompress_woff2(bytes)
|
|
32
|
+
.map(Cow::Owned)
|
|
33
|
+
.unwrap_or_else(|_| {
|
|
34
|
+
#[cfg(feature = "tracing")]
|
|
35
|
+
tracing::warn!("Failed to decompress woff2 font");
|
|
36
|
+
Cow::Borrowed(bytes)
|
|
37
|
+
}),
|
|
38
|
+
_ => Cow::Borrowed(bytes),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[cfg(feature = "svg")]
|
|
43
|
+
use std::sync::{Arc, LazyLock};
|
|
44
|
+
#[cfg(feature = "svg")]
|
|
45
|
+
use usvg::fontdb;
|
|
46
|
+
#[cfg(feature = "svg")]
|
|
47
|
+
pub(crate) static FONT_DB: LazyLock<Arc<fontdb::Database>> = LazyLock::new(|| {
|
|
48
|
+
let mut db = fontdb::Database::new();
|
|
49
|
+
db.load_system_fonts();
|
|
50
|
+
Arc::new(db)
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/// Which kind of CSS image layer list (`background-image` or `mask-image`) to
|
|
54
|
+
/// flush from style to dedicated storage on the node.
|
|
55
|
+
#[derive(Clone, Copy, Debug)]
|
|
56
|
+
pub enum ImageLayerKind {
|
|
57
|
+
Background,
|
|
58
|
+
Mask,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
impl ImageLayerKind {
|
|
62
|
+
pub fn image_type(self, idx: usize) -> ImageType {
|
|
63
|
+
match self {
|
|
64
|
+
Self::Background => ImageType::Background(idx),
|
|
65
|
+
Self::Mask => ImageType::Mask(idx),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[derive(Clone, Copy, Debug)]
|
|
71
|
+
pub enum ImageType {
|
|
72
|
+
Image,
|
|
73
|
+
Background(usize),
|
|
74
|
+
Mask(usize),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// A point
|
|
78
|
+
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
|
|
79
|
+
pub struct Point<T> {
|
|
80
|
+
/// The x coordinate
|
|
81
|
+
pub x: T,
|
|
82
|
+
/// The y coordinate
|
|
83
|
+
pub y: T,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
impl Point<f64> {
|
|
87
|
+
pub const ZERO: Self = Point { x: 0.0, y: 0.0 };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Debug print an RcDom
|
|
91
|
+
pub fn walk_tree(indent: usize, node: &Node) {
|
|
92
|
+
// Skip all-whitespace text nodes entirely
|
|
93
|
+
if let NodeData::Text(data) = &node.data {
|
|
94
|
+
if data.content.chars().all(|c| c.is_ascii_whitespace()) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
print!("{}", " ".repeat(indent));
|
|
100
|
+
let id = node.id;
|
|
101
|
+
match &node.data {
|
|
102
|
+
NodeData::Document(_) => println!("#Document {id}"),
|
|
103
|
+
|
|
104
|
+
NodeData::Text(data) => {
|
|
105
|
+
if data.content.chars().all(|c| c.is_ascii_whitespace()) {
|
|
106
|
+
println!("{id} #text: <whitespace>");
|
|
107
|
+
} else {
|
|
108
|
+
let content = data.content.trim();
|
|
109
|
+
if content.len() > 10 {
|
|
110
|
+
println!(
|
|
111
|
+
"#text {id}: {}...",
|
|
112
|
+
content
|
|
113
|
+
.split_at(content.char_indices().take(10).last().unwrap().0)
|
|
114
|
+
.0
|
|
115
|
+
.escape_default()
|
|
116
|
+
)
|
|
117
|
+
} else {
|
|
118
|
+
println!("#text {id}: {}", data.content.trim().escape_default())
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
NodeData::Comment { .. } => println!("<!-- COMMENT {id} -->"),
|
|
124
|
+
|
|
125
|
+
NodeData::AnonymousBlock(_) => println!("{id} AnonymousBlock"),
|
|
126
|
+
|
|
127
|
+
NodeData::Element(data) => {
|
|
128
|
+
print!("<{} {id}", data.name.local);
|
|
129
|
+
for attr in data.attrs.iter() {
|
|
130
|
+
print!(" {}=\"{}\"", attr.name.local, attr.value);
|
|
131
|
+
}
|
|
132
|
+
if !node.children.is_empty() {
|
|
133
|
+
println!(">");
|
|
134
|
+
} else {
|
|
135
|
+
println!("/>");
|
|
136
|
+
}
|
|
137
|
+
} // NodeData::Doctype {
|
|
138
|
+
// ref name,
|
|
139
|
+
// ref public_id,
|
|
140
|
+
// ref system_id,
|
|
141
|
+
// } => println!("<!DOCTYPE {} \"{}\" \"{}\">", name, public_id, system_id),
|
|
142
|
+
// NodeData::ProcessingInstruction { .. } => unreachable!(),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if !node.children.is_empty() {
|
|
146
|
+
for child_id in node.children.iter() {
|
|
147
|
+
walk_tree(indent + 2, node.with(*child_id));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if let NodeData::Element(data) = &node.data {
|
|
151
|
+
println!("{}</{}>", " ".repeat(indent), data.name.local);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// Parse an SVG image.
|
|
157
|
+
#[cfg(feature = "svg")]
|
|
158
|
+
pub(crate) fn parse_svg_image(source: &[u8]) -> Result<crate::node::SvgImageData, usvg::Error> {
|
|
159
|
+
let options = usvg::Options {
|
|
160
|
+
fontdb: Arc::clone(&*FONT_DB),
|
|
161
|
+
..Default::default()
|
|
162
|
+
};
|
|
163
|
+
crate::node::SvgImageData::from_data(source, &options)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pub trait ToColorColor {
|
|
167
|
+
/// Converts a color into the `AlphaColor<Srgb>` type from the `color` crate
|
|
168
|
+
fn as_color_color(&self) -> Color;
|
|
169
|
+
}
|
|
170
|
+
impl ToColorColor for AbsoluteColor {
|
|
171
|
+
fn as_color_color(&self) -> Color {
|
|
172
|
+
Color::new(
|
|
173
|
+
*self
|
|
174
|
+
.to_color_space(style::color::ColorSpace::Srgb)
|
|
175
|
+
.raw_components(),
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[cfg(all(test, feature = "svg"))]
|
|
181
|
+
mod svg_tests {
|
|
182
|
+
use super::parse_svg_image;
|
|
183
|
+
|
|
184
|
+
#[test]
|
|
185
|
+
fn svg_without_xmlns_parses() {
|
|
186
|
+
let src = br#"<svg viewBox="0 0 24 24"><path d="M0 0h24v24H0z"/></svg>"#;
|
|
187
|
+
let svg = parse_svg_image(src).unwrap();
|
|
188
|
+
assert!(!svg.tree.root().children().is_empty());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[test]
|
|
192
|
+
fn missing_height_is_computed_from_width_and_viewbox_ratio() {
|
|
193
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200"><rect width="100%" height="100%" fill="green"/></svg>"#;
|
|
194
|
+
let svg = parse_svg_image(src).unwrap();
|
|
195
|
+
assert_eq!(svg.intrinsic_width(), Some(200.0));
|
|
196
|
+
assert_eq!(svg.intrinsic_height(), None);
|
|
197
|
+
assert_eq!(svg.viewbox_aspect_ratio(), Some(1.0));
|
|
198
|
+
assert_eq!(svg.tree.size().width(), 200.0);
|
|
199
|
+
assert_eq!(svg.intrinsic_size(), (200.0, 200.0));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[test]
|
|
203
|
+
fn viewbox_only_has_no_intrinsic_dimensions() {
|
|
204
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 485 58"></svg>"#;
|
|
205
|
+
let svg = parse_svg_image(src).unwrap();
|
|
206
|
+
assert_eq!(svg.intrinsic_width(), None);
|
|
207
|
+
assert_eq!(svg.intrinsic_height(), None);
|
|
208
|
+
// The aspect ratio is still available from the viewBox.
|
|
209
|
+
assert!((svg.aspect_ratio() - (485.0 / 58.0)).abs() < 1e-3);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[test]
|
|
213
|
+
fn absolute_dimensions_are_intrinsic() {
|
|
214
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 48 32"></svg>"#;
|
|
215
|
+
let svg = parse_svg_image(src).unwrap();
|
|
216
|
+
assert_eq!(svg.intrinsic_width(), Some(24.0));
|
|
217
|
+
assert_eq!(svg.intrinsic_height(), Some(16.0));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#[test]
|
|
221
|
+
fn percentage_dimensions_are_not_intrinsic() {
|
|
222
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50%" viewBox="0 0 200 100"></svg>"#;
|
|
223
|
+
let svg = parse_svg_image(src).unwrap();
|
|
224
|
+
assert_eq!(svg.intrinsic_width(), None);
|
|
225
|
+
assert_eq!(svg.intrinsic_height(), None);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[test]
|
|
229
|
+
fn unit_lengths_are_intrinsic() {
|
|
230
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="1.5em" viewBox="0 0 48 32"></svg>"#;
|
|
231
|
+
let svg = parse_svg_image(src).unwrap();
|
|
232
|
+
assert!(svg.intrinsic_width().is_some());
|
|
233
|
+
assert!(svg.intrinsic_height().is_some());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#[test]
|
|
237
|
+
fn non_numeric_dimensions_are_not_intrinsic() {
|
|
238
|
+
let src = br#"<svg xmlns="http://www.w3.org/2000/svg" width="auto" height="foo" viewBox="0 0 200 100"></svg>"#;
|
|
239
|
+
let svg = parse_svg_image(src).unwrap();
|
|
240
|
+
assert_eq!(svg.intrinsic_width(), None);
|
|
241
|
+
assert_eq!(svg.intrinsic_height(), None);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// Creates an markup5ever::QualName.
|
|
246
|
+
/// Given a local name and an optional namespace
|
|
247
|
+
#[macro_export]
|
|
248
|
+
macro_rules! qual_name {
|
|
249
|
+
($local:tt $(, $ns:ident)?) => {
|
|
250
|
+
$crate::QualName {
|
|
251
|
+
prefix: None,
|
|
252
|
+
ns: $crate::ns!($($ns)?),
|
|
253
|
+
local: $crate::local_name!($local),
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
package/wasm/blitz_pdf_bg.wasm
CHANGED
|
Binary file
|