1use 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#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
21pub enum RevisionView {
22 #[default]
24 Accepted,
25 Tracked,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ImageData {
32 pub data: Vec<u8>,
34 pub content_type: String,
36}
37
38#[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 pub fn new(images: &HashMap<String, ImageData>) -> Self {
49 Self::with_hasher(images, MediaId::from_bytes)
50 }
51
52 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 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#[derive(Debug, Clone)]
111pub struct LayoutInput {
112 pub document: CT_Document,
114 pub automatic_hyphenation: bool,
116 pub math_properties: Option<MathProperties>,
118 pub revision_view: RevisionView,
120 pub styles: CT_Styles,
122 pub numbering: Option<CT_Numbering>,
124 pub headers: HashMap<String, CT_HdrFtr>,
126 pub footers: HashMap<String, CT_HdrFtr>,
128 pub images: HashMap<String, ImageData>,
130 pub charts: HashMap<String, std::result::Result<Box<CT_ChartSpace>, String>>,
132 pub chart_theme: CT_OfficeStyleSheet,
134 pub chart_color_map: ColorMap,
136 pub core_properties: Option<CoreProperties>,
138 pub hyperlink_urls: HashMap<String, String>,
140 pub footnotes: Option<CT_Footnotes>,
142 pub endnotes: Option<CT_Footnotes>,
144 pub theme: Option<Theme>,
146 pub fonts: Vec<FontFile>,
149}