diff --git a/crates/core/src/document/html/engine.rs b/crates/core/src/document/html/engine.rs index a7afabe..6f76a1e 100644 --- a/crates/core/src/document/html/engine.rs +++ b/crates/core/src/document/html/engine.rs @@ -943,7 +943,15 @@ impl Engine { } else if end_index < text.len() { let penalty = if c == '-' { self.hyphen_penalty } else { 0 }; let flagged = penalty > 0; - items.push(ParagraphItem::Penalty { width: 0, penalty, flagged }); + if matches!(parent_style.text_align, TextAlign::Justify | TextAlign::Center) { + items.push(ParagraphItem::Penalty { width: 0, penalty, flagged }); + } else { + let stretch = 3 * space_plan.glyph_advance(0); + items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); + items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + items.push(ParagraphItem::Penalty { width: 0, penalty: 10*penalty, flagged: true }); + items.push(ParagraphItem::Glue { width: 0, stretch: -stretch, shrink: 0 }); + } } } start_index += chunk.len(); @@ -1004,11 +1012,6 @@ impl Engine { style.text_indent }; - let stretch_tolerance = if style.text_align == TextAlign::Justify { - self.stretch_tolerance - } else { - 10.0 - }; let (ascender, descender) = { let fonts = self.fonts.as_mut().unwrap(); let font = fonts.get_mut(style.font_kind, style.font_style, style.font_weight); @@ -1118,25 +1121,21 @@ impl Engine { let mut line_lengths: Vec = para_shape.iter().map(|(a, b)| b - a).collect(); line_lengths[0] -= text_indent; - let mut bps = total_fit(&items, &line_lengths, stretch_tolerance, 0); + let mut bps = total_fit(&items, &line_lengths, self.stretch_tolerance, 0); let mut hyph_indices = Vec::new(); let mut glue_drifts = Vec::new(); - if bps.is_empty() { - let dictionary = if style.text_align == TextAlign::Justify { - hyph_lang(style.language.as_ref().map_or(DEFAULT_HYPH_LANG, String::as_str)) - .and_then(|lang| HYPHENATION_PATTERNS.get(&lang)) - } else { - None - }; - - items = self.hyphenate_paragraph(dictionary, items, &mut hyph_indices); - bps = total_fit(&items, &line_lengths, stretch_tolerance, 0); + if bps.is_empty() && style.text_align != TextAlign::Center { + if let Some(dictionary) = hyph_lang(style.language.as_ref().map_or(DEFAULT_HYPH_LANG, String::as_str)) + .and_then(|lang| HYPHENATION_PATTERNS.get(&lang)) { + items = self.hyphenate_paragraph(style, dictionary, items, &mut hyph_indices); + bps = total_fit(&items, &line_lengths, self.stretch_tolerance, 0); + } } if bps.is_empty() { - bps = standard_fit(&items, &line_lengths, stretch_tolerance); + bps = standard_fit(&items, &line_lengths, self.stretch_tolerance); } if bps.is_empty() { @@ -1495,69 +1494,72 @@ impl Engine { } } - fn hyphenate_paragraph(&mut self, dictionary: Option<&Standard>, items: Vec>, hyph_indices: &mut Vec<[usize; 2]>) -> Vec> { + fn hyphenate_paragraph(&mut self, style: &StyleData, dictionary: &Standard, items: Vec>, hyph_indices: &mut Vec<[usize; 2]>) -> Vec> { let mut hyph_items = Vec::with_capacity(items.len()); for itm in items { match itm { ParagraphItem::Box { data: ParagraphElement::Text(ref element), .. } => { let text = &element.text; - let hyphen_width = if dictionary.is_some() { + let (hyphen_width, stretch) = { let font = self.fonts.as_mut().unwrap() .get_mut(element.font_kind, element.font_style, element.font_weight); font.set_size(element.font_size, self.dpi); - font.plan("-", None, element.font_features.as_deref()).width - } else { - 0 + let plan = font.plan(" -", None, element.font_features.as_deref()); + (plan.glyph_advance(1), 3 * plan.glyph_advance(0)) }; - if let Some(dict) = dictionary { - let mut index_before = text.find(char::is_alphabetic).unwrap_or_else(|| text.len()); - if index_before > 0 { - let subelem = self.box_from_chunk(&text[0..index_before], - 0, + let mut index_before = text.find(char::is_alphabetic).unwrap_or_else(|| text.len()); + if index_before > 0 { + let subelem = self.box_from_chunk(&text[0..index_before], + 0, + element); + hyph_items.push(subelem); + } + + let mut index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) + .map(|i| index_before + i) + .unwrap_or_else(|| text.len()); + while index_before < index_after { + let mut index = 0; + let chunk = &text[index_before..index_after]; + let len_before = hyph_items.len(); + + for segment in dictionary.hyphenate(chunk).iter().segments() { + let subelem = self.box_from_chunk(segment, + index_before + index, element); hyph_items.push(subelem); - } - - let mut index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) - .map(|i| index_before + i) - .unwrap_or_else(|| text.len()); - while index_before < index_after { - let mut index = 0; - let chunk = &text[index_before..index_after]; - let len_before = hyph_items.len(); - for segment in dict.hyphenate(chunk).iter().segments() { - let subelem = self.box_from_chunk(segment, - index_before + index, - element); - hyph_items.push(subelem); - index += segment.len(); - if index < chunk.len() { + index += segment.len(); + if index < chunk.len() { + if style.text_align == TextAlign::Justify { hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: self.hyphen_penalty, flagged: true }); + } else { + hyph_items.push(ParagraphItem::Penalty { width: 0, penalty: INFINITE_PENALTY, flagged: false }); + hyph_items.push(ParagraphItem::Glue { width: 0, stretch, shrink: 0 }); + hyph_items.push(ParagraphItem::Penalty { width: hyphen_width, penalty: 10*self.hyphen_penalty, flagged: true }); + hyph_items.push(ParagraphItem::Glue { width: 0, stretch: -stretch, shrink: 0 }); } } - let len_after = hyph_items.len(); - if len_after > 1 + len_before { - hyph_indices.push([len_before, len_after]); - } - index_before = text[index_after..].find(char::is_alphabetic) - .map(|i| index_after + i) - .unwrap_or_else(|| text.len()); - if index_before > index_after { - let subelem = self.box_from_chunk(&text[index_after..index_before], - index_after, - &element); - hyph_items.push(subelem); - } + } - index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) - .map(|i| index_before + i) - .unwrap_or_else(|| text.len()); + let len_after = hyph_items.len(); + if len_after > 1 + len_before { + hyph_indices.push([len_before, len_after]); } - } else { - let subelem = self.box_from_chunk(text, 0, element); - hyph_items.push(subelem); + index_before = text[index_after..].find(char::is_alphabetic) + .map(|i| index_after + i) + .unwrap_or_else(|| text.len()); + if index_before > index_after { + let subelem = self.box_from_chunk(&text[index_after..index_before], + index_after, + &element); + hyph_items.push(subelem); + } + + index_after = text[index_before..].find(|c: char| !c.is_alphabetic()) + .map(|i| index_before + i) + .unwrap_or_else(|| text.len()); } }, _ => { hyph_items.push(itm) }, @@ -1612,7 +1614,13 @@ impl Engine { line_stats = LineStats::default(); merged_items.push(itm); + if i >= start_index && i < end_index { + start_index = i + 1; + } } else if i >= start_index && i < end_index { + if i > start_index { + index_drift += 1; + } if let ParagraphItem::Box { width, data } = itm { match merged_element { ParagraphElement::Text(TextElement { ref mut text, .. }) => { @@ -1627,8 +1635,6 @@ impl Engine { if !line_stats.started { line_stats.started = true; } - } else { - index_drift += 2; } if i == end_index - 1 { j += 1;