Skip to main content

rustyfi_backend/
doc.rs

1//! Document-level PDF extras: link annotations, named destinations, the
2//! outline (bookmark) tree, and per-page decoration-graphics overlays.
3//! Pure data — accumulated lang-side (`Interp`, during `fire_hooks`),
4//! carried on `DocumentValue::extras`, emitted by both PDF writers.
5//! Upstream: annotation.ml / namedDest.ml / outline.ml.
6
7use crate::graphics::{Color, GraphicsElem};
8use crate::length::Length;
9
10/// One `/Annots` entry (upstream `Annotation.t` + its rect/border payload).
11#[derive(Clone, Debug, PartialEq)]
12pub struct Annot {
13    /// 0-based page index the annotation is attached to.
14    pub page: usize,
15    /// `(x1, y1, x2, y2)` in PDF points, y-up, lower-left/upper-right —
16    /// already `annotation.ml:22`'s `(x, y - dpt, x + wid, y + hgt)`.
17    pub rect: (Length, Length, Length, Length),
18    pub action: AnnotAction,
19    /// `/Border` width + `/C` color (upstream `borderopt`); `None` ⇒
20    /// `/Border [0 0 0]` (upstream emits a zero border, NOT the PDF default).
21    pub border: Option<(Length, Color)>,
22}
23
24/// The link action (upstream `Pdfaction.Uri` / `Pdfaction.GotoName`).
25#[derive(Clone, Debug, PartialEq)]
26pub enum AnnotAction {
27    Uri(String),
28    GotoName(String),
29}
30
31/// One named destination (upstream `NamedDest`: `(name, (x, y), pageno)`),
32/// emitted as `/Dests { name: [page /XYZ x y 0] }`.
33#[derive(Clone, Debug, PartialEq)]
34pub struct NamedDest {
35    pub page: usize, // 0-based
36    pub name: String,
37    pub x: Length, // PDF points, y-up
38    pub y: Length,
39}
40
41/// One outline entry (upstream `Outline`: `(level, text, key, isopen)` with
42/// the key already resolved to a `/Dests` name via `NamedDest.get`).
43#[derive(Clone, Debug, PartialEq)]
44pub struct OutlineEntry {
45    pub level: i64,
46    pub text: String,
47    pub dest_name: String,
48    pub is_open: bool,
49}
50
51/// `register-document-information`'s payload (upstream `tDOCINFODIC` =
52/// `document-information-dictionary`,
53/// `dev-0-1-0:src/frontend/primitives.cppo.ml:98-107`): `/Info` dictionary
54/// fields. Structural on the language side (`t_doc_info_dictionary()` is a
55/// closed record, not a nominal synonym type).
56#[derive(Clone, Debug, Default, PartialEq, Eq)]
57pub struct DocInfo {
58    pub title: Option<String>,
59    pub subject: Option<String>,
60    pub author: Option<String>,
61    /// Joined with a single space at emission time (upstream
62    /// `String.concat " "`, `documentInformationDictionary.ml`), only if
63    /// non-empty.
64    pub keywords: Vec<String>,
65}
66
67/// Everything the PDF writers need beyond `pages`/`images`.
68#[derive(Clone, Debug, Default, PartialEq)]
69pub struct DocExtras {
70    pub annotations: Vec<Annot>,
71    pub destinations: Vec<NamedDest>,
72    pub outline: Vec<OutlineEntry>,
73    /// One overlay per page (may be shorter than `pages`; missing = empty):
74    /// deco graphics fired at placement time, absolute PDF y-up coordinates,
75    /// drawn UNDER the page's text (background fills/borders).
76    pub page_graphics: Vec<Vec<GraphicsElem>>,
77    /// `register-document-information`'s registered value, `None` when
78    /// never called (both PDF writers gate the whole `/Info` dict emission
79    /// on this — every document that never calls
80    /// `register-document-information` stays byte-identical).
81    pub doc_info: Option<DocInfo>,
82}