mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Handle boxes and blocks a bit better in HTML export (#5744)
Co-authored-by: Martin Haug <3874949+reknih@users.noreply.github.com>
This commit is contained in:
parent
6fe1e20afb
commit
cd044825fc
@ -14,7 +14,7 @@ use typst_library::html::{
|
||||
use typst_library::introspection::{
|
||||
Introspector, Locator, LocatorLink, SplitLocator, TagElem,
|
||||
};
|
||||
use typst_library::layout::{Abs, Axes, BoxElem, Region, Size};
|
||||
use typst_library::layout::{Abs, Axes, BlockBody, BlockElem, BoxElem, Region, Size};
|
||||
use typst_library::model::{DocumentInfo, ParElem};
|
||||
use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
|
||||
use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
|
||||
@ -197,13 +197,34 @@ fn handle(
|
||||
.into(),
|
||||
);
|
||||
} else if let Some(elem) = child.to_packed::<BoxElem>() {
|
||||
// FIXME: Very incomplete and hacky, but makes boxes kind fulfill their
|
||||
// purpose for now.
|
||||
// TODO: This is rather incomplete.
|
||||
if let Some(body) = elem.body(styles) {
|
||||
let children =
|
||||
html_fragment(engine, body, locator.next(&elem.span()), styles)?;
|
||||
output.extend(children);
|
||||
output.push(
|
||||
HtmlElement::new(tag::span)
|
||||
.with_attr(attr::style, "display: inline-block;")
|
||||
.with_children(children)
|
||||
.spanned(elem.span())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
} else if let Some((elem, body)) =
|
||||
child
|
||||
.to_packed::<BlockElem>()
|
||||
.and_then(|elem| match elem.body(styles) {
|
||||
Some(BlockBody::Content(body)) => Some((elem, body)),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
// TODO: This is rather incomplete.
|
||||
let children = html_fragment(engine, body, locator.next(&elem.span()), styles)?;
|
||||
output.push(
|
||||
HtmlElement::new(tag::div)
|
||||
.with_children(children)
|
||||
.spanned(elem.span())
|
||||
.into(),
|
||||
);
|
||||
} else if child.is::<SpaceElem>() {
|
||||
output.push(HtmlNode::text(' ', child.span()));
|
||||
} else if let Some(elem) = child.to_packed::<TextElem>() {
|
||||
|
@ -210,7 +210,10 @@ impl HtmlAttr {
|
||||
|
||||
/// Creates a compile-time constant `HtmlAttr`.
|
||||
///
|
||||
/// Should only be used in const contexts because it can panic.
|
||||
/// Must only be used in const contexts (in a constant definition or
|
||||
/// explicit `const { .. }` block) because otherwise a panic for a malformed
|
||||
/// attribute or not auto-internible constant will only be caught at
|
||||
/// runtime.
|
||||
#[track_caller]
|
||||
pub const fn constant(string: &'static str) -> Self {
|
||||
if string.is_empty() {
|
||||
@ -605,6 +608,7 @@ pub mod tag {
|
||||
/// Predefined constants for HTML attributes.
|
||||
///
|
||||
/// Note: These are very incomplete.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub mod attr {
|
||||
use super::HtmlAttr;
|
||||
|
||||
@ -619,13 +623,18 @@ pub mod attr {
|
||||
|
||||
attrs! {
|
||||
charset
|
||||
cite
|
||||
colspan
|
||||
content
|
||||
href
|
||||
name
|
||||
value
|
||||
reversed
|
||||
role
|
||||
rowspan
|
||||
start
|
||||
style
|
||||
value
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const aria_level: HtmlAttr = HtmlAttr::constant("aria-level");
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use crate::foundations::{
|
||||
cast, elem, scope, Array, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
||||
Styles, TargetElem,
|
||||
};
|
||||
use crate::html::{attr, tag, HtmlAttr, HtmlElem};
|
||||
use crate::html::{attr, tag, HtmlElem};
|
||||
use crate::layout::{Alignment, BlockElem, Em, HAlignment, Length, VAlignment, VElem};
|
||||
use crate::model::{ListItemLike, ListLike, Numbering, NumberingPattern, ParElem};
|
||||
|
||||
@ -229,10 +229,10 @@ impl Show for Packed<EnumElem> {
|
||||
if TargetElem::target_in(styles).is_html() {
|
||||
let mut elem = HtmlElem::new(tag::ol);
|
||||
if self.reversed(styles) {
|
||||
elem = elem.with_attr(HtmlAttr::constant("reversed"), "reversed");
|
||||
elem = elem.with_attr(attr::reversed, "reversed");
|
||||
}
|
||||
if let Some(n) = self.start(styles).custom() {
|
||||
elem = elem.with_attr(HtmlAttr::constant("start"), eco_format!("{n}"));
|
||||
elem = elem.with_attr(attr::start, eco_format!("{n}"));
|
||||
}
|
||||
let body = Content::sequence(self.children.iter().map(|item| {
|
||||
let mut li = HtmlElem::new(tag::li);
|
||||
|
@ -4,7 +4,7 @@ use crate::foundations::{
|
||||
cast, elem, Content, Depth, Label, NativeElement, Packed, Show, ShowSet, Smart,
|
||||
StyleChain, Styles, TargetElem,
|
||||
};
|
||||
use crate::html::{tag, HtmlAttr, HtmlElem};
|
||||
use crate::html::{attr, tag, HtmlElem};
|
||||
use crate::introspection::Locatable;
|
||||
use crate::layout::{
|
||||
Alignment, BlockBody, BlockElem, Em, HElem, PadElem, Spacing, VElem,
|
||||
@ -194,10 +194,7 @@ impl Show for Packed<QuoteElem> {
|
||||
if let Some(Attribution::Content(attribution)) = attribution {
|
||||
if let Some(link) = attribution.to_packed::<LinkElem>() {
|
||||
if let LinkTarget::Dest(Destination::Url(url)) = &link.dest {
|
||||
elem = elem.with_attr(
|
||||
HtmlAttr::constant("cite"),
|
||||
url.clone().into_inner(),
|
||||
);
|
||||
elem = elem.with_attr(attr::cite, url.clone().into_inner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use crate::foundations::{
|
||||
cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
||||
TargetElem,
|
||||
};
|
||||
use crate::html::{tag, HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag};
|
||||
use crate::html::{attr, tag, HtmlAttrs, HtmlElem, HtmlTag};
|
||||
use crate::introspection::Locator;
|
||||
use crate::layout::grid::resolve::{table_to_cellgrid, Cell, CellGrid, Entry};
|
||||
use crate::layout::{
|
||||
@ -268,10 +268,10 @@ fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content {
|
||||
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);
|
||||
attrs.push(attr::colspan, colspan);
|
||||
}
|
||||
if let Some(rowspan) = span(cell.rowspan(styles)) {
|
||||
attrs.push(HtmlAttr::constant("rowspan"), rowspan);
|
||||
attrs.push(attr::rowspan, rowspan);
|
||||
}
|
||||
HtmlElem::new(tag)
|
||||
.with_body(Some(cell.body.clone()))
|
||||
|
15
tests/ref/html/block-html.html
Normal file
15
tests/ref/html/block-html.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Paragraph
|
||||
</p>
|
||||
<div>
|
||||
Div
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
12
tests/ref/html/box-html.html
Normal file
12
tests/ref/html/box-html.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Text <span style="display: inline-block;">Span</span>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -264,6 +264,13 @@ First!
|
||||
image("/assets/images/rhino.png", width: 30pt)
|
||||
)
|
||||
|
||||
--- box-html html ---
|
||||
Text #box[Span].
|
||||
|
||||
--- block-html html ---
|
||||
Paragraph
|
||||
#block[Div]
|
||||
|
||||
--- container-layoutable-child ---
|
||||
// Test box/block sizing with directly layoutable child.
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user