diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs index cec4ca9ef..c5217e8ef 100644 --- a/src/library/text/deco.rs +++ b/src/library/text/deco.rs @@ -90,6 +90,7 @@ pub fn decorate( deco: &Decoration, fonts: &FontStore, text: &Text, + shift: Length, pos: Point, width: Length, ) { @@ -102,7 +103,7 @@ pub fn decorate( }; let evade = deco.evade && deco.line != STRIKETHROUGH; - let offset = deco.offset.unwrap_or(-metrics.position.at(text.size)); + let offset = deco.offset.unwrap_or(-metrics.position.at(text.size)) - shift; let stroke = deco.stroke.unwrap_or(Stroke { paint: text.fill, thickness: metrics.thickness.at(text.size), diff --git a/src/library/text/par.rs b/src/library/text/par.rs index d86eb175a..695d80667 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -555,10 +555,7 @@ fn prepare<'a>( let shift = styles.get(TextNode::BASELINE); if !shift.is_zero() { - Arc::make_mut(&mut frame).baseline = Some( - frame.baseline.unwrap_or(frame.size.y) - - styles.get(TextNode::BASELINE), - ); + Arc::make_mut(&mut frame).translate(Point::with_y(shift)); } items.push(Item::Frame(frame)); diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs index 67fabbd54..1f3d2f55f 100644 --- a/src/library/text/shaping.rs +++ b/src/library/text/shaping.rs @@ -86,12 +86,17 @@ impl<'a> ShapedText<'a> { let mut frame = Frame::new(size); frame.baseline = Some(top); + let shift = self.styles.get(TextNode::BASELINE); + let lang = self.styles.get(TextNode::LANG); + let decos = self.styles.get(TextNode::DECO); + let fill = self.styles.get(TextNode::FILL); + let link = self.styles.get(TextNode::LINK); + for ((face_id, y_offset), group) in self.glyphs.as_ref().group_by_key(|g| (g.face_id, g.y_offset)) { - let pos = Point::new(offset, top + y_offset.at(self.size)); + let pos = Point::new(offset, top + shift + y_offset.at(self.size)); - let fill = self.styles.get(TextNode::FILL); let glyphs = group .iter() .map(|glyph| Glyph { @@ -111,7 +116,7 @@ impl<'a> ShapedText<'a> { let text = Text { face_id, size: self.size, - lang: self.styles.get(TextNode::LANG), + lang, fill, glyphs, }; @@ -119,8 +124,8 @@ impl<'a> ShapedText<'a> { let width = text.width(); // Apply line decorations. - for deco in self.styles.get(TextNode::DECO) { - decorate(&mut frame, &deco, fonts, &text, pos, width); + for deco in &decos { + decorate(&mut frame, &deco, fonts, &text, shift, pos, width); } frame.insert(text_layer, pos, Element::Text(text)); @@ -128,8 +133,8 @@ impl<'a> ShapedText<'a> { } // Apply link if it exists. - if let Some(url) = self.styles.get(TextNode::LINK) { - frame.link(url.clone()); + if let Some(dest) = link { + frame.link(dest.clone()); } frame @@ -408,8 +413,6 @@ fn shape_segment<'a>( let cluster = info.cluster as usize; if info.glyph_id != 0 { - let baseline_shift = ctx.styles.get(TextNode::BASELINE); - // Add the glyph to the shaped output. // TODO: Don't ignore y_advance. ctx.glyphs.push(ShapedGlyph { @@ -417,8 +420,7 @@ fn shape_segment<'a>( glyph_id: info.glyph_id as u16, x_advance: face.to_em(pos[i].x_advance), x_offset: face.to_em(pos[i].x_offset), - y_offset: face.to_em(pos[i].y_offset) - + Em::from_length(baseline_shift, ctx.size), + y_offset: face.to_em(pos[i].y_offset), cluster: base + cluster, safe_to_break: !info.unsafe_to_break(), c: text[cluster ..].chars().next().unwrap(), diff --git a/src/library/text/shift.rs b/src/library/text/shift.rs index 3efa3720a..4eacd3c8a 100644 --- a/src/library/text/shift.rs +++ b/src/library/text/shift.rs @@ -17,7 +17,7 @@ pub type SuperNode = ShiftNode; /// Shift the text into subscript. pub type SubNode = ShiftNode; -#[node(showable)] +#[node] impl ShiftNode { /// Whether to prefer the dedicated sub- and superscript characters of the font. pub const TYPOGRAPHIC: bool = true; @@ -33,8 +33,8 @@ impl ShiftNode { } impl Show for ShiftNode { - fn unguard(&self, sel: Selector) -> ShowNode { - Self(self.0.unguard(sel)).pack() + fn unguard(&self, _: Selector) -> ShowNode { + Self(self.0.clone()).pack() } fn encode(&self, _: StyleChain) -> Dict { @@ -43,9 +43,9 @@ impl Show for ShiftNode { fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult { let mut transformed = None; - if styles.get(ShiftNode::::TYPOGRAPHIC) { + if styles.get(Self::TYPOGRAPHIC) { if let Some(text) = search_text(&self.0, S) { - if check_str_in_family(&mut ctx.fonts, &text, styles) { + if is_shapable(&mut ctx.fonts, &text, styles) { transformed = Some(Content::Text(text)); } } @@ -53,8 +53,8 @@ impl Show for ShiftNode { Ok(transformed.unwrap_or_else(|| { let mut map = StyleMap::new(); - map.set(TextNode::BASELINE, styles.get(ShiftNode::::BASELINE)); - map.set(TextNode::SIZE, styles.get(ShiftNode::::SIZE)); + map.set(TextNode::BASELINE, styles.get(Self::BASELINE)); + map.set(TextNode::SIZE, styles.get(Self::SIZE)); self.0.clone().styled_with_map(map) })) } @@ -73,9 +73,8 @@ fn search_text(content: &Content, mode: ScriptKind) -> Option { } None } - Content::Space => Some(EcoString::from(" ")), + Content::Space => Some(' '.into()), Content::Empty => Some(EcoString::new()), - Content::Styled(arc) => search_text(&arc.0, mode), Content::Sequence(seq) => { let mut full = EcoString::new(); for item in seq.iter() { @@ -92,11 +91,10 @@ fn search_text(content: &Content, mode: ScriptKind) -> Option { /// Checks whether the first retrievable family contains all code points of the /// given string. -fn check_str_in_family(fonts: &mut FontStore, text: &str, styles: StyleChain) -> bool { +fn is_shapable(fonts: &mut FontStore, text: &str, styles: StyleChain) -> bool { for family in styles.get(TextNode::FAMILY).iter() { if let Some(face_id) = fonts.select(family.as_str(), variant(styles)) { - let face = fonts.get(face_id); - let ttf = face.ttf(); + let ttf = fonts.get(face_id).ttf(); return text.chars().all(|c| ttf.glyph_index(c).is_some()); } } @@ -110,8 +108,7 @@ fn convert_script(text: &str, mode: ScriptKind) -> Option { let mut result = EcoString::with_capacity(text.len()); let converter = match mode { SUPERSCRIPT => to_superscript_codepoint, - SUBSCRIPT => to_subscript_codepoint, - _ => panic!("unknown script kind"), + SUBSCRIPT | _ => to_subscript_codepoint, }; for c in text.chars() { diff --git a/tests/ref/text/baseline.png b/tests/ref/text/baseline.png index 7baa01679..37bb19499 100644 Binary files a/tests/ref/text/baseline.png and b/tests/ref/text/baseline.png differ diff --git a/tests/ref/text/shifts.png b/tests/ref/text/shifts.png index 52e897fd4..c1e9dec6b 100644 Binary files a/tests/ref/text/shifts.png and b/tests/ref/text/shifts.png differ diff --git a/tests/typ/text/shifts.typ b/tests/typ/text/shifts.typ index 624db7e1f..b70425c4e 100644 --- a/tests/typ/text/shifts.typ +++ b/tests/typ/text/shifts.typ @@ -10,3 +10,10 @@ --- #set super(typographic: false, baseline: -0.25em, size: 0.7em) n#super[1], n#sub[2], ... n#super[N] + +--- +#set underline(stroke: 0.5pt, offset: 0.15em) + +#underline[The claim#super[\[4\]]] has been disputed. \ +The claim#super[#underline[\[4\]]] has been disputed. \ +It really has been#super(box(text(baseline: 0pt, underline[\[4\]]))) \