1use std::collections::HashMap;
4
5use oxml_layout::{
6 Diagnostic, Effect, GroupElement, MediaId, Paint, Path, Point, Rect, Stroke, Transform,
7};
8use rpptx_chart::CT_ChartSpace;
9
10mod context;
11mod font;
12mod style;
13mod text;
14
15pub use context::{
16 BackgroundContent, BackgroundSource, EffectiveBackground, FlattenedItem, FlattenedSource,
17 ResolveCtx,
18};
19pub use style::{EffectiveShapeStyle, ResolveError};
20pub use text::{EffectiveListStyle, EffectiveTextProperties};
21
22#[derive(Clone, Debug, Default, Eq, PartialEq)]
24pub struct ScopedMediaIds {
25 pub slide: HashMap<String, MediaId>,
26 pub layout: HashMap<String, MediaId>,
27 pub master: HashMap<String, MediaId>,
28 pub media_content_types: HashMap<MediaId, String>,
30}
31
32impl ScopedMediaIds {
33 pub fn get(&self, source: FlattenedSource, relationship_id: &str) -> Option<MediaId> {
35 match source {
36 FlattenedSource::Slide => &self.slide,
37 FlattenedSource::Layout => &self.layout,
38 FlattenedSource::Master => &self.master,
39 FlattenedSource::Background => return None,
40 }
41 .get(relationship_id)
42 .copied()
43 }
44
45 pub fn content_type(&self, source: FlattenedSource, relationship_id: &str) -> Option<&str> {
47 let media_id = self.get(source, relationship_id)?;
48 self.media_content_types.get(&media_id).map(String::as_str)
49 }
50}
51
52#[derive(Clone, Debug, PartialEq)]
54pub enum ChartResource {
55 Parsed(Box<CT_ChartSpace>),
56 External(String),
57 MissingTarget(String),
58 Invalid(String),
59}
60
61#[derive(Clone, Debug, Default, PartialEq)]
63pub struct ScopedChartResources {
64 pub slide: HashMap<String, ChartResource>,
65 pub layout: HashMap<String, ChartResource>,
66 pub master: HashMap<String, ChartResource>,
67}
68
69impl ScopedChartResources {
70 pub fn get(&self, source: FlattenedSource, relationship_id: &str) -> Option<&ChartResource> {
72 match source {
73 FlattenedSource::Slide => &self.slide,
74 FlattenedSource::Layout => &self.layout,
75 FlattenedSource::Master => &self.master,
76 FlattenedSource::Background => return None,
77 }
78 .get(relationship_id)
79 }
80}
81
82#[derive(Clone, Debug, Default, Eq, PartialEq)]
84pub struct ScopedHyperlinkTargets {
85 pub slide: HashMap<String, String>,
86 pub layout: HashMap<String, String>,
87 pub master: HashMap<String, String>,
88}
89
90impl ScopedHyperlinkTargets {
91 pub fn get(&self, source: FlattenedSource, relationship_id: &str) -> Option<&str> {
93 match source {
94 FlattenedSource::Slide => &self.slide,
95 FlattenedSource::Layout => &self.layout,
96 FlattenedSource::Master => &self.master,
97 FlattenedSource::Background => return None,
98 }
99 .get(relationship_id)
100 .map(String::as_str)
101 }
102}
103
104#[derive(Clone, Debug, PartialEq)]
106pub struct ResolvedSlide {
107 pub size: (f64, f64),
109 pub background: Option<ResolvedBackground>,
110 pub shapes: Vec<ResolvedShape>,
112 pub diagnostics: Vec<Diagnostic>,
113}
114
115#[derive(Clone, Debug, PartialEq)]
117pub enum ResolvedBackground {
118 Paint(Paint),
119 Image(ResolvedImage),
120}
121
122#[derive(Clone, Debug, PartialEq)]
124pub struct ResolvedShape {
125 pub group_transform: Transform,
127 pub bounds: Rect,
128 pub rotation_deg: f64,
129 pub flip_h: bool,
130 pub flip_v: bool,
131 pub geometry: ResolvedGeometry,
132 pub fill: Option<Paint>,
133 pub image_fill: Option<ResolvedImage>,
134 pub line: Option<Stroke>,
135 pub head_end: Option<ResolvedLineEnd>,
136 pub tail_end: Option<ResolvedLineEnd>,
137 pub shadow: Option<Effect>,
138 pub content: ResolvedContent,
139 pub unsupported: Option<&'static str>,
141}
142
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub struct ResolvedLineEnd {
146 pub kind: ResolvedLineEndKind,
147 pub width: ResolvedLineEndSize,
148 pub length: ResolvedLineEndSize,
149}
150
151#[derive(Clone, Copy, Debug, Eq, PartialEq)]
153pub enum ResolvedLineEndKind {
154 Triangle,
155 Stealth,
156 Diamond,
157 Oval,
158 Arrow,
159}
160
161#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
163pub enum ResolvedLineEndSize {
164 Small,
165 #[default]
166 Medium,
167 Large,
168}
169
170#[derive(Clone, Debug, PartialEq)]
172pub enum ResolvedGeometry {
173 Rectangle,
174 Custom {
175 paths: Vec<Path>,
176 text_rect: Option<Rect>,
177 },
178 BoundsFallback,
179}
180
181#[derive(Clone, Debug, PartialEq)]
183pub enum ResolvedContent {
184 None,
185 Text(ResolvedTextBody),
186 Image(ResolvedImage),
187 Table(ResolvedTable),
188 Group(GroupElement),
189}
190
191#[derive(Clone, Debug, PartialEq)]
193pub struct ResolvedImage {
194 pub media: MediaId,
195 pub src_rect: Option<CropRect>,
196 pub placement: ResolvedImagePlacement,
197 pub dpi: Option<f64>,
198 pub rotate_with_shape: bool,
199}
200
201#[derive(Clone, Debug, PartialEq)]
203pub enum ResolvedImagePlacement {
204 Stretch { fill_rect: Option<CropRect> },
205 Tile(ResolvedTilePlacement),
206}
207
208impl Default for ResolvedImagePlacement {
209 fn default() -> Self {
210 Self::Stretch { fill_rect: None }
211 }
212}
213
214#[derive(Clone, Copy, Debug, PartialEq)]
216pub struct ResolvedTilePlacement {
217 pub translation: Point,
218 pub scale_x: f64,
219 pub scale_y: f64,
220 pub flip: ResolvedTileFlip,
221 pub alignment: ResolvedRectAlignment,
222}
223
224#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
226pub enum ResolvedTileFlip {
227 #[default]
228 None,
229 Horizontal,
230 Vertical,
231 Both,
232}
233
234#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
236pub enum ResolvedRectAlignment {
237 #[default]
238 TopLeft,
239 Top,
240 TopRight,
241 Left,
242 Center,
243 Right,
244 BottomLeft,
245 Bottom,
246 BottomRight,
247}
248
249#[derive(Clone, Copy, Debug, Default, PartialEq)]
251pub struct CropRect {
252 pub left: f64,
253 pub top: f64,
254 pub right: f64,
255 pub bottom: f64,
256}
257
258#[derive(Clone, Debug, PartialEq)]
260pub struct ResolvedTextBody {
261 pub insets: TextInsets,
262 pub anchor: TextAnchor,
263 pub wrap: bool,
264 pub vertical: TextDirection,
265 pub space_first_last_paragraph: bool,
266 pub autofit: ResolvedAutofit,
267 pub paragraphs: Vec<ResolvedParagraph>,
268}
269
270#[derive(Clone, Copy, Debug, Default, PartialEq)]
272pub struct TextInsets {
273 pub left: f64,
274 pub top: f64,
275 pub right: f64,
276 pub bottom: f64,
277}
278
279#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
280pub enum TextAnchor {
281 #[default]
282 Top,
283 Center,
284 Bottom,
285 Justified,
286 Distributed,
287}
288
289#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
290pub enum TextDirection {
291 #[default]
292 Horizontal,
293 Vertical,
294 Vertical270,
295 EastAsianVertical,
296 MongolianVertical,
297 WordArtVertical,
298 WordArtVerticalRtl,
299}
300
301#[derive(Clone, Copy, Debug, Default, PartialEq)]
302pub enum ResolvedAutofit {
303 #[default]
304 None,
305 Shape,
306 Normal {
307 font_scale: Option<f64>,
308 line_spacing_reduction: Option<f64>,
309 },
310}
311
312#[derive(Clone, Debug, Default, PartialEq)]
314pub struct ResolvedParagraph {
315 pub level: u8,
316 pub left_margin: f64,
317 pub right_margin: f64,
318 pub indent: f64,
319 pub alignment: ParagraphAlignment,
320 pub line_spacing: Option<ResolvedTextSpacing>,
321 pub space_before: Option<ResolvedTextSpacing>,
322 pub space_after: Option<ResolvedTextSpacing>,
323 pub bullet: Option<ResolvedBullet>,
324 pub end_style: ResolvedRunStyle,
325 pub runs: Vec<ResolvedTextRun>,
326}
327
328#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
329pub enum ParagraphAlignment {
330 #[default]
331 Left,
332 Center,
333 Right,
334 Justified,
335 Distributed,
336}
337
338#[derive(Clone, Debug, PartialEq)]
340pub enum ResolvedTextSpacing {
341 Percent(f64),
342 Points(f64),
343}
344
345#[derive(Clone, Debug, PartialEq)]
347pub enum ResolvedBulletSize {
348 Percent(f64),
349 Points(f64),
350}
351
352#[derive(Clone, Debug, PartialEq)]
353pub enum ResolvedBullet {
354 Character {
355 character: String,
356 font: Option<String>,
357 color: Option<oxml_layout::Color>,
358 size: Option<ResolvedBulletSize>,
359 },
360 AutoNumber {
361 scheme: String,
362 start_at: u32,
363 font: Option<String>,
364 color: Option<oxml_layout::Color>,
365 size: Option<ResolvedBulletSize>,
366 },
367}
368
369#[derive(Clone, Debug, PartialEq)]
371pub enum ResolvedTextRun {
372 Text {
373 text: String,
374 style: ResolvedRunStyle,
375 },
376 Break,
377 Field {
378 text: String,
379 field_type: Option<String>,
380 style: ResolvedRunStyle,
381 },
382}
383
384#[derive(Clone, Debug, Default, PartialEq)]
386pub struct ResolvedRunStyle {
387 pub font_size: Option<f64>,
388 pub bold: bool,
389 pub italic: bool,
390 pub all_caps: bool,
391 pub underline: bool,
392 pub strike: bool,
393 pub spacing: Option<f64>,
394 pub baseline: Option<f64>,
395 pub fill: Option<Paint>,
396 pub latin_typeface: Option<String>,
397 pub east_asian_typeface: Option<String>,
398 pub complex_script_typeface: Option<String>,
399 pub symbol_typeface: Option<String>,
400 pub hyperlink_url: Option<String>,
402}
403
404#[derive(Clone, Debug, Default, PartialEq)]
406pub struct ResolvedTable {
407 pub right_to_left: bool,
408 pub column_widths: Vec<f64>,
409 pub rows: Vec<ResolvedTableRow>,
410}
411
412#[derive(Clone, Debug, Default, PartialEq)]
413pub struct ResolvedTableRow {
414 pub height: f64,
415 pub cells: Vec<ResolvedTableCell>,
416}
417
418#[derive(Clone, Debug, PartialEq)]
419pub struct ResolvedTableCell {
420 pub text: Option<ResolvedTextBody>,
421 pub fill: Option<Paint>,
422 pub margins: TextInsets,
423 pub left: Option<ResolvedTableBorder>,
424 pub right: Option<ResolvedTableBorder>,
425 pub top: Option<ResolvedTableBorder>,
426 pub bottom: Option<ResolvedTableBorder>,
427 pub row_span: u32,
428 pub grid_span: u32,
429 pub horizontal_merge: bool,
430 pub vertical_merge: bool,
431}
432
433impl Default for ResolvedTableCell {
434 fn default() -> Self {
435 Self {
436 text: None,
437 fill: None,
438 margins: TextInsets::default(),
439 left: None,
440 right: None,
441 top: None,
442 bottom: None,
443 row_span: 1,
444 grid_span: 1,
445 horizontal_merge: false,
446 vertical_merge: false,
447 }
448 }
449}
450
451#[derive(Clone, Debug, Default, PartialEq)]
453pub struct ResolvedTableBorder {
454 pub stroke: Option<Stroke>,
455 pub priority: u8,
456}