mirror of
https://github.com/typst/typst
synced 2025-05-16 01:55:28 +08:00
initial subheader resolving and api
This commit is contained in:
parent
48d0a07ef4
commit
5e2241ab65
@ -634,7 +634,7 @@ mod test {
|
|||||||
},
|
},
|
||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
vec![],
|
||||||
None,
|
None,
|
||||||
entries,
|
entries,
|
||||||
)
|
)
|
||||||
@ -1172,7 +1172,7 @@ mod test {
|
|||||||
},
|
},
|
||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
vec![],
|
||||||
None,
|
None,
|
||||||
entries,
|
entries,
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use std::num::{NonZeroI64, NonZeroIsize, NonZeroU64, NonZeroUsize, ParseIntError};
|
use std::num::{
|
||||||
|
NonZeroI64, NonZeroIsize, NonZeroU32, NonZeroU64, NonZeroUsize, ParseIntError,
|
||||||
|
};
|
||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -482,3 +484,16 @@ cast! {
|
|||||||
"number too large"
|
"number too large"
|
||||||
})?,
|
})?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cast! {
|
||||||
|
NonZeroU32,
|
||||||
|
self => Value::Int(self.get() as _),
|
||||||
|
v: i64 => v
|
||||||
|
.try_into()
|
||||||
|
.and_then(|v: u32| v.try_into())
|
||||||
|
.map_err(|_| if v <= 0 {
|
||||||
|
"number must be positive"
|
||||||
|
} else {
|
||||||
|
"number too large"
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub mod resolve;
|
pub mod resolve;
|
||||||
|
|
||||||
use std::num::NonZeroUsize;
|
use std::num::{NonZeroU32, NonZeroUsize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use comemo::Track;
|
use comemo::Track;
|
||||||
@ -468,6 +468,14 @@ pub struct GridHeader {
|
|||||||
#[default(true)]
|
#[default(true)]
|
||||||
pub repeat: bool,
|
pub repeat: bool,
|
||||||
|
|
||||||
|
/// The level of the header. Must not be zero.
|
||||||
|
///
|
||||||
|
/// This is used during repetition multiple headers at once. When a header
|
||||||
|
/// with a lower level starts repeating, all headers with a lower level stop
|
||||||
|
/// repeating.
|
||||||
|
#[default(NonZeroU32::ONE)]
|
||||||
|
pub level: NonZeroU32,
|
||||||
|
|
||||||
/// The cells and lines within the header.
|
/// The cells and lines within the header.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<GridItem>,
|
pub children: Vec<GridItem>,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::{NonZeroU32, NonZeroUsize};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -48,6 +48,7 @@ pub fn grid_to_cellgrid<'a>(
|
|||||||
let children = elem.children.iter().map(|child| match child {
|
let children = elem.children.iter().map(|child| match child {
|
||||||
GridChild::Header(header) => ResolvableGridChild::Header {
|
GridChild::Header(header) => ResolvableGridChild::Header {
|
||||||
repeat: header.repeat(styles),
|
repeat: header.repeat(styles),
|
||||||
|
level: header.level(styles),
|
||||||
span: header.span(),
|
span: header.span(),
|
||||||
items: header.children.iter().map(resolve_item),
|
items: header.children.iter().map(resolve_item),
|
||||||
},
|
},
|
||||||
@ -101,6 +102,7 @@ pub fn table_to_cellgrid<'a>(
|
|||||||
let children = elem.children.iter().map(|child| match child {
|
let children = elem.children.iter().map(|child| match child {
|
||||||
TableChild::Header(header) => ResolvableGridChild::Header {
|
TableChild::Header(header) => ResolvableGridChild::Header {
|
||||||
repeat: header.repeat(styles),
|
repeat: header.repeat(styles),
|
||||||
|
level: header.level(styles),
|
||||||
span: header.span(),
|
span: header.span(),
|
||||||
items: header.children.iter().map(resolve_item),
|
items: header.children.iter().map(resolve_item),
|
||||||
},
|
},
|
||||||
@ -647,7 +649,7 @@ impl<'a> Entry<'a> {
|
|||||||
|
|
||||||
/// Any grid child, which can be either a header or an item.
|
/// Any grid child, which can be either a header or an item.
|
||||||
pub enum ResolvableGridChild<T: ResolvableCell, I> {
|
pub enum ResolvableGridChild<T: ResolvableCell, I> {
|
||||||
Header { repeat: bool, span: Span, items: I },
|
Header { repeat: bool, level: NonZeroU32, span: Span, items: I },
|
||||||
Footer { repeat: bool, span: Span, items: I },
|
Footer { repeat: bool, span: Span, items: I },
|
||||||
Item(ResolvableGridItem<T>),
|
Item(ResolvableGridItem<T>),
|
||||||
}
|
}
|
||||||
@ -668,10 +670,10 @@ pub struct CellGrid<'a> {
|
|||||||
/// Gutter rows are not included.
|
/// Gutter rows are not included.
|
||||||
/// Contains up to 'rows_without_gutter.len() + 1' vectors of lines.
|
/// Contains up to 'rows_without_gutter.len() + 1' vectors of lines.
|
||||||
pub hlines: Vec<Vec<Line>>,
|
pub hlines: Vec<Vec<Line>>,
|
||||||
/// The repeatable footer of this grid.
|
|
||||||
pub footer: Option<Repeatable<Footer>>,
|
|
||||||
/// The repeatable headers of this grid.
|
/// The repeatable headers of this grid.
|
||||||
pub headers: Vec<Repeatable<Header>>,
|
pub headers: Vec<Repeatable<Header>>,
|
||||||
|
/// The repeatable footer of this grid.
|
||||||
|
pub footer: Option<Repeatable<Footer>>,
|
||||||
/// Whether this grid has gutters.
|
/// Whether this grid has gutters.
|
||||||
pub has_gutter: bool,
|
pub has_gutter: bool,
|
||||||
}
|
}
|
||||||
@ -684,7 +686,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
cells: impl IntoIterator<Item = Cell<'a>>,
|
cells: impl IntoIterator<Item = Cell<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entries = cells.into_iter().map(Entry::Cell).collect();
|
let entries = cells.into_iter().map(Entry::Cell).collect();
|
||||||
Self::new_internal(tracks, gutter, vec![], vec![], None, None, entries)
|
Self::new_internal(tracks, gutter, vec![], vec![], vec![], None, entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the cell grid, given the tracks and resolved entries.
|
/// Generates the cell grid, given the tracks and resolved entries.
|
||||||
@ -693,7 +695,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
gutter: Axes<&[Sizing]>,
|
gutter: Axes<&[Sizing]>,
|
||||||
vlines: Vec<Vec<Line>>,
|
vlines: Vec<Vec<Line>>,
|
||||||
hlines: Vec<Vec<Line>>,
|
hlines: Vec<Vec<Line>>,
|
||||||
header: Option<Repeatable<Header>>,
|
headers: Vec<Repeatable<Header>>,
|
||||||
footer: Option<Repeatable<Footer>>,
|
footer: Option<Repeatable<Footer>>,
|
||||||
entries: Vec<Entry<'a>>,
|
entries: Vec<Entry<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -747,8 +749,8 @@ impl<'a> CellGrid<'a> {
|
|||||||
entries,
|
entries,
|
||||||
vlines,
|
vlines,
|
||||||
hlines,
|
hlines,
|
||||||
|
headers,
|
||||||
footer,
|
footer,
|
||||||
headers: header.into_iter().collect(),
|
|
||||||
has_gutter,
|
has_gutter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -972,6 +974,9 @@ struct RowGroupData {
|
|||||||
span: Span,
|
span: Span,
|
||||||
kind: RowGroupKind,
|
kind: RowGroupKind,
|
||||||
|
|
||||||
|
/// Level of this header or footer.
|
||||||
|
repeatable_level: NonZeroU32,
|
||||||
|
|
||||||
/// Start of the range of indices of hlines at the top of the row group.
|
/// Start of the range of indices of hlines at the top of the row group.
|
||||||
/// This is always the first index after the last hline before we started
|
/// This is always the first index after the last hline before we started
|
||||||
/// building the row group - any upcoming hlines would appear at least at
|
/// building the row group - any upcoming hlines would appear at least at
|
||||||
@ -1019,7 +1024,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
let mut pending_vlines: Vec<(Span, Line)> = vec![];
|
let mut pending_vlines: Vec<(Span, Line)> = vec![];
|
||||||
let has_gutter = self.gutter.any(|tracks| !tracks.is_empty());
|
let has_gutter = self.gutter.any(|tracks| !tracks.is_empty());
|
||||||
|
|
||||||
let mut header: Option<Header> = None;
|
let mut headers: Vec<Header> = vec![];
|
||||||
let mut repeat_header = false;
|
let mut repeat_header = false;
|
||||||
|
|
||||||
// Stores where the footer is supposed to end, its span, and the
|
// Stores where the footer is supposed to end, its span, and the
|
||||||
@ -1063,7 +1068,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
columns,
|
columns,
|
||||||
&mut pending_hlines,
|
&mut pending_hlines,
|
||||||
&mut pending_vlines,
|
&mut pending_vlines,
|
||||||
&mut header,
|
&mut headers,
|
||||||
&mut repeat_header,
|
&mut repeat_header,
|
||||||
&mut footer,
|
&mut footer,
|
||||||
&mut repeat_footer,
|
&mut repeat_footer,
|
||||||
@ -1084,9 +1089,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
row_amount,
|
row_amount,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (header, footer) = self.finalize_headers_and_footers(
|
let (headers, footer) = self.finalize_headers_and_footers(
|
||||||
has_gutter,
|
has_gutter,
|
||||||
header,
|
headers,
|
||||||
repeat_header,
|
repeat_header,
|
||||||
footer,
|
footer,
|
||||||
repeat_footer,
|
repeat_footer,
|
||||||
@ -1098,7 +1103,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
self.gutter,
|
self.gutter,
|
||||||
vlines,
|
vlines,
|
||||||
hlines,
|
hlines,
|
||||||
header,
|
headers,
|
||||||
footer,
|
footer,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
))
|
))
|
||||||
@ -1118,7 +1123,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
columns: usize,
|
columns: usize,
|
||||||
pending_hlines: &mut Vec<(Span, Line, bool)>,
|
pending_hlines: &mut Vec<(Span, Line, bool)>,
|
||||||
pending_vlines: &mut Vec<(Span, Line)>,
|
pending_vlines: &mut Vec<(Span, Line)>,
|
||||||
header: &mut Option<Header>,
|
headers: &mut Vec<Header>,
|
||||||
repeat_header: &mut bool,
|
repeat_header: &mut bool,
|
||||||
footer: &mut Option<(usize, Span, Footer)>,
|
footer: &mut Option<(usize, Span, Footer)>,
|
||||||
repeat_footer: &mut bool,
|
repeat_footer: &mut bool,
|
||||||
@ -1158,15 +1163,12 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
let mut first_available_row = 0;
|
let mut first_available_row = 0;
|
||||||
|
|
||||||
let (header_footer_items, simple_item) = match child {
|
let (header_footer_items, simple_item) = match child {
|
||||||
ResolvableGridChild::Header { repeat, span, items, .. } => {
|
ResolvableGridChild::Header { repeat, level, span, items, .. } => {
|
||||||
if header.is_some() {
|
|
||||||
bail!(span, "cannot have more than one header");
|
|
||||||
}
|
|
||||||
|
|
||||||
row_group_data = Some(RowGroupData {
|
row_group_data = Some(RowGroupData {
|
||||||
range: None,
|
range: None,
|
||||||
span,
|
span,
|
||||||
kind: RowGroupKind::Header,
|
kind: RowGroupKind::Header,
|
||||||
|
repeatable_level: level,
|
||||||
top_hlines_start: pending_hlines.len(),
|
top_hlines_start: pending_hlines.len(),
|
||||||
top_hlines_end: None,
|
top_hlines_end: None,
|
||||||
});
|
});
|
||||||
@ -1198,6 +1200,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
range: None,
|
range: None,
|
||||||
span,
|
span,
|
||||||
kind: RowGroupKind::Footer,
|
kind: RowGroupKind::Footer,
|
||||||
|
repeatable_level: NonZeroU32::ONE,
|
||||||
top_hlines_start: pending_hlines.len(),
|
top_hlines_start: pending_hlines.len(),
|
||||||
top_hlines_end: None,
|
top_hlines_end: None,
|
||||||
});
|
});
|
||||||
@ -1330,7 +1333,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
cell_y,
|
cell_y,
|
||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
header.as_ref(),
|
headers,
|
||||||
footer.as_ref(),
|
footer.as_ref(),
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
&mut local_auto_index,
|
&mut local_auto_index,
|
||||||
@ -1518,15 +1521,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
|
|
||||||
match row_group.kind {
|
match row_group.kind {
|
||||||
RowGroupKind::Header => {
|
RowGroupKind::Header => {
|
||||||
if group_range.start != 0 {
|
headers.push(Header {
|
||||||
bail!(
|
|
||||||
row_group.span,
|
|
||||||
"header must start at the first row";
|
|
||||||
hint: "remove any rows before the header"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
*header = Some(Header {
|
|
||||||
start: group_range.start,
|
start: group_range.start,
|
||||||
|
|
||||||
// Later on, we have to correct this number in case there
|
// Later on, we have to correct this number in case there
|
||||||
@ -1535,7 +1530,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// below.
|
// below.
|
||||||
end: group_range.end,
|
end: group_range.end,
|
||||||
|
|
||||||
level: 1,
|
level: row_group.repeatable_level.get(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1730,13 +1725,14 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
fn finalize_headers_and_footers(
|
fn finalize_headers_and_footers(
|
||||||
&self,
|
&self,
|
||||||
has_gutter: bool,
|
has_gutter: bool,
|
||||||
header: Option<Header>,
|
headers: Vec<Header>,
|
||||||
repeat_header: bool,
|
repeat_header: bool,
|
||||||
footer: Option<(usize, Span, Footer)>,
|
footer: Option<(usize, Span, Footer)>,
|
||||||
repeat_footer: bool,
|
repeat_footer: bool,
|
||||||
row_amount: usize,
|
row_amount: usize,
|
||||||
) -> SourceResult<(Option<Repeatable<Header>>, Option<Repeatable<Footer>>)> {
|
) -> SourceResult<(Vec<Repeatable<Header>>, Option<Repeatable<Footer>>)> {
|
||||||
let header = header
|
let headers: Vec<Repeatable<Header>> = headers
|
||||||
|
.into_iter()
|
||||||
.map(|mut header| {
|
.map(|mut header| {
|
||||||
// Repeat the gutter below a header (hence why we don't
|
// Repeat the gutter below a header (hence why we don't
|
||||||
// subtract 1 from the gutter case).
|
// subtract 1 from the gutter case).
|
||||||
@ -1774,7 +1770,8 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
} else {
|
} else {
|
||||||
Repeatable::NotRepeated(header)
|
Repeatable::NotRepeated(header)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let footer = footer
|
let footer = footer
|
||||||
.map(|(footer_end, footer_span, mut footer)| {
|
.map(|(footer_end, footer_span, mut footer)| {
|
||||||
@ -1782,8 +1779,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
bail!(footer_span, "footer must end at the last row");
|
bail!(footer_span, "footer must end at the last row");
|
||||||
}
|
}
|
||||||
|
|
||||||
let header_end =
|
// TODO: will need a global slice of headers and footers for
|
||||||
header.as_ref().map(Repeatable::unwrap).map(|header| header.end);
|
// when we have multiple footers
|
||||||
|
let last_header_end =
|
||||||
|
headers.last().map(Repeatable::unwrap).map(|header| header.end);
|
||||||
|
|
||||||
if has_gutter {
|
if has_gutter {
|
||||||
// Convert the footer's start index to post-gutter coordinates.
|
// Convert the footer's start index to post-gutter coordinates.
|
||||||
@ -1792,7 +1791,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// Include the gutter right before the footer, unless there is
|
// Include the gutter right before the footer, unless there is
|
||||||
// none, or the gutter is already included in the header (no
|
// none, or the gutter is already included in the header (no
|
||||||
// rows between the header and the footer).
|
// rows between the header and the footer).
|
||||||
if header_end != Some(footer.start) {
|
if last_header_end != Some(footer.start) {
|
||||||
footer.start = footer.start.saturating_sub(1);
|
footer.start = footer.start.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1820,7 +1819,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((header, footer))
|
Ok((headers, footer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the cell's fields based on grid-wide properties.
|
/// Resolves the cell's fields based on grid-wide properties.
|
||||||
@ -1991,24 +1990,26 @@ fn expand_row_group(
|
|||||||
|
|
||||||
/// Check if a cell's fixed row would conflict with a header or footer.
|
/// Check if a cell's fixed row would conflict with a header or footer.
|
||||||
fn check_for_conflicting_cell_row(
|
fn check_for_conflicting_cell_row(
|
||||||
header: Option<&Header>,
|
headers: &[Header],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footer: Option<&(usize, Span, Footer)>,
|
||||||
cell_y: usize,
|
cell_y: usize,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
) -> HintedStrResult<()> {
|
) -> HintedStrResult<()> {
|
||||||
if let Some(header) = header {
|
// TODO: use upcoming headers slice to make this an O(1) check
|
||||||
// NOTE: y + rowspan >, not >=, header.start, to check if the rowspan
|
// NOTE: y + rowspan >, not >=, header.start, to check if the rowspan
|
||||||
// enters the header. For example, consider a rowspan of 1: if
|
// enters the header. For example, consider a rowspan of 1: if
|
||||||
// `y + 1 = header.start` holds, that means `y < header.start`, and it
|
// `y + 1 = header.start` holds, that means `y < header.start`, and it
|
||||||
// only occupies one row (`y`), so the cell is actually not in
|
// only occupies one row (`y`), so the cell is actually not in
|
||||||
// conflict.
|
// conflict.
|
||||||
if cell_y < header.end && cell_y + rowspan > header.start {
|
if headers
|
||||||
|
.iter()
|
||||||
|
.any(|header| cell_y < header.end && cell_y + rowspan > header.start)
|
||||||
|
{
|
||||||
bail!(
|
bail!(
|
||||||
"cell would conflict with header spanning the same position";
|
"cell would conflict with header spanning the same position";
|
||||||
hint: "try moving the cell or the header"
|
hint: "try moving the cell or the header"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((_, _, footer)) = footer {
|
if let Some((_, _, footer)) = footer {
|
||||||
if cell_y < footer.end && cell_y + rowspan > footer.start {
|
if cell_y < footer.end && cell_y + rowspan > footer.start {
|
||||||
@ -2037,7 +2038,7 @@ fn resolve_cell_position(
|
|||||||
cell_y: Smart<usize>,
|
cell_y: Smart<usize>,
|
||||||
colspan: usize,
|
colspan: usize,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
header: Option<&Header>,
|
headers: &[Header],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footer: Option<&(usize, Span, Footer)>,
|
||||||
resolved_cells: &[Option<Entry>],
|
resolved_cells: &[Option<Entry>],
|
||||||
auto_index: &mut usize,
|
auto_index: &mut usize,
|
||||||
@ -2062,7 +2063,7 @@ fn resolve_cell_position(
|
|||||||
// but automatically-positioned cells will avoid conflicts by
|
// but automatically-positioned cells will avoid conflicts by
|
||||||
// simply skipping existing cells, headers and footers.
|
// simply skipping existing cells, headers and footers.
|
||||||
let resolved_index = find_next_available_position::<false>(
|
let resolved_index = find_next_available_position::<false>(
|
||||||
header,
|
headers,
|
||||||
footer,
|
footer,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
columns,
|
columns,
|
||||||
@ -2102,7 +2103,7 @@ fn resolve_cell_position(
|
|||||||
// footer (but only if it isn't already in one, otherwise there
|
// footer (but only if it isn't already in one, otherwise there
|
||||||
// will already be a separate check).
|
// will already be a separate check).
|
||||||
if !in_row_group {
|
if !in_row_group {
|
||||||
check_for_conflicting_cell_row(header, footer, cell_y, rowspan)?;
|
check_for_conflicting_cell_row(headers, footer, cell_y, rowspan)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_index(cell_x, cell_y)
|
cell_index(cell_x, cell_y)
|
||||||
@ -2120,7 +2121,7 @@ fn resolve_cell_position(
|
|||||||
// ('None'), in which case we'd create a new row to place this
|
// ('None'), in which case we'd create a new row to place this
|
||||||
// cell in.
|
// cell in.
|
||||||
find_next_available_position::<true>(
|
find_next_available_position::<true>(
|
||||||
header,
|
headers,
|
||||||
footer,
|
footer,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
columns,
|
columns,
|
||||||
@ -2134,7 +2135,7 @@ fn resolve_cell_position(
|
|||||||
// footer (but only if it isn't already in one, otherwise there
|
// footer (but only if it isn't already in one, otherwise there
|
||||||
// will already be a separate check).
|
// will already be a separate check).
|
||||||
if !in_row_group {
|
if !in_row_group {
|
||||||
check_for_conflicting_cell_row(header, footer, cell_y, rowspan)?;
|
check_for_conflicting_cell_row(headers, footer, cell_y, rowspan)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's find the first column which has that row available.
|
// Let's find the first column which has that row available.
|
||||||
@ -2168,7 +2169,7 @@ fn resolve_cell_position(
|
|||||||
/// have cells specified by the user) as well as any headers and footers.
|
/// have cells specified by the user) as well as any headers and footers.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn find_next_available_position<const SKIP_ROWS: bool>(
|
fn find_next_available_position<const SKIP_ROWS: bool>(
|
||||||
header: Option<&Header>,
|
headers: &[Header],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footer: Option<&(usize, Span, Footer)>,
|
||||||
resolved_cells: &[Option<Entry<'_>>],
|
resolved_cells: &[Option<Entry<'_>>],
|
||||||
columns: usize,
|
columns: usize,
|
||||||
@ -2195,9 +2196,9 @@ fn find_next_available_position<const SKIP_ROWS: bool>(
|
|||||||
// would become impractically large before this overflows.
|
// would become impractically large before this overflows.
|
||||||
resolved_index += 1;
|
resolved_index += 1;
|
||||||
}
|
}
|
||||||
} else if let Some(header) =
|
} else if let Some(header) = headers.iter().find(|header| {
|
||||||
header.filter(|header| resolved_index < header.end * columns)
|
(header.start * columns..header.end * columns).contains(&resolved_index)
|
||||||
{
|
}) {
|
||||||
// Skip header (can't place a cell inside it from outside it).
|
// Skip header (can't place a cell inside it from outside it).
|
||||||
resolved_index = header.end * columns;
|
resolved_index = header.end * columns;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::{NonZeroU32, NonZeroUsize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
@ -494,6 +494,14 @@ pub struct TableHeader {
|
|||||||
#[default(true)]
|
#[default(true)]
|
||||||
pub repeat: bool,
|
pub repeat: bool,
|
||||||
|
|
||||||
|
/// The level of the header. Must not be zero.
|
||||||
|
///
|
||||||
|
/// This is used during repetition multiple headers at once. When a header
|
||||||
|
/// with a lower level starts repeating, all headers with a lower level stop
|
||||||
|
/// repeating.
|
||||||
|
#[default(NonZeroU32::ONE)]
|
||||||
|
pub level: NonZeroU32,
|
||||||
|
|
||||||
/// The cells and lines within the header.
|
/// The cells and lines within the header.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<TableItem>,
|
pub children: Vec<TableItem>,
|
||||||
|
@ -26,7 +26,7 @@ pub use once_cell;
|
|||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::iter::{Chain, Flatten, Rev};
|
use std::iter::{Chain, Flatten, Rev};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::{NonZeroU32, NonZeroUsize};
|
||||||
use std::ops::{Add, Deref, Div, Mul, Neg, Sub};
|
use std::ops::{Add, Deref, Div, Mul, Neg, Sub};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -72,6 +72,13 @@ impl NonZeroExt for NonZeroUsize {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NonZeroExt for NonZeroU32 {
|
||||||
|
const ONE: Self = match Self::new(1) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Extra methods for [`Arc`].
|
/// Extra methods for [`Arc`].
|
||||||
pub trait ArcExt<T> {
|
pub trait ArcExt<T> {
|
||||||
/// Takes the inner value if there is exactly one strong reference and
|
/// Takes the inner value if there is exactly one strong reference and
|
||||||
|
Loading…
x
Reference in New Issue
Block a user