mirror of
https://github.com/typst/typst
synced 2025-05-20 03:55:29 +08:00
Add hash impls for all nodes
This prepares the incremental PR. Co-Authored-By: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
14f093bfee
commit
e27f6c1014
@ -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" }
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
58
src/font.rs
58
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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| {
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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""#);
|
||||
|
Loading…
x
Reference in New Issue
Block a user