#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PageOrientation {
Portrait = 0,
Landscape = 1,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PaperSize {
A4,
A3,
A5,
Letter,
Legal,
Tabloid,
B4,
B5,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MarginUnit {
Millimeters,
Inches,
Points,
HwpUnits,
}
#[derive(Debug, Clone)]
pub struct PageMargins {
pub left: u32,
pub right: u32,
pub top: u32,
pub bottom: u32,
pub header: u32,
pub footer: u32,
pub gutter: u32,
pub mirror_margins: bool,
}
#[derive(Debug, Clone)]
pub struct PageLayout {
pub paper_size: PaperSize,
pub orientation: PageOrientation,
pub width: u32,
pub height: u32,
pub margins: PageMargins,
pub columns: u16,
pub column_spacing: u32,
pub column_line: bool,
pub page_border: bool,
pub background_color: Option<u32>,
pub start_page_number: u16,
pub page_number_format: crate::model::header_footer::PageNumberFormat,
}
impl Default for PageMargins {
fn default() -> Self {
Self {
left: 8504, right: 8504, top: 5669, bottom: 4252, header: 4252, footer: 4252, gutter: 0,
mirror_margins: false,
}
}
}
impl Default for PageLayout {
fn default() -> Self {
Self {
paper_size: PaperSize::A4,
orientation: PageOrientation::Portrait,
width: 59528, height: 84188, margins: PageMargins::default(),
columns: 1,
column_spacing: 1417, column_line: false,
page_border: false,
background_color: None,
start_page_number: 1,
page_number_format: crate::model::header_footer::PageNumberFormat::Numeric,
}
}
}
impl PaperSize {
pub fn dimensions_hwp_units(&self) -> (u32, u32) {
match self {
PaperSize::A4 => (59528, 84188), PaperSize::A3 => (84188, 119055), PaperSize::A5 => (41929, 59528), PaperSize::Letter => (61200, 79200), PaperSize::Legal => (61200, 100800), PaperSize::Tabloid => (79200, 122400), PaperSize::B4 => (70866, 100063), PaperSize::B5 => (49606, 70866), PaperSize::Custom => (59528, 84188), }
}
pub fn name(&self) -> &'static str {
match self {
PaperSize::A4 => "A4",
PaperSize::A3 => "A3",
PaperSize::A5 => "A5",
PaperSize::Letter => "Letter",
PaperSize::Legal => "Legal",
PaperSize::Tabloid => "Tabloid",
PaperSize::B4 => "B4",
PaperSize::B5 => "B5",
PaperSize::Custom => "Custom",
}
}
}
impl PageMargins {
pub fn new_mm(left: f32, right: f32, top: f32, bottom: f32) -> Self {
Self {
left: mm_to_hwp_units(left),
right: mm_to_hwp_units(right),
top: mm_to_hwp_units(top),
bottom: mm_to_hwp_units(bottom),
header: mm_to_hwp_units(15.0), footer: mm_to_hwp_units(15.0), gutter: 0,
mirror_margins: false,
}
}
pub fn new_inches(left: f32, right: f32, top: f32, bottom: f32) -> Self {
Self {
left: inches_to_hwp_units(left),
right: inches_to_hwp_units(right),
top: inches_to_hwp_units(top),
bottom: inches_to_hwp_units(bottom),
header: inches_to_hwp_units(0.6), footer: inches_to_hwp_units(0.6), gutter: 0,
mirror_margins: false,
}
}
pub fn with_header_footer_mm(mut self, header: f32, footer: f32) -> Self {
self.header = mm_to_hwp_units(header);
self.footer = mm_to_hwp_units(footer);
self
}
pub fn with_gutter_mm(mut self, gutter: f32) -> Self {
self.gutter = mm_to_hwp_units(gutter);
self
}
pub fn with_mirror_margins(mut self, mirror: bool) -> Self {
self.mirror_margins = mirror;
self
}
pub fn narrow() -> Self {
Self::new_mm(12.7, 12.7, 12.7, 12.7)
}
pub fn normal() -> Self {
Self::new_mm(25.4, 25.4, 25.4, 25.4)
}
pub fn wide() -> Self {
Self::new_mm(50.8, 50.8, 25.4, 25.4)
}
}
impl PageLayout {
pub fn new(paper_size: PaperSize, orientation: PageOrientation) -> Self {
let (width, height) = paper_size.dimensions_hwp_units();
let (final_width, final_height) = match orientation {
PageOrientation::Portrait => (width, height),
PageOrientation::Landscape => (height, width),
};
Self {
paper_size,
orientation,
width: final_width,
height: final_height,
..Default::default()
}
}
pub fn a4_portrait() -> Self {
Self::new(PaperSize::A4, PageOrientation::Portrait)
}
pub fn a4_landscape() -> Self {
Self::new(PaperSize::A4, PageOrientation::Landscape)
}
pub fn letter_portrait() -> Self {
Self::new(PaperSize::Letter, PageOrientation::Portrait)
}
pub fn letter_landscape() -> Self {
Self::new(PaperSize::Letter, PageOrientation::Landscape)
}
pub fn custom_mm(width_mm: f32, height_mm: f32, orientation: PageOrientation) -> Self {
let width = mm_to_hwp_units(width_mm);
let height = mm_to_hwp_units(height_mm);
let (final_width, final_height) = match orientation {
PageOrientation::Portrait => (width, height),
PageOrientation::Landscape => (height, width),
};
Self {
paper_size: PaperSize::Custom,
orientation,
width: final_width,
height: final_height,
..Default::default()
}
}
pub fn with_margins(mut self, margins: PageMargins) -> Self {
self.margins = margins;
self
}
pub fn with_columns(mut self, columns: u16, spacing_mm: f32) -> Self {
self.columns = columns;
self.column_spacing = mm_to_hwp_units(spacing_mm);
self
}
pub fn with_column_line(mut self, show_line: bool) -> Self {
self.column_line = show_line;
self
}
pub fn with_background_color(mut self, color: u32) -> Self {
self.background_color = Some(color);
self
}
pub fn with_page_numbering(
mut self,
start: u16,
format: crate::model::header_footer::PageNumberFormat,
) -> Self {
self.start_page_number = start;
self.page_number_format = format;
self
}
pub fn effective_width(&self) -> u32 {
let total_margin = self.margins.left + self.margins.right + self.margins.gutter;
if self.width > total_margin {
self.width - total_margin
} else {
1000 }
}
pub fn effective_height(&self) -> u32 {
let total_margin = self.margins.top + self.margins.bottom;
if self.height > total_margin {
self.height - total_margin
} else {
1000 }
}
pub fn column_width(&self) -> u32 {
if self.columns <= 1 {
self.effective_width()
} else {
let total_spacing = (self.columns - 1) as u32 * self.column_spacing;
let available_width = if self.effective_width() > total_spacing {
self.effective_width() - total_spacing
} else {
1000
};
available_width / self.columns as u32
}
}
pub fn to_bytes(&self) -> Vec<u8> {
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::Cursor;
let mut data = Vec::new();
let mut writer = Cursor::new(&mut data);
writer.write_u32::<LittleEndian>(self.width).unwrap();
writer.write_u32::<LittleEndian>(self.height).unwrap();
writer.write_u32::<LittleEndian>(self.margins.left).unwrap();
writer
.write_u32::<LittleEndian>(self.margins.right)
.unwrap();
writer.write_u32::<LittleEndian>(self.margins.top).unwrap();
writer
.write_u32::<LittleEndian>(self.margins.bottom)
.unwrap();
writer
.write_u32::<LittleEndian>(self.margins.header)
.unwrap();
writer
.write_u32::<LittleEndian>(self.margins.footer)
.unwrap();
writer
.write_u32::<LittleEndian>(self.margins.gutter)
.unwrap();
writer.write_u16::<LittleEndian>(self.columns).unwrap();
writer
.write_u32::<LittleEndian>(self.column_spacing)
.unwrap();
let mut properties = 0u32;
if self.orientation == PageOrientation::Landscape {
properties |= 0x01;
}
if self.margins.mirror_margins {
properties |= 0x02;
}
if self.column_line {
properties |= 0x04;
}
if self.page_border {
properties |= 0x08;
}
writer.write_u32::<LittleEndian>(properties).unwrap();
writer
.write_u16::<LittleEndian>(self.start_page_number)
.unwrap();
writer.write_u8(self.page_number_format as u8).unwrap();
if let Some(color) = self.background_color {
writer.write_u8(1).unwrap(); writer.write_u32::<LittleEndian>(color).unwrap();
} else {
writer.write_u8(0).unwrap(); }
data
}
}
pub fn mm_to_hwp_units(mm: f32) -> u32 {
(mm * 283.465).round() as u32 }
pub fn inches_to_hwp_units(inches: f32) -> u32 {
(inches * 7200.0).round() as u32 }
pub fn hwp_units_to_mm(units: u32) -> f32 {
units as f32 / 283.465
}
pub fn hwp_units_to_inches(units: u32) -> f32 {
units as f32 / 7200.0
}