Skip to content

Commit 9906d5b

Browse files
committed
engine_spx2html: add ability to associate specific HTML tags with new font families
1 parent 317a5e0 commit 9906d5b

2 files changed

Lines changed: 106 additions & 25 deletions

File tree

crates/engine_spx2html/src/html.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ macro_rules! emit_element_data {
1313
empty($empty:literal)
1414
autoclosed($($acvname:ident),*)
1515
],)+) => {
16-
#[derive(Clone, Debug, Eq, PartialEq)]
16+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1717
pub enum Element {
1818
$($vname,)+
1919
Other(String),

crates/engine_spx2html/src/lib.rs

Lines changed: 105 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ impl<'a> XdvEvents for EngineState<'a> {
151151

152152
let do_init = match &mut self.state {
153153
State::Invalid => false,
154-
State::Initializing(s) => s.cur_font_family_definition.is_none(),
154+
State::Initializing(s) => {
155+
s.cur_font_family_definition.is_none()
156+
&& s.cur_font_family_tag_associations.is_none()
157+
}
155158
State::Emitting(_) => false,
156159
};
157160

@@ -245,7 +248,10 @@ struct InitializationState {
245248
main_body_font_num: Option<i32>,
246249
/// Keyed by the "regular" font-num
247250
font_families: HashMap<FontNum, FontFamily>,
251+
tag_associations: HashMap<Element, FontNum>,
252+
248253
cur_font_family_definition: Option<FontFamilyBuilder>,
254+
cur_font_family_tag_associations: Option<FontFamilyTagAssociator>,
249255

250256
variables: HashMap<String, String>,
251257
}
@@ -262,7 +268,10 @@ impl Default for InitializationState {
262268
font_data: Default::default(),
263269
main_body_font_num: None,
264270
font_families: Default::default(),
271+
tag_associations: Default::default(),
272+
265273
cur_font_family_definition: None,
274+
cur_font_family_tag_associations: None,
266275

267276
variables: Default::default(),
268277
}
@@ -380,6 +389,10 @@ impl InitializationState {
380389
self.handle_start_define_font_family()
381390
} else if contents == "tdux:endDefineFontFamily" {
382391
self.handle_end_define_font_family(common)
392+
} else if contents == "tdux:startFontFamilyTagAssociations" {
393+
self.handle_start_font_family_tag_associations()
394+
} else if contents == "tdux:endFontFamilyTagAssociations" {
395+
self.handle_end_font_family_tag_associations(common)
383396
} else if let Some(_remainder) = contents.strip_prefix("tdux:provideFile ") {
384397
tt_warning!(common.status, "ignoring too-soon tdux:provideFile special");
385398
Ok(())
@@ -468,6 +481,30 @@ impl InitializationState {
468481
Ok(())
469482
}
470483

484+
// "Font family tag associations", telling us which font family is the
485+
// default depending on which tag we're in. For instance, typical templates
486+
// will default to the monospace font inside `<code>` tags.
487+
488+
fn handle_start_font_family_tag_associations(&mut self) -> Result<()> {
489+
self.cur_font_family_tag_associations = Some(FontFamilyTagAssociator::default());
490+
Ok(())
491+
}
492+
493+
fn handle_end_font_family_tag_associations(&mut self, common: &mut Common) -> Result<()> {
494+
if let Some(mut a) = self.cur_font_family_tag_associations.take() {
495+
for (k, v) in a.assoc.drain() {
496+
self.tag_associations.insert(k, v);
497+
}
498+
} else {
499+
tt_warning!(
500+
common.status,
501+
"end of font-family tag-association block that didn't start"
502+
);
503+
}
504+
505+
Ok(())
506+
}
507+
471508
/// In the initialization state, this should only get called if we're in a
472509
/// font-family definition (in which case we're using the contents to learn
473510
/// the definition of a font family). Otherwise, the higher-level callback
@@ -479,24 +516,37 @@ impl InitializationState {
479516
_glyphs: &[u16],
480517
_xs: &[i32],
481518
_ys: &[i32],
482-
_common: &mut Common,
519+
common: &mut Common,
483520
) -> Result<()> {
484-
let b = self.cur_font_family_definition.as_mut().unwrap();
485-
486-
if text.starts_with("bold-italic") {
487-
b.bold_italic = Some(font_num);
488-
} else if text.starts_with("bold") {
489-
b.bold = Some(font_num);
490-
} else if text.starts_with("italic") {
491-
b.italic = Some(font_num);
492-
} else {
493-
b.regular = Some(font_num);
521+
if let Some(b) = self.cur_font_family_definition.as_mut() {
522+
if text.starts_with("bold-italic") {
523+
b.bold_italic = Some(font_num);
524+
} else if text.starts_with("bold") {
525+
b.bold = Some(font_num);
526+
} else if text.starts_with("italic") {
527+
b.italic = Some(font_num);
528+
} else {
529+
b.regular = Some(font_num);
494530

495-
// Say that the "regular" font of the first font family definition
496-
// is the main body font.
497-
if self.main_body_font_num.is_none() {
498-
self.main_body_font_num = Some(font_num);
531+
// Say that the "regular" font of the first font family definition
532+
// is the main body font.
533+
if self.main_body_font_num.is_none() {
534+
self.main_body_font_num = Some(font_num);
535+
}
499536
}
537+
} else if let Some(a) = self.cur_font_family_tag_associations.as_mut() {
538+
for tagname in text.split_whitespace() {
539+
let el: Element = tagname.parse().unwrap();
540+
a.assoc.insert(el, font_num);
541+
}
542+
} else {
543+
// This shouldn't happen; the top-level processor should exit init
544+
// phase if it's invoked and none of the above cases hold.
545+
tt_warning!(
546+
common.status,
547+
"internal bug; losing text `{}` in initialization phase",
548+
text
549+
);
500550
}
501551

502552
Ok(())
@@ -559,6 +609,7 @@ impl InitializationState {
559609
context,
560610
fonts: self.fonts,
561611
font_families: self.font_families,
612+
tag_associations: self.tag_associations,
562613
rems_per_tex,
563614
font_data: self.font_data,
564615
next_template_path: self.next_template_path,
@@ -589,6 +640,7 @@ struct EmittingState {
589640

590641
/// Keyed by the "regular" font
591642
font_families: HashMap<FontNum, FontFamily>,
643+
tag_associations: HashMap<Element, FontNum>,
592644

593645
rems_per_tex: f32,
594646
font_data: HashMap<usize, FontData>,
@@ -770,10 +822,21 @@ impl EmittingState {
770822

771823
let el = self.create_elem(name, true, common);
772824

773-
let new_item = ElementState {
774-
elem: Some(el),
775-
origin,
776-
..*self.cur_elstate()
825+
let new_item = {
826+
let cur = self.cur_elstate();
827+
828+
let font_family_id = self
829+
.tag_associations
830+
.get(&el)
831+
.copied()
832+
.unwrap_or(cur.font_family_id);
833+
834+
ElementState {
835+
elem: Some(el),
836+
origin,
837+
font_family_id,
838+
..*cur
839+
}
777840
};
778841

779842
self.elem_stack.push(new_item);
@@ -1034,11 +1097,24 @@ impl EmittingState {
10341097
}
10351098

10361099
let el = self.create_elem(tagname, true, common);
1037-
let mut elstate = ElementState {
1038-
elem: Some(el),
1039-
origin: ElementOrigin::Manual,
1040-
..*self.cur_elstate()
1100+
1101+
let mut elstate = {
1102+
let cur = self.cur_elstate();
1103+
1104+
let font_family_id = self
1105+
.tag_associations
1106+
.get(&el)
1107+
.copied()
1108+
.unwrap_or(cur.font_family_id);
1109+
1110+
ElementState {
1111+
elem: Some(el),
1112+
origin: ElementOrigin::Manual,
1113+
font_family_id,
1114+
..*cur
1115+
}
10411116
};
1117+
10421118
let mut classes = Vec::new();
10431119
let mut styles = Vec::new();
10441120
let mut unquoted_attrs = Vec::new();
@@ -1971,3 +2047,8 @@ enum FamilyRelativeFontId {
19712047
/// family.
19722048
Other(FontNum),
19732049
}
2050+
2051+
#[derive(Debug, Default)]
2052+
struct FontFamilyTagAssociator {
2053+
assoc: HashMap<Element, FontNum>,
2054+
}

0 commit comments

Comments
 (0)