Skip to main content

rdocx_layout/
input.rs

1//! Input types for the layout engine.
2
3use std::collections::HashMap;
4
5use oxml_chart::CT_ChartSpace;
6use oxml_drawing::color::ColorMap;
7use oxml_drawing::theme::CT_OfficeStyleSheet;
8pub use oxml_layout::FontFile;
9use oxml_layout::MediaId;
10use rdocx_oxml::core_properties::CoreProperties;
11use rdocx_oxml::document::CT_Document;
12use rdocx_oxml::footnotes::CT_Footnotes;
13use rdocx_oxml::header_footer::CT_HdrFtr;
14use rdocx_oxml::math::MathProperties;
15use rdocx_oxml::numbering::CT_Numbering;
16use rdocx_oxml::styles::CT_Styles;
17use rdocx_oxml::theme::Theme;
18
19/// The tracked-revision projection used for Word layout.
20#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
21pub enum RevisionView {
22    /// Render the document as though all modeled revisions were accepted.
23    #[default]
24    Accepted,
25    /// Render both sides of modeled revisions with tracked decorations.
26    Tracked,
27}
28
29/// Image data keyed by relationship/embed ID.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ImageData {
32    /// Raw image bytes (PNG, JPEG, etc.).
33    pub data: Vec<u8>,
34    /// MIME content type (e.g., "image/png").
35    pub content_type: String,
36}
37
38/// Collision-safe media lookup shared by layout and pagination.
39#[derive(Debug, Clone)]
40pub struct MediaRegistry {
41    relationship_ids: HashMap<String, MediaId>,
42    media: HashMap<MediaId, ImageData>,
43    missing_id: MediaId,
44}
45
46impl MediaRegistry {
47    /// Resolve relationship IDs and image bytes once for a layout operation.
48    pub fn new(images: &HashMap<String, ImageData>) -> Self {
49        Self::with_hasher(images, MediaId::from_bytes)
50    }
51
52    /// Resolve the renderer-local ID for one package relationship.
53    pub fn id_for_relationship(&self, relationship_id: &str) -> MediaId {
54        self.relationship_ids
55            .get(relationship_id)
56            .copied()
57            .unwrap_or(self.missing_id)
58    }
59
60    /// Return the image bytes and content types keyed by resolved media ID.
61    pub fn media(&self) -> &HashMap<MediaId, ImageData> {
62        &self.media
63    }
64
65    pub(crate) fn with_hasher<F>(images: &HashMap<String, ImageData>, media_id_for_bytes: F) -> Self
66    where
67        F: Fn(&[u8]) -> MediaId,
68    {
69        let missing_id = media_id_for_bytes(&[]);
70        let mut media = HashMap::from([(
71            missing_id,
72            ImageData {
73                data: Vec::new(),
74                content_type: String::new(),
75            },
76        )]);
77        let mut relationship_ids = HashMap::new();
78        let mut images = images.iter().collect::<Vec<_>>();
79        images.sort_unstable_by(|(left_id, left), (right_id, right)| {
80            left.data
81                .cmp(&right.data)
82                .then_with(|| left.content_type.cmp(&right.content_type))
83                .then_with(|| left_id.cmp(right_id))
84        });
85
86        for (relationship_id, image) in images {
87            let mut media_id = media_id_for_bytes(&image.data);
88            loop {
89                match media.get(&media_id) {
90                    Some(existing) if existing.data == image.data => break,
91                    Some(_) => media_id.0 = media_id.0.wrapping_add(1),
92                    None => {
93                        media.insert(media_id, image.clone());
94                        break;
95                    }
96                }
97            }
98            relationship_ids.insert(relationship_id.clone(), media_id);
99        }
100
101        Self {
102            relationship_ids,
103            media,
104            missing_id,
105        }
106    }
107}
108
109/// All inputs needed to lay out a DOCX document.
110#[derive(Debug, Clone)]
111pub struct LayoutInput {
112    /// The parsed document content.
113    pub document: CT_Document,
114    /// Whether document settings enable automatic hyphenation.
115    pub automatic_hyphenation: bool,
116    /// Document-wide OfficeMath defaults from the settings part.
117    pub math_properties: Option<MathProperties>,
118    /// The tracked-revision projection to lay out.
119    pub revision_view: RevisionView,
120    /// Style definitions.
121    pub styles: CT_Styles,
122    /// Numbering definitions (optional).
123    pub numbering: Option<CT_Numbering>,
124    /// Header parts keyed by relationship ID.
125    pub headers: HashMap<String, CT_HdrFtr>,
126    /// Footer parts keyed by relationship ID.
127    pub footers: HashMap<String, CT_HdrFtr>,
128    /// Images keyed by embed ID.
129    pub images: HashMap<String, ImageData>,
130    /// Parsed chart parts, or contextual relationship failures, keyed by ID.
131    pub charts: HashMap<String, std::result::Result<Box<CT_ChartSpace>, String>>,
132    /// DrawingML theme used by the shared chart renderer.
133    pub chart_theme: CT_OfficeStyleSheet,
134    /// Standard Word chart colour mapping.
135    pub chart_color_map: ColorMap,
136    /// Document core properties (metadata).
137    pub core_properties: Option<CoreProperties>,
138    /// Hyperlink URLs keyed by relationship ID.
139    pub hyperlink_urls: HashMap<String, String>,
140    /// Footnote definitions.
141    pub footnotes: Option<CT_Footnotes>,
142    /// Endnote definitions.
143    pub endnotes: Option<CT_Footnotes>,
144    /// Document theme (colors + fonts).
145    pub theme: Option<Theme>,
146    /// User-provided or DOCX-embedded font files.
147    /// These are loaded before system fonts, so they take priority.
148    pub fonts: Vec<FontFile>,
149}