mirror of
https://github.com/typst/typst
synced 2025-08-23 11:14:13 +08:00
Ensure that hyphenation is possible after a tag (#6807)
This commit is contained in:
parent
720f01a11c
commit
206792bf38
@ -46,6 +46,11 @@ pub enum Item<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Item<'a> {
|
||||
/// Whether this is a tag item.
|
||||
pub fn is_tag(&self) -> bool {
|
||||
matches!(self, Self::Tag(_))
|
||||
}
|
||||
|
||||
/// If this a text item, return it.
|
||||
pub fn text(&self) -> Option<&ShapedText<'a>> {
|
||||
match self {
|
||||
|
@ -80,8 +80,7 @@ impl Line<'_> {
|
||||
// CJK character at line end should not be adjusted.
|
||||
if self
|
||||
.items
|
||||
.last()
|
||||
.and_then(Item::text)
|
||||
.trailing_text()
|
||||
.map(|s| s.cjk_justifiable_at_last())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
@ -176,7 +175,7 @@ pub fn line<'a>(
|
||||
// Add a hyphen at the line start, if a previous dash should be repeated.
|
||||
if let Some(pred) = pred
|
||||
&& pred.dash == Some(Dash::Hard)
|
||||
&& let Some(base) = pred.items.last_text()
|
||||
&& let Some(base) = pred.items.trailing_text()
|
||||
&& should_repeat_hyphen(base.lang, full)
|
||||
&& let Some(hyphen) =
|
||||
ShapedText::hyphen(engine, p.config.fallback, base, trim, false)
|
||||
@ -188,7 +187,7 @@ pub fn line<'a>(
|
||||
|
||||
// Add a hyphen at the line end, if we ended on a soft hyphen.
|
||||
if dash == Some(Dash::Soft)
|
||||
&& let Some(base) = items.last_text()
|
||||
&& let Some(base) = items.trailing_text()
|
||||
&& let Some(hyphen) =
|
||||
ShapedText::hyphen(engine, p.config.fallback, base, trim, true)
|
||||
{
|
||||
@ -253,7 +252,7 @@ fn trim_weak_spacing(items: &mut Items) {
|
||||
}
|
||||
|
||||
// Trim weak spacing at the end of the line.
|
||||
while matches!(items.last(), Some(Item::Absolute(_, true))) {
|
||||
while matches!(items.iter().next_back(), Some(Item::Absolute(_, true))) {
|
||||
items.pop();
|
||||
}
|
||||
}
|
||||
@ -355,7 +354,7 @@ fn adjust_cj_at_line_boundaries(p: &Preparation, text: &str, items: &mut Items)
|
||||
|
||||
/// Add spacing around punctuation marks for CJ glyphs at the line start.
|
||||
fn adjust_cj_at_line_start(p: &Preparation, items: &mut Items) {
|
||||
let Some(shaped) = items.first_text_mut() else { return };
|
||||
let Some(shaped) = items.leading_text_mut() else { return };
|
||||
let Some(glyph) = shaped.glyphs.first() else { return };
|
||||
|
||||
if glyph.is_cjk_right_aligned_punctuation() {
|
||||
@ -380,7 +379,7 @@ fn adjust_cj_at_line_start(p: &Preparation, items: &mut Items) {
|
||||
|
||||
/// Add spacing around punctuation marks for CJ glyphs at the line end.
|
||||
fn adjust_cj_at_line_end(p: &Preparation, items: &mut Items) {
|
||||
let Some(shaped) = items.last_text_mut() else { return };
|
||||
let Some(shaped) = items.trailing_text_mut() else { return };
|
||||
let Some(glyph) = shaped.glyphs.last() else { return };
|
||||
|
||||
// Deal with CJK punctuation at line ends.
|
||||
@ -481,7 +480,7 @@ pub fn commit(
|
||||
}
|
||||
|
||||
// Handle hanging punctuation to the left.
|
||||
if let Some(Item::Text(text)) = line.items.first()
|
||||
if let Some(text) = line.items.leading_text()
|
||||
&& let Some(glyph) = text.glyphs.first()
|
||||
&& !text.dir.is_positive()
|
||||
&& text.styles.get(TextElem::overhang)
|
||||
@ -493,7 +492,7 @@ pub fn commit(
|
||||
}
|
||||
|
||||
// Handle hanging punctuation to the right.
|
||||
if let Some(Item::Text(text)) = line.items.last()
|
||||
if let Some(text) = line.items.trailing_text()
|
||||
&& let Some(glyph) = text.glyphs.last()
|
||||
&& text.dir.is_positive()
|
||||
&& text.styles.get(TextElem::overhang)
|
||||
@ -685,7 +684,7 @@ impl<'a> Items<'a> {
|
||||
}
|
||||
|
||||
/// Iterate over the items.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Item<'a>> {
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Item<'a>> {
|
||||
self.0.iter().map(|(_, item)| &**item)
|
||||
}
|
||||
|
||||
@ -694,33 +693,30 @@ impl<'a> Items<'a> {
|
||||
///
|
||||
/// Note that this is different from `.iter().enumerate()` which would
|
||||
/// provide the indices in visual order!
|
||||
pub fn indexed_iter(&self) -> impl Iterator<Item = &(usize, ItemEntry<'a>)> {
|
||||
pub fn indexed_iter(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = &(usize, ItemEntry<'a>)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Access the first item.
|
||||
pub fn first(&self) -> Option<&Item<'a>> {
|
||||
self.0.first().map(|(_, item)| &**item)
|
||||
/// Access the first item (skipping tags), if it is text.
|
||||
pub fn leading_text(&self) -> Option<&ShapedText<'a>> {
|
||||
self.0.iter().find(|(_, item)| !item.is_tag())?.1.text()
|
||||
}
|
||||
|
||||
/// Access the last item.
|
||||
pub fn last(&self) -> Option<&Item<'a>> {
|
||||
self.0.last().map(|(_, item)| &**item)
|
||||
/// Access the first item (skipping tags) mutably, if it is text.
|
||||
pub fn leading_text_mut(&mut self) -> Option<&mut ShapedText<'a>> {
|
||||
self.0.iter_mut().find(|(_, item)| !item.is_tag())?.1.text_mut()
|
||||
}
|
||||
|
||||
/// Access the last item, if it is text.
|
||||
pub fn last_text(&self) -> Option<&ShapedText<'a>> {
|
||||
self.0.last()?.1.text()
|
||||
/// Access the last item (skipping tags), if it is text.
|
||||
pub fn trailing_text(&self) -> Option<&ShapedText<'a>> {
|
||||
self.0.iter().rev().find(|(_, item)| !item.is_tag())?.1.text()
|
||||
}
|
||||
|
||||
/// Access the first item mutably, if it is text.
|
||||
pub fn first_text_mut(&mut self) -> Option<&mut ShapedText<'a>> {
|
||||
self.0.first_mut()?.1.text_mut()
|
||||
}
|
||||
|
||||
/// Access the last item mutably, if it is text.
|
||||
pub fn last_text_mut(&mut self) -> Option<&mut ShapedText<'a>> {
|
||||
self.0.last_mut()?.1.text_mut()
|
||||
/// Access the last item (skipping tags) mutably, if it is text.
|
||||
pub fn trailing_text_mut(&mut self) -> Option<&mut ShapedText<'a>> {
|
||||
self.0.iter_mut().rev().find(|(_, item)| !item.is_tag())?.1.text_mut()
|
||||
}
|
||||
|
||||
/// Reorder the items starting at the given index to RTL.
|
||||
|
BIN
tests/ref/issue-hyphenate-after-tag.png
Normal file
BIN
tests/ref/issue-hyphenate-after-tag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 674 B |
@ -171,3 +171,12 @@ Hello-#text(red)[world]
|
||||
#set text(costs: (hyphenation: 1%, runt: 2%))
|
||||
#set text(costs: (widow: 3%))
|
||||
#context test(text.costs, (hyphenation: 1%, runt: 2%, widow: 3%, orphan: 100%))
|
||||
|
||||
--- issue-hyphenate-after-tag ---
|
||||
// Ensure that an invisible tag does not prevent hyphenation.
|
||||
#set page(width: 50pt)
|
||||
#set text(hyphenate: true)
|
||||
#show "Tree": emph
|
||||
#show emph: set text(red)
|
||||
#show emph: it => it + metadata(none)
|
||||
Treebeard
|
||||
|
Loading…
x
Reference in New Issue
Block a user