Handle compiler panics when handling infinite lengths (#2215)
This commit is contained in:
parent
ffcd951bc8
commit
553da642bd
@ -738,12 +738,12 @@ pub struct Paper {
|
||||
impl Paper {
|
||||
/// The width of the paper.
|
||||
pub fn width(self) -> Abs {
|
||||
Abs::mm(self.width.0)
|
||||
Abs::mm(self.width.get())
|
||||
}
|
||||
|
||||
/// The height of the paper.
|
||||
pub fn height(self) -> Abs {
|
||||
Abs::mm(self.height.0)
|
||||
Abs::mm(self.height.get())
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,8 +756,8 @@ macro_rules! papers {
|
||||
impl Paper {
|
||||
$(pub const $var: Self = Self {
|
||||
name: $name,
|
||||
width: Scalar($width),
|
||||
height: Scalar($height),
|
||||
width: Scalar::new($width),
|
||||
height: Scalar::new($height),
|
||||
};)*
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,9 @@ pub(crate) fn field(value: &Value, field: &str) -> StrResult<Value> {
|
||||
"cap" => stroke.line_cap.into_value(),
|
||||
"join" => stroke.line_join.into_value(),
|
||||
"dash" => stroke.dash_pattern.clone().into_value(),
|
||||
"miter-limit" => stroke.miter_limit.map(|limit| limit.0).into_value(),
|
||||
"miter-limit" => {
|
||||
stroke.miter_limit.map(|limit| limit.get()).into_value()
|
||||
}
|
||||
_ => return missing(),
|
||||
}
|
||||
} else if let Some(align) = dynamic.downcast::<Align>() {
|
||||
|
@ -336,7 +336,7 @@ impl PageContext<'_, '_> {
|
||||
}
|
||||
}
|
||||
if self.state.stroke.as_ref().map(|s| &s.miter_limit) != Some(miter_limit) {
|
||||
self.content.set_miter_limit(miter_limit.0 as f32);
|
||||
self.content.set_miter_limit(miter_limit.get() as f32);
|
||||
}
|
||||
self.state.stroke = Some(stroke.clone());
|
||||
}
|
||||
|
@ -496,7 +496,7 @@ fn render_shape(
|
||||
line_cap: line_cap.into(),
|
||||
line_join: line_join.into(),
|
||||
dash,
|
||||
miter_limit: miter_limit.0 as f32,
|
||||
miter_limit: miter_limit.get() as f32,
|
||||
};
|
||||
canvas.stroke_path(&path, &paint, &stroke, ts, mask);
|
||||
}
|
||||
|
@ -323,7 +323,8 @@ impl SVGRenderer {
|
||||
LineJoin::Bevel => "bevel",
|
||||
},
|
||||
);
|
||||
self.xml.write_attribute("stroke-miterlimit", &stroke.miter_limit.0);
|
||||
self.xml
|
||||
.write_attribute("stroke-miterlimit", &stroke.miter_limit.get());
|
||||
if let Some(pattern) = &stroke.dash_pattern {
|
||||
self.xml.write_attribute("stroke-dashoffset", &pattern.phase.to_pt());
|
||||
self.xml.write_attribute(
|
||||
|
@ -7,22 +7,22 @@ pub struct Abs(Scalar);
|
||||
impl Abs {
|
||||
/// The zero length.
|
||||
pub const fn zero() -> Self {
|
||||
Self(Scalar(0.0))
|
||||
Self(Scalar::ZERO)
|
||||
}
|
||||
|
||||
/// The infinite length.
|
||||
pub const fn inf() -> Self {
|
||||
Self(Scalar(f64::INFINITY))
|
||||
Self(Scalar::INFINITY)
|
||||
}
|
||||
|
||||
/// Create an absolute length from a number of raw units.
|
||||
pub const fn raw(raw: f64) -> Self {
|
||||
Self(Scalar(raw))
|
||||
Self(Scalar::new(raw))
|
||||
}
|
||||
|
||||
/// Create an absolute length from a value in a unit.
|
||||
pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
|
||||
Self(Scalar(val * unit.raw_scale()))
|
||||
Self(Scalar::new(val * unit.raw_scale()))
|
||||
}
|
||||
|
||||
/// Create an absolute length from a number of points.
|
||||
@ -47,7 +47,7 @@ impl Abs {
|
||||
|
||||
/// Get the value of this absolute length in raw units.
|
||||
pub const fn to_raw(self) -> f64 {
|
||||
(self.0).0
|
||||
(self.0).get()
|
||||
}
|
||||
|
||||
/// Get the value of this absolute length in a unit.
|
||||
|
@ -18,17 +18,17 @@ pub struct Angle(Scalar);
|
||||
impl Angle {
|
||||
/// The zero angle.
|
||||
pub const fn zero() -> Self {
|
||||
Self(Scalar(0.0))
|
||||
Self(Scalar::ZERO)
|
||||
}
|
||||
|
||||
/// Create an angle from a number of raw units.
|
||||
pub const fn raw(raw: f64) -> Self {
|
||||
Self(Scalar(raw))
|
||||
Self(Scalar::new(raw))
|
||||
}
|
||||
|
||||
/// Create an angle from a value in a unit.
|
||||
pub fn with_unit(val: f64, unit: AngleUnit) -> Self {
|
||||
Self(Scalar(val * unit.raw_scale()))
|
||||
Self(Scalar::new(val * unit.raw_scale()))
|
||||
}
|
||||
|
||||
/// Create an angle from a number of radians.
|
||||
@ -43,7 +43,7 @@ impl Angle {
|
||||
|
||||
/// Get the value of this angle in raw units.
|
||||
pub const fn to_raw(self) -> f64 {
|
||||
(self.0).0
|
||||
(self.0).get()
|
||||
}
|
||||
|
||||
/// Get the value of this angle in a unit.
|
||||
|
@ -9,29 +9,29 @@ pub struct Em(Scalar);
|
||||
impl Em {
|
||||
/// The zero em length.
|
||||
pub const fn zero() -> Self {
|
||||
Self(Scalar(0.0))
|
||||
Self(Scalar::ZERO)
|
||||
}
|
||||
|
||||
/// The font size.
|
||||
pub const fn one() -> Self {
|
||||
Self(Scalar(1.0))
|
||||
Self(Scalar::ONE)
|
||||
}
|
||||
|
||||
/// Create a font-relative length.
|
||||
pub const fn new(em: f64) -> Self {
|
||||
Self(Scalar(em))
|
||||
Self(Scalar::new(em))
|
||||
}
|
||||
|
||||
/// Create an em length from font units at the given units per em.
|
||||
pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
|
||||
Self(Scalar(units.into() / units_per_em))
|
||||
Self(Scalar::new(units.into() / units_per_em))
|
||||
}
|
||||
|
||||
/// Create an em length from a length at the given font size.
|
||||
pub fn from_length(length: Abs, font_size: Abs) -> Self {
|
||||
let result = length / font_size;
|
||||
if result.is_finite() {
|
||||
Self(Scalar(result))
|
||||
Self(Scalar::new(result))
|
||||
} else {
|
||||
Self::zero()
|
||||
}
|
||||
@ -39,7 +39,7 @@ impl Em {
|
||||
|
||||
/// The number of em units.
|
||||
pub const fn get(self) -> f64 {
|
||||
(self.0).0
|
||||
(self.0).get()
|
||||
}
|
||||
|
||||
/// The absolute value of this em length.
|
||||
|
@ -19,22 +19,22 @@ pub struct Fr(Scalar);
|
||||
impl Fr {
|
||||
/// Takes up zero space: `0fr`.
|
||||
pub const fn zero() -> Self {
|
||||
Self(Scalar(0.0))
|
||||
Self(Scalar::ZERO)
|
||||
}
|
||||
|
||||
/// Takes up as much space as all other items with this fraction: `1fr`.
|
||||
pub const fn one() -> Self {
|
||||
Self(Scalar(1.0))
|
||||
Self(Scalar::ONE)
|
||||
}
|
||||
|
||||
/// Create a new fraction.
|
||||
pub const fn new(ratio: f64) -> Self {
|
||||
Self(Scalar(ratio))
|
||||
Self(Scalar::new(ratio))
|
||||
}
|
||||
|
||||
/// Get the underlying number.
|
||||
pub const fn get(self) -> f64 {
|
||||
(self.0).0
|
||||
(self.0).get()
|
||||
}
|
||||
|
||||
/// The absolute value of this fraction.
|
||||
|
@ -18,22 +18,22 @@ pub struct Ratio(Scalar);
|
||||
impl Ratio {
|
||||
/// A ratio of `0%` represented as `0.0`.
|
||||
pub const fn zero() -> Self {
|
||||
Self(Scalar(0.0))
|
||||
Self(Scalar::ZERO)
|
||||
}
|
||||
|
||||
/// A ratio of `100%` represented as `1.0`.
|
||||
pub const fn one() -> Self {
|
||||
Self(Scalar(1.0))
|
||||
Self(Scalar::ONE)
|
||||
}
|
||||
|
||||
/// Create a new ratio from a value, where `1.0` means `100%`.
|
||||
pub const fn new(ratio: f64) -> Self {
|
||||
Self(Scalar(ratio))
|
||||
Self(Scalar::new(ratio))
|
||||
}
|
||||
|
||||
/// Get the underlying ratio.
|
||||
pub const fn get(self) -> f64 {
|
||||
(self.0).0
|
||||
(self.0).get()
|
||||
}
|
||||
|
||||
/// Whether the ratio is zero.
|
||||
|
@ -4,7 +4,40 @@ use super::*;
|
||||
///
|
||||
/// Panics if it's `NaN` during any of those operations.
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Scalar(pub f64);
|
||||
pub struct Scalar(f64);
|
||||
|
||||
// We have to detect NaNs this way since `f64::is_nan` isn’t const
|
||||
// on stable yet:
|
||||
// ([tracking issue](https://github.com/rust-lang/rust/issues/57241))
|
||||
#[allow(clippy::unusual_byte_groupings)]
|
||||
const fn is_nan_const(x: f64) -> bool {
|
||||
// Safety: all bit patterns are valid for u64, and f64 has no padding bits.
|
||||
// We cannot use `f64::to_bits` because it is not const.
|
||||
let x_bits = unsafe { std::mem::transmute::<f64, u64>(x) };
|
||||
(x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0
|
||||
}
|
||||
|
||||
impl Scalar {
|
||||
/// Creates a [`Scalar`] with the given value.
|
||||
///
|
||||
/// If the value is NaN, then it is set to `0.0` in the result.
|
||||
pub const fn new(x: f64) -> Self {
|
||||
Self(if is_nan_const(x) { 0.0 } else { x })
|
||||
}
|
||||
|
||||
/// Gets the value of this [`Scalar`].
|
||||
#[inline]
|
||||
pub const fn get(self) -> f64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// The scalar containing `0.0`.
|
||||
pub const ZERO: Self = Self(0.0);
|
||||
/// The scalar containing `1.0`.
|
||||
pub const ONE: Self = Self(1.0);
|
||||
/// The scalar containing `f64::INFINITY`.
|
||||
pub const INFINITY: Self = Self(f64::INFINITY);
|
||||
}
|
||||
|
||||
impl Numeric for Scalar {
|
||||
fn zero() -> Self {
|
||||
@ -18,7 +51,7 @@ impl Numeric for Scalar {
|
||||
|
||||
impl From<f64> for Scalar {
|
||||
fn from(float: f64) -> Self {
|
||||
Self(float)
|
||||
Self::new(float)
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +121,7 @@ impl Neg for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Self(-self.0)
|
||||
Self::new(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,13 +129,13 @@ impl<T: Into<Self>> Add<T> for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 + rhs.into().0)
|
||||
Self::new(self.0 + rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> AddAssign<T> for Scalar {
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
self.0 += rhs.into().0;
|
||||
*self = *self + rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,13 +143,13 @@ impl<T: Into<Self>> Sub<T> for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 - rhs.into().0)
|
||||
Self::new(self.0 - rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> SubAssign<T> for Scalar {
|
||||
fn sub_assign(&mut self, rhs: T) {
|
||||
self.0 -= rhs.into().0;
|
||||
*self = *self - rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,13 +157,13 @@ impl<T: Into<Self>> Mul<T> for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 * rhs.into().0)
|
||||
Self::new(self.0 * rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> MulAssign<T> for Scalar {
|
||||
fn mul_assign(&mut self, rhs: T) {
|
||||
self.0 *= rhs.into().0;
|
||||
*self = *self * rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,13 +171,13 @@ impl<T: Into<Self>> Div<T> for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 / rhs.into().0)
|
||||
Self::new(self.0 / rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> DivAssign<T> for Scalar {
|
||||
fn div_assign(&mut self, rhs: T) {
|
||||
self.0 /= rhs.into().0;
|
||||
*self = *self / rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,24 +185,24 @@ impl<T: Into<Self>> Rem<T> for Scalar {
|
||||
type Output = Self;
|
||||
|
||||
fn rem(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 % rhs.into().0)
|
||||
Self::new(self.0 % rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> RemAssign<T> for Scalar {
|
||||
fn rem_assign(&mut self, rhs: T) {
|
||||
self.0 %= rhs.into().0;
|
||||
*self = *self % rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum for Scalar {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
Self(iter.map(|s| s.0).sum())
|
||||
Self::new(iter.map(|s| s.0).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Self> for Scalar {
|
||||
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||
Self(iter.map(|s| s.0).sum())
|
||||
Self::new(iter.map(|s| s.0).sum())
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ cast! {
|
||||
line_cap,
|
||||
line_join,
|
||||
dash_pattern,
|
||||
miter_limit: miter_limit.map(Scalar),
|
||||
miter_limit: miter_limit.map(Scalar::new),
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -460,7 +460,7 @@ impl Default for FixedStroke {
|
||||
line_cap: LineCap::Butt,
|
||||
line_join: LineJoin::Miter,
|
||||
dash_pattern: None,
|
||||
miter_limit: Scalar(4.0),
|
||||
miter_limit: Scalar::new(4.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +128,20 @@
|
||||
// Error: 2-8 invalid hexadecimal number: 0x123z
|
||||
#0x123z
|
||||
|
||||
---
|
||||
// Test that multiplying infinite numbers by certain units does not crash.
|
||||
#(float("inf") * 1pt)
|
||||
#(float("inf") * 1em)
|
||||
#(float("inf") * (1pt + 1em))
|
||||
|
||||
---
|
||||
// Test that trying to produce a NaN scalar (such as in lengths) does not crash.
|
||||
#let infpt = float("inf") * 1pt
|
||||
#test(infpt - infpt, 0pt)
|
||||
#test(infpt + (-infpt), 0pt)
|
||||
// TODO: this result is surprising
|
||||
#test(infpt / float("inf"), 0pt)
|
||||
|
||||
---
|
||||
// Test boolean operators.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user