Skip to main content

rustex_lib/shipout/
mod.rs

1pub(crate) mod annotations;
2pub(crate) mod html;
3pub(crate) mod nodes;
4pub(crate) mod state;
5pub(crate) mod utils;
6
7use crate::engine::nodes::{LineSkip, RusTeXNode};
8use crate::engine::{Refs, Res, SRef, Types};
9use crate::shipout::state::{
10    Common, HLike, Math, ModeKind, Row, SVG, Shipout, ShipoutNodeH, ShipoutNodeM, ShipoutNodeT,
11    ShipoutNodeTable, ShipoutNodeV, ShipoutState, VLike,
12};
13use crate::shipout::utils::{HNodes, MNode, MNodes, VNodes};
14use crate::utils::{Flex, Margin};
15use tex_engine::engine::stomach::methods::ParLineSpec;
16use tex_engine::pdflatex::nodes::{PDFDest, PDFNode};
17use tex_engine::tex::nodes::NodeTrait;
18use tex_engine::tex::nodes::boxes::{HBoxInfo, TeXBox, ToOrSpread, VBoxInfo};
19use tex_engine::tex::nodes::horizontal::HNode;
20use tex_engine::tex::nodes::math::{
21    MathAtom, MathFontStyle, MathGroup, MathKernel, MathNode, MathNucleus, MathStyle, MathStyleType,
22};
23use tex_engine::tex::nodes::vertical::VNode;
24use tex_engine::tex::numerics::{Dim32, Skip};
25use tex_engine::utils::errors::TeXError;
26/*
27pub(crate) fn make_page<F:FnOnce(Refs,&mut ShipoutState) -> Res<()>>(engine:Refs,state:&mut ShipoutState,f:F) -> Res<HTMLNode> {
28    let mut page = state.do_in_and(HTMLNode::page(),None,|state| {
29        f(engine,state)
30    })?;
31    let page_width = engine.state.get_primitive_dim(PRIMITIVES.pdfpagewidth);
32    let text_width = engine.state.get_primitive_dim(PRIMITIVES.hsize);
33    let top_font = state.fonts.first().unwrap();
34    page.font = Some((top_font.clone(),true));
35    page.styles.insert("--rustex-text-width".into(),dim_to_num(text_width.0).into());
36    page.styles.insert("--rustex-page-width".into(),dim_to_num(page_width.0).into());
37    page.styles.insert("line-height".into(),"1.2".into()); // TODO
38    Ok(page)
39}
40
41 */
42
43pub fn shipout(engine: Refs, n: VNode<Types>) -> Res<()> {
44    //println!("Here: {}\n\n-------------------------------------------\n\n",n.display());
45    match n {
46        VNode::Box(TeXBox::V { children, .. }) => {
47            let children = get_page_inner(children.into_vec());
48            /*println!("--------------------------------------------");
49            for c in &children {
50                println!("{}",c.display());
51            }*/
52            ShipoutState::split_state(engine, |state| state.do_vlist(&mut children.into()))
53                .map_err(|e| TeXError::General(format!("Not allowed in V-Mode: {e:?}")))?;
54            /*
55                        println!("--------------------------------------------");
56                        println!("{:?}",engine.aux.extension.state.output);
57                        println!("--------------------------------------------");
58            */
59        }
60        _ => unreachable!(),
61    }
62    Ok(())
63}
64
65impl<Mode: VLike> Shipout<'_, '_, Mode> {
66    fn do_vlist(&mut self, children: &mut VNodes) -> Result<(), Option<VNode<Types>>> {
67        //let mut empty: bool = true;
68        while let Some(c) = children.next() {
69            match c {
70                VNode::Custom(RusTeXNode::PDFNode(
71                    PDFNode::PDFOutline(_)
72                    | PDFNode::PDFPageAttr(_)
73                    | PDFNode::PDFPagesAttr(_)
74                    | PDFNode::PDFCatalog(_)
75                    | PDFNode::PDFSave
76                    | PDFNode::PDFAnnot(_)
77                    | PDFNode::PDFLiteral(_)
78                    | PDFNode::XForm(_)
79                    | PDFNode::Obj(_),
80                ))
81                | VNode::Penalty(_)
82                | VNode::Mark(..)
83                | VNode::Custom(
84                    RusTeXNode::PageBegin | RusTeXNode::PageEnd | RusTeXNode::HAlignEnd,
85                ) => (),
86                VNode::Custom(RusTeXNode::PGFEscape(bx)) => children.prefix(vec![VNode::Box(bx)]), // TODO?
87                VNode::Custom(RusTeXNode::PDFNode(PDFNode::Color(act))) => self.do_color(act),
88                VNode::Custom(RusTeXNode::FontChange(font, global)) => self.open_font(font, global),
89                VNode::Custom(RusTeXNode::FontChangeEnd) => self.close_font(),
90                VNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFStartLink(link))) => {
91                    self.open_link(link)
92                }
93                VNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFEndLink)) => self.close_link(),
94                VNode::Custom(RusTeXNode::AnnotBegin {
95                    start,
96                    attrs,
97                    styles,
98                    classes,
99                    tag,
100                }) => self.open_annot(
101                    start,
102                    attrs.into(),
103                    styles.into(),
104                    classes.into_iter().map(String::into).collect(),
105                    tag,
106                ),
107                VNode::Custom(RusTeXNode::AnnotEnd(end)) => self.close_annot(end),
108                VNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFMatrix {
109                    scale,
110                    rotate,
111                    skewx,
112                    skewy,
113                })) => self.open_matrix(scale, rotate, skewx, skewy),
114                VNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFRestore)) => self.close_matrix(),
115                VNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFDest(PDFDest { id, .. }))) => {
116                    self.push(Common::PDFDest(id).into())
117                }
118                VNode::Whatsit(wi) => wi.call(self.engine).map_err(|_| None)?,
119                VNode::Custom(RusTeXNode::PGFGBegin { .. } | RusTeXNode::PGFGEnd) => (), // TODO?
120                VNode::Custom(RusTeXNode::PGFSvg {
121                    bx,
122                    minx,
123                    miny,
124                    maxx,
125                    maxy,
126                }) => {
127                    if let TeXBox::H {
128                        children,
129                        start,
130                        end,
131                        ..
132                    } = bx
133                    {
134                        self.in_svg(start, end, minx.0, miny.0, maxx.0, maxy.0, |state| {
135                            state.do_svglist(&mut children.into()).map_err(|_| None)
136                        })
137                        .map_err(|_| None)?
138                    } else {
139                        unreachable!()
140                    }
141                }
142                VNode::Leaders(_) => (), // TODO?
143                VNode::Custom(RusTeXNode::Literal(s)) => self.push(Common::Literal(s).into()),
144
145                // ----------------------------------------------------------
146                VNode::VKern(d) => self.skipv(d.into()),
147                VNode::VSkip(s) => self.skipv(s.into()),
148                VNode::VFil => self.skipv(Margin::fil()),
149                VNode::VFill => self.skipv(Margin::fill()),
150                VNode::Vss => self.skipv(Margin::ss()),
151                VNode::VFilneg => self.skipv(Margin {
152                    base: 0,
153                    stretch: Flex::Fil(-1),
154                    shrink: Flex::Fixed(0),
155                }),
156                VNode::Custom(RusTeXNode::ParagraphBegin {
157                    specs,
158                    start,
159                    end,
160                    lineskip,
161                    parskip,
162                }) => self.do_par(children, specs, start, end, lineskip, parskip)?,
163                // empty = false
164                VNode::Custom(RusTeXNode::HAlignBegin { lineskip }) => {
165                    self.do_halign(children, lineskip)?
166                }
167                // empty = false
168                // ----------------------------------------------------------
169                /*VNode::Box(TeXBox::H {info:HBoxInfo::HAlignRow,children,start,end,..}) => {
170                    let mut redos = Vec::new();
171                    while let Some(c) = self.nodes.pop() { match c {
172                        ShipoutNodeV::HAlign {children,num_cols,uses_color,uses_font} => {
173                            todo!()
174                        }
175                        c => redos.push(c)
176                    } }
177                    if !redos.is_empty() {
178                        todo!()
179                    }
180                }*/
181                VNode::Box(tb) => {
182                    let _ = tb.height();
183                    let _ = tb.width();
184                    let _ = tb.depth();
185                    match tb {
186                        TeXBox::V {
187                            info:
188                                VBoxInfo::VBox {
189                                    scaled: ToOrSpread::None,
190                                    assigned_width: None,
191                                    assigned_height: None,
192                                    assigned_depth: None,
193                                    moved_left: None,
194                                    raised: None,
195                                    ..
196                                },
197                            children: next,
198                            ..
199                        } => {
200                            children.prefix(next.into());
201                        }
202                        TeXBox::V {
203                            info,
204                            children,
205                            start,
206                            end,
207                        } if matches!(info, VBoxInfo::VBox { .. } | VBoxInfo::VTop { .. }) => self
208                            .in_v(start, end, info, |state| {
209                                state.do_vlist(&mut children.into())
210                            })?,
211                        TeXBox::H {
212                            info,
213                            children,
214                            start,
215                            end,
216                            preskip,
217                        } if matches!(info, HBoxInfo::HBox { .. }) => self
218                            .in_h(start, end, info, preskip, |state| {
219                                state.do_hlist(&mut children.into())
220                            })
221                            .map_err(|_| None)?,
222                        TeXBox::H {
223                            info: HBoxInfo::HAlignRow,
224                            children,
225                            start,
226                            end,
227                            ..
228                        } => {
229                            let mut redos = Vec::new();
230                            let mut done = false;
231                            while let Some(mut c) = self.nodes.pop() {
232                                match &mut c {
233                                    ShipoutNodeV::HAlign {
234                                        children: chs,
235                                        num_cols,
236                                        uses_color,
237                                        uses_font,
238                                        ..
239                                    } => {
240                                        chs.push(ShipoutNodeTable::NoAlign {
241                                            uses_font: redos
242                                                .iter()
243                                                .any(|c: &ShipoutNodeV| c.uses_previous_font()),
244                                            uses_color: redos
245                                                .iter()
246                                                .any(|c: &ShipoutNodeV| c.uses_previous_color()),
247                                            children: std::mem::take(&mut redos),
248                                        });
249                                        self.reopen_halign(
250                                            start,
251                                            end,
252                                            chs,
253                                            num_cols,
254                                            uses_color,
255                                            uses_font,
256                                            |state| state.do_row(children),
257                                        )
258                                        .map_err(|_| None)?;
259                                        done = true;
260                                        break;
261                                    }
262                                    _ => redos.push(c),
263                                }
264                            }
265                            if !done {
266                                for r in redos.into_iter().rev() {
267                                    self.push(r)
268                                }
269                            }
270                            // TODO lost table row
271                        }
272                        _ => todo!("{tb:?}"),
273                    }
274                }
275                VNode::HRule {
276                    width,
277                    height,
278                    depth,
279                    ..
280                } => self.push(ShipoutNodeV::HRule {
281                    width,
282                    height,
283                    depth,
284                }),
285                VNode::Custom(RusTeXNode::PDFNode(PDFNode::XImage(_))) => todo!(),
286
287                _ => todo!("{c:?}"),
288            }
289        }
290        Ok(())
291    }
292
293    fn do_par(
294        &mut self,
295        children: &mut VNodes,
296        specs: Vec<ParLineSpec<Types>>,
297        start: SRef,
298        end: SRef,
299        lineskip: LineSkip,
300        parskip: Skip<Dim32>,
301    ) -> Result<(), Option<VNode<Types>>> {
302        // hack for parlines that may have been \lastboxed
303        let ret = self.in_par(specs, start, end, lineskip, parskip, |state| {
304            let mut later = Vec::new();
305            let mut emergency_break = false;
306            let mut is_empty = true;
307            while let Some(c) = children.next() {
308                match c {
309                    VNode::VSkip(_)
310                    | VNode::VFil
311                    | VNode::VFill
312                    | VNode::VFilneg
313                    | VNode::Vss
314                    | VNode::Mark(..)
315                    | VNode::VKern(_) => (),
316                    VNode::Custom(RusTeXNode::ParagraphEnd) if is_empty => emergency_break = true,
317                    VNode::Custom(RusTeXNode::ParagraphEnd) => return Ok(later),
318                    VNode::Box(TeXBox::H {
319                        info:
320                            HBoxInfo::ParLine {
321                                ends_with_line_break,
322                                ..
323                            },
324                        children,
325                        ..
326                    }) => {
327                        is_empty = false;
328                        state.do_hlist(&mut children.into()).map_err(|_| None)?;
329                        if ends_with_line_break {
330                            state.push(ShipoutNodeH::LineBreak)
331                        }
332                    }
333                    VNode::Box(TeXBox::H {
334                        info:
335                            HBoxInfo::HBox {
336                                moved_left: None,
337                                raised: None,
338                                ..
339                            },
340                        children,
341                        ..
342                    }) if emergency_break => {
343                        is_empty = false;
344                        state.do_hlist(&mut children.into()).map_err(|_| None)?
345                    }
346                    _ => {
347                        later.push(c);
348                        if emergency_break {
349                            return Ok(later);
350                        }
351                    }
352                }
353            }
354            Ok(later)
355        })?;
356        children.prefix(ret);
357        Ok(())
358    }
359
360    fn do_halign(
361        &mut self,
362        children: &mut VNodes,
363        lineskip: LineSkip,
364    ) -> Result<(), Option<VNode<Types>>> {
365        self.in_halign(lineskip, move |state| {
366            while let Some(row) = children.next() {
367                match row {
368                    VNode::Custom(RusTeXNode::HAlignEnd) => break,
369                    VNode::Box(TeXBox::H {
370                        info: HBoxInfo::HAlignRow,
371                        children,
372                        start,
373                        end,
374                        ..
375                    }) => {
376                        state
377                            .in_row(start, end, |state| state.do_row(children))
378                            .map_err(|_| None)?;
379                    }
380                    VNode::Box(
381                        b @ TeXBox::H {
382                            info: HBoxInfo::HAlignCell { .. },
383                            start,
384                            end,
385                            ..
386                        },
387                    ) => {
388                        let c = vec![HNode::Box(b)].into();
389                        state
390                            .in_row(start, end, |state| state.do_row(c))
391                            .map_err(|_| None)?;
392                    }
393                    v => {
394                        let children = vec![v];
395                        match state.in_noalign(|s| s.do_vlist(&mut children.into())) {
396                            Ok(()) => (),
397                            Err(Some(VNode::Custom(RusTeXNode::HAlignEnd))) => break,
398                            Err(Some(VNode::Box(TeXBox::H {
399                                info: HBoxInfo::HAlignRow,
400                                children,
401                                start,
402                                end,
403                                ..
404                            }))) => {
405                                state
406                                    .in_row(start, end, |state| state.do_row(children))
407                                    .map_err(|_| None)?;
408                            }
409                            o => return o,
410                        }
411                    }
412                }
413            }
414            Ok(())
415        })
416    }
417}
418
419impl Shipout<'_, '_, Row> {
420    fn do_row(&mut self, children: Box<[HNode<Types>]>) -> Result<(), Option<HNode<Types>>> {
421        for c in children {
422            match c {
423                HNode::Box(TeXBox::H {
424                    info: HBoxInfo::HAlignCell { spans, .. },
425                    children,
426                    start,
427                    end,
428                    ..
429                }) => self
430                    .in_cell(
431                        start,
432                        end,
433                        children
434                            .iter()
435                            .max_by_key(|e| e.height().0 + e.depth().0)
436                            .map(|e| e.height() + e.depth())
437                            .unwrap_or_default(),
438                        spans,
439                        |state| state.do_hlist(&mut children.into()),
440                    )
441                    .map_err(|_| None)?,
442                _ => todo!(),
443            }
444        }
445        Ok(())
446    }
447}
448
449impl<Mode: HLike> Shipout<'_, '_, Mode> {
450    fn do_hlist(&mut self, children: &mut HNodes) -> Result<(), Option<HNode<Types>>> {
451        while let Some(c) = children.next() {
452            match c {
453                HNode::Custom(RusTeXNode::PDFNode(
454                    PDFNode::PDFOutline(_)
455                    | PDFNode::PDFPageAttr(_)
456                    | PDFNode::PDFPagesAttr(_)
457                    | PDFNode::PDFCatalog(_)
458                    | PDFNode::PDFSave
459                    | PDFNode::PDFAnnot(_)
460                    | PDFNode::PDFLiteral(_)
461                    | PDFNode::XForm(_)
462                    | PDFNode::Obj(_),
463                ))
464                | HNode::Penalty(_)
465                | HNode::Mark(..)
466                | HNode::Custom(
467                    RusTeXNode::PageBegin | RusTeXNode::PageEnd | RusTeXNode::HAlignEnd,
468                ) => (),
469                HNode::Custom(RusTeXNode::PGFEscape(bx)) => children.prefix(vec![HNode::Box(bx)]),
470                HNode::Custom(RusTeXNode::PDFNode(PDFNode::Color(act))) => self.do_color(act),
471                HNode::Custom(RusTeXNode::FontChange(font, global)) => self.open_font(font, global),
472                HNode::Custom(RusTeXNode::FontChangeEnd) => self.close_font(),
473                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFStartLink(link))) => {
474                    self.open_link(link)
475                }
476                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFEndLink)) => self.close_link(),
477                HNode::Custom(RusTeXNode::AnnotBegin {
478                    start,
479                    attrs,
480                    styles,
481                    classes,
482                    tag,
483                }) => self.open_annot(
484                    start,
485                    attrs.into(),
486                    styles.into(),
487                    classes.into_iter().map(String::into).collect(),
488                    tag,
489                ),
490                HNode::Custom(RusTeXNode::AnnotEnd(end)) => self.close_annot(end),
491                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFMatrix {
492                    scale,
493                    rotate,
494                    skewx,
495                    skewy,
496                })) => self.open_matrix(scale, rotate, skewx, skewy),
497                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFRestore)) => self.close_matrix(),
498                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFDest(PDFDest { id, .. }))) => {
499                    self.push(Common::PDFDest(id).into())
500                }
501                HNode::Whatsit(wi) => wi.call(self.engine).map_err(|_| None)?,
502                HNode::Custom(RusTeXNode::Literal(s)) => self.push(Common::Literal(s).into()),
503                HNode::Custom(RusTeXNode::PGFGBegin { .. } | RusTeXNode::PGFGEnd) => (), // TODO maybe? Only seems to happen in escape-boxes in svg
504                HNode::Custom(RusTeXNode::PGFSvg {
505                    bx,
506                    minx,
507                    miny,
508                    maxx,
509                    maxy,
510                }) => {
511                    if let TeXBox::H {
512                        children,
513                        start,
514                        end,
515                        ..
516                    } = bx
517                    {
518                        self.in_svg(start, end, minx.0, miny.0, maxx.0, maxy.0, |state| {
519                            state.do_svglist(&mut children.into()).map_err(|_| None)
520                        })
521                        .map_err(|_| None)?
522                    } else {
523                        unreachable!()
524                    }
525                }
526                HNode::Leaders(_) => (), // TODO?
527
528                HNode::HSkip(sk) => self.skiph(sk.into()),
529                HNode::HKern(kn) => self.skiph(kn.into()),
530                HNode::HFil => self.skiph(Margin::fil()),
531                HNode::HFill => self.skiph(Margin::fill()),
532                HNode::Hss => self.skiph(Margin::ss()),
533                HNode::HFilneg => self.skiph(Margin {
534                    base: 0,
535                    stretch: Flex::Fil(-1),
536                    shrink: Flex::Fixed(0),
537                }),
538
539                HNode::Box(bx) => {
540                    let _ = bx.height();
541                    let _ = bx.width();
542                    let _ = bx.depth();
543                    match bx {
544                        TeXBox::V {
545                            info,
546                            children,
547                            start,
548                            end,
549                        } if matches!(info, VBoxInfo::VBox { .. } | VBoxInfo::VTop { .. }) => self
550                            .in_v(start, end, info, |state| {
551                                state.do_vlist(&mut children.into())
552                            })
553                            .map_err(|_| None)?,
554                        TeXBox::H {
555                            info: HBoxInfo::ParIndent(d),
556                            ..
557                        } => self.indent(d.0),
558                        TeXBox::H {
559                            info:
560                                HBoxInfo::HBox {
561                                    scaled: ToOrSpread::None,
562                                    assigned_width: None,
563                                    assigned_height: None,
564                                    assigned_depth: None,
565                                    moved_left: None,
566                                    raised: None,
567                                    ..
568                                },
569                            children: next,
570                            ..
571                        } if Mode::kind() == ModeKind::H => {
572                            children.prefix(next.into());
573                        }
574                        TeXBox::H {
575                            info,
576                            children,
577                            start,
578                            end,
579                            preskip,
580                        } if matches!(info, HBoxInfo::HBox { .. }) => self
581                            .in_h(start, end, info, preskip, |state| {
582                                state.do_hlist(&mut children.into())
583                            })
584                            .map_err(|_| None)?,
585                        TeXBox::H {
586                            info: mut info @ HBoxInfo::ParLine { .. },
587                            children,
588                            start,
589                            end,
590                            ..
591                        } => {
592                            info.to_hbox();
593                            self.in_h(start, end, info, None, |state| {
594                                state.do_hlist(&mut children.into())
595                            })
596                            .map_err(|_| None)?
597                        }
598                        _ => todo!("{bx:?}"),
599                    }
600                }
601                HNode::MathGroup(MathGroup {
602                    start,
603                    end,
604                    display,
605                    children,
606                    ..
607                }) => self
608                    .in_math(
609                        start,
610                        end,
611                        display.map(|(a, b)| (a.into(), b.into())).into(),
612                        |state| state.do_mathlist(&mut children.into()),
613                    )
614                    .map_err(|_| None)?,
615
616                HNode::VRule {
617                    width,
618                    height,
619                    depth,
620                    ..
621                } => self.push(ShipoutNodeH::VRule {
622                    width,
623                    height,
624                    depth,
625                }),
626                HNode::Char { char, font } => {
627                    let r =
628                        ShipoutNodeH::char(char, font, self.engine, &mut self.top_state.font_data);
629                    self.push(r)
630                }
631                HNode::Space => self.push(ShipoutNodeH::Space),
632                HNode::Custom(RusTeXNode::PDFNode(PDFNode::XImage(img))) => {
633                    self.push(ShipoutNodeH::Img(img))
634                }
635                HNode::Accent { accent, char, font } => {
636                    let r = ShipoutNodeH::accent(
637                        char,
638                        accent,
639                        font,
640                        self.engine,
641                        &mut self.top_state.font_data,
642                    );
643                    self.push(r)
644                }
645                _ => todo!("{c:?}"),
646            }
647        }
648        Ok(())
649    }
650}
651
652impl Shipout<'_, '_, Math> {
653    fn do_mathlist(
654        &mut self,
655        children: &mut MNodes,
656    ) -> Result<(), Option<MathNode<Types, MathFontStyle<Types>>>> {
657        while let Some(c) = children.next() {
658            match c {
659                MNode::Custom(RusTeXNode::PDFNode(
660                    PDFNode::PDFOutline(_)
661                    | PDFNode::PDFPageAttr(_)
662                    | PDFNode::PDFPagesAttr(_)
663                    | PDFNode::PDFCatalog(_)
664                    | PDFNode::PDFSave
665                    | PDFNode::PDFAnnot(_)
666                    | PDFNode::PDFLiteral(_)
667                    | PDFNode::XForm(_)
668                    | PDFNode::Obj(_),
669                ))
670                | MNode::Penalty(_)
671                | MNode::Mark(..)
672                | MNode::Custom(
673                    RusTeXNode::PageBegin | RusTeXNode::PageEnd | RusTeXNode::HAlignEnd,
674                ) => (),
675                MNode::Custom(RusTeXNode::PGFEscape(_bx)) => todo!(), // children.prefix(vec!(VNode::Box(bx))),
676                MNode::Custom(RusTeXNode::PDFNode(PDFNode::Color(act))) => self.do_color(act),
677                MNode::Custom(RusTeXNode::FontChange(font, global)) => self.open_font(font, global),
678                MNode::Custom(RusTeXNode::FontChangeEnd) => self.close_font(),
679                MNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFStartLink(link))) => {
680                    self.open_link(link)
681                }
682                MNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFEndLink)) => self.close_link(),
683                MNode::Custom(RusTeXNode::AnnotBegin {
684                    start,
685                    attrs,
686                    styles,
687                    classes,
688                    tag,
689                }) => self.open_annot(
690                    start,
691                    attrs.into(),
692                    styles.into(),
693                    classes.into_iter().map(String::into).collect(),
694                    tag,
695                ),
696                MNode::Custom(RusTeXNode::AnnotEnd(end)) => self.close_annot(end),
697                MNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFMatrix {
698                    scale,
699                    rotate,
700                    skewx,
701                    skewy,
702                })) => self.open_matrix(scale, rotate, skewx, skewy),
703                MNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFRestore)) => self.close_matrix(),
704                MNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFDest(PDFDest { id, .. }))) => {
705                    self.push(Common::PDFDest(id).into())
706                }
707                MNode::Whatsit(wi) => wi.call(self.engine).map_err(|_| None)?,
708                MNode::Custom(RusTeXNode::Literal(s)) => self.push(Common::Literal(s).into()),
709                MNode::Custom(RusTeXNode::PDFNode(PDFNode::XImage(img))) => {
710                    self.push(ShipoutNodeM::Img(img))
711                }
712                MNode::Custom(RusTeXNode::PGFGBegin { .. } | RusTeXNode::PGFGEnd) => todo!(),
713                MNode::Space => self.push(ShipoutNodeM::Space),
714                MNode::MSkip { skip, .. } => self.push(ShipoutNodeM::MSkip {
715                    base: skip.base.0,
716                    mu: true,
717                }),
718                MNode::MKern { kern, .. } => self.push(ShipoutNodeM::MSkip {
719                    base: kern.0,
720                    mu: true,
721                }),
722                MNode::HSkip(skip) => self.push(ShipoutNodeM::MSkip {
723                    base: skip.base.0,
724                    mu: false,
725                }),
726                MNode::HKern(kern) => self.push(ShipoutNodeM::MSkip {
727                    base: kern.0,
728                    mu: false,
729                }),
730                MNode::HFil | MNode::HFill | MNode::Hss | MNode::HFilneg => (), // TODO maybe?
731                MNode::Choice(ls) => children.prefix(ls.0.into_vec()),
732                MNode::Leaders(..) => (), // TODO?
733
734                MNode::Atom(MathAtom { nucleus, sub, sup }) => {
735                    let sub = sub.map(|inner| {
736                        |state: &mut Shipout<Math>| state.do_mathlist(&mut inner.into())
737                    });
738                    let sup = sup.map(|inner| {
739                        |state: &mut Shipout<Math>| state.do_mathlist(&mut inner.into())
740                    });
741                    match nucleus {
742                        MathNucleus::Accent { accent, inner } => {
743                            self.accent(
744                                accent.0,
745                                accent.1,
746                                |state| state.do_mathlist(&mut inner.into()),
747                                sub,
748                                sup,
749                            )?;
750                        }
751                        MathNucleus::Inner(kernel) => {
752                            self.inner(|state| state.do_kernel(kernel), sub, sup)?;
753                        }
754                        MathNucleus::Middle(char, s) => {
755                            self.middle(char, s, sub, sup)?;
756                        }
757                        MathNucleus::Overline(kernel) => {
758                            self.overline(|state| state.do_kernel(kernel), sub, sup)?;
759                        }
760                        MathNucleus::Underline(kernel) => {
761                            self.underline(|state| state.do_kernel(kernel), sub, sup)?;
762                        }
763                        MathNucleus::LeftRight {
764                            start,
765                            end,
766                            left,
767                            right,
768                            children,
769                        } => {
770                            self.left_right(
771                                start,
772                                end,
773                                left,
774                                right,
775                                |state| state.do_mathlist(&mut children.into()),
776                                sub,
777                                sup,
778                            )?;
779                        }
780                        MathNucleus::Radical { rad, inner } => {
781                            self.radical(
782                                rad.0,
783                                rad.1,
784                                |state| state.do_mathlist(&mut inner.into()),
785                                sub,
786                                sup,
787                            )?;
788                        }
789                        MathNucleus::Simple {
790                            cls,
791                            kernel,
792                            limits,
793                        } => {
794                            self.with_class(
795                                cls,
796                                limits.unwrap_or(false),
797                                |state| state.do_kernel(kernel),
798                                sub,
799                                sup,
800                            )?;
801                        }
802                        c @ MathNucleus::VCenter { .. } => {
803                            let wd = c.width().0;
804                            let MathNucleus::VCenter {
805                                start,
806                                end,
807                                children,
808                                ..
809                            } = c
810                            else {
811                                unreachable!()
812                            };
813                            self.vcenter(
814                                start,
815                                end,
816                                wd,
817                                |state| state.do_vlist(&mut children.into()),
818                                sub,
819                                sup,
820                            )?;
821                        }
822                    }
823                }
824                MathNode::Over {
825                    start,
826                    end,
827                    top,
828                    bottom,
829                    sep,
830                    left,
831                    right,
832                } => {
833                    self.over(
834                        start,
835                        end,
836                        left,
837                        right,
838                        sep,
839                        |state| state.do_mathlist(&mut top.into()).map_err(|_| ()),
840                        |state| state.do_mathlist(&mut bottom.into()).map_err(|_| ()),
841                    )?;
842                }
843                MathNode::VRule {
844                    width,
845                    height,
846                    depth,
847                    ..
848                } => self.push(ShipoutNodeM::VRule {
849                    width,
850                    height,
851                    depth,
852                }),
853
854                _ => todo!("{c:?}"),
855            }
856        }
857        Ok(())
858    }
859    fn do_kernel(
860        &mut self,
861        kernel: MathKernel<Types, MathFontStyle<Types>>,
862    ) -> Result<(), Option<MathNode<Types, MathFontStyle<Types>>>> {
863        match kernel {
864            MathKernel::Char { char, style } => self.do_mathchar(
865                char,
866                style.font,
867                style.cramped,
868                style.style == MathStyleType::Display,
869            ),
870            MathKernel::List { children, .. } => self.do_mathlist(&mut children.into())?,
871            MathKernel::Empty => (),
872            MathKernel::Box(bx) => {
873                let _ = bx.width();
874                let _ = bx.height();
875                let _ = bx.depth();
876                let wd = bx.assigned_width();
877                let ht = bx.assigned_height();
878                let dp = bx.assigned_depth();
879                if bx.is_empty() {
880                    match (wd, ht, dp) {
881                        (Some(Dim32(0)) | None, Some(Dim32(0)) | None, Some(Dim32(0)) | None) => (),
882                        _ => self.push(ShipoutNodeM::Phantom {
883                            width: wd.map(|d| d.0).unwrap_or_default(),
884                            height: ht.map(|d| d.0).unwrap_or_default(),
885                            depth: dp.map(|d| d.0).unwrap_or_default(),
886                        }),
887                    }
888                } else {
889                    match bx {
890                        TeXBox::V {
891                            start,
892                            end,
893                            info,
894                            children,
895                            ..
896                        } => self
897                            .in_v(start, end, info, |state| {
898                                state.do_vlist(&mut children.into())
899                            })
900                            .map_err(|_| None)?,
901                        TeXBox::H {
902                            start,
903                            end,
904                            preskip,
905                            info,
906                            children,
907                            ..
908                        } => self
909                            .in_h(start, end, info, preskip, |state| {
910                                state.do_hlist(&mut children.into())
911                            })
912                            .map_err(|_| None)?,
913                    }
914                }
915            }
916        }
917        Ok(())
918    }
919}
920
921impl Shipout<'_, '_, SVG> {
922    fn do_svglist(&mut self, children: &mut HNodes) -> Result<(), Option<HNode<Types>>> {
923        while let Some(c) = children.next() {
924            match c {
925                HNode::Custom(RusTeXNode::PDFNode(
926                    PDFNode::PDFOutline(_)
927                    | PDFNode::PDFPageAttr(_)
928                    | PDFNode::PDFPagesAttr(_)
929                    | PDFNode::PDFCatalog(_)
930                    | PDFNode::PDFSave
931                    | PDFNode::PDFAnnot(_)
932                    | PDFNode::PDFLiteral(_)
933                    | PDFNode::XForm(_)
934                    | PDFNode::Obj(_),
935                ))
936                | HNode::Penalty(_)
937                | HNode::Mark(..)
938                | HNode::Custom(
939                    RusTeXNode::PageBegin | RusTeXNode::PageEnd | RusTeXNode::HAlignEnd,
940                ) => (),
941                HNode::Custom(RusTeXNode::PDFNode(PDFNode::Color(act))) => self.do_color(act),
942                HNode::Custom(RusTeXNode::FontChange(font, global)) => self.open_font(font, global),
943                HNode::Custom(RusTeXNode::FontChangeEnd) => self.close_font(),
944                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFStartLink(link))) => {
945                    self.open_link(link)
946                }
947                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFEndLink)) => self.close_link(),
948                HNode::Custom(RusTeXNode::AnnotBegin {
949                    start,
950                    attrs,
951                    styles,
952                    classes,
953                    tag,
954                }) => self.open_annot(
955                    start,
956                    attrs.into(),
957                    styles.into(),
958                    classes.into_iter().map(String::into).collect(),
959                    tag,
960                ),
961                HNode::Custom(RusTeXNode::AnnotEnd(end)) => self.close_annot(end),
962                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFMatrix {
963                    scale,
964                    rotate,
965                    skewx,
966                    skewy,
967                })) => self.open_matrix(scale, rotate, skewx, skewy),
968                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFRestore)) => self.close_matrix(),
969                HNode::Custom(RusTeXNode::PDFNode(PDFNode::PDFDest(PDFDest { id, .. }))) => {
970                    self.push(Common::PDFDest(id).into())
971                }
972
973                HNode::Whatsit(wi) => wi.call(self.engine).map_err(|_| None)?,
974                HNode::Leaders(_) => (), // TODO?
975                HNode::Custom(RusTeXNode::Literal(s)) => self.push(Common::Literal(s).into()),
976
977                HNode::Custom(RusTeXNode::PGFGBegin { attrs, tag }) => self.open_node(attrs, tag),
978                HNode::Custom(RusTeXNode::PGFGEnd) => self.close_node(),
979                HNode::Box(TeXBox::H { children: chs, .. }) => children.prefix(chs.into_vec()),
980                HNode::Space | HNode::Hss => (),
981                HNode::Custom(RusTeXNode::PGFEscape(bx @ TeXBox::H { .. })) => {
982                    let _ = bx.height();
983                    let _ = bx.width();
984                    let _ = bx.depth();
985                    let TeXBox::H {
986                        children,
987                        start,
988                        end,
989                        info,
990                        preskip,
991                        ..
992                    } = bx
993                    else {
994                        unreachable!()
995                    };
996                    self.in_h(start, end, info, preskip, |state| {
997                        state.do_hlist(&mut children.into())
998                    })
999                    .map_err(|_| None)?
1000                }
1001                HNode::Custom(RusTeXNode::PGFEscape(bx @ TeXBox::V { .. })) => {
1002                    let _ = bx.height();
1003                    let _ = bx.width();
1004                    let _ = bx.depth();
1005                    let TeXBox::V {
1006                        children,
1007                        start,
1008                        end,
1009                        info,
1010                        ..
1011                    } = bx
1012                    else {
1013                        unreachable!()
1014                    };
1015                    self.in_v(start, end, info, |state| {
1016                        state.do_vlist(&mut children.into())
1017                    })
1018                    .map_err(|_| None)?
1019                }
1020                HNode::MathGroup(ref mg) => {
1021                    //TODO should not happen
1022                    let bx = HBoxInfo::new_box(ToOrSpread::None);
1023                    let MathGroup { start, end, .. } = mg;
1024                    let start = start.clone();
1025                    let end = end.clone();
1026                    let children = vec![c];
1027                    self.in_h(start, end, bx, None, |state| {
1028                        state.do_hlist(&mut children.into())
1029                    })
1030                    .map_err(|_| None)?
1031                }
1032                HNode::Char { .. } => (), // TODO?
1033                _ => todo!("{c:?}"),
1034            }
1035        }
1036        Ok(())
1037    }
1038}
1039
1040fn get_page_inner(children: Vec<VNode<Types>>) -> Vec<VNode<Types>> {
1041    let mut ret = Vec::new();
1042    let mut list: VNodes = children.into();
1043    while let Some(c) = list.next() {
1044        match c {
1045            VNode::Box(TeXBox::V { children, .. })
1046                if children
1047                    .iter()
1048                    .any(|n| matches!(n, VNode::Custom(RusTeXNode::PageBegin))) =>
1049            {
1050                ret.extend(children.into_vec().into_iter().filter(|p| {
1051                    !matches!(
1052                        p,
1053                        VNode::Custom(RusTeXNode::PageBegin | RusTeXNode::PageEnd)
1054                    )
1055                }))
1056            }
1057            VNode::Box(TeXBox::V { children, .. }) => list.prefix(children.into_vec()),
1058            VNode::Box(TeXBox::H { children, .. }) if hbox_works(&children) => {
1059                get_page_hbox(children, &mut ret, &mut list)
1060            }
1061            VNode::VSkip(_)
1062            | VNode::VKern(_)
1063            | VNode::VFil
1064            | VNode::VFill
1065            | VNode::VFilneg
1066            | VNode::Vss
1067            | VNode::Penalty(_)
1068            | VNode::Mark(..) => (),
1069            VNode::Custom(RusTeXNode::PDFNode(
1070                PDFNode::PDFDest(..)
1071                | PDFNode::PDFCatalog(_)
1072                | PDFNode::PDFLiteral(_)
1073                | PDFNode::XForm(..)
1074                | PDFNode::Obj(..)
1075                | PDFNode::PDFOutline(_),
1076            )) => (),
1077            VNode::Custom(RusTeXNode::PageBegin) => {
1078                for c in list.by_ref() {
1079                    if let VNode::Custom(RusTeXNode::PageEnd) = c {
1080                        break;
1081                    } else {
1082                        ret.push(c)
1083                    }
1084                }
1085            }
1086            _ => ret.push(c),
1087        }
1088    }
1089    ret
1090}
1091
1092fn get_page_hbox(children: Box<[HNode<Types>]>, ret: &mut Vec<VNode<Types>>, list: &mut VNodes) {
1093    for c in children.into_vec().into_iter() {
1094        match c {
1095            HNode::HSkip(_)
1096            | HNode::Hss
1097            | HNode::Space
1098            | HNode::HKern(_)
1099            | HNode::HFil
1100            | HNode::HFill
1101            | HNode::HFilneg
1102            | HNode::Penalty(_)
1103            | HNode::Mark(_, _)
1104            | HNode::Custom(RusTeXNode::PDFNode(
1105                PDFNode::PDFDest(..)
1106                | PDFNode::PDFCatalog(_)
1107                | PDFNode::PDFLiteral(_)
1108                | PDFNode::XForm(..)
1109                | PDFNode::Obj(..)
1110                | PDFNode::PDFOutline(_),
1111            )) => (),
1112            HNode::Custom(
1113                n @ (RusTeXNode::PDFNode(PDFNode::Color(_))
1114                | RusTeXNode::FontChange(..)
1115                | RusTeXNode::FontChangeEnd),
1116            ) => ret.push(VNode::Custom(n)),
1117            HNode::Box(TeXBox::H { children, .. }) if hbox_works(&children) => {
1118                get_page_hbox(children, ret, list)
1119            }
1120            HNode::Box(b) => ret.push(VNode::Box(b)), //list.prefix(vec![VNode::Box(b)]),
1121            _ => unreachable!(),
1122        }
1123    }
1124}
1125
1126fn hbox_works(children: &[HNode<Types>]) -> bool {
1127    children.iter().all(|n| {
1128        matches!(
1129            n,
1130            HNode::HSkip(_)
1131                | HNode::Hss
1132                | HNode::Space
1133                | HNode::HKern(_)
1134                | HNode::HFil
1135                | HNode::HFill
1136                | HNode::HFilneg
1137                | HNode::Penalty(_)
1138                | HNode::Mark(_, _)
1139                | HNode::Custom(RusTeXNode::PDFNode(
1140                    PDFNode::PDFDest(..)
1141                        | PDFNode::PDFCatalog(_)
1142                        | PDFNode::PDFLiteral(_)
1143                        | PDFNode::XForm(..)
1144                        | PDFNode::Obj(..)
1145                        | PDFNode::PDFOutline(_)
1146                ))
1147                //| HNode::Custom(RusTeXNode::PDFNode(PDFNode::Color(_)))
1148                //| HNode::Custom(RusTeXNode::FontChange(..) | RusTeXNode::FontChangeEnd)
1149                | HNode::Box(TeXBox::H { .. })
1150                | HNode::Box(TeXBox::V { .. })
1151        )
1152    })
1153}