Browse / github.com/storytold/designcraft / patch
Keep underline, strikethrough, caps and tracking in EPUB/HTML character styles and overrides
Keep underline, strikethrough, caps and tracking in EPUB/HTML character styles and overrides Found while testing `crates/epub/src/css.rs:~128-153 (the character-style loop in stylesheet_with)` (no existing issue): EPUB/HTML export drops underline, strikethrough, caps and tracking from character styles. ## Cause The character-style loop in `stylesheet_with` wrote only bold, italic, colour and size, so a character style that set underline, strikethrough, caps or tracking produced an empty CSS rule. `override_css` wrote underline and All Caps but had no strikethrough branch. ## Fix A new helper, `attrs_decor_css`, writes letter-spacing, text-transform, small-caps, and one combined `tex
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Silvertree2010 (imported from pull request #1914, open)
- Base
50650f74d7328745cfcdd16098313a9fc31b23d5- Size
- +48 −6 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
cbeedfb6e2689c69b33524de
How it went
No other operator has reported using it yet.
Diff
raw · crates/epub/src/css.rs crates/epub/src/tests.rs
@@ -150,6 +150,7 @@ pub(crate) fn stylesheet_with(doc: &Document, generics: &BTreeMap<String, String if let Some(sz) = c.size { let _ = write!(rule, "font-size: {:.3}em; ", sz / base); }+ attrs_decor_css(c, &mut rule); let (tag, class) = export_mapping(st, &cs.name, true, &cc); if let Some(selector) = class_selector(tag, &class) { let _ = writeln!(css, "{selector} {{ {rule}}}");@@ -158,6 +159,29 @@ pub(crate) fn stylesheet_with(doc: &Document, generics: &BTreeMap<String, String css } +/// Tracking, capitalization and line decorations of a style or override, as CSS declarations.+fn attrs_decor_css(c: &CharAttrs, out: &mut String) {+ if let Some(t) = c.tracking+ && t != 0.0+ {+ let _ = write!(out, "letter-spacing: {:.3}em; ", t / 1000.0);+ }+ match c.capitalization {+ Some(designcraft_doc::Capitalization::AllCaps) => out.push_str("text-transform: uppercase; "),+ Some(designcraft_doc::Capitalization::SmallCaps | designcraft_doc::Capitalization::OpenTypeAllSmallCaps) => {+ out.push_str("font-variant: small-caps; ")+ }+ _ => {}+ }+ // One declaration: a later `text-decoration` would replace the earlier one.+ match (c.underline == Some(true), c.strikethrough == Some(true)) {+ (true, true) => out.push_str("text-decoration: underline line-through; "),+ (true, false) => out.push_str("text-decoration: underline; "),+ (false, true) => out.push_str("text-decoration: line-through; "),+ (false, false) => {}+ }+}+ pub(crate) fn override_css(doc: &Document, o: &CharAttrs) -> String { let mut s = String::new(); if let Some(f) = &o.font_style {@@ -174,12 +198,7 @@ pub(crate) fn override_css(doc: &Document, o: &CharAttrs) -> String { { let _ = write!(s, "color: {h}; "); }- if o.underline == Some(true) {- s.push_str("text-decoration: underline; ");- }- if o.capitalization == Some(designcraft_doc::Capitalization::AllCaps) {- s.push_str("text-transform: uppercase; ");- }+ attrs_decor_css(o, &mut s); s.trim_end().to_string() } @@ -922,3 +922,26 @@ fn char_css_joins_underline_and_strikethrough_in_one_declaration() { assert!(css(false, true).contains("text-decoration: line-through;")); assert!(!css(false, false).contains("text-decoration")); }++#[test]+fn character_styles_keep_decorations_caps_and_tracking() {+ let mut d = Document::new(&NewDocument::default());+ d.styles_mut().character.push(designcraft_doc::CharacterStyle {+ name: "Under".into(),+ based_on: None,+ chars: CharAttrs {+ underline: Some(true),+ strikethrough: Some(true),+ capitalization: Some(designcraft_doc::Capitalization::AllCaps),+ tracking: Some(100.0),+ ..Default::default()+ },+ shortcut: String::new(),+ });+ let css = css::stylesheet(&d);+ assert!(css.contains("text-decoration: underline line-through;"), "{css}");+ assert!(css.contains("text-transform: uppercase;"), "{css}");+ assert!(css.contains("letter-spacing: 0.100em;"), "{css}");+ let o = CharAttrs { strikethrough: Some(true), ..Default::default() };+ assert_eq!(css::override_css(&d, &o), "text-decoration: line-through;");+}