mirror of
https://github.com/typst/typst
synced 2025-07-27 14:27:56 +08:00
feat: generate tags for lists and enums
This commit is contained in:
parent
a495724813
commit
eb9a3359d5
@ -7,6 +7,7 @@ use typst_library::introspection::Locator;
|
|||||||
use typst_library::layout::grid::resolve::{Cell, CellGrid};
|
use typst_library::layout::grid::resolve::{Cell, CellGrid};
|
||||||
use typst_library::layout::{Axes, Fragment, HAlignment, Regions, Sizing, VAlignment};
|
use typst_library::layout::{Axes, Fragment, HAlignment, Regions, Sizing, VAlignment};
|
||||||
use typst_library::model::{EnumElem, ListElem, Numbering, ParElem, ParbreakElem};
|
use typst_library::model::{EnumElem, ListElem, Numbering, ParElem, ParbreakElem};
|
||||||
|
use typst_library::pdf::PdfMarkerTag;
|
||||||
use typst_library::text::TextElem;
|
use typst_library::text::TextElem;
|
||||||
|
|
||||||
use crate::grid::GridLayouter;
|
use crate::grid::GridLayouter;
|
||||||
@ -48,12 +49,16 @@ pub fn layout_list(
|
|||||||
if !tight {
|
if !tight {
|
||||||
body += ParbreakElem::shared();
|
body += ParbreakElem::shared();
|
||||||
}
|
}
|
||||||
|
let body = body.set(ListElem::depth, Depth(1));
|
||||||
|
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
|
cells.push(Cell::new(
|
||||||
|
PdfMarkerTag::ListItemLabel(marker.clone()),
|
||||||
|
locator.next(&marker.span()),
|
||||||
|
));
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::new(
|
cells.push(Cell::new(
|
||||||
body.set(ListElem::depth, Depth(1)),
|
PdfMarkerTag::ListItemBody(body),
|
||||||
locator.next(&item.body.span()),
|
locator.next(&item.body.span()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -142,11 +147,13 @@ pub fn layout_enum(
|
|||||||
body += ParbreakElem::shared();
|
body += ParbreakElem::shared();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let body = body.set(EnumElem::parents, smallvec![number]);
|
||||||
|
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::new(resolved, locator.next(&())));
|
cells.push(Cell::new(PdfMarkerTag::ListItemLabel(resolved), locator.next(&())));
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::new(
|
cells.push(Cell::new(
|
||||||
body.set(EnumElem::parents, smallvec![number]),
|
PdfMarkerTag::ListItemBody(body),
|
||||||
locator.next(&item.body.span()),
|
locator.next(&item.body.span()),
|
||||||
));
|
));
|
||||||
number =
|
number =
|
||||||
|
@ -20,11 +20,12 @@ use typst_library::math::EquationElem;
|
|||||||
use typst_library::model::{
|
use typst_library::model::{
|
||||||
Attribution, BibliographyElem, CiteElem, CiteGroup, CslSource, Destination, EmphElem,
|
Attribution, BibliographyElem, CiteElem, CiteGroup, CslSource, Destination, EmphElem,
|
||||||
EnumElem, FigureCaption, FigureElem, FootnoteElem, FootnoteEntry, HeadingElem,
|
EnumElem, FigureCaption, FigureElem, FootnoteElem, FootnoteEntry, HeadingElem,
|
||||||
LinkElem, LinkTarget, ListElem, Outlinable, OutlineBody, OutlineElem, OutlineEntry,
|
LinkElem, LinkTarget, ListElem, Outlinable, OutlineElem, OutlineEntry, ParElem,
|
||||||
ParElem, ParbreakElem, QuoteElem, RefElem, StrongElem, TableCell, TableElem,
|
ParbreakElem, QuoteElem, RefElem, StrongElem, TableCell, TableElem, TermsElem, Works,
|
||||||
TermsElem, Works,
|
};
|
||||||
|
use typst_library::pdf::{
|
||||||
|
ArtifactElem, EmbedElem, PdfMarkerTag, PdfMarkerTagKind, PdfTagElem,
|
||||||
};
|
};
|
||||||
use typst_library::pdf::{ArtifactElem, EmbedElem, PdfTagElem};
|
|
||||||
use typst_library::text::{
|
use typst_library::text::{
|
||||||
DecoLine, Decoration, HighlightElem, ItalicToggle, LinebreakElem, LocalName,
|
DecoLine, Decoration, HighlightElem, ItalicToggle, LinebreakElem, LocalName,
|
||||||
OverlineElem, RawElem, RawLine, ScriptKind, ShiftSettings, Smallcaps, SmallcapsElem,
|
OverlineElem, RawElem, RawLine, ScriptKind, ShiftSettings, Smallcaps, SmallcapsElem,
|
||||||
@ -56,7 +57,6 @@ pub fn register(rules: &mut NativeRuleMap) {
|
|||||||
rules.register(Paged, FOOTNOTE_RULE);
|
rules.register(Paged, FOOTNOTE_RULE);
|
||||||
rules.register(Paged, FOOTNOTE_ENTRY_RULE);
|
rules.register(Paged, FOOTNOTE_ENTRY_RULE);
|
||||||
rules.register(Paged, OUTLINE_RULE);
|
rules.register(Paged, OUTLINE_RULE);
|
||||||
rules.register(Paged, OUTLINE_BODY_RULE);
|
|
||||||
rules.register(Paged, OUTLINE_ENTRY_RULE);
|
rules.register(Paged, OUTLINE_ENTRY_RULE);
|
||||||
rules.register(Paged, REF_RULE);
|
rules.register(Paged, REF_RULE);
|
||||||
rules.register(Paged, CITE_GROUP_RULE);
|
rules.register(Paged, CITE_GROUP_RULE);
|
||||||
@ -108,6 +108,7 @@ pub fn register(rules: &mut NativeRuleMap) {
|
|||||||
rules.register(Paged, EMBED_RULE);
|
rules.register(Paged, EMBED_RULE);
|
||||||
rules.register(Paged, PDF_TAG_RULE);
|
rules.register(Paged, PDF_TAG_RULE);
|
||||||
rules.register(Paged, PDF_ARTIFACT_RULE);
|
rules.register(Paged, PDF_ARTIFACT_RULE);
|
||||||
|
rules.register(Paged, PDF_MARKER_TAG_RULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const STRONG_RULE: ShowFn<StrongElem> = |elem, _, styles| {
|
const STRONG_RULE: ShowFn<StrongElem> = |elem, _, styles| {
|
||||||
@ -466,13 +467,14 @@ const OUTLINE_RULE: ShowFn<OutlineElem> = |elem, engine, styles| {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the entries into a marker for pdf tagging.
|
// Wrap the entries into a marker for pdf tagging.
|
||||||
seq.push(OutlineBody::new(Content::sequence(entries)).pack());
|
seq.push(
|
||||||
|
PdfMarkerTag::new(PdfMarkerTagKind::OutlineBody, Content::sequence(entries))
|
||||||
|
.pack(),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Content::sequence(seq))
|
Ok(Content::sequence(seq))
|
||||||
};
|
};
|
||||||
|
|
||||||
const OUTLINE_BODY_RULE: ShowFn<OutlineBody> = |elem, _, _| Ok(elem.body.clone());
|
|
||||||
|
|
||||||
const OUTLINE_ENTRY_RULE: ShowFn<OutlineEntry> = |elem, engine, styles| {
|
const OUTLINE_ENTRY_RULE: ShowFn<OutlineEntry> = |elem, engine, styles| {
|
||||||
let span = elem.span();
|
let span = elem.span();
|
||||||
let context = Context::new(None, Some(styles));
|
let context = Context::new(None, Some(styles));
|
||||||
@ -931,3 +933,5 @@ const EMBED_RULE: ShowFn<EmbedElem> = |_, _, _| Ok(Content::empty());
|
|||||||
const PDF_TAG_RULE: ShowFn<PdfTagElem> = |elem, _, _| Ok(elem.body.clone());
|
const PDF_TAG_RULE: ShowFn<PdfTagElem> = |elem, _, _| Ok(elem.body.clone());
|
||||||
|
|
||||||
const PDF_ARTIFACT_RULE: ShowFn<ArtifactElem> = |elem, _, _| Ok(elem.body.clone());
|
const PDF_ARTIFACT_RULE: ShowFn<ArtifactElem> = |elem, _, _| Ok(elem.body.clone());
|
||||||
|
|
||||||
|
const PDF_MARKER_TAG_RULE: ShowFn<PdfMarkerTag> = |elem, _, _| Ok(elem.body.clone());
|
||||||
|
@ -266,13 +266,6 @@ impl LocalName for Packed<OutlineElem> {
|
|||||||
const KEY: &'static str = "outline";
|
const KEY: &'static str = "outline";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only used to delimit the outline in tagged PDF.
|
|
||||||
#[elem(Locatable)]
|
|
||||||
pub struct OutlineBody {
|
|
||||||
#[required]
|
|
||||||
pub body: Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines how an outline is indented.
|
/// Defines how an outline is indented.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub enum OutlineIndent {
|
pub enum OutlineIndent {
|
||||||
|
@ -262,3 +262,43 @@ impl TableHeaderScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to delimit content for tagged PDF.
|
||||||
|
#[elem(Locatable)]
|
||||||
|
pub struct PdfMarkerTag {
|
||||||
|
#[required]
|
||||||
|
pub kind: PdfMarkerTagKind,
|
||||||
|
#[required]
|
||||||
|
pub body: Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pdf_marker_tag {
|
||||||
|
($(#[doc = $doc:expr] $variant:ident,)+) => {
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Cast)]
|
||||||
|
pub enum PdfMarkerTagKind {
|
||||||
|
$(
|
||||||
|
#[doc = $doc]
|
||||||
|
$variant
|
||||||
|
),+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfMarkerTag {
|
||||||
|
$(
|
||||||
|
#[doc = $doc]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn $variant(body: Content) -> Content {
|
||||||
|
Self::new(PdfMarkerTagKind::$variant, body).pack()
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf_marker_tag! {
|
||||||
|
/// `TOC`
|
||||||
|
OutlineBody,
|
||||||
|
/// `Lbl` (marker) of the list item
|
||||||
|
ListItemLabel,
|
||||||
|
/// `LBody` of the enum item
|
||||||
|
ListItemBody,
|
||||||
|
}
|
||||||
|
85
crates/typst-pdf/src/tags/list.rs
Normal file
85
crates/typst-pdf/src/tags/list.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use krilla::tagging::{ListNumbering, TagKind};
|
||||||
|
|
||||||
|
use crate::tags::TagNode;
|
||||||
|
|
||||||
|
pub(crate) struct ListCtx {
|
||||||
|
numbering: ListNumbering,
|
||||||
|
items: Vec<ListItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ListItem {
|
||||||
|
label: Vec<TagNode>,
|
||||||
|
body: Option<Vec<TagNode>>,
|
||||||
|
sub_list: Option<TagNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListCtx {
|
||||||
|
pub(crate) fn new(numbering: ListNumbering) -> Self {
|
||||||
|
Self { numbering, items: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_label(&mut self, nodes: Vec<TagNode>) {
|
||||||
|
self.items.push(ListItem { label: nodes, body: None, sub_list: None });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_body(&mut self, mut nodes: Vec<TagNode>) {
|
||||||
|
let item = self.items.last_mut().expect("ListItemLabel");
|
||||||
|
|
||||||
|
// Nested lists are expected to have the following structure:
|
||||||
|
//
|
||||||
|
// Typst code
|
||||||
|
// ```
|
||||||
|
// - a
|
||||||
|
// - b
|
||||||
|
// - c
|
||||||
|
// - d
|
||||||
|
// - e
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Structure tree
|
||||||
|
// ```
|
||||||
|
// <L>
|
||||||
|
// <LI>
|
||||||
|
// <Lbl> `-`
|
||||||
|
// <LBody> `a`
|
||||||
|
// <LI>
|
||||||
|
// <Lbl> `-`
|
||||||
|
// <LBody> `b`
|
||||||
|
// <L>
|
||||||
|
// <LI>
|
||||||
|
// <Lbl> `-`
|
||||||
|
// <LBody> `c`
|
||||||
|
// <LI>
|
||||||
|
// <Lbl> `-`
|
||||||
|
// <LBody> `d`
|
||||||
|
// <LI>
|
||||||
|
// <Lbl> `-`
|
||||||
|
// <LBody> `d`
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// So move the nested list out of the list item.
|
||||||
|
if let [_, TagNode::Group(tag, _)] = nodes.as_slice() {
|
||||||
|
if matches!(tag.kind, TagKind::L(_)) {
|
||||||
|
item.sub_list = nodes.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.body = Some(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_list(self, mut nodes: Vec<TagNode>) -> TagNode {
|
||||||
|
for item in self.items.into_iter() {
|
||||||
|
nodes.push(TagNode::Group(
|
||||||
|
TagKind::LI.into(),
|
||||||
|
vec![
|
||||||
|
TagNode::Group(TagKind::Lbl.into(), item.label),
|
||||||
|
TagNode::Group(TagKind::LBody.into(), item.body.unwrap_or_default()),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
if let Some(sub_list) = item.sub_list {
|
||||||
|
nodes.push(sub_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagNode::Group(TagKind::L(self.numbering).into(), nodes)
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,8 @@ use krilla::configure::Validator;
|
|||||||
use krilla::page::Page;
|
use krilla::page::Page;
|
||||||
use krilla::surface::Surface;
|
use krilla::surface::Surface;
|
||||||
use krilla::tagging::{
|
use krilla::tagging::{
|
||||||
ArtifactType, ContentTag, Identifier, Node, SpanTag, TableDataCell, Tag, TagBuilder,
|
ArtifactType, ContentTag, Identifier, ListNumbering, Node, SpanTag, TableDataCell,
|
||||||
TagGroup, TagKind, TagTree,
|
Tag, TagBuilder, TagGroup, TagKind, TagTree,
|
||||||
};
|
};
|
||||||
use typst_library::diag::SourceResult;
|
use typst_library::diag::SourceResult;
|
||||||
use typst_library::foundations::{
|
use typst_library::foundations::{
|
||||||
@ -17,17 +17,21 @@ 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, FigureCaption, FigureElem, HeadingElem, Outlinable, OutlineBody,
|
Destination, EnumElem, FigureCaption, FigureElem, HeadingElem, ListElem, Outlinable,
|
||||||
OutlineEntry, TableCell, TableElem,
|
OutlineEntry, TableCell, TableElem,
|
||||||
};
|
};
|
||||||
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfTagElem, PdfTagKind};
|
use typst_library::pdf::{
|
||||||
|
ArtifactElem, ArtifactKind, PdfMarkerTag, PdfMarkerTagKind, PdfTagElem, PdfTagKind,
|
||||||
|
};
|
||||||
use typst_library::visualize::ImageElem;
|
use typst_library::visualize::ImageElem;
|
||||||
|
|
||||||
use crate::convert::GlobalContext;
|
use crate::convert::GlobalContext;
|
||||||
use crate::link::LinkAnnotation;
|
use crate::link::LinkAnnotation;
|
||||||
|
use crate::tags::list::ListCtx;
|
||||||
use crate::tags::outline::OutlineCtx;
|
use crate::tags::outline::OutlineCtx;
|
||||||
use crate::tags::table::TableCtx;
|
use crate::tags::table::TableCtx;
|
||||||
|
|
||||||
|
mod list;
|
||||||
mod outline;
|
mod outline;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
@ -58,16 +62,32 @@ pub(crate) fn handle_start(
|
|||||||
PdfTagKind::Part => TagKind::Part.into(),
|
PdfTagKind::Part => TagKind::Part.into(),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
} else if let Some(heading) = elem.to_packed::<HeadingElem>() {
|
} else if let Some(tag) = elem.to_packed::<PdfMarkerTag>() {
|
||||||
let level = heading.level().try_into().unwrap_or(NonZeroU32::MAX);
|
match tag.kind {
|
||||||
let name = heading.body.plain_text().to_string();
|
PdfMarkerTagKind::OutlineBody => {
|
||||||
TagKind::Hn(level, Some(name)).into()
|
push_stack(gc, loc, StackEntryKind::Outline(OutlineCtx::new()))?;
|
||||||
} else if let Some(_) = elem.to_packed::<OutlineBody>() {
|
return Ok(());
|
||||||
push_stack(gc, loc, StackEntryKind::Outline(OutlineCtx::new()))?;
|
}
|
||||||
return Ok(());
|
PdfMarkerTagKind::ListItemLabel => {
|
||||||
|
push_stack(gc, loc, StackEntryKind::ListItemLabel)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
PdfMarkerTagKind::ListItemBody => {
|
||||||
|
push_stack(gc, loc, StackEntryKind::ListItemBody)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let Some(entry) = elem.to_packed::<OutlineEntry>() {
|
} else if let Some(entry) = elem.to_packed::<OutlineEntry>() {
|
||||||
push_stack(gc, loc, StackEntryKind::OutlineEntry(entry.clone()))?;
|
push_stack(gc, loc, StackEntryKind::OutlineEntry(entry.clone()))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
} else if let Some(_list) = elem.to_packed::<ListElem>() {
|
||||||
|
let numbering = ListNumbering::Circle; // TODO: infer numbering from `list.marker`
|
||||||
|
push_stack(gc, loc, StackEntryKind::List(ListCtx::new(numbering)))?;
|
||||||
|
return Ok(());
|
||||||
|
} else if let Some(_enumeration) = elem.to_packed::<EnumElem>() {
|
||||||
|
let numbering = ListNumbering::Decimal; // TODO: infer numbering from `enum.numbering`
|
||||||
|
push_stack(gc, loc, StackEntryKind::List(ListCtx::new(numbering)))?;
|
||||||
|
return Ok(());
|
||||||
} else if let Some(_) = elem.to_packed::<FigureElem>() {
|
} else if let Some(_) = elem.to_packed::<FigureElem>() {
|
||||||
let alt = None; // TODO
|
let alt = None; // TODO
|
||||||
TagKind::Figure.with_alt_text(alt)
|
TagKind::Figure.with_alt_text(alt)
|
||||||
@ -112,6 +132,10 @@ pub(crate) fn handle_start(
|
|||||||
push_stack(gc, loc, StackEntryKind::TableCell(cell.clone()))?;
|
push_stack(gc, loc, StackEntryKind::TableCell(cell.clone()))?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
} else if let Some(heading) = elem.to_packed::<HeadingElem>() {
|
||||||
|
let level = heading.level().try_into().unwrap_or(NonZeroU32::MAX);
|
||||||
|
let name = heading.body.plain_text().to_string();
|
||||||
|
TagKind::Hn(level, Some(name)).into()
|
||||||
} else if let Some(link) = elem.to_packed::<LinkMarker>() {
|
} else if let Some(link) = elem.to_packed::<LinkMarker>() {
|
||||||
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()))?;
|
||||||
@ -171,6 +195,17 @@ pub(crate) fn handle_end(gc: &mut GlobalContext, surface: &mut Surface, loc: Loc
|
|||||||
table_ctx.insert(&cell, entry.nodes);
|
table_ctx.insert(&cell, entry.nodes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
StackEntryKind::List(list) => list.build_list(entry.nodes),
|
||||||
|
StackEntryKind::ListItemLabel => {
|
||||||
|
let list_ctx = gc.tags.parent_list().expect("parent list");
|
||||||
|
list_ctx.push_label(entry.nodes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StackEntryKind::ListItemBody => {
|
||||||
|
let list_ctx = gc.tags.parent_list().expect("parent list");
|
||||||
|
list_ctx.push_body(entry.nodes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
StackEntryKind::Link(_, link) => {
|
StackEntryKind::Link(_, link) => {
|
||||||
let alt = link.alt.as_ref().map(EcoString::to_string);
|
let alt = link.alt.as_ref().map(EcoString::to_string);
|
||||||
let tag = TagKind::Link.with_alt_text(alt);
|
let tag = TagKind::Link.with_alt_text(alt);
|
||||||
@ -309,6 +344,10 @@ impl Tags {
|
|||||||
self.parent()?.as_table_mut()
|
self.parent()?.as_table_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parent_list(&mut self) -> Option<&mut ListCtx> {
|
||||||
|
self.parent()?.as_list_mut()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn find_parent_link(&self) -> Option<(LinkId, &Packed<LinkMarker>)> {
|
pub(crate) fn find_parent_link(&self) -> Option<(LinkId, &Packed<LinkMarker>)> {
|
||||||
self.stack.iter().rev().find_map(|entry| entry.kind.as_link())
|
self.stack.iter().rev().find_map(|entry| entry.kind.as_link())
|
||||||
}
|
}
|
||||||
@ -378,6 +417,9 @@ pub(crate) enum StackEntryKind {
|
|||||||
OutlineEntry(Packed<OutlineEntry>),
|
OutlineEntry(Packed<OutlineEntry>),
|
||||||
Table(TableCtx),
|
Table(TableCtx),
|
||||||
TableCell(Packed<TableCell>),
|
TableCell(Packed<TableCell>),
|
||||||
|
List(ListCtx),
|
||||||
|
ListItemLabel,
|
||||||
|
ListItemBody,
|
||||||
Link(LinkId, Packed<LinkMarker>),
|
Link(LinkId, Packed<LinkMarker>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,6 +448,14 @@ impl StackEntryKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_list_mut(&mut self) -> Option<&mut ListCtx> {
|
||||||
|
if let Self::List(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn as_link(&self) -> Option<(LinkId, &Packed<LinkMarker>)> {
|
pub(crate) fn as_link(&self) -> Option<(LinkId, &Packed<LinkMarker>)> {
|
||||||
if let Self::Link(id, link) = self {
|
if let Self::Link(id, link) = self {
|
||||||
Some((*id, link))
|
Some((*id, link))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user