katana-document-viewer 0.1.4

KatanA document viewer artifact, render evaluation, and export foundation.
Documentation
use super::{SurfaceLine, SurfaceLineAlignment, SurfaceLineLevel};
use crate::export_surface_helpers::{LIST_INDENT, PAGE_PADDING, QUOTE_INDENT};

const HEADING_1_FONT_SIZE: f32 = 40.0;
const HEADING_2_FONT_SIZE: f32 = 34.0;
const HEADING_DEFAULT_FONT_SIZE: f32 = 28.0;
const BODY_FONT_SIZE: f32 = 24.0;
const CODE_FONT_SIZE: f32 = 22.0;

const HEADING_1_LINE_HEIGHT: u32 = 92;
const HEADING_2_LINE_HEIGHT: u32 = 78;
const HEADING_DEFAULT_LINE_HEIGHT: u32 = 66;
const BODY_LINE_HEIGHT: u32 = 46;
const CODE_LINE_HEIGHT: u32 = 34;

impl SurfaceLine {
    #[cfg(test)]
    pub(crate) fn heading(level: u8, text: String) -> Self {
        Self {
            spans: vec![crate::export_surface_span::SurfaceTextSpan::plain(
                text.clone(),
            )],
            text,
            level: SurfaceLineLevel::Heading(level),
            quote_depth: 0,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    pub(crate) fn heading_spans(
        level: u8,
        spans: Vec<crate::export_surface_span::SurfaceTextSpan>,
    ) -> Self {
        Self {
            text: spans.iter().map(|span| span.text.as_str()).collect(),
            spans,
            level: SurfaceLineLevel::Heading(level),
            quote_depth: 0,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    #[cfg(test)]
    pub(crate) fn body(text: String) -> Self {
        Self::body_with_quote(text, 0)
    }

    pub(crate) fn body_with_quote(text: String, quote_depth: u32) -> Self {
        Self {
            spans: vec![crate::export_surface_span::SurfaceTextSpan::plain(
                text.clone(),
            )],
            text,
            level: SurfaceLineLevel::Body,
            quote_depth,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    pub(crate) fn body_centered(text: String) -> Self {
        Self {
            spans: vec![crate::export_surface_span::SurfaceTextSpan::plain(
                text.clone(),
            )],
            text,
            level: SurfaceLineLevel::Body,
            quote_depth: 0,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Center,
        }
    }

    pub(crate) fn centered_spans(spans: Vec<crate::export_surface_span::SurfaceTextSpan>) -> Self {
        Self {
            text: spans.iter().map(|span| span.text.as_str()).collect(),
            spans,
            level: SurfaceLineLevel::Body,
            quote_depth: 0,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Center,
        }
    }

    pub(crate) fn body_spans(
        spans: Vec<crate::export_surface_span::SurfaceTextSpan>,
        quote_depth: u32,
    ) -> Self {
        Self {
            text: spans.iter().map(|span| span.text.as_str()).collect(),
            spans,
            level: SurfaceLineLevel::Body,
            quote_depth,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    pub(crate) fn body_spans_with_indent(
        spans: Vec<crate::export_surface_span::SurfaceTextSpan>,
        quote_depth: u32,
        indent_depth: u32,
    ) -> Self {
        Self {
            text: spans.iter().map(|span| span.text.as_str()).collect(),
            spans,
            level: SurfaceLineLevel::Body,
            quote_depth,
            indent_depth,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    pub(crate) fn code_spans(spans: Vec<crate::export_surface_span::SurfaceTextSpan>) -> Self {
        Self {
            text: spans.iter().map(|span| span.text.as_str()).collect(),
            spans,
            level: SurfaceLineLevel::Code,
            quote_depth: 0,
            indent_depth: 0,
            alignment: SurfaceLineAlignment::Left,
        }
    }

    pub(crate) fn x(&self) -> u32 {
        PAGE_PADDING + self.quote_depth * QUOTE_INDENT + self.indent_depth * LIST_INDENT
    }

    pub(crate) fn font_size(&self) -> f32 {
        match self.level {
            SurfaceLineLevel::Heading(1) => HEADING_1_FONT_SIZE,
            SurfaceLineLevel::Heading(2) => HEADING_2_FONT_SIZE,
            SurfaceLineLevel::Heading(_) => HEADING_DEFAULT_FONT_SIZE,
            SurfaceLineLevel::Body => BODY_FONT_SIZE,
            SurfaceLineLevel::Code => CODE_FONT_SIZE,
        }
    }

    pub(crate) fn line_height(&self) -> u32 {
        match self.level {
            SurfaceLineLevel::Heading(1) => HEADING_1_LINE_HEIGHT,
            SurfaceLineLevel::Heading(2) => HEADING_2_LINE_HEIGHT,
            SurfaceLineLevel::Heading(_) => HEADING_DEFAULT_LINE_HEIGHT,
            SurfaceLineLevel::Body => BODY_LINE_HEIGHT,
            SurfaceLineLevel::Code => CODE_LINE_HEIGHT,
        }
    }

    pub(crate) fn text_y(&self, y: u32) -> u32 {
        y + self.top_margin()
    }

    pub(crate) fn quote_depth(&self) -> u32 {
        self.quote_depth
    }

    pub(crate) fn is_code(&self) -> bool {
        matches!(self.level, SurfaceLineLevel::Code)
    }

    pub(crate) fn is_heading(&self) -> bool {
        matches!(self.level, SurfaceLineLevel::Heading(_))
    }

    pub(crate) fn is_centered(&self) -> bool {
        matches!(self.alignment, SurfaceLineAlignment::Center)
    }

    #[cfg(test)]
    pub(crate) fn debug_style_tags(&self) -> Vec<String> {
        super::export_surface_line_debug::debug_style_tags(self)
    }

    pub(crate) fn indent_depth(&self) -> u32 {
        self.indent_depth
    }
}

#[cfg(test)]
#[path = "export_surface_line_impl_tests.rs"]
mod tests;