Skip to main content

rust_docx/
model.rs

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/// Mirrors TypeScript `ParagraphAlignment`.
34#[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/// Mirrors TypeScript `HeadingLevel`.
44#[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/// Mirrors TypeScript `TextRunBorderStyle`.
88#[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/// Mirrors TypeScript `VerticalAlign`.
106#[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/// Mirrors TypeScript `TextStyle`.
182#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct TextStyle {
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub text_shadow: Option<TextShadow>,
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub text_outline: Option<TextOutline>,
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub bold: Option<bool>,
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub italic: Option<bool>,
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub underline: Option<bool>,
195    #[serde(skip_serializing_if = "Option::is_none")]
196    pub strike: Option<bool>,
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub all_caps: Option<bool>,
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub small_caps: Option<bool>,
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub color: Option<String>,
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub highlight: Option<String>,
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub background_color: Option<String>,
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub font_size_pt: Option<f64>,
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub font_size_cs_pt: Option<f64>,
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub font_family: Option<String>,
213    /// Import-time `font_family` value used to distinguish a later edit through
214    /// the backwards-compatible single-family API from the preserved slots.
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub source_font_family: Option<String>,
217    /// Explicit OOXML `w:rFonts` slots. `font_family` remains the resolved
218    /// single-family fallback used by existing renderers and callers.
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub font_family_ascii: Option<String>,
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub font_family_h_ansi: Option<String>,
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub font_family_east_asia: Option<String>,
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub font_family_cs: Option<String>,
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub font_theme_ascii: Option<String>,
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub font_theme_h_ansi: Option<String>,
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub font_theme_east_asia: Option<String>,
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub font_theme_cs: Option<String>,
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub resolved_font_family_ascii: Option<String>,
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub resolved_font_family_h_ansi: Option<String>,
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub resolved_font_family_east_asia: Option<String>,
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub resolved_font_family_cs: Option<String>,
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub font_hint: Option<String>,
245    /// OOXML `w:lang` slots plus run-direction flags that participate in font
246    /// selection for complex-script text.
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub language: Option<String>,
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub language_east_asia: Option<String>,
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub language_bidi: Option<String>,
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub right_to_left: Option<bool>,
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub complex_script: Option<bool>,
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub character_spacing_twips: Option<i64>,
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub character_scale_percent: Option<u16>,
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub source_character_scale: Option<String>,
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub kerning_min_font_size_pt: Option<f64>,
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub ligatures: Option<TextLigatures>,
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub contextual_alternates: Option<bool>,
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub vertical_align: Option<VerticalAlign>,
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub run_border: Option<TextRunBorderStyle>,
273}
274
275#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct TextShadow {
278    pub color: String,
279    pub opacity: f64,
280    pub offset_x_px: f64,
281    pub offset_y_px: f64,
282    pub blur_radius_px: f64,
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub source_xml: Option<String>,
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub source_color: Option<String>,
287}
288
289#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct TextOutline {
292    pub color: String,
293    pub opacity: f64,
294    pub width_px: f64,
295    pub line_join: String,
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub source_xml: Option<String>,
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub source_color: Option<String>,
300}
301
302/// Fully resolved formatting shared until a caller edits its value.
303#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
304#[serde(transparent)]
305pub struct SharedTextStyle(std::sync::Arc<TextStyle>);
306
307impl SharedTextStyle {
308    pub fn into_owned(self) -> TextStyle {
309        std::sync::Arc::unwrap_or_clone(self.0)
310    }
311}
312
313impl From<TextStyle> for SharedTextStyle {
314    fn from(style: TextStyle) -> Self {
315        Self(std::sync::Arc::new(style))
316    }
317}
318
319impl std::ops::Deref for SharedTextStyle {
320    type Target = TextStyle;
321
322    fn deref(&self) -> &TextStyle {
323        &self.0
324    }
325}
326
327impl std::ops::DerefMut for SharedTextStyle {
328    fn deref_mut(&mut self) -> &mut TextStyle {
329        std::sync::Arc::make_mut(&mut self.0)
330    }
331}
332
333/// Mirrors TypeScript `TextRunNode.noteReference`.
334#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336pub struct NoteReference {
337    pub kind: NoteReferenceKind,
338    pub id: i64,
339}
340
341/// Mirrors TypeScript `TextRunNode.noteReference.kind`.
342#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
343pub enum NoteReferenceKind {
344    #[serde(rename = "footnote")]
345    Footnote,
346    #[serde(rename = "endnote")]
347    Endnote,
348}
349
350/// Mirrors TypeScript `TextRunNode`.
351#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
352#[serde(rename_all = "camelCase")]
353pub struct TextRunNode {
354    #[serde(default = "default_text_run_type", rename = "type")]
355    pub r#type: TextRunNodeType,
356    pub text: String,
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub style: Option<SharedTextStyle>,
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub link: Option<String>,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub note_reference: Option<NoteReference>,
363}
364
365/// Discriminator for `TextRunNode`.
366#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
367pub enum TextRunNodeType {
368    #[serde(rename = "text")]
369    Text,
370}
371
372/// Mirrors TypeScript `ImageRunNode.crop`.
373#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct ImageCrop {
376    #[serde(skip_serializing_if = "Option::is_none")]
377    pub left_fraction: Option<f64>,
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub top_fraction: Option<f64>,
380    #[serde(skip_serializing_if = "Option::is_none")]
381    pub right_fraction: Option<f64>,
382    #[serde(skip_serializing_if = "Option::is_none")]
383    pub bottom_fraction: Option<f64>,
384}
385
386/// Mirrors TypeScript `ImageRunNode.floating.horizontalAlign`.
387#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
388#[serde(rename_all = "camelCase")]
389pub enum ImageHorizontalAlign {
390    Left,
391    Center,
392    Right,
393    Inside,
394    Outside,
395}
396
397/// Mirrors TypeScript `ImageRunNode.floating.verticalAlign`.
398#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
399#[serde(rename_all = "camelCase")]
400pub enum ImageVerticalAlign {
401    Top,
402    Center,
403    Bottom,
404    Inside,
405    Outside,
406}
407
408/// Mirrors TypeScript `ImageRunNode.floating.wrapType`.
409#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
410#[serde(rename_all = "camelCase")]
411pub enum ImageWrapType {
412    #[serde(rename = "none")]
413    WrapNone,
414    #[serde(rename = "square")]
415    Square,
416    #[serde(rename = "tight")]
417    Tight,
418    #[serde(rename = "through")]
419    Through,
420    #[serde(rename = "topAndBottom")]
421    TopAndBottom,
422}
423
424/// Mirrors TypeScript `ImageRunNode.floating.wrapText`.
425#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub enum ImageWrapText {
428    #[serde(rename = "bothSides")]
429    BothSides,
430    #[serde(rename = "left")]
431    Left,
432    #[serde(rename = "right")]
433    Right,
434    #[serde(rename = "largest")]
435    Largest,
436}
437
438/// Mirrors TypeScript `ImageRunNode.floating`.
439#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
440#[serde(rename_all = "camelCase")]
441pub struct ImageFloating {
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub x_px: Option<f64>,
444    #[serde(skip_serializing_if = "Option::is_none")]
445    pub y_px: Option<f64>,
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub horizontal_align: Option<ImageHorizontalAlign>,
448    #[serde(skip_serializing_if = "Option::is_none")]
449    pub vertical_align: Option<ImageVerticalAlign>,
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub horizontal_relative_to: Option<String>,
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub vertical_relative_to: Option<String>,
454    #[serde(skip_serializing_if = "Option::is_none")]
455    pub dist_l_px: Option<f64>,
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub dist_r_px: Option<f64>,
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub dist_t_px: Option<f64>,
460    #[serde(skip_serializing_if = "Option::is_none")]
461    pub dist_b_px: Option<f64>,
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub wrap_type: Option<ImageWrapType>,
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub wrap_text: Option<ImageWrapText>,
466    #[serde(skip_serializing_if = "Option::is_none")]
467    pub behind_document: Option<bool>,
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub z_index: Option<i64>,
470}
471
472#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
473#[serde(rename_all = "camelCase")]
474pub struct ImagePaintBounds {
475    pub x_px: f64,
476    pub y_px: f64,
477    pub width_px: f64,
478    pub height_px: f64,
479    pub layout_width_px: f64,
480    pub layout_height_px: f64,
481}
482
483#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
484#[serde(rename_all = "camelCase")]
485pub struct ImagePropertyAttribute {
486    pub name: String,
487    pub value: String,
488}
489
490/// Ordered presentation data, including extension properties not rendered by the viewer.
491#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
492#[serde(rename_all = "camelCase")]
493pub struct ImagePropertyNode {
494    pub name: String,
495    #[serde(default, skip_serializing_if = "Vec::is_empty")]
496    pub attributes: Vec<ImagePropertyAttribute>,
497    #[serde(default, skip_serializing_if = "Vec::is_empty")]
498    pub children: Vec<ImagePropertyNode>,
499    #[serde(default, skip_serializing_if = "Option::is_none")]
500    pub text: Option<String>,
501}
502
503#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
504#[serde(rename_all = "camelCase")]
505pub struct ImageAssetReference {
506    pub element: String,
507    pub kind: String,
508    #[serde(skip_serializing_if = "Option::is_none")]
509    pub relationship_id: Option<String>,
510    #[serde(skip_serializing_if = "Option::is_none")]
511    pub target: Option<String>,
512    pub embedded_asset_available: bool,
513}
514
515#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
516#[serde(rename_all = "camelCase")]
517pub struct ImageTransform {
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub rotation_degrees: Option<f64>,
520    #[serde(skip_serializing_if = "Option::is_none")]
521    pub flip_horizontal: Option<bool>,
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub flip_vertical: Option<bool>,
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub offset_x_emu: Option<f64>,
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub offset_y_emu: Option<f64>,
528    #[serde(skip_serializing_if = "Option::is_none")]
529    pub width_emu: Option<f64>,
530    #[serde(skip_serializing_if = "Option::is_none")]
531    pub height_emu: Option<f64>,
532}
533
534#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
535#[serde(rename_all = "camelCase")]
536pub struct ImageTile {
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub offset_x_emu: Option<f64>,
539    #[serde(skip_serializing_if = "Option::is_none")]
540    pub offset_y_emu: Option<f64>,
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub scale_x: Option<f64>,
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub scale_y: Option<f64>,
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub alignment: Option<String>,
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub flip: Option<String>,
549}
550
551/// Parsed properties describe source intent independently of rendering coverage.
552#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
553#[serde(rename_all = "camelCase")]
554pub struct ImagePresentation {
555    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
556    pub transform_applied: bool,
557    #[serde(default, skip_serializing_if = "Vec::is_empty")]
558    pub assets: Vec<ImageAssetReference>,
559    #[serde(skip_serializing_if = "Option::is_none")]
560    pub transform: Option<ImageTransform>,
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub source_rectangle: Option<ImageCrop>,
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub fill_rectangle: Option<ImageCrop>,
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub tile: Option<ImageTile>,
567    #[serde(default, skip_serializing_if = "Vec::is_empty")]
568    pub properties: Vec<ImagePropertyNode>,
569}
570
571/// Mirrors TypeScript `ImageRunNode`.
572#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
573#[serde(rename_all = "camelCase")]
574pub struct ImageRunNode {
575    #[serde(default, rename = "model3d", skip_serializing_if = "Option::is_none")]
576    pub model_3d: Option<Box<rust_office_art::model3d::Model3D>>,
577    #[serde(default, skip_serializing_if = "Option::is_none")]
578    pub presentation: Option<Box<ImagePresentation>>,
579    #[serde(default, skip_serializing_if = "Option::is_none")]
580    pub chart_part_name: Option<String>,
581    #[serde(default, skip_serializing_if = "Option::is_none")]
582    pub chart_data: Option<Box<crate::ChartData>>,
583    #[serde(default, skip_serializing_if = "Option::is_none")]
584    pub paint_bounds: Option<Box<ImagePaintBounds>>,
585    #[serde(default, skip_serializing_if = "Option::is_none")]
586    pub math_ml: Option<String>,
587    #[serde(default, skip_serializing_if = "Option::is_none")]
588    pub content_nodes: Option<Vec<DocNode>>,
589    #[serde(default, skip_serializing_if = "Option::is_none")]
590    pub semantic_kind: Option<String>,
591    #[serde(default, skip_serializing_if = "Option::is_none")]
592    pub block_id: Option<String>,
593    #[serde(default = "default_image_run_type", rename = "type")]
594    pub r#type: ImageRunNodeType,
595    #[serde(skip_serializing_if = "Option::is_none")]
596    pub src: Option<String>,
597    #[serde(skip_serializing_if = "Option::is_none")]
598    pub alt: Option<String>,
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub width_px: Option<f64>,
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub height_px: Option<f64>,
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub part_name: Option<String>,
605    #[serde(skip_serializing_if = "Option::is_none")]
606    pub content_type: Option<String>,
607    #[serde(default, skip_serializing_if = "Option::is_none")]
608    pub data: Option<Vec<u8>>,
609    #[serde(skip_serializing_if = "Option::is_none")]
610    pub source_xml: Option<String>,
611    #[serde(skip_serializing_if = "Option::is_none")]
612    pub crop: Option<ImageCrop>,
613    #[serde(skip_serializing_if = "Option::is_none")]
614    pub css_filter: Option<String>,
615    #[serde(skip_serializing_if = "Option::is_none")]
616    pub css_opacity: Option<f64>,
617    #[serde(skip_serializing_if = "Option::is_none")]
618    pub floating: Option<ImageFloating>,
619    #[serde(skip_serializing_if = "Option::is_none")]
620    pub synthetic_text_box: Option<bool>,
621    #[serde(skip_serializing_if = "Option::is_none")]
622    pub text_box_text: Option<String>,
623}
624
625/// Discriminator for `ImageRunNode`.
626#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
627pub enum ImageRunNodeType {
628    #[serde(rename = "image")]
629    Image,
630}
631
632/// Mirrors TypeScript `FormFieldType`.
633#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
634#[serde(rename_all = "camelCase")]
635pub enum FormFieldType {
636    #[serde(rename = "checkbox")]
637    Checkbox,
638    #[serde(rename = "text")]
639    Text,
640    #[serde(rename = "date")]
641    Date,
642    #[serde(rename = "dropdown")]
643    Dropdown,
644}
645
646/// Mirrors TypeScript `FormFieldSourceKind`.
647#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
648pub enum FormFieldSourceKind {
649    #[serde(rename = "sdt")]
650    Sdt,
651    #[serde(rename = "legacy")]
652    Legacy,
653}
654
655/// Mirrors TypeScript `FormFieldOption`.
656#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
657#[serde(rename_all = "camelCase")]
658pub struct FormFieldOption {
659    pub display_text: String,
660    #[serde(skip_serializing_if = "Option::is_none")]
661    pub value: Option<String>,
662}
663
664/// Mirrors TypeScript `FormFieldTextWidgetSettings`.
665#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
666#[serde(rename_all = "camelCase")]
667pub struct FormFieldTextWidgetSettings {
668    #[serde(skip_serializing_if = "Option::is_none")]
669    pub input_type: Option<String>,
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub default_text: Option<String>,
672    #[serde(skip_serializing_if = "Option::is_none")]
673    pub max_length: Option<i64>,
674    #[serde(skip_serializing_if = "Option::is_none")]
675    pub text_format: Option<String>,
676}
677
678/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings`.
679#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
680#[serde(rename_all = "camelCase")]
681pub struct FormFieldCheckboxWidgetSettings {
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub default_checked: Option<bool>,
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub size_mode: Option<FormFieldCheckboxSizeMode>,
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub size_pt: Option<f64>,
688}
689
690/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings.sizeMode`.
691#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
692pub enum FormFieldCheckboxSizeMode {
693    #[serde(rename = "auto")]
694    Auto,
695    #[serde(rename = "exact")]
696    Exact,
697}
698
699/// Mirrors TypeScript `FormFieldDropdownWidgetSettings`.
700#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
701#[serde(rename_all = "camelCase")]
702pub struct FormFieldDropdownWidgetSettings {
703    #[serde(skip_serializing_if = "Option::is_none")]
704    pub default_value: Option<String>,
705}
706
707/// Mirrors TypeScript `FormFieldWidgetSettings`.
708#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
709#[serde(rename_all = "camelCase")]
710pub struct FormFieldWidgetSettings {
711    #[serde(skip_serializing_if = "Option::is_none")]
712    pub name: Option<String>,
713    #[serde(skip_serializing_if = "Option::is_none")]
714    pub enabled: Option<bool>,
715    #[serde(skip_serializing_if = "Option::is_none")]
716    pub calc_on_exit: Option<bool>,
717    #[serde(skip_serializing_if = "Option::is_none")]
718    pub text: Option<FormFieldTextWidgetSettings>,
719    #[serde(skip_serializing_if = "Option::is_none")]
720    pub checkbox: Option<FormFieldCheckboxWidgetSettings>,
721    #[serde(skip_serializing_if = "Option::is_none")]
722    pub dropdown: Option<FormFieldDropdownWidgetSettings>,
723}
724
725#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
726#[serde(rename_all = "camelCase")]
727pub struct FormFieldSourcePreview {
728    pub relationship_id: String,
729    pub shape_id: String,
730    pub checked: bool,
731    pub image: Box<ImageRunNode>,
732}
733
734/// Mirrors TypeScript `FormFieldRunNode`.
735#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
736#[serde(rename_all = "camelCase")]
737pub struct FormFieldRunNode {
738    #[serde(default = "default_form_field_run_type", rename = "type")]
739    pub r#type: FormFieldRunNodeType,
740    pub field_type: FormFieldType,
741    #[serde(skip_serializing_if = "Option::is_none")]
742    pub source_kind: Option<FormFieldSourceKind>,
743    #[serde(skip_serializing_if = "Option::is_none")]
744    pub id: Option<i64>,
745    #[serde(skip_serializing_if = "Option::is_none")]
746    pub tag: Option<String>,
747    #[serde(skip_serializing_if = "Option::is_none")]
748    pub title: Option<String>,
749    #[serde(skip_serializing_if = "Option::is_none")]
750    pub placeholder: Option<String>,
751    #[serde(skip_serializing_if = "Option::is_none")]
752    pub checked: Option<bool>,
753    #[serde(skip_serializing_if = "Option::is_none")]
754    pub value: Option<String>,
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub options: Option<Vec<FormFieldOption>>,
757    #[serde(skip_serializing_if = "Option::is_none")]
758    pub widget: Option<FormFieldWidgetSettings>,
759    #[serde(skip_serializing_if = "Option::is_none")]
760    pub checked_symbol: Option<String>,
761    #[serde(skip_serializing_if = "Option::is_none")]
762    pub unchecked_symbol: Option<String>,
763    #[serde(skip_serializing_if = "Option::is_none")]
764    pub style: Option<SharedTextStyle>,
765    #[serde(skip_serializing_if = "Option::is_none")]
766    pub link: Option<String>,
767    #[serde(skip_serializing_if = "Option::is_none")]
768    pub source_xml: Option<String>,
769    #[serde(default, skip_serializing_if = "Option::is_none")]
770    pub source_preview: Option<Box<FormFieldSourcePreview>>,
771}
772
773/// Discriminator for `FormFieldRunNode`.
774#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
775pub enum FormFieldRunNodeType {
776    #[serde(rename = "form-field")]
777    FormField,
778}
779
780/// Mirrors TypeScript `ParagraphChildNode`.
781#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
782#[serde(untagged)]
783pub enum ParagraphChildNode {
784    Text(TextRunNode),
785    Image(Box<ImageRunNode>),
786    FormField(Box<FormFieldRunNode>),
787}
788
789/// Mirrors TypeScript `ParagraphNumbering`.
790#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
791#[serde(rename_all = "camelCase")]
792pub struct ParagraphNumbering {
793    pub num_id: i64,
794    pub ilvl: i64,
795}
796
797/// Mirrors TypeScript `ParagraphSpacing.lineRule`.
798#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
799pub enum ParagraphLineRule {
800    #[serde(rename = "auto")]
801    Auto,
802    #[serde(rename = "exact")]
803    Exact,
804    #[serde(rename = "atLeast")]
805    AtLeast,
806}
807
808/// Mirrors TypeScript `ParagraphSpacing`.
809#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
810#[serde(rename_all = "camelCase")]
811pub struct ParagraphSpacing {
812    #[serde(skip_serializing_if = "Option::is_none")]
813    pub before_twips: Option<i64>,
814    #[serde(skip_serializing_if = "Option::is_none")]
815    pub after_twips: Option<i64>,
816    #[serde(skip_serializing_if = "Option::is_none")]
817    pub before_auto_spacing: Option<bool>,
818    #[serde(skip_serializing_if = "Option::is_none")]
819    pub after_auto_spacing: Option<bool>,
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub line_twips: Option<i64>,
822    #[serde(skip_serializing_if = "Option::is_none")]
823    pub line_rule: Option<ParagraphLineRule>,
824}
825
826/// Mirrors TypeScript `ParagraphIndent`.
827#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
828#[serde(rename_all = "camelCase")]
829pub struct ParagraphIndent {
830    #[serde(skip_serializing_if = "Option::is_none")]
831    pub left_twips: Option<i64>,
832    #[serde(skip_serializing_if = "Option::is_none")]
833    pub right_twips: Option<i64>,
834    #[serde(skip_serializing_if = "Option::is_none")]
835    pub first_line_twips: Option<i64>,
836    #[serde(skip_serializing_if = "Option::is_none")]
837    pub hanging_twips: Option<i64>,
838}
839
840/// Mirrors TypeScript `ParagraphBorderStyle`.
841#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
842#[serde(rename_all = "camelCase")]
843pub struct ParagraphBorderStyle {
844    #[serde(rename = "type")]
845    pub border_type: String,
846    #[serde(skip_serializing_if = "Option::is_none")]
847    pub color: Option<String>,
848    #[serde(skip_serializing_if = "Option::is_none")]
849    pub size_eighth_pt: Option<f64>,
850    #[serde(skip_serializing_if = "Option::is_none")]
851    pub space_pt: Option<f64>,
852    #[serde(skip_serializing_if = "Option::is_none")]
853    pub frame: Option<bool>,
854    #[serde(skip_serializing_if = "Option::is_none")]
855    pub shadow: Option<bool>,
856}
857
858/// Mirrors TypeScript `ParagraphBorderSet`.
859#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
860#[serde(rename_all = "camelCase")]
861pub struct ParagraphBorderSet {
862    #[serde(skip_serializing_if = "Option::is_none")]
863    pub top: Option<ParagraphBorderStyle>,
864    #[serde(skip_serializing_if = "Option::is_none")]
865    pub right: Option<ParagraphBorderStyle>,
866    #[serde(skip_serializing_if = "Option::is_none")]
867    pub bottom: Option<ParagraphBorderStyle>,
868    #[serde(skip_serializing_if = "Option::is_none")]
869    pub left: Option<ParagraphBorderStyle>,
870    #[serde(skip_serializing_if = "Option::is_none")]
871    pub between: Option<ParagraphBorderStyle>,
872    #[serde(skip_serializing_if = "Option::is_none")]
873    pub bar: Option<ParagraphBorderStyle>,
874}
875
876/// Mirrors TypeScript `ParagraphTabStop.alignment`.
877#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
878#[serde(rename_all = "camelCase")]
879pub enum ParagraphTabStopAlignment {
880    Clear,
881    Left,
882    Center,
883    Right,
884    Decimal,
885    Bar,
886}
887
888/// Mirrors TypeScript `ParagraphTabStop.leader`.
889#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
890pub enum ParagraphTabStopLeader {
891    #[serde(rename = "none")]
892    LeaderNone,
893    #[serde(rename = "dot")]
894    Dot,
895    #[serde(rename = "hyphen")]
896    Hyphen,
897    #[serde(rename = "underscore")]
898    Underscore,
899    #[serde(rename = "middleDot")]
900    MiddleDot,
901}
902
903/// Mirrors TypeScript `ParagraphTabStop`.
904#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
905#[serde(rename_all = "camelCase")]
906pub struct ParagraphTabStop {
907    #[serde(skip_serializing_if = "Option::is_none")]
908    pub alignment: Option<ParagraphTabStopAlignment>,
909    #[serde(skip_serializing_if = "Option::is_none")]
910    pub leader: Option<ParagraphTabStopLeader>,
911    #[serde(skip_serializing_if = "Option::is_none")]
912    pub position_twips: Option<i64>,
913}
914
915/// Mirrors TypeScript `ParagraphStyle.dropCap.type`.
916#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
917pub enum ParagraphDropCapType {
918    #[serde(rename = "drop")]
919    Drop,
920    #[serde(rename = "margin")]
921    Margin,
922}
923
924/// Mirrors TypeScript `ParagraphStyle.dropCap`.
925#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
926#[serde(rename_all = "camelCase")]
927pub struct ParagraphDropCap {
928    #[serde(rename = "type")]
929    pub drop_cap_type: ParagraphDropCapType,
930    #[serde(skip_serializing_if = "Option::is_none")]
931    pub lines: Option<i64>,
932    #[serde(skip_serializing_if = "Option::is_none")]
933    pub wrap: Option<String>,
934    #[serde(skip_serializing_if = "Option::is_none")]
935    pub horizontal_anchor: Option<String>,
936    #[serde(skip_serializing_if = "Option::is_none")]
937    pub vertical_anchor: Option<String>,
938    #[serde(skip_serializing_if = "Option::is_none")]
939    pub x_twips: Option<i64>,
940    #[serde(skip_serializing_if = "Option::is_none")]
941    pub y_twips: Option<i64>,
942    #[serde(skip_serializing_if = "Option::is_none")]
943    pub horizontal_space_twips: Option<i64>,
944    #[serde(skip_serializing_if = "Option::is_none")]
945    pub vertical_space_twips: Option<i64>,
946}
947
948#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
949#[serde(rename_all = "camelCase")]
950pub enum ParagraphTextAlignment {
951    Auto,
952    Top,
953    Center,
954    Baseline,
955    Bottom,
956}
957
958/// Mirrors TypeScript `ParagraphStyle`.
959#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
960#[serde(rename_all = "camelCase")]
961pub struct ParagraphStyle {
962    #[serde(skip_serializing_if = "Option::is_none")]
963    pub source_before_auto_spacing: Option<bool>,
964    #[serde(skip_serializing_if = "Option::is_none")]
965    pub source_after_auto_spacing: Option<bool>,
966    #[serde(skip_serializing_if = "Option::is_none")]
967    pub align: Option<ParagraphAlignment>,
968    #[serde(skip_serializing_if = "Option::is_none")]
969    pub snap_to_grid: Option<bool>,
970    #[serde(skip_serializing_if = "Option::is_none")]
971    pub source_snap_to_grid: Option<bool>,
972    #[serde(skip_serializing_if = "Option::is_none")]
973    pub source_has_snap_to_grid: Option<bool>,
974    #[serde(skip_serializing_if = "Option::is_none")]
975    pub source_inherited_snap_to_grid: Option<bool>,
976    #[serde(skip_serializing_if = "Option::is_none")]
977    pub source_table_snap_to_grid: Option<bool>,
978    #[serde(skip_serializing_if = "Option::is_none")]
979    pub text_alignment: Option<ParagraphTextAlignment>,
980    #[serde(skip_serializing_if = "Option::is_none")]
981    pub source_text_alignment: Option<ParagraphTextAlignment>,
982    #[serde(skip_serializing_if = "Option::is_none")]
983    pub source_has_text_alignment: Option<bool>,
984    #[serde(skip_serializing_if = "Option::is_none")]
985    pub source_inherited_text_alignment: Option<ParagraphTextAlignment>,
986    #[serde(skip_serializing_if = "Option::is_none")]
987    pub source_table_text_alignment: Option<ParagraphTextAlignment>,
988    #[serde(skip_serializing_if = "Option::is_none")]
989    pub heading_level: Option<HeadingLevel>,
990    #[serde(skip_serializing_if = "Option::is_none")]
991    pub style_id: Option<String>,
992    #[serde(skip_serializing_if = "Option::is_none")]
993    pub style_name: Option<String>,
994    #[serde(skip_serializing_if = "Option::is_none")]
995    pub numbering: Option<ParagraphNumbering>,
996    #[serde(skip_serializing_if = "Option::is_none")]
997    pub spacing: Option<ParagraphSpacing>,
998    #[serde(skip_serializing_if = "Option::is_none")]
999    pub indent: Option<ParagraphIndent>,
1000    #[serde(skip_serializing_if = "Option::is_none")]
1001    pub background_color: Option<String>,
1002    #[serde(skip_serializing_if = "Option::is_none")]
1003    pub borders: Option<ParagraphBorderSet>,
1004    #[serde(skip_serializing_if = "Option::is_none")]
1005    pub tab_stops: Option<Vec<ParagraphTabStop>>,
1006    #[serde(skip_serializing_if = "Option::is_none")]
1007    pub contextual_spacing: Option<bool>,
1008    #[serde(skip_serializing_if = "Option::is_none")]
1009    pub keep_next: Option<bool>,
1010    #[serde(skip_serializing_if = "Option::is_none")]
1011    pub keep_lines: Option<bool>,
1012    #[serde(skip_serializing_if = "Option::is_none")]
1013    pub widow_control: Option<bool>,
1014    #[serde(skip_serializing_if = "Option::is_none")]
1015    pub page_break_before: Option<bool>,
1016    #[serde(skip_serializing_if = "Option::is_none")]
1017    pub drop_cap: Option<ParagraphDropCap>,
1018}
1019
1020/// Text-independent run provenance for a validated surgical source XML patch.
1021#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1022#[serde(rename_all = "camelCase")]
1023pub struct ParagraphSourceTextPatchRun {
1024    #[serde(skip_serializing_if = "Option::is_none")]
1025    pub style: Option<SharedTextStyle>,
1026    #[serde(skip_serializing_if = "Option::is_none")]
1027    pub link: Option<String>,
1028    #[serde(skip_serializing_if = "Option::is_none")]
1029    pub note_reference: Option<NoteReference>,
1030}
1031
1032#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1033#[serde(rename_all = "camelCase")]
1034pub struct ParagraphSourceTextPatch {
1035    pub runs: Vec<ParagraphSourceTextPatchRun>,
1036}
1037
1038/// Imported paragraph-mark default, character-style, and direct layers.
1039#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1040#[serde(rename_all = "camelCase")]
1041pub struct ParagraphMarkFormattingSource {
1042    #[serde(skip_serializing_if = "Option::is_none")]
1043    pub default_style: Option<SharedTextStyle>,
1044    #[serde(skip_serializing_if = "Option::is_none")]
1045    pub character_style: Option<SharedTextStyle>,
1046    #[serde(skip_serializing_if = "Option::is_none")]
1047    pub direct_style: Option<SharedTextStyle>,
1048}
1049
1050/// Mirrors TypeScript `ParagraphNode`.
1051#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1052#[serde(rename_all = "camelCase")]
1053pub struct ParagraphNode {
1054    #[serde(default, skip_serializing_if = "Option::is_none")]
1055    pub review_content: Option<Vec<crate::revisions::ReviewContent>>,
1056    #[serde(default, skip_serializing_if = "Option::is_none")]
1057    pub block_id: Option<String>,
1058    #[serde(default = "default_paragraph_node_type", rename = "type")]
1059    pub r#type: ParagraphNodeType,
1060    pub children: Vec<ParagraphChildNode>,
1061    #[serde(skip_serializing_if = "Option::is_none")]
1062    pub style: Option<SharedParagraphStyle>,
1063    #[serde(skip_serializing_if = "Option::is_none")]
1064    pub paragraph_mark_style: Option<SharedTextStyle>,
1065    #[serde(skip_serializing_if = "Option::is_none")]
1066    pub source_paragraph_mark_style: Option<SharedTextStyle>,
1067    #[serde(skip_serializing_if = "Option::is_none")]
1068    pub source_paragraph_mark_formatting: Option<ParagraphMarkFormattingSource>,
1069    #[serde(skip_serializing_if = "Option::is_none")]
1070    pub source_paragraph_mark_properties_xml: Option<String>,
1071    #[serde(skip_serializing_if = "Option::is_none")]
1072    pub paragraph_mark_deleted: Option<bool>,
1073    #[serde(skip_serializing_if = "Option::is_none")]
1074    pub source_xml: Option<String>,
1075    #[serde(skip_serializing_if = "Option::is_none")]
1076    pub source_text_patch: Option<ParagraphSourceTextPatch>,
1077    /// Immutable import-time run metadata used to prove that a later surgical
1078    /// annotation edit is still targeting the exact parsed run state.
1079    #[serde(skip_serializing_if = "Option::is_none")]
1080    pub source_run_provenance: Option<ParagraphSourceTextPatch>,
1081}
1082
1083/// Discriminator for `ParagraphNode`.
1084#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1085pub enum ParagraphNodeType {
1086    #[serde(rename = "paragraph")]
1087    Paragraph,
1088}
1089
1090/// Mirrors TypeScript `TableBoxSpacing`.
1091#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1092#[serde(rename_all = "camelCase")]
1093pub struct TableBoxSpacing {
1094    #[serde(skip_serializing_if = "Option::is_none")]
1095    pub top_twips: Option<i64>,
1096    #[serde(skip_serializing_if = "Option::is_none")]
1097    pub right_twips: Option<i64>,
1098    #[serde(skip_serializing_if = "Option::is_none")]
1099    pub bottom_twips: Option<i64>,
1100    #[serde(skip_serializing_if = "Option::is_none")]
1101    pub left_twips: Option<i64>,
1102}
1103
1104/// Mirrors TypeScript `TableBorderStyle`.
1105#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1106#[serde(rename_all = "camelCase")]
1107pub struct TableBorderStyle {
1108    #[serde(rename = "type")]
1109    pub border_type: String,
1110    #[serde(skip_serializing_if = "Option::is_none")]
1111    pub color: Option<String>,
1112    #[serde(skip_serializing_if = "Option::is_none")]
1113    pub size_eighth_pt: Option<f64>,
1114}
1115
1116/// Mirrors TypeScript `TableBorderSet`.
1117#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1118#[serde(rename_all = "camelCase")]
1119pub struct TableBorderSet {
1120    #[serde(skip_serializing_if = "Option::is_none")]
1121    pub top: Option<TableBorderStyle>,
1122    #[serde(skip_serializing_if = "Option::is_none")]
1123    pub right: Option<TableBorderStyle>,
1124    #[serde(skip_serializing_if = "Option::is_none")]
1125    pub bottom: Option<TableBorderStyle>,
1126    #[serde(skip_serializing_if = "Option::is_none")]
1127    pub left: Option<TableBorderStyle>,
1128    #[serde(skip_serializing_if = "Option::is_none")]
1129    pub inside_h: Option<TableBorderStyle>,
1130    #[serde(skip_serializing_if = "Option::is_none")]
1131    pub inside_v: Option<TableBorderStyle>,
1132    #[serde(skip_serializing_if = "Option::is_none")]
1133    pub tl2br: Option<TableBorderStyle>,
1134    #[serde(skip_serializing_if = "Option::is_none")]
1135    pub tr2bl: Option<TableBorderStyle>,
1136}
1137
1138/// Mirrors TypeScript `TableCellStyle.verticalAlign`.
1139#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1140#[serde(rename_all = "camelCase")]
1141pub enum TableCellVerticalAlign {
1142    Top,
1143    Center,
1144    Bottom,
1145}
1146
1147/// Preferred width with an explicit measurement kind.
1148#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1149#[serde(tag = "type", rename_all = "camelCase")]
1150pub enum TablePreferredWidth {
1151    Dxa { value: f64 },
1152    Pct { value: f64 },
1153    Auto,
1154    Nil,
1155}
1156
1157/// Imported width baseline used to distinguish edits to either width API.
1158#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1159#[serde(rename_all = "camelCase")]
1160pub struct TableWidthSource {
1161    #[serde(skip_serializing_if = "Option::is_none")]
1162    pub preferred_width: Option<TablePreferredWidth>,
1163    #[serde(skip_serializing_if = "Option::is_none")]
1164    pub width_twips: Option<i64>,
1165    #[serde(skip_serializing_if = "Option::is_none")]
1166    pub inherited_preferred_width: Option<TablePreferredWidth>,
1167}
1168
1169#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1170#[serde(rename_all = "camelCase")]
1171pub enum TableAlignment {
1172    Left,
1173    Center,
1174    Right,
1175}
1176
1177/// Mirrors TypeScript `TableCellStyle`.
1178#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1179#[serde(rename_all = "camelCase")]
1180pub struct TableCellStyle {
1181    #[serde(skip_serializing_if = "Option::is_none")]
1182    pub background_color: Option<String>,
1183    #[serde(skip_serializing_if = "Option::is_none")]
1184    pub grid_span: Option<i64>,
1185    #[serde(skip_serializing_if = "Option::is_none")]
1186    pub row_span: Option<i64>,
1187    #[serde(skip_serializing_if = "Option::is_none")]
1188    pub v_merge_continuation: Option<bool>,
1189    #[serde(skip_serializing_if = "Option::is_none")]
1190    pub width_twips: Option<i64>,
1191    #[serde(skip_serializing_if = "Option::is_none")]
1192    pub preferred_width: Option<TablePreferredWidth>,
1193    #[serde(skip_serializing_if = "Option::is_none")]
1194    pub source_width: Option<TableWidthSource>,
1195    #[serde(skip_serializing_if = "Option::is_none")]
1196    pub margin_twips: Option<TableBoxSpacing>,
1197    #[serde(skip_serializing_if = "Option::is_none")]
1198    pub vertical_align: Option<TableCellVerticalAlign>,
1199    #[serde(skip_serializing_if = "Option::is_none")]
1200    pub text_direction: Option<String>,
1201    #[serde(skip_serializing_if = "Option::is_none")]
1202    pub source_text_direction: Option<String>,
1203    #[serde(skip_serializing_if = "Option::is_none")]
1204    pub no_wrap: Option<bool>,
1205    #[serde(skip_serializing_if = "Option::is_none")]
1206    pub source_no_wrap: Option<bool>,
1207    #[serde(skip_serializing_if = "Option::is_none")]
1208    pub borders: Option<TableBorderSet>,
1209}
1210
1211/// Mirrors TypeScript `TableCellContentNode`.
1212#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1213#[serde(untagged)]
1214pub enum TableCellContentNode {
1215    Paragraph(ParagraphNode),
1216    Table(Box<TableNode>),
1217}
1218
1219/// Mirrors TypeScript `TableCellNode`.
1220#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1221#[serde(rename_all = "camelCase")]
1222pub struct TableCellNode {
1223    #[serde(default = "default_table_cell_node_type", rename = "type")]
1224    pub r#type: TableCellNodeType,
1225    pub nodes: Vec<TableCellContentNode>,
1226    #[serde(skip_serializing_if = "Option::is_none")]
1227    pub style: Option<TableCellStyle>,
1228}
1229
1230/// Discriminator for `TableCellNode`.
1231#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1232pub enum TableCellNodeType {
1233    #[serde(rename = "table-cell")]
1234    TableCell,
1235}
1236
1237/// Mirrors TypeScript `TableRowStyle.heightRule`.
1238#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1239pub enum TableRowHeightRule {
1240    #[serde(rename = "auto")]
1241    Auto,
1242    #[serde(rename = "atLeast")]
1243    AtLeast,
1244    #[serde(rename = "exact")]
1245    Exact,
1246}
1247
1248/// Imported row geometry used to distinguish a clear from an omitted legacy field.
1249#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1250#[serde(rename_all = "camelCase")]
1251pub struct TableRowGeometrySource {
1252    #[serde(skip_serializing_if = "Option::is_none")]
1253    pub grid_before: Option<i64>,
1254    #[serde(skip_serializing_if = "Option::is_none")]
1255    pub grid_after: Option<i64>,
1256    #[serde(skip_serializing_if = "Option::is_none")]
1257    pub width_before: Option<TablePreferredWidth>,
1258    #[serde(skip_serializing_if = "Option::is_none")]
1259    pub width_after: Option<TablePreferredWidth>,
1260}
1261
1262/// Mirrors TypeScript `TableRowStyle`.
1263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1264#[serde(rename_all = "camelCase")]
1265pub struct TableRowStyle {
1266    #[serde(skip_serializing_if = "Option::is_none")]
1267    pub grid_before: Option<i64>,
1268    #[serde(skip_serializing_if = "Option::is_none")]
1269    pub grid_after: Option<i64>,
1270    #[serde(skip_serializing_if = "Option::is_none")]
1271    pub width_before: Option<TablePreferredWidth>,
1272    #[serde(skip_serializing_if = "Option::is_none")]
1273    pub width_after: Option<TablePreferredWidth>,
1274    #[serde(skip_serializing_if = "Option::is_none")]
1275    pub source_row_geometry: Option<TableRowGeometrySource>,
1276    #[serde(skip_serializing_if = "Option::is_none")]
1277    pub background_color: Option<String>,
1278    #[serde(skip_serializing_if = "Option::is_none")]
1279    pub height_twips: Option<i64>,
1280    #[serde(skip_serializing_if = "Option::is_none")]
1281    pub height_rule: Option<TableRowHeightRule>,
1282    #[serde(skip_serializing_if = "Option::is_none")]
1283    pub cant_split: Option<bool>,
1284    #[serde(skip_serializing_if = "Option::is_none")]
1285    pub is_header: Option<bool>,
1286}
1287
1288/// Mirrors TypeScript `TableRowNode`.
1289#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1290#[serde(rename_all = "camelCase")]
1291pub struct TableRowNode {
1292    #[serde(default = "default_table_row_node_type", rename = "type")]
1293    pub r#type: TableRowNodeType,
1294    pub cells: Vec<TableCellNode>,
1295    #[serde(skip_serializing_if = "Option::is_none")]
1296    pub style: Option<TableRowStyle>,
1297}
1298
1299/// Discriminator for `TableRowNode`.
1300#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1301pub enum TableRowNodeType {
1302    #[serde(rename = "table-row")]
1303    TableRow,
1304}
1305
1306/// Mirrors TypeScript `TableStyle.layout`.
1307#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1308pub enum TableLayout {
1309    #[serde(rename = "fixed")]
1310    Fixed,
1311    #[serde(rename = "autofit")]
1312    Autofit,
1313}
1314
1315/// Mirrors TypeScript `TableStyle.floating`.
1316#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1317#[serde(rename_all = "camelCase")]
1318pub struct TableFloating {
1319    #[serde(skip_serializing_if = "Option::is_none")]
1320    pub x_twips: Option<i64>,
1321    #[serde(skip_serializing_if = "Option::is_none")]
1322    pub y_twips: Option<i64>,
1323    #[serde(skip_serializing_if = "Option::is_none")]
1324    pub left_from_text_twips: Option<i64>,
1325    #[serde(skip_serializing_if = "Option::is_none")]
1326    pub right_from_text_twips: Option<i64>,
1327    #[serde(skip_serializing_if = "Option::is_none")]
1328    pub top_from_text_twips: Option<i64>,
1329    #[serde(skip_serializing_if = "Option::is_none")]
1330    pub bottom_from_text_twips: Option<i64>,
1331    #[serde(skip_serializing_if = "Option::is_none")]
1332    pub horizontal_anchor: Option<String>,
1333    #[serde(skip_serializing_if = "Option::is_none")]
1334    pub vertical_anchor: Option<String>,
1335    #[serde(skip_serializing_if = "Option::is_none")]
1336    pub horizontal_align: Option<ImageHorizontalAlign>,
1337    #[serde(skip_serializing_if = "Option::is_none")]
1338    pub vertical_align: Option<ImageVerticalAlign>,
1339}
1340
1341/// Mirrors TypeScript `TableStyle`.
1342#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1343#[serde(rename_all = "camelCase")]
1344pub struct TableStyle {
1345    #[serde(skip_serializing_if = "Option::is_none")]
1346    pub style_id: Option<String>,
1347    #[serde(skip_serializing_if = "Option::is_none")]
1348    pub source_style_id: Option<String>,
1349    #[serde(skip_serializing_if = "Option::is_none")]
1350    pub width_twips: Option<i64>,
1351    #[serde(skip_serializing_if = "Option::is_none")]
1352    pub preferred_width: Option<TablePreferredWidth>,
1353    #[serde(skip_serializing_if = "Option::is_none")]
1354    pub source_width: Option<TableWidthSource>,
1355    #[serde(skip_serializing_if = "Option::is_none")]
1356    pub alignment: Option<TableAlignment>,
1357    #[serde(skip_serializing_if = "Option::is_none")]
1358    pub source_alignment: Option<TableAlignment>,
1359    #[serde(skip_serializing_if = "Option::is_none")]
1360    pub source_inherited_alignment: Option<TableAlignment>,
1361    #[serde(skip_serializing_if = "Option::is_none")]
1362    pub bidi_visual: Option<bool>,
1363    #[serde(skip_serializing_if = "Option::is_none")]
1364    pub source_bidi_visual: Option<bool>,
1365    #[serde(skip_serializing_if = "Option::is_none")]
1366    pub source_inherited_bidi_visual: Option<bool>,
1367    #[serde(skip_serializing_if = "Option::is_none")]
1368    pub indent_twips: Option<i64>,
1369    #[serde(skip_serializing_if = "Option::is_none")]
1370    pub layout: Option<TableLayout>,
1371    #[serde(skip_serializing_if = "Option::is_none")]
1372    pub cell_spacing_twips: Option<i64>,
1373    #[serde(skip_serializing_if = "Option::is_none")]
1374    pub cell_margin_twips: Option<TableBoxSpacing>,
1375    #[serde(skip_serializing_if = "Option::is_none")]
1376    pub column_widths_twips: Option<Vec<i64>>,
1377    /// Logical intervals with coincident physical boundaries. Project these
1378    /// columns for layout and materialize them before structural edits.
1379    #[serde(skip_serializing_if = "Option::is_none")]
1380    pub collapsed_grid_columns: Option<Vec<usize>>,
1381    #[serde(skip_serializing_if = "Option::is_none")]
1382    pub borders: Option<TableBorderSet>,
1383    #[serde(skip_serializing_if = "Option::is_none")]
1384    pub floating: Option<TableFloating>,
1385}
1386
1387/// Text-only paragraph replacement retained inside an otherwise untouched table.
1388#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1389#[serde(rename_all = "camelCase")]
1390pub struct TableSourceTextPatch {
1391    pub source_paragraph_xml: String,
1392    pub paragraph: ParagraphNode,
1393}
1394
1395/// Mirrors TypeScript `TableNode`.
1396#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1397#[serde(rename_all = "camelCase")]
1398pub struct TableNode {
1399    #[serde(default, skip_serializing_if = "Option::is_none")]
1400    pub review_rows: Option<Vec<crate::revisions::ReviewRow>>,
1401    #[serde(default, skip_serializing_if = "Option::is_none")]
1402    pub block_id: Option<String>,
1403    #[serde(default = "default_table_node_type", rename = "type")]
1404    pub r#type: TableNodeType,
1405    pub rows: Vec<TableRowNode>,
1406    #[serde(skip_serializing_if = "Option::is_none")]
1407    pub style: Option<TableStyle>,
1408    #[serde(skip_serializing_if = "Option::is_none")]
1409    pub source_xml: Option<String>,
1410    #[serde(skip_serializing_if = "Option::is_none")]
1411    pub source_text_patches: Option<Vec<TableSourceTextPatch>>,
1412}
1413
1414/// Discriminator for `TableNode`.
1415#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1416pub enum TableNodeType {
1417    #[serde(rename = "table")]
1418    Table,
1419}
1420
1421/// Mirrors TypeScript `DocNode`.
1422#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1423#[serde(untagged)]
1424pub enum DocNode {
1425    Paragraph(ParagraphNode),
1426    Table(TableNode),
1427}
1428
1429/// Mirrors TypeScript `HeaderSection`.
1430#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1431#[serde(rename_all = "camelCase")]
1432pub struct HeaderSection {
1433    pub part_name: String,
1434    #[serde(skip_serializing_if = "Option::is_none")]
1435    pub reference_type: Option<String>,
1436    pub nodes: Vec<DocNode>,
1437}
1438
1439/// Mirrors TypeScript `FooterSection`.
1440#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1441#[serde(rename_all = "camelCase")]
1442pub struct FooterSection {
1443    pub part_name: String,
1444    #[serde(skip_serializing_if = "Option::is_none")]
1445    pub reference_type: Option<String>,
1446    pub nodes: Vec<DocNode>,
1447}
1448
1449/// Mirrors TypeScript `DocumentSection`.
1450#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1451#[serde(rename_all = "camelCase")]
1452pub struct DocumentSection {
1453    pub start_node_index: i64,
1454    #[serde(skip_serializing_if = "Option::is_none")]
1455    pub section_properties_xml: Option<String>,
1456    pub header_sections: Vec<HeaderSection>,
1457    pub footer_sections: Vec<FooterSection>,
1458}
1459
1460/// Mirrors TypeScript `ParagraphStyleDefinition`.
1461#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1462#[serde(rename_all = "camelCase")]
1463pub struct ParagraphStyleDefinition {
1464    pub id: String,
1465    pub name: String,
1466    #[serde(skip_serializing_if = "Option::is_none")]
1467    pub based_on_id: Option<String>,
1468    #[serde(skip_serializing_if = "Option::is_none")]
1469    pub next_style_id: Option<String>,
1470    #[serde(skip_serializing_if = "Option::is_none")]
1471    pub align: Option<ParagraphAlignment>,
1472    #[serde(skip_serializing_if = "Option::is_none")]
1473    pub snap_to_grid: Option<bool>,
1474    #[serde(skip_serializing_if = "Option::is_none")]
1475    pub source_has_snap_to_grid: Option<bool>,
1476    #[serde(skip_serializing_if = "Option::is_none")]
1477    pub text_alignment: Option<ParagraphTextAlignment>,
1478    #[serde(skip_serializing_if = "Option::is_none")]
1479    pub source_has_text_alignment: Option<bool>,
1480    #[serde(skip_serializing_if = "Option::is_none")]
1481    pub heading_level: Option<HeadingLevel>,
1482    #[serde(skip_serializing_if = "Option::is_none")]
1483    pub numbering: Option<ParagraphNumbering>,
1484    #[serde(skip_serializing_if = "Option::is_none")]
1485    pub spacing: Option<ParagraphSpacing>,
1486    #[serde(skip_serializing_if = "Option::is_none")]
1487    pub indent: Option<ParagraphIndent>,
1488    #[serde(skip_serializing_if = "Option::is_none")]
1489    pub background_color: Option<String>,
1490    #[serde(skip_serializing_if = "Option::is_none")]
1491    pub borders: Option<ParagraphBorderSet>,
1492    #[serde(skip_serializing_if = "Option::is_none")]
1493    pub tab_stops: Option<Vec<ParagraphTabStop>>,
1494    #[serde(skip_serializing_if = "Option::is_none")]
1495    pub contextual_spacing: Option<bool>,
1496    #[serde(skip_serializing_if = "Option::is_none")]
1497    pub keep_next: Option<bool>,
1498    #[serde(skip_serializing_if = "Option::is_none")]
1499    pub keep_lines: Option<bool>,
1500    #[serde(skip_serializing_if = "Option::is_none")]
1501    pub widow_control: Option<bool>,
1502    #[serde(skip_serializing_if = "Option::is_none")]
1503    pub page_break_before: Option<bool>,
1504    #[serde(skip_serializing_if = "Option::is_none")]
1505    pub run_style: Option<TextStyle>,
1506    #[serde(skip_serializing_if = "Option::is_none")]
1507    pub ui_priority: Option<i64>,
1508    #[serde(skip_serializing_if = "Option::is_none")]
1509    pub is_default: Option<bool>,
1510    #[serde(skip_serializing_if = "Option::is_none")]
1511    pub is_primary: Option<bool>,
1512}
1513
1514/// Mirrors TypeScript `NumberingLevelDefinition.suffix`.
1515#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1516pub enum NumberingLevelSuffix {
1517    #[serde(rename = "tab")]
1518    Tab,
1519    #[serde(rename = "space")]
1520    Space,
1521    #[serde(rename = "nothing")]
1522    Nothing,
1523}
1524
1525/// Mirrors TypeScript `NumberingLevelDefinition`.
1526#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1527#[serde(rename_all = "camelCase")]
1528pub struct NumberingLevelDefinition {
1529    pub ilvl: i64,
1530    #[serde(skip_serializing_if = "Option::is_none")]
1531    pub start: Option<i64>,
1532    #[serde(skip_serializing_if = "Option::is_none")]
1533    pub format: Option<String>,
1534    #[serde(skip_serializing_if = "Option::is_none")]
1535    pub text: Option<String>,
1536    #[serde(skip_serializing_if = "Option::is_none")]
1537    pub suffix: Option<NumberingLevelSuffix>,
1538    /// `w:lvlRestart`: one-based level whose use restarts this level; 0 means
1539    /// this level never restarts. Absent means the OOXML default (restart when
1540    /// any shallower level is used).
1541    #[serde(skip_serializing_if = "Option::is_none")]
1542    pub lvl_restart: Option<i64>,
1543    /// `w:isLgl`: legal numbering — every placeholder in this level's text
1544    /// renders as decimal regardless of the referenced levels' formats.
1545    #[serde(skip_serializing_if = "Option::is_none")]
1546    pub is_lgl: Option<bool>,
1547    #[serde(skip_serializing_if = "Option::is_none")]
1548    pub indent: Option<ParagraphIndent>,
1549    #[serde(skip_serializing_if = "Option::is_none")]
1550    pub run_style: Option<TextStyle>,
1551    #[serde(skip_serializing_if = "Option::is_none")]
1552    pub bullet_font_family: Option<String>,
1553    #[serde(skip_serializing_if = "Option::is_none")]
1554    pub bullet_color: Option<String>,
1555    #[serde(skip_serializing_if = "Option::is_none")]
1556    pub picture_bullet_id: Option<i64>,
1557    #[serde(skip_serializing_if = "Option::is_none")]
1558    pub picture_bullet: Option<NumberingPictureBulletDefinition>,
1559}
1560
1561/// Mirrors TypeScript `NumberingAbstractDefinition`.
1562#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1563#[serde(rename_all = "camelCase")]
1564pub struct NumberingAbstractDefinition {
1565    pub abstract_num_id: i64,
1566    pub levels: Vec<NumberingLevelDefinition>,
1567}
1568
1569/// Mirrors TypeScript `NumberingInstanceDefinition`.
1570#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1571#[serde(rename_all = "camelCase")]
1572pub struct NumberingInstanceDefinition {
1573    pub num_id: i64,
1574    pub abstract_num_id: i64,
1575    #[serde(skip_serializing_if = "Option::is_none")]
1576    pub level_start_overrides: Option<HashMap<String, i64>>,
1577    #[serde(skip_serializing_if = "Option::is_none")]
1578    pub level_overrides: Option<Vec<NumberingLevelDefinition>>,
1579}
1580
1581/// Mirrors TypeScript `NumberingDefinitionSet`.
1582#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1583#[serde(rename_all = "camelCase")]
1584pub struct NumberingDefinitionSet {
1585    pub abstracts: Vec<NumberingAbstractDefinition>,
1586    pub instances: Vec<NumberingInstanceDefinition>,
1587}
1588
1589/// Mirrors TypeScript `NumberingPictureBulletDefinition`.
1590#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1591#[serde(rename_all = "camelCase")]
1592pub struct NumberingPictureBulletDefinition {
1593    pub num_pic_bullet_id: i64,
1594    #[serde(skip_serializing_if = "Option::is_none")]
1595    pub src: Option<String>,
1596    #[serde(skip_serializing_if = "Option::is_none")]
1597    pub width_px: Option<f64>,
1598    #[serde(skip_serializing_if = "Option::is_none")]
1599    pub height_px: Option<f64>,
1600    #[serde(skip_serializing_if = "Option::is_none")]
1601    pub part_name: Option<String>,
1602    #[serde(skip_serializing_if = "Option::is_none")]
1603    pub content_type: Option<String>,
1604}
1605
1606/// Mirrors TypeScript `DocumentNoteDefinition`.
1607#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1608#[serde(rename_all = "camelCase")]
1609pub struct DocumentNoteDefinition {
1610    pub id: i64,
1611    pub text: String,
1612    #[serde(skip_serializing_if = "Option::is_none")]
1613    pub nodes: Option<Vec<DocNode>>,
1614}
1615
1616/// Mirrors TypeScript `DocumentCommentDefinition`.
1617#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1618#[serde(rename_all = "camelCase")]
1619pub struct DocumentCommentDefinition {
1620    #[serde(default, skip_serializing_if = "Option::is_none")]
1621    pub nodes: Option<Vec<DocNode>>,
1622    pub id: i64,
1623    #[serde(skip_serializing_if = "Option::is_none")]
1624    pub author: Option<String>,
1625    #[serde(skip_serializing_if = "Option::is_none")]
1626    pub initials: Option<String>,
1627    #[serde(skip_serializing_if = "Option::is_none")]
1628    pub date: Option<String>,
1629    pub text: String,
1630    #[serde(skip_serializing_if = "Option::is_none")]
1631    pub parent_id: Option<i64>,
1632    #[serde(skip_serializing_if = "Option::is_none")]
1633    pub resolved: Option<bool>,
1634    #[serde(skip_serializing_if = "Option::is_none")]
1635    pub source_xml: Option<String>,
1636    #[serde(skip_serializing_if = "Option::is_none")]
1637    pub extended_paragraph_id: Option<String>,
1638    #[serde(skip_serializing_if = "Option::is_none")]
1639    pub source_resolved: Option<bool>,
1640    #[serde(skip_serializing_if = "Option::is_none")]
1641    pub resolution_dirty: Option<bool>,
1642    #[serde(skip_serializing_if = "Option::is_none")]
1643    pub is_new: Option<bool>,
1644}
1645
1646/// Mirrors TypeScript `DocumentCompatibilitySettings`.
1647#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1648#[serde(rename_all = "camelCase")]
1649pub struct DocumentCompatibilitySettings {
1650    #[serde(skip_serializing_if = "Option::is_none")]
1651    pub adjust_line_height_in_table: Option<bool>,
1652    #[serde(skip_serializing_if = "Option::is_none")]
1653    pub no_leading: Option<bool>,
1654    #[serde(
1655        rename = "suppressTopSpacingWP",
1656        skip_serializing_if = "Option::is_none"
1657    )]
1658    pub suppress_top_spacing_wp: Option<bool>,
1659    #[serde(
1660        rename = "truncateFontHeightsLikeWP6",
1661        skip_serializing_if = "Option::is_none"
1662    )]
1663    pub truncate_font_heights_like_wp6: Option<bool>,
1664    #[serde(skip_serializing_if = "Option::is_none")]
1665    pub no_extra_line_spacing: Option<bool>,
1666    #[serde(rename = "spaceForUL", skip_serializing_if = "Option::is_none")]
1667    pub space_for_ul: Option<bool>,
1668    #[serde(skip_serializing_if = "Option::is_none")]
1669    pub suppress_bottom_spacing: Option<bool>,
1670    #[serde(skip_serializing_if = "Option::is_none")]
1671    pub compatibility_mode: Option<i64>,
1672    #[serde(
1673        rename = "enableOpenTypeFeatures",
1674        skip_serializing_if = "Option::is_none"
1675    )]
1676    pub enable_open_type_features: Option<bool>,
1677    #[serde(skip_serializing_if = "Option::is_none")]
1678    pub suppress_spacing_before_after_page_break: Option<bool>,
1679    #[serde(skip_serializing_if = "Option::is_none")]
1680    pub use_printer_metrics: Option<bool>,
1681    #[serde(skip_serializing_if = "Option::is_none")]
1682    pub use_fixed_html_paragraph_spacing: Option<bool>,
1683    #[serde(skip_serializing_if = "Option::is_none")]
1684    pub do_not_break_wrapped_tables: Option<bool>,
1685    #[serde(skip_serializing_if = "Option::is_none")]
1686    pub do_not_break_constrained_forced_table: Option<bool>,
1687    #[serde(skip_serializing_if = "Option::is_none")]
1688    pub even_and_odd_headers: Option<bool>,
1689}
1690
1691/// Mirrors TypeScript `DocModel.metadata`.
1692#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1693#[serde(rename_all = "camelCase")]
1694pub struct DocModelMetadata {
1695    #[serde(default, skip_serializing_if = "Option::is_none")]
1696    pub document_id: Option<String>,
1697    #[serde(default, skip_serializing_if = "Option::is_none")]
1698    pub source_part_names: Option<std::collections::BTreeMap<String, String>>,
1699    pub source_parts: i64,
1700    pub warnings: Vec<String>,
1701    #[serde(skip_serializing_if = "Option::is_none")]
1702    pub document_page_count: Option<i64>,
1703    #[serde(skip_serializing_if = "Option::is_none")]
1704    pub document_open_tag: Option<String>,
1705    #[serde(skip_serializing_if = "Option::is_none")]
1706    pub document_background_color: Option<String>,
1707    #[serde(skip_serializing_if = "Option::is_none")]
1708    pub section_properties_xml: Option<String>,
1709    #[serde(skip_serializing_if = "Option::is_none")]
1710    pub sections: Option<Vec<DocumentSection>>,
1711    pub header_sections: Vec<HeaderSection>,
1712    pub footer_sections: Vec<FooterSection>,
1713    pub paragraph_styles: Vec<ParagraphStyleDefinition>,
1714    #[serde(skip_serializing_if = "Option::is_none")]
1715    pub default_paragraph_style_id: Option<String>,
1716    #[serde(skip_serializing_if = "Option::is_none")]
1717    pub default_tab_stop_twips: Option<i64>,
1718    #[serde(skip_serializing_if = "Option::is_none")]
1719    pub document_settings_imported: Option<bool>,
1720    #[serde(skip_serializing_if = "Option::is_none")]
1721    pub line_spacing_compatibility_imported: Option<bool>,
1722    #[serde(skip_serializing_if = "Option::is_none")]
1723    pub numbering_definitions: Option<NumberingDefinitionSet>,
1724    #[serde(skip_serializing_if = "Option::is_none")]
1725    pub compatibility: Option<DocumentCompatibilitySettings>,
1726    #[serde(skip_serializing_if = "Option::is_none")]
1727    pub footnotes: Option<Vec<DocumentNoteDefinition>>,
1728    #[serde(skip_serializing_if = "Option::is_none")]
1729    pub endnotes: Option<Vec<DocumentNoteDefinition>>,
1730    #[serde(skip_serializing_if = "Option::is_none")]
1731    pub comments: Option<Vec<DocumentCommentDefinition>>,
1732}
1733
1734/// Mirrors TypeScript `DocModel`.
1735#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1736#[serde(rename_all = "camelCase")]
1737pub struct DocModel {
1738    pub nodes: Vec<DocNode>,
1739    pub metadata: DocModelMetadata,
1740}
1741
1742#[cfg(test)]
1743mod deserialize_tests {
1744    use super::{DocModel, DocNode, HeadingLevel, ParagraphAlignment};
1745
1746    #[test]
1747    fn deserializes_minimal_js_doc_model() {
1748        let json = r#"{"nodes":[{"type":"paragraph","children":[{"type":"text","text":"Hi"}]}],"metadata":{"sourceParts":1,"warnings":[],"headerSections":[],"footerSections":[],"paragraphStyles":[]}}"#;
1749        let model: DocModel = serde_json::from_str(json).expect("should deserialize");
1750        assert_eq!(model.nodes.len(), 1);
1751    }
1752
1753    #[test]
1754    fn deserializes_heading_level_two() {
1755        let json = r#"{"type":"paragraph","style":{"headingLevel":2},"children":[{"type":"text","text":"Added"}]}"#;
1756        let node: DocNode = serde_json::from_str(json).expect("heading level 2");
1757        let DocNode::Paragraph(paragraph) = node else {
1758            panic!("expected paragraph");
1759        };
1760        assert_eq!(
1761            paragraph.style.and_then(|style| style.heading_level),
1762            Some(HeadingLevel::Two)
1763        );
1764    }
1765
1766    #[test]
1767    fn deserializes_align_center() {
1768        let json = r#"{"type":"paragraph","style":{"align":"center"},"children":[{"type":"text","text":"Added"}]}"#;
1769        let node: DocNode = serde_json::from_str(json).expect("align center");
1770        let DocNode::Paragraph(paragraph) = node else {
1771            panic!("expected paragraph");
1772        };
1773        assert_eq!(
1774            paragraph.style.and_then(|style| style.align),
1775            Some(ParagraphAlignment::Center)
1776        );
1777    }
1778}
1779
1780/// Fully resolved formatting shared until a caller edits its value.
1781#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1782#[serde(transparent)]
1783pub struct SharedParagraphStyle(std::sync::Arc<ParagraphStyle>);
1784
1785impl SharedParagraphStyle {
1786    pub fn into_owned(self) -> ParagraphStyle {
1787        std::sync::Arc::unwrap_or_clone(self.0)
1788    }
1789}
1790
1791impl From<ParagraphStyle> for SharedParagraphStyle {
1792    fn from(style: ParagraphStyle) -> Self {
1793        Self(std::sync::Arc::new(style))
1794    }
1795}
1796
1797impl std::ops::Deref for SharedParagraphStyle {
1798    type Target = ParagraphStyle;
1799
1800    fn deref(&self) -> &ParagraphStyle {
1801        &self.0
1802    }
1803}
1804
1805impl std::ops::DerefMut for SharedParagraphStyle {
1806    fn deref_mut(&mut self) -> &mut ParagraphStyle {
1807        std::sync::Arc::make_mut(&mut self.0)
1808    }
1809}