use crate::error::{HwpError, Result};
use crate::parser::record::Record;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum HeaderFooterType {
Header = 0,
Footer = 1,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PageApplyType {
All = 0,
FirstPage = 1,
EvenPages = 2,
OddPages = 3,
}
#[derive(Debug, Clone)]
pub struct HeaderFooter {
pub header_footer_type: HeaderFooterType,
pub apply_type: PageApplyType,
pub text: String,
pub char_shape_id: u16,
pub para_shape_id: u16,
pub alignment: u8,
pub include_page_number: bool,
pub page_number_format: u8,
pub height: u32,
pub margin: u32,
}
impl Default for HeaderFooter {
fn default() -> Self {
Self {
header_footer_type: HeaderFooterType::Header,
apply_type: PageApplyType::All,
text: String::new(),
char_shape_id: 0,
para_shape_id: 0,
alignment: 0, include_page_number: false,
page_number_format: 1, height: 1000, margin: 500, }
}
}
impl HeaderFooter {
pub fn new_header(text: &str) -> Self {
Self {
header_footer_type: HeaderFooterType::Header,
text: text.to_string(),
..Default::default()
}
}
pub fn new_footer(text: &str) -> Self {
Self {
header_footer_type: HeaderFooterType::Footer,
text: text.to_string(),
..Default::default()
}
}
pub fn with_page_number(mut self, format: PageNumberFormat) -> Self {
self.include_page_number = true;
self.page_number_format = format as u8;
self
}
pub fn with_alignment(mut self, alignment: HeaderFooterAlignment) -> Self {
self.alignment = alignment as u8;
self
}
pub fn with_apply_type(mut self, apply_type: PageApplyType) -> Self {
self.apply_type = apply_type;
self
}
pub fn with_height_mm(mut self, height_mm: u32) -> Self {
self.height = height_mm * 100; self
}
pub fn with_margin_mm(mut self, margin_mm: u32) -> Self {
self.margin = margin_mm * 100; self
}
pub fn to_bytes(&self) -> Vec<u8> {
use crate::utils::encoding::string_to_utf16le;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{Cursor, Write};
let mut data = Vec::new();
let mut writer = Cursor::new(&mut data);
writer.write_u8(self.header_footer_type as u8).unwrap();
writer.write_u8(self.apply_type as u8).unwrap();
writer.write_u8(self.alignment).unwrap();
writer
.write_u8(if self.include_page_number { 1 } else { 0 })
.unwrap();
writer.write_u8(self.page_number_format).unwrap();
writer.write_u32::<LittleEndian>(self.height).unwrap();
writer.write_u32::<LittleEndian>(self.margin).unwrap();
writer
.write_u16::<LittleEndian>(self.char_shape_id)
.unwrap();
writer
.write_u16::<LittleEndian>(self.para_shape_id)
.unwrap();
let text_utf16 = string_to_utf16le(&self.text);
writer
.write_u16::<LittleEndian>(text_utf16.len() as u16 / 2)
.unwrap();
writer.write_all(&text_utf16).unwrap();
data
}
pub fn from_record(record: &Record) -> Result<Self> {
let mut reader = record.data_reader();
if record.data.len() < 40 {
return Err(HwpError::InvalidFormat(
"HeaderFooter record too short".to_string(),
));
}
let _field1 = reader.read_u32()?; let _field2 = reader.read_u32()?; let height = reader.read_u32()?; let _height2 = reader.read_u32()?; let left_margin = reader.read_u32()?; let _top_margin = reader.read_u32()?; let _right_margin = reader.read_u32()?; let _bottom_margin = reader.read_u32()?; let _field9 = reader.read_u32()?; let _field10 = reader.read_u32()?;
Ok(Self {
header_footer_type: HeaderFooterType::Header,
apply_type: PageApplyType::All,
text: String::new(), char_shape_id: 0,
para_shape_id: 0,
alignment: 0,
include_page_number: false,
page_number_format: 1,
height,
margin: left_margin,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PageNumberFormat {
Numeric = 1,
RomanLower = 2,
RomanUpper = 3,
AlphaLower = 4,
AlphaUpper = 5,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum HeaderFooterAlignment {
Left = 0,
Center = 1,
Right = 2,
}
#[derive(Debug, Clone, Default)]
pub struct HeaderFooterCollection {
pub items: Vec<HeaderFooter>,
}
impl HeaderFooterCollection {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn add_header(&mut self, header: HeaderFooter) {
self.items.push(header);
}
pub fn add_footer(&mut self, footer: HeaderFooter) {
self.items.push(footer);
}
pub fn find_by_type(
&self,
header_footer_type: HeaderFooterType,
apply_type: PageApplyType,
) -> Option<&HeaderFooter> {
self.items.iter().find(|item| {
item.header_footer_type == header_footer_type && item.apply_type == apply_type
})
}
pub fn headers(&self) -> Vec<&HeaderFooter> {
self.items
.iter()
.filter(|item| item.header_footer_type == HeaderFooterType::Header)
.collect()
}
pub fn footers(&self) -> Vec<&HeaderFooter> {
self.items
.iter()
.filter(|item| item.header_footer_type == HeaderFooterType::Footer)
.collect()
}
}