summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfrozolotl <44589151+frozolotl@users.noreply.github.com>2024-03-01 09:18:32 +0000
committerGitHub <noreply@github.com>2024-03-01 09:18:32 +0000
commit67ba8d6c0e7b3326283a8f67d2546cd52df59970 (patch)
treed22794627b579d5425b682cde6a436518ef12f89
parent030041466b5b8453ca23e43a6385f4592f78a56c (diff)
Add rounded corners and strokes to highlight (#3526)
-rw-r--r--crates/typst/src/text/deco.rs65
-rw-r--r--tests/ref/text/deco.pngbin55184 -> 64132 bytes
-rw-r--r--tests/typ/text/deco.typ12
3 files changed, 66 insertions, 11 deletions
diff --git a/crates/typst/src/text/deco.rs b/crates/typst/src/text/deco.rs
index 7f0b9e46..d105a353 100644
--- a/crates/typst/src/text/deco.rs
+++ b/crates/typst/src/text/deco.rs
@@ -5,12 +5,14 @@ use ttf_parser::{GlyphId, OutlineBuilder};
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain};
-use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
+use crate::layout::{
+ Abs, Corners, Em, Frame, FrameItem, Length, Point, Rel, Sides, Size,
+};
use crate::syntax::Span;
use crate::text::{
BottomEdge, BottomEdgeMetric, TextElem, TextItem, TopEdge, TopEdgeMetric,
};
-use crate::visualize::{Color, FixedStroke, Geometry, Paint, Stroke};
+use crate::visualize::{styled_rect, Color, FixedStroke, Geometry, Paint, Stroke};
/// Underlines text.
///
@@ -283,6 +285,12 @@ pub struct HighlightElem {
#[default(Color::from_u8(0xFF, 0xFD, 0x11, 0xA1).into())]
pub fill: Paint,
+ /// The highlight's border color. See the
+ /// [rectangle's documentation]($rect.stroke) for more details.
+ #[resolve]
+ #[fold]
+ pub stroke: Sides<Option<Option<Stroke>>>,
+
/// The top end of the background rectangle.
///
/// ```example
@@ -316,6 +324,12 @@ pub struct HighlightElem {
#[resolve]
pub extent: Length,
+ /// How much to round the highlight's corners. See the
+ /// [rectangle's documentation]($rect.radius) for more details.
+ #[resolve]
+ #[fold]
+ pub radius: Corners<Option<Rel<Length>>>,
+
/// The content that should be highlighted.
#[required]
pub body: Content,
@@ -327,8 +341,13 @@ impl Show for Packed<HighlightElem> {
Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Highlight {
fill: self.fill(styles),
+ stroke: self
+ .stroke(styles)
+ .unwrap_or_default()
+ .map(|stroke| stroke.map(Stroke::unwrap_or_default)),
top_edge: self.top_edge(styles),
bottom_edge: self.bottom_edge(styles),
+ radius: self.radius(styles).unwrap_or_default(),
},
extent: self.extent(styles),
}])))
@@ -348,10 +367,30 @@ pub struct Decoration {
/// A kind of decorative line.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum DecoLine {
- Underline { stroke: Stroke<Abs>, offset: Smart<Abs>, evade: bool, background: bool },
- Strikethrough { stroke: Stroke<Abs>, offset: Smart<Abs>, background: bool },
- Overline { stroke: Stroke<Abs>, offset: Smart<Abs>, evade: bool, background: bool },
- Highlight { fill: Paint, top_edge: TopEdge, bottom_edge: BottomEdge },
+ Underline {
+ stroke: Stroke<Abs>,
+ offset: Smart<Abs>,
+ evade: bool,
+ background: bool,
+ },
+ Strikethrough {
+ stroke: Stroke<Abs>,
+ offset: Smart<Abs>,
+ background: bool,
+ },
+ Overline {
+ stroke: Stroke<Abs>,
+ offset: Smart<Abs>,
+ evade: bool,
+ background: bool,
+ },
+ Highlight {
+ fill: Paint,
+ stroke: Sides<Option<FixedStroke>>,
+ top_edge: TopEdge,
+ bottom_edge: BottomEdge,
+ radius: Corners<Rel<Abs>>,
+ },
}
/// Add line decorations to a single run of shaped text.
@@ -365,12 +404,18 @@ pub(crate) fn decorate(
) {
let font_metrics = text.font.metrics();
- if let DecoLine::Highlight { fill, top_edge, bottom_edge } = &deco.line {
+ if let DecoLine::Highlight { fill, stroke, top_edge, bottom_edge, radius } =
+ &deco.line
+ {
let (top, bottom) = determine_edges(text, *top_edge, *bottom_edge);
- let rect = Geometry::Rect(Size::new(width + 2.0 * deco.extent, top - bottom))
- .filled(fill.clone());
+ let size = Size::new(width + 2.0 * deco.extent, top - bottom);
+ let rects = styled_rect(size, *radius, Some(fill.clone()), stroke.clone());
let origin = Point::new(pos.x - deco.extent, pos.y - top - shift);
- frame.prepend(origin, FrameItem::Shape(rect, Span::detached()));
+ frame.prepend_multiple(
+ rects
+ .into_iter()
+ .map(|shape| (origin, FrameItem::Shape(shape, Span::detached()))),
+ );
return;
}
diff --git a/tests/ref/text/deco.png b/tests/ref/text/deco.png
index 782e628c..3a11e72f 100644
--- a/tests/ref/text/deco.png
+++ b/tests/ref/text/deco.png
Binary files differ
diff --git a/tests/typ/text/deco.typ b/tests/typ/text/deco.typ
index dbf86a86..cc9b9b3a 100644
--- a/tests/typ/text/deco.typ
+++ b/tests/typ/text/deco.typ
@@ -59,6 +59,17 @@ We can also specify a customized value
#highlight[abc #sym.integral]
---
+// Test highlight radius
+#highlight(radius: 3pt)[abc],
+#highlight(radius: 1em)[#lorem(5)]
+
+---
+// Test highlight stroke
+#highlight(stroke: 2pt + blue)[abc]
+#highlight(stroke: (top: blue, left: red, bottom: green, right: orange))[abc]
+#highlight(stroke: 1pt, radius: 3pt)[#lorem(5)]
+
+---
// Test underline background
#set underline(background: true, stroke: (thickness: 0.5em, paint: red, cap: "round"))
#underline[This is in the background]
@@ -68,7 +79,6 @@ We can also specify a customized value
#set overline(background: true, stroke: (thickness: 0.5em, paint: red, cap: "round"))
#overline[This is in the background]
-
---
// Test strike background
#set strike(background: true, stroke: 5pt + red)