mirror of
https://github.com/typst/typst
synced 2025-08-06 19:27:55 +08:00
Compare commits
8 Commits
eba9568ac0
...
cf06237556
Author | SHA1 | Date | |
---|---|---|---|
|
cf06237556 | ||
|
1c10342a37 | ||
|
6336c70a2d | ||
|
b2b2593832 | ||
|
b112772a1e | ||
|
41b45b4128 | ||
|
c22c47b9c9 | ||
|
63c4720ed2 |
@ -120,7 +120,10 @@ fn write_element(w: &mut Writer, element: &HtmlElement) -> SourceResult<()> {
|
|||||||
|
|
||||||
/// Whether the element should be pretty-printed.
|
/// Whether the element should be pretty-printed.
|
||||||
fn is_pretty(element: &HtmlElement) -> bool {
|
fn is_pretty(element: &HtmlElement) -> bool {
|
||||||
tag::is_block_by_default(element.tag) || matches!(element.tag, tag::meta)
|
matches!(
|
||||||
|
element.tag,
|
||||||
|
tag::meta | tag::table | tag::thead | tag::tbody | tag::tfoot | tag::tr
|
||||||
|
) || tag::is_block_by_default(element.tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Escape a character.
|
/// Escape a character.
|
||||||
|
@ -9,9 +9,9 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
||||||
TargetElem,
|
TargetElem,
|
||||||
};
|
};
|
||||||
use crate::html::{tag, HtmlAttr, HtmlAttrs, HtmlElem};
|
use crate::html::{tag, HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag};
|
||||||
use crate::introspection::Locator;
|
use crate::introspection::Locator;
|
||||||
use crate::layout::grid::resolve::table_to_cellgrid;
|
use crate::layout::grid::resolve::{table_to_cellgrid, Cell, CellGrid, Entry};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
show_grid_cell, Abs, Alignment, BlockElem, Celled, GridCell, GridFooter, GridHLine,
|
show_grid_cell, Abs, Alignment, BlockElem, Celled, GridCell, GridFooter, GridHLine,
|
||||||
GridHeader, GridVLine, Length, OuterHAlignment, OuterVAlignment, Rel, Sides,
|
GridHeader, GridVLine, Length, OuterHAlignment, OuterVAlignment, Rel, Sides,
|
||||||
@ -262,34 +262,61 @@ impl TableElem {
|
|||||||
type TableFooter;
|
type TableFooter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content {
|
||||||
|
let cell = cell.body.clone();
|
||||||
|
let Some(cell) = cell.to_packed::<TableCell>() else { return cell };
|
||||||
|
let mut attrs = HtmlAttrs::default();
|
||||||
|
let span = |n: NonZeroUsize| (n != NonZeroUsize::MIN).then(|| n.to_string());
|
||||||
|
if let Some(colspan) = span(cell.colspan(styles)) {
|
||||||
|
attrs.push(HtmlAttr::constant("colspan"), colspan);
|
||||||
|
}
|
||||||
|
if let Some(rowspan) = span(cell.rowspan(styles)) {
|
||||||
|
attrs.push(HtmlAttr::constant("rowspan"), rowspan);
|
||||||
|
}
|
||||||
|
HtmlElem::new(tag)
|
||||||
|
.with_body(Some(cell.body.clone()))
|
||||||
|
.with_attrs(attrs)
|
||||||
|
.pack()
|
||||||
|
.spanned(cell.span())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
|
||||||
|
let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack();
|
||||||
|
let mut rows: Vec<_> = grid.entries.chunks(grid.cols.len()).collect();
|
||||||
|
|
||||||
|
let tr = |tag, row: &[Entry]| {
|
||||||
|
let row = row
|
||||||
|
.iter()
|
||||||
|
.flat_map(|entry| entry.as_cell())
|
||||||
|
.map(|cell| show_cell_html(tag, cell, styles));
|
||||||
|
elem(tag::tr, Content::sequence(row))
|
||||||
|
};
|
||||||
|
|
||||||
|
let footer = grid.footer.map(|ft| {
|
||||||
|
let rows = rows.drain(ft.unwrap().start..);
|
||||||
|
elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row))))
|
||||||
|
});
|
||||||
|
let header = grid.header.map(|hd| {
|
||||||
|
let rows = rows.drain(..hd.unwrap().end);
|
||||||
|
elem(tag::thead, Content::sequence(rows.map(|row| tr(tag::th, row))))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut body = Content::sequence(rows.into_iter().map(|row| tr(tag::td, row)));
|
||||||
|
if header.is_some() || footer.is_some() {
|
||||||
|
body = elem(tag::tbody, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = header.into_iter().chain(core::iter::once(body)).chain(footer);
|
||||||
|
elem(tag::table, Content::sequence(content))
|
||||||
|
}
|
||||||
|
|
||||||
impl Show for Packed<TableElem> {
|
impl Show for Packed<TableElem> {
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(if TargetElem::target_in(styles).is_html() {
|
Ok(if TargetElem::target_in(styles).is_html() {
|
||||||
// TODO: This is a hack, it is not clear whether the locator is actually used by HTML.
|
// TODO: This is a hack, it is not clear whether the locator is actually used by HTML.
|
||||||
// How can we find out whether locator is actually used?
|
// How can we find out whether locator is actually used?
|
||||||
let locator = Locator::root();
|
let locator = Locator::root();
|
||||||
let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack();
|
show_cellgrid_html(table_to_cellgrid(self, engine, locator, styles)?, styles)
|
||||||
let grid = table_to_cellgrid(self, engine, locator, styles)?;
|
|
||||||
let rows = grid.entries.chunks(grid.cols.len()).map(|row| {
|
|
||||||
let row = row.iter().flat_map(|entry| entry.as_cell());
|
|
||||||
elem(tag::tr, Content::sequence(row.map(|cell| cell.body.clone())))
|
|
||||||
});
|
|
||||||
let mut rows: Vec<_> = rows.collect();
|
|
||||||
|
|
||||||
let footer = grid.footer.map(|ft| {
|
|
||||||
elem(tag::tfoot, Content::sequence(rows.drain(ft.unwrap().start..)))
|
|
||||||
});
|
|
||||||
let header = grid.header.map(|hd| {
|
|
||||||
elem(tag::thead, Content::sequence(rows.drain(..hd.unwrap().end)))
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut body = Content::sequence(rows);
|
|
||||||
if header.is_some() || footer.is_some() {
|
|
||||||
body = elem(tag::tbody, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = header.into_iter().chain(core::iter::once(body)).chain(footer);
|
|
||||||
elem(tag::table, Content::sequence(content))
|
|
||||||
} else {
|
} else {
|
||||||
BlockElem::multi_layouter(self.clone(), engine.routines.layout_table).pack()
|
BlockElem::multi_layouter(self.clone(), engine.routines.layout_table).pack()
|
||||||
}
|
}
|
||||||
@ -737,20 +764,7 @@ cast! {
|
|||||||
|
|
||||||
impl Show for Packed<TableCell> {
|
impl Show for Packed<TableCell> {
|
||||||
fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
if TargetElem::target_in(styles).is_html() {
|
show_grid_cell(self.body.clone(), self.inset(styles), self.align(styles))
|
||||||
let mut attrs = HtmlAttrs::default();
|
|
||||||
let span = |n: NonZeroUsize| (n != NonZeroUsize::MIN).then(|| n.to_string());
|
|
||||||
if let Some(colspan) = span(self.colspan(styles)) {
|
|
||||||
attrs.push(HtmlAttr::constant("colspan"), colspan);
|
|
||||||
}
|
|
||||||
if let Some(rowspan) = span(self.rowspan(styles)) {
|
|
||||||
attrs.push(HtmlAttr::constant("rowspan"), rowspan);
|
|
||||||
}
|
|
||||||
let body = Some(self.body.clone());
|
|
||||||
Ok(HtmlElem::new(tag::td).with_body(body).with_attrs(attrs).pack())
|
|
||||||
} else {
|
|
||||||
show_grid_cell(self.body.clone(), self.inset(styles), self.align(styles))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +228,8 @@ static EXCEPTION_MAP: phf::Map<&'static str, Exception> = phf::phf_map! {
|
|||||||
.style(FontStyle::Oblique),
|
.style(FontStyle::Oblique),
|
||||||
"NewCMSans10-Regular" => Exception::new()
|
"NewCMSans10-Regular" => Exception::new()
|
||||||
.family("New Computer Modern Sans"),
|
.family("New Computer Modern Sans"),
|
||||||
|
"NewCMSansMath-Regular" => Exception::new()
|
||||||
|
.family("New Computer Modern Sans Math"),
|
||||||
"NewCMUncial08-Bold" => Exception::new()
|
"NewCMUncial08-Bold" => Exception::new()
|
||||||
.family("New Computer Modern Uncial 08"),
|
.family("New Computer Modern Uncial 08"),
|
||||||
"NewCMUncial08-Book" => Exception::new()
|
"NewCMUncial08-Book" => Exception::new()
|
||||||
|
@ -1605,10 +1605,12 @@ impl AtNewline {
|
|||||||
_ => true,
|
_ => true,
|
||||||
},
|
},
|
||||||
AtNewline::StopParBreak => parbreak,
|
AtNewline::StopParBreak => parbreak,
|
||||||
AtNewline::RequireColumn(min_col) => match column {
|
AtNewline::RequireColumn(min_col) => {
|
||||||
Some(column) => column <= min_col,
|
// Don't stop if this newline doesn't start a column (this may
|
||||||
None => false, // Don't stop if we had no column.
|
// be checked on the boundary of lexer modes, since we only
|
||||||
},
|
// report a column in Markup).
|
||||||
|
column.is_some_and(|column| column <= min_col)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1703,10 +1705,13 @@ impl<'s> Parser<'s> {
|
|||||||
self.token.newline.is_some()
|
self.token.newline.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of characters until the most recent newline from the current
|
/// The number of characters until the most recent newline from the start of
|
||||||
/// token, or 0 if it did not follow a newline.
|
/// the current token. Uses a cached value from the newline mode if present.
|
||||||
fn current_column(&self) -> usize {
|
fn current_column(&self) -> usize {
|
||||||
self.token.newline.and_then(|newline| newline.column).unwrap_or(0)
|
self.token
|
||||||
|
.newline
|
||||||
|
.and_then(|newline| newline.column)
|
||||||
|
.unwrap_or_else(|| self.lexer.column(self.token.start))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current token's text.
|
/// The current token's text.
|
||||||
|
@ -5,6 +5,31 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<table><thead><tr><td>The</td><td>first</td><td>and</td></tr><tr><td>the</td><td>second</td><td>row</td></tr></thead><tbody><tr><td>Foo</td><td rowspan="2">Baz</td><td>Bar</td></tr><tr><td>1</td><td>2</td></tr><tr><td colspan="2">3</td><td>4</td></tr></tbody><tfoot><tr><td>The</td><td>last</td><td>row</td></tr></tfoot></table>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>The</th><th>first</th><th>and</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>the</th><th>second</th><th>row</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Foo</td><td rowspan="2">Baz</td><td>Bar</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>1</td><td>2</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">3</td><td>4</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td>The</td><td>last</td><td>row</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -77,6 +77,49 @@ _Shopping list_
|
|||||||
|
|
||||||
#test(indented, manual)
|
#test(indented, manual)
|
||||||
|
|
||||||
|
--- list-indent-bracket-nesting ---
|
||||||
|
// Test list indent nesting behavior when directly at a starting bracket.
|
||||||
|
|
||||||
|
#let indented = {
|
||||||
|
[- indented
|
||||||
|
- less
|
||||||
|
]
|
||||||
|
[- indented
|
||||||
|
- same
|
||||||
|
- then less
|
||||||
|
- then same
|
||||||
|
]
|
||||||
|
[- indented
|
||||||
|
- more
|
||||||
|
- then same
|
||||||
|
- then less
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let item = list.item
|
||||||
|
#let manual = {
|
||||||
|
{
|
||||||
|
item[indented]; [ ]
|
||||||
|
item[less]; [ ]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
item[indented]; [ ]
|
||||||
|
item[same]; [ ]
|
||||||
|
item[then less #{
|
||||||
|
item[then same]
|
||||||
|
}]; [ ]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
item[indented #{
|
||||||
|
item[more]
|
||||||
|
}]; [ ]
|
||||||
|
item[then same]; [ ]
|
||||||
|
item[then less]; [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#test(indented, manual)
|
||||||
|
|
||||||
--- list-tabs ---
|
--- list-tabs ---
|
||||||
// This works because tabs are used consistently.
|
// This works because tabs are used consistently.
|
||||||
- A with 1 tab
|
- A with 1 tab
|
||||||
|
Loading…
x
Reference in New Issue
Block a user