pptx-to-md 1.0.0

Parse Microsoft PowerPoint files (.pptx) and OpenDocument Presentations (.odp) into Markdown (.md)
Documentation
#[derive(Debug)]
pub struct Presentation {
    pub metadata: crate::PresentationMetadata,
    pub slides: Vec<crate::Slide>,
    pub diagnostics: Vec<ParseDiagnostic>,
}

#[derive(Debug, Clone)]
pub enum SlideElement {
    Text(TextElement, ElementPosition),
    Table(TableElement, ElementPosition),
    Image(ImageReference, ElementPosition),
    List(ListElement, ElementPosition),
    Unknown,
}

impl SlideElement {
    pub fn position(&self) -> ElementPosition {
        match self {
            SlideElement::Text(_, pos)
            | SlideElement::Image(_, pos)
            | SlideElement::List(_, pos)
            | SlideElement::Table(_, pos) => *pos,
            SlideElement::Unknown => ElementPosition::default(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct ImageReference {
    pub id: String,
    pub target: String,
}

#[derive(Debug, Clone)]
pub struct TextElement {
    pub runs: Vec<Run>,
}

#[derive(Debug, Default, Clone)]
pub struct Formatting {
    pub bold: bool,
    pub italic: bool,
    pub underlined: bool,
    pub strikethrough: bool,
    pub baseline: Baseline,
    pub font_size_points: Option<f32>,
    pub lang: String,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum Baseline {
    #[default]
    Normal,
    Superscript,
    Subscript,
}

#[derive(Debug, Clone)]
pub struct Run {
    pub text: String,
    pub formatting: Formatting,
    pub link_target: Option<String>,
}

impl Run {
    pub fn extract(&self) -> String {
        self.text.to_string()
    }

    pub fn render_as_md(&self) -> String {
        crate::markdown::render_run(self)
    }
}

#[derive(Debug, Clone)]
pub struct TableElement {
    pub rows: Vec<TableRow>,
}

#[derive(Debug, Clone)]
pub struct TableRow {
    pub cells: Vec<TableCell>,
}

#[derive(Debug, Clone, Default)]
pub struct TableCell {
    pub runs: Vec<Run>,
    pub paragraphs: Vec<Paragraph>,
    pub row_span: usize,
    pub column_span: usize,
    pub covered: bool,
}

#[derive(Debug, Clone)]
pub struct ListElement {
    pub items: Vec<ListItem>,
}

#[derive(Debug, Clone)]
pub struct ListItem {
    pub level: u32,
    pub is_ordered: bool,
    pub runs: Vec<Run>,
}

#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct ElementPosition {
    pub x: i64,
    pub y: i64,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Bounds {
    pub x: i64,
    pub y: i64,
    pub width: i64,
    pub height: i64,
}

impl From<ElementPosition> for Bounds {
    fn from(position: ElementPosition) -> Self {
        Self {
            x: position.x,
            y: position.y,
            width: 0,
            height: 0,
        }
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum TextRole {
    Title,
    Subtitle,
    Heading,
    Body,
    Caption,
    #[default]
    Other,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum ParagraphAlignment {
    #[default]
    Start,
    Center,
    End,
    Justify,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListKind {
    Bullet { character: Option<String> },
    Ordered { style: Option<String>, start: u32 },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListInfo {
    pub level: u32,
    pub kind: ListKind,
}

#[derive(Debug, Clone, Default)]
pub struct Paragraph {
    pub runs: Vec<Run>,
    pub alignment: ParagraphAlignment,
    pub list: Option<ListInfo>,
    pub list_explicit: bool,
}

impl Paragraph {
    pub fn plain(runs: Vec<Run>) -> Self {
        Self {
            runs,
            ..Self::default()
        }
    }

    pub fn text(&self) -> String {
        self.runs.iter().map(|run| run.text.as_str()).collect()
    }
}

#[derive(Debug, Clone, Default)]
pub struct TextBlock {
    pub role: TextRole,
    pub paragraphs: Vec<Paragraph>,
}

#[derive(Debug, Clone, Default)]
pub struct SemanticTableCell {
    pub paragraphs: Vec<Paragraph>,
    pub row_span: usize,
    pub column_span: usize,
    pub covered: bool,
}

#[derive(Debug, Clone, Default)]
pub struct SemanticTableRow {
    pub cells: Vec<SemanticTableCell>,
}

#[derive(Debug, Clone, Default)]
pub struct SemanticTable {
    pub rows: Vec<SemanticTableRow>,
}

#[derive(Debug, Clone)]
pub struct ImageBlock {
    pub reference: ImageReference,
    pub alt_text: Option<String>,
    pub mime_type: Option<String>,
}

#[derive(Debug, Clone)]
pub struct UnsupportedBlock {
    pub kind: String,
    pub fallback_text: Option<String>,
}

#[derive(Debug, Clone)]
pub enum SlideBlockContent {
    Text(TextBlock),
    Table(SemanticTable),
    Image(ImageBlock),
    Unsupported(UnsupportedBlock),
}

#[derive(Debug, Clone)]
pub struct SlideBlock {
    pub bounds: Bounds,
    pub source_order: usize,
    pub content: SlideBlockContent,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum ReadingOrder {
    Source,
    #[default]
    Spatial,
}

#[derive(Debug, Clone)]
pub struct MarkdownOptions {
    pub reading_order: ReadingOrder,
    pub include_slide_number_as_comment: bool,
    pub include_speaker_notes: bool,
    pub include_comments: bool,
    pub render_unsupported_comments: bool,
}

impl Default for MarkdownOptions {
    fn default() -> Self {
        Self {
            reading_order: ReadingOrder::Spatial,
            include_slide_number_as_comment: true,
            include_speaker_notes: false,
            include_comments: false,
            render_unsupported_comments: true,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticSeverity {
    Warning,
    Error,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseDiagnostic {
    pub severity: DiagnosticSeverity,
    pub message: String,
    pub source: Option<String>,
}

#[cfg(test)]
#[path = "../tests/unit/types.rs"]
mod tests;