Skip to main content

folio_annot/
types.rs

1//! Specialized annotation type helpers.
2//!
3//! Provides convenience accessors for type-specific annotation properties.
4
5use crate::annot::Annot;
6use folio_core::{Point, QuadPoint};
7use folio_cos::PdfObject;
8
9/// Get highlight/underline/strikeout/squiggly quad points.
10pub fn get_quad_points(annot: &Annot) -> Vec<QuadPoint> {
11    let arr = match annot.dict().get(b"QuadPoints".as_slice()) {
12        Some(PdfObject::Array(a)) => a,
13        _ => return Vec::new(),
14    };
15
16    let mut quads = Vec::new();
17    let mut i = 0;
18    while i + 7 < arr.len() {
19        if let (Some(x1), Some(y1), Some(x2), Some(y2), Some(x3), Some(y3), Some(x4), Some(y4)) = (
20            arr[i].as_f64(),
21            arr[i + 1].as_f64(),
22            arr[i + 2].as_f64(),
23            arr[i + 3].as_f64(),
24            arr[i + 4].as_f64(),
25            arr[i + 5].as_f64(),
26            arr[i + 6].as_f64(),
27            arr[i + 7].as_f64(),
28        ) {
29            quads.push(QuadPoint::new(
30                Point::new(x1, y1),
31                Point::new(x2, y2),
32                Point::new(x3, y3),
33                Point::new(x4, y4),
34            ));
35        }
36        i += 8;
37    }
38    quads
39}
40
41/// Get line annotation endpoints.
42pub fn get_line_endpoints(annot: &Annot) -> Option<(Point, Point)> {
43    let arr = annot.dict().get(b"L".as_slice())?.as_array()?;
44    if arr.len() >= 4 {
45        Some((
46            Point::new(arr[0].as_f64()?, arr[1].as_f64()?),
47            Point::new(arr[2].as_f64()?, arr[3].as_f64()?),
48        ))
49    } else {
50        None
51    }
52}
53
54/// Get ink annotation ink lists (array of stroke paths).
55pub fn get_ink_lists(annot: &Annot) -> Vec<Vec<Point>> {
56    let ink_list = match annot.dict().get(b"InkList".as_slice()) {
57        Some(PdfObject::Array(a)) => a,
58        _ => return Vec::new(),
59    };
60
61    ink_list
62        .iter()
63        .filter_map(|stroke| {
64            let arr = stroke.as_array()?;
65            let mut points = Vec::new();
66            let mut i = 0;
67            while i + 1 < arr.len() {
68                if let (Some(x), Some(y)) = (arr[i].as_f64(), arr[i + 1].as_f64()) {
69                    points.push(Point::new(x, y));
70                }
71                i += 2;
72            }
73            Some(points)
74        })
75        .collect()
76}
77
78/// Get polygon/polyline vertices.
79pub fn get_vertices(annot: &Annot) -> Vec<Point> {
80    let arr = match annot.dict().get(b"Vertices".as_slice()) {
81        Some(PdfObject::Array(a)) => a,
82        _ => return Vec::new(),
83    };
84
85    let mut points = Vec::new();
86    let mut i = 0;
87    while i + 1 < arr.len() {
88        if let (Some(x), Some(y)) = (arr[i].as_f64(), arr[i + 1].as_f64()) {
89            points.push(Point::new(x, y));
90        }
91        i += 2;
92    }
93    points
94}
95
96/// Get the URI from a Link annotation's action.
97pub fn get_link_uri(annot: &Annot) -> Option<String> {
98    let action = annot.dict().get(b"A".as_slice())?;
99    let action_dict = action.as_dict()?;
100    let s_type = action_dict.get(b"S".as_slice())?.as_name()?;
101    if s_type == b"URI" {
102        action_dict
103            .get(b"URI".as_slice())?
104            .as_str()
105            .map(|s| String::from_utf8_lossy(s).into_owned())
106    } else {
107        None
108    }
109}