printpdf 0.7.0

Rust library for writing PDF files
Documentation
//! Scaling types for reducing errors between conversions between point (pt) and millimeter (mm)

use std::num::FpCategory;
use std::cmp::Ordering;

macro_rules! impl_partialeq {
    ($t:ty) => {
        impl PartialEq for $t {
            // custom compare function because of floating point inaccuracy
            fn eq(&self, other: &$t) -> bool {
                if (self.0.classify() == FpCategory::Zero || self.0.classify() == FpCategory::Normal) &&  (other.0.classify() == FpCategory::Zero || other.0.classify() == FpCategory::Normal) {
                    // four floating point numbers have to match
                    (self.0 * 1000.0).round() == (other.0 * 1000.0).round()
                } else {
                    false
                }
            }
        }
    };
}

macro_rules! impl_ord {
    ($t:ty) => {
        impl Ord for $t {
            // custom compare function to offer ordering
            fn cmp(&self, other: &$t) -> Ordering {
                if self.0 < other.0 {
                    Ordering::Less
                } else if self.0 > other.0 {
                    Ordering::Greater
                } else {
                    Ordering::Equal
                }
            }
        }
    };
}

/// Scale in millimeter
#[derive(Debug, Default, Copy, Clone, PartialOrd)]
pub struct Mm(pub f32);

impl Mm {
    pub fn into_pt(&self) -> Pt {
        let pt: Pt = (*self).into();
        pt
    }
}

impl From<Pt> for Mm {
    fn from(value: Pt) -> Mm {
        Mm(value.0 * 0.352_778_f32)
    }
}

impl Eq for Mm {}

impl_partialeq!(Mm);
impl_ord!(Mm);

/// Scale in point
#[derive(Debug, Default, Copy, Clone, PartialOrd)]
pub struct Pt(pub f32);

impl From<Mm> for Pt {
    fn from(value: Mm) -> Pt {
        Pt(value.0 * 2.834_646_f32)
    }
}

impl From<Pt> for ::lopdf::Object {
    fn from(value: Pt) -> Self {
        Self::Real(value.0)
    }
}

impl Eq for Pt {}

impl_partialeq!(Pt);
impl_ord!(Pt);

/// Scale in pixels
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Px(pub usize);

impl Px {
    pub fn into_pt(self, dpi: f32) -> Pt {
        Mm(self.0 as f32 * (25.4 / dpi)).into()
    }
}

use std::ops::{Add, Div, Mul, Sub};
use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign};

macro_rules! impl_add_self {
    ($type:ident) => {
        impl Add for $type {
            type Output = Self;
            fn add(self, other: Self) -> Self {
                Self {
                    0: self.0 + other.0,
                }
            }
        }
    };
}

macro_rules! impl_add_assign_self {
    ($type:ident) => {
        impl AddAssign for $type {
            fn add_assign(&mut self, other: Self) {
                self.0 += other.0;
            }
        }
    };
}

macro_rules! impl_sub_assign_self {
    ($type:ident) => {
        impl SubAssign for $type {
            fn sub_assign(&mut self, other: Self) {
                self.0 -= other.0;
            }
        }
    };
}

macro_rules! impl_sub_self {
    ($type:ident) => {
        impl Sub for $type {
            type Output = Self;
            fn sub(self, other: Self) -> Self {
                Self {
                    0: self.0 - other.0,
                }
            }
        }
    };
}

macro_rules! impl_mul_f32 {
    ($type:ident) => {
        impl Mul<f32> for $type {
            type Output = Self;
            fn mul(self, other: f32) -> Self {
                Self { 0: self.0 * other }
            }
        }
    };
}

macro_rules! impl_mul_assign_f32 {
    ($type:ident) => {
        impl MulAssign<f32> for $type {
            fn mul_assign(&mut self, other: f32) {
                self.0 *= other;
            }
        }
    };
}

macro_rules! impl_div {
    ($type:ident) => {
        impl Div<$type> for $type {
            type Output = f32;
            fn div(self, other: $type) -> Self::Output {
                self.0 / other.0
            }
        }
        impl Div<f32> for $type {
            type Output = Self;
            fn div(self, other: f32) -> Self::Output {
                Self { 0: self.0 / other }
            }
        }
    };
}

macro_rules! impl_div_assign_f32 {
    ($type:ident) => {
        impl DivAssign<f32> for $type {
            fn div_assign(&mut self, other: f32) {
                self.0 /= other;
            }
        }
    };
}

impl_add_self!(Mm);
impl_add_self!(Pt);
impl_add_self!(Px);

impl_add_assign_self!(Mm);
impl_add_assign_self!(Pt);
impl_add_assign_self!(Px);

impl_sub_assign_self!(Mm);
impl_sub_assign_self!(Pt);
impl_sub_assign_self!(Px);

impl_sub_self!(Mm);
impl_sub_self!(Pt);
impl_sub_self!(Px);

impl_mul_f32!(Mm);
impl_mul_f32!(Pt);

impl_mul_assign_f32!(Mm);
impl_mul_assign_f32!(Pt);

impl_div!(Mm);
impl_div!(Pt);

impl_div_assign_f32!(Mm);
impl_div_assign_f32!(Pt);

#[test]
fn point_to_mm_conversion() {
    let pt1: Mm = Pt(1.0).into();
    let pt2: Mm = Pt(15.0).into();
    assert_eq!(pt1, Mm(0.352778));
    assert_eq!(pt2, Mm(5.29167));
}

#[test]
fn mm_to_point_conversion() {
    let mm1: Pt = Mm(1.0).into();
    let mm2: Pt = Mm(23.0).into();
    assert_eq!(mm1, Pt(2.83464745483286));
    assert_eq!(mm2, Pt(65.1969));
}

#[test]
fn mm_eq_zero_check() {
    let mm1: Mm = Mm(0.0).into();
    let mm2: Mm = Mm(0.0).into();
    assert_eq!(mm1, mm2);
    assert_eq!(mm1, Mm(0.0));
    assert_eq!(mm2, Mm(0.0));
}

#[test]
fn max_mm() {
    let mm_vector = vec![Mm(0.0), Mm(1.0), Mm(2.0)];
    assert_eq!(mm_vector.iter().max().unwrap(), &Mm(2.0));
}

#[test]
fn min_mm() {
    let mm_vector = vec![Mm(0.0), Mm(1.0), Mm(2.0)];
    assert_eq!(mm_vector.iter().min().unwrap(), &Mm(0.0));
}


#[test]
fn pt_eq_zero_check() {
    let pt1: Pt = Pt(0.0).into();
    let pt2: Pt = Pt(0.0).into();
    assert_eq!(pt1, pt2);
    assert_eq!(pt1, Pt(0.0));
    assert_eq!(pt2, Pt(0.0));
}

#[test]
fn max_pt() {
    let pt_vector = vec![Pt(0.0), Pt(1.0), Pt(2.0)];
    assert_eq!(pt_vector.iter().max().unwrap(), &Pt(2.0));
}

#[test]
fn min_pt() {
    let pt_vector = vec![Pt(0.0), Pt(1.0), Pt(2.0)];
    assert_eq!(pt_vector.iter().min().unwrap(), &Pt(0.0));
}