summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst-pdf/src/page.rs2
-rw-r--r--crates/typst-render/src/lib.rs25
-rw-r--r--crates/typst/src/layout/abs.rs5
-rw-r--r--tests/ref/visualize/shape-rect.pngbin247599 -> 261994 bytes
-rw-r--r--tests/typ/visualize/shape-rect.typ15
5 files changed, 41 insertions, 6 deletions
diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs
index 590ee905..aa3c92cc 100644
--- a/crates/typst-pdf/src/page.rs
+++ b/crates/typst-pdf/src/page.rs
@@ -827,7 +827,7 @@ fn write_shape(ctx: &mut PageContext, pos: Point, shape: &Shape) {
Geometry::Rect(size) => {
let w = size.x.to_f32();
let h = size.y.to_f32();
- if w > 0.0 && h > 0.0 {
+ if w.abs() > f32::EPSILON && h.abs() > f32::EPSILON {
ctx.content.rect(x, y, w, h);
}
}
diff --git a/crates/typst-render/src/lib.rs b/crates/typst-render/src/lib.rs
index e9dd3fbf..fdacb597 100644
--- a/crates/typst-render/src/lib.rs
+++ b/crates/typst-render/src/lib.rs
@@ -570,7 +570,18 @@ fn render_shape(canvas: &mut sk::Pixmap, state: State, shape: &Shape) -> Option<
Geometry::Rect(size) => {
let w = size.x.to_f32();
let h = size.y.to_f32();
- let rect = sk::Rect::from_xywh(0.0, 0.0, w, h)?;
+ let rect = if w < 0.0 || h < 0.0 {
+ // Skia doesn't normally allow for negative dimensions, but
+ // Typst supports them, so we apply a transform if needed
+ // Because this operation is expensive according to tiny-skia's
+ // docs, we prefer to not apply it if not needed
+ let transform = sk::Transform::from_scale(w.signum(), h.signum());
+ let rect = sk::Rect::from_xywh(0.0, 0.0, w.abs(), h.abs())?;
+ rect.transform(transform)?
+ } else {
+ sk::Rect::from_xywh(0.0, 0.0, w, h)?
+ };
+
sk::PathBuilder::from_rect(rect)
}
Geometry::Path(ref path) => convert_path(path)?,
@@ -941,8 +952,10 @@ fn to_sk_paint<'a>(
.container_transform
.post_concat(state.transform.invert().unwrap()),
};
- let width = (container_size.x.to_f32() * state.pixel_per_pt).ceil() as u32;
- let height = (container_size.y.to_f32() * state.pixel_per_pt).ceil() as u32;
+ let width =
+ (container_size.x.to_f32().abs() * state.pixel_per_pt).ceil() as u32;
+ let height =
+ (container_size.y.to_f32().abs() * state.pixel_per_pt).ceil() as u32;
*pixmap = Some(cached(
gradient,
@@ -958,8 +971,10 @@ fn to_sk_paint<'a>(
sk::SpreadMode::Pad,
sk::FilterQuality::Nearest,
1.0,
- fill_transform
- .pre_scale(1.0 / state.pixel_per_pt, 1.0 / state.pixel_per_pt),
+ fill_transform.pre_scale(
+ container_size.x.signum() as f32 / state.pixel_per_pt,
+ container_size.y.signum() as f32 / state.pixel_per_pt,
+ ),
);
sk_paint.anti_alias = gradient.anti_alias();
diff --git a/crates/typst/src/layout/abs.rs b/crates/typst/src/layout/abs.rs
index b1916795..5c07c5a0 100644
--- a/crates/typst/src/layout/abs.rs
+++ b/crates/typst/src/layout/abs.rs
@@ -117,6 +117,11 @@ impl Abs {
pub fn approx_eq(self, other: Self) -> bool {
self == other || (self - other).to_raw().abs() < 1e-6
}
+
+ /// Returns a number that represent the sign of this length
+ pub fn signum(self) -> f64 {
+ self.0.get().signum()
+ }
}
impl Numeric for Abs {
diff --git a/tests/ref/visualize/shape-rect.png b/tests/ref/visualize/shape-rect.png
index 3eda642f..a279341e 100644
--- a/tests/ref/visualize/shape-rect.png
+++ b/tests/ref/visualize/shape-rect.png
Binary files differ
diff --git a/tests/typ/visualize/shape-rect.typ b/tests/typ/visualize/shape-rect.typ
index ea0e66b0..bd9cf1ae 100644
--- a/tests/typ/visualize/shape-rect.typ
+++ b/tests/typ/visualize/shape-rect.typ
@@ -61,3 +61,18 @@
#set par(justify: true)
#lorem(100)
#rect(lorem(100))
+
+---
+// Negative dimensions
+#rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse left]
+
+#rect(width: 1cm, fill: gradient.linear(red, blue))[Left]
+
+#align(center, rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse center])
+
+#align(center, rect(width: 1cm, fill: gradient.linear(red, blue))[Center])
+
+#align(right, rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse right])
+
+#align(right, rect(width: 1cm, fill: gradient.linear(red, blue))[Right])
+