use std::num::NonZeroUsize;
use ecow::EcoVec;
use typst_utils::NonZeroExt;
use crate::foundations::{Dict, Value, cast, dict};
use crate::layout::{Length, Point};
#[derive(Clone, Debug, Hash)]
pub enum DocumentPosition {
Paged(PagedPosition),
Html(HtmlPosition),
}
impl DocumentPosition {
pub fn as_paged(self) -> Option<PagedPosition> {
match self {
DocumentPosition::Paged(position) => Some(position),
_ => None,
}
}
pub fn as_paged_or_default(self) -> PagedPosition {
self.as_paged().unwrap_or(PagedPosition::ORIGIN)
}
pub fn as_html(self) -> Option<HtmlPosition> {
match self {
DocumentPosition::Html(position) => Some(position),
_ => None,
}
}
}
impl From<PagedPosition> for DocumentPosition {
fn from(value: PagedPosition) -> Self {
Self::Paged(value)
}
}
impl From<HtmlPosition> for DocumentPosition {
fn from(value: HtmlPosition) -> Self {
Self::Html(value)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct PagedPosition {
pub page: NonZeroUsize,
pub point: Point,
}
impl PagedPosition {
pub const ORIGIN: PagedPosition =
PagedPosition { page: NonZeroUsize::ONE, point: Point::zero() };
}
cast! {
PagedPosition,
self => Value::Dict(self.into()),
mut dict: Dict => {
let page = dict.take("page")?.cast()?;
let x: Length = dict.take("x")?.cast()?;
let y: Length = dict.take("y")?.cast()?;
dict.finish(&["page", "x", "y"])?;
Self { page, point: Point::new(x.abs, y.abs) }
},
}
impl From<PagedPosition> for Dict {
fn from(pos: PagedPosition) -> Self {
dict! {
"page" => pos.page,
"x" => pos.point.x,
"y" => pos.point.y,
}
}
}
#[derive(Clone, Debug, Hash)]
pub struct HtmlPosition {
element: EcoVec<usize>,
inner: Option<InnerHtmlPosition>,
}
impl HtmlPosition {
pub fn new(element: EcoVec<usize>) -> Self {
Self { element, inner: None }
}
pub fn at_char(self, offset: usize) -> Self {
Self {
element: self.element,
inner: Some(InnerHtmlPosition::Character(offset)),
}
}
pub fn in_frame(self, point: Point) -> Self {
Self {
element: self.element,
inner: Some(InnerHtmlPosition::Frame(point)),
}
}
pub fn details(&self) -> Option<&InnerHtmlPosition> {
self.inner.as_ref()
}
pub fn element(&self) -> impl Iterator<Item = usize> {
self.element.iter().copied()
}
}
#[derive(Clone, Debug, Hash)]
pub enum InnerHtmlPosition {
Frame(Point),
Character(usize),
}