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::numbering::CT_Numbering;
15use rdocx_oxml::styles::CT_Styles;
16use rdocx_oxml::theme::Theme;
17
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
20pub enum RevisionView {
21 #[default]
23 Accepted,
24 Tracked,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ImageData {
31 pub data: Vec<u8>,
33 pub content_type: String,
35}
36
37#[derive(Debug, Clone)]
39pub struct MediaRegistry {
40 relationship_ids: HashMap<String, MediaId>,
41 media: HashMap<MediaId, ImageData>,
42 missing_id: MediaId,
43}
44
45impl MediaRegistry {
46 pub fn new(images: &HashMap<String, ImageData>) -> Self {
48 Self::with_hasher(images, MediaId::from_bytes)
49 }
50
51 pub fn id_for_relationship(&self, relationship_id: &str) -> MediaId {
53 self.relationship_ids
54 .get(relationship_id)
55 .copied()
56 .unwrap_or(self.missing_id)
57 }
58
59 pub fn media(&self) -> &HashMap<MediaId, ImageData> {
61 &self.media
62 }
63
64 pub(crate) fn with_hasher<F>(images: &HashMap<String, ImageData>, media_id_for_bytes: F) -> Self
65 where
66 F: Fn(&[u8]) -> MediaId,
67 {
68 let missing_id = media_id_for_bytes(&[]);
69 let mut media = HashMap::from([(
70 missing_id,
71 ImageData {
72 data: Vec::new(),
73 content_type: String::new(),
74 },
75 )]);
76 let mut relationship_ids = HashMap::new();
77 let mut images = images.iter().collect::<Vec<_>>();
78 images.sort_unstable_by(|(left_id, left), (right_id, right)| {
79 left.data
80 .cmp(&right.data)
81 .then_with(|| left.content_type.cmp(&right.content_type))
82 .then_with(|| left_id.cmp(right_id))
83 });
84
85 for (relationship_id, image) in images {
86 let mut media_id = media_id_for_bytes(&image.data);
87 loop {
88 match media.get(&media_id) {
89 Some(existing) if existing.data == image.data => break,
90 Some(_) => media_id.0 = media_id.0.wrapping_add(1),
91 None => {
92 media.insert(media_id, image.clone());
93 break;
94 }
95 }
96 }
97 relationship_ids.insert(relationship_id.clone(), media_id);
98 }
99
100 Self {
101 relationship_ids,
102 media,
103 missing_id,
104 }
105 }
106}
107
108#[derive(Debug, Clone)]
110pub struct LayoutInput {
111 pub document: CT_Document,
113 pub automatic_hyphenation: bool,
115 pub revision_view: RevisionView,
117 pub styles: CT_Styles,
119 pub numbering: Option<CT_Numbering>,
121 pub headers: HashMap<String, CT_HdrFtr>,
123 pub footers: HashMap<String, CT_HdrFtr>,
125 pub images: HashMap<String, ImageData>,
127 pub charts: HashMap<String, std::result::Result<Box<CT_ChartSpace>, String>>,
129 pub chart_theme: CT_OfficeStyleSheet,
131 pub chart_color_map: ColorMap,
133 pub core_properties: Option<CoreProperties>,
135 pub hyperlink_urls: HashMap<String, String>,
137 pub footnotes: Option<CT_Footnotes>,
139 pub endnotes: Option<CT_Footnotes>,
141 pub theme: Option<Theme>,
143 pub fonts: Vec<FontFile>,
146}