use serde::{Deserialize, Serialize};
use super::style::{StyleMap, Transform};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Paginated {
pub version: String,
#[serde(rename = "type")]
pub presentation_type: String,
pub defaults: PaginatedDefaults,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pages: Vec<Page>,
#[serde(default, skip_serializing_if = "StyleMap::is_empty")]
pub styles: StyleMap,
}
impl Default for Paginated {
fn default() -> Self {
Self {
version: crate::SPEC_VERSION.to_string(),
presentation_type: "paginated".to_string(),
defaults: PaginatedDefaults::default(),
pages: Vec::new(),
styles: StyleMap::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaginatedDefaults {
pub page_size: PageSize,
pub orientation: Orientation,
pub margins: Margins,
}
impl Default for PaginatedDefaults {
fn default() -> Self {
Self {
page_size: PageSize::letter(),
orientation: Orientation::Portrait,
margins: Margins::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PageSize {
Named(StandardPageSize),
Custom {
width: String,
height: String,
},
}
impl PageSize {
#[must_use]
pub fn letter() -> Self {
Self::Named(StandardPageSize::Letter)
}
#[must_use]
pub fn legal() -> Self {
Self::Named(StandardPageSize::Legal)
}
#[must_use]
pub fn a4() -> Self {
Self::Named(StandardPageSize::A4)
}
#[must_use]
pub fn a5() -> Self {
Self::Named(StandardPageSize::A5)
}
}
impl Default for PageSize {
fn default() -> Self {
Self::letter()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StandardPageSize {
Letter,
Legal,
A4,
A5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Orientation {
#[default]
Portrait,
Landscape,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Margins {
pub top: String,
pub right: String,
pub bottom: String,
pub left: String,
}
impl Default for Margins {
fn default() -> Self {
Self {
top: "1in".to_string(),
right: "1in".to_string(),
bottom: "1in".to_string(),
left: "1in".to_string(),
}
}
}
impl Margins {
#[must_use]
pub fn all(value: impl Into<String>) -> Self {
let v = value.into();
Self {
top: v.clone(),
right: v.clone(),
bottom: v.clone(),
left: v,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Page {
pub number: u32,
#[serde(default)]
pub elements: Vec<PageElement>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PageElement {
pub block_id: String,
pub position: Position,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub style: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub overflow: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transform: Option<Transform>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Position {
pub x: String,
pub y: String,
pub width: String,
pub height: String,
}
impl Position {
#[must_use]
pub fn new(x: impl Into<String>, y: impl Into<String>, width: impl Into<String>) -> Self {
Self {
x: x.into(),
y: y.into(),
width: width.into(),
height: "auto".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FlowElement {
#[serde(rename = "type")]
pub element_type: String,
pub block_ids: Vec<String>,
#[serde(default = "default_columns")]
pub columns: u32,
pub start_page: u32,
pub regions: Vec<FlowRegion>,
}
fn default_columns() -> u32 {
1
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FlowRegion {
pub page: u32,
pub position: Position,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_paginated_default() {
let p = Paginated::default();
assert_eq!(p.presentation_type, "paginated");
assert_eq!(p.defaults.page_size, PageSize::letter());
assert_eq!(p.defaults.orientation, Orientation::Portrait);
}
#[test]
fn test_margins() {
let m = Margins::all("0.5in");
assert_eq!(m.top, "0.5in");
assert_eq!(m.right, "0.5in");
assert_eq!(m.bottom, "0.5in");
assert_eq!(m.left, "0.5in");
}
#[test]
fn test_serialization() {
let p = Paginated::default();
let json = serde_json::to_string_pretty(&p).unwrap();
assert!(json.contains("\"type\": \"paginated\""));
assert!(json.contains("\"pageSize\": \"letter\""));
}
#[test]
fn test_custom_page_size() {
let size = PageSize::Custom {
width: "8in".to_string(),
height: "10in".to_string(),
};
let json = serde_json::to_string(&size).unwrap();
assert!(json.contains("\"width\":\"8in\""));
}
}