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,462 @@
1
+ //! Construction of the paint tree (`Node::paint_children` and
2
+ //! `Node::stacking_context`) from the laid-out box tree.
3
+ //!
4
+ //! The pass runs once per frame after layout and builds *topology* only: which
5
+ //! node ids each node paints itself and which z-indexed descendants are hoisted
6
+ //! to the nearest stacking-context root. Geometry (a hoisted box's offset from
7
+ //! its stacking-context root) is derived at use time from `final_layout()` and
8
+ //! memoised per geometry generation, so replaying a cached list for a clean
9
+ //! subtree can never paint or hit-test a box where layout no longer puts it.
10
+
11
+ use blitz_traits::node_id::NodeId;
12
+ use std::cell::Cell;
13
+ use std::ops::Range;
14
+ use thin_vec::ThinVec;
15
+
16
+ use crate::layout::damage::ALL_DAMAGE;
17
+ use crate::tree::NodeTree;
18
+ use crate::{BaseDocument, Node};
19
+ use style::properties::generated::longhands::position::computed_value::T as Position;
20
+ use style::selector_parser::RestyleDamage;
21
+ use style::values::computed::Float;
22
+ use style::values::specified::box_::DisplayInside;
23
+ use taffy::{Point, Rect};
24
+
25
+ /// Offset of the hoisted child `child_id` from the border box of its
26
+ /// stacking-context root `sc_root_id`, excluding the child's own
27
+ /// `final_layout().location`: the sum of `location - scroll_offset` of every
28
+ /// box strictly between the two on the child's positioning chain.
29
+ ///
30
+ /// Walks `Node::containing_block` (the out-of-flow containing block for boxes
31
+ /// hoisted by Taffy, otherwise `layout_parent`, which is set for every layout
32
+ /// child by `resolve_layout_children`, anonymous blocks included).
33
+ pub fn hoisted_child_position(tree: &NodeTree, sc_root_id: NodeId, child_id: NodeId) -> Point<f32> {
34
+ let child = &tree[child_id];
35
+ let mut position = Point::ZERO;
36
+ let mut current = child.containing_block();
37
+
38
+ // A fixed-position box whose containing block is the root element is
39
+ // positioned against the viewport and does not scroll with it: cancel the
40
+ // scroll the walk below (or the stacking-context root itself) applies.
41
+ // A fixed box contained by a transformed ancestor scrolls with it.
42
+ if child.taffy_position() == taffy::Position::Fixed
43
+ && let Some(cb) = current.and_then(|id| tree.get(id))
44
+ && cb.containing_block().is_none()
45
+ {
46
+ let scroll_offset = *cb.scroll_offset();
47
+ let viewport_scroll = tree.viewport_scroll();
48
+ position.x += scroll_offset.x as f32 + viewport_scroll.x as f32;
49
+ position.y += scroll_offset.y as f32 + viewport_scroll.y as f32;
50
+ }
51
+
52
+ while let Some(id) = current {
53
+ if id == sc_root_id {
54
+ break;
55
+ }
56
+ let Some(node) = tree.get(id) else {
57
+ break;
58
+ };
59
+ let location = node.final_layout().location;
60
+ let scroll_offset = *node.scroll_offset();
61
+ position.x += location.x - scroll_offset.x as f32;
62
+ position.y += location.y - scroll_offset.y as f32;
63
+ current = node.containing_block();
64
+ }
65
+ position
66
+ }
67
+
68
+ /// A child with a z_index that is hoisted up to it's containing Stacking Context for paint purposes
69
+ #[derive(Debug, Clone)]
70
+ pub struct HoistedPaintChild {
71
+ pub node_id: NodeId,
72
+ pub z_index: i32,
73
+ /// `(geometry generation, offset from the stacking-context root)`, see
74
+ /// [`Self::position`]. Generation `0` is never current.
75
+ position: Cell<(u64, Point<f32>)>,
76
+ }
77
+
78
+ impl HoistedPaintChild {
79
+ fn new(node_id: NodeId, z_index: i32) -> Self {
80
+ Self {
81
+ node_id,
82
+ z_index,
83
+ position: Cell::new((0, Point::ZERO)),
84
+ }
85
+ }
86
+
87
+ /// The child's offset from the border box of its stacking-context root
88
+ /// (`sc_root_id`), excluding the child's own `final_layout().location`.
89
+ /// Computed at most once per geometry generation.
90
+ pub fn position(&self, tree: &NodeTree, sc_root_id: NodeId) -> Point<f32> {
91
+ let generation = tree.geometry_generation();
92
+ let (stamp, cached) = self.position.get();
93
+ if stamp == generation {
94
+ return cached;
95
+ }
96
+ let position = hoisted_child_position(tree, sc_root_id, self.node_id);
97
+ self.position.set((generation, position));
98
+ position
99
+ }
100
+
101
+ /// Whether [`Self::position`] is memoised for the current geometry generation.
102
+ pub fn position_is_cached(&self, tree: &NodeTree) -> bool {
103
+ self.position.get().0 == tree.geometry_generation()
104
+ }
105
+ }
106
+
107
+ #[derive(Debug)]
108
+ pub struct StackingContext {
109
+ pub children: ThinVec<HoistedPaintChild>,
110
+ /// The number of hoisted point children with negative z_index
111
+ pub negative_z_count: u32,
112
+ /// `(geometry generation, bounding box of the hoisted children)`, see
113
+ /// [`Self::hoisted_content_bbox`].
114
+ hoisted_content_bbox: Cell<(u64, Rect<f32>)>,
115
+ }
116
+
117
+ impl StackingContext {
118
+ /// Build a sorted stacking context from the hoisted entries collected for it.
119
+ fn from_children(children: ThinVec<HoistedPaintChild>) -> Self {
120
+ let mut sc = Self {
121
+ children,
122
+ negative_z_count: 0,
123
+ hoisted_content_bbox: Cell::new((0, Rect::ZERO)),
124
+ };
125
+ sc.sort();
126
+ sc
127
+ }
128
+
129
+ /// Bounding box (in CSS pixels relative to the stacking-context root
130
+ /// `sc_root_id`, unscrolled) of the scrollable overflow of all hoisted
131
+ /// children, including their transforms. `scale` is the device pixel
132
+ /// ratio `scrollable_overflow` is stored in. Computed at most once per
133
+ /// geometry generation.
134
+ pub fn hoisted_content_bbox(
135
+ &self,
136
+ tree: &NodeTree,
137
+ sc_root_id: NodeId,
138
+ scale: f64,
139
+ ) -> Rect<f32> {
140
+ let generation = tree.geometry_generation();
141
+ let (stamp, cached) = self.hoisted_content_bbox.get();
142
+ if stamp == generation {
143
+ return cached;
144
+ }
145
+
146
+ let child_rect = |child: &HoistedPaintChild| -> Rect<f32> {
147
+ let node = &tree[child.node_id];
148
+ let position = child.position(tree, sc_root_id);
149
+ let location = node.final_layout().location;
150
+ let mut overflow = *node.scrollable_overflow();
151
+ if let Some(transform) = node.transform().as_deref() {
152
+ overflow = transform.transform_rect_bbox(overflow);
153
+ }
154
+ let left = position.x + location.x;
155
+ let top = position.y + location.y;
156
+ Rect {
157
+ left: left + (overflow.x0 / scale) as f32,
158
+ top: top + (overflow.y0 / scale) as f32,
159
+ right: left + (overflow.x1 / scale) as f32,
160
+ bottom: top + (overflow.y1 / scale) as f32,
161
+ }
162
+ };
163
+
164
+ let mut area = Rect::ZERO;
165
+ if let Some((first, rest)) = self.children.split_first() {
166
+ area = child_rect(first);
167
+ for child in rest {
168
+ let pos = child_rect(child);
169
+ area.left = area.left.min(pos.left);
170
+ area.top = area.top.min(pos.top);
171
+ area.right = area.right.max(pos.right);
172
+ area.bottom = area.bottom.max(pos.bottom);
173
+ }
174
+ }
175
+ self.hoisted_content_bbox.set((generation, area));
176
+ area
177
+ }
178
+
179
+ pub fn sort(&mut self) {
180
+ self.children.sort_by_key(|c| c.z_index);
181
+ self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
182
+ }
183
+
184
+ pub fn neg_z_range(&self) -> Range<usize> {
185
+ 0..(self.negative_z_count as usize)
186
+ }
187
+
188
+ pub fn pos_z_range(&self) -> Range<usize> {
189
+ (self.negative_z_count as usize)..self.children.len()
190
+ }
191
+
192
+ pub fn neg_z_hoisted_children(
193
+ &self,
194
+ ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
195
+ self.children[self.neg_z_range()].iter()
196
+ }
197
+
198
+ pub fn pos_z_hoisted_children(
199
+ &self,
200
+ ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
201
+ self.children[self.pos_z_range()].iter()
202
+ }
203
+ }
204
+
205
+ impl BaseDocument {
206
+ /// Rebuild `paint_children` and `stacking_context` for the tree rooted at
207
+ /// `root_id` from the laid-out box tree.
208
+ ///
209
+ /// Each node's layout children are placed either in its `paint_children`
210
+ /// (sorted by paint order) or, when they have a non-zero `z-index` and are
211
+ /// positioned or flex/grid items, in the nearest ancestor stacking context.
212
+ ///
213
+ /// Subtrees whose stored damage lacks `REBUILD_STACKING_CONTEXT` are skipped: their
214
+ /// own lists are still valid and the entries they contributed to the
215
+ /// enclosing stacking context are replayed from `Node::sc_contribution_cache`.
216
+ pub(crate) fn build_paint_tree(&mut self, root_id: NodeId) {
217
+ self.build_paint_tree_impl(root_id, None);
218
+ }
219
+
220
+ /// `stacking_context` is the entry list of the nearest ancestor stacking
221
+ /// context, or `None` when `node_id` is itself a stacking-context root.
222
+ fn build_paint_tree_impl(
223
+ &mut self,
224
+ node_id: NodeId,
225
+ stacking_context: Option<&mut ThinVec<HoistedPaintChild>>,
226
+ ) {
227
+ {
228
+ let node = &self.nodes[node_id];
229
+ // `propagate_damage_flags` stores the union of a node's own and
230
+ // its descendants' damage on the node, so the absence of
231
+ // `REBUILD_STACKING_CONTEXT` (which every heavier level implies)
232
+ // means nothing in the subtree can have changed a paint list.
233
+ // Anonymous boxes are never skipped: damage marking walks the DOM
234
+ // parent chain, which bypasses them.
235
+ //
236
+ // Whether a node is a stacking-context root also depends on its
237
+ // parent (z-indexed flex/grid items), which does not damage the
238
+ // node itself, so the role the caller resolved must match the one
239
+ // this node was last built with.
240
+ let role_unchanged = node.stacking_context.is_some() == stacking_context.is_none();
241
+ let clean = role_unchanged
242
+ && !node
243
+ .damage()
244
+ .unwrap_or(ALL_DAMAGE)
245
+ .contains(RestyleDamage::REBUILD_STACKING_CONTEXT)
246
+ && !node.is_anonymous();
247
+ if clean {
248
+ if let Some(stacking_context) = stacking_context {
249
+ stacking_context.extend(node.sc_contribution_cache.borrow().iter().cloned());
250
+ }
251
+ return;
252
+ }
253
+ }
254
+
255
+ let Some(display) = self.nodes[node_id].display_style() else {
256
+ return;
257
+ };
258
+
259
+ // Entries hoisted out of this subtree are collected directly into the
260
+ // storage that will hold them afterwards: the node's own stacking
261
+ // context for a root, otherwise its contribution cache (from which
262
+ // they are copied into the enclosing stacking context below).
263
+ let mut entries = {
264
+ let node = &mut self.nodes[node_id];
265
+ let mut entries = match stacking_context {
266
+ None => node
267
+ .stacking_context
268
+ .take()
269
+ .map(|sc| sc.children)
270
+ .unwrap_or_default(),
271
+ Some(_) => std::mem::take(&mut *node.sc_contribution_cache.borrow_mut()),
272
+ };
273
+ entries.clear();
274
+ entries
275
+ };
276
+
277
+ let children = self.nodes[node_id].layout_children.borrow_mut().take();
278
+ if let Some(children) = children {
279
+ let is_flex_or_grid =
280
+ matches!(display.inside(), DisplayInside::Flex | DisplayInside::Grid);
281
+
282
+ // Out-of-flow boxes are laid out (and painted) relative to their
283
+ // containing block, as recorded by Taffy's out-of-flow pass in the
284
+ // containing block's `hoisted_children`. A box whose containing
285
+ // block is not its layout parent is owned by the containing block;
286
+ // its layout parent neither visits nor lists it.
287
+ let owned_here = |doc: &Self, child_id: NodeId| -> bool {
288
+ doc.nodes[child_id]
289
+ .oof_containing_block
290
+ .get()
291
+ .is_none_or(|cb| cb == node_id)
292
+ };
293
+ // Boxes hoisted to this node from further down the tree, keyed by
294
+ // the index of the layout child they descend from: they are visited
295
+ // and listed in tree order, directly after that child (CSS 2.1
296
+ // Appendix E). Boxes whose layout-parent chain does not lead through
297
+ // a layout child of this node come after all of them.
298
+ let mut hoisted_past_parent: Vec<(usize, NodeId)> = self.nodes[node_id]
299
+ .hoisted_children
300
+ .borrow()
301
+ .iter()
302
+ .copied()
303
+ .filter(|&id| {
304
+ self.nodes
305
+ .get(id)
306
+ .is_some_and(|n| n.layout_parent.get() != Some(node_id))
307
+ })
308
+ .map(|id| {
309
+ let anchor = self
310
+ .layout_child_containing(node_id, id)
311
+ .and_then(|anchor| children.iter().position(|&c| c == anchor))
312
+ .unwrap_or(children.len());
313
+ (anchor, id)
314
+ })
315
+ .collect();
316
+ hoisted_past_parent.sort_by_key(|&(anchor, _)| anchor);
317
+ let mut next_hoisted = 0;
318
+
319
+ let mut paint_children = self.nodes[node_id]
320
+ .paint_children
321
+ .borrow_mut()
322
+ .take()
323
+ .unwrap_or_default();
324
+ paint_children.clear();
325
+ paint_children.reserve(children.len() + hoisted_past_parent.len());
326
+
327
+ for (index, &child_id) in children.iter().enumerate() {
328
+ if owned_here(self, child_id) {
329
+ let is_sc_root = self.nodes[child_id].is_stacking_context_root(is_flex_or_grid);
330
+ self.build_paint_tree_impl(
331
+ child_id,
332
+ match is_sc_root {
333
+ true => None,
334
+ false => Some(&mut entries),
335
+ },
336
+ );
337
+
338
+ // Only stacking contexts with a non-zero z-index are lifted
339
+ // out of tree order into the enclosing stacking context's
340
+ // z-sorted list (CSS 2.1 Appendix E).
341
+ let z_index = self.nodes[child_id].z_index();
342
+ if is_sc_root && z_index != 0 {
343
+ entries.push(HoistedPaintChild::new(child_id, z_index))
344
+ } else {
345
+ paint_children.push(child_id);
346
+ }
347
+ }
348
+
349
+ while let Some(&(anchor, hoisted_id)) = hoisted_past_parent.get(next_hoisted)
350
+ && anchor == index
351
+ {
352
+ self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
353
+ next_hoisted += 1;
354
+ }
355
+ }
356
+ for &(_, hoisted_id) in &hoisted_past_parent[next_hoisted..] {
357
+ self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
358
+ }
359
+
360
+ paint_children
361
+ .sort_by_cached_key(|id| node_to_paint_order(&self.nodes[*id], is_flex_or_grid));
362
+
363
+ let node = &self.nodes[node_id];
364
+ *node.paint_children.borrow_mut() = Some(paint_children);
365
+ *node.layout_children.borrow_mut() = Some(children);
366
+ }
367
+
368
+ let node = &mut self.nodes[node_id];
369
+ if let Some(stacking_context) = stacking_context {
370
+ node.stacking_context = None;
371
+ stacking_context.extend(entries.iter().cloned());
372
+ *node.sc_contribution_cache.borrow_mut() = entries;
373
+ } else {
374
+ node.sc_contribution_cache.borrow_mut().clear();
375
+ node.stacking_context = Some(Box::new(StackingContext::from_children(entries)));
376
+ }
377
+ }
378
+
379
+ /// The layout child of `node_id` whose subtree contains `id`, following
380
+ /// `layout_parent`.
381
+ fn layout_child_containing(&self, node_id: NodeId, id: NodeId) -> Option<NodeId> {
382
+ let mut current = id;
383
+ loop {
384
+ let parent = self.nodes.get(current)?.layout_parent.get()?;
385
+ if parent == node_id {
386
+ return Some(current);
387
+ }
388
+ current = parent;
389
+ }
390
+ }
391
+
392
+ /// Visit an out-of-flow box hoisted to its containing block past its
393
+ /// layout parent and place it in the paint lists: z-indexed boxes go to
394
+ /// the enclosing stacking context like any other z-indexed positioned
395
+ /// child, the rest join `paint_children` at the same paint level as other
396
+ /// positioned boxes (the caller's stable sort keeps tree order).
397
+ fn attach_hoisted_past_parent(
398
+ &mut self,
399
+ child_id: NodeId,
400
+ paint_children: &mut ThinVec<NodeId>,
401
+ entries: &mut ThinVec<HoistedPaintChild>,
402
+ ) {
403
+ self.build_paint_tree_impl(
404
+ child_id,
405
+ match self.nodes[child_id].is_stacking_context_root(false) {
406
+ true => None,
407
+ false => Some(entries),
408
+ },
409
+ );
410
+
411
+ let z_index = self.nodes[child_id].z_index();
412
+ if z_index != 0 {
413
+ entries.push(HoistedPaintChild::new(child_id, z_index));
414
+ } else {
415
+ paint_children.push(child_id);
416
+ }
417
+ }
418
+ }
419
+
420
+ #[inline(always)]
421
+ fn position_to_order(pos: Position) -> i32 {
422
+ match pos {
423
+ Position::Static => 0,
424
+ // All positioned descendants with z-index: auto share one paint
425
+ // level (CSS 2.1 Appendix E step 8); the stable sort keeps them in
426
+ // tree order among themselves, above in-flow content and floats.
427
+ Position::Relative | Position::Sticky | Position::Absolute | Position::Fixed => 2,
428
+ }
429
+ }
430
+ #[inline(always)]
431
+ fn float_to_order(pos: Float) -> i32 {
432
+ match pos {
433
+ Float::None => 0,
434
+ _ => 1,
435
+ }
436
+ }
437
+
438
+ /// Paint sort key: (paint level, order-modified position). Positioned
439
+ /// (z-index: auto) descendants paint above in-flow content (CSS 2.1
440
+ /// Appendix E step 8); within a level the stable sort preserves
441
+ /// (order-modified) document order.
442
+ #[inline(always)]
443
+ fn node_to_paint_order(node: &Node, is_flex_or_grid: bool) -> (i32, i32) {
444
+ let Some(style) = node.primary_styles() else {
445
+ return (0, 0);
446
+ };
447
+ let position = style.clone_position();
448
+ if is_flex_or_grid {
449
+ match position {
450
+ Position::Static => (0, style.clone_order()),
451
+ Position::Relative | Position::Sticky => (2, style.clone_order()),
452
+ // Out-of-flow children are not flex/grid items: `order` does
453
+ // not apply; tree order does.
454
+ Position::Absolute | Position::Fixed => (2, 0),
455
+ }
456
+ } else {
457
+ (
458
+ position_to_order(position) + float_to_order(style.clone_float()),
459
+ 0,
460
+ )
461
+ }
462
+ }