mirror of
https://github.com/typst/typst
synced 2025-05-15 09:35:28 +08:00
add levels and ranges to headers and footers
This commit is contained in:
parent
1f1c133878
commit
20179bbe7a
@ -426,8 +426,24 @@ pub struct Line {
|
|||||||
/// A repeatable grid header. Starts at the first row.
|
/// A repeatable grid header. Starts at the first row.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
|
/// The first row included in this header.
|
||||||
|
pub start: usize,
|
||||||
/// The index after the last row included in this header.
|
/// The index after the last row included in this header.
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
|
/// The header's level.
|
||||||
|
///
|
||||||
|
/// Higher level headers repeat together with lower level headers. If a
|
||||||
|
/// lower level header stops repeating, all higher level headers do as
|
||||||
|
/// well.
|
||||||
|
pub level: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
/// The header's range of included rows.
|
||||||
|
#[inline]
|
||||||
|
pub fn range(&self) -> Range<usize> {
|
||||||
|
self.start..self.end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A repeatable grid footer. Stops at the last row.
|
/// A repeatable grid footer. Stops at the last row.
|
||||||
@ -435,6 +451,20 @@ pub struct Header {
|
|||||||
pub struct Footer {
|
pub struct Footer {
|
||||||
/// The first row included in this footer.
|
/// The first row included in this footer.
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
|
/// The index after the last row included in this footer.
|
||||||
|
pub end: usize,
|
||||||
|
/// The footer's level.
|
||||||
|
///
|
||||||
|
/// Used similarly to header level.
|
||||||
|
pub level: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Footer {
|
||||||
|
/// The footer's range of included rows.
|
||||||
|
#[inline]
|
||||||
|
pub fn range(&self) -> Range<usize> {
|
||||||
|
self.start..self.end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A possibly repeatable grid object.
|
/// A possibly repeatable grid object.
|
||||||
@ -638,10 +668,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 header of this grid.
|
|
||||||
pub header: Option<Repeatable<Header>>,
|
|
||||||
/// The repeatable footer of this grid.
|
/// The repeatable footer of this grid.
|
||||||
pub footer: Option<Repeatable<Footer>>,
|
pub footer: Option<Repeatable<Footer>>,
|
||||||
|
/// The repeatable headers of this grid.
|
||||||
|
pub headers: Vec<Repeatable<Header>>,
|
||||||
/// Whether this grid has gutters.
|
/// Whether this grid has gutters.
|
||||||
pub has_gutter: bool,
|
pub has_gutter: bool,
|
||||||
}
|
}
|
||||||
@ -717,8 +747,8 @@ impl<'a> CellGrid<'a> {
|
|||||||
entries,
|
entries,
|
||||||
vlines,
|
vlines,
|
||||||
hlines,
|
hlines,
|
||||||
header,
|
|
||||||
footer,
|
footer,
|
||||||
|
headers: header.into_iter().collect(),
|
||||||
has_gutter,
|
has_gutter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -852,6 +882,11 @@ impl<'a> CellGrid<'a> {
|
|||||||
self.cols.len()
|
self.cols.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn has_repeated_headers(&self) -> bool {
|
||||||
|
self.headers.iter().any(|h| matches!(h, Repeatable::Repeated(_)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves and positions all cells in the grid before creating it.
|
/// Resolves and positions all cells in the grid before creating it.
|
||||||
@ -1492,11 +1527,15 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*header = Some(Header {
|
*header = Some(Header {
|
||||||
|
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
|
||||||
// is gutter. But only once all cells have been analyzed
|
// is gutter. But only once all cells have been analyzed
|
||||||
// and the header has fully expanded in the fixup loop
|
// and the header has fully expanded in the fixup loop
|
||||||
// below.
|
// below.
|
||||||
end: group_range.end,
|
end: group_range.end,
|
||||||
|
|
||||||
|
level: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1514,6 +1553,8 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// before the footer might not be included as part of
|
// before the footer might not be included as part of
|
||||||
// the footer if it is contained within the header.
|
// the footer if it is contained within the header.
|
||||||
start: group_range.start,
|
start: group_range.start,
|
||||||
|
end: group_range.end,
|
||||||
|
level: 1,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1940,8 +1981,12 @@ fn check_for_conflicting_cell_row(
|
|||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
) -> HintedStrResult<()> {
|
) -> HintedStrResult<()> {
|
||||||
if let Some(header) = header {
|
if let Some(header) = header {
|
||||||
// TODO: check start (right now zero, always satisfied)
|
// NOTE: y + rowspan >, not >=, header.start, to check if the rowspan
|
||||||
if cell_y < header.end {
|
// enters the header. For example, consider a rowspan of 1: if
|
||||||
|
// `y + 1 = header.start` holds, that means `y < header.start`, and it
|
||||||
|
// only occupies one row (`y`), so the cell is actually not in
|
||||||
|
// conflict.
|
||||||
|
if 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"
|
||||||
@ -1949,13 +1994,8 @@ fn check_for_conflicting_cell_row(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((footer_end, _, footer)) = footer {
|
if let Some((_, _, footer)) = footer {
|
||||||
// NOTE: y + rowspan >, not >=, footer.start, to check if the rowspan
|
if cell_y < footer.end && cell_y + rowspan > footer.start {
|
||||||
// enters the footer. For example, consider a rowspan of 1: if
|
|
||||||
// `y + 1 = footer.start` holds, that means `y < footer.start`, and it
|
|
||||||
// only occupies one row (`y`), so the cell is actually not in
|
|
||||||
// conflict.
|
|
||||||
if cell_y < *footer_end && cell_y + rowspan > footer.start {
|
|
||||||
bail!(
|
bail!(
|
||||||
"cell would conflict with footer spanning the same position";
|
"cell would conflict with footer spanning the same position";
|
||||||
hint: "try reducing the cell's rowspan or moving the footer"
|
hint: "try reducing the cell's rowspan or moving the footer"
|
||||||
|
@ -296,7 +296,9 @@ fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
|
|||||||
let rows = rows.drain(ft.unwrap().start..);
|
let rows = rows.drain(ft.unwrap().start..);
|
||||||
elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row))))
|
elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row))))
|
||||||
});
|
});
|
||||||
let header = grid.header.map(|hd| {
|
// TODO: Headers and footers in arbitrary positions
|
||||||
|
// Right now, only those at either end are accepted
|
||||||
|
let header = grid.headers.first().filter(|h| h.unwrap().start == 0).map(|hd| {
|
||||||
let rows = rows.drain(..hd.unwrap().end);
|
let rows = rows.drain(..hd.unwrap().end);
|
||||||
elem(tag::thead, Content::sequence(rows.map(|row| tr(tag::th, row))))
|
elem(tag::thead, Content::sequence(rows.map(|row| tr(tag::th, row))))
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user