summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--src/color.rs4
-rw-r--r--src/exec/state.rs8
-rw-r--r--src/font.rs58
-rw-r--r--src/geom/align.rs2
-rw-r--r--src/geom/dir.rs2
-rw-r--r--src/geom/gen.rs9
-rw-r--r--src/geom/length.rs39
-rw-r--r--src/geom/linear.rs20
-rw-r--r--src/geom/path.rs2
-rw-r--r--src/geom/point.rs4
-rw-r--r--src/geom/relative.rs24
-rw-r--r--src/geom/sides.rs2
-rw-r--r--src/geom/size.rs15
-rw-r--r--src/geom/spec.rs10
-rw-r--r--src/layout/background.rs6
-rw-r--r--src/layout/fixed.rs2
-rw-r--r--src/layout/frame.rs2
-rw-r--r--src/layout/mod.rs54
-rw-r--r--src/layout/pad.rs2
-rw-r--r--src/layout/par.rs24
-rw-r--r--src/layout/shaping.rs8
-rw-r--r--src/layout/stack.rs24
-rw-r--r--src/library/font.rs2
-rw-r--r--src/library/image.rs4
-rw-r--r--src/library/shapes.rs10
-rw-r--r--src/pdf/mod.rs2
-rw-r--r--src/pretty.rs2
28 files changed, 198 insertions, 145 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 6c77b29a..ed46ccd9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,8 @@ debug = 0
opt-level = 2
[dependencies]
+decorum = { version = "0.3.1", default-features = false, features = ["serialize-serde"] }
+fxhash = "0.2.1"
image = { version = "0.23", default-features = false, features = ["jpeg", "png"] }
miniz_oxide = "0.3"
pdf-writer = { path = "../pdf-writer" }
diff --git a/src/color.rs b/src/color.rs
index 5a93e0e9..c3ab5aa9 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -6,7 +6,7 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// A color in a dynamic format.
-#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Color {
/// An 8-bit RGBA color: `#423abaff`.
Rgba(RgbaColor),
@@ -29,7 +29,7 @@ impl Debug for Color {
}
/// An 8-bit RGBA color: `#423abaff`.
-#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct RgbaColor {
/// Red channel.
pub r: u8,
diff --git a/src/exec/state.rs b/src/exec/state.rs
index 99c162c6..9a8971cc 100644
--- a/src/exec/state.rs
+++ b/src/exec/state.rs
@@ -184,7 +184,7 @@ impl Default for FontState {
size: Length::pt(11.0),
top_edge: VerticalFontMetric::CapHeight,
bottom_edge: VerticalFontMetric::Baseline,
- scale: Linear::ONE,
+ scale: Linear::one(),
color: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
strong: false,
emph: false,
@@ -193,7 +193,7 @@ impl Default for FontState {
}
/// Properties used for font selection and layout.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct FontProps {
/// The list of font families to use for shaping.
pub families: Rc<FamilyList>,
@@ -210,7 +210,7 @@ pub struct FontProps {
}
/// Font family definitions.
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FamilyList {
/// The user-defined list of font families.
pub list: Vec<FontFamily>,
@@ -255,7 +255,7 @@ impl Default for FamilyList {
}
/// A generic or named font family.
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum FontFamily {
Serif,
SansSerif,
diff --git a/src/font.rs b/src/font.rs
index 01434d0a..dd81fa88 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -92,7 +92,7 @@ impl Face {
}
/// Identifies a vertical metric of a font.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum VerticalFontMetric {
/// The distance from the baseline to the typographic ascender.
///
@@ -169,7 +169,7 @@ pub struct FaceInfo {
}
/// Properties that distinguish a face from other faces in the same family.
-#[derive(Default, Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Default, Debug, Copy, Clone, PartialEq, Hash, Serialize, Deserialize)]
pub struct FontVariant {
/// The style of the face (normal / italic / oblique).
pub style: FontStyle,
@@ -187,7 +187,7 @@ impl FontVariant {
}
/// The style of a font face.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FontStyle {
@@ -233,7 +233,8 @@ impl Display for FontStyle {
}
/// The weight of a font face.
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct FontWeight(u16);
@@ -353,42 +354,43 @@ impl Debug for FontWeight {
}
/// The width of a font face.
-#[derive(Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
#[serde(transparent)]
-pub struct FontStretch(f32);
+pub struct FontStretch(u16);
impl FontStretch {
/// Ultra-condensed stretch (50%).
- pub const ULTRA_CONDENSED: Self = Self(0.5);
+ pub const ULTRA_CONDENSED: Self = Self(500);
/// Extra-condensed stretch weight (62.5%).
- pub const EXTRA_CONDENSED: Self = Self(0.625);
+ pub const EXTRA_CONDENSED: Self = Self(625);
/// Condensed stretch (75%).
- pub const CONDENSED: Self = Self(0.75);
+ pub const CONDENSED: Self = Self(750);
/// Semi-condensed stretch (87.5%).
- pub const SEMI_CONDENSED: Self = Self(0.875);
+ pub const SEMI_CONDENSED: Self = Self(875);
/// Normal stretch (100%).
- pub const NORMAL: Self = Self(1.0);
+ pub const NORMAL: Self = Self(1000);
/// Semi-expanded stretch (112.5%).
- pub const SEMI_EXPANDED: Self = Self(1.125);
+ pub const SEMI_EXPANDED: Self = Self(1125);
/// Expanded stretch (125%).
- pub const EXPANDED: Self = Self(1.25);
+ pub const EXPANDED: Self = Self(1250);
/// Extra-expanded stretch (150%).
- pub const EXTRA_EXPANDED: Self = Self(1.5);
+ pub const EXTRA_EXPANDED: Self = Self(1500);
/// Ultra-expanded stretch (200%).
- pub const ULTRA_EXPANDED: Self = Self(2.0);
+ pub const ULTRA_EXPANDED: Self = Self(2000);
/// Create a font stretch from a ratio between 0.5 and 2.0, clamping it if
/// necessary.
pub fn from_ratio(ratio: f32) -> Self {
- Self(ratio.max(0.5).min(2.0))
+ Self((ratio.max(0.5).min(2.0) * 1000.0) as u16)
}
/// Create a font stretch from an OpenType-style number between 1 and 9,
@@ -425,29 +427,29 @@ impl FontStretch {
/// The ratio between 0.5 and 2.0 corresponding to this stretch.
pub fn to_ratio(self) -> f32 {
- self.0
+ self.0 as f32 / 1000.0
}
/// The lowercase string representation of this stretch is one of the named
/// ones.
pub fn to_str(self) -> Option<&'static str> {
Some(match self {
- s if s == Self::ULTRA_CONDENSED => "ultra-condensed",
- s if s == Self::EXTRA_CONDENSED => "extra-condensed",
- s if s == Self::CONDENSED => "condensed",
- s if s == Self::SEMI_CONDENSED => "semi-condensed",
- s if s == Self::NORMAL => "normal",
- s if s == Self::SEMI_EXPANDED => "semi-expanded",
- s if s == Self::EXPANDED => "expanded",
- s if s == Self::EXTRA_EXPANDED => "extra-expanded",
- s if s == Self::ULTRA_EXPANDED => "ultra-expanded",
+ Self::ULTRA_CONDENSED => "ultra-condensed",
+ Self::EXTRA_CONDENSED => "extra-condensed",
+ Self::CONDENSED => "condensed",
+ Self::SEMI_CONDENSED => "semi-condensed",
+ Self::NORMAL => "normal",
+ Self::SEMI_EXPANDED => "semi-expanded",
+ Self::EXPANDED => "expanded",
+ Self::EXTRA_EXPANDED => "extra-expanded",
+ Self::ULTRA_EXPANDED => "ultra-expanded",
_ => return None,
})
}
/// The absolute ratio distance between this and another font stretch.
pub fn distance(self, other: Self) -> f32 {
- (self.0 - other.0).abs()
+ (self.to_ratio() - other.to_ratio()).abs()
}
}
@@ -461,7 +463,7 @@ impl Display for FontStretch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.to_str() {
Some(name) => f.pad(name),
- None => write!(f, "{}", self.0),
+ None => write!(f, "{}", self.to_ratio()),
}
}
}
diff --git a/src/geom/align.rs b/src/geom/align.rs
index 515efdf2..f6e5bb2a 100644
--- a/src/geom/align.rs
+++ b/src/geom/align.rs
@@ -1,7 +1,7 @@
use super::*;
/// Where to align something along a directed axis.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Align {
/// Align at the start of the axis.
Start,
diff --git a/src/geom/dir.rs b/src/geom/dir.rs
index cfcb4c09..7b224b55 100644
--- a/src/geom/dir.rs
+++ b/src/geom/dir.rs
@@ -1,7 +1,7 @@
use super::*;
/// The four directions into which content can be laid out.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir {
/// Left to right.
LTR,
diff --git a/src/geom/gen.rs b/src/geom/gen.rs
index 91f7499c..c530ff2b 100644
--- a/src/geom/gen.rs
+++ b/src/geom/gen.rs
@@ -1,7 +1,7 @@
use super::*;
/// A container with a main and cross component.
-#[derive(Default, Copy, Clone, Eq, PartialEq)]
+#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Gen<T> {
/// The cross component.
pub cross: T,
@@ -26,7 +26,12 @@ impl<T> Gen<T> {
impl Gen<Length> {
/// The zero value.
- pub const ZERO: Self = Self { main: Length::ZERO, cross: Length::ZERO };
+ pub fn zero() -> Self {
+ Self {
+ main: Length::zero(),
+ cross: Length::zero(),
+ }
+ }
}
impl<T> Get<GenAxis> for Gen<T> {
diff --git a/src/geom/length.rs b/src/geom/length.rs
index 8bc50e97..2439000c 100644
--- a/src/geom/length.rs
+++ b/src/geom/length.rs
@@ -1,18 +1,22 @@
-use super::*;
-
+use decorum::NotNan;
use serde::{Deserialize, Serialize};
+use super::*;
+
/// An absolute length.
-#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
+#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Length {
/// The length in raw units.
- raw: f64,
+ raw: NotNan<f64>,
}
impl Length {
/// The zero length.
- pub const ZERO: Self = Self { raw: 0.0 };
+ pub fn zero() -> Self {
+ Self { raw: 0.0.into() }
+ }
/// Create a length from a number of points.
pub fn pt(pt: f64) -> Self {
@@ -35,8 +39,8 @@ impl Length {
}
/// Create a length from a number of raw units.
- pub const fn raw(raw: f64) -> Self {
- Self { raw }
+ pub fn raw(raw: f64) -> Self {
+ Self { raw: raw.into() }
}
/// Convert this to a number of points.
@@ -60,18 +64,18 @@ impl Length {
}
/// Get the value of this length in raw units.
- pub const fn to_raw(self) -> f64 {
- self.raw
+ pub fn to_raw(self) -> f64 {
+ self.raw.into()
}
/// Create a length from a value in a unit.
pub fn with_unit(val: f64, unit: LengthUnit) -> Self {
- Self { raw: val * unit.raw_scale() }
+ Self { raw: (val * unit.raw_scale()).into() }
}
/// Get the value of this length in unit.
pub fn to_unit(self, unit: LengthUnit) -> f64 {
- self.raw / unit.raw_scale()
+ self.to_raw() / unit.raw_scale()
}
/// The minimum of this and another length.
@@ -106,17 +110,12 @@ impl Length {
/// Whether the length is finite.
pub fn is_finite(self) -> bool {
- self.raw.is_finite()
+ self.raw.into_inner().is_finite()
}
/// Whether the length is infinite.
pub fn is_infinite(self) -> bool {
- self.raw.is_infinite()
- }
-
- /// Whether the length is `NaN`.
- pub fn is_nan(self) -> bool {
- self.raw.is_nan()
+ self.raw.into_inner().is_infinite()
}
}
@@ -189,7 +188,7 @@ impl Div for Length {
type Output = f64;
fn div(self, other: Self) -> f64 {
- self.raw / other.raw
+ self.to_raw() / other.to_raw()
}
}
@@ -200,7 +199,7 @@ assign_impl!(Length /= f64);
impl Sum for Length {
fn sum<I: Iterator<Item = Length>>(iter: I) -> Self {
- iter.fold(Length::ZERO, Add::add)
+ iter.fold(Length::zero(), Add::add)
}
}
diff --git a/src/geom/linear.rs b/src/geom/linear.rs
index 54ff71e6..20473821 100644
--- a/src/geom/linear.rs
+++ b/src/geom/linear.rs
@@ -1,7 +1,7 @@
use super::*;
/// A combined relative and absolute length.
-#[derive(Default, Copy, Clone, PartialEq)]
+#[derive(Default, Copy, Clone, PartialEq, Hash)]
pub struct Linear {
/// The relative part.
pub rel: Relative,
@@ -11,10 +11,20 @@ pub struct Linear {
impl Linear {
/// The zero linear.
- pub const ZERO: Self = Self { rel: Relative::ZERO, abs: Length::ZERO };
+ pub fn zero() -> Self {
+ Self {
+ rel: Relative::zero(),
+ abs: Length::zero(),
+ }
+ }
/// The linear with a relative part of `100%` and no absolute part.
- pub const ONE: Self = Self { rel: Relative::ONE, abs: Length::ZERO };
+ pub fn one() -> Self {
+ Self {
+ rel: Relative::one(),
+ abs: Length::zero(),
+ }
+ }
/// Create a new linear.
pub fn new(rel: Relative, abs: Length) -> Self {
@@ -46,13 +56,13 @@ impl Debug for Linear {
impl From<Length> for Linear {
fn from(abs: Length) -> Self {
- Self { rel: Relative::ZERO, abs }
+ Self { rel: Relative::zero(), abs }
}
}
impl From<Relative> for Linear {
fn from(rel: Relative) -> Self {
- Self { rel, abs: Length::ZERO }
+ Self { rel, abs: Length::zero() }
}
}
diff --git a/src/geom/path.rs b/src/geom/path.rs
index 8878b6f1..f6be2754 100644
--- a/src/geom/path.rs
+++ b/src/geom/path.rs
@@ -30,7 +30,7 @@ impl Path {
let m = 0.551784;
let mx = m * rx;
let my = m * ry;
- let z = Length::ZERO;
+ let z = Length::zero();
let point = Point::new;
let mut path = Self::new();
path.move_to(point(-rx, z));
diff --git a/src/geom/point.rs b/src/geom/point.rs
index 479bb137..babbdfef 100644
--- a/src/geom/point.rs
+++ b/src/geom/point.rs
@@ -13,7 +13,9 @@ pub struct Point {
impl Point {
/// The origin point.
- pub const ZERO: Self = Self { x: Length::ZERO, y: Length::ZERO };
+ pub fn zero() -> Self {
+ Self { x: Length::zero(), y: Length::zero() }
+ }
/// Create a new point from x and y coordinate.
pub fn new(x: Length, y: Length) -> Self {
diff --git a/src/geom/relative.rs b/src/geom/relative.rs
index bbae5aba..86759bbe 100644
--- a/src/geom/relative.rs
+++ b/src/geom/relative.rs
@@ -1,34 +1,40 @@
+use decorum::NotNan;
+
use super::*;
/// A relative length.
///
/// _Note_: `50%` is represented as `0.5` here, but stored as `50.0` in the
/// corresponding [literal](crate::syntax::Expr::Percent).
-#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
-pub struct Relative(f64);
+#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Hash)]
+pub struct Relative(NotNan<f64>);
impl Relative {
/// A ratio of `0%` represented as `0.0`.
- pub const ZERO: Self = Self(0.0);
+ pub fn zero() -> Self {
+ Self(0.0.into())
+ }
/// A ratio of `100%` represented as `1.0`.
- pub const ONE: Self = Self(1.0);
+ pub fn one() -> Self {
+ Self(1.0.into())
+ }
/// Create a new relative value.
pub fn new(ratio: f64) -> Self {
- Self(ratio)
+ Self(ratio.into())
}
/// Get the underlying ratio.
pub fn get(self) -> f64 {
- self.0
+ self.0.into()
}
/// Resolve this relative to the given `length`.
pub fn resolve(self, length: Length) -> Length {
// We don't want NaNs.
if length.is_infinite() {
- Length::ZERO
+ Length::zero()
} else {
self.get() * length
}
@@ -42,7 +48,7 @@ impl Relative {
impl Display for Relative {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}%", 100.0 * self.0)
+ write!(f, "{}%", 100.0 * self.get())
}
}
@@ -98,7 +104,7 @@ impl Div for Relative {
type Output = f64;
fn div(self, other: Self) -> f64 {
- self.0 / other.0
+ self.get() / other.get()
}
}
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index af728ed4..f9fdc01f 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -1,7 +1,7 @@
use super::*;
/// A container with left, top, right and bottom components.
-#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Sides<T> {
/// The value for the left side.
pub left: T,
diff --git a/src/geom/size.rs b/src/geom/size.rs
index d81525e6..e20c24af 100644
--- a/src/geom/size.rs
+++ b/src/geom/size.rs
@@ -13,10 +13,12 @@ pub struct Size {
impl Size {
/// The zero size.
- pub const ZERO: Self = Self {
- width: Length::ZERO,
- height: Length::ZERO,
- };
+ pub fn zero() -> Self {
+ Self {
+ width: Length::zero(),
+ height: Length::zero(),
+ }
+ }
/// Create a new size from width and height.
pub fn new(width: Length, height: Length) -> Self {
@@ -43,11 +45,6 @@ impl Size {
self.width.is_infinite() || self.height.is_infinite()
}
- /// Whether any of the two components is `NaN`.
- pub fn is_nan(self) -> bool {
- self.width.is_nan() || self.height.is_nan()
- }
-
/// Convert to a point.
pub fn to_point(self) -> Point {
Point::new(self.width, self.height)
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index e5479ba4..713f0a16 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -29,10 +29,12 @@ impl<T> Spec<T> {
impl Spec<Length> {
/// The zero value.
- pub const ZERO: Self = Self {
- horizontal: Length::ZERO,
- vertical: Length::ZERO,
- };
+ pub fn zero() -> Self {
+ Self {
+ horizontal: Length::zero(),
+ vertical: Length::zero(),
+ }
+ }
/// Convert to a point.
pub fn to_point(self) -> Point {
diff --git a/src/layout/background.rs b/src/layout/background.rs
index a5afbc4a..3a76a264 100644
--- a/src/layout/background.rs
+++ b/src/layout/background.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that places a rectangular filled background behind its child.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct BackgroundNode {
/// The kind of shape to use as a background.
pub shape: BackgroundShape,
@@ -12,7 +12,7 @@ pub struct BackgroundNode {
}
/// The kind of shape to use as a background.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BackgroundShape {
Rect,
Ellipse,
@@ -24,7 +24,7 @@ impl Layout for BackgroundNode {
for frame in &mut frames {
let (point, shape) = match self.shape {
- BackgroundShape::Rect => (Point::ZERO, Shape::Rect(frame.size)),
+ BackgroundShape::Rect => (Point::zero(), Shape::Rect(frame.size)),
BackgroundShape::Ellipse => {
(frame.size.to_point() / 2.0, Shape::Ellipse(frame.size))
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index a0e5e973..7c28e8e5 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that can fix its child's width and height.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct FixedNode {
/// The fixed width, if any.
pub width: Option<Linear>,
diff --git a/src/layout/frame.rs b/src/layout/frame.rs
index cf8ddb09..61a84d6d 100644
--- a/src/layout/frame.rs
+++ b/src/layout/frame.rs
@@ -96,7 +96,7 @@ pub enum Shape {
}
/// How text and shapes are filled.
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Copy, Clone, PartialEq, Hash, Serialize, Deserialize)]
pub enum Fill {
/// A solid color.
Color(Color),
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 7997f584..207d5bed 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -18,6 +18,10 @@ pub use stack::*;
use std::any::Any;
use std::fmt::{self, Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use decorum::NotNan;
+use fxhash::FxHasher64;
use crate::env::Env;
use crate::geom::*;
@@ -64,39 +68,62 @@ impl PageRun {
}
/// A wrapper around a dynamic layouting node.
-pub struct AnyNode(Box<dyn Bounds>);
+pub struct AnyNode {
+ node: Box<dyn Bounds>,
+ hash: u64,
+}
impl AnyNode {
/// Create a new instance from any node that satisifies the required bounds.
- pub fn new<T>(any: T) -> Self
+ pub fn new<T>(node: T) -> Self
where
- T: Layout + Debug + Clone + PartialEq + 'static,
+ T: Layout + Debug + Clone + PartialEq + Hash + 'static,
{
- Self(Box::new(any))
+ let hash = {
+ let mut state = FxHasher64::default();
+ node.hash(&mut state);
+ state.finish()
+ };
+
+ Self { node: Box::new(node), hash }
+ }
+
+ /// The cached hash for the boxed node.
+ pub fn hash(&self) -> u64 {
+ self.hash
}
}
impl Layout for AnyNode {
fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec<Frame> {
- self.0.layout(ctx, regions)
+ self.node.layout(ctx, regions)
}
}
impl Clone for AnyNode {
fn clone(&self) -> Self {
- Self(self.0.dyn_clone())
+ Self {
+ node: self.node.dyn_clone(),
+ hash: self.hash,
+ }
}
}
impl PartialEq for AnyNode {
fn eq(&self, other: &Self) -> bool {
- self.0.dyn_eq(other.0.as_ref())
+ self.node.dyn_eq(other.node.as_ref())
+ }
+}
+
+impl Hash for AnyNode {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_u64(self.hash);
}
}
impl Debug for AnyNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
+ self.node.fmt(f)
}
}
@@ -202,10 +229,7 @@ impl Regions {
///
/// If this is true, calling `next()` will have no effect.
pub fn in_full_last(&self) -> bool {
- self.backlog.is_empty()
- && self.last.map_or(true, |size| {
- self.current.is_nan() || size.is_nan() || self.current == size
- })
+ self.backlog.is_empty() && self.last.map_or(true, |size| self.current == size)
}
/// Advance to the next region if there is any.
@@ -217,9 +241,9 @@ impl Regions {
}
/// Shrink `current` to ensure that the aspect ratio can be satisfied.
- pub fn apply_aspect_ratio(&mut self, aspect: f64) {
- let width = self.current.width.min(aspect * self.current.height);
- let height = width / aspect;
+ pub fn apply_aspect_ratio(&mut self, aspect: NotNan<f64>) {
+ let width = self.current.width.min(aspect.into_inner() * self.current.height);
+ let height = width / aspect.into_inner();
self.current = Size::new(width, height);
}
}
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index ad24d62c..ccf0d5e1 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that adds padding to its child.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct PadNode {
/// The amount of padding.
pub padding: Sides<Linear>,
diff --git a/src/layout/par.rs b/src/layout/par.rs
index 22400dd3..f21778de 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -10,7 +10,7 @@ use crate::util::{RangeExt, SliceExt};
type Range = std::ops::Range<usize>;
/// A node that arranges its children into a paragraph.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct ParNode {
/// The inline direction of this paragraph.
pub dir: Dir,
@@ -21,7 +21,7 @@ pub struct ParNode {
}
/// A child of a paragraph node.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Hash)]
pub enum ParChild {
/// Spacing between other nodes.
Spacing(Length),
@@ -255,7 +255,7 @@ impl ParItem<'_> {
/// The size of the item.
pub fn size(&self) -> Size {
match self {
- Self::Spacing(amount) => Size::new(*amount, Length::ZERO),
+ Self::Spacing(amount) => Size::new(*amount, Length::zero()),
Self::Text(shaped, _) => shaped.size,
Self::Frame(frame, _) => frame.size,
}
@@ -264,7 +264,7 @@ impl ParItem<'_> {
/// The baseline of the item.
pub fn baseline(&self) -> Length {
match self {
- Self::Spacing(_) => Length::ZERO,
+ Self::Spacing(_) => Length::zero(),
Self::Text(shaped, _) => shaped.baseline,
Self::Frame(frame, _) => frame.baseline,
}
@@ -287,7 +287,7 @@ impl<'a> LineStack<'a> {
regions,
finished: vec![],
lines: vec![],
- size: Size::ZERO,
+ size: Size::zero(),
}
}
@@ -308,13 +308,13 @@ impl<'a> LineStack<'a> {
}
let mut output = Frame::new(self.size, self.size.height);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
let mut first = true;
for line in std::mem::take(&mut self.lines) {
let frame = line.build(self.size.width);
- let pos = Point::new(Length::ZERO, offset);
+ let pos = Point::new(Length::zero(), offset);
if first {
output.baseline = pos.y + frame.baseline;
first = false;
@@ -326,7 +326,7 @@ impl<'a> LineStack<'a> {
self.finished.push(output);
self.regions.next();
- self.size = Size::ZERO;
+ self.size = Size::zero();
}
fn finish(mut self) -> Vec<Frame> {
@@ -421,9 +421,9 @@ impl<'a> LineLayout<'a> {
}
}
- let mut width = Length::ZERO;
- let mut top = Length::ZERO;
- let mut bottom = Length::ZERO;
+ let mut width = Length::zero();
+ let mut top = Length::zero();
+ let mut bottom = Length::zero();
// Measure the size of the line.
for item in first.iter().chain(items).chain(&last) {
@@ -452,7 +452,7 @@ impl<'a> LineLayout<'a> {
let free = size.width - self.size.width;
let mut output = Frame::new(size, self.baseline);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
let mut ruler = Align::Start;
self.reordered(|item| {
diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs
index da25b7a6..f8ab7037 100644
--- a/src/layout/shaping.rs
+++ b/src/layout/shaping.rs
@@ -62,7 +62,7 @@ impl<'a> ShapedText<'a> {
/// Build the shaped text's frame.
pub fn build(&self) -> Frame {
let mut frame = Frame::new(self.size, self.baseline);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) {
let pos = Point::new(offset, self.baseline);
@@ -331,9 +331,9 @@ fn measure(
glyphs: &[ShapedGlyph],
props: &FontProps,
) -> (Size, Length) {
- let mut width = Length::ZERO;
- let mut top = Length::ZERO;
- let mut bottom = Length::ZERO;
+ let mut width = Length::zero();
+ let mut top = Length::zero();
+ let mut bottom = Length::zero();
let mut expand_vertical = |face: &Face| {
top.set_max(face.vertical_metric(props.top_edge).to_length(props.size));
bottom.set_max(-face.vertical_metric(props.bottom_edge).to_length(props.size));
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 11f9c3d7..bb767378 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,7 +1,9 @@
+use decorum::NotNan;
+
use super::*;
/// A node that stacks its children.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct StackNode {
/// The `main` and `cross` directions of this stack.
///
@@ -11,13 +13,13 @@ pub struct StackNode {
/// The fixed aspect ratio between width and height, if any.
///
/// The resulting frames will satisfy `width = aspect * height`.
- pub aspect: Option<f64>,
+ pub aspect: Option<NotNan<f64>>,
/// The nodes to be stacked.
pub children: Vec<StackChild>,
}
/// A child of a stack node.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub enum StackChild {
/// Spacing between other nodes.
Spacing(Length),
@@ -56,7 +58,7 @@ impl From<StackNode> for AnyNode {
struct StackLayouter {
dirs: Gen<Dir>,
- aspect: Option<f64>,
+ aspect: Option<NotNan<f64>>,
main: SpecAxis,
regions: Regions,
finished: Vec<Frame>,
@@ -67,7 +69,7 @@ struct StackLayouter {
}
impl StackLayouter {
- fn new(dirs: Gen<Dir>, aspect: Option<f64>, mut regions: Regions) -> Self {
+ fn new(dirs: Gen<Dir>, aspect: Option<NotNan<f64>>, mut regions: Regions) -> Self {
if let Some(aspect) = aspect {
regions.apply_aspect_ratio(aspect);
}
@@ -79,7 +81,7 @@ impl StackLayouter {
finished: vec![],
frames: vec![],
full: regions.current,
- size: Gen::ZERO,
+ size: Gen::zero(),
ruler: Align::Start,
regions,
}
@@ -122,11 +124,11 @@ impl StackLayouter {
if let Some(aspect) = self.aspect {
let width = size
.width
- .max(aspect * size.height)
+ .max(aspect.into_inner() * size.height)
.min(self.full.width)
- .min(aspect * self.full.height);
+ .min(aspect.into_inner() * self.full.height);
- size = Size::new(width, width / aspect);
+ size = Size::new(width, width / aspect.into_inner());
}
let mut output = Frame::new(size, size.height);
@@ -141,7 +143,7 @@ impl StackLayouter {
// Align along the cross axis.
let cross = aligns
.cross
- .resolve(self.dirs.cross, Length::ZERO .. size.cross - child.cross);
+ .resolve(self.dirs.cross, Length::zero() .. size.cross - child.cross);
// Align along the main axis.
let main = aligns.main.resolve(
@@ -163,7 +165,7 @@ impl StackLayouter {
output.push_frame(pos, frame);
}
- self.size = Gen::ZERO;
+ self.size = Gen::zero();
self.ruler = Align::Start;
self.regions.next();
if let Some(aspect) = self.aspect {
diff --git a/src/library/font.rs b/src/library/font.rs
index 33a521f5..b3b037cd 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -68,7 +68,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
if let Some(linear) = size {
if linear.rel.is_zero() {
ctx.state.font.size = linear.abs;
- ctx.state.font.scale = Relative::ONE.into();
+ ctx.state.font.scale = Linear::one();
} else {
ctx.state.font.scale = linear;
}
diff --git a/src/library/image.rs b/src/library/image.rs
index 9b880d04..b73c26a9 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -32,7 +32,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
/// An image node.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
struct ImageNode {
/// The id of the image file.
id: ImageId,
@@ -73,7 +73,7 @@ impl Layout for ImageNode {
};
let mut frame = Frame::new(size, size.height);
- frame.push(Point::ZERO, Element::Image(self.id, size));
+ frame.push(Point::zero(), Element::Image(self.id, size));
vec![frame]
}
}
diff --git a/src/library/shapes.rs b/src/library/shapes.rs
index ce8f634b..05cc2e2d 100644
--- a/src/library/shapes.rs
+++ b/src/library/shapes.rs
@@ -1,5 +1,7 @@
use std::f64::consts::SQRT_2;
+use decorum::NotNan;
+
use super::*;
use crate::color::Color;
use crate::layout::{BackgroundNode, BackgroundShape, Fill, FixedNode, PadNode};
@@ -47,14 +49,14 @@ pub fn square(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let height = width.is_none().then(|| args.eat_named(ctx, "height")).flatten();
let fill = args.eat_named(ctx, "fill");
let body = args.eat::<TemplateValue>(ctx).unwrap_or_default();
- rect_impl("square", width, height, Some(1.0), fill, body)
+ rect_impl("square", width, height, Some(1.0.into()), fill, body)
}
fn rect_impl(
name: &str,
width: Option<Linear>,
height: Option<Linear>,
- aspect: Option<f64>,
+ aspect: Option<NotNan<f64>>,
fill: Option<Color>,
body: TemplateValue,
) -> Value {
@@ -119,14 +121,14 @@ pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let height = width.is_none().then(|| args.eat_named(ctx, "height")).flatten();
let fill = args.eat_named(ctx, "fill");
let body = args.eat::<TemplateValue>(ctx).unwrap_or_default();
- ellipse_impl("circle", width, height, Some(1.0), fill, body)
+ ellipse_impl("circle", width, height, Some(1.0.into()), fill, body)
}
fn ellipse_impl(
name: &str,
width: Option<Linear>,
height: Option<Linear>,
- aspect: Option<f64>,
+ aspect: Option<NotNan<f64>>,
fill: Option<Color>,
body: TemplateValue,
) -> Value {
diff --git a/src/pdf/mod.rs b/src/pdf/mod.rs
index 763dc769..1769e2ae 100644
--- a/src/pdf/mod.rs
+++ b/src/pdf/mod.rs
@@ -132,7 +132,7 @@ impl<'a> PdfExporter<'a> {
// We only write font switching actions when the used face changes. To
// do that, we need to remember the active face.
let mut face = FaceId::MAX;
- let mut size = Length::ZERO;
+ let mut size = Length::zero();
let mut fill: Option<Fill> = None;
for (pos, element) in &page.elements {
diff --git a/src/pretty.rs b/src/pretty.rs
index 4e03ed84..acdb46a0 100644
--- a/src/pretty.rs
+++ b/src/pretty.rs
@@ -742,7 +742,7 @@ mod tests {
test_value(3.14, "3.14");
test_value(Length::pt(5.5), "5.5pt");
test_value(Angle::deg(90.0), "90deg");
- test_value(Relative::ONE / 2.0, "50%");
+ test_value(Relative::one() / 2.0, "50%");
test_value(Relative::new(0.3) + Length::cm(2.0), "30% + 2cm");
test_value(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101");
test_value("hello", r#""hello""#);