Skip to main content

fleuron/layout/
note.rs

1//! Footnotes: what each note is numbered, what its body comes to,
2//! and the area at the foot of the page its body is set in.
3//!
4//! A note is written in the flow and set under the column its
5//! reference lands in. The area grows as the column takes notes, and
6//! the room the column has for lines shrinks with it, so a line that
7//! no longer fits moves on and takes its note with it.
8//!
9//! A note whose body outruns the room the column has left is split.
10//! The rest of it opens the area of the next column, above the notes
11//! that column takes for itself, and carries no reference of its own.
12//!
13//! A page that does not divide has one column, so its area is the
14//! width of its content box.
15
16use std::collections::BTreeMap;
17use std::sync::Arc;
18
19use crate::content::{Block, Book, Inline, NodeId, block_id, cell_blocks, notes_in_inlines};
20use crate::pages::{DrawItem, PageBox};
21use crate::style::{ComputedStyle, StyleTree};
22
23use super::Paginator;
24use super::build::{Builder, children, decorate};
25use super::flow::{Flow, Placed};
26use super::fragment::{Fragment, Marker};
27
28/// What a book whose notes are numbered by page says when the
29/// numbering does not settle.
30pub(crate) const UNSETTLED: &str = "The notes are numbered by page and the numbering did not \
31                                    settle. The numbers are the ones of the last layout.";
32
33/// One note the flow sets at the foot of a page: what it is
34/// numbered, and its body as fragments of the footnote area.
35///
36/// The lines that hold its reference carry it, so a line moved to
37/// the next page moves the note with it.
38#[derive(Debug)]
39pub struct Note {
40    /// The note element.
41    pub node: NodeId,
42    /// The number its reference prints.
43    pub number: u32,
44    /// Its body, broken to the measure of the area.
45    pub fragments: Vec<Fragment>,
46}
47
48impl Note {
49    /// The height of the fragments from `from`, the space above each
50    /// one included. `under` is whether anything stands above the
51    /// note in the area: the note that opens one drops the space
52    /// above it.
53    pub(super) fn height(&self, from: usize, under: bool) -> f32 {
54        (from..self.fragments.len())
55            .map(|index| self.step(index, under || index > from))
56            .sum()
57    }
58
59    /// How far one fragment of the body takes the area down: its own
60    /// height, and the space above it where anything is set above it.
61    pub(super) fn step(&self, index: usize, under: bool) -> f32 {
62        let fragment = &self.fragments[index];
63        let lead = if under { fragment.lead } else { 0.0 };
64        lead + fragment.fixed + fragment.height
65    }
66}
67
68/// What the notes of a book are numbered.
69///
70/// The counter runs through the book, and `counter-reset: note`
71/// restarts it: on a section, every chapter starts again; on the
72/// footnote area, every page does.
73#[derive(Debug, Clone, Default, PartialEq, Eq)]
74pub(crate) struct Numbering {
75    numbers: BTreeMap<NodeId, u32>,
76}
77
78impl Numbering {
79    /// The numbers the notes of `book` take, read in document order
80    /// with the restarts the cascade asks for.
81    pub(crate) fn of(book: &Book, styles: &StyleTree) -> Numbering {
82        let mut numbering = Numbering::default();
83        let mut next = 1;
84        for section in &book.sections {
85            numbering.restart(&mut next, styles.style(section.id));
86            numbering.blocks(&section.blocks, styles, &mut next);
87        }
88        numbering
89    }
90
91    /// The same numbers, restarted on every page: `pages` says which
92    /// page each note was set on, and `start` is the number the first
93    /// note on a page takes.
94    pub(crate) fn on_pages(&self, pages: &BTreeMap<NodeId, u32>, start: u32) -> Numbering {
95        let mut numbers = BTreeMap::new();
96        let mut page = None;
97        let mut next = start;
98        // The map is in node order, which is the order the notes were
99        // written, and a page takes its notes in that order too.
100        for (node, at) in pages {
101            if page != Some(*at) {
102                page = Some(*at);
103                next = start;
104            }
105            numbers.insert(*node, next);
106            next += 1;
107        }
108        Numbering { numbers }
109    }
110
111    /// What one note's reference prints. A note the numbering has
112    /// never seen is the first of its own.
113    pub(crate) fn number(&self, node: NodeId) -> u32 {
114        self.numbers.get(&node).copied().unwrap_or(1)
115    }
116
117    fn blocks(&mut self, blocks: &[Block], styles: &StyleTree, next: &mut u32) {
118        for block in blocks {
119            self.restart(next, styles.style(block_id(block)));
120            match block {
121                Block::Heading { inlines, .. } | Block::Paragraph { inlines, .. } => {
122                    self.inlines(inlines, styles, next)
123                }
124                Block::Blockquote { blocks, .. } => self.blocks(blocks, styles, next),
125                Block::List { items, .. } => {
126                    for item in items {
127                        self.restart(next, styles.style(item.id));
128                        self.blocks(&item.blocks, styles, next);
129                    }
130                }
131                Block::Table { head, body, .. } => {
132                    for blocks in cell_blocks(head, body) {
133                        self.blocks(blocks, styles, next);
134                    }
135                }
136                Block::CodeBlock { .. }
137                | Block::ThematicBreak { .. }
138                | Block::PageBreak { .. }
139                | Block::ColumnBreak { .. }
140                | Block::Image { .. } => {}
141            }
142        }
143    }
144
145    /// The same over the notes written among one block's inlines. A
146    /// note written inside another takes the number after it.
147    fn inlines(&mut self, inlines: &[Inline], styles: &StyleTree, next: &mut u32) {
148        for note in notes_in_inlines(inlines) {
149            let Inline::Note { id, blocks, .. } = note else {
150                continue;
151            };
152            self.numbers.insert(*id, *next);
153            *next += 1;
154            self.blocks(blocks, styles, next);
155        }
156    }
157
158    fn restart(&mut self, next: &mut u32, style: &ComputedStyle) {
159        if let Some(number) = style.note_reset {
160            *next = number;
161        }
162    }
163}
164
165impl StyleTree {
166    /// Whether the notes of the book are numbered by the page they
167    /// are set on, which the footnote area asks for by restarting the
168    /// counter on itself.
169    pub(crate) fn numbers_notes_per_page(&self) -> bool {
170        self.style(self.notes_area()).note_reset.is_some()
171    }
172
173    /// The number the first note of a page takes.
174    pub(crate) fn first_note_number(&self) -> u32 {
175        self.style(self.notes_area()).note_reset.unwrap_or(1)
176    }
177}
178
179impl Paginator<'_> {
180    /// What one note's reference prints.
181    pub(super) fn note_number(&self, node: NodeId) -> u32 {
182        self.notes.borrow().number(node)
183    }
184
185    /// One note as the flow can set it: its body broken to the
186    /// measure of the footnote area, with its number hanging before
187    /// its first line.
188    pub(super) fn note(&self, note: &Inline, source: Option<&str>) -> Option<Arc<Note>> {
189        let Inline::Note {
190            id,
191            blocks,
192            position,
193            ..
194        } = note
195        else {
196            return None;
197        };
198        let number = self.note_number(*id);
199        let (x, measure) = self.note_measure();
200        let style = self.styles.style(*id).clone();
201        let mut builder = Builder::new(self, source);
202        let start = builder.open(*id, &style, &[], x, measure);
203        let (inner, narrowed) = style.content_box(x, measure);
204        builder.blocks(
205            children(self.styles, *id, blocks, *position),
206            inner,
207            narrowed,
208        );
209        if let Some(marker) = self.note_marker(&style, number, x, measure) {
210            builder.hang(start, marker);
211        }
212        builder.close(&style, start);
213        if builder
214            .fragments
215            .iter()
216            .any(|fragment| fragment.notes.is_some())
217        {
218            self.warn(
219                "A note was written inside another note. The note inside is left out.".to_string(),
220                None,
221            );
222        }
223        Some(Arc::new(Note {
224            node: *id,
225            number,
226            fragments: builder.fragments,
227        }))
228    }
229
230    /// Where the notes of a column start, and how wide they are set.
231    pub(super) fn note_measure(&self) -> (f32, f32) {
232        self.area_style().content_box(0.0, self.note_width())
233    }
234
235    /// How wide the area is: the measure of a column, which is the
236    /// content box of a page that divides into one.
237    pub(super) fn note_width(&self) -> f32 {
238        self.styles.default_page().geometry.measure()
239    }
240
241    /// The number the note is set beside, shaped in the note's own
242    /// style. It hangs in the indent to the left of the note, the way
243    /// the marker of a list item does.
244    fn note_marker(
245        &self,
246        style: &ComputedStyle,
247        number: u32,
248        x: f32,
249        measure: f32,
250    ) -> Option<Marker> {
251        let text = style.list_style_type.marker(number)?;
252        let line = self.line_of(&text, &style.paragraph())?;
253        let (left, _) = style.content_box(x, measure);
254        let x = left - self.line_width(&line);
255        Some(Marker { line, x })
256    }
257
258    /// What the footnote area is styled by.
259    pub(super) fn area_style(&self) -> &ComputedStyle {
260        self.styles.style(self.styles.notes_area())
261    }
262
263    /// Takes what the notes of the book are numbered. `paginate`
264    /// reads it from the book it is handed. A caller that builds one
265    /// section's fragments on its own sets it here.
266    pub(crate) fn number(&self, numbering: Numbering) {
267        *self.notes.borrow_mut() = numbering;
268    }
269}
270
271/// The area one page sets its notes in: where the notes go, and what
272/// the box around them paints.
273pub(super) struct Area {
274    /// Top of the area's border box, from the content box's top.
275    pub(super) top: f32,
276    /// What the whole area takes, the box's own edges included.
277    pub(super) height: f32,
278    /// The fragments set in it: the note each one is of, which of its
279    /// fragments it is, and its top from the top of the area's
280    /// content box.
281    pub(super) placed: Vec<(f32, Arc<Note>, usize)>,
282    /// The notes the page could not set, each with the fragment of it
283    /// the next page opens with.
284    pub(super) left: Vec<(Arc<Note>, usize)>,
285}
286
287/// What the area's own box takes above and below the notes in it.
288pub(super) fn area_edges(style: &ComputedStyle) -> (f32, f32) {
289    let border = style.border.widths();
290    (
291        style.margin.top + border.top + style.padding.top,
292        style.margin.bottom + border.bottom + style.padding.bottom,
293    )
294}
295
296impl Paginator<'_> {
297    /// What the notes of one page come to: the area they fill from
298    /// the foot of the content box upwards, and the note the page
299    /// could not finish.
300    ///
301    /// `foot` is where the content of the page ends and `height` is
302    /// the content box. Notes are set in the order their references
303    /// were, from the first fragment each one still owes, and the
304    /// area takes as many as the room left holds.
305    pub(super) fn area(
306        &self,
307        notes: &[(Arc<Note>, usize)],
308        foot: f32,
309        height: f32,
310    ) -> Option<Area> {
311        if notes.is_empty() {
312            return None;
313        }
314        let style = self.area_style();
315        let (above, below) = area_edges(style);
316        let room = height - foot - above - below;
317        let mut placed: Vec<(f32, Arc<Note>, usize)> = Vec::new();
318        let mut cursor = 0.0f32;
319        let mut owed: Vec<(Arc<Note>, usize)> = Vec::new();
320        for (note, from) in notes {
321            if !owed.is_empty() {
322                owed.push((note.clone(), *from));
323                continue;
324            }
325            let mut at = *from;
326            while at < note.fragments.len() {
327                let step = note.step(at, !placed.is_empty());
328                // The area takes one fragment whatever room is left,
329                // the way a fragment taller than a page is set on it
330                // and overflows it.
331                if cursor + step > room && !placed.is_empty() {
332                    break;
333                }
334                cursor += step;
335                placed.push((cursor - note.fragments[at].height, note.clone(), at));
336                at += 1;
337            }
338            if at < note.fragments.len() {
339                owed.push((note.clone(), at));
340            }
341        }
342        if placed.is_empty() {
343            return Some(Area {
344                top: height,
345                height: 0.0,
346                placed,
347                left: owed,
348            });
349        }
350        let outer = above + cursor + below;
351        Some(Area {
352            top: (height - outer).max(foot),
353            height: outer,
354            placed,
355            left: owed,
356        })
357    }
358
359    /// What one page's area paints, in page coordinates, and the
360    /// border box every block of it takes there. `origin` is the
361    /// page's content box.
362    pub(super) fn area_items(
363        &self,
364        area: &Area,
365        origin: (f32, f32),
366    ) -> (Vec<DrawItem>, Vec<(NodeId, PageBox)>) {
367        let style = self.area_style();
368        let (above, _) = area_edges(style);
369        let (x, _) = self.note_measure();
370        let (left, width) = style.border_box(0.0, self.note_width());
371        let top = area.top + style.margin.top;
372        let height = (area.height - style.margin.top - style.margin.bottom).max(0.0);
373        let ink = |edge: crate::style::Border| edge.color.unwrap_or(style.color);
374        let mut items = super::flow::box_items(
375            origin.0 + left,
376            origin.1 + top,
377            width,
378            height,
379            style.border_radius.resolve(width, height),
380            style.border.widths(),
381            crate::style::Edges {
382                top: ink(style.border.top),
383                right: ink(style.border.right),
384                bottom: ink(style.border.bottom),
385                left: ink(style.border.left),
386            },
387            &self.backdrop(&style.background),
388            style.z_index,
389        );
390        let inner = (origin.0 + x, origin.1 + area.top + above);
391        let placed: Vec<(f32, &Fragment)> = area
392            .placed
393            .iter()
394            .map(|(at, note, index)| (*at, &note.fragments[*index]))
395            .collect();
396        let (mut boxes, mut areas) = decorate(&placed);
397        super::flow::shift(&mut boxes, inner.0, inner.1);
398        super::flow::shift_boxes(&mut areas, inner.0, inner.1);
399        items.append(&mut boxes);
400        for (at, fragment) in placed {
401            items.append(&mut self.fragment_items(fragment, inner.0, inner.1 + at));
402        }
403        (items, areas)
404    }
405}
406
407impl Flow<'_, '_> {
408    /// Where the column being filled has to stop: the content box,
409    /// less what the notes of that column need at the foot of it.
410    /// `fragment` is the one about to be placed, whose own notes join
411    /// them.
412    ///
413    /// This is the push-back: a note makes the column shorter, and a
414    /// line that no longer fits under it moves on and takes the note
415    /// its reference holds with it.
416    pub(super) fn room(&self, fragment: &Fragment) -> f32 {
417        let coming = fragment.notes.as_deref().map(Vec::as_slice).unwrap_or(&[]);
418        let owed = self.owed(self.column);
419        if owed.is_empty() && coming.is_empty() {
420            return self.height;
421        }
422        let (above, below) = area_edges(self.paginator.area_style());
423        let mut wanted = above + below;
424        for (index, (note, from)) in owed.iter().enumerate() {
425            wanted += note.height(*from, index > 0);
426        }
427        let under = !owed.is_empty();
428        for (index, note) in coming.iter().enumerate() {
429            wanted += note.height(0, under || index > 0);
430        }
431        (self.height - wanted).max(0.0)
432    }
433
434    /// The notes one column of the page being built owes, in the
435    /// order their references were set.
436    fn owed(&self, column: u32) -> Vec<(Arc<Note>, usize)> {
437        self.notes
438            .iter()
439            .filter(|(at, _, _)| *at == column)
440            .map(|(_, note, from)| (note.clone(), *from))
441            .collect()
442    }
443
444    /// What the notes of the page being closed come to: one area
445    /// under each column that owes any, with `placed` the fragments
446    /// on the page.
447    ///
448    /// A column sets its notes under the foot of its own text. What
449    /// one column cannot hold is set under the next, and what the
450    /// last of them cannot hold opens the next page.
451    pub(super) fn notes_areas(&self, placed: &[Placed]) -> Vec<(u32, Area)> {
452        let mut areas = Vec::new();
453        let mut left: Vec<(Arc<Note>, usize)> = Vec::new();
454        for column in 0..self.columns.max(1) {
455            let mut owed = std::mem::take(&mut left);
456            owed.append(&mut self.owed(column));
457            if owed.is_empty() {
458                continue;
459            }
460            let foot = placed
461                .iter()
462                .filter(|placed| placed.column == column)
463                .map(|placed| placed.top + placed.height)
464                .fold(0.0, f32::max);
465            let Some(area) = self.paginator.area(&owed, foot, self.height) else {
466                continue;
467            };
468            left = area.left.clone();
469            areas.push((column, area));
470        }
471        areas
472    }
473
474    /// Records the page the references of its notes were set on, and
475    /// hands what the page could not set to the next one.
476    pub(super) fn notes_closed(&mut self, areas: &[(u32, Area)]) {
477        let page = self.pages.len() as u32;
478        for (_, note, from) in &self.notes {
479            if *from == 0 {
480                self.note_pages.insert(note.node, page);
481            }
482        }
483        self.notes = areas
484            .last()
485            .map(|(_, area)| area.left.clone())
486            .unwrap_or_default()
487            .into_iter()
488            .map(|(note, from)| (0, note, from))
489            .collect();
490    }
491}