mirror of
https://github.com/typst/typst
synced 2025-07-27 14:27:56 +08:00
feat: insert footnotes after the reference in the reading order
This commit is contained in:
parent
bf75ab858d
commit
bc09df0c8b
@ -1,4 +1,5 @@
|
|||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
@ -18,8 +19,8 @@ use typst_library::foundations::{
|
|||||||
use typst_library::introspection::Location;
|
use typst_library::introspection::Location;
|
||||||
use typst_library::layout::RepeatElem;
|
use typst_library::layout::RepeatElem;
|
||||||
use typst_library::model::{
|
use typst_library::model::{
|
||||||
Destination, EnumElem, FigureCaption, FigureElem, FootnoteEntry, HeadingElem,
|
Destination, EnumElem, FigureCaption, FigureElem, FootnoteElem, FootnoteEntry,
|
||||||
ListElem, Outlinable, OutlineEntry, TableCell, TableElem, TermsElem,
|
HeadingElem, ListElem, Outlinable, OutlineEntry, TableCell, TableElem, TermsElem,
|
||||||
};
|
};
|
||||||
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfMarkerTag, PdfMarkerTagKind};
|
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfMarkerTag, PdfMarkerTagKind};
|
||||||
use typst_library::visualize::ImageElem;
|
use typst_library::visualize::ImageElem;
|
||||||
@ -142,8 +143,13 @@ pub(crate) fn handle_start(
|
|||||||
let link_id = gc.tags.next_link_id();
|
let link_id = gc.tags.next_link_id();
|
||||||
push_stack(gc, loc, StackEntryKind::Link(link_id, link.clone()))?;
|
push_stack(gc, loc, StackEntryKind::Link(link_id, link.clone()))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if let Some(_) = elem.to_packed::<FootnoteEntry>() {
|
} else if let Some(_) = elem.to_packed::<FootnoteElem>() {
|
||||||
TagKind::Note.into()
|
push_stack(gc, loc, StackEntryKind::FootNoteRef)?;
|
||||||
|
return Ok(());
|
||||||
|
} else if let Some(entry) = elem.to_packed::<FootnoteEntry>() {
|
||||||
|
let footnote_loc = entry.note.location().unwrap();
|
||||||
|
push_stack(gc, loc, StackEntryKind::FootNoteEntry(footnote_loc))?;
|
||||||
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@ -217,6 +223,19 @@ pub(crate) fn handle_end(gc: &mut GlobalContext, surface: &mut Surface, loc: Loc
|
|||||||
}
|
}
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
StackEntryKind::FootNoteRef => {
|
||||||
|
// transparently inset all children.
|
||||||
|
gc.tags.extend(entry.nodes);
|
||||||
|
gc.tags.push(TagNode::FootnoteEntry(loc));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StackEntryKind::FootNoteEntry(footnote_loc) => {
|
||||||
|
// Store footnotes separately so they can be inserted directly after
|
||||||
|
// the footnote reference in the reading order.
|
||||||
|
let tag = TagNode::Group(TagKind::Note.into(), entry.nodes);
|
||||||
|
gc.tags.footnotes.insert(footnote_loc, tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
gc.tags.push(node);
|
gc.tags.push(node);
|
||||||
@ -294,6 +313,11 @@ pub(crate) struct Tags {
|
|||||||
pub(crate) stack: TagStack,
|
pub(crate) stack: TagStack,
|
||||||
/// A list of placeholders corresponding to a [`TagNode::Placeholder`].
|
/// A list of placeholders corresponding to a [`TagNode::Placeholder`].
|
||||||
pub(crate) placeholders: Placeholders,
|
pub(crate) placeholders: Placeholders,
|
||||||
|
/// Footnotes are inserted directly after the footenote reference in the
|
||||||
|
/// reading order. Because of some layouting bugs, the entry might appear
|
||||||
|
/// before the reference in the text, so we only resolve them once tags
|
||||||
|
/// for the whole document are generated.
|
||||||
|
pub(crate) footnotes: HashMap<Location, TagNode>,
|
||||||
pub(crate) in_artifact: Option<(Location, ArtifactKind)>,
|
pub(crate) in_artifact: Option<(Location, ArtifactKind)>,
|
||||||
/// Used to group multiple link annotations using quad points.
|
/// Used to group multiple link annotations using quad points.
|
||||||
pub(crate) link_id: LinkId,
|
pub(crate) link_id: LinkId,
|
||||||
@ -310,11 +334,13 @@ impl Tags {
|
|||||||
Self {
|
Self {
|
||||||
stack: TagStack(Vec::new()),
|
stack: TagStack(Vec::new()),
|
||||||
placeholders: Placeholders(Vec::new()),
|
placeholders: Placeholders(Vec::new()),
|
||||||
|
footnotes: HashMap::new(),
|
||||||
in_artifact: None,
|
in_artifact: None,
|
||||||
|
|
||||||
tree: Vec::new(),
|
|
||||||
link_id: LinkId(0),
|
link_id: LinkId(0),
|
||||||
table_id: TableId(0),
|
table_id: TableId(0),
|
||||||
|
|
||||||
|
tree: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,6 +352,14 @@ impl Tags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn extend(&mut self, nodes: impl IntoIterator<Item = TagNode>) {
|
||||||
|
if let Some(entry) = self.stack.last_mut() {
|
||||||
|
entry.nodes.extend(nodes);
|
||||||
|
} else {
|
||||||
|
self.tree.extend(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn build_tree(&mut self) -> TagTree {
|
pub(crate) fn build_tree(&mut self) -> TagTree {
|
||||||
let children = std::mem::take(&mut self.tree)
|
let children = std::mem::take(&mut self.tree)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -346,6 +380,10 @@ impl Tags {
|
|||||||
}
|
}
|
||||||
TagNode::Leaf(identifier) => Node::Leaf(identifier),
|
TagNode::Leaf(identifier) => Node::Leaf(identifier),
|
||||||
TagNode::Placeholder(placeholder) => self.placeholders.take(placeholder),
|
TagNode::Placeholder(placeholder) => self.placeholders.take(placeholder),
|
||||||
|
TagNode::FootnoteEntry(loc) => {
|
||||||
|
let node = self.footnotes.remove(&loc).expect("footnote");
|
||||||
|
self.resolve_node(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,6 +496,11 @@ pub(crate) enum StackEntryKind {
|
|||||||
ListItemLabel,
|
ListItemLabel,
|
||||||
ListItemBody,
|
ListItemBody,
|
||||||
Link(LinkId, Packed<LinkMarker>),
|
Link(LinkId, Packed<LinkMarker>),
|
||||||
|
/// The footnote reference in the text.
|
||||||
|
FootNoteRef,
|
||||||
|
/// The footnote entry at the end of the page. Contains the [`Location`] of
|
||||||
|
/// the [`FootnoteElem`](typst_library::model::FootnoteElem).
|
||||||
|
FootNoteEntry(Location),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackEntryKind {
|
impl StackEntryKind {
|
||||||
@ -509,6 +552,7 @@ pub(crate) enum TagNode {
|
|||||||
/// Allows inserting a placeholder into the tag tree.
|
/// Allows inserting a placeholder into the tag tree.
|
||||||
/// Currently used for [`krilla::page::Page::add_tagged_annotation`].
|
/// Currently used for [`krilla::page::Page::add_tagged_annotation`].
|
||||||
Placeholder(Placeholder),
|
Placeholder(Placeholder),
|
||||||
|
FootnoteEntry(Location),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user