1use std::fmt::{self, Debug, Formatter};
4use std::sync::Arc;
5
6use typst_syntax::Span;
7use typst_utils::{LazyHash, Numeric};
8
9use crate::foundations::Label;
10use crate::introspection::{Location, Tag};
11use crate::layout::{Abs, Axes, FixedAlignment, Point, Size, Transform};
12use crate::model::Destination;
13use crate::text::TextItem;
14use crate::visualize::{Color, Curve, FixedStroke, Geometry, Image, Paint, Shape};
15
16#[derive(Default, Clone, Hash)]
18pub struct Frame {
19 size: Size,
21 baseline: Option<Abs>,
24 items: Arc<LazyHash<Vec<(Point, FrameItem)>>>,
26 kind: FrameKind,
30}
31
32impl Frame {
34 #[track_caller]
38 pub fn new(size: Size, kind: FrameKind) -> Self {
39 assert!(size.is_finite());
40 Self {
41 size,
42 baseline: None,
43 items: Arc::new(LazyHash::new(vec![])),
44 kind,
45 }
46 }
47
48 #[track_caller]
52 pub fn soft(size: Size) -> Self {
53 Self::new(size, FrameKind::Soft)
54 }
55
56 #[track_caller]
60 pub fn hard(size: Size) -> Self {
61 Self::new(size, FrameKind::Hard)
62 }
63
64 pub fn set_kind(&mut self, kind: FrameKind) {
66 self.kind = kind;
67 }
68
69 pub fn with_kind(mut self, kind: FrameKind) -> Self {
71 self.kind = kind;
72 self
73 }
74
75 pub fn kind(&self) -> FrameKind {
77 self.kind
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.items.is_empty()
83 }
84
85 pub fn size(&self) -> Size {
87 self.size
88 }
89
90 pub fn size_mut(&mut self) -> &mut Size {
92 &mut self.size
93 }
94
95 pub fn set_size(&mut self, size: Size) {
97 self.size = size;
98 }
99
100 pub fn width(&self) -> Abs {
102 self.size.x
103 }
104
105 pub fn height(&self) -> Abs {
107 self.size.y
108 }
109
110 pub fn baseline(&self) -> Abs {
112 self.baseline.unwrap_or(self.size.y)
113 }
114
115 pub fn has_baseline(&self) -> bool {
117 self.baseline.is_some()
118 }
119
120 pub fn set_baseline(&mut self, baseline: Abs) {
122 self.baseline = Some(baseline);
123 }
124
125 pub fn clear_baseline(&mut self) {
129 self.baseline = None;
130 }
131
132 pub fn ascent(&self) -> Abs {
137 self.baseline()
138 }
139
140 pub fn descent(&self) -> Abs {
142 self.size.y - self.baseline()
143 }
144
145 pub fn items(&self) -> std::slice::Iter<'_, (Point, FrameItem)> {
148 self.items.iter()
149 }
150}
151
152impl Frame {
154 pub fn layer(&self) -> usize {
157 self.items.len()
158 }
159
160 pub fn push(&mut self, pos: Point, item: FrameItem) {
162 Arc::make_mut(&mut self.items).push((pos, item));
163 }
164
165 pub fn push_multiple<I>(&mut self, items: I)
170 where
171 I: IntoIterator<Item = (Point, FrameItem)>,
172 {
173 Arc::make_mut(&mut self.items).extend(items);
174 }
175
176 pub fn push_frame(&mut self, pos: Point, frame: Frame) {
181 if self.should_inline(&frame) {
182 self.inline(self.layer(), pos, frame);
183 } else {
184 self.push(pos, FrameItem::Group(GroupItem::new(frame)));
185 }
186 }
187
188 #[track_caller]
192 pub fn insert(&mut self, layer: usize, pos: Point, item: FrameItem) {
193 Arc::make_mut(&mut self.items).insert(layer, (pos, item));
194 }
195
196 pub fn prepend(&mut self, pos: Point, item: FrameItem) {
198 self.insert(0, pos, item);
199 }
200
201 pub fn prepend_multiple<I>(&mut self, items: I)
206 where
207 I: IntoIterator<Item = (Point, FrameItem)>,
208 {
209 Arc::make_mut(&mut self.items).splice(0..0, items);
210 }
211
212 pub fn prepend_frame(&mut self, pos: Point, frame: Frame) {
214 if self.should_inline(&frame) {
215 self.inline(0, pos, frame);
216 } else {
217 self.prepend(pos, FrameItem::Group(GroupItem::new(frame)));
218 }
219 }
220
221 pub fn retain(&mut self, mut filter: impl FnMut(&mut FrameItem) -> bool) {
223 Arc::make_mut(&mut self.items).retain_mut(|(_, item)| filter(item));
224 }
225
226 fn should_inline(&self, frame: &Frame) -> bool {
228 frame.kind().is_soft() && (self.items.is_empty() || frame.items.len() <= 5)
230 }
231
232 fn inline(&mut self, layer: usize, pos: Point, frame: Frame) {
234 if frame.items.is_empty() {
236 return;
237 }
238
239 if pos.is_zero() && self.items.is_empty() {
241 self.items = frame.items;
242 return;
243 }
244
245 let range = layer..layer;
248 if pos.is_zero() {
249 let sink = Arc::make_mut(&mut self.items);
250 match Arc::try_unwrap(frame.items) {
251 Ok(items) => {
252 sink.splice(range, items.into_inner());
253 }
254 Err(arc) => {
255 sink.splice(range, arc.iter().cloned());
256 }
257 }
258 return;
259 }
260
261 let sink = Arc::make_mut(&mut self.items);
264 match Arc::try_unwrap(frame.items) {
265 Ok(items) => {
266 sink.splice(
267 range,
268 items.into_inner().into_iter().map(|(p, e)| (p + pos, e)),
269 );
270 }
271 Err(arc) => {
272 sink.splice(range, arc.iter().cloned().map(|(p, e)| (p + pos, e)));
273 }
274 }
275 }
276}
277
278impl Frame {
280 pub fn clear(&mut self) {
282 if Arc::strong_count(&self.items) == 1 {
283 Arc::make_mut(&mut self.items).clear();
284 } else {
285 self.items = Arc::new(LazyHash::new(vec![]));
286 }
287 }
288
289 pub fn resize(&mut self, target: Size, align: Axes<FixedAlignment>) -> Point {
293 if self.size == target {
294 return Point::zero();
295 }
296 let offset =
297 align.zip_map(target - self.size, FixedAlignment::position).to_point();
298 self.size = target;
299 self.translate(offset);
300 offset
301 }
302
303 pub fn translate(&mut self, offset: Point) {
305 if !offset.is_zero() {
306 if let Some(baseline) = &mut self.baseline {
307 *baseline += offset.y;
308 }
309 for (point, _) in Arc::make_mut(&mut self.items).iter_mut() {
310 *point += offset;
311 }
312 }
313 }
314
315 pub fn translate_visual(&mut self, offset: Point) {
317 if !offset.is_zero() {
318 for (point, _) in Arc::make_mut(&mut self.items).iter_mut() {
319 *point += offset;
320 }
321 }
322 }
323
324 pub fn hide(&mut self) {
326 self.retain(|item| match item {
327 FrameItem::Group(group) => {
328 group.frame.hide();
329 !group.frame.is_empty()
330 }
331 FrameItem::Tag(_) => true,
332 _ => false,
333 });
334 }
335
336 pub fn fill(&mut self, fill: impl Into<Paint>) {
338 self.prepend(
339 Point::zero(),
340 FrameItem::Shape(Geometry::Rect(self.size()).filled(fill), Span::detached()),
341 );
342 }
343
344 pub fn transform(&mut self, transform: Transform) {
346 if !self.is_empty() {
347 self.group(|g| g.transform = transform);
348 }
349 }
350
351 pub fn clip(&mut self, clip_curve: Curve) {
357 if !self.is_empty() {
358 self.group(|g| g.clip = Some(clip_curve));
359 }
360 }
361
362 pub fn label(&mut self, label: Label) {
364 self.group(|g| g.label = Some(label));
365 }
366
367 pub fn set_parent(&mut self, parent: FrameParent) {
370 if !self.is_empty() {
371 self.group(|g| g.parent = Some(parent));
372 }
373 }
374
375 fn group<F>(&mut self, f: F)
377 where
378 F: FnOnce(&mut GroupItem),
379 {
380 let mut wrapper = Frame::soft(self.size);
381 wrapper.baseline = self.baseline;
382 let mut group = GroupItem::new(std::mem::take(self));
383 f(&mut group);
384 wrapper.push(Point::zero(), FrameItem::Group(group));
385 *self = wrapper;
386 }
387}
388
389impl Frame {
391 pub fn mark_box(mut self) -> Self {
393 self.mark_box_in_place();
394 self
395 }
396
397 pub fn mark_box_in_place(&mut self) {
399 self.insert(
400 0,
401 Point::zero(),
402 FrameItem::Shape(
403 Geometry::Rect(self.size).filled(Color::TEAL.with_alpha(0.5)),
404 Span::detached(),
405 ),
406 );
407 self.insert(
408 1,
409 Point::with_y(self.baseline()),
410 FrameItem::Shape(
411 Geometry::Line(Point::with_x(self.size.x))
412 .stroked(FixedStroke::from_pair(Color::RED, Abs::pt(1.0))),
413 Span::detached(),
414 ),
415 );
416 }
417
418 pub fn mark_point(&mut self, pos: Point) {
420 let radius = Abs::pt(2.0);
421 self.push(
422 pos - Point::splat(radius),
423 FrameItem::Shape(
424 Geometry::Curve(Curve::ellipse(Size::splat(2.0 * radius)))
425 .filled(Color::GREEN),
426 Span::detached(),
427 ),
428 );
429 }
430
431 pub fn mark_line(&mut self, y: Abs) {
433 self.push(
434 Point::with_y(y),
435 FrameItem::Shape(
436 Geometry::Line(Point::with_x(self.size.x))
437 .stroked(FixedStroke::from_pair(Color::GREEN, Abs::pt(1.0))),
438 Span::detached(),
439 ),
440 );
441 }
442}
443
444impl Debug for Frame {
445 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
446 f.write_str("Frame ")?;
447 f.debug_list()
448 .entries(self.items.iter().map(|(_, item)| item))
449 .finish()
450 }
451}
452
453#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
459pub enum FrameKind {
460 #[default]
465 Soft,
466 Hard,
470}
471
472impl FrameKind {
473 pub fn is_soft(self) -> bool {
475 matches!(self, Self::Soft)
476 }
477
478 pub fn is_hard(self) -> bool {
480 matches!(self, Self::Hard)
481 }
482}
483
484#[derive(Clone, Hash)]
486pub enum FrameItem {
487 Group(GroupItem),
489 Text(TextItem),
491 Shape(Shape, Span),
493 Image(Image, Size, Span),
495 Link(Destination, Size),
497 Tag(Tag),
499}
500
501impl Debug for FrameItem {
502 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
503 match self {
504 Self::Group(group) => group.fmt(f),
505 Self::Text(text) => write!(f, "{text:?}"),
506 Self::Shape(shape, _) => write!(f, "{shape:?}"),
507 Self::Image(image, _, _) => write!(f, "{image:?}"),
508 Self::Link(dest, _) => write!(f, "Link({dest:?})"),
509 Self::Tag(tag) => write!(f, "{tag:?}"),
510 }
511 }
512}
513
514#[derive(Clone, Hash)]
516pub struct GroupItem {
517 pub frame: Frame,
519 pub transform: Transform,
521 pub clip: Option<Curve>,
523 pub label: Option<Label>,
525 pub parent: Option<FrameParent>,
530}
531
532impl GroupItem {
533 pub fn new(frame: Frame) -> Self {
535 Self {
536 frame,
537 transform: Transform::identity(),
538 clip: None,
539 label: None,
540 parent: None,
541 }
542 }
543}
544
545impl Debug for GroupItem {
546 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
547 f.write_str("Group ")?;
548 self.frame.fmt(f)
549 }
550}
551
552#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
555pub struct FrameParent {
556 pub location: Location,
558 pub inherit: Inherit,
560}
561
562impl FrameParent {
563 pub const fn new(location: Location, inherit: Inherit) -> Self {
565 Self { location, inherit }
566 }
567}
568
569#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
593pub enum Inherit {
594 Yes,
595 No,
596}