katana-document-viewer 0.1.4

KatanA document viewer artifact, render evaluation, and export foundation.
Documentation
use super::SurfaceBlock;
use crate::export_surface_helpers::{PAGE_PADDING, SURFACE_PAGE_HEIGHT};

pub(super) struct SurfacePagePlan {
    pub(super) pages: Vec<Vec<usize>>,
}

impl SurfacePagePlan {
    pub(super) fn from_blocks(blocks: &[SurfaceBlock]) -> Self {
        let mut pages = Vec::new();
        let mut current = Vec::new();
        let mut y = PAGE_PADDING;
        let mut index = 0;
        while let Some(block) = blocks.get(index) {
            if Self::should_move_to_next_page(blocks, index, y) {
                pages.push(current);
                current = Vec::new();
                y = PAGE_PADDING;
                continue;
            }
            if y > PAGE_PADDING && y + block.height() > Self::page_bottom() {
                pages.push(current);
                current = Vec::new();
                y = PAGE_PADDING;
                continue;
            }
            current.push(index);
            y += block.height();
            index += 1;
        }
        if !current.is_empty() {
            pages.push(current);
        }
        Self { pages }
    }

    fn should_move_to_next_page(blocks: &[SurfaceBlock], index: usize, y: u32) -> bool {
        if y == PAGE_PADDING {
            return false;
        }
        let Some(keep_height) = Self::heading_keep_with_next_height(blocks, index) else {
            return false;
        };
        keep_height <= Self::page_content_height() && y + keep_height > Self::page_bottom()
    }

    fn heading_keep_with_next_height(blocks: &[SurfaceBlock], index: usize) -> Option<u32> {
        let block = blocks.get(index)?;
        if !block.is_heading() {
            return None;
        }
        let mut height = block.height();
        let mut cursor = index + 1;
        let mut has_following_block = false;
        while let Some(next) = blocks.get(cursor) {
            height += next.height();
            has_following_block = true;
            cursor += 1;
            if !next.is_heading() {
                break;
            }
        }
        has_following_block.then_some(height)
    }

    fn page_bottom() -> u32 {
        SURFACE_PAGE_HEIGHT - PAGE_PADDING
    }

    fn page_content_height() -> u32 {
        SURFACE_PAGE_HEIGHT - PAGE_PADDING * 2
    }
}