diff --git a/crates/typst-html/src/css.rs b/crates/typst-html/src/css.rs index 5916d314..ba0b8942 100644 --- a/crates/typst-html/src/css.rs +++ b/crates/typst-html/src/css.rs @@ -26,7 +26,6 @@ impl Properties { } /// Adds a new property in builder-style. - #[expect(unused)] pub fn with(mut self, property: &str, value: impl Display) -> Self { self.push(property, value); self diff --git a/crates/typst-html/src/lib.rs b/crates/typst-html/src/lib.rs index 373da607..68311e8a 100644 --- a/crates/typst-html/src/lib.rs +++ b/crates/typst-html/src/lib.rs @@ -16,7 +16,7 @@ mod typed; pub use self::document::html_document; pub use self::dom::*; pub use self::encode::html; -pub use self::rules::register; +pub use self::rules::{html_span_filled, register}; use ecow::EcoString; use typst_library::Category; diff --git a/crates/typst-html/src/rules.rs b/crates/typst-html/src/rules.rs index acf9046c..0633d4a9 100644 --- a/crates/typst-html/src/rules.rs +++ b/crates/typst-html/src/rules.rs @@ -17,7 +17,7 @@ use typst_library::text::{ HighlightElem, LinebreakElem, OverlineElem, RawElem, RawLine, SmallcapsElem, SpaceElem, StrikeElem, SubElem, SuperElem, UnderlineElem, }; -use typst_library::visualize::ImageElem; +use typst_library::visualize::{Color, ImageElem}; use crate::{FrameElem, HtmlAttrs, HtmlElem, HtmlTag, attr, css, tag}; @@ -427,6 +427,20 @@ const RAW_RULE: ShowFn = |elem, _, styles| { }) }; +/// This is used by `RawElem::synthesize` through a routine. +/// +/// It's a temporary workaround until `TextElem::fill` is supported in HTML +/// export. +#[doc(hidden)] +pub fn html_span_filled(content: Content, color: Color) -> Content { + let span = content.span(); + HtmlElem::new(tag::span) + .with_styles(css::Properties::new().with("color", css::color(color))) + .with_body(Some(content)) + .pack() + .spanned(span) +} + const RAW_LINE_RULE: ShowFn = |elem, _, _| Ok(elem.body.clone()); const IMAGE_RULE: ShowFn = |elem, engine, styles| { diff --git a/crates/typst-library/src/routines.rs b/crates/typst-library/src/routines.rs index 6fa7d69c..3a16e102 100644 --- a/crates/typst-library/src/routines.rs +++ b/crates/typst-library/src/routines.rs @@ -15,6 +15,7 @@ use crate::foundations::{ use crate::introspection::{Introspector, Locator, SplitLocator}; use crate::layout::{Frame, Region}; use crate::model::DocumentInfo; +use crate::visualize::Color; /// Defines the `Routines` struct. macro_rules! routines { @@ -95,6 +96,12 @@ routines! { /// Constructs the `html` module. fn html_module() -> Module + + /// Wraps content in a span with a color. + /// + /// This is a temporary workaround until `TextElem::fill` is supported in + /// HTML export. + fn html_span_filled(content: Content, color: Color) -> Content } /// Defines what kind of realization we are performing. diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index 3d74ed0a..7a3af26f 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -18,11 +18,12 @@ use crate::diag::{ use crate::engine::Engine; use crate::foundations::{ Bytes, Content, Derived, OneOrMultiple, Packed, PlainText, ShowSet, Smart, - StyleChain, Styles, Synthesize, cast, elem, scope, + StyleChain, Styles, Synthesize, Target, TargetElem, cast, elem, scope, }; use crate::layout::{Em, HAlignment}; use crate::loading::{DataSource, Load}; use crate::model::{Figurable, ParElem}; +use crate::routines::Routines; use crate::text::{FontFamily, FontList, LocalName, TextElem, TextSize}; use crate::visualize::Color; @@ -300,8 +301,12 @@ impl RawElem { } impl Synthesize for Packed { - fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> { - let seq = self.highlight(styles); + fn synthesize( + &mut self, + engine: &mut Engine, + styles: StyleChain, + ) -> SourceResult<()> { + let seq = self.highlight(engine.routines, styles); self.lines = Some(seq); Ok(()) } @@ -309,7 +314,7 @@ impl Synthesize for Packed { impl Packed { #[comemo::memoize] - fn highlight(&self, styles: StyleChain) -> Vec> { + fn highlight(&self, routines: &Routines, styles: StyleChain) -> Vec> { let elem = self.as_ref(); let lines = preprocess(&elem.text, styles, self.span()); @@ -341,6 +346,7 @@ impl Packed { }; let foreground = theme.settings.foreground.unwrap_or(synt::Color::BLACK); + let target = styles.get(TargetElem::target); let mut seq = vec![]; if matches!(lang.as_deref(), Some("typ" | "typst" | "typc" | "typm")) { @@ -363,7 +369,15 @@ impl Packed { let span_offset = text[..range.start] .rfind('\n') .map_or(0, |i| range.start - (i + 1)); - styled(&text[range], foreground, style, span, span_offset) + styled( + routines, + target, + &text[range], + foreground, + style, + span, + span_offset, + ) }, &mut |i, range, line| { let span = lines.get(i).map_or_else(Span::detached, |l| l.1); @@ -400,6 +414,8 @@ impl Packed { .flatten() { line_content.push(styled( + routines, + target, piece, foreground, style, @@ -769,6 +785,8 @@ fn preprocess( /// Style a piece of text with a syntect style. fn styled( + routines: &Routines, + target: Target, piece: &str, foreground: synt::Color, style: synt::Style, @@ -782,7 +800,11 @@ fn styled( } if style.foreground != foreground { - body = body.set(TextElem::fill, to_typst(style.foreground).into()); + let color = to_typst(style.foreground); + body = match target { + Target::Html => (routines.html_span_filled)(body, color), + Target::Paged => body.set(TextElem::fill, color.into()), + }; } if style.font_style.contains(synt::FontStyle::BOLD) { diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs index d00b9879..cad7a829 100644 --- a/crates/typst/src/lib.rs +++ b/crates/typst/src/lib.rs @@ -358,4 +358,5 @@ pub static ROUTINES: LazyLock = LazyLock::new(|| Routines { realize: typst_realize::realize, layout_frame: typst_layout::layout_frame, html_module: typst_html::module, + html_span_filled: typst_html::html_span_filled, }); diff --git a/tests/ref/html/raw-html.html b/tests/ref/html/raw-html.html index 2df62ca8..18cf19c9 100644 --- a/tests/ref/html/raw-html.html +++ b/tests/ref/html/raw-html.html @@ -6,6 +6,6 @@

This is *inline*.

-
#set text(blue)
*Hello* _world_!
+
#set text(blue)
*Hello* _world_!