mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-31 15:49:42 +00:00
Merge pull request #1491 from Hamza0101/fix/underline-under-tabs
An underline runs across a tab, as the rest of the line's text (#1466)
This commit is contained in:
3 files changed
+98
-2
No files matched your search
@@ -165,6 +165,20 @@ pub struct PlacedGlyph {
|
||||
}
|
||||
|
||||
impl PlacedGlyph {
|
||||
/// Whether an underline or a strikethrough runs across this glyph.
|
||||
///
|
||||
/// A tab is not drawn, but it is a character with an advance, and the rule runs across its
|
||||
/// whole width as InDesign draws it: forms rule their fill-in lines with underlined tabs, and
|
||||
/// without this they lost every writing line (#1466). The spaces a line break trimmed off the
|
||||
/// end, the break characters themselves and the markers' carriers are not part of the line's
|
||||
/// text and are not spanned.
|
||||
pub fn spans_rules(&self) -> bool {
|
||||
if self.visible {
|
||||
return true;
|
||||
}
|
||||
self.adv > 0.0 && matches!(self.rendered_char, '\t' | designcraft_doc::story::RIGHT_INDENT_TAB)
|
||||
}
|
||||
|
||||
/// In a vertical frame, the turn that sets this glyph upright, applied after drawing it at `x`
|
||||
/// on `baseline`: it hangs from its vertical origin ([`designcraft_fonts::FontFace::v_origin`],
|
||||
/// centred across) at `x` on the line's centre, the middle of the font's em box; a tate-chu-yoko
|
||||
|
||||
+37
-1
@@ -160,7 +160,7 @@ fn cluster_chars(members: &[PlacedGlyph], cluster: &str) -> Option<Vec<(usize, c
|
||||
fn text_rules(cs: &ComposedStory, ft: &FrameText, underline: bool) -> Vec<(designcraft_compose::Rule, Rect)> {
|
||||
let mut bars: Vec<(designcraft_compose::Rule, Rect)> = Vec::new();
|
||||
for line in &ft.lines {
|
||||
for g in line.glyphs.iter().filter(|g| g.visible) {
|
||||
for g in line.glyphs.iter().filter(|g| g.spans_rules()) {
|
||||
let style = &cs.styles[g.style as usize];
|
||||
let (on, rule) = if underline { (style.underline, &style.underline_rule) } else { (style.strikethrough, &style.strike_rule) };
|
||||
if !on {
|
||||
@@ -638,3 +638,39 @@ fn glyph_outlines(glyphs: &[PlacedGlyph], size: f64, tan: f64) -> BezPath {
|
||||
}
|
||||
bp
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod rule_tests {
|
||||
use super::*;
|
||||
use designcraft_compose::Cache;
|
||||
use designcraft_doc::{Document, ParaFormat, SpreadRef};
|
||||
use designcraft_geom::Rect;
|
||||
|
||||
/// A tab draws nothing but advances to its stop, and the underline runs across it, as it does
|
||||
/// on screen: a form's fill-in lines are underlined tabs (#1466).
|
||||
#[test]
|
||||
fn an_underline_runs_across_a_tab() {
|
||||
let mut doc = Document::new(&Default::default());
|
||||
for style in &mut doc.styles_mut().paragraph {
|
||||
style.chars.underline = Some(true);
|
||||
}
|
||||
let layer = doc.default_layer();
|
||||
let (_, sid) =
|
||||
doc.add_text_frame(SpreadRef::Doc(0), Rect::new(36.0, 36.0, 400.0, 300.0), layer, "Name\tCity", ParaFormat::default()).unwrap();
|
||||
let cs = Cache::new().get(&doc, sid, None);
|
||||
let line = &cs.frames[0].lines[0];
|
||||
let tab = line.glyphs.iter().find(|g| g.rendered_char == '\t').expect("the tab is placed on the line");
|
||||
let bars = text_rules(&cs, &cs.frames[0], true);
|
||||
assert_eq!(bars.len(), 1, "one bar across the line, not one before the tab and one after: {bars:?}");
|
||||
let bar = bars[0].1;
|
||||
assert!(bar.x0 <= tab.x + 0.5 && bar.x1 >= tab.x + tab.adv - 0.5, "the bar covers the tab: {bar:?}, tab {} + {}", tab.x, tab.adv);
|
||||
// The strikethrough of the same text behaves the same way.
|
||||
for style in &mut doc.styles_mut().paragraph {
|
||||
style.chars.underline = None;
|
||||
style.chars.strikethrough = Some(true);
|
||||
}
|
||||
let cs = Cache::new().get(&doc, sid, None);
|
||||
assert!(text_rules(&cs, &cs.frames[0], true).is_empty(), "no underline now");
|
||||
assert_eq!(text_rules(&cs, &cs.frames[0], false).len(), 1, "one strike across the line");
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ fn build_line(db: &FontDb, cs: &ComposedStory, l: &Line, vertical: bool) -> Line
|
||||
let mut strikes: Vec<(String, f32, Rect)> = Vec::new();
|
||||
let mut glyphs = 0;
|
||||
for g in &l.glyphs {
|
||||
if !g.visible {
|
||||
if !g.spans_rules() {
|
||||
continue;
|
||||
}
|
||||
let style = &cs.styles[g.style as usize];
|
||||
@@ -185,6 +185,10 @@ fn build_line(db: &FontDb, cs: &ComposedStory, l: &Line, vertical: bool) -> Line
|
||||
}
|
||||
}
|
||||
}
|
||||
// A tab carries the rule across its width but draws nothing itself.
|
||||
if !g.visible {
|
||||
continue;
|
||||
}
|
||||
let outline = db.outline(&g.face, g.gid);
|
||||
if outline.elements().is_empty() {
|
||||
continue;
|
||||
@@ -725,3 +729,45 @@ mod cache_identity_tests {
|
||||
assert_eq!(glyphs.lines[0].runs, runs);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod rule_tests {
|
||||
use super::*;
|
||||
|
||||
/// A tab is not drawn, but it is a character with an advance, and InDesign underlines it:
|
||||
/// forms rule their fill-in lines with underlined tabs (`Name ⇥⇥⇥⇥, city ⇥⇥⇥⇥`). The bars
|
||||
/// stopped at the last letter before each tab, so an imported form lost every writing line
|
||||
/// (#1466).
|
||||
#[test]
|
||||
fn an_underline_runs_across_a_tab() {
|
||||
let mut doc = designcraft_doc::Document::new(&Default::default());
|
||||
for style in &mut doc.styles_mut().paragraph {
|
||||
style.chars.underline = Some(true);
|
||||
}
|
||||
let layer = doc.default_layer();
|
||||
let (_, sid) = doc
|
||||
.add_text_frame(
|
||||
designcraft_doc::SpreadRef::Doc(0),
|
||||
Rect::new(36.0, 36.0, 400.0, 300.0),
|
||||
layer,
|
||||
"Name\tCity",
|
||||
designcraft_doc::ParaFormat::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let owner = crate::Cache::new();
|
||||
let cs = owner.get(&doc, sid, None);
|
||||
let line = &cs.frames[0].lines[0];
|
||||
let tab = line.glyphs.iter().find(|g| g.rendered_char == '\t').expect("the tab is placed on the line");
|
||||
assert!(!tab.visible, "a tab draws nothing itself");
|
||||
assert!(tab.adv > 1.0, "it advances to its stop: {}", tab.adv);
|
||||
|
||||
let mut cache = GlyphCache::default();
|
||||
cache.tick();
|
||||
let glyphs = cache.get(&cs, &cs.frames[0]);
|
||||
let bars = &glyphs.lines[0].underlines;
|
||||
assert_eq!(bars.len(), 1, "one bar across the line, not one before the tab and one after: {bars:?}");
|
||||
let bar = bars[0].2;
|
||||
assert!(bar.x0 <= tab.x + 0.5, "the bar starts at the text, not at the tab: {bar:?}");
|
||||
assert!(bar.x1 >= tab.x + tab.adv - 0.5, "the bar covers the tab's width: {bar:?}, tab {} + {}", tab.x, tab.adv);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user