Skip to main content

rdocx_layout/
output.rs

1//! Output types for the layout engine: positioned page frames, glyph runs, etc.
2
3/// A point in 2D space (in typographic points from the top-left corner).
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct Point {
6    pub x: f64,
7    pub y: f64,
8}
9
10/// An axis-aligned rectangle.
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub struct Rect {
13    pub x: f64,
14    pub y: f64,
15    pub width: f64,
16    pub height: f64,
17}
18
19/// An RGBA color with components in [0.0, 1.0].
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub struct Color {
22    pub r: f64,
23    pub g: f64,
24    pub b: f64,
25    pub a: f64,
26}
27
28impl Color {
29    pub const BLACK: Color = Color {
30        r: 0.0,
31        g: 0.0,
32        b: 0.0,
33        a: 1.0,
34    };
35    pub const WHITE: Color = Color {
36        r: 1.0,
37        g: 1.0,
38        b: 1.0,
39        a: 1.0,
40    };
41
42    /// Parse a hex color string like "FF0000" to Color.
43    pub fn from_hex(hex: &str) -> Self {
44        let hex = hex.trim_start_matches('#');
45        if hex.len() >= 6 {
46            let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0) as f64 / 255.0;
47            let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0) as f64 / 255.0;
48            let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0) as f64 / 255.0;
49            Color { r, g, b, a: 1.0 }
50        } else {
51            Color::BLACK
52        }
53    }
54}
55
56/// Opaque font identifier assigned by FontManager.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub struct FontId(pub u32);
59
60/// Kind of field for post-pagination substitution.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum FieldKind {
63    /// Current page number.
64    Page,
65    /// Total number of pages.
66    NumPages,
67}
68
69/// A positioned run of shaped glyphs.
70#[derive(Debug, Clone)]
71pub struct GlyphRun {
72    /// Baseline origin of the first glyph (in points).
73    pub origin: Point,
74    /// Font identifier (from FontManager).
75    pub font_id: FontId,
76    /// Font size in points.
77    pub font_size: f64,
78    /// Shaped glyph IDs.
79    pub glyph_ids: Vec<u16>,
80    /// Per-glyph advances in points.
81    pub advances: Vec<f64>,
82    /// Original text (for PDF ToUnicode mapping).
83    pub text: String,
84    /// Text color.
85    pub color: Color,
86    /// Whether the font is bold.
87    pub bold: bool,
88    /// Whether the font is italic.
89    pub italic: bool,
90    /// If this glyph run is a field placeholder, the kind of field.
91    pub field_kind: Option<FieldKind>,
92    /// If this glyph run is a footnote/endnote reference marker, its ID.
93    pub footnote_id: Option<i32>,
94}
95
96/// A positioned element on a page.
97#[derive(Debug, Clone)]
98pub enum PositionedElement {
99    /// A run of shaped text glyphs.
100    Text(GlyphRun),
101    /// A line segment (for borders, underlines, strikethrough).
102    Line {
103        start: Point,
104        end: Point,
105        width: f64,
106        color: Color,
107        /// Optional dash pattern (dash_on, dash_off) in points. None = solid line.
108        dash_pattern: Option<(f64, f64)>,
109    },
110    /// A filled rectangle (for shading, highlights).
111    FilledRect { rect: Rect, color: Color },
112    /// An inline image.
113    Image {
114        rect: Rect,
115        data: Vec<u8>,
116        content_type: String,
117        /// Embed relationship ID (used to resolve image data post-pagination).
118        embed_id: Option<String>,
119    },
120    /// A link annotation (hyperlink).
121    LinkAnnotation { rect: Rect, url: String },
122}
123
124/// A single page of laid-out content.
125#[derive(Debug, Clone)]
126pub struct PageFrame {
127    /// 1-based page number.
128    pub page_number: usize,
129    /// Page width in points.
130    pub width: f64,
131    /// Page height in points.
132    pub height: f64,
133    /// All positioned elements on this page.
134    pub elements: Vec<PositionedElement>,
135}
136
137/// Font data for embedding in PDF output.
138#[derive(Debug, Clone)]
139pub struct FontData {
140    /// Font identifier.
141    pub id: FontId,
142    /// Font family name.
143    pub family: String,
144    /// Raw TTF/OTF bytes for PDF embedding.
145    pub data: Vec<u8>,
146    /// Face index within a font collection.
147    pub face_index: u32,
148    /// Whether this is a bold variant.
149    pub bold: bool,
150    /// Whether this is an italic variant.
151    pub italic: bool,
152}
153
154/// Document metadata to pass through to PDF output.
155#[derive(Debug, Clone, Default)]
156pub struct DocumentMetadata {
157    /// Document title.
158    pub title: Option<String>,
159    /// Document author.
160    pub author: Option<String>,
161    /// Document subject.
162    pub subject: Option<String>,
163    /// Document keywords.
164    pub keywords: Option<String>,
165    /// Creator application.
166    pub creator: Option<String>,
167}
168
169/// An outline/bookmark entry for PDF generation.
170#[derive(Debug, Clone)]
171pub struct OutlineEntry {
172    /// The heading text.
173    pub title: String,
174    /// Heading level (1 for Heading1, 2 for Heading2, etc.).
175    pub level: u32,
176    /// 0-based page index this heading appears on.
177    pub page_index: usize,
178    /// Y position on the page (in points from top).
179    pub y_position: f64,
180}
181
182/// The complete result of laying out a document.
183#[derive(Debug, Clone)]
184pub struct LayoutResult {
185    /// Laid-out pages.
186    pub pages: Vec<PageFrame>,
187    /// Font data for all fonts used.
188    pub fonts: Vec<FontData>,
189    /// Optional document metadata for PDF output.
190    pub metadata: Option<DocumentMetadata>,
191    /// Outline/bookmark entries from headings.
192    pub outlines: Vec<OutlineEntry>,
193}