Skip to main content

pdf_annot/builder/
types.rs

1//! Value types used by the annotation builder: rectangles, subtypes, icons,
2//! and line endings.
3
4#[cfg(feature = "write")]
5use lopdf::Object;
6
7/// PDF annotation rectangle in page user-space coordinates.
8///
9/// Coordinates are PDF user units (1/72 inch) with the origin at the
10/// bottom-left of the page. The rectangle is the annotation's clickable /
11/// visible area on the page; for markup annotations it is also used as
12/// the bounding box of the marked text region.
13///
14/// `(x0, y0)` is one corner and `(x1, y1)` is the opposite corner; the
15/// constructor does not require a particular ordering, but a zero-area
16/// rectangle (width == 0 or height == 0) is rejected by
17/// [`super::AnnotationBuilder::build`].
18#[cfg(feature = "write")]
19#[derive(Debug, Clone, Copy)]
20pub struct AnnotRect {
21    /// X coordinate of the first corner, in PDF user-space points.
22    pub x0: f64,
23    /// Y coordinate of the first corner, in PDF user-space points.
24    pub y0: f64,
25    /// X coordinate of the opposite corner, in PDF user-space points.
26    pub x1: f64,
27    /// Y coordinate of the opposite corner, in PDF user-space points.
28    pub y1: f64,
29}
30
31#[cfg(feature = "write")]
32impl AnnotRect {
33    /// Construct a rectangle from two opposite corners. No ordering is
34    /// required between `(x0, y0)` and `(x1, y1)` — width and height are
35    /// computed as absolute differences.
36    pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Self {
37        Self { x0, y0, x1, y1 }
38    }
39
40    /// Width in PDF user-space points (always non-negative).
41    pub fn width(&self) -> f64 {
42        (self.x1 - self.x0).abs()
43    }
44
45    /// Height in PDF user-space points (always non-negative).
46    pub fn height(&self) -> f64 {
47        (self.y1 - self.y0).abs()
48    }
49
50    pub(super) fn as_array(&self) -> Object {
51        Object::Array(vec![
52            Object::Real(self.x0 as f32),
53            Object::Real(self.y0 as f32),
54            Object::Real(self.x1 as f32),
55            Object::Real(self.y1 as f32),
56        ])
57    }
58}
59
60/// PDF annotation subtype as defined by ISO 32000-2 §12.5.6.
61///
62/// Each variant maps directly to the `/Subtype` name written into the
63/// annotation dictionary. Use the typed constructors on
64/// [`super::AnnotationBuilder`] (`square`, `highlight`, `link_uri`, …) instead
65/// of constructing this enum directly when possible — they wire up the
66/// per-subtype defaults that are required for a valid annotation.
67#[cfg(feature = "write")]
68#[derive(Debug, Clone, Copy)]
69pub enum AnnotSubtype {
70    /// Filled or stroked rectangle (`/Square`). Used for emphasis or
71    /// region marking. Pair with [`super::AnnotationBuilder::interior_color`]
72    /// for a fill.
73    Square,
74    /// Filled or stroked ellipse (`/Circle`) inscribed in the
75    /// rectangle. Pair with [`super::AnnotationBuilder::interior_color`] for a
76    /// fill.
77    Circle,
78    /// Line segment (`/Line`) between two points, optionally with arrow
79    /// or other end caps. See [`super::AnnotationBuilder::line`] and
80    /// [`super::AnnotationBuilder::line_endings`].
81    Line,
82    /// Text-markup highlight (`/Highlight`) — semi-transparent yellow
83    /// fill over a text range. Use [`super::AnnotationBuilder::quad_points`]
84    /// to mark the actual text quadrilaterals.
85    Highlight,
86    /// Text-markup underline (`/Underline`) drawn beneath a text range.
87    Underline,
88    /// Text-markup strike-through (`/StrikeOut`) drawn through a text
89    /// range.
90    StrikeOut,
91    /// Text-markup squiggly underline (`/Squiggly`) — typically used to
92    /// flag spelling or grammar concerns.
93    Squiggly,
94    /// Free-text annotation (`/FreeText`) — text rendered directly on
95    /// the page (vs. a popup note). See
96    /// [`super::AnnotationBuilder::free_text`].
97    FreeText,
98    /// Sticky-note annotation (`/Text`). Despite the name, this is the
99    /// "popup comment" style — a small icon on the page that opens a
100    /// text bubble in the viewer. See
101    /// [`super::AnnotationBuilder::sticky_note`].
102    Text,
103    /// Rubber stamp annotation (`/Stamp`) — APPROVED, DRAFT,
104    /// CONFIDENTIAL, etc. See [`crate::StampName`] for the standard set and
105    /// [`super::AnnotationBuilder::stamp`] / [`super::AnnotationBuilder::stamp_custom`].
106    Stamp,
107    /// Free-hand ink annotation (`/Ink`) consisting of one or more
108    /// stroke paths. See [`super::AnnotationBuilder::ink`].
109    Ink,
110    /// Closed polygon (`/Polygon`) defined by a list of vertices.
111    /// Optionally filled with [`super::AnnotationBuilder::interior_color`].
112    Polygon,
113    /// Open polyline (`/PolyLine`) defined by a list of vertices.
114    /// Stroked but not filled.
115    PolyLine,
116    /// Hyperlink annotation (`/Link`) — invisible by default, the
117    /// viewer renders it as a clickable region. See
118    /// [`super::AnnotationBuilder::link_uri`] and
119    /// [`super::AnnotationBuilder::link_dest`].
120    Link,
121}
122
123#[cfg(feature = "write")]
124impl AnnotSubtype {
125    pub(super) fn as_str(&self) -> &'static str {
126        match self {
127            Self::Square => "Square",
128            Self::Circle => "Circle",
129            Self::Line => "Line",
130            Self::Highlight => "Highlight",
131            Self::Underline => "Underline",
132            Self::StrikeOut => "StrikeOut",
133            Self::Squiggly => "Squiggly",
134            Self::FreeText => "FreeText",
135            Self::Text => "Text",
136            Self::Stamp => "Stamp",
137            Self::Ink => "Ink",
138            Self::Polygon => "Polygon",
139            Self::PolyLine => "PolyLine",
140            Self::Link => "Link",
141        }
142    }
143}
144
145/// Standard icon names for `/Text` (sticky-note) annotations per
146/// ISO 32000-2 §12.5.6.4 Table 180.
147///
148/// The icon is the small graphic that the viewer renders on the page;
149/// clicking it opens the popup note containing the annotation's
150/// `/Contents` text.
151#[cfg(feature = "write")]
152#[derive(Debug, Clone, Copy)]
153pub enum TextIcon {
154    /// Speech-bubble icon — the most common "this is a comment" icon.
155    Comment,
156    /// Key icon — typically used to flag access-control or
157    /// authentication notes.
158    Key,
159    /// Generic note-paper icon.
160    Note,
161    /// Question-mark / help icon for clarification requests.
162    Help,
163    /// New-paragraph icon (¶ with a plus) for editorial markup.
164    NewParagraph,
165    /// Paragraph icon (¶) for editorial markup.
166    Paragraph,
167    /// Caret/insert icon for indicating insertion points in editorial
168    /// review.
169    Insert,
170}
171
172#[cfg(feature = "write")]
173impl TextIcon {
174    pub(super) fn as_str(&self) -> &'static str {
175        match self {
176            Self::Comment => "Comment",
177            Self::Key => "Key",
178            Self::Note => "Note",
179            Self::Help => "Help",
180            Self::NewParagraph => "NewParagraph",
181            Self::Paragraph => "Paragraph",
182            Self::Insert => "Insert",
183        }
184    }
185}
186
187/// Line-ending style for `/Line`, `/PolyLine`, and free-text callout
188/// annotations per ISO 32000-2 §12.5.6.7 Table 179.
189///
190/// Pass two values to [`super::AnnotationBuilder::line_endings`] — the first
191/// applies to the start point, the second to the end point.
192#[cfg(feature = "write")]
193#[derive(Debug, Clone, Copy)]
194pub enum LineEnding {
195    /// `/None` — no end cap; the line terminates flush.
196    None,
197    /// `/Square` — solid square end cap.
198    Square,
199    /// `/Circle` — solid circle end cap.
200    Circle,
201    /// `/Diamond` — diamond / rhombus end cap.
202    Diamond,
203    /// `/OpenArrow` — open V-shaped arrowhead pointing away from the line.
204    OpenArrow,
205    /// `/ClosedArrow` — filled triangular arrowhead pointing away from the line.
206    ClosedArrow,
207    /// `/Butt` — short perpendicular tick at the end of the line.
208    Butt,
209    /// `/ROpenArrow` — reversed open arrow (points back along the line).
210    ROpenArrow,
211    /// `/RClosedArrow` — reversed filled arrow (points back along the line).
212    RClosedArrow,
213    /// `/Slash` — short diagonal slash at the end of the line.
214    Slash,
215}
216
217#[cfg(feature = "write")]
218impl LineEnding {
219    pub(super) fn as_str(&self) -> &'static str {
220        match self {
221            Self::None => "None",
222            Self::Square => "Square",
223            Self::Circle => "Circle",
224            Self::Diamond => "Diamond",
225            Self::OpenArrow => "OpenArrow",
226            Self::ClosedArrow => "ClosedArrow",
227            Self::Butt => "Butt",
228            Self::ROpenArrow => "ROpenArrow",
229            Self::RClosedArrow => "RClosedArrow",
230            Self::Slash => "Slash",
231        }
232    }
233}