//! Finished documents.
use std::fmt::{self, Debug, Formatter, Write};
use std::num::NonZeroUsize;
use std::str::FromStr;
use std::sync::Arc;
use ecow::EcoString;
use crate::eval::{cast_from_value, cast_to_value, dict, Dict, Value};
use crate::font::Font;
use crate::geom::{
self, rounded_rect, Abs, Align, Axes, Color, Corners, Dir, Em, Geometry, Length,
Numeric, Paint, Point, Rel, RgbaColor, Shape, Sides, Size, Stroke, Transform,
};
use crate::image::Image;
use crate::model::{node, Content, Fold, StableId, StyleChain};
/// A finished document with metadata and page frames.
#[derive(Debug, Default, Clone, Hash)]
pub struct Document {
/// The page frames.
pub pages: Vec,
/// The document's title.
pub title: Option,
/// The document's author.
pub author: Vec,
}
/// A finished layout with elements at fixed positions.
#[derive(Default, Clone, Hash)]
pub struct Frame {
/// The size of the frame.
size: Size,
/// The baseline of the frame measured from the top. If this is `None`, the
/// frame's implicit baseline is at the bottom.
baseline: Option,
/// The elements composing this layout.
elements: Arc>,
}
/// Constructor, accessors and setters.
impl Frame {
/// Create a new, empty frame.
///
/// Panics the size is not finite.
#[track_caller]
pub fn new(size: Size) -> Self {
assert!(size.is_finite());
Self { size, baseline: None, elements: Arc::new(vec![]) }
}
/// Whether the frame contains no elements.
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
/// The size of the frame.
pub fn size(&self) -> Size {
self.size
}
/// The size of the frame, mutably.
pub fn size_mut(&mut self) -> &mut Size {
&mut self.size
}
/// Set the size of the frame.
pub fn set_size(&mut self, size: Size) {
self.size = size;
}
/// The width of the frame.
pub fn width(&self) -> Abs {
self.size.x
}
/// The height of the frame.
pub fn height(&self) -> Abs {
self.size.y
}
/// The vertical position of the frame's baseline.
pub fn baseline(&self) -> Abs {
self.baseline.unwrap_or(self.size.y)
}
/// Whether the frame has a non-default baseline.
pub fn has_baseline(&self) -> bool {
self.baseline.is_some()
}
/// Set the frame's baseline from the top.
pub fn set_baseline(&mut self, baseline: Abs) {
self.baseline = Some(baseline);
}
/// The distance from the baseline to the top of the frame.
///
/// This is the same as `baseline()`, but more in line with the terminology
/// used in math layout.
pub fn ascent(&self) -> Abs {
self.baseline()
}
/// The distance from the baseline to the bottom of the frame.
pub fn descent(&self) -> Abs {
self.size.y - self.baseline()
}
/// An iterator over the elements inside this frame alongside their
/// positions relative to the top-left of the frame.
pub fn elements(&self) -> std::slice::Iter<'_, (Point, Element)> {
self.elements.iter()
}
/// Recover the text inside of the frame and its children.
pub fn text(&self) -> EcoString {
let mut text = EcoString::new();
for (_, element) in self.elements() {
match element {
Element::Text(content) => {
for glyph in &content.glyphs {
text.push(glyph.c);
}
}
Element::Group(group) => text.push_str(&group.frame.text()),
_ => {}
}
}
text
}
}
/// Insert elements and subframes.
impl Frame {
/// The layer the next item will be added on. This corresponds to the number
/// of elements in the frame.
pub fn layer(&self) -> usize {
self.elements.len()
}
/// Add an element at a position in the foreground.
pub fn push(&mut self, pos: Point, element: Element) {
Arc::make_mut(&mut self.elements).push((pos, element));
}
/// Add a frame at a position in the foreground.
///
/// Automatically decides whether to inline the frame or to include it as a
/// group based on the number of elements in it.
pub fn push_frame(&mut self, pos: Point, frame: Frame) {
if self.should_inline(&frame) {
self.inline(self.layer(), pos, frame);
} else {
self.push(pos, Element::Group(Group::new(frame)));
}
}
/// Insert an element at the given layer in the frame.
///
/// This panics if the layer is greater than the number of layers present.
#[track_caller]
pub fn insert(&mut self, layer: usize, pos: Point, element: Element) {
Arc::make_mut(&mut self.elements).insert(layer, (pos, element));
}
/// Add an element at a position in the background.
pub fn prepend(&mut self, pos: Point, element: Element) {
Arc::make_mut(&mut self.elements).insert(0, (pos, element));
}
/// Add multiple elements at a position in the background.
///
/// The first element in the iterator will be the one that is most in the
/// background.
pub fn prepend_multiple(&mut self, elements: I)
where
I: IntoIterator,
{
Arc::make_mut(&mut self.elements).splice(0..0, elements);
}
/// Add a frame at a position in the background.
pub fn prepend_frame(&mut self, pos: Point, frame: Frame) {
if self.should_inline(&frame) {
self.inline(0, pos, frame);
} else {
self.prepend(pos, Element::Group(Group::new(frame)));
}
}
/// Whether the given frame should be inlined.
fn should_inline(&self, frame: &Frame) -> bool {
self.elements.is_empty() || frame.elements.len() <= 5
}
/// Inline a frame at the given layer.
fn inline(&mut self, layer: usize, pos: Point, frame: Frame) {
// Try to just reuse the elements.
if pos.is_zero() && self.elements.is_empty() {
self.elements = frame.elements;
return;
}
// Try to transfer the elements without adjusting the position.
// Also try to reuse the elements if the Arc isn't shared.
let range = layer..layer;
if pos.is_zero() {
let sink = Arc::make_mut(&mut self.elements);
match Arc::try_unwrap(frame.elements) {
Ok(elements) => {
sink.splice(range, elements);
}
Err(arc) => {
sink.splice(range, arc.iter().cloned());
}
}
return;
}
// We must adjust the element positions.
// But still try to reuse the elements if the Arc isn't shared.
let sink = Arc::make_mut(&mut self.elements);
match Arc::try_unwrap(frame.elements) {
Ok(elements) => {
sink.splice(range, elements.into_iter().map(|(p, e)| (p + pos, e)));
}
Err(arc) => {
sink.splice(range, arc.iter().cloned().map(|(p, e)| (p + pos, e)));
}
}
}
}
/// Modify the frame.
impl Frame {
/// Remove all elements from the frame.
pub fn clear(&mut self) {
if Arc::strong_count(&self.elements) == 1 {
Arc::make_mut(&mut self.elements).clear();
} else {
self.elements = Arc::new(vec![]);
}
}
/// Resize the frame to a new size, distributing new space according to the
/// given alignments.
pub fn resize(&mut self, target: Size, aligns: Axes) {
if self.size != target {
let offset = Point::new(
aligns.x.position(target.x - self.size.x),
aligns.y.position(target.y - self.size.y),
);
self.size = target;
self.translate(offset);
}
}
/// Move the baseline and contents of the frame by an offset.
pub fn translate(&mut self, offset: Point) {
if !offset.is_zero() {
if let Some(baseline) = &mut self.baseline {
*baseline += offset.y;
}
for (point, _) in Arc::make_mut(&mut self.elements) {
*point += offset;
}
}
}
/// Attach the metadata from this style chain to the frame.
pub fn meta(&mut self, styles: StyleChain) {
if self.is_empty() {
return;
}
for meta in styles.get(MetaNode::DATA) {
if matches!(meta, Meta::Hidden) {
self.clear();
break;
}
self.push(Point::zero(), Element::Meta(meta, self.size));
}
}
/// Add a background fill.
pub fn fill(&mut self, fill: Paint) {
self.prepend(
Point::zero(),
Element::Shape(Geometry::Rect(self.size()).filled(fill)),
);
}
/// Add a fill and stroke with optional radius and outset to the frame.
pub fn fill_and_stroke(
&mut self,
fill: Option,
stroke: Sides