sdocx 1.1.0

Reverse-engineered tooling and SDK for converting Samsung Notes (.sdocx) files
Documentation
use crate::{BoundingBox, ObjectSpanLayoutConstraint};

use super::{BorderPath, CellPosition, ObjectDiagnosticKind};

#[derive(Clone, Copy)]
pub(in crate::render) enum TableArtworkGeometry {
    Unprepared,
    Saved {
        source_origin: [f32; 2],
        paint_offset: [f32; 2],
    },
    Cached,
}

impl TableArtworkGeometry {
    pub fn compatibility_export(
        constraint: ObjectSpanLayoutConstraint,
        source_bounds: BoundingBox,
        offset: [f64; 2],
    ) -> Result<Self, ObjectDiagnosticKind> {
        match constraint {
            ObjectSpanLayoutConstraint::Normal => {
                let source_origin = [source_bounds.x_min as f32, source_bounds.y_min as f32];
                let paint_offset = offset.map(|coordinate| coordinate as f32);
                if !source_origin
                    .into_iter()
                    .chain(paint_offset)
                    .all(f32::is_finite)
                {
                    return Err(ObjectDiagnosticKind::InvalidBounds);
                }
                Ok(Self::Saved {
                    source_origin,
                    paint_offset,
                })
            }
            ObjectSpanLayoutConstraint::OverPages
            | ObjectSpanLayoutConstraint::OverPagesOverlapPadding => Ok(Self::Cached),
            _ => Err(ObjectDiagnosticKind::UnsupportedContent),
        }
    }

    #[cfg(test)]
    fn native_drawing(
        constraint: ObjectSpanLayoutConstraint,
        source_drawn_bounds: BoundingBox,
        scroll_x: f32,
    ) -> Result<Self, ObjectDiagnosticKind> {
        Self::compatibility_export(constraint, source_drawn_bounds, [f64::from(scroll_x), 0.0])
    }

    pub fn cached(self) -> bool {
        matches!(self, Self::Cached)
    }

    pub fn background(self, saved: BoundingBox, cached: BoundingBox) -> BoundingBox {
        match self {
            Self::Unprepared => saved,
            Self::Saved {
                source_origin,
                paint_offset,
            } => {
                let [left, top, right, bottom] = native_rect(saved);
                rect([
                    (left - source_origin[0]) + paint_offset[0],
                    (top - source_origin[1]) + paint_offset[1],
                    (right - source_origin[0]) + paint_offset[0],
                    (bottom - source_origin[1]) + paint_offset[1],
                ])
            }
            Self::Cached => cached,
        }
    }

    pub fn border(self, path: BorderPath, frame: BoundingBox) -> BorderPath {
        match self {
            Self::Unprepared => path,
            Self::Saved {
                source_origin,
                paint_offset,
            } => {
                let [x1, y1, x2, y2] = path.endpoints;
                let offset = [
                    source_origin[0] - paint_offset[0],
                    source_origin[1] - paint_offset[1],
                ];
                BorderPath {
                    endpoints: [
                        x1 - offset[0],
                        y1 - offset[1],
                        x2 - offset[0],
                        y2 - offset[1],
                    ],
                    ..path
                }
            }
            Self::Cached => path.on_frame(frame),
        }
    }
}

#[cfg(test)]
fn offset_frame(frame: BoundingBox, origin: [f64; 2]) -> Result<BoundingBox, ObjectDiagnosticKind> {
    let [left, top, right, bottom] = native_rect(frame);
    let [x, y] = origin.map(|coordinate| coordinate as f32);
    let coordinates = [left + x, top + y, right + x, bottom + y];
    if !coordinates.into_iter().all(f32::is_finite) {
        return Err(ObjectDiagnosticKind::InvalidBounds);
    }
    Ok(rect(coordinates))
}

pub(in crate::render) struct CellBackgroundGeometry {
    pub bounds: BoundingBox,
    pub radii: Option<[f32; 2]>,
    pub square_corners: [Option<BoundingBox>; 4],
}

impl CellBackgroundGeometry {
    pub fn new(
        bounds: BoundingBox,
        position: CellPosition,
        span: [u32; 2],
        shape: [usize; 2],
        radii: [f32; 2],
    ) -> Self {
        let [rx, ry] = radii;
        let first_row = position.row == 0;
        let first_column = position.column == 0;
        let last_row = position.row.checked_add(span[0] as usize) == Some(shape[0]);
        let last_column = position.column.checked_add(span[1] as usize) == Some(shape[1]);
        let corners = [
            first_row && first_column,
            first_row && last_column,
            last_row && last_column,
            last_row && first_column,
        ];
        if rx <= 0.0 || ry <= 0.0 || !corners.into_iter().any(|corner| corner) {
            return Self {
                bounds,
                radii: None,
                square_corners: [None; 4],
            };
        }
        let [left, top, right, bottom] = native_rect(bounds);
        let patches = [
            [left, top, left + rx, top + ry],
            [right - rx, top, right, top + ry],
            [right - rx, bottom - ry, right, bottom],
            [left, bottom - ry, left + rx, bottom],
        ];
        let square_corners =
            std::array::from_fn(|index| (!corners[index]).then(|| rect(patches[index])));
        Self {
            bounds,
            radii: Some(radii),
            square_corners,
        }
    }
}

fn native_rect(rect: BoundingBox) -> [f32; 4] {
    [rect.x_min, rect.y_min, rect.x_max, rect.y_max].map(|coordinate| coordinate as f32)
}

pub(in crate::render) fn finite_artwork_rect(rect: BoundingBox) -> bool {
    native_rect(rect).into_iter().all(f32::is_finite)
}

fn rect([x_min, y_min, x_max, y_max]: [f32; 4]) -> BoundingBox {
    BoundingBox {
        x_min: f64::from(x_min),
        y_min: f64::from(y_min),
        x_max: f64::from(x_max),
        y_max: f64::from(y_max),
    }
}

#[cfg(test)]
mod native_tests;

#[cfg(test)]
mod clone_origin_tests;