mod chart;
mod drawing;
mod table;
use std::collections::HashMap;
pub use chart::*;
pub use drawing::*;
pub use table::*;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum Alignment {
#[default]
Left,
Center,
Right,
Justify,
Distribute,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TabAlignment {
Left,
Center,
Right,
Decimal,
}
#[derive(Clone, Debug)]
pub struct TabStop {
pub position: f32,
pub alignment: TabAlignment,
pub leader: Option<char>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum VertAlign {
#[default]
Baseline,
Superscript,
Subscript,
}
pub struct HeaderFooter {
pub blocks: Vec<Block>,
}
pub struct Footnote {
pub paragraphs: Vec<Paragraph>,
}
#[derive(Clone, Debug)]
pub struct Comment {
pub initials: String,
pub text: String,
pub display_index: u32,
}
#[derive(Clone, Copy, Debug)]
pub enum LineSpacing {
Auto(f32), Exact(f32), AtLeast(f32), }
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum DocGridType {
#[default]
Default,
Lines,
LinesAndChars,
SnapToChars,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SectionBreakType {
NextPage,
Continuous,
OddPage,
EvenPage,
}
pub struct ColumnDef {
pub width: f32,
pub space: f32,
}
pub struct ColumnsConfig {
pub columns: Vec<ColumnDef>,
pub sep: bool,
}
pub struct SectionProperties {
pub page_width: f32,
pub page_height: f32,
pub margin_top: f32,
pub margin_bottom: f32,
pub margin_top_fixed: bool,
pub margin_bottom_fixed: bool,
pub margin_left: f32,
pub margin_right: f32,
pub header_margin: f32,
pub footer_margin: f32,
pub header_default: Option<HeaderFooter>,
pub header_first: Option<HeaderFooter>,
pub header_even: Option<HeaderFooter>,
pub footer_default: Option<HeaderFooter>,
pub footer_first: Option<HeaderFooter>,
pub footer_even: Option<HeaderFooter>,
pub different_first_page: bool,
pub line_pitch: f32,
pub grid_type: DocGridType,
pub break_type: SectionBreakType,
pub columns: Option<ColumnsConfig>,
pub page_num_start: Option<u32>,
pub page_num_format: Option<String>,
pub page_borders: Option<PageBorders>,
pub vertical_align: PageVerticalAlign,
pub line_numbering: Option<LineNumbering>,
pub footnote_num_fmt: Option<String>,
pub endnote_num_fmt: Option<String>,
}
impl SectionProperties {
pub fn text_width(&self) -> f32 {
self.page_width - self.margin_left - self.margin_right
}
pub fn line_grid_pitch(&self) -> Option<f32> {
(matches!(
self.grid_type,
DocGridType::Lines | DocGridType::LinesAndChars | DocGridType::SnapToChars
) && self.line_pitch > 0.0)
.then_some(self.line_pitch)
}
}
#[derive(Clone, Copy)]
pub struct LineNumbering {
pub count_by: u32,
pub start: i32,
pub distance: Option<f32>,
pub restart: LineNumberRestart,
}
#[derive(Clone, Copy, Default, PartialEq)]
pub enum LineNumberRestart {
#[default]
NewPage,
NewSection,
Continuous,
}
#[derive(Clone, Copy, Default, PartialEq)]
pub enum PageVerticalAlign {
#[default]
Top,
Center,
Both,
Bottom,
}
#[derive(Clone, Copy, Default, PartialEq)]
pub enum PageBorderDisplay {
#[default]
AllPages,
FirstPage,
NotFirstPage,
}
#[derive(Clone, Default)]
pub struct PageBorders {
pub top: Option<ParagraphBorder>,
pub bottom: Option<ParagraphBorder>,
pub left: Option<ParagraphBorder>,
pub right: Option<ParagraphBorder>,
pub offset_from_page: bool,
pub display: PageBorderDisplay,
}
pub struct Section {
pub properties: SectionProperties,
pub blocks: Vec<Block>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FontFamily {
Auto,
Roman,
Swiss,
Modern,
Script,
Decorative,
}
#[derive(Clone, Debug)]
pub struct FontTableEntry {
pub alt_name: Option<String>,
pub family: FontFamily,
pub charset: Option<u8>,
}
pub type FontTable = HashMap<String, FontTableEntry>;
pub struct Document {
pub sections: Vec<Section>,
pub line_spacing: LineSpacing,
pub embedded_fonts: HashMap<(String, bool, bool), Vec<u8>>,
pub footnotes: HashMap<u32, Footnote>,
pub footnote_separator: Option<Paragraph>,
pub endnotes: HashMap<u32, Footnote>,
pub comments: HashMap<u32, Comment>,
pub font_table: FontTable,
pub even_and_odd_headers: bool,
pub mirror_margins: bool,
pub default_tab_stop: f32,
pub style_id_to_name: HashMap<String, String>,
pub theme_minor_font: String,
pub title: Option<String>,
pub author: Option<String>,
pub subject: Option<String>,
pub keywords: Option<String>,
pub default_lang: Option<String>,
pub compress_punctuation: bool,
pub compat_mode: u32,
pub do_not_expand_shift_return: bool,
pub adjust_line_height_in_table: bool,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HorizontalPosition {
Offset(f32),
AlignCenter,
AlignLeft,
AlignRight,
}
impl Default for HorizontalPosition {
fn default() -> Self {
Self::Offset(0.0)
}
}
impl HorizontalPosition {
pub fn offset_or_zero(self) -> f32 {
match self {
Self::Offset(o) => o,
_ => 0.0,
}
}
pub fn place(self, origin: f32, area_w: f32, obj_w: f32) -> f32 {
match self {
HorizontalPosition::AlignCenter => origin + (area_w - obj_w) / 2.0,
HorizontalPosition::AlignRight => origin + area_w - obj_w,
HorizontalPosition::AlignLeft => origin,
HorizontalPosition::Offset(o) => origin + o,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VerticalPosition {
Offset(f32),
AlignTop,
AlignCenter,
AlignBottom,
}
impl Default for VerticalPosition {
fn default() -> Self {
Self::Offset(0.0)
}
}
impl VerticalPosition {
pub fn offset_or_zero(self) -> f32 {
match self {
Self::Offset(o) => o,
_ => 0.0,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum HRelativeFrom {
Page,
Margin,
#[default]
Column,
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum VRelativeFrom {
Page,
Margin,
TopMargin,
#[default]
Paragraph,
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum WrapType {
#[default]
None,
Square,
Tight,
Through,
TopAndBottom,
}
impl WrapType {
pub fn wraps_beside(self) -> bool {
matches!(self, WrapType::Square | WrapType::Tight | WrapType::Through)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum WrapText {
#[default]
BothSides,
Left,
Right,
Largest,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FrameProperties {
pub h_relative_from: HRelativeFrom,
pub h_position: HorizontalPosition,
pub v_relative_from: VRelativeFrom,
pub y_offset: f32,
pub width: f32,
pub height: f32,
pub text_below: bool,
pub h_space: f32,
pub v_space: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParagraphBorder {
pub width_pt: f32,
pub space_pt: f32,
pub color: [u8; 3],
}
#[derive(Clone, Default)]
pub struct ParagraphBorders {
pub top: Option<ParagraphBorder>,
pub bottom: Option<ParagraphBorder>,
pub left: Option<ParagraphBorder>,
pub right: Option<ParagraphBorder>,
pub between: Option<ParagraphBorder>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ListItem {
pub level: u8,
pub list_id: u32,
pub numbering: pdf_writer::types::ListNumbering,
}
#[derive(Default)]
pub struct Paragraph {
pub runs: Vec<Run>,
pub style_id: Option<String>,
pub space_before: f32,
pub space_after: f32,
pub space_before_auto: bool,
pub space_after_auto: bool,
pub content_height: f32,
pub alignment: Alignment,
pub indent_left: f32,
pub indent_right: f32,
pub indent_hanging: f32,
pub indent_first_line: f32,
pub list_label: String,
pub list_label_font: Option<String>,
pub list_label_font_size: Option<f32>,
pub list_label_bold: bool,
pub list_label_color: Option<[u8; 3]>,
pub list_item: Option<ListItem>,
pub starts_toc_field: bool,
pub num_level_tab_stop: Option<f32>,
pub contextual_spacing: bool,
pub keep_next: bool,
pub keep_lines: bool,
pub widow_control: bool,
pub line_spacing: Option<LineSpacing>,
pub image: Option<EmbeddedImage>,
pub borders: ParagraphBorders,
pub shading: Option<[u8; 3]>,
pub page_break_before: bool,
pub page_break_before_explicit: bool,
pub page_break_after: bool,
pub page_break_at: Option<usize>,
pub column_break_before: bool,
pub column_break_after: bool,
pub clears_floats: bool,
pub tab_stops: Vec<TabStop>,
pub floating_images: Vec<FloatingImage>,
pub textboxes: Vec<Textbox>,
pub connectors: Vec<ConnectorShape>,
pub inline_chart: Option<InlineChart>,
pub smartart: Vec<SmartArtDiagram>,
pub horizontal_rule: Option<HorizontalRule>,
pub is_section_break: bool,
pub bookmarks: Vec<String>,
pub outline_level: Option<u8>,
pub paragraph_mark_vanish: bool,
pub paragraph_mark_font_size: Option<f32>,
pub paragraph_mark_font_name: Option<String>,
pub paragraph_mark_position: f32,
pub snap_to_grid: bool,
pub auto_space_de: bool,
pub auto_space_dn: bool,
pub frame_props: Option<FrameProperties>,
}
pub struct HorizontalRule {
pub height_pt: f32,
pub fill_color: [u8; 3],
pub width_pct: f32,
pub is_standard: bool,
}
#[derive(Clone)]
pub struct Run {
pub text: String,
pub font_size: f32,
pub font_name: String,
pub east_asia_font_name: Option<String>,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub double_underline: bool,
pub strikethrough: bool,
pub dstrike: bool,
pub char_spacing: f32,
pub text_scale: f32,
pub caps: bool,
pub small_caps: bool,
pub vanish: bool,
pub color: Option<[u8; 3]>, pub highlight: Option<[u8; 3]>,
pub shading: Option<[u8; 3]>,
pub border: Option<ParagraphBorder>,
pub is_tab: bool,
pub ptab_alignment: Option<TabAlignment>,
pub is_line_break: bool,
pub vertical_align: VertAlign,
pub field_code: Option<FieldCode>,
pub hyperlink_url: Option<String>,
pub inline_image: Option<EmbeddedImage>,
pub footnote_id: Option<u32>,
pub is_footnote_ref_mark: bool,
pub endnote_id: Option<u32>,
pub is_endnote_ref_mark: bool,
pub kern_threshold: Option<f32>,
pub position: f32,
pub char_style_id: Option<String>,
pub text_outline: Option<TextOutline>,
pub text_fill: Option<TextFill>,
pub text_shadow: Option<TextShadow>,
pub text_glow: Option<TextGlow>,
pub lang: Option<String>,
pub text_lang: Option<std::sync::Arc<str>>,
pub text_lang_east_asia: Option<std::sync::Arc<str>>,
pub font_size_from_default: bool,
pub font_name_from_default: bool,
pub bold_is_direct: bool,
pub italic_is_direct: bool,
pub comment_ids: Vec<u32>,
pub is_math: bool,
pub formula: Option<std::sync::Arc<str>>,
pub checkbox: Option<FormCheckbox>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FormCheckbox {
pub size: f32,
pub checked: bool,
}
impl FormCheckbox {
pub const TEXT: &'static str = "\u{2610}";
}
impl Run {
pub fn kerns_at(&self, font_size: f32) -> bool {
self.kern_threshold
.is_some_and(|t| t > 0.0 && font_size >= t)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextOutline {
pub width_pt: f32,
pub color: [u8; 3],
}
#[derive(Clone, Debug, PartialEq)]
pub enum TextFill {
Solid([u8; 3]),
Gradient {
stops: Vec<([u8; 3], f32)>,
angle_deg: f32,
},
NoFill,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextShadow {
pub color: [u8; 3],
pub offset_x: f32,
pub offset_y: f32,
pub alpha: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextGlow {
pub color: [u8; 3],
pub radius_pt: f32,
}
impl Default for Run {
fn default() -> Self {
Self {
text: String::new(),
font_size: 0.0,
font_name: String::new(),
east_asia_font_name: None,
bold: false,
italic: false,
underline: false,
double_underline: false,
strikethrough: false,
dstrike: false,
char_spacing: 0.0,
text_scale: 100.0,
caps: false,
small_caps: false,
vanish: false,
color: None,
highlight: None,
shading: None,
border: None,
is_tab: false,
ptab_alignment: None,
is_line_break: false,
vertical_align: VertAlign::Baseline,
field_code: None,
hyperlink_url: None,
inline_image: None,
footnote_id: None,
is_footnote_ref_mark: false,
endnote_id: None,
is_endnote_ref_mark: false,
kern_threshold: None,
position: 0.0,
char_style_id: None,
text_outline: None,
text_fill: None,
text_shadow: None,
text_glow: None,
lang: None,
text_lang: None,
text_lang_east_asia: None,
font_size_from_default: false,
font_name_from_default: false,
bold_is_direct: false,
italic_is_direct: false,
comment_ids: Vec::new(),
is_math: false,
formula: None,
checkbox: None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum FieldCode {
Page,
NumPages,
StyleRef {
name: String,
number: bool,
},
PageRef(String),
If(Vec<IfPart>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum IfPart {
Text(String),
Field(FieldCode),
}
pub enum Block {
Paragraph(Paragraph),
Table(Table),
}