summaryrefslogtreecommitdiff
path: root/crates/typst-pdf/src/link.rs
blob: 64cb8f0a2710e7d482fc2b9b79b864ec2f209b9b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use krilla::action::{Action, LinkAction};
use krilla::annotation::{LinkAnnotation, Target};
use krilla::destination::XyzDestination;
use krilla::geom::Rect;
use typst_library::layout::{Abs, Point, Size};
use typst_library::model::Destination;

use crate::convert::{FrameContext, GlobalContext};
use crate::util::{AbsExt, PointExt};

pub(crate) fn handle_link(
    fc: &mut FrameContext,
    gc: &mut GlobalContext,
    dest: &Destination,
    size: Size,
) {
    let mut min_x = Abs::inf();
    let mut min_y = Abs::inf();
    let mut max_x = -Abs::inf();
    let mut max_y = -Abs::inf();

    let pos = Point::zero();

    // Compute the bounding box of the transformed link.
    for point in [
        pos,
        pos + Point::with_x(size.x),
        pos + Point::with_y(size.y),
        pos + size.to_point(),
    ] {
        let t = point.transform(fc.state().transform());
        min_x.set_min(t.x);
        min_y.set_min(t.y);
        max_x.set_max(t.x);
        max_y.set_max(t.y);
    }

    let x1 = min_x.to_f32();
    let x2 = max_x.to_f32();
    let y1 = min_y.to_f32();
    let y2 = max_y.to_f32();

    let rect = Rect::from_ltrb(x1, y1, x2, y2).unwrap();

    // TODO: Support quad points.

    let pos = match dest {
        Destination::Url(u) => {
            fc.push_annotation(
                LinkAnnotation::new(
                    rect,
                    None,
                    Target::Action(Action::Link(LinkAction::new(u.to_string()))),
                )
                .into(),
            );
            return;
        }
        Destination::Position(p) => *p,
        Destination::Location(loc) => {
            if let Some(nd) = gc.loc_to_names.get(loc) {
                // If a named destination has been registered, it's already guaranteed to
                // not point to an excluded page.
                fc.push_annotation(
                    LinkAnnotation::new(
                        rect,
                        None,
                        Target::Destination(krilla::destination::Destination::Named(
                            nd.clone(),
                        )),
                    )
                    .into(),
                );
                return;
            } else {
                gc.document.introspector.position(*loc)
            }
        }
    };

    let page_index = pos.page.get() - 1;
    if let Some(index) = gc.page_index_converter.pdf_page_index(page_index) {
        fc.push_annotation(
            LinkAnnotation::new(
                rect,
                None,
                Target::Destination(krilla::destination::Destination::Xyz(
                    XyzDestination::new(index, pos.point.to_krilla()),
                )),
            )
            .into(),
        );
    }
}