blitz-pdf 0.1.2 → 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.
Files changed (64) hide show
  1. package/README.md +26 -0
  2. package/THIRD_PARTY_NOTICES.md +5 -2
  3. package/docs/layout-regressions.md +65 -0
  4. package/docs/pagination-regressions.md +89 -0
  5. package/package.json +2 -1
  6. package/vendor/README.md +35 -0
  7. package/vendor/blitz-dom/Cargo.toml +113 -0
  8. package/vendor/blitz-dom/LICENSE-APACHE +176 -0
  9. package/vendor/blitz-dom/LICENSE-MIT +23 -0
  10. package/vendor/blitz-dom/assets/default.css +1142 -0
  11. package/vendor/blitz-dom/assets/moz-bullet-font.otf +0 -0
  12. package/vendor/blitz-dom/src/accessibility.rs +255 -0
  13. package/vendor/blitz-dom/src/config.rs +97 -0
  14. package/vendor/blitz-dom/src/cssom.rs +971 -0
  15. package/vendor/blitz-dom/src/debug.rs +206 -0
  16. package/vendor/blitz-dom/src/document.rs +3395 -0
  17. package/vendor/blitz-dom/src/events/driver.rs +427 -0
  18. package/vendor/blitz-dom/src/events/focus.rs +40 -0
  19. package/vendor/blitz-dom/src/events/ime.rs +31 -0
  20. package/vendor/blitz-dom/src/events/keyboard.rs +174 -0
  21. package/vendor/blitz-dom/src/events/mod.rs +315 -0
  22. package/vendor/blitz-dom/src/events/pointer.rs +849 -0
  23. package/vendor/blitz-dom/src/font_metrics.rs +177 -0
  24. package/vendor/blitz-dom/src/form.rs +463 -0
  25. package/vendor/blitz-dom/src/html.rs +38 -0
  26. package/vendor/blitz-dom/src/iframe.rs +186 -0
  27. package/vendor/blitz-dom/src/layout/construct.rs +1328 -0
  28. package/vendor/blitz-dom/src/layout/damage.rs +642 -0
  29. package/vendor/blitz-dom/src/layout/inline.rs +1160 -0
  30. package/vendor/blitz-dom/src/layout/list.rs +237 -0
  31. package/vendor/blitz-dom/src/layout/mod.rs +969 -0
  32. package/vendor/blitz-dom/src/layout/paint_tree.rs +462 -0
  33. package/vendor/blitz-dom/src/layout/replaced.rs +439 -0
  34. package/vendor/blitz-dom/src/layout/table.rs +967 -0
  35. package/vendor/blitz-dom/src/layout/text_transform.rs +979 -0
  36. package/vendor/blitz-dom/src/layout/writing_mode.rs +437 -0
  37. package/vendor/blitz-dom/src/lib.rs +160 -0
  38. package/vendor/blitz-dom/src/mutator.rs +1818 -0
  39. package/vendor/blitz-dom/src/net.rs +649 -0
  40. package/vendor/blitz-dom/src/node/attributes.rs +60 -0
  41. package/vendor/blitz-dom/src/node/custom_widget.rs +178 -0
  42. package/vendor/blitz-dom/src/node/element.rs +1106 -0
  43. package/vendor/blitz-dom/src/node/mod.rs +30 -0
  44. package/vendor/blitz-dom/src/node/node.rs +2036 -0
  45. package/vendor/blitz-dom/src/node/scrollbar.rs +237 -0
  46. package/vendor/blitz-dom/src/node/serialize.rs +225 -0
  47. package/vendor/blitz-dom/src/node/stylo_data.rs +142 -0
  48. package/vendor/blitz-dom/src/node/svg.rs +265 -0
  49. package/vendor/blitz-dom/src/node/text.rs +804 -0
  50. package/vendor/blitz-dom/src/query_selector.rs +275 -0
  51. package/vendor/blitz-dom/src/resolve.rs +474 -0
  52. package/vendor/blitz-dom/src/resolved_style.rs +797 -0
  53. package/vendor/blitz-dom/src/scrolling.rs +780 -0
  54. package/vendor/blitz-dom/src/selection.rs +122 -0
  55. package/vendor/blitz-dom/src/stylo.rs +1518 -0
  56. package/vendor/blitz-dom/src/stylo_device.rs +88 -0
  57. package/vendor/blitz-dom/src/stylo_to_cursor_icon.rs +49 -0
  58. package/vendor/blitz-dom/src/stylo_to_kurbo.rs +129 -0
  59. package/vendor/blitz-dom/src/stylo_to_parley.rs +655 -0
  60. package/vendor/blitz-dom/src/traversal.rs +600 -0
  61. package/vendor/blitz-dom/src/tree.rs +136 -0
  62. package/vendor/blitz-dom/src/url.rs +63 -0
  63. package/vendor/blitz-dom/src/util.rs +256 -0
  64. package/wasm/blitz_pdf_bg.wasm +0 -0
@@ -0,0 +1,255 @@
1
+ use crate::{BaseDocument, ElementData, Node as BlitzDomNode, local_name};
2
+ use accesskit::{Node as AccessKitNode, NodeId, Role, TreeId, TreeInfo, TreeUpdate};
3
+ use style::properties::longhands::visibility;
4
+
5
+ impl BaseDocument {
6
+ pub fn build_accessibility_tree(&self) -> TreeUpdate {
7
+ let mut nodes = std::collections::HashMap::new();
8
+ let mut window = AccessKitNode::new(Role::Window);
9
+ let mut hidden_nodes = std::collections::HashSet::new();
10
+
11
+ self.visit(|node_id, node| {
12
+ if node.is_hidden_from_accessibility_tree()
13
+ || node
14
+ .parent
15
+ .map(|p| hidden_nodes.contains(&p))
16
+ .unwrap_or(false)
17
+ {
18
+ hidden_nodes.insert(node_id);
19
+ return;
20
+ }
21
+ let parent = node
22
+ .parent
23
+ .and_then(|parent_id| nodes.get_mut(&parent_id))
24
+ .map(|(_, parent)| parent)
25
+ .unwrap_or(&mut window);
26
+ let (id, builder) = self.build_accessibility_node(node, parent);
27
+
28
+ nodes.insert(node_id, (id, builder));
29
+ });
30
+
31
+ let mut nodes: Vec<_> = nodes
32
+ .into_iter()
33
+ .map(|(_, (id, node))| (id, node))
34
+ .collect();
35
+ nodes.push((NodeId(u64::MAX), window));
36
+
37
+ let tree = TreeInfo::new(NodeId(u64::MAX));
38
+ TreeUpdate {
39
+ tree_id: TreeId::ROOT,
40
+ nodes,
41
+ tree: Some(tree),
42
+ focus: NodeId(self.focus_node_id.map(|id| id.as_u64()).unwrap_or(u64::MAX)),
43
+ }
44
+ }
45
+
46
+ fn build_accessibility_node(
47
+ &self,
48
+ node: &BlitzDomNode,
49
+ parent: &mut AccessKitNode,
50
+ ) -> (NodeId, AccessKitNode) {
51
+ let id = NodeId(node.id.as_u64());
52
+
53
+ let mut builder = AccessKitNode::default();
54
+ if node.parent.is_none() {
55
+ builder.set_role(Role::Window)
56
+ } else if let Some(element_data) = node.element_data() {
57
+ let name = element_data.name.local.to_string();
58
+ let role_attr = element_data.attr(local_name!("role"));
59
+
60
+ // TODO: The roles of elements with strong native semantics cannot be overridden; see
61
+ // https://www.w3.org/TR/wai-aria-1.2/#host_general_conflict.
62
+ let role = role_attr
63
+ .and_then(role_from_name)
64
+ .or_else(|| role_from_element_data(element_data))
65
+ .unwrap_or(Role::Unknown);
66
+
67
+ builder.set_role(role);
68
+ builder.set_html_tag(name);
69
+
70
+ // https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion
71
+ if element_data.attr(local_name!("aria-hidden")) == Some("true") {
72
+ builder.set_hidden();
73
+ }
74
+ } else if node.is_text_node() {
75
+ builder.set_role(Role::TextRun);
76
+ builder.set_value(node.text_content());
77
+ parent.push_labelled_by(id)
78
+ }
79
+
80
+ parent.push_child(id);
81
+
82
+ (id, builder)
83
+ }
84
+ }
85
+
86
+ impl BlitzDomNode {
87
+ // https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion
88
+ fn is_hidden_from_accessibility_tree(&self) -> bool {
89
+ self.try_stylo_element_data()
90
+ .as_ref()
91
+ .and_then(|s| s.get())
92
+ .map(|s| {
93
+ s.styles.is_display_none()
94
+ || s.styles.primary().clone_visibility()
95
+ == visibility::computed_value::T::Hidden
96
+ })
97
+ .unwrap_or(false)
98
+ }
99
+ }
100
+
101
+ fn role_from_name(name: &str) -> Option<Role> {
102
+ match name {
103
+ "alert" => Some(Role::Alert),
104
+ "alertdialog" => Some(Role::AlertDialog),
105
+ "button" => Some(Role::Button),
106
+ "checkbox" => Some(Role::CheckBox),
107
+ "dialog" => Some(Role::Dialog),
108
+ "gridcell" => Some(Role::GridCell),
109
+ "link" => Some(Role::Link),
110
+ "log" => Some(Role::Log),
111
+ "marquee" => Some(Role::Marquee),
112
+ "menuitem" => Some(Role::MenuItem),
113
+ "menuitemcheckbox" => Some(Role::MenuItemCheckBox),
114
+ "menuitemradio" => Some(Role::MenuItemRadio),
115
+ "option" => Some(Role::ListBoxOption),
116
+ "progressbar" => Some(Role::ProgressIndicator),
117
+ "radio" => Some(Role::RadioButton),
118
+ "scrollbar" => Some(Role::ScrollBar),
119
+ "slider" => Some(Role::Slider),
120
+ "spinbutton" => Some(Role::SpinButton),
121
+ "status" => Some(Role::Status),
122
+ "tab" => Some(Role::Tab),
123
+ "tabpanel" => Some(Role::TabPanel),
124
+ "textbox" => Some(Role::TextInput),
125
+ "timer" => Some(Role::Timer),
126
+ "tooltip" => Some(Role::Tooltip),
127
+ "treeitem" => Some(Role::TreeItem),
128
+ "combobox" => Some(Role::ComboBox),
129
+ "grid" => Some(Role::Grid),
130
+ "listbox" => Some(Role::ListBox),
131
+ "menu" => Some(Role::Menu),
132
+ "menubar" => Some(Role::MenuBar),
133
+ "radiogroup" => Some(Role::RadioGroup),
134
+ "tablist" => Some(Role::TabList),
135
+ "tree" => Some(Role::Tree),
136
+ "treegrid" => Some(Role::TreeGrid),
137
+ "article" => Some(Role::Article),
138
+ "columnheader" => Some(Role::ColumnHeader),
139
+ "definition" => Some(Role::Definition),
140
+ "document" => Some(Role::Document),
141
+ "group" => Some(Role::Group),
142
+ "heading" => Some(Role::Heading),
143
+ "img" => Some(Role::Image),
144
+ "list" => Some(Role::List),
145
+ "listitem" => Some(Role::ListItem),
146
+ "math" => Some(Role::Math),
147
+ "note" => Some(Role::Note),
148
+ "region" => Some(Role::Region),
149
+ "row" => Some(Role::Row),
150
+ "rowgroup" => Some(Role::RowGroup),
151
+ "rowheader" => Some(Role::RowHeader),
152
+ "toolbar" => Some(Role::Toolbar),
153
+ "application" => Some(Role::Application),
154
+ "banner" => Some(Role::Banner),
155
+ "complementary" => Some(Role::Complementary),
156
+ "contentinfo" => Some(Role::ContentInfo),
157
+ "form" => Some(Role::Form),
158
+ "main" => Some(Role::Main),
159
+ "navigation" => Some(Role::Navigation),
160
+ "search" => Some(Role::Search),
161
+ _ => None,
162
+ }
163
+ }
164
+
165
+ fn role_from_element_data(element_data: &ElementData) -> Option<Role> {
166
+ // <https://www.w3.org/TR/html-aam-1.0/>
167
+ match &*element_data.name.local {
168
+ // Document structure
169
+ "article" => Some(Role::Article),
170
+ "aside" => Some(Role::Complementary),
171
+ "footer" => Some(Role::Footer),
172
+ "header" => Some(Role::Header),
173
+ "main" => Some(Role::Main),
174
+ "nav" => Some(Role::Navigation),
175
+ "search" => Some(Role::Search),
176
+ "section" => Some(Role::Section),
177
+ "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => Some(Role::Heading),
178
+ "p" => Some(Role::Paragraph),
179
+ "blockquote" => Some(Role::Blockquote),
180
+ "figure" => Some(Role::Figure),
181
+ "figcaption" | "caption" => Some(Role::Caption),
182
+ "hr" => Some(Role::Splitter),
183
+
184
+ // Grouping
185
+ "ul" | "ol" | "menu" => Some(Role::List),
186
+ "li" => Some(Role::ListItem),
187
+ "dl" => Some(Role::DescriptionList),
188
+ "dt" => Some(Role::Term),
189
+ "dd" => Some(Role::Definition),
190
+ "dialog" => Some(Role::Dialog),
191
+ "fieldset" => Some(Role::Group),
192
+ "form" => Some(Role::Form),
193
+ "div" => Some(Role::GenericContainer),
194
+
195
+ // Tables
196
+ "table" => Some(Role::Table),
197
+ "thead" | "tbody" | "tfoot" => Some(Role::RowGroup),
198
+ "tr" => Some(Role::Row),
199
+ "td" => Some(Role::Cell),
200
+ "th" => match element_data.attr(local_name!("scope")) {
201
+ Some("row") | Some("rowgroup") => Some(Role::RowHeader),
202
+ _ => Some(Role::ColumnHeader),
203
+ },
204
+
205
+ // Interactive
206
+ // An <a> is only a link when it has an href.
207
+ "a" => match element_data.attr(local_name!("href")) {
208
+ Some(_) => Some(Role::Link),
209
+ None => Some(Role::GenericContainer),
210
+ },
211
+ "button" => Some(Role::Button),
212
+ "label" => Some(Role::Label),
213
+ "legend" => Some(Role::Label),
214
+ "select" => match element_data.attr(local_name!("multiple")) {
215
+ Some(_) => Some(Role::ListBox),
216
+ None => Some(Role::ComboBox),
217
+ },
218
+ "option" => Some(Role::ListBoxOption),
219
+ "textarea" => Some(Role::MultilineTextInput),
220
+ "progress" => Some(Role::ProgressIndicator),
221
+ "meter" => Some(Role::Meter),
222
+ "output" => Some(Role::Status),
223
+ "summary" => Some(Role::DisclosureTriangle),
224
+
225
+ // Inline semantics
226
+ "code" => Some(Role::Code),
227
+ "em" => Some(Role::Emphasis),
228
+ "strong" => Some(Role::Strong),
229
+ "mark" => Some(Role::Mark),
230
+ "time" => Some(Role::Time),
231
+ "img" => Some(Role::Image),
232
+ "iframe" => Some(Role::Iframe),
233
+
234
+ "input" => {
235
+ let ty = element_data.attr(local_name!("type")).unwrap_or("text");
236
+ match ty {
237
+ "button" | "submit" | "reset" => Some(Role::Button),
238
+ "checkbox" => Some(Role::CheckBox),
239
+ "color" => Some(Role::ColorWell),
240
+ "date" => Some(Role::DateInput),
241
+ "datetime-local" => Some(Role::DateTimeInput),
242
+ "email" => Some(Role::EmailInput),
243
+ "number" => Some(Role::NumberInput),
244
+ "password" => Some(Role::PasswordInput),
245
+ "radio" => Some(Role::RadioButton),
246
+ "range" => Some(Role::Slider),
247
+ "search" => Some(Role::SearchInput),
248
+ "tel" => Some(Role::PhoneNumberInput),
249
+ "time" => Some(Role::TimeInput),
250
+ _ => Some(Role::TextInput),
251
+ }
252
+ }
253
+ _ => None,
254
+ }
255
+ }
@@ -0,0 +1,97 @@
1
+ use crate::{HtmlParserProvider, UserAgentStylesheet};
2
+ use blitz_traits::{
3
+ navigation::NavigationProvider,
4
+ net::{AbortSignal, NetProvider},
5
+ shell::{ShellProvider, Viewport},
6
+ };
7
+ use parley::FontContext;
8
+ use std::sync::Arc;
9
+ use style::media_queries::MediaType;
10
+
11
+ /// Strategy for Stylo's style traversal during `resolve`.
12
+ ///
13
+ /// Two `Document`s resolving on [`StyleThreading::Parallel`] concurrently
14
+ /// share Stylo's global thread pool and can panic with
15
+ /// `already mutably borrowed` — see
16
+ /// <https://github.com/DioxusLabs/blitz/issues/430>. Set
17
+ /// [`StyleThreading::Sequential`] on documents that may resolve from a
18
+ /// user thread while another `Parallel` resolve is in flight.
19
+ #[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
20
+ pub enum StyleThreading {
21
+ /// Use Stylo's parallel traversal via its global rayon thread pool.
22
+ /// Fastest for a single document; panics if another `Parallel` resolve
23
+ /// is in flight on a different thread.
24
+ Parallel,
25
+ /// Run style traversal sequentially on the calling thread, bypassing
26
+ /// the global pool. Safe to use from many user threads concurrently.
27
+ #[default]
28
+ Sequential,
29
+ }
30
+
31
+ /// Options used when constructing a [`BaseDocument`](crate::BaseDocument)
32
+ pub struct DocumentConfig {
33
+ /// The initial `Viewport`
34
+ pub viewport: Option<Viewport>,
35
+ /// Exact CSS viewport dimensions, independent of the integer window size.
36
+ pub layout_viewport: Option<(f32, f32)>,
37
+ /// Snap box edges to whole CSS pixels. Disable for vector output.
38
+ pub round_layout: bool,
39
+ /// The base url which relative URLs are resolved against
40
+ pub base_url: Option<String>,
41
+ /// User Agent stylesheets, in cascade order. Defaults to `vec![UserAgentStylesheet::default_sheet()]`.
42
+ ///
43
+ /// The list is used as given, so replacing it removes the default sheet unless it is
44
+ /// included, e.g. `vec![UserAgentStylesheet::default_sheet(), custom]`.
45
+ pub ua_stylesheets: Vec<UserAgentStylesheet>,
46
+ /// Net provider to handle network requests for resources
47
+ pub net_provider: Option<Arc<dyn NetProvider>>,
48
+ /// Navigation provider to handle link clicks and form submissions
49
+ pub navigation_provider: Option<Arc<dyn NavigationProvider>>,
50
+ /// Shell provider to redraw requests, clipboard, etc
51
+ pub shell_provider: Option<Arc<dyn ShellProvider>>,
52
+ /// HTML parser provider. Used to parse HTML for setInnerHTML
53
+ pub html_parser_provider: Option<Arc<dyn HtmlParserProvider>>,
54
+ /// Parley `FontContext`
55
+ pub font_ctx: Option<FontContext>,
56
+ /// The CSS media type used to evaluate `@media` rules.
57
+ /// Defaults to [`MediaType::screen`].
58
+ pub media_type: Option<MediaType>,
59
+ /// Strategy for Stylo's style traversal.
60
+ /// Defaults to [`StyleThreading::Parallel`].
61
+ pub style_threading: StyleThreading,
62
+ /// Whether incremental layout is enabled. Defaults to `true`.
63
+ ///
64
+ /// When disabled, every node is treated as damaged on each `resolve` and the
65
+ /// whole tree is reconstructed and laid out from scratch through the same
66
+ /// damage-driven pipeline used by incremental mode.
67
+ pub incremental: Option<bool>,
68
+ /// If set, every sub-resource `Request` blitz-dom creates for this
69
+ /// document will carry this signal. Aborting it cancels every in-flight
70
+ /// fetch tied to this document.
71
+ pub abort_signal: Option<AbortSignal>,
72
+ /// How deeply this document is nested within other documents
73
+ /// (0 for a root document). Used to limit `<iframe>` nesting depth.
74
+ pub subdocument_depth: usize,
75
+ }
76
+
77
+ impl Default for DocumentConfig {
78
+ fn default() -> Self {
79
+ Self {
80
+ viewport: None,
81
+ layout_viewport: None,
82
+ round_layout: true,
83
+ base_url: None,
84
+ ua_stylesheets: vec![UserAgentStylesheet::default_sheet()],
85
+ net_provider: None,
86
+ navigation_provider: None,
87
+ shell_provider: None,
88
+ html_parser_provider: None,
89
+ font_ctx: None,
90
+ media_type: None,
91
+ style_threading: StyleThreading::default(),
92
+ incremental: None,
93
+ abort_signal: None,
94
+ subdocument_depth: 0,
95
+ }
96
+ }
97
+ }