#[cfg(feature = "office")]
mod docx;
#[cfg(feature = "office")]
mod pptx;
#[cfg(feature = "office")]
mod styles;
#[cfg(feature = "office")]
mod xlsx;
#[cfg(feature = "office")]
pub use docx::DocxConverter;
#[cfg(feature = "office")]
pub use pptx::PptxConverter;
#[cfg(feature = "office")]
pub use xlsx::XlsxConverter;
use crate::error::{Error, Result};
use crate::writer::PageSize;
use std::path::Path;
#[derive(Debug, Clone, Copy)]
pub struct Margins {
pub top: f32,
pub bottom: f32,
pub left: f32,
pub right: f32,
}
impl Default for Margins {
fn default() -> Self {
Self {
top: 72.0, bottom: 72.0, left: 72.0, right: 72.0, }
}
}
impl Margins {
pub fn uniform(margin: f32) -> Self {
Self {
top: margin,
bottom: margin,
left: margin,
right: margin,
}
}
pub fn none() -> Self {
Self::uniform(0.0)
}
}
#[derive(Debug, Clone)]
pub struct OfficeConfig {
pub page_size: PageSize,
pub margins: Margins,
pub embed_fonts: bool,
pub default_font: String,
pub default_font_size: f32,
pub line_height: f32,
pub include_images: bool,
}
impl Default for OfficeConfig {
fn default() -> Self {
Self {
page_size: PageSize::Letter,
margins: Margins::default(),
embed_fonts: false,
default_font: "Helvetica".to_string(),
default_font_size: 11.0,
line_height: 1.2,
include_images: true,
}
}
}
impl OfficeConfig {
pub fn a4() -> Self {
Self {
page_size: PageSize::A4,
..Default::default()
}
}
pub fn letter() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct OfficeConverter {
config: OfficeConfig,
}
impl OfficeConverter {
pub fn new() -> Self {
Self::default()
}
pub fn with_config(config: OfficeConfig) -> Self {
Self { config }
}
pub fn config(&self) -> &OfficeConfig {
&self.config
}
#[cfg(feature = "office")]
pub fn convert_docx(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
let bytes = std::fs::read(path.as_ref())?;
self.convert_docx_bytes(&bytes)
}
#[cfg(feature = "office")]
pub fn convert_docx_bytes(&self, bytes: &[u8]) -> Result<Vec<u8>> {
let converter = DocxConverter::new(self.config.clone());
converter.convert(bytes)
}
#[cfg(feature = "office")]
pub fn convert_xlsx(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
let bytes = std::fs::read(path.as_ref())?;
self.convert_xlsx_bytes(&bytes)
}
#[cfg(feature = "office")]
pub fn convert_xlsx_bytes(&self, bytes: &[u8]) -> Result<Vec<u8>> {
let converter = XlsxConverter::new(self.config.clone());
converter.convert(bytes)
}
#[cfg(feature = "office")]
pub fn convert_pptx(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
let bytes = std::fs::read(path.as_ref())?;
self.convert_pptx_bytes(&bytes)
}
#[cfg(feature = "office")]
pub fn convert_pptx_bytes(&self, bytes: &[u8]) -> Result<Vec<u8>> {
let converter = PptxConverter::new(self.config.clone());
converter.convert(bytes)
}
pub fn convert(&self, path: impl AsRef<Path>) -> Result<Vec<u8>> {
let path = path.as_ref();
let extension = path
.extension()
.and_then(|e| e.to_str())
.map(|e| e.to_lowercase())
.unwrap_or_default();
match extension.as_str() {
#[cfg(feature = "office")]
"docx" => self.convert_docx(path),
#[cfg(feature = "office")]
"xlsx" | "xls" => self.convert_xlsx(path),
#[cfg(feature = "office")]
"pptx" => self.convert_pptx(path),
_ => Err(Error::InvalidPdf(format!("Unsupported file format: {}", extension))),
}
}
#[cfg(not(feature = "office"))]
pub fn convert_docx(&self, _path: impl AsRef<Path>) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
#[cfg(not(feature = "office"))]
pub fn convert_docx_bytes(&self, _bytes: &[u8]) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
#[cfg(not(feature = "office"))]
pub fn convert_xlsx(&self, _path: impl AsRef<Path>) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
#[cfg(not(feature = "office"))]
pub fn convert_xlsx_bytes(&self, _bytes: &[u8]) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
#[cfg(not(feature = "office"))]
pub fn convert_pptx(&self, _path: impl AsRef<Path>) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
#[cfg(not(feature = "office"))]
pub fn convert_pptx_bytes(&self, _bytes: &[u8]) -> Result<Vec<u8>> {
Err(Error::InvalidPdf("Office conversion requires the 'office' feature".to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_margins_default() {
let margins = Margins::default();
assert_eq!(margins.top, 72.0);
assert_eq!(margins.bottom, 72.0);
assert_eq!(margins.left, 72.0);
assert_eq!(margins.right, 72.0);
}
#[test]
fn test_margins_uniform() {
let margins = Margins::uniform(36.0);
assert_eq!(margins.top, 36.0);
assert_eq!(margins.bottom, 36.0);
}
#[test]
fn test_config_default() {
let config = OfficeConfig::default();
assert_eq!(config.default_font, "Helvetica");
assert_eq!(config.default_font_size, 11.0);
}
#[test]
fn test_converter_new() {
let converter = OfficeConverter::new();
assert_eq!(converter.config().default_font, "Helvetica");
}
}