summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-06-26 15:44:45 +0200
committerGitHub <noreply@github.com>2025-06-26 13:44:45 +0000
commit9311f6f08eca583b6214834148c94685f0161c21 (patch)
tree3d217e63cbd2fd162abe98ba0f9818058c39dc27 /crates
parent7420ec972ffd6e4893fa24d12992b375da6b11fa (diff)
Basic support for text decoration functions in HTML (#6510)
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-library/src/text/deco.rs32
1 files changed, 31 insertions, 1 deletions
diff --git a/crates/typst-library/src/text/deco.rs b/crates/typst-library/src/text/deco.rs
index 7aa06e81..d745a48f 100644
--- a/crates/typst-library/src/text/deco.rs
+++ b/crates/typst-library/src/text/deco.rs
@@ -2,7 +2,10 @@ use smallvec::smallvec;
use crate::diag::SourceResult;
use crate::engine::Engine;
-use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain};
+use crate::foundations::{
+ elem, Content, NativeElement, Packed, Show, Smart, StyleChain, TargetElem,
+};
+use crate::html::{attr, tag, HtmlElem};
use crate::layout::{Abs, Corners, Length, Rel, Sides};
use crate::text::{BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric};
use crate::visualize::{Color, FixedStroke, Paint, Stroke};
@@ -81,6 +84,16 @@ pub struct UnderlineElem {
impl Show for Packed<UnderlineElem> {
#[typst_macros::time(name = "underline", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
+ if TargetElem::target_in(styles).is_html() {
+ // Note: In modern HTML, `<u>` is not the underline element, but
+ // rather an "Unarticulated Annotation" element (see HTML spec
+ // 4.5.22). Using `text-decoration` instead is recommended by MDN.
+ return Ok(HtmlElem::new(tag::span)
+ .with_attr(attr::style, "text-decoration: underline")
+ .with_body(Some(self.body.clone()))
+ .pack());
+ }
+
Ok(self.body.clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Underline {
stroke: self.stroke(styles).unwrap_or_default(),
@@ -173,6 +186,13 @@ pub struct OverlineElem {
impl Show for Packed<OverlineElem> {
#[typst_macros::time(name = "overline", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
+ if TargetElem::target_in(styles).is_html() {
+ return Ok(HtmlElem::new(tag::span)
+ .with_attr(attr::style, "text-decoration: overline")
+ .with_body(Some(self.body.clone()))
+ .pack());
+ }
+
Ok(self.body.clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Overline {
stroke: self.stroke(styles).unwrap_or_default(),
@@ -250,6 +270,10 @@ pub struct StrikeElem {
impl Show for Packed<StrikeElem> {
#[typst_macros::time(name = "strike", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
+ if TargetElem::target_in(styles).is_html() {
+ return Ok(HtmlElem::new(tag::s).with_body(Some(self.body.clone())).pack());
+ }
+
Ok(self.body.clone().styled(TextElem::set_deco(smallvec![Decoration {
// Note that we do not support evade option for strikethrough.
line: DecoLine::Strikethrough {
@@ -345,6 +369,12 @@ pub struct HighlightElem {
impl Show for Packed<HighlightElem> {
#[typst_macros::time(name = "highlight", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
+ if TargetElem::target_in(styles).is_html() {
+ return Ok(HtmlElem::new(tag::mark)
+ .with_body(Some(self.body.clone()))
+ .pack());
+ }
+
Ok(self.body.clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Highlight {
fill: self.fill(styles),