1use std::collections::HashMap;
4
5pub use oxml_layout::FontFile;
6use oxml_layout::MediaId;
7use rdocx_oxml::core_properties::CoreProperties;
8use rdocx_oxml::document::CT_Document;
9use rdocx_oxml::footnotes::CT_Footnotes;
10use rdocx_oxml::header_footer::CT_HdrFtr;
11use rdocx_oxml::numbering::CT_Numbering;
12use rdocx_oxml::styles::CT_Styles;
13use rdocx_oxml::theme::Theme;
14
15#[derive(Debug, Clone)]
17pub struct ImageData {
18 pub data: Vec<u8>,
20 pub content_type: String,
22}
23
24#[derive(Debug, Clone)]
26pub struct MediaRegistry {
27 relationship_ids: HashMap<String, MediaId>,
28 media: HashMap<MediaId, ImageData>,
29 missing_id: MediaId,
30}
31
32impl MediaRegistry {
33 pub fn new(images: &HashMap<String, ImageData>) -> Self {
35 Self::with_hasher(images, MediaId::from_bytes)
36 }
37
38 pub fn id_for_relationship(&self, relationship_id: &str) -> MediaId {
40 self.relationship_ids
41 .get(relationship_id)
42 .copied()
43 .unwrap_or(self.missing_id)
44 }
45
46 pub fn media(&self) -> &HashMap<MediaId, ImageData> {
48 &self.media
49 }
50
51 pub(crate) fn with_hasher<F>(images: &HashMap<String, ImageData>, media_id_for_bytes: F) -> Self
52 where
53 F: Fn(&[u8]) -> MediaId,
54 {
55 let missing_id = media_id_for_bytes(&[]);
56 let mut media = HashMap::from([(
57 missing_id,
58 ImageData {
59 data: Vec::new(),
60 content_type: String::new(),
61 },
62 )]);
63 let mut relationship_ids = HashMap::new();
64 let mut images = images.iter().collect::<Vec<_>>();
65 images.sort_unstable_by(|(left_id, left), (right_id, right)| {
66 left.data
67 .cmp(&right.data)
68 .then_with(|| left.content_type.cmp(&right.content_type))
69 .then_with(|| left_id.cmp(right_id))
70 });
71
72 for (relationship_id, image) in images {
73 let mut media_id = media_id_for_bytes(&image.data);
74 loop {
75 match media.get(&media_id) {
76 Some(existing) if existing.data == image.data => break,
77 Some(_) => media_id.0 = media_id.0.wrapping_add(1),
78 None => {
79 media.insert(media_id, image.clone());
80 break;
81 }
82 }
83 }
84 relationship_ids.insert(relationship_id.clone(), media_id);
85 }
86
87 Self {
88 relationship_ids,
89 media,
90 missing_id,
91 }
92 }
93}
94
95#[derive(Debug, Clone)]
97pub struct LayoutInput {
98 pub document: CT_Document,
100 pub styles: CT_Styles,
102 pub numbering: Option<CT_Numbering>,
104 pub headers: HashMap<String, CT_HdrFtr>,
106 pub footers: HashMap<String, CT_HdrFtr>,
108 pub images: HashMap<String, ImageData>,
110 pub core_properties: Option<CoreProperties>,
112 pub hyperlink_urls: HashMap<String, String>,
114 pub footnotes: Option<CT_Footnotes>,
116 pub endnotes: Option<CT_Footnotes>,
118 pub theme: Option<Theme>,
120 pub fonts: Vec<FontFile>,
123}