Skip to content

Commit 86b767d

Browse files
committed
engine_spx2html: handle glyphs without associated ActualText
With the glyph-mapping framework that we've put together, this is now actually straightforward. The only hard part is actually dealing with holding mutable borrows of `self` that make it challenging to placate the borrow checker.
1 parent 34df103 commit 86b767d

1 file changed

Lines changed: 88 additions & 7 deletions

File tree

  • crates/engine_spx2html/src

crates/engine_spx2html/src/lib.rs

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ impl EmittingState {
956956
}
957957

958958
/// Figure out if we need to push a space into the text content right now.
959-
fn is_space_needed(&mut self, x0: i32, cur_font_num: Option<FontNum>) -> bool {
959+
fn is_space_needed(&self, x0: i32, cur_font_num: Option<FontNum>) -> bool {
960960
// We never want a leading space.
961961
if self.current_content.is_empty() {
962962
return false;
@@ -1554,19 +1554,100 @@ impl EmittingState {
15541554
});
15551555
}
15561556
} else {
1557+
// Ideally, the vast majority of the time we are using
1558+
// handle_text_and_glyphs and not this function, outside of
1559+
// canvases. But sometimes we get spare glyphs outside of the canvas
1560+
// context. We can use our glyph-mapping infrastructure to try to
1561+
// translate them to Unicode, hoping for the best that the naive
1562+
// inversion suffices.
1563+
15571564
self.set_up_for_font(xs[0], font_num, common);
15581565

15591566
let fi = a_ok_or!(
15601567
self.fonts.get(&font_num);
15611568
["undeclared font {} in glyph run", font_num]
15621569
);
15631570

1564-
tt_warning!(
1565-
common.status,
1566-
"TODO HANDLE glyph_run OUTSIDE OF CANVAS: {} {:?}",
1567-
fi.rel_url,
1568-
glyphs
1569-
);
1571+
// Super lame! Ideally we could just hold a long-lived mutable
1572+
// borrow of `fd`, but that gets treated as a long-lived mutable
1573+
// borrow of `self` which basically makes all other pieces of state
1574+
// inaccessible. So we need to keep on looking up into
1575+
// `self.font_data`, and even then we need to avoid functions that
1576+
// operation on `&mut self` because `fi` is a long-lived *immutable*
1577+
// borrow of `self`. I don't think we can avoid this without doing
1578+
// some significant rearranging of the data structures to allow the
1579+
// different pieces to be borrowed separately.
1580+
1581+
let mut ch_str_buf = [0u8; 4];
1582+
1583+
for (idx, glyph) in glyphs.iter().copied().enumerate() {
1584+
let mc = {
1585+
let fd = self.font_data.get(&fi.fd_key).unwrap();
1586+
fd.lookup_mapping(glyph)
1587+
};
1588+
1589+
if let Some(mc) = mc {
1590+
let (mut ch, need_alt) = match mc {
1591+
MapEntry::Direct(c) => (c, false),
1592+
MapEntry::SubSuperScript(c, _) => (c, true),
1593+
MapEntry::MathGrowingVariant(c, _, _) => (c, true),
1594+
};
1595+
1596+
let alt_index = if need_alt {
1597+
let fd = self.font_data.get_mut(&fi.fd_key).unwrap();
1598+
let map = fd.request_alternative(glyph, ch);
1599+
ch = map.usv;
1600+
Some(map.alternate_map_index)
1601+
} else {
1602+
None
1603+
};
1604+
1605+
// For later: we could select the "default" font at an outer
1606+
// level and only emit tags as needed in here.
1607+
let font_sel = fi.selection_style_text(alt_index);
1608+
1609+
// Stringify the character so that we can use html_escape in
1610+
// case it's a `<` or whatever.
1611+
let ch_as_str = ch.encode_utf8(&mut ch_str_buf);
1612+
1613+
// XXX this is (part of) push_space_if_needed
1614+
if self.is_space_needed(xs[idx], Some(font_num)) {
1615+
self.current_content.push(' ');
1616+
}
1617+
1618+
write!(self.current_content, "<span style=\"{}\">", font_sel).unwrap();
1619+
html_escape::encode_text_to_string(ch_as_str, &mut self.current_content);
1620+
write!(self.current_content, "</span>").unwrap();
1621+
} else {
1622+
tt_warning!(
1623+
common.status,
1624+
"unable to reverse-map glyph {} in font `{}` (face {})",
1625+
glyph,
1626+
fi.rel_url,
1627+
fi.face_index
1628+
);
1629+
}
1630+
1631+
// Jump through the hoops needed by automatic space insertion:
1632+
1633+
let gm = {
1634+
let fd = self.font_data.get(&fi.fd_key).unwrap();
1635+
fd.lookup_metrics(glyphs[idx], fi.size)
1636+
};
1637+
1638+
let advance = match gm {
1639+
Some(gm) => gm.advance,
1640+
None => 0,
1641+
};
1642+
1643+
// XXX this is fn update_content_pos():
1644+
self.last_content_x = xs[idx] + advance;
1645+
1646+
let cur_space_width = self.maybe_get_font_space_width(Some(font_num));
1647+
if cur_space_width.is_some() {
1648+
self.last_content_space_width = cur_space_width;
1649+
}
1650+
}
15701651
}
15711652

15721653
Ok(())

0 commit comments

Comments
 (0)