1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5fn default_text_run_type() -> TextRunNodeType {
6 TextRunNodeType::Text
7}
8
9fn default_image_run_type() -> ImageRunNodeType {
10 ImageRunNodeType::Image
11}
12
13fn default_form_field_run_type() -> FormFieldRunNodeType {
14 FormFieldRunNodeType::FormField
15}
16
17fn default_paragraph_node_type() -> ParagraphNodeType {
18 ParagraphNodeType::Paragraph
19}
20
21fn default_table_cell_node_type() -> TableCellNodeType {
22 TableCellNodeType::TableCell
23}
24
25fn default_table_row_node_type() -> TableRowNodeType {
26 TableRowNodeType::TableRow
27}
28
29fn default_table_node_type() -> TableNodeType {
30 TableNodeType::Table
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub enum ParagraphAlignment {
37 Left,
38 Center,
39 Right,
40 Justify,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
45#[serde(into = "i64")]
46pub enum HeadingLevel {
47 One = 1,
48 Two = 2,
49 Three = 3,
50 Four = 4,
51 Five = 5,
52 Six = 6,
53 Seven = 7,
54 Eight = 8,
55 Nine = 9,
56}
57
58impl<'de> Deserialize<'de> for HeadingLevel {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 let value = i64::deserialize(deserializer)?;
64 match value {
65 1 => Ok(HeadingLevel::One),
66 2 => Ok(HeadingLevel::Two),
67 3 => Ok(HeadingLevel::Three),
68 4 => Ok(HeadingLevel::Four),
69 5 => Ok(HeadingLevel::Five),
70 6 => Ok(HeadingLevel::Six),
71 7 => Ok(HeadingLevel::Seven),
72 8 => Ok(HeadingLevel::Eight),
73 9 => Ok(HeadingLevel::Nine),
74 _ => Err(serde::de::Error::custom(format!(
75 "invalid heading level {value}"
76 ))),
77 }
78 }
79}
80
81impl From<HeadingLevel> for i64 {
82 fn from(value: HeadingLevel) -> Self {
83 value as i64
84 }
85}
86
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct TextRunBorderStyle {
91 #[serde(rename = "type")]
92 pub border_type: String,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub color: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub size_eighth_pt: Option<f64>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub space_pt: Option<f64>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub frame: Option<bool>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub shadow: Option<bool>,
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub enum VerticalAlign {
109 #[serde(rename = "superscript")]
110 Superscript,
111 #[serde(rename = "subscript")]
112 Subscript,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub enum TextLigatures {
118 None,
119 Standard,
120 Contextual,
121 Historical,
122 Discretional,
123 StandardContextual,
124 StandardHistorical,
125 ContextualHistorical,
126 StandardDiscretional,
127 ContextualDiscretional,
128 HistoricalDiscretional,
129 StandardContextualHistorical,
130 StandardContextualDiscretional,
131 StandardHistoricalDiscretional,
132 ContextualHistoricalDiscretional,
133 All,
134}
135
136impl TextLigatures {
137 pub fn from_xml_value(value: &str) -> Option<Self> {
138 match value {
139 "none" => Some(Self::None),
140 "standard" => Some(Self::Standard),
141 "contextual" => Some(Self::Contextual),
142 "historical" => Some(Self::Historical),
143 "discretional" => Some(Self::Discretional),
144 "standardContextual" => Some(Self::StandardContextual),
145 "standardHistorical" => Some(Self::StandardHistorical),
146 "contextualHistorical" => Some(Self::ContextualHistorical),
147 "standardDiscretional" => Some(Self::StandardDiscretional),
148 "contextualDiscretional" => Some(Self::ContextualDiscretional),
149 "historicalDiscretional" => Some(Self::HistoricalDiscretional),
150 "standardContextualHistorical" => Some(Self::StandardContextualHistorical),
151 "standardContextualDiscretional" => Some(Self::StandardContextualDiscretional),
152 "standardHistoricalDiscretional" => Some(Self::StandardHistoricalDiscretional),
153 "contextualHistoricalDiscretional" => Some(Self::ContextualHistoricalDiscretional),
154 "all" => Some(Self::All),
155 _ => None,
156 }
157 }
158
159 pub fn xml_value(self) -> &'static str {
160 match self {
161 Self::None => "none",
162 Self::Standard => "standard",
163 Self::Contextual => "contextual",
164 Self::Historical => "historical",
165 Self::Discretional => "discretional",
166 Self::StandardContextual => "standardContextual",
167 Self::StandardHistorical => "standardHistorical",
168 Self::ContextualHistorical => "contextualHistorical",
169 Self::StandardDiscretional => "standardDiscretional",
170 Self::ContextualDiscretional => "contextualDiscretional",
171 Self::HistoricalDiscretional => "historicalDiscretional",
172 Self::StandardContextualHistorical => "standardContextualHistorical",
173 Self::StandardContextualDiscretional => "standardContextualDiscretional",
174 Self::StandardHistoricalDiscretional => "standardHistoricalDiscretional",
175 Self::ContextualHistoricalDiscretional => "contextualHistoricalDiscretional",
176 Self::All => "all",
177 }
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct TextStyle {
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub bold: Option<bool>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub italic: Option<bool>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub underline: Option<bool>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub strike: Option<bool>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub all_caps: Option<bool>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub small_caps: Option<bool>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub color: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub highlight: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub background_color: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub font_size_pt: Option<f64>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub font_size_cs_pt: Option<f64>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub font_family: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
212 pub source_font_family: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none")]
216 pub font_family_ascii: Option<String>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub font_family_h_ansi: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub font_family_east_asia: Option<String>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub font_family_cs: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub font_theme_ascii: Option<String>,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub font_theme_h_ansi: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub font_theme_east_asia: Option<String>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub font_theme_cs: Option<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub resolved_font_family_ascii: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub resolved_font_family_h_ansi: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub resolved_font_family_east_asia: Option<String>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub resolved_font_family_cs: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub font_hint: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
244 pub language: Option<String>,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub language_east_asia: Option<String>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub language_bidi: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub right_to_left: Option<bool>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub complex_script: Option<bool>,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub character_spacing_twips: Option<i64>,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub character_scale_percent: Option<u16>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub source_character_scale: Option<String>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub kerning_min_font_size_pt: Option<f64>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub ligatures: Option<TextLigatures>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub contextual_alternates: Option<bool>,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub vertical_align: Option<VerticalAlign>,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub run_border: Option<TextRunBorderStyle>,
269}
270
271#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
273#[serde(transparent)]
274pub struct SharedTextStyle(std::sync::Arc<TextStyle>);
275
276impl SharedTextStyle {
277 pub fn into_owned(self) -> TextStyle {
278 std::sync::Arc::unwrap_or_clone(self.0)
279 }
280}
281
282impl From<TextStyle> for SharedTextStyle {
283 fn from(style: TextStyle) -> Self {
284 Self(std::sync::Arc::new(style))
285 }
286}
287
288impl std::ops::Deref for SharedTextStyle {
289 type Target = TextStyle;
290
291 fn deref(&self) -> &TextStyle {
292 &self.0
293 }
294}
295
296impl std::ops::DerefMut for SharedTextStyle {
297 fn deref_mut(&mut self) -> &mut TextStyle {
298 std::sync::Arc::make_mut(&mut self.0)
299 }
300}
301
302#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
304#[serde(rename_all = "camelCase")]
305pub struct NoteReference {
306 pub kind: NoteReferenceKind,
307 pub id: i64,
308}
309
310#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
312pub enum NoteReferenceKind {
313 #[serde(rename = "footnote")]
314 Footnote,
315 #[serde(rename = "endnote")]
316 Endnote,
317}
318
319#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct TextRunNode {
323 #[serde(default = "default_text_run_type", rename = "type")]
324 pub r#type: TextRunNodeType,
325 pub text: String,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub style: Option<SharedTextStyle>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub link: Option<String>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub note_reference: Option<NoteReference>,
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
336pub enum TextRunNodeType {
337 #[serde(rename = "text")]
338 Text,
339}
340
341#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
343#[serde(rename_all = "camelCase")]
344pub struct ImageCrop {
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub left_fraction: Option<f64>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub top_fraction: Option<f64>,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub right_fraction: Option<f64>,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub bottom_fraction: Option<f64>,
353}
354
355#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub enum ImageHorizontalAlign {
359 Left,
360 Center,
361 Right,
362 Inside,
363 Outside,
364}
365
366#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
368#[serde(rename_all = "camelCase")]
369pub enum ImageVerticalAlign {
370 Top,
371 Center,
372 Bottom,
373 Inside,
374 Outside,
375}
376
377#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub enum ImageWrapType {
381 #[serde(rename = "none")]
382 WrapNone,
383 #[serde(rename = "square")]
384 Square,
385 #[serde(rename = "tight")]
386 Tight,
387 #[serde(rename = "through")]
388 Through,
389 #[serde(rename = "topAndBottom")]
390 TopAndBottom,
391}
392
393#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
395#[serde(rename_all = "camelCase")]
396pub enum ImageWrapText {
397 #[serde(rename = "bothSides")]
398 BothSides,
399 #[serde(rename = "left")]
400 Left,
401 #[serde(rename = "right")]
402 Right,
403 #[serde(rename = "largest")]
404 Largest,
405}
406
407#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
409#[serde(rename_all = "camelCase")]
410pub struct ImageFloating {
411 #[serde(skip_serializing_if = "Option::is_none")]
412 pub x_px: Option<f64>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub y_px: Option<f64>,
415 #[serde(skip_serializing_if = "Option::is_none")]
416 pub horizontal_align: Option<ImageHorizontalAlign>,
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub vertical_align: Option<ImageVerticalAlign>,
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub horizontal_relative_to: Option<String>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub vertical_relative_to: Option<String>,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub dist_l_px: Option<f64>,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 pub dist_r_px: Option<f64>,
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub dist_t_px: Option<f64>,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 pub dist_b_px: Option<f64>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub wrap_type: Option<ImageWrapType>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub wrap_text: Option<ImageWrapText>,
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub behind_document: Option<bool>,
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub z_index: Option<i64>,
439}
440
441#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
442#[serde(rename_all = "camelCase")]
443pub struct ImagePaintBounds {
444 pub x_px: f64,
445 pub y_px: f64,
446 pub width_px: f64,
447 pub height_px: f64,
448 pub layout_width_px: f64,
449 pub layout_height_px: f64,
450}
451
452#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct ImagePropertyAttribute {
455 pub name: String,
456 pub value: String,
457}
458
459#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct ImagePropertyNode {
463 pub name: String,
464 #[serde(default, skip_serializing_if = "Vec::is_empty")]
465 pub attributes: Vec<ImagePropertyAttribute>,
466 #[serde(default, skip_serializing_if = "Vec::is_empty")]
467 pub children: Vec<ImagePropertyNode>,
468 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub text: Option<String>,
470}
471
472#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
473#[serde(rename_all = "camelCase")]
474pub struct ImageAssetReference {
475 pub element: String,
476 pub kind: String,
477 #[serde(skip_serializing_if = "Option::is_none")]
478 pub relationship_id: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub target: Option<String>,
481 pub embedded_asset_available: bool,
482}
483
484#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
485#[serde(rename_all = "camelCase")]
486pub struct ImageTransform {
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub rotation_degrees: Option<f64>,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub flip_horizontal: Option<bool>,
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub flip_vertical: Option<bool>,
493 #[serde(skip_serializing_if = "Option::is_none")]
494 pub offset_x_emu: Option<f64>,
495 #[serde(skip_serializing_if = "Option::is_none")]
496 pub offset_y_emu: Option<f64>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub width_emu: Option<f64>,
499 #[serde(skip_serializing_if = "Option::is_none")]
500 pub height_emu: Option<f64>,
501}
502
503#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
504#[serde(rename_all = "camelCase")]
505pub struct ImageTile {
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub offset_x_emu: Option<f64>,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub offset_y_emu: Option<f64>,
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub scale_x: Option<f64>,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub scale_y: Option<f64>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub alignment: Option<String>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub flip: Option<String>,
518}
519
520#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
522#[serde(rename_all = "camelCase")]
523pub struct ImagePresentation {
524 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
525 pub transform_applied: bool,
526 #[serde(default, skip_serializing_if = "Vec::is_empty")]
527 pub assets: Vec<ImageAssetReference>,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub transform: Option<ImageTransform>,
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub source_rectangle: Option<ImageCrop>,
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub fill_rectangle: Option<ImageCrop>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub tile: Option<ImageTile>,
536 #[serde(default, skip_serializing_if = "Vec::is_empty")]
537 pub properties: Vec<ImagePropertyNode>,
538}
539
540#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
542#[serde(rename_all = "camelCase")]
543pub struct ImageRunNode {
544 #[serde(default, skip_serializing_if = "Option::is_none")]
545 pub presentation: Option<Box<ImagePresentation>>,
546 #[serde(default, skip_serializing_if = "Option::is_none")]
547 pub chart_part_name: Option<String>,
548 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub chart_data: Option<Box<crate::ChartData>>,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
551 pub paint_bounds: Option<Box<ImagePaintBounds>>,
552 #[serde(default, skip_serializing_if = "Option::is_none")]
553 pub math_ml: Option<String>,
554 #[serde(default, skip_serializing_if = "Option::is_none")]
555 pub content_nodes: Option<Vec<DocNode>>,
556 #[serde(default, skip_serializing_if = "Option::is_none")]
557 pub semantic_kind: Option<String>,
558 #[serde(default, skip_serializing_if = "Option::is_none")]
559 pub block_id: Option<String>,
560 #[serde(default = "default_image_run_type", rename = "type")]
561 pub r#type: ImageRunNodeType,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub src: Option<String>,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 pub alt: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub width_px: Option<f64>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub height_px: Option<f64>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub part_name: Option<String>,
572 #[serde(skip_serializing_if = "Option::is_none")]
573 pub content_type: Option<String>,
574 #[serde(default, skip_serializing_if = "Option::is_none")]
575 pub data: Option<Vec<u8>>,
576 #[serde(skip_serializing_if = "Option::is_none")]
577 pub source_xml: Option<String>,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub crop: Option<ImageCrop>,
580 #[serde(skip_serializing_if = "Option::is_none")]
581 pub css_filter: Option<String>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub css_opacity: Option<f64>,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub floating: Option<ImageFloating>,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub synthetic_text_box: Option<bool>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub text_box_text: Option<String>,
590}
591
592#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
594pub enum ImageRunNodeType {
595 #[serde(rename = "image")]
596 Image,
597}
598
599#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
601#[serde(rename_all = "camelCase")]
602pub enum FormFieldType {
603 #[serde(rename = "checkbox")]
604 Checkbox,
605 #[serde(rename = "text")]
606 Text,
607 #[serde(rename = "date")]
608 Date,
609 #[serde(rename = "dropdown")]
610 Dropdown,
611}
612
613#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
615pub enum FormFieldSourceKind {
616 #[serde(rename = "sdt")]
617 Sdt,
618 #[serde(rename = "legacy")]
619 Legacy,
620}
621
622#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
624#[serde(rename_all = "camelCase")]
625pub struct FormFieldOption {
626 pub display_text: String,
627 #[serde(skip_serializing_if = "Option::is_none")]
628 pub value: Option<String>,
629}
630
631#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct FormFieldTextWidgetSettings {
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub input_type: Option<String>,
637 #[serde(skip_serializing_if = "Option::is_none")]
638 pub default_text: Option<String>,
639 #[serde(skip_serializing_if = "Option::is_none")]
640 pub max_length: Option<i64>,
641 #[serde(skip_serializing_if = "Option::is_none")]
642 pub text_format: Option<String>,
643}
644
645#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
647#[serde(rename_all = "camelCase")]
648pub struct FormFieldCheckboxWidgetSettings {
649 #[serde(skip_serializing_if = "Option::is_none")]
650 pub default_checked: Option<bool>,
651 #[serde(skip_serializing_if = "Option::is_none")]
652 pub size_mode: Option<FormFieldCheckboxSizeMode>,
653 #[serde(skip_serializing_if = "Option::is_none")]
654 pub size_pt: Option<f64>,
655}
656
657#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
659pub enum FormFieldCheckboxSizeMode {
660 #[serde(rename = "auto")]
661 Auto,
662 #[serde(rename = "exact")]
663 Exact,
664}
665
666#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
668#[serde(rename_all = "camelCase")]
669pub struct FormFieldDropdownWidgetSettings {
670 #[serde(skip_serializing_if = "Option::is_none")]
671 pub default_value: Option<String>,
672}
673
674#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
676#[serde(rename_all = "camelCase")]
677pub struct FormFieldWidgetSettings {
678 #[serde(skip_serializing_if = "Option::is_none")]
679 pub name: Option<String>,
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub enabled: Option<bool>,
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub calc_on_exit: Option<bool>,
684 #[serde(skip_serializing_if = "Option::is_none")]
685 pub text: Option<FormFieldTextWidgetSettings>,
686 #[serde(skip_serializing_if = "Option::is_none")]
687 pub checkbox: Option<FormFieldCheckboxWidgetSettings>,
688 #[serde(skip_serializing_if = "Option::is_none")]
689 pub dropdown: Option<FormFieldDropdownWidgetSettings>,
690}
691
692#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
693#[serde(rename_all = "camelCase")]
694pub struct FormFieldSourcePreview {
695 pub relationship_id: String,
696 pub shape_id: String,
697 pub checked: bool,
698 pub image: Box<ImageRunNode>,
699}
700
701#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
703#[serde(rename_all = "camelCase")]
704pub struct FormFieldRunNode {
705 #[serde(default = "default_form_field_run_type", rename = "type")]
706 pub r#type: FormFieldRunNodeType,
707 pub field_type: FormFieldType,
708 #[serde(skip_serializing_if = "Option::is_none")]
709 pub source_kind: Option<FormFieldSourceKind>,
710 #[serde(skip_serializing_if = "Option::is_none")]
711 pub id: Option<i64>,
712 #[serde(skip_serializing_if = "Option::is_none")]
713 pub tag: Option<String>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub title: Option<String>,
716 #[serde(skip_serializing_if = "Option::is_none")]
717 pub placeholder: Option<String>,
718 #[serde(skip_serializing_if = "Option::is_none")]
719 pub checked: Option<bool>,
720 #[serde(skip_serializing_if = "Option::is_none")]
721 pub value: Option<String>,
722 #[serde(skip_serializing_if = "Option::is_none")]
723 pub options: Option<Vec<FormFieldOption>>,
724 #[serde(skip_serializing_if = "Option::is_none")]
725 pub widget: Option<FormFieldWidgetSettings>,
726 #[serde(skip_serializing_if = "Option::is_none")]
727 pub checked_symbol: Option<String>,
728 #[serde(skip_serializing_if = "Option::is_none")]
729 pub unchecked_symbol: Option<String>,
730 #[serde(skip_serializing_if = "Option::is_none")]
731 pub style: Option<SharedTextStyle>,
732 #[serde(skip_serializing_if = "Option::is_none")]
733 pub link: Option<String>,
734 #[serde(skip_serializing_if = "Option::is_none")]
735 pub source_xml: Option<String>,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
737 pub source_preview: Option<Box<FormFieldSourcePreview>>,
738}
739
740#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
742pub enum FormFieldRunNodeType {
743 #[serde(rename = "form-field")]
744 FormField,
745}
746
747#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
749#[serde(untagged)]
750pub enum ParagraphChildNode {
751 Text(TextRunNode),
752 Image(Box<ImageRunNode>),
753 FormField(Box<FormFieldRunNode>),
754}
755
756#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
758#[serde(rename_all = "camelCase")]
759pub struct ParagraphNumbering {
760 pub num_id: i64,
761 pub ilvl: i64,
762}
763
764#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
766pub enum ParagraphLineRule {
767 #[serde(rename = "auto")]
768 Auto,
769 #[serde(rename = "exact")]
770 Exact,
771 #[serde(rename = "atLeast")]
772 AtLeast,
773}
774
775#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
777#[serde(rename_all = "camelCase")]
778pub struct ParagraphSpacing {
779 #[serde(skip_serializing_if = "Option::is_none")]
780 pub before_twips: Option<i64>,
781 #[serde(skip_serializing_if = "Option::is_none")]
782 pub after_twips: Option<i64>,
783 #[serde(skip_serializing_if = "Option::is_none")]
784 pub before_auto_spacing: Option<bool>,
785 #[serde(skip_serializing_if = "Option::is_none")]
786 pub after_auto_spacing: Option<bool>,
787 #[serde(skip_serializing_if = "Option::is_none")]
788 pub line_twips: Option<i64>,
789 #[serde(skip_serializing_if = "Option::is_none")]
790 pub line_rule: Option<ParagraphLineRule>,
791}
792
793#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
795#[serde(rename_all = "camelCase")]
796pub struct ParagraphIndent {
797 #[serde(skip_serializing_if = "Option::is_none")]
798 pub left_twips: Option<i64>,
799 #[serde(skip_serializing_if = "Option::is_none")]
800 pub right_twips: Option<i64>,
801 #[serde(skip_serializing_if = "Option::is_none")]
802 pub first_line_twips: Option<i64>,
803 #[serde(skip_serializing_if = "Option::is_none")]
804 pub hanging_twips: Option<i64>,
805}
806
807#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
809#[serde(rename_all = "camelCase")]
810pub struct ParagraphBorderStyle {
811 #[serde(rename = "type")]
812 pub border_type: String,
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub color: Option<String>,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub size_eighth_pt: Option<f64>,
817 #[serde(skip_serializing_if = "Option::is_none")]
818 pub space_pt: Option<f64>,
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub frame: Option<bool>,
821 #[serde(skip_serializing_if = "Option::is_none")]
822 pub shadow: Option<bool>,
823}
824
825#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
827#[serde(rename_all = "camelCase")]
828pub struct ParagraphBorderSet {
829 #[serde(skip_serializing_if = "Option::is_none")]
830 pub top: Option<ParagraphBorderStyle>,
831 #[serde(skip_serializing_if = "Option::is_none")]
832 pub right: Option<ParagraphBorderStyle>,
833 #[serde(skip_serializing_if = "Option::is_none")]
834 pub bottom: Option<ParagraphBorderStyle>,
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub left: Option<ParagraphBorderStyle>,
837 #[serde(skip_serializing_if = "Option::is_none")]
838 pub between: Option<ParagraphBorderStyle>,
839 #[serde(skip_serializing_if = "Option::is_none")]
840 pub bar: Option<ParagraphBorderStyle>,
841}
842
843#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
845#[serde(rename_all = "camelCase")]
846pub enum ParagraphTabStopAlignment {
847 Clear,
848 Left,
849 Center,
850 Right,
851 Decimal,
852 Bar,
853}
854
855#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
857pub enum ParagraphTabStopLeader {
858 #[serde(rename = "none")]
859 LeaderNone,
860 #[serde(rename = "dot")]
861 Dot,
862 #[serde(rename = "hyphen")]
863 Hyphen,
864 #[serde(rename = "underscore")]
865 Underscore,
866 #[serde(rename = "middleDot")]
867 MiddleDot,
868}
869
870#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
872#[serde(rename_all = "camelCase")]
873pub struct ParagraphTabStop {
874 #[serde(skip_serializing_if = "Option::is_none")]
875 pub alignment: Option<ParagraphTabStopAlignment>,
876 #[serde(skip_serializing_if = "Option::is_none")]
877 pub leader: Option<ParagraphTabStopLeader>,
878 #[serde(skip_serializing_if = "Option::is_none")]
879 pub position_twips: Option<i64>,
880}
881
882#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
884pub enum ParagraphDropCapType {
885 #[serde(rename = "drop")]
886 Drop,
887 #[serde(rename = "margin")]
888 Margin,
889}
890
891#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
893#[serde(rename_all = "camelCase")]
894pub struct ParagraphDropCap {
895 #[serde(rename = "type")]
896 pub drop_cap_type: ParagraphDropCapType,
897 #[serde(skip_serializing_if = "Option::is_none")]
898 pub lines: Option<i64>,
899 #[serde(skip_serializing_if = "Option::is_none")]
900 pub wrap: Option<String>,
901 #[serde(skip_serializing_if = "Option::is_none")]
902 pub horizontal_anchor: Option<String>,
903 #[serde(skip_serializing_if = "Option::is_none")]
904 pub vertical_anchor: Option<String>,
905 #[serde(skip_serializing_if = "Option::is_none")]
906 pub x_twips: Option<i64>,
907 #[serde(skip_serializing_if = "Option::is_none")]
908 pub y_twips: Option<i64>,
909 #[serde(skip_serializing_if = "Option::is_none")]
910 pub horizontal_space_twips: Option<i64>,
911 #[serde(skip_serializing_if = "Option::is_none")]
912 pub vertical_space_twips: Option<i64>,
913}
914
915#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
916#[serde(rename_all = "camelCase")]
917pub enum ParagraphTextAlignment {
918 Auto,
919 Top,
920 Center,
921 Baseline,
922 Bottom,
923}
924
925#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
927#[serde(rename_all = "camelCase")]
928pub struct ParagraphStyle {
929 #[serde(skip_serializing_if = "Option::is_none")]
930 pub source_before_auto_spacing: Option<bool>,
931 #[serde(skip_serializing_if = "Option::is_none")]
932 pub source_after_auto_spacing: Option<bool>,
933 #[serde(skip_serializing_if = "Option::is_none")]
934 pub align: Option<ParagraphAlignment>,
935 #[serde(skip_serializing_if = "Option::is_none")]
936 pub snap_to_grid: Option<bool>,
937 #[serde(skip_serializing_if = "Option::is_none")]
938 pub source_snap_to_grid: Option<bool>,
939 #[serde(skip_serializing_if = "Option::is_none")]
940 pub source_has_snap_to_grid: Option<bool>,
941 #[serde(skip_serializing_if = "Option::is_none")]
942 pub source_inherited_snap_to_grid: Option<bool>,
943 #[serde(skip_serializing_if = "Option::is_none")]
944 pub source_table_snap_to_grid: Option<bool>,
945 #[serde(skip_serializing_if = "Option::is_none")]
946 pub text_alignment: Option<ParagraphTextAlignment>,
947 #[serde(skip_serializing_if = "Option::is_none")]
948 pub source_text_alignment: Option<ParagraphTextAlignment>,
949 #[serde(skip_serializing_if = "Option::is_none")]
950 pub source_has_text_alignment: Option<bool>,
951 #[serde(skip_serializing_if = "Option::is_none")]
952 pub source_inherited_text_alignment: Option<ParagraphTextAlignment>,
953 #[serde(skip_serializing_if = "Option::is_none")]
954 pub source_table_text_alignment: Option<ParagraphTextAlignment>,
955 #[serde(skip_serializing_if = "Option::is_none")]
956 pub heading_level: Option<HeadingLevel>,
957 #[serde(skip_serializing_if = "Option::is_none")]
958 pub style_id: Option<String>,
959 #[serde(skip_serializing_if = "Option::is_none")]
960 pub style_name: Option<String>,
961 #[serde(skip_serializing_if = "Option::is_none")]
962 pub numbering: Option<ParagraphNumbering>,
963 #[serde(skip_serializing_if = "Option::is_none")]
964 pub spacing: Option<ParagraphSpacing>,
965 #[serde(skip_serializing_if = "Option::is_none")]
966 pub indent: Option<ParagraphIndent>,
967 #[serde(skip_serializing_if = "Option::is_none")]
968 pub background_color: Option<String>,
969 #[serde(skip_serializing_if = "Option::is_none")]
970 pub borders: Option<ParagraphBorderSet>,
971 #[serde(skip_serializing_if = "Option::is_none")]
972 pub tab_stops: Option<Vec<ParagraphTabStop>>,
973 #[serde(skip_serializing_if = "Option::is_none")]
974 pub contextual_spacing: Option<bool>,
975 #[serde(skip_serializing_if = "Option::is_none")]
976 pub keep_next: Option<bool>,
977 #[serde(skip_serializing_if = "Option::is_none")]
978 pub keep_lines: Option<bool>,
979 #[serde(skip_serializing_if = "Option::is_none")]
980 pub widow_control: Option<bool>,
981 #[serde(skip_serializing_if = "Option::is_none")]
982 pub page_break_before: Option<bool>,
983 #[serde(skip_serializing_if = "Option::is_none")]
984 pub drop_cap: Option<ParagraphDropCap>,
985}
986
987#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
989#[serde(rename_all = "camelCase")]
990pub struct ParagraphSourceTextPatchRun {
991 #[serde(skip_serializing_if = "Option::is_none")]
992 pub style: Option<SharedTextStyle>,
993 #[serde(skip_serializing_if = "Option::is_none")]
994 pub link: Option<String>,
995 #[serde(skip_serializing_if = "Option::is_none")]
996 pub note_reference: Option<NoteReference>,
997}
998
999#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1000#[serde(rename_all = "camelCase")]
1001pub struct ParagraphSourceTextPatch {
1002 pub runs: Vec<ParagraphSourceTextPatchRun>,
1003}
1004
1005#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1007#[serde(rename_all = "camelCase")]
1008pub struct ParagraphMarkFormattingSource {
1009 #[serde(skip_serializing_if = "Option::is_none")]
1010 pub default_style: Option<SharedTextStyle>,
1011 #[serde(skip_serializing_if = "Option::is_none")]
1012 pub character_style: Option<SharedTextStyle>,
1013 #[serde(skip_serializing_if = "Option::is_none")]
1014 pub direct_style: Option<SharedTextStyle>,
1015}
1016
1017#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1019#[serde(rename_all = "camelCase")]
1020pub struct ParagraphNode {
1021 #[serde(default, skip_serializing_if = "Option::is_none")]
1022 pub block_id: Option<String>,
1023 #[serde(default = "default_paragraph_node_type", rename = "type")]
1024 pub r#type: ParagraphNodeType,
1025 pub children: Vec<ParagraphChildNode>,
1026 #[serde(skip_serializing_if = "Option::is_none")]
1027 pub style: Option<SharedParagraphStyle>,
1028 #[serde(skip_serializing_if = "Option::is_none")]
1029 pub paragraph_mark_style: Option<SharedTextStyle>,
1030 #[serde(skip_serializing_if = "Option::is_none")]
1031 pub source_paragraph_mark_style: Option<SharedTextStyle>,
1032 #[serde(skip_serializing_if = "Option::is_none")]
1033 pub source_paragraph_mark_formatting: Option<ParagraphMarkFormattingSource>,
1034 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub source_paragraph_mark_properties_xml: Option<String>,
1036 #[serde(skip_serializing_if = "Option::is_none")]
1037 pub paragraph_mark_deleted: Option<bool>,
1038 #[serde(skip_serializing_if = "Option::is_none")]
1039 pub source_xml: Option<String>,
1040 #[serde(skip_serializing_if = "Option::is_none")]
1041 pub source_text_patch: Option<ParagraphSourceTextPatch>,
1042 #[serde(skip_serializing_if = "Option::is_none")]
1045 pub source_run_provenance: Option<ParagraphSourceTextPatch>,
1046}
1047
1048#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1050pub enum ParagraphNodeType {
1051 #[serde(rename = "paragraph")]
1052 Paragraph,
1053}
1054
1055#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1057#[serde(rename_all = "camelCase")]
1058pub struct TableBoxSpacing {
1059 #[serde(skip_serializing_if = "Option::is_none")]
1060 pub top_twips: Option<i64>,
1061 #[serde(skip_serializing_if = "Option::is_none")]
1062 pub right_twips: Option<i64>,
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 pub bottom_twips: Option<i64>,
1065 #[serde(skip_serializing_if = "Option::is_none")]
1066 pub left_twips: Option<i64>,
1067}
1068
1069#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1071#[serde(rename_all = "camelCase")]
1072pub struct TableBorderStyle {
1073 #[serde(rename = "type")]
1074 pub border_type: String,
1075 #[serde(skip_serializing_if = "Option::is_none")]
1076 pub color: Option<String>,
1077 #[serde(skip_serializing_if = "Option::is_none")]
1078 pub size_eighth_pt: Option<f64>,
1079}
1080
1081#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1083#[serde(rename_all = "camelCase")]
1084pub struct TableBorderSet {
1085 #[serde(skip_serializing_if = "Option::is_none")]
1086 pub top: Option<TableBorderStyle>,
1087 #[serde(skip_serializing_if = "Option::is_none")]
1088 pub right: Option<TableBorderStyle>,
1089 #[serde(skip_serializing_if = "Option::is_none")]
1090 pub bottom: Option<TableBorderStyle>,
1091 #[serde(skip_serializing_if = "Option::is_none")]
1092 pub left: Option<TableBorderStyle>,
1093 #[serde(skip_serializing_if = "Option::is_none")]
1094 pub inside_h: Option<TableBorderStyle>,
1095 #[serde(skip_serializing_if = "Option::is_none")]
1096 pub inside_v: Option<TableBorderStyle>,
1097 #[serde(skip_serializing_if = "Option::is_none")]
1098 pub tl2br: Option<TableBorderStyle>,
1099 #[serde(skip_serializing_if = "Option::is_none")]
1100 pub tr2bl: Option<TableBorderStyle>,
1101}
1102
1103#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1105#[serde(rename_all = "camelCase")]
1106pub enum TableCellVerticalAlign {
1107 Top,
1108 Center,
1109 Bottom,
1110}
1111
1112#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1114#[serde(tag = "type", rename_all = "camelCase")]
1115pub enum TablePreferredWidth {
1116 Dxa { value: f64 },
1117 Pct { value: f64 },
1118 Auto,
1119 Nil,
1120}
1121
1122#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1124#[serde(rename_all = "camelCase")]
1125pub struct TableWidthSource {
1126 #[serde(skip_serializing_if = "Option::is_none")]
1127 pub preferred_width: Option<TablePreferredWidth>,
1128 #[serde(skip_serializing_if = "Option::is_none")]
1129 pub width_twips: Option<i64>,
1130 #[serde(skip_serializing_if = "Option::is_none")]
1131 pub inherited_preferred_width: Option<TablePreferredWidth>,
1132}
1133
1134#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1135#[serde(rename_all = "camelCase")]
1136pub enum TableAlignment {
1137 Left,
1138 Center,
1139 Right,
1140}
1141
1142#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1144#[serde(rename_all = "camelCase")]
1145pub struct TableCellStyle {
1146 #[serde(skip_serializing_if = "Option::is_none")]
1147 pub background_color: Option<String>,
1148 #[serde(skip_serializing_if = "Option::is_none")]
1149 pub grid_span: Option<i64>,
1150 #[serde(skip_serializing_if = "Option::is_none")]
1151 pub row_span: Option<i64>,
1152 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub v_merge_continuation: Option<bool>,
1154 #[serde(skip_serializing_if = "Option::is_none")]
1155 pub width_twips: Option<i64>,
1156 #[serde(skip_serializing_if = "Option::is_none")]
1157 pub preferred_width: Option<TablePreferredWidth>,
1158 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub source_width: Option<TableWidthSource>,
1160 #[serde(skip_serializing_if = "Option::is_none")]
1161 pub margin_twips: Option<TableBoxSpacing>,
1162 #[serde(skip_serializing_if = "Option::is_none")]
1163 pub vertical_align: Option<TableCellVerticalAlign>,
1164 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub text_direction: Option<String>,
1166 #[serde(skip_serializing_if = "Option::is_none")]
1167 pub source_text_direction: Option<String>,
1168 #[serde(skip_serializing_if = "Option::is_none")]
1169 pub no_wrap: Option<bool>,
1170 #[serde(skip_serializing_if = "Option::is_none")]
1171 pub source_no_wrap: Option<bool>,
1172 #[serde(skip_serializing_if = "Option::is_none")]
1173 pub borders: Option<TableBorderSet>,
1174}
1175
1176#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1178#[serde(untagged)]
1179pub enum TableCellContentNode {
1180 Paragraph(ParagraphNode),
1181 Table(Box<TableNode>),
1182}
1183
1184#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1186#[serde(rename_all = "camelCase")]
1187pub struct TableCellNode {
1188 #[serde(default = "default_table_cell_node_type", rename = "type")]
1189 pub r#type: TableCellNodeType,
1190 pub nodes: Vec<TableCellContentNode>,
1191 #[serde(skip_serializing_if = "Option::is_none")]
1192 pub style: Option<TableCellStyle>,
1193}
1194
1195#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1197pub enum TableCellNodeType {
1198 #[serde(rename = "table-cell")]
1199 TableCell,
1200}
1201
1202#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1204pub enum TableRowHeightRule {
1205 #[serde(rename = "auto")]
1206 Auto,
1207 #[serde(rename = "atLeast")]
1208 AtLeast,
1209 #[serde(rename = "exact")]
1210 Exact,
1211}
1212
1213#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1215#[serde(rename_all = "camelCase")]
1216pub struct TableRowGeometrySource {
1217 #[serde(skip_serializing_if = "Option::is_none")]
1218 pub grid_before: Option<i64>,
1219 #[serde(skip_serializing_if = "Option::is_none")]
1220 pub grid_after: Option<i64>,
1221 #[serde(skip_serializing_if = "Option::is_none")]
1222 pub width_before: Option<TablePreferredWidth>,
1223 #[serde(skip_serializing_if = "Option::is_none")]
1224 pub width_after: Option<TablePreferredWidth>,
1225}
1226
1227#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1229#[serde(rename_all = "camelCase")]
1230pub struct TableRowStyle {
1231 #[serde(skip_serializing_if = "Option::is_none")]
1232 pub grid_before: Option<i64>,
1233 #[serde(skip_serializing_if = "Option::is_none")]
1234 pub grid_after: Option<i64>,
1235 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub width_before: Option<TablePreferredWidth>,
1237 #[serde(skip_serializing_if = "Option::is_none")]
1238 pub width_after: Option<TablePreferredWidth>,
1239 #[serde(skip_serializing_if = "Option::is_none")]
1240 pub source_row_geometry: Option<TableRowGeometrySource>,
1241 #[serde(skip_serializing_if = "Option::is_none")]
1242 pub background_color: Option<String>,
1243 #[serde(skip_serializing_if = "Option::is_none")]
1244 pub height_twips: Option<i64>,
1245 #[serde(skip_serializing_if = "Option::is_none")]
1246 pub height_rule: Option<TableRowHeightRule>,
1247 #[serde(skip_serializing_if = "Option::is_none")]
1248 pub cant_split: Option<bool>,
1249 #[serde(skip_serializing_if = "Option::is_none")]
1250 pub is_header: Option<bool>,
1251}
1252
1253#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1255#[serde(rename_all = "camelCase")]
1256pub struct TableRowNode {
1257 #[serde(default = "default_table_row_node_type", rename = "type")]
1258 pub r#type: TableRowNodeType,
1259 pub cells: Vec<TableCellNode>,
1260 #[serde(skip_serializing_if = "Option::is_none")]
1261 pub style: Option<TableRowStyle>,
1262}
1263
1264#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1266pub enum TableRowNodeType {
1267 #[serde(rename = "table-row")]
1268 TableRow,
1269}
1270
1271#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1273pub enum TableLayout {
1274 #[serde(rename = "fixed")]
1275 Fixed,
1276 #[serde(rename = "autofit")]
1277 Autofit,
1278}
1279
1280#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1282#[serde(rename_all = "camelCase")]
1283pub struct TableFloating {
1284 #[serde(skip_serializing_if = "Option::is_none")]
1285 pub x_twips: Option<i64>,
1286 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub y_twips: Option<i64>,
1288 #[serde(skip_serializing_if = "Option::is_none")]
1289 pub left_from_text_twips: Option<i64>,
1290 #[serde(skip_serializing_if = "Option::is_none")]
1291 pub right_from_text_twips: Option<i64>,
1292 #[serde(skip_serializing_if = "Option::is_none")]
1293 pub top_from_text_twips: Option<i64>,
1294 #[serde(skip_serializing_if = "Option::is_none")]
1295 pub bottom_from_text_twips: Option<i64>,
1296 #[serde(skip_serializing_if = "Option::is_none")]
1297 pub horizontal_anchor: Option<String>,
1298 #[serde(skip_serializing_if = "Option::is_none")]
1299 pub vertical_anchor: Option<String>,
1300 #[serde(skip_serializing_if = "Option::is_none")]
1301 pub horizontal_align: Option<ImageHorizontalAlign>,
1302 #[serde(skip_serializing_if = "Option::is_none")]
1303 pub vertical_align: Option<ImageVerticalAlign>,
1304}
1305
1306#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1308#[serde(rename_all = "camelCase")]
1309pub struct TableStyle {
1310 #[serde(skip_serializing_if = "Option::is_none")]
1311 pub style_id: Option<String>,
1312 #[serde(skip_serializing_if = "Option::is_none")]
1313 pub source_style_id: Option<String>,
1314 #[serde(skip_serializing_if = "Option::is_none")]
1315 pub width_twips: Option<i64>,
1316 #[serde(skip_serializing_if = "Option::is_none")]
1317 pub preferred_width: Option<TablePreferredWidth>,
1318 #[serde(skip_serializing_if = "Option::is_none")]
1319 pub source_width: Option<TableWidthSource>,
1320 #[serde(skip_serializing_if = "Option::is_none")]
1321 pub alignment: Option<TableAlignment>,
1322 #[serde(skip_serializing_if = "Option::is_none")]
1323 pub source_alignment: Option<TableAlignment>,
1324 #[serde(skip_serializing_if = "Option::is_none")]
1325 pub source_inherited_alignment: Option<TableAlignment>,
1326 #[serde(skip_serializing_if = "Option::is_none")]
1327 pub bidi_visual: Option<bool>,
1328 #[serde(skip_serializing_if = "Option::is_none")]
1329 pub source_bidi_visual: Option<bool>,
1330 #[serde(skip_serializing_if = "Option::is_none")]
1331 pub source_inherited_bidi_visual: Option<bool>,
1332 #[serde(skip_serializing_if = "Option::is_none")]
1333 pub indent_twips: Option<i64>,
1334 #[serde(skip_serializing_if = "Option::is_none")]
1335 pub layout: Option<TableLayout>,
1336 #[serde(skip_serializing_if = "Option::is_none")]
1337 pub cell_spacing_twips: Option<i64>,
1338 #[serde(skip_serializing_if = "Option::is_none")]
1339 pub cell_margin_twips: Option<TableBoxSpacing>,
1340 #[serde(skip_serializing_if = "Option::is_none")]
1341 pub column_widths_twips: Option<Vec<i64>>,
1342 #[serde(skip_serializing_if = "Option::is_none")]
1345 pub collapsed_grid_columns: Option<Vec<usize>>,
1346 #[serde(skip_serializing_if = "Option::is_none")]
1347 pub borders: Option<TableBorderSet>,
1348 #[serde(skip_serializing_if = "Option::is_none")]
1349 pub floating: Option<TableFloating>,
1350}
1351
1352#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1354#[serde(rename_all = "camelCase")]
1355pub struct TableSourceTextPatch {
1356 pub source_paragraph_xml: String,
1357 pub paragraph: ParagraphNode,
1358}
1359
1360#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1362#[serde(rename_all = "camelCase")]
1363pub struct TableNode {
1364 #[serde(default, skip_serializing_if = "Option::is_none")]
1365 pub block_id: Option<String>,
1366 #[serde(default = "default_table_node_type", rename = "type")]
1367 pub r#type: TableNodeType,
1368 pub rows: Vec<TableRowNode>,
1369 #[serde(skip_serializing_if = "Option::is_none")]
1370 pub style: Option<TableStyle>,
1371 #[serde(skip_serializing_if = "Option::is_none")]
1372 pub source_xml: Option<String>,
1373 #[serde(skip_serializing_if = "Option::is_none")]
1374 pub source_text_patches: Option<Vec<TableSourceTextPatch>>,
1375}
1376
1377#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1379pub enum TableNodeType {
1380 #[serde(rename = "table")]
1381 Table,
1382}
1383
1384#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1386#[serde(untagged)]
1387pub enum DocNode {
1388 Paragraph(ParagraphNode),
1389 Table(TableNode),
1390}
1391
1392#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1394#[serde(rename_all = "camelCase")]
1395pub struct HeaderSection {
1396 pub part_name: String,
1397 #[serde(skip_serializing_if = "Option::is_none")]
1398 pub reference_type: Option<String>,
1399 pub nodes: Vec<DocNode>,
1400}
1401
1402#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1404#[serde(rename_all = "camelCase")]
1405pub struct FooterSection {
1406 pub part_name: String,
1407 #[serde(skip_serializing_if = "Option::is_none")]
1408 pub reference_type: Option<String>,
1409 pub nodes: Vec<DocNode>,
1410}
1411
1412#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1414#[serde(rename_all = "camelCase")]
1415pub struct DocumentSection {
1416 pub start_node_index: i64,
1417 #[serde(skip_serializing_if = "Option::is_none")]
1418 pub section_properties_xml: Option<String>,
1419 pub header_sections: Vec<HeaderSection>,
1420 pub footer_sections: Vec<FooterSection>,
1421}
1422
1423#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1425#[serde(rename_all = "camelCase")]
1426pub struct ParagraphStyleDefinition {
1427 pub id: String,
1428 pub name: String,
1429 #[serde(skip_serializing_if = "Option::is_none")]
1430 pub based_on_id: Option<String>,
1431 #[serde(skip_serializing_if = "Option::is_none")]
1432 pub next_style_id: Option<String>,
1433 #[serde(skip_serializing_if = "Option::is_none")]
1434 pub align: Option<ParagraphAlignment>,
1435 #[serde(skip_serializing_if = "Option::is_none")]
1436 pub snap_to_grid: Option<bool>,
1437 #[serde(skip_serializing_if = "Option::is_none")]
1438 pub source_has_snap_to_grid: Option<bool>,
1439 #[serde(skip_serializing_if = "Option::is_none")]
1440 pub text_alignment: Option<ParagraphTextAlignment>,
1441 #[serde(skip_serializing_if = "Option::is_none")]
1442 pub source_has_text_alignment: Option<bool>,
1443 #[serde(skip_serializing_if = "Option::is_none")]
1444 pub heading_level: Option<HeadingLevel>,
1445 #[serde(skip_serializing_if = "Option::is_none")]
1446 pub numbering: Option<ParagraphNumbering>,
1447 #[serde(skip_serializing_if = "Option::is_none")]
1448 pub spacing: Option<ParagraphSpacing>,
1449 #[serde(skip_serializing_if = "Option::is_none")]
1450 pub indent: Option<ParagraphIndent>,
1451 #[serde(skip_serializing_if = "Option::is_none")]
1452 pub background_color: Option<String>,
1453 #[serde(skip_serializing_if = "Option::is_none")]
1454 pub borders: Option<ParagraphBorderSet>,
1455 #[serde(skip_serializing_if = "Option::is_none")]
1456 pub tab_stops: Option<Vec<ParagraphTabStop>>,
1457 #[serde(skip_serializing_if = "Option::is_none")]
1458 pub contextual_spacing: Option<bool>,
1459 #[serde(skip_serializing_if = "Option::is_none")]
1460 pub keep_next: Option<bool>,
1461 #[serde(skip_serializing_if = "Option::is_none")]
1462 pub keep_lines: Option<bool>,
1463 #[serde(skip_serializing_if = "Option::is_none")]
1464 pub widow_control: Option<bool>,
1465 #[serde(skip_serializing_if = "Option::is_none")]
1466 pub page_break_before: Option<bool>,
1467 #[serde(skip_serializing_if = "Option::is_none")]
1468 pub run_style: Option<TextStyle>,
1469 #[serde(skip_serializing_if = "Option::is_none")]
1470 pub ui_priority: Option<i64>,
1471 #[serde(skip_serializing_if = "Option::is_none")]
1472 pub is_default: Option<bool>,
1473 #[serde(skip_serializing_if = "Option::is_none")]
1474 pub is_primary: Option<bool>,
1475}
1476
1477#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1479pub enum NumberingLevelSuffix {
1480 #[serde(rename = "tab")]
1481 Tab,
1482 #[serde(rename = "space")]
1483 Space,
1484 #[serde(rename = "nothing")]
1485 Nothing,
1486}
1487
1488#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1490#[serde(rename_all = "camelCase")]
1491pub struct NumberingLevelDefinition {
1492 pub ilvl: i64,
1493 #[serde(skip_serializing_if = "Option::is_none")]
1494 pub start: Option<i64>,
1495 #[serde(skip_serializing_if = "Option::is_none")]
1496 pub format: Option<String>,
1497 #[serde(skip_serializing_if = "Option::is_none")]
1498 pub text: Option<String>,
1499 #[serde(skip_serializing_if = "Option::is_none")]
1500 pub suffix: Option<NumberingLevelSuffix>,
1501 #[serde(skip_serializing_if = "Option::is_none")]
1505 pub lvl_restart: Option<i64>,
1506 #[serde(skip_serializing_if = "Option::is_none")]
1509 pub is_lgl: Option<bool>,
1510 #[serde(skip_serializing_if = "Option::is_none")]
1511 pub indent: Option<ParagraphIndent>,
1512 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub run_style: Option<TextStyle>,
1514 #[serde(skip_serializing_if = "Option::is_none")]
1515 pub bullet_font_family: Option<String>,
1516 #[serde(skip_serializing_if = "Option::is_none")]
1517 pub bullet_color: Option<String>,
1518 #[serde(skip_serializing_if = "Option::is_none")]
1519 pub picture_bullet_id: Option<i64>,
1520 #[serde(skip_serializing_if = "Option::is_none")]
1521 pub picture_bullet: Option<NumberingPictureBulletDefinition>,
1522}
1523
1524#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1526#[serde(rename_all = "camelCase")]
1527pub struct NumberingAbstractDefinition {
1528 pub abstract_num_id: i64,
1529 pub levels: Vec<NumberingLevelDefinition>,
1530}
1531
1532#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1534#[serde(rename_all = "camelCase")]
1535pub struct NumberingInstanceDefinition {
1536 pub num_id: i64,
1537 pub abstract_num_id: i64,
1538 #[serde(skip_serializing_if = "Option::is_none")]
1539 pub level_start_overrides: Option<HashMap<String, i64>>,
1540 #[serde(skip_serializing_if = "Option::is_none")]
1541 pub level_overrides: Option<Vec<NumberingLevelDefinition>>,
1542}
1543
1544#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1546#[serde(rename_all = "camelCase")]
1547pub struct NumberingDefinitionSet {
1548 pub abstracts: Vec<NumberingAbstractDefinition>,
1549 pub instances: Vec<NumberingInstanceDefinition>,
1550}
1551
1552#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1554#[serde(rename_all = "camelCase")]
1555pub struct NumberingPictureBulletDefinition {
1556 pub num_pic_bullet_id: i64,
1557 #[serde(skip_serializing_if = "Option::is_none")]
1558 pub src: Option<String>,
1559 #[serde(skip_serializing_if = "Option::is_none")]
1560 pub width_px: Option<f64>,
1561 #[serde(skip_serializing_if = "Option::is_none")]
1562 pub height_px: Option<f64>,
1563 #[serde(skip_serializing_if = "Option::is_none")]
1564 pub part_name: Option<String>,
1565 #[serde(skip_serializing_if = "Option::is_none")]
1566 pub content_type: Option<String>,
1567}
1568
1569#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1571#[serde(rename_all = "camelCase")]
1572pub struct DocumentNoteDefinition {
1573 pub id: i64,
1574 pub text: String,
1575 #[serde(skip_serializing_if = "Option::is_none")]
1576 pub nodes: Option<Vec<DocNode>>,
1577}
1578
1579#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1581#[serde(rename_all = "camelCase")]
1582pub struct DocumentCommentDefinition {
1583 #[serde(default, skip_serializing_if = "Option::is_none")]
1584 pub nodes: Option<Vec<DocNode>>,
1585 pub id: i64,
1586 #[serde(skip_serializing_if = "Option::is_none")]
1587 pub author: Option<String>,
1588 #[serde(skip_serializing_if = "Option::is_none")]
1589 pub initials: Option<String>,
1590 #[serde(skip_serializing_if = "Option::is_none")]
1591 pub date: Option<String>,
1592 pub text: String,
1593 #[serde(skip_serializing_if = "Option::is_none")]
1594 pub parent_id: Option<i64>,
1595 #[serde(skip_serializing_if = "Option::is_none")]
1596 pub resolved: Option<bool>,
1597 #[serde(skip_serializing_if = "Option::is_none")]
1598 pub source_xml: Option<String>,
1599 #[serde(skip_serializing_if = "Option::is_none")]
1600 pub extended_paragraph_id: Option<String>,
1601 #[serde(skip_serializing_if = "Option::is_none")]
1602 pub source_resolved: Option<bool>,
1603 #[serde(skip_serializing_if = "Option::is_none")]
1604 pub resolution_dirty: Option<bool>,
1605 #[serde(skip_serializing_if = "Option::is_none")]
1606 pub is_new: Option<bool>,
1607}
1608
1609#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1611#[serde(rename_all = "camelCase")]
1612pub struct DocumentCompatibilitySettings {
1613 #[serde(skip_serializing_if = "Option::is_none")]
1614 pub adjust_line_height_in_table: Option<bool>,
1615 #[serde(skip_serializing_if = "Option::is_none")]
1616 pub no_leading: Option<bool>,
1617 #[serde(
1618 rename = "suppressTopSpacingWP",
1619 skip_serializing_if = "Option::is_none"
1620 )]
1621 pub suppress_top_spacing_wp: Option<bool>,
1622 #[serde(
1623 rename = "truncateFontHeightsLikeWP6",
1624 skip_serializing_if = "Option::is_none"
1625 )]
1626 pub truncate_font_heights_like_wp6: Option<bool>,
1627 #[serde(skip_serializing_if = "Option::is_none")]
1628 pub no_extra_line_spacing: Option<bool>,
1629 #[serde(rename = "spaceForUL", skip_serializing_if = "Option::is_none")]
1630 pub space_for_ul: Option<bool>,
1631 #[serde(skip_serializing_if = "Option::is_none")]
1632 pub suppress_bottom_spacing: Option<bool>,
1633 #[serde(skip_serializing_if = "Option::is_none")]
1634 pub compatibility_mode: Option<i64>,
1635 #[serde(
1636 rename = "enableOpenTypeFeatures",
1637 skip_serializing_if = "Option::is_none"
1638 )]
1639 pub enable_open_type_features: Option<bool>,
1640 #[serde(skip_serializing_if = "Option::is_none")]
1641 pub suppress_spacing_before_after_page_break: Option<bool>,
1642 #[serde(skip_serializing_if = "Option::is_none")]
1643 pub use_printer_metrics: Option<bool>,
1644 #[serde(skip_serializing_if = "Option::is_none")]
1645 pub use_fixed_html_paragraph_spacing: Option<bool>,
1646 #[serde(skip_serializing_if = "Option::is_none")]
1647 pub do_not_break_wrapped_tables: Option<bool>,
1648 #[serde(skip_serializing_if = "Option::is_none")]
1649 pub do_not_break_constrained_forced_table: Option<bool>,
1650 #[serde(skip_serializing_if = "Option::is_none")]
1651 pub even_and_odd_headers: Option<bool>,
1652}
1653
1654#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1656#[serde(rename_all = "camelCase")]
1657pub struct DocModelMetadata {
1658 #[serde(default, skip_serializing_if = "Option::is_none")]
1659 pub document_id: Option<String>,
1660 #[serde(default, skip_serializing_if = "Option::is_none")]
1661 pub source_part_names: Option<std::collections::BTreeMap<String, String>>,
1662 pub source_parts: i64,
1663 pub warnings: Vec<String>,
1664 #[serde(skip_serializing_if = "Option::is_none")]
1665 pub document_page_count: Option<i64>,
1666 #[serde(skip_serializing_if = "Option::is_none")]
1667 pub document_open_tag: Option<String>,
1668 #[serde(skip_serializing_if = "Option::is_none")]
1669 pub document_background_color: Option<String>,
1670 #[serde(skip_serializing_if = "Option::is_none")]
1671 pub section_properties_xml: Option<String>,
1672 #[serde(skip_serializing_if = "Option::is_none")]
1673 pub sections: Option<Vec<DocumentSection>>,
1674 pub header_sections: Vec<HeaderSection>,
1675 pub footer_sections: Vec<FooterSection>,
1676 pub paragraph_styles: Vec<ParagraphStyleDefinition>,
1677 #[serde(skip_serializing_if = "Option::is_none")]
1678 pub default_paragraph_style_id: Option<String>,
1679 #[serde(skip_serializing_if = "Option::is_none")]
1680 pub default_tab_stop_twips: Option<i64>,
1681 #[serde(skip_serializing_if = "Option::is_none")]
1682 pub document_settings_imported: Option<bool>,
1683 #[serde(skip_serializing_if = "Option::is_none")]
1684 pub line_spacing_compatibility_imported: Option<bool>,
1685 #[serde(skip_serializing_if = "Option::is_none")]
1686 pub numbering_definitions: Option<NumberingDefinitionSet>,
1687 #[serde(skip_serializing_if = "Option::is_none")]
1688 pub compatibility: Option<DocumentCompatibilitySettings>,
1689 #[serde(skip_serializing_if = "Option::is_none")]
1690 pub footnotes: Option<Vec<DocumentNoteDefinition>>,
1691 #[serde(skip_serializing_if = "Option::is_none")]
1692 pub endnotes: Option<Vec<DocumentNoteDefinition>>,
1693 #[serde(skip_serializing_if = "Option::is_none")]
1694 pub comments: Option<Vec<DocumentCommentDefinition>>,
1695}
1696
1697#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1699#[serde(rename_all = "camelCase")]
1700pub struct DocModel {
1701 pub nodes: Vec<DocNode>,
1702 pub metadata: DocModelMetadata,
1703}
1704
1705#[cfg(test)]
1706mod deserialize_tests {
1707 use super::{DocModel, DocNode, HeadingLevel, ParagraphAlignment};
1708
1709 #[test]
1710 fn deserializes_minimal_js_doc_model() {
1711 let json = r#"{"nodes":[{"type":"paragraph","children":[{"type":"text","text":"Hi"}]}],"metadata":{"sourceParts":1,"warnings":[],"headerSections":[],"footerSections":[],"paragraphStyles":[]}}"#;
1712 let model: DocModel = serde_json::from_str(json).expect("should deserialize");
1713 assert_eq!(model.nodes.len(), 1);
1714 }
1715
1716 #[test]
1717 fn deserializes_heading_level_two() {
1718 let json = r#"{"type":"paragraph","style":{"headingLevel":2},"children":[{"type":"text","text":"Added"}]}"#;
1719 let node: DocNode = serde_json::from_str(json).expect("heading level 2");
1720 let DocNode::Paragraph(paragraph) = node else {
1721 panic!("expected paragraph");
1722 };
1723 assert_eq!(
1724 paragraph.style.and_then(|style| style.heading_level),
1725 Some(HeadingLevel::Two)
1726 );
1727 }
1728
1729 #[test]
1730 fn deserializes_align_center() {
1731 let json = r#"{"type":"paragraph","style":{"align":"center"},"children":[{"type":"text","text":"Added"}]}"#;
1732 let node: DocNode = serde_json::from_str(json).expect("align center");
1733 let DocNode::Paragraph(paragraph) = node else {
1734 panic!("expected paragraph");
1735 };
1736 assert_eq!(
1737 paragraph.style.and_then(|style| style.align),
1738 Some(ParagraphAlignment::Center)
1739 );
1740 }
1741}
1742
1743#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1745#[serde(transparent)]
1746pub struct SharedParagraphStyle(std::sync::Arc<ParagraphStyle>);
1747
1748impl SharedParagraphStyle {
1749 pub fn into_owned(self) -> ParagraphStyle {
1750 std::sync::Arc::unwrap_or_clone(self.0)
1751 }
1752}
1753
1754impl From<ParagraphStyle> for SharedParagraphStyle {
1755 fn from(style: ParagraphStyle) -> Self {
1756 Self(std::sync::Arc::new(style))
1757 }
1758}
1759
1760impl std::ops::Deref for SharedParagraphStyle {
1761 type Target = ParagraphStyle;
1762
1763 fn deref(&self) -> &ParagraphStyle {
1764 &self.0
1765 }
1766}
1767
1768impl std::ops::DerefMut for SharedParagraphStyle {
1769 fn deref_mut(&mut self) -> &mut ParagraphStyle {
1770 std::sync::Arc::make_mut(&mut self.0)
1771 }
1772}