Skip to main content

pdf_annot/
markup.rs

1//! Markup annotations: Text, Highlight, Underline, StrikeOut, Squiggly, Caret, Popup.
2
3// Copyright (c) 2026 Innovation Trigger B.V.
4//
5// PDFluent is available under two licences, at your option: the GNU AGPLv3, or
6// the PDFluent Commercial Licence. See the LICENSE file in this repository --
7// that file travels with the copy you received, which a URL does not.
8
9extern crate alloc;
10
11use crate::annotation::Annotation;
12use crate::types::*;
13use pdf_syntax::object::dict::keys::*;
14use pdf_syntax::object::{Name, Rect};
15
16/// A text (sticky note) annotation.
17#[derive(Debug)]
18pub struct TextAnnotation {
19    /// Whether the annotation is initially open.
20    pub open: bool,
21    /// The icon name.
22    pub icon: alloc::string::String,
23}
24
25impl TextAnnotation {
26    /// Extract text annotation properties from an annotation.
27    pub fn from_annot(annot: &Annotation<'_>) -> Self {
28        let dict = annot.dict();
29        let open = dict.get::<bool>(Name::new(b"Open")).unwrap_or(false);
30        let icon = dict
31            .get::<Name>(NAME)
32            .map(|n| alloc::string::String::from(n.as_str()))
33            .unwrap_or_else(|| alloc::string::String::from("Note"));
34        Self { open, icon }
35    }
36}
37
38/// The type of text markup.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum TextMarkupType {
41    /// Highlight.
42    Highlight,
43    /// Underline.
44    Underline,
45    /// Squiggly underline.
46    Squiggly,
47    /// Strikeout.
48    StrikeOut,
49}
50
51/// A text markup annotation (Highlight, Underline, Squiggly, StrikeOut).
52#[derive(Debug)]
53pub struct TextMarkupAnnotation {
54    /// The markup type.
55    pub markup_type: TextMarkupType,
56    /// The quadrilateral points defining the marked-up region.
57    pub quad_points: Option<QuadPoints>,
58}
59
60impl TextMarkupAnnotation {
61    /// Extract text markup properties from an annotation.
62    pub fn from_annot(annot: &Annotation<'_>) -> Option<Self> {
63        let markup_type = match annot.annotation_type() {
64            AnnotationType::Highlight => TextMarkupType::Highlight,
65            AnnotationType::Underline => TextMarkupType::Underline,
66            AnnotationType::Squiggly => TextMarkupType::Squiggly,
67            AnnotationType::StrikeOut => TextMarkupType::StrikeOut,
68            _ => return None,
69        };
70        let quad_points = annot.quad_points();
71        Some(Self {
72            markup_type,
73            quad_points,
74        })
75    }
76}
77
78/// A caret annotation (ISO 32000-2 §12.5.6.18).
79#[derive(Debug)]
80pub struct CaretAnnotation {
81    /// The caret symbol.
82    pub symbol: alloc::string::String,
83    /// Rectangle differences.
84    pub rd: Option<Rect>,
85}
86
87impl CaretAnnotation {
88    /// Extract caret annotation properties.
89    pub fn from_annot(annot: &Annotation<'_>) -> Self {
90        let dict = annot.dict();
91        let symbol = dict
92            .get::<Name>(SY)
93            .map(|n| alloc::string::String::from(n.as_str()))
94            .unwrap_or_else(|| alloc::string::String::from("None"));
95        let rd = dict.get::<Rect>(RD);
96        Self { symbol, rd }
97    }
98}
99
100/// A popup annotation (ISO 32000-2 §12.5.6.14).
101#[derive(Debug)]
102pub struct PopupAnnotation {
103    /// Whether the popup is initially open.
104    pub open: bool,
105}
106
107impl PopupAnnotation {
108    /// Extract popup annotation properties.
109    pub fn from_annot(annot: &Annotation<'_>) -> Self {
110        let open = annot
111            .dict()
112            .get::<bool>(Name::new(b"Open"))
113            .unwrap_or(false);
114        Self { open }
115    }
116}