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.
Files changed (64) hide show
  1. package/README.md +6 -6
  2. package/THIRD_PARTY_NOTICES.md +5 -2
  3. package/docs/layout-regressions.md +65 -0
  4. package/docs/pagination-regressions.md +7 -6
  5. package/package.json +1 -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,474 @@
1
+ //! Resolve style and layout
2
+
3
+ use blitz_traits::node_id::NodeId;
4
+ use std::cell::RefCell;
5
+
6
+ use debug_timer::debug_timer;
7
+ use kurbo::{Affine, Rect};
8
+ use parley::LayoutContext;
9
+ use selectors::Element as _;
10
+ use style::dom::TDocument;
11
+
12
+ #[cfg(feature = "parallel-construct")]
13
+ use rayon::prelude::*;
14
+
15
+ // FIXME: static thread_local FontCtx isn't necessarily correct in multi-document context.
16
+ // Should use thread_local crate with ThreadLocal value store in the Document.
17
+ thread_local! {
18
+ pub(crate) static LAYOUT_CTX: RefCell<Option<Box<LayoutContext<TextBrush>>>> = const { RefCell::new(None) };
19
+ }
20
+
21
+ use taffy::AvailableSpace;
22
+
23
+ use crate::{
24
+ BaseDocument,
25
+ layout::{
26
+ LayoutPassState,
27
+ construct::{
28
+ ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
29
+ ConstructionTaskResultData, LayoutChildren, build_inline_layout_into,
30
+ collect_layout_children,
31
+ },
32
+ damage::{ALL_DAMAGE, CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC},
33
+ },
34
+ node::TextBrush,
35
+ };
36
+
37
+ impl BaseDocument {
38
+ /// Restyle the tree and then relayout it
39
+ pub fn resolve(&mut self, current_time_for_animations: f64) {
40
+ if TDocument::as_node(&self.root_node())
41
+ .first_element_child()
42
+ .is_none()
43
+ {
44
+ #[cfg(feature = "tracing")]
45
+ tracing::warn!("No DOM - not resolving");
46
+ return;
47
+ }
48
+
49
+ // Process messages that have been sent to our message channel (e.g. loaded resource)
50
+ self.handle_messages();
51
+
52
+ // While render-blocking resources (e.g. stylesheets linked from the `<head>`) are
53
+ // still loading, don't resolve styles or layout (matching how browsers block
54
+ // rendering). Resolving styles before the document's stylesheets have loaded would
55
+ // give elements computed styles based on an incomplete cascade, and a later restyle
56
+ // (once the stylesheet loads) would treat those as genuine "before-change styles",
57
+ // spuriously starting CSS transitions from unstyled values. See issue #689.
58
+ //
59
+ // `handle_messages` above must still run so that loaded resources are ingested and
60
+ // this state can clear.
61
+ if self.has_pending_critical_resources() {
62
+ return;
63
+ }
64
+
65
+ self.resolve_scroll_animation();
66
+
67
+ // Drop scrollbar-activity entries whose fade-out has finished (also
68
+ // sheds entries for removed nodes).
69
+ {
70
+ use crate::node::scrollbar::{FADE_DELAY, FADE_DURATION};
71
+ self.scrollbar_activity
72
+ .retain(|_, last| last.elapsed() < FADE_DELAY + FADE_DURATION);
73
+ }
74
+
75
+ let root_node_id = self.root_element().id;
76
+ debug_timer!(timer, feature = "log-phase-times");
77
+
78
+ // Apply any device changes (viewport resize, zoom, color-scheme, etc)
79
+ // accumulated since the last resolve as a single device rebuild.
80
+ self.flush_pending_device_changes();
81
+
82
+ // we need to resolve stylist first since it will need to drive our layout bits
83
+ self.resolve_stylist(current_time_for_animations);
84
+ timer.record_time("style");
85
+
86
+ // Non-incremental mode is "every node is damaged every frame": mark the
87
+ // whole tree and let the same damage pipeline reconstruct and relayout
88
+ // everything.
89
+ if !self.incremental_layout {
90
+ self.mark_all_damaged();
91
+ timer.record_time("mark_all");
92
+ }
93
+
94
+ // Propagate damage flags (from mutation and restyles) up and down the tree
95
+ self.propagate_damage_flags(root_node_id);
96
+ timer.record_time("damage");
97
+
98
+ // Fix up tree for layout (insert anonymous blocks as necessary, etc)
99
+ self.resolve_layout_children();
100
+ #[cfg(debug_assertions)]
101
+ self.assert_layout_parents_consistent();
102
+ timer.record_time("construct");
103
+
104
+ self.resolve_deferred_tasks();
105
+ // Flush background/mask images from style to dedicated storage on the
106
+ // nodes whose style changed (queued by the style traversal and by
107
+ // pseudo-element box construction), fetching any not-yet-loaded images.
108
+ self.flush_pending_style_images();
109
+ timer.record_time("pconstruct");
110
+
111
+ // Next we resolve layout with the data resolved by stlist
112
+ self.resolve_layout();
113
+ timer.record_time("layout");
114
+ self.nodes.bump_geometry_generation();
115
+
116
+ // Resolve transforms
117
+ self.resolve_transforms(root_node_id);
118
+ timer.record_time("transform");
119
+
120
+ // Build paint children / stacking contexts for damaged subtrees. Runs
121
+ // after layout and transforms so that stacking-context roots created by
122
+ // this frame's transforms are picked up, and before damage is cleared.
123
+ self.build_paint_tree(root_node_id);
124
+ timer.record_time("paint_tree");
125
+
126
+ // Clear all damage and dirty flags, walking only subtrees which are
127
+ // marked as (potentially) containing damage.
128
+ let doc_node_id = self.root_node().id;
129
+ self.clear_damage_and_dirty_flags(doc_node_id);
130
+ timer.record_time("c_damage");
131
+
132
+ // Re-resolve the hover node from the pointer position against the fresh
133
+ // layout. This must run *after* the damage/dirty flags are cleared
134
+ // above, so that the restyle hint and ancestor `dirty_descendants`
135
+ // flags set by any resulting hover change survive into the next resolve
136
+ // pass (the clearing loop would otherwise wipe them). Any resulting
137
+ // restyle is picked up on the next resolve pass; a redraw is requested
138
+ // if the hovered node actually changes.
139
+ self.refresh_hover();
140
+
141
+ let mut subdoc_is_animating = false;
142
+ for &node_id in &self.sub_document_nodes {
143
+ let node = &mut self.nodes[node_id];
144
+ let size = node.final_layout().size;
145
+ if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
146
+ // Set viewport
147
+ // viewport_mut handles change detection. So we just unconditionally set the values;
148
+ let mut sub_viewport = sub_doc.viewport_mut();
149
+ sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
150
+ sub_viewport.zoom = self.viewport.zoom;
151
+ sub_viewport.color_scheme = self.viewport.color_scheme;
152
+
153
+ let viewport_scale = self.viewport.scale();
154
+ sub_viewport.window_size = (
155
+ (size.width * viewport_scale) as u32,
156
+ (size.height * viewport_scale) as u32,
157
+ );
158
+ drop(sub_viewport);
159
+
160
+ sub_doc.resolve(current_time_for_animations);
161
+
162
+ subdoc_is_animating |= sub_doc.is_animating();
163
+ }
164
+ }
165
+ self.subdoc_is_animating = subdoc_is_animating;
166
+ timer.record_time("subdocs");
167
+
168
+ timer.print_times(&format!("Resolve({}): ", self.id()));
169
+ }
170
+
171
+ fn resolve_transforms(&mut self, node_id: NodeId) -> Rect {
172
+ if !self.nodes.contains_key(node_id) {
173
+ return Rect::ZERO;
174
+ }
175
+
176
+ let scale = self.viewport.scale_f64();
177
+
178
+ if !self.nodes[node_id]
179
+ .damage()
180
+ .map(|d| d.contains(style::selector_parser::RestyleDamage::RECALCULATE_OVERFLOW))
181
+ .unwrap_or(false)
182
+ {
183
+ let node = &self.nodes[node_id];
184
+ let location = node.final_layout().location.map(|v| v as f64 * scale);
185
+
186
+ let mut transform = Affine::translate((location.x, location.y));
187
+ if let Some(t) = node.transform().as_deref() {
188
+ transform *= *t
189
+ }
190
+
191
+ let overflow = *node.scrollable_overflow();
192
+ return transform.transform_rect_bbox(overflow);
193
+ }
194
+
195
+ let transform = self.nodes[node_id].set_transform(scale as f32);
196
+
197
+ let w = self.nodes[node_id].final_layout().size.width as f64 * scale;
198
+ let h = self.nodes[node_id].final_layout().size.height as f64 * scale;
199
+ let mut overflow = Rect::new(0.0, 0.0, w, h);
200
+
201
+ let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
202
+
203
+ if let Some(ref children) = layout_children {
204
+ for &child_id in children {
205
+ // Out-of-flow children are laid out relative to their containing
206
+ // block, not their DOM parent: they are visited (and their overflow
207
+ // accounted for) via the containing block's hoisted list below.
208
+ if self.nodes[child_id].is_out_of_flow() {
209
+ continue;
210
+ }
211
+ let child_rect_in_self = self.resolve_transforms(child_id);
212
+ overflow = overflow.union(child_rect_in_self);
213
+ }
214
+ }
215
+ let hoisted_children =
216
+ std::mem::take(&mut *self.nodes[node_id].hoisted_children.borrow_mut());
217
+ for &child_id in &hoisted_children {
218
+ if !self.nodes.contains_key(child_id) {
219
+ continue;
220
+ }
221
+ let child_rect_in_self = self.resolve_transforms(child_id);
222
+ overflow = overflow.union(child_rect_in_self);
223
+ }
224
+ *self.nodes[node_id].hoisted_children.borrow_mut() = hoisted_children;
225
+ for pseudo in [self.nodes[node_id].before(), self.nodes[node_id].after()] {
226
+ let Some(pseudo) = pseudo else { continue };
227
+ if self.nodes[pseudo].is_out_of_flow() {
228
+ continue;
229
+ }
230
+ let child_rect_in_self = self.resolve_transforms(pseudo);
231
+ overflow = overflow.union(child_rect_in_self);
232
+ }
233
+
234
+ *self.nodes[node_id].scrollable_overflow_mut() = overflow;
235
+ *self.nodes[node_id].layout_children.get_mut() = layout_children;
236
+
237
+ let scaled_x = self.nodes[node_id].final_layout().location.x as f64 * scale;
238
+ let scaled_y = self.nodes[node_id].final_layout().location.y as f64 * scale;
239
+
240
+ let full = if let Some(t) = transform {
241
+ Affine::translate((scaled_x, scaled_y)) * t
242
+ } else {
243
+ Affine::translate((scaled_x, scaled_y))
244
+ };
245
+
246
+ full.transform_rect_bbox(overflow)
247
+ }
248
+
249
+ /// Ensure that the layout_children field is populated for all nodes
250
+ pub fn resolve_layout_children(&mut self) {
251
+ resolve_layout_children_recursive(self, self.root_node().id);
252
+
253
+ fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: NodeId) {
254
+ // Anonymous blocks and pseudo-elements can be removed from the slab
255
+ // between render passes. Bail out rather than panicking on a stale key.
256
+ if doc.nodes.get(node_id).is_none() {
257
+ return;
258
+ }
259
+
260
+ let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
261
+ let _flags = doc.nodes[node_id].flags;
262
+
263
+ if damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
264
+ //} || flags.contains(NodeFlags::IS_INLINE_ROOT) {
265
+
266
+ // Deallocate the anonymous blocks created for this node in the
267
+ // previous construction round. They live only in the slab, so
268
+ // reconstructing without freeing them would leak a slab entry per
269
+ // anonymous block per reconstruction.
270
+ let old_anonymous_blocks = std::mem::take(&mut doc.nodes[node_id].anonymous_blocks);
271
+ for anon_id in old_anonymous_blocks {
272
+ doc.deallocate_anonymous_block(anon_id);
273
+ }
274
+
275
+ let mut collected = LayoutChildren::default();
276
+ collect_layout_children(doc, node_id, &mut collected);
277
+ let layout_children = collected.children;
278
+ doc.nodes[node_id].anonymous_blocks = collected.anonymous_blocks;
279
+
280
+ // Recurse into newly collected layout children
281
+ for child_id in layout_children.iter().copied() {
282
+ resolve_layout_children_recursive(doc, child_id);
283
+ doc.nodes[child_id].layout_parent.set(Some(node_id));
284
+ if let Some(mut data) = doc.nodes[child_id]
285
+ .try_stylo_element_data_mut()
286
+ .and_then(|s| s.get_mut())
287
+ {
288
+ data.damage
289
+ .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
290
+ }
291
+ }
292
+
293
+ *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
294
+ // *doc.nodes[node_id].paint_children.borrow_mut() = Some(layout_children);
295
+
296
+ damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
297
+ // damage.insert(RestyleDamage::RELAYOUT | RestyleDamage::REPAINT);
298
+ } else {
299
+ //if damage.contains(CONSTRUCT_DESCENDENT) {
300
+ let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
301
+ if let Some(layout_children) = layout_children {
302
+ for child_id in layout_children.iter().copied() {
303
+ // Anonymous blocks and pseudo-elements can be removed from the
304
+ // slab between render passes; skip stale IDs.
305
+ if !doc.nodes.contains_key(child_id) {
306
+ continue;
307
+ }
308
+ resolve_layout_children_recursive(doc, child_id);
309
+ doc.nodes[child_id].layout_parent.set(Some(node_id));
310
+ }
311
+
312
+ *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
313
+ }
314
+
315
+ // damage.remove(CONSTRUCT_DESCENDENT);
316
+ // damage.insert(RestyleDamage::RELAYOUT | RestyleDamage::REPAINT);
317
+ }
318
+
319
+ doc.nodes[node_id].set_damage(damage);
320
+ }
321
+ }
322
+
323
+ /// Every layout child must point back at its container via
324
+ /// `layout_parent`, which `propagate_damage_flags` relies on to reach
325
+ /// anonymous boxes.
326
+ #[cfg(debug_assertions)]
327
+ fn assert_layout_parents_consistent(&self) {
328
+ use crate::node::NodeFlags;
329
+
330
+ for (parent_id, node) in self.nodes.iter() {
331
+ if !node.flags.contains(NodeFlags::IS_IN_DOCUMENT) {
332
+ continue;
333
+ }
334
+ let Some(children) = node.layout_children.borrow().clone() else {
335
+ continue;
336
+ };
337
+ for child_id in children {
338
+ let Some(child) = self.nodes.get(child_id) else {
339
+ panic!("layout child {child_id:?} of {parent_id:?} is not in the slab");
340
+ };
341
+ debug_assert_eq!(
342
+ child.layout_parent.get(),
343
+ Some(parent_id),
344
+ "layout_parent of {child_id:?} does not point at its layout container {parent_id:?}"
345
+ );
346
+ }
347
+ }
348
+ }
349
+
350
+ pub fn resolve_deferred_tasks(&mut self) {
351
+ let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
352
+
353
+ // Deduplicate deferred tasks by node_id to avoid redundant work
354
+ deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
355
+ deferred_construction_nodes.dedup_by_key(|task| task.node_id);
356
+
357
+ #[cfg(feature = "parallel-construct")]
358
+ let iter = deferred_construction_nodes.into_par_iter();
359
+ #[cfg(not(feature = "parallel-construct"))]
360
+ let iter = deferred_construction_nodes.into_iter();
361
+
362
+ let results: Vec<ConstructionTaskResult> = iter
363
+ .map(|task: ConstructionTask| match task.data {
364
+ ConstructionTaskData::InlineLayout(mut layout) => {
365
+ #[cfg(feature = "parallel-construct")]
366
+ let mut layout_ctx = LAYOUT_CTX
367
+ .take()
368
+ .unwrap_or_else(|| Box::new(LayoutContext::new()));
369
+ #[cfg(feature = "parallel-construct")]
370
+ let layout_ctx_mut = &mut layout_ctx;
371
+
372
+ #[cfg(feature = "parallel-construct")]
373
+ let mut font_ctx = self
374
+ .thread_font_contexts
375
+ .get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
376
+ .borrow_mut();
377
+ #[cfg(feature = "parallel-construct")]
378
+ let font_ctx_mut = &mut *font_ctx;
379
+
380
+ #[cfg(not(feature = "parallel-construct"))]
381
+ let layout_ctx_mut = &mut self.layout_ctx;
382
+ #[cfg(not(feature = "parallel-construct"))]
383
+ let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
384
+
385
+ layout.content_widths = None;
386
+ build_inline_layout_into(
387
+ &self.nodes,
388
+ layout_ctx_mut,
389
+ font_ctx_mut,
390
+ &mut layout,
391
+ self.viewport.scale(),
392
+ task.node_id,
393
+ );
394
+
395
+ #[cfg(feature = "parallel-construct")]
396
+ {
397
+ LAYOUT_CTX.set(Some(layout_ctx));
398
+ }
399
+
400
+ // If layout doesn't contain any inline boxes, then it is safe to populate the content_widths
401
+ // cache during this parallelized stage.
402
+ // if layout.layout.inline_boxes().is_empty() {
403
+ // layout.content_widths();
404
+ // }
405
+
406
+ ConstructionTaskResult {
407
+ node_id: task.node_id,
408
+ data: ConstructionTaskResultData::InlineLayout(layout),
409
+ }
410
+ }
411
+ })
412
+ .collect();
413
+
414
+ for result in results {
415
+ match result.data {
416
+ ConstructionTaskResultData::InlineLayout(layout) => {
417
+ self.nodes[result.node_id].clear_layout_cache();
418
+ self.nodes[result.node_id]
419
+ .element_data_mut()
420
+ .unwrap()
421
+ .inline_layout_data = Some(layout);
422
+ }
423
+ }
424
+ }
425
+
426
+ self.deferred_construction_nodes.clear();
427
+ }
428
+
429
+ /// Walk the nodes now that they're properly styled and transfer their styles to the taffy style system
430
+ ///
431
+ /// TODO: update taffy to use an associated type instead of slab key
432
+ /// TODO: update taffy to support traited styles so we don't even need to rely on taffy for storage
433
+ pub fn resolve_layout(&mut self) {
434
+ let size = self.stylist.device().au_viewport_size();
435
+
436
+ let available_space = taffy::Size {
437
+ width: AvailableSpace::Definite(size.width.to_f32_px()),
438
+ height: AvailableSpace::Definite(size.height.to_f32_px()),
439
+ };
440
+
441
+ let root_element_id = crate::taffy_node_id(self.root_element().id);
442
+
443
+ // println!("\n\nRESOLVE LAYOUT\n===========\n");
444
+
445
+ let mut state = LayoutPassState::new(self);
446
+ #[cfg(feature = "writing-mode")]
447
+ let available_space = {
448
+ state.layout_wm = state.layout_wm_of(crate::dom_node_id(root_element_id));
449
+ if state.layout_wm.is_vertical() {
450
+ available_space.transpose()
451
+ } else {
452
+ available_space
453
+ }
454
+ };
455
+ taffy::compute_root_layout(&mut state, root_element_id, available_space);
456
+ #[cfg(feature = "writing-mode")]
457
+ state.physicalise_and_round_layout(root_element_id);
458
+ #[cfg(not(feature = "writing-mode"))]
459
+ if state.round_layout {
460
+ taffy::round_layout(&mut state, root_element_id);
461
+ } else {
462
+ // Publish geometry before resolving transforms and paint bounds, so
463
+ // measurement, pagination and vector painting use the same boxes.
464
+ for (_, node) in state.nodes.iter_mut() {
465
+ if node.element_data().is_some() {
466
+ *node.final_layout_mut() = *node.unrounded_layout();
467
+ }
468
+ }
469
+ }
470
+
471
+ // println!("\n\n");
472
+ // taffy::print_tree(self, root_node_id)
473
+ }
474
+ }