1use std::fmt::{self, Debug, Formatter};
4use std::num::NonZeroUsize;
5use std::sync::Arc;
6
7use typst_syntax::Span;
8use typst_utils::{LazyHash, Numeric};
9
10use crate::foundations::{cast, dict, Dict, Label, Value};
11use crate::introspection::{Location, Tag};
12use crate::layout::{Abs, Axes, FixedAlignment, Length, Point, Size, Transform};
13use crate::model::Destination;
14use crate::text::TextItem;
15use crate::visualize::{Color, Curve, FixedStroke, Geometry, Image, Paint, Shape};
16
17#[derive(Default, Clone, Hash)]
19pub struct Frame {
20 size: Size,
22 baseline: Option<Abs>,
25 items: Arc<LazyHash<Vec<(Point, FrameItem)>>>,
27 kind: FrameKind,
31}
32
33impl Frame {
35 #[track_caller]
39 pub fn new(size: Size, kind: FrameKind) -> Self {
40 assert!(size.is_finite());
41 Self {
42 size,
43 baseline: None,
44 items: Arc::new(LazyHash::new(vec![])),
45 kind,
46 }
47 }
48
49 #[track_caller]
53 pub fn soft(size: Size) -> Self {
54 Self::new(size, FrameKind::Soft)
55 }
56
57 #[track_caller]
61 pub fn hard(size: Size) -> Self {
62 Self::new(size, FrameKind::Hard)
63 }
64
65 pub fn set_kind(&mut self, kind: FrameKind) {
67 self.kind = kind;
68 }
69
70 pub fn with_kind(mut self, kind: FrameKind) -> Self {
72 self.kind = kind;
73 self
74 }
75
76 pub fn kind(&self) -> FrameKind {
78 self.kind
79 }
80
81 pub fn is_empty(&self) -> bool {
83 self.items.is_empty()
84 }
85
86 pub fn size(&self) -> Size {
88 self.size
89 }
90
91 pub fn size_mut(&mut self) -> &mut Size {
93 &mut self.size
94 }
95
96 pub fn set_size(&mut self, size: Size) {
98 self.size = size;
99 }
100
101 pub fn width(&self) -> Abs {
103 self.size.x
104 }
105
106 pub fn height(&self) -> Abs {
108 self.size.y
109 }
110
111 pub fn baseline(&self) -> Abs {
113 self.baseline.unwrap_or(self.size.y)
114 }
115
116 pub fn has_baseline(&self) -> bool {
118 self.baseline.is_some()
119 }
120
121 pub fn set_baseline(&mut self, baseline: Abs) {
123 self.baseline = Some(baseline);
124 }
125
126 pub fn ascent(&self) -> Abs {
131 self.baseline()
132 }
133
134 pub fn descent(&self) -> Abs {
136 self.size.y - self.baseline()
137 }
138
139 pub fn items(&self) -> std::slice::Iter<'_, (Point, FrameItem)> {
142 self.items.iter()
143 }
144}
145
146impl Frame {
148 pub fn layer(&self) -> usize {
151 self.items.len()
152 }
153
154 pub fn push(&mut self, pos: Point, item: FrameItem) {
156 Arc::make_mut(&mut self.items).push((pos, item));
157 }
158
159 pub fn push_multiple<I>(&mut self, items: I)
164 where
165 I: IntoIterator<Item = (Point, FrameItem)>,
166 {
167 Arc::make_mut(&mut self.items).extend(items);
168 }
169
170 pub fn push_frame(&mut self, pos: Point, frame: Frame) {
175 if self.should_inline(&frame) {
176 self.inline(self.layer(), pos, frame);
177 } else {
178 self.push(pos, FrameItem::Group(GroupItem::new(frame)));
179 }
180 }
181
182 #[track_caller]
186 pub fn insert(&mut self, layer: usize, pos: Point, item: FrameItem) {
187 Arc::make_mut(&mut self.items).insert(layer, (pos, item));
188 }
189
190 pub fn prepend(&mut self, pos: Point, item: FrameItem) {
192 self.insert(0, pos, item);
193 }
194
195 pub fn prepend_multiple<I>(&mut self, items: I)
200 where
201 I: IntoIterator<Item = (Point, FrameItem)>,
202 {
203 Arc::make_mut(&mut self.items).splice(0..0, items);
204 }
205
206 pub fn prepend_frame(&mut self, pos: Point, frame: Frame) {
208 if self.should_inline(&frame) {
209 self.inline(0, pos, frame);
210 } else {
211 self.prepend(pos, FrameItem::Group(GroupItem::new(frame)));
212 }
213 }
214
215 fn should_inline(&self, frame: &Frame) -> bool {
217 frame.kind().is_soft() && (self.items.is_empty() || frame.items.len() <= 5)
219 }
220
221 fn inline(&mut self, layer: usize, pos: Point, frame: Frame) {
223 if frame.items.is_empty() {
225 return;
226 }
227
228 if pos.is_zero() && self.items.is_empty() {
230 self.items = frame.items;
231 return;
232 }
233
234 let range = layer..layer;
237 if pos.is_zero() {
238 let sink = Arc::make_mut(&mut self.items);
239 match Arc::try_unwrap(frame.items) {
240 Ok(items) => {
241 sink.splice(range, items.into_inner());
242 }
243 Err(arc) => {
244 sink.splice(range, arc.iter().cloned());
245 }
246 }
247 return;
248 }
249
250 let sink = Arc::make_mut(&mut self.items);
253 match Arc::try_unwrap(frame.items) {
254 Ok(items) => {
255 sink.splice(
256 range,
257 items.into_inner().into_iter().map(|(p, e)| (p + pos, e)),
258 );
259 }
260 Err(arc) => {
261 sink.splice(range, arc.iter().cloned().map(|(p, e)| (p + pos, e)));
262 }
263 }
264 }
265}
266
267impl Frame {
269 pub fn clear(&mut self) {
271 if Arc::strong_count(&self.items) == 1 {
272 Arc::make_mut(&mut self.items).clear();
273 } else {
274 self.items = Arc::new(LazyHash::new(vec![]));
275 }
276 }
277
278 pub fn resize(&mut self, target: Size, align: Axes<FixedAlignment>) -> Point {
282 if self.size == target {
283 return Point::zero();
284 }
285 let offset =
286 align.zip_map(target - self.size, FixedAlignment::position).to_point();
287 self.size = target;
288 self.translate(offset);
289 offset
290 }
291
292 pub fn translate(&mut self, offset: Point) {
294 if !offset.is_zero() {
295 if let Some(baseline) = &mut self.baseline {
296 *baseline += offset.y;
297 }
298 for (point, _) in Arc::make_mut(&mut self.items).iter_mut() {
299 *point += offset;
300 }
301 }
302 }
303
304 pub fn hide(&mut self) {
306 Arc::make_mut(&mut self.items).retain_mut(|(_, item)| match item {
307 FrameItem::Group(group) => {
308 group.frame.hide();
309 !group.frame.is_empty()
310 }
311 FrameItem::Tag(_) => true,
312 _ => false,
313 });
314 }
315
316 pub fn fill(&mut self, fill: impl Into<Paint>) {
318 self.prepend(
319 Point::zero(),
320 FrameItem::Shape(Geometry::Rect(self.size()).filled(fill), Span::detached()),
321 );
322 }
323
324 pub fn transform(&mut self, transform: Transform) {
326 if !self.is_empty() {
327 self.group(|g| g.transform = transform);
328 }
329 }
330
331 pub fn clip(&mut self, clip_curve: Curve) {
337 if !self.is_empty() {
338 self.group(|g| g.clip = Some(clip_curve));
339 }
340 }
341
342 pub fn label(&mut self, label: Label) {
344 self.group(|g| g.label = Some(label));
345 }
346
347 pub fn set_parent(&mut self, parent: Location) {
350 if !self.is_empty() {
351 self.group(|g| g.parent = Some(parent));
352 }
353 }
354
355 fn group<F>(&mut self, f: F)
357 where
358 F: FnOnce(&mut GroupItem),
359 {
360 let mut wrapper = Frame::soft(self.size);
361 wrapper.baseline = self.baseline;
362 let mut group = GroupItem::new(std::mem::take(self));
363 f(&mut group);
364 wrapper.push(Point::zero(), FrameItem::Group(group));
365 *self = wrapper;
366 }
367}
368
369impl Frame {
371 pub fn mark_box(mut self) -> Self {
373 self.mark_box_in_place();
374 self
375 }
376
377 pub fn mark_box_in_place(&mut self) {
379 self.insert(
380 0,
381 Point::zero(),
382 FrameItem::Shape(
383 Geometry::Rect(self.size).filled(Color::TEAL.with_alpha(0.5)),
384 Span::detached(),
385 ),
386 );
387 self.insert(
388 1,
389 Point::with_y(self.baseline()),
390 FrameItem::Shape(
391 Geometry::Line(Point::with_x(self.size.x))
392 .stroked(FixedStroke::from_pair(Color::RED, Abs::pt(1.0))),
393 Span::detached(),
394 ),
395 );
396 }
397
398 pub fn mark_point(&mut self, pos: Point) {
400 let radius = Abs::pt(2.0);
401 self.push(
402 pos - Point::splat(radius),
403 FrameItem::Shape(
404 Geometry::Curve(Curve::ellipse(Size::splat(2.0 * radius)))
405 .filled(Color::GREEN),
406 Span::detached(),
407 ),
408 );
409 }
410
411 pub fn mark_line(&mut self, y: Abs) {
413 self.push(
414 Point::with_y(y),
415 FrameItem::Shape(
416 Geometry::Line(Point::with_x(self.size.x))
417 .stroked(FixedStroke::from_pair(Color::GREEN, Abs::pt(1.0))),
418 Span::detached(),
419 ),
420 );
421 }
422}
423
424impl Debug for Frame {
425 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
426 f.write_str("Frame ")?;
427 f.debug_list()
428 .entries(self.items.iter().map(|(_, item)| item))
429 .finish()
430 }
431}
432
433#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)]
439pub enum FrameKind {
440 #[default]
445 Soft,
446 Hard,
450}
451
452impl FrameKind {
453 pub fn is_soft(self) -> bool {
455 matches!(self, Self::Soft)
456 }
457
458 pub fn is_hard(self) -> bool {
460 matches!(self, Self::Hard)
461 }
462}
463
464#[derive(Clone, Hash)]
466pub enum FrameItem {
467 Group(GroupItem),
469 Text(TextItem),
471 Shape(Shape, Span),
473 Image(Image, Size, Span),
475 Link(Destination, Size),
477 Tag(Tag),
479}
480
481impl Debug for FrameItem {
482 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
483 match self {
484 Self::Group(group) => group.fmt(f),
485 Self::Text(text) => write!(f, "{text:?}"),
486 Self::Shape(shape, _) => write!(f, "{shape:?}"),
487 Self::Image(image, _, _) => write!(f, "{image:?}"),
488 Self::Link(dest, _) => write!(f, "Link({dest:?})"),
489 Self::Tag(tag) => write!(f, "{tag:?}"),
490 }
491 }
492}
493
494#[derive(Clone, Hash)]
496pub struct GroupItem {
497 pub frame: Frame,
499 pub transform: Transform,
501 pub clip: Option<Curve>,
503 pub label: Option<Label>,
505 pub parent: Option<Location>,
508}
509
510impl GroupItem {
511 pub fn new(frame: Frame) -> Self {
513 Self {
514 frame,
515 transform: Transform::identity(),
516 clip: None,
517 label: None,
518 parent: None,
519 }
520 }
521}
522
523impl Debug for GroupItem {
524 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
525 f.write_str("Group ")?;
526 self.frame.fmt(f)
527 }
528}
529
530#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
532pub struct Position {
533 pub page: NonZeroUsize,
535 pub point: Point,
537}
538
539cast! {
540 Position,
541 self => Value::Dict(self.into()),
542 mut dict: Dict => {
543 let page = dict.take("page")?.cast()?;
544 let x: Length = dict.take("x")?.cast()?;
545 let y: Length = dict.take("y")?.cast()?;
546 dict.finish(&["page", "x", "y"])?;
547 Self { page, point: Point::new(x.abs, y.abs) }
548 },
549}
550
551impl From<Position> for Dict {
552 fn from(pos: Position) -> Self {
553 dict! {
554 "page" => pos.page,
555 "x" => pos.point.x,
556 "y" => pos.point.y,
557 }
558 }
559}