use hwpforge_foundation::{GutterType, HwpUnit};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct PageSettings {
pub width: HwpUnit,
pub height: HwpUnit,
pub margin_left: HwpUnit,
pub margin_right: HwpUnit,
pub margin_top: HwpUnit,
pub margin_bottom: HwpUnit,
pub header_margin: HwpUnit,
pub footer_margin: HwpUnit,
#[serde(default, skip_serializing_if = "HwpUnit::is_zero")]
pub gutter: HwpUnit,
#[serde(default)]
pub gutter_type: GutterType,
#[serde(default)]
pub mirror_margins: bool,
#[serde(default)]
pub landscape: bool,
}
const _: () = assert!(std::mem::size_of::<PageSettings>() == 40);
impl PageSettings {
pub fn a4() -> Self {
Self {
width: HwpUnit::from_mm(210.0).unwrap(),
height: HwpUnit::from_mm(297.0).unwrap(),
margin_left: HwpUnit::from_mm(20.0).unwrap(),
margin_right: HwpUnit::from_mm(20.0).unwrap(),
margin_top: HwpUnit::from_mm(20.0).unwrap(),
margin_bottom: HwpUnit::from_mm(20.0).unwrap(),
header_margin: HwpUnit::from_mm(10.0).unwrap(),
footer_margin: HwpUnit::from_mm(10.0).unwrap(),
gutter: HwpUnit::ZERO,
gutter_type: GutterType::LeftOnly,
mirror_margins: false,
landscape: false,
}
}
pub fn letter() -> Self {
Self {
width: HwpUnit::from_inch(8.5).unwrap(),
height: HwpUnit::from_inch(11.0).unwrap(),
margin_left: HwpUnit::from_inch(1.0).unwrap(),
margin_right: HwpUnit::from_inch(1.0).unwrap(),
margin_top: HwpUnit::from_inch(1.0).unwrap(),
margin_bottom: HwpUnit::from_inch(1.0).unwrap(),
header_margin: HwpUnit::from_inch(0.5).unwrap(),
footer_margin: HwpUnit::from_inch(0.5).unwrap(),
gutter: HwpUnit::ZERO,
gutter_type: GutterType::LeftOnly,
mirror_margins: false,
landscape: false,
}
}
pub fn printable_width(&self) -> HwpUnit {
self.width - self.margin_left - self.margin_right
}
pub fn printable_height(&self) -> HwpUnit {
self.height - self.margin_top - self.margin_bottom
}
}
impl Default for PageSettings {
fn default() -> Self {
Self::a4()
}
}
impl std::fmt::Display for PageSettings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PageSettings({:.1}mm x {:.1}mm)", self.width.to_mm(), self.height.to_mm())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a4_dimensions() {
let a4 = PageSettings::a4();
assert!((a4.width.to_mm() - 210.0).abs() < 0.1, "width: {}", a4.width.to_mm());
assert!((a4.height.to_mm() - 297.0).abs() < 0.1, "height: {}", a4.height.to_mm());
}
#[test]
fn a4_margins() {
let a4 = PageSettings::a4();
assert!((a4.margin_left.to_mm() - 20.0).abs() < 0.1);
assert!((a4.margin_right.to_mm() - 20.0).abs() < 0.1);
assert!((a4.margin_top.to_mm() - 20.0).abs() < 0.1);
assert!((a4.margin_bottom.to_mm() - 20.0).abs() < 0.1);
}
#[test]
fn a4_header_footer_margins() {
let a4 = PageSettings::a4();
assert!((a4.header_margin.to_mm() - 10.0).abs() < 0.1);
assert!((a4.footer_margin.to_mm() - 10.0).abs() < 0.1);
}
#[test]
fn letter_dimensions() {
let letter = PageSettings::letter();
assert_eq!(letter.width.as_i32(), 61200);
assert_eq!(letter.height.as_i32(), 79200);
}
#[test]
fn letter_margins() {
let letter = PageSettings::letter();
assert_eq!(letter.margin_left.as_i32(), 7200);
assert_eq!(letter.margin_right.as_i32(), 7200);
assert_eq!(letter.margin_top.as_i32(), 7200);
assert_eq!(letter.margin_bottom.as_i32(), 7200);
}
#[test]
fn default_is_a4() {
assert_eq!(PageSettings::default(), PageSettings::a4());
}
#[test]
fn printable_width() {
let a4 = PageSettings::a4();
let pw = a4.printable_width();
assert!((pw.to_mm() - 170.0).abs() < 0.5, "printable width: {}mm", pw.to_mm());
}
#[test]
fn printable_height() {
let a4 = PageSettings::a4();
let ph = a4.printable_height();
assert!((ph.to_mm() - 257.0).abs() < 0.5, "printable height: {}mm", ph.to_mm());
}
#[test]
fn custom_page_with_struct_update() {
let custom = PageSettings {
width: HwpUnit::from_mm(148.0).unwrap(),
height: HwpUnit::from_mm(210.0).unwrap(),
..PageSettings::a4()
};
assert!((custom.width.to_mm() - 148.0).abs() < 0.1);
assert!((custom.height.to_mm() - 210.0).abs() < 0.1);
assert!((custom.margin_left.to_mm() - 20.0).abs() < 0.1);
}
#[test]
fn size_assertion() {
assert_eq!(std::mem::size_of::<PageSettings>(), 40);
}
#[test]
fn display_format() {
let a4 = PageSettings::a4();
let s = a4.to_string();
assert!(s.contains("210.0"), "display: {s}");
assert!(s.contains("297.0"), "display: {s}");
}
#[test]
fn copy_semantics() {
let a = PageSettings::a4();
let b = a; assert_eq!(a, b);
}
#[test]
fn serde_roundtrip() {
let ps = PageSettings::a4();
let json = serde_json::to_string(&ps).unwrap();
let back: PageSettings = serde_json::from_str(&json).unwrap();
assert_eq!(ps, back);
}
#[test]
fn letter_serde_roundtrip() {
let ps = PageSettings::letter();
let json = serde_json::to_string(&ps).unwrap();
let back: PageSettings = serde_json::from_str(&json).unwrap();
assert_eq!(ps, back);
}
}