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 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    /// Import-time `font_family` value used to distinguish a later edit through
210    /// the backwards-compatible single-family API from the preserved slots.
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub source_font_family: Option<String>,
213    /// Explicit OOXML `w:rFonts` slots. `font_family` remains the resolved
214    /// single-family fallback used by existing renderers and callers.
215    #[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    /// OOXML `w:lang` slots plus run-direction flags that participate in font
242    /// selection for complex-script text.
243    #[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/// Fully resolved formatting shared until a caller edits its value.
272#[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/// Mirrors TypeScript `TextRunNode.noteReference`.
303#[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/// Mirrors TypeScript `TextRunNode.noteReference.kind`.
311#[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/// Mirrors TypeScript `TextRunNode`.
320#[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/// Discriminator for `TextRunNode`.
335#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
336pub enum TextRunNodeType {
337    #[serde(rename = "text")]
338    Text,
339}
340
341/// Mirrors TypeScript `ImageRunNode.crop`.
342#[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/// Mirrors TypeScript `ImageRunNode.floating.horizontalAlign`.
356#[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/// Mirrors TypeScript `ImageRunNode.floating.verticalAlign`.
367#[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/// Mirrors TypeScript `ImageRunNode.floating.wrapType`.
378#[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/// Mirrors TypeScript `ImageRunNode.floating.wrapText`.
394#[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/// Mirrors TypeScript `ImageRunNode.floating`.
408#[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/// Ordered presentation data, including extension properties not rendered by the viewer.
460#[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/// Parsed properties describe source intent independently of rendering coverage.
521#[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/// Mirrors TypeScript `ImageRunNode`.
541#[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/// Discriminator for `ImageRunNode`.
593#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
594pub enum ImageRunNodeType {
595    #[serde(rename = "image")]
596    Image,
597}
598
599/// Mirrors TypeScript `FormFieldType`.
600#[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/// Mirrors TypeScript `FormFieldSourceKind`.
614#[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/// Mirrors TypeScript `FormFieldOption`.
623#[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/// Mirrors TypeScript `FormFieldTextWidgetSettings`.
632#[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/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings`.
646#[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/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings.sizeMode`.
658#[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/// Mirrors TypeScript `FormFieldDropdownWidgetSettings`.
667#[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/// Mirrors TypeScript `FormFieldWidgetSettings`.
675#[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/// Mirrors TypeScript `FormFieldRunNode`.
702#[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/// Discriminator for `FormFieldRunNode`.
741#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
742pub enum FormFieldRunNodeType {
743    #[serde(rename = "form-field")]
744    FormField,
745}
746
747/// Mirrors TypeScript `ParagraphChildNode`.
748#[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/// Mirrors TypeScript `ParagraphNumbering`.
757#[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/// Mirrors TypeScript `ParagraphSpacing.lineRule`.
765#[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/// Mirrors TypeScript `ParagraphSpacing`.
776#[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/// Mirrors TypeScript `ParagraphIndent`.
794#[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/// Mirrors TypeScript `ParagraphBorderStyle`.
808#[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/// Mirrors TypeScript `ParagraphBorderSet`.
826#[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/// Mirrors TypeScript `ParagraphTabStop.alignment`.
844#[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/// Mirrors TypeScript `ParagraphTabStop.leader`.
856#[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/// Mirrors TypeScript `ParagraphTabStop`.
871#[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/// Mirrors TypeScript `ParagraphStyle.dropCap.type`.
883#[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/// Mirrors TypeScript `ParagraphStyle.dropCap`.
892#[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/// Mirrors TypeScript `ParagraphStyle`.
926#[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/// Text-independent run provenance for a validated surgical source XML patch.
988#[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/// Imported paragraph-mark default, character-style, and direct layers.
1006#[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/// Mirrors TypeScript `ParagraphNode`.
1018#[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 review_content: Option<Vec<crate::revisions::ReviewContent>>,
1023    #[serde(default, skip_serializing_if = "Option::is_none")]
1024    pub block_id: Option<String>,
1025    #[serde(default = "default_paragraph_node_type", rename = "type")]
1026    pub r#type: ParagraphNodeType,
1027    pub children: Vec<ParagraphChildNode>,
1028    #[serde(skip_serializing_if = "Option::is_none")]
1029    pub style: Option<SharedParagraphStyle>,
1030    #[serde(skip_serializing_if = "Option::is_none")]
1031    pub paragraph_mark_style: Option<SharedTextStyle>,
1032    #[serde(skip_serializing_if = "Option::is_none")]
1033    pub source_paragraph_mark_style: Option<SharedTextStyle>,
1034    #[serde(skip_serializing_if = "Option::is_none")]
1035    pub source_paragraph_mark_formatting: Option<ParagraphMarkFormattingSource>,
1036    #[serde(skip_serializing_if = "Option::is_none")]
1037    pub source_paragraph_mark_properties_xml: Option<String>,
1038    #[serde(skip_serializing_if = "Option::is_none")]
1039    pub paragraph_mark_deleted: Option<bool>,
1040    #[serde(skip_serializing_if = "Option::is_none")]
1041    pub source_xml: Option<String>,
1042    #[serde(skip_serializing_if = "Option::is_none")]
1043    pub source_text_patch: Option<ParagraphSourceTextPatch>,
1044    /// Immutable import-time run metadata used to prove that a later surgical
1045    /// annotation edit is still targeting the exact parsed run state.
1046    #[serde(skip_serializing_if = "Option::is_none")]
1047    pub source_run_provenance: Option<ParagraphSourceTextPatch>,
1048}
1049
1050/// Discriminator for `ParagraphNode`.
1051#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1052pub enum ParagraphNodeType {
1053    #[serde(rename = "paragraph")]
1054    Paragraph,
1055}
1056
1057/// Mirrors TypeScript `TableBoxSpacing`.
1058#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1059#[serde(rename_all = "camelCase")]
1060pub struct TableBoxSpacing {
1061    #[serde(skip_serializing_if = "Option::is_none")]
1062    pub top_twips: Option<i64>,
1063    #[serde(skip_serializing_if = "Option::is_none")]
1064    pub right_twips: Option<i64>,
1065    #[serde(skip_serializing_if = "Option::is_none")]
1066    pub bottom_twips: Option<i64>,
1067    #[serde(skip_serializing_if = "Option::is_none")]
1068    pub left_twips: Option<i64>,
1069}
1070
1071/// Mirrors TypeScript `TableBorderStyle`.
1072#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1073#[serde(rename_all = "camelCase")]
1074pub struct TableBorderStyle {
1075    #[serde(rename = "type")]
1076    pub border_type: String,
1077    #[serde(skip_serializing_if = "Option::is_none")]
1078    pub color: Option<String>,
1079    #[serde(skip_serializing_if = "Option::is_none")]
1080    pub size_eighth_pt: Option<f64>,
1081}
1082
1083/// Mirrors TypeScript `TableBorderSet`.
1084#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1085#[serde(rename_all = "camelCase")]
1086pub struct TableBorderSet {
1087    #[serde(skip_serializing_if = "Option::is_none")]
1088    pub top: Option<TableBorderStyle>,
1089    #[serde(skip_serializing_if = "Option::is_none")]
1090    pub right: Option<TableBorderStyle>,
1091    #[serde(skip_serializing_if = "Option::is_none")]
1092    pub bottom: Option<TableBorderStyle>,
1093    #[serde(skip_serializing_if = "Option::is_none")]
1094    pub left: Option<TableBorderStyle>,
1095    #[serde(skip_serializing_if = "Option::is_none")]
1096    pub inside_h: Option<TableBorderStyle>,
1097    #[serde(skip_serializing_if = "Option::is_none")]
1098    pub inside_v: Option<TableBorderStyle>,
1099    #[serde(skip_serializing_if = "Option::is_none")]
1100    pub tl2br: Option<TableBorderStyle>,
1101    #[serde(skip_serializing_if = "Option::is_none")]
1102    pub tr2bl: Option<TableBorderStyle>,
1103}
1104
1105/// Mirrors TypeScript `TableCellStyle.verticalAlign`.
1106#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1107#[serde(rename_all = "camelCase")]
1108pub enum TableCellVerticalAlign {
1109    Top,
1110    Center,
1111    Bottom,
1112}
1113
1114/// Preferred width with an explicit measurement kind.
1115#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1116#[serde(tag = "type", rename_all = "camelCase")]
1117pub enum TablePreferredWidth {
1118    Dxa { value: f64 },
1119    Pct { value: f64 },
1120    Auto,
1121    Nil,
1122}
1123
1124/// Imported width baseline used to distinguish edits to either width API.
1125#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1126#[serde(rename_all = "camelCase")]
1127pub struct TableWidthSource {
1128    #[serde(skip_serializing_if = "Option::is_none")]
1129    pub preferred_width: Option<TablePreferredWidth>,
1130    #[serde(skip_serializing_if = "Option::is_none")]
1131    pub width_twips: Option<i64>,
1132    #[serde(skip_serializing_if = "Option::is_none")]
1133    pub inherited_preferred_width: Option<TablePreferredWidth>,
1134}
1135
1136#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1137#[serde(rename_all = "camelCase")]
1138pub enum TableAlignment {
1139    Left,
1140    Center,
1141    Right,
1142}
1143
1144/// Mirrors TypeScript `TableCellStyle`.
1145#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1146#[serde(rename_all = "camelCase")]
1147pub struct TableCellStyle {
1148    #[serde(skip_serializing_if = "Option::is_none")]
1149    pub background_color: Option<String>,
1150    #[serde(skip_serializing_if = "Option::is_none")]
1151    pub grid_span: Option<i64>,
1152    #[serde(skip_serializing_if = "Option::is_none")]
1153    pub row_span: Option<i64>,
1154    #[serde(skip_serializing_if = "Option::is_none")]
1155    pub v_merge_continuation: Option<bool>,
1156    #[serde(skip_serializing_if = "Option::is_none")]
1157    pub width_twips: Option<i64>,
1158    #[serde(skip_serializing_if = "Option::is_none")]
1159    pub preferred_width: Option<TablePreferredWidth>,
1160    #[serde(skip_serializing_if = "Option::is_none")]
1161    pub source_width: Option<TableWidthSource>,
1162    #[serde(skip_serializing_if = "Option::is_none")]
1163    pub margin_twips: Option<TableBoxSpacing>,
1164    #[serde(skip_serializing_if = "Option::is_none")]
1165    pub vertical_align: Option<TableCellVerticalAlign>,
1166    #[serde(skip_serializing_if = "Option::is_none")]
1167    pub text_direction: Option<String>,
1168    #[serde(skip_serializing_if = "Option::is_none")]
1169    pub source_text_direction: Option<String>,
1170    #[serde(skip_serializing_if = "Option::is_none")]
1171    pub no_wrap: Option<bool>,
1172    #[serde(skip_serializing_if = "Option::is_none")]
1173    pub source_no_wrap: Option<bool>,
1174    #[serde(skip_serializing_if = "Option::is_none")]
1175    pub borders: Option<TableBorderSet>,
1176}
1177
1178/// Mirrors TypeScript `TableCellContentNode`.
1179#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1180#[serde(untagged)]
1181pub enum TableCellContentNode {
1182    Paragraph(ParagraphNode),
1183    Table(Box<TableNode>),
1184}
1185
1186/// Mirrors TypeScript `TableCellNode`.
1187#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1188#[serde(rename_all = "camelCase")]
1189pub struct TableCellNode {
1190    #[serde(default = "default_table_cell_node_type", rename = "type")]
1191    pub r#type: TableCellNodeType,
1192    pub nodes: Vec<TableCellContentNode>,
1193    #[serde(skip_serializing_if = "Option::is_none")]
1194    pub style: Option<TableCellStyle>,
1195}
1196
1197/// Discriminator for `TableCellNode`.
1198#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1199pub enum TableCellNodeType {
1200    #[serde(rename = "table-cell")]
1201    TableCell,
1202}
1203
1204/// Mirrors TypeScript `TableRowStyle.heightRule`.
1205#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1206pub enum TableRowHeightRule {
1207    #[serde(rename = "auto")]
1208    Auto,
1209    #[serde(rename = "atLeast")]
1210    AtLeast,
1211    #[serde(rename = "exact")]
1212    Exact,
1213}
1214
1215/// Imported row geometry used to distinguish a clear from an omitted legacy field.
1216#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1217#[serde(rename_all = "camelCase")]
1218pub struct TableRowGeometrySource {
1219    #[serde(skip_serializing_if = "Option::is_none")]
1220    pub grid_before: Option<i64>,
1221    #[serde(skip_serializing_if = "Option::is_none")]
1222    pub grid_after: Option<i64>,
1223    #[serde(skip_serializing_if = "Option::is_none")]
1224    pub width_before: Option<TablePreferredWidth>,
1225    #[serde(skip_serializing_if = "Option::is_none")]
1226    pub width_after: Option<TablePreferredWidth>,
1227}
1228
1229/// Mirrors TypeScript `TableRowStyle`.
1230#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1231#[serde(rename_all = "camelCase")]
1232pub struct TableRowStyle {
1233    #[serde(skip_serializing_if = "Option::is_none")]
1234    pub grid_before: Option<i64>,
1235    #[serde(skip_serializing_if = "Option::is_none")]
1236    pub grid_after: Option<i64>,
1237    #[serde(skip_serializing_if = "Option::is_none")]
1238    pub width_before: Option<TablePreferredWidth>,
1239    #[serde(skip_serializing_if = "Option::is_none")]
1240    pub width_after: Option<TablePreferredWidth>,
1241    #[serde(skip_serializing_if = "Option::is_none")]
1242    pub source_row_geometry: Option<TableRowGeometrySource>,
1243    #[serde(skip_serializing_if = "Option::is_none")]
1244    pub background_color: Option<String>,
1245    #[serde(skip_serializing_if = "Option::is_none")]
1246    pub height_twips: Option<i64>,
1247    #[serde(skip_serializing_if = "Option::is_none")]
1248    pub height_rule: Option<TableRowHeightRule>,
1249    #[serde(skip_serializing_if = "Option::is_none")]
1250    pub cant_split: Option<bool>,
1251    #[serde(skip_serializing_if = "Option::is_none")]
1252    pub is_header: Option<bool>,
1253}
1254
1255/// Mirrors TypeScript `TableRowNode`.
1256#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1257#[serde(rename_all = "camelCase")]
1258pub struct TableRowNode {
1259    #[serde(default = "default_table_row_node_type", rename = "type")]
1260    pub r#type: TableRowNodeType,
1261    pub cells: Vec<TableCellNode>,
1262    #[serde(skip_serializing_if = "Option::is_none")]
1263    pub style: Option<TableRowStyle>,
1264}
1265
1266/// Discriminator for `TableRowNode`.
1267#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1268pub enum TableRowNodeType {
1269    #[serde(rename = "table-row")]
1270    TableRow,
1271}
1272
1273/// Mirrors TypeScript `TableStyle.layout`.
1274#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1275pub enum TableLayout {
1276    #[serde(rename = "fixed")]
1277    Fixed,
1278    #[serde(rename = "autofit")]
1279    Autofit,
1280}
1281
1282/// Mirrors TypeScript `TableStyle.floating`.
1283#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1284#[serde(rename_all = "camelCase")]
1285pub struct TableFloating {
1286    #[serde(skip_serializing_if = "Option::is_none")]
1287    pub x_twips: Option<i64>,
1288    #[serde(skip_serializing_if = "Option::is_none")]
1289    pub y_twips: Option<i64>,
1290    #[serde(skip_serializing_if = "Option::is_none")]
1291    pub left_from_text_twips: Option<i64>,
1292    #[serde(skip_serializing_if = "Option::is_none")]
1293    pub right_from_text_twips: Option<i64>,
1294    #[serde(skip_serializing_if = "Option::is_none")]
1295    pub top_from_text_twips: Option<i64>,
1296    #[serde(skip_serializing_if = "Option::is_none")]
1297    pub bottom_from_text_twips: Option<i64>,
1298    #[serde(skip_serializing_if = "Option::is_none")]
1299    pub horizontal_anchor: Option<String>,
1300    #[serde(skip_serializing_if = "Option::is_none")]
1301    pub vertical_anchor: Option<String>,
1302    #[serde(skip_serializing_if = "Option::is_none")]
1303    pub horizontal_align: Option<ImageHorizontalAlign>,
1304    #[serde(skip_serializing_if = "Option::is_none")]
1305    pub vertical_align: Option<ImageVerticalAlign>,
1306}
1307
1308/// Mirrors TypeScript `TableStyle`.
1309#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1310#[serde(rename_all = "camelCase")]
1311pub struct TableStyle {
1312    #[serde(skip_serializing_if = "Option::is_none")]
1313    pub style_id: Option<String>,
1314    #[serde(skip_serializing_if = "Option::is_none")]
1315    pub source_style_id: Option<String>,
1316    #[serde(skip_serializing_if = "Option::is_none")]
1317    pub width_twips: Option<i64>,
1318    #[serde(skip_serializing_if = "Option::is_none")]
1319    pub preferred_width: Option<TablePreferredWidth>,
1320    #[serde(skip_serializing_if = "Option::is_none")]
1321    pub source_width: Option<TableWidthSource>,
1322    #[serde(skip_serializing_if = "Option::is_none")]
1323    pub alignment: Option<TableAlignment>,
1324    #[serde(skip_serializing_if = "Option::is_none")]
1325    pub source_alignment: Option<TableAlignment>,
1326    #[serde(skip_serializing_if = "Option::is_none")]
1327    pub source_inherited_alignment: Option<TableAlignment>,
1328    #[serde(skip_serializing_if = "Option::is_none")]
1329    pub bidi_visual: Option<bool>,
1330    #[serde(skip_serializing_if = "Option::is_none")]
1331    pub source_bidi_visual: Option<bool>,
1332    #[serde(skip_serializing_if = "Option::is_none")]
1333    pub source_inherited_bidi_visual: Option<bool>,
1334    #[serde(skip_serializing_if = "Option::is_none")]
1335    pub indent_twips: Option<i64>,
1336    #[serde(skip_serializing_if = "Option::is_none")]
1337    pub layout: Option<TableLayout>,
1338    #[serde(skip_serializing_if = "Option::is_none")]
1339    pub cell_spacing_twips: Option<i64>,
1340    #[serde(skip_serializing_if = "Option::is_none")]
1341    pub cell_margin_twips: Option<TableBoxSpacing>,
1342    #[serde(skip_serializing_if = "Option::is_none")]
1343    pub column_widths_twips: Option<Vec<i64>>,
1344    /// Logical intervals with coincident physical boundaries. Project these
1345    /// columns for layout and materialize them before structural edits.
1346    #[serde(skip_serializing_if = "Option::is_none")]
1347    pub collapsed_grid_columns: Option<Vec<usize>>,
1348    #[serde(skip_serializing_if = "Option::is_none")]
1349    pub borders: Option<TableBorderSet>,
1350    #[serde(skip_serializing_if = "Option::is_none")]
1351    pub floating: Option<TableFloating>,
1352}
1353
1354/// Text-only paragraph replacement retained inside an otherwise untouched table.
1355#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1356#[serde(rename_all = "camelCase")]
1357pub struct TableSourceTextPatch {
1358    pub source_paragraph_xml: String,
1359    pub paragraph: ParagraphNode,
1360}
1361
1362/// Mirrors TypeScript `TableNode`.
1363#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1364#[serde(rename_all = "camelCase")]
1365pub struct TableNode {
1366    #[serde(default, skip_serializing_if = "Option::is_none")]
1367    pub review_rows: Option<Vec<crate::revisions::ReviewRow>>,
1368    #[serde(default, skip_serializing_if = "Option::is_none")]
1369    pub block_id: Option<String>,
1370    #[serde(default = "default_table_node_type", rename = "type")]
1371    pub r#type: TableNodeType,
1372    pub rows: Vec<TableRowNode>,
1373    #[serde(skip_serializing_if = "Option::is_none")]
1374    pub style: Option<TableStyle>,
1375    #[serde(skip_serializing_if = "Option::is_none")]
1376    pub source_xml: Option<String>,
1377    #[serde(skip_serializing_if = "Option::is_none")]
1378    pub source_text_patches: Option<Vec<TableSourceTextPatch>>,
1379}
1380
1381/// Discriminator for `TableNode`.
1382#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1383pub enum TableNodeType {
1384    #[serde(rename = "table")]
1385    Table,
1386}
1387
1388/// Mirrors TypeScript `DocNode`.
1389#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1390#[serde(untagged)]
1391pub enum DocNode {
1392    Paragraph(ParagraphNode),
1393    Table(TableNode),
1394}
1395
1396/// Mirrors TypeScript `HeaderSection`.
1397#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1398#[serde(rename_all = "camelCase")]
1399pub struct HeaderSection {
1400    pub part_name: String,
1401    #[serde(skip_serializing_if = "Option::is_none")]
1402    pub reference_type: Option<String>,
1403    pub nodes: Vec<DocNode>,
1404}
1405
1406/// Mirrors TypeScript `FooterSection`.
1407#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1408#[serde(rename_all = "camelCase")]
1409pub struct FooterSection {
1410    pub part_name: String,
1411    #[serde(skip_serializing_if = "Option::is_none")]
1412    pub reference_type: Option<String>,
1413    pub nodes: Vec<DocNode>,
1414}
1415
1416/// Mirrors TypeScript `DocumentSection`.
1417#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1418#[serde(rename_all = "camelCase")]
1419pub struct DocumentSection {
1420    pub start_node_index: i64,
1421    #[serde(skip_serializing_if = "Option::is_none")]
1422    pub section_properties_xml: Option<String>,
1423    pub header_sections: Vec<HeaderSection>,
1424    pub footer_sections: Vec<FooterSection>,
1425}
1426
1427/// Mirrors TypeScript `ParagraphStyleDefinition`.
1428#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1429#[serde(rename_all = "camelCase")]
1430pub struct ParagraphStyleDefinition {
1431    pub id: String,
1432    pub name: String,
1433    #[serde(skip_serializing_if = "Option::is_none")]
1434    pub based_on_id: Option<String>,
1435    #[serde(skip_serializing_if = "Option::is_none")]
1436    pub next_style_id: Option<String>,
1437    #[serde(skip_serializing_if = "Option::is_none")]
1438    pub align: Option<ParagraphAlignment>,
1439    #[serde(skip_serializing_if = "Option::is_none")]
1440    pub snap_to_grid: Option<bool>,
1441    #[serde(skip_serializing_if = "Option::is_none")]
1442    pub source_has_snap_to_grid: Option<bool>,
1443    #[serde(skip_serializing_if = "Option::is_none")]
1444    pub text_alignment: Option<ParagraphTextAlignment>,
1445    #[serde(skip_serializing_if = "Option::is_none")]
1446    pub source_has_text_alignment: Option<bool>,
1447    #[serde(skip_serializing_if = "Option::is_none")]
1448    pub heading_level: Option<HeadingLevel>,
1449    #[serde(skip_serializing_if = "Option::is_none")]
1450    pub numbering: Option<ParagraphNumbering>,
1451    #[serde(skip_serializing_if = "Option::is_none")]
1452    pub spacing: Option<ParagraphSpacing>,
1453    #[serde(skip_serializing_if = "Option::is_none")]
1454    pub indent: Option<ParagraphIndent>,
1455    #[serde(skip_serializing_if = "Option::is_none")]
1456    pub background_color: Option<String>,
1457    #[serde(skip_serializing_if = "Option::is_none")]
1458    pub borders: Option<ParagraphBorderSet>,
1459    #[serde(skip_serializing_if = "Option::is_none")]
1460    pub tab_stops: Option<Vec<ParagraphTabStop>>,
1461    #[serde(skip_serializing_if = "Option::is_none")]
1462    pub contextual_spacing: Option<bool>,
1463    #[serde(skip_serializing_if = "Option::is_none")]
1464    pub keep_next: Option<bool>,
1465    #[serde(skip_serializing_if = "Option::is_none")]
1466    pub keep_lines: Option<bool>,
1467    #[serde(skip_serializing_if = "Option::is_none")]
1468    pub widow_control: Option<bool>,
1469    #[serde(skip_serializing_if = "Option::is_none")]
1470    pub page_break_before: Option<bool>,
1471    #[serde(skip_serializing_if = "Option::is_none")]
1472    pub run_style: Option<TextStyle>,
1473    #[serde(skip_serializing_if = "Option::is_none")]
1474    pub ui_priority: Option<i64>,
1475    #[serde(skip_serializing_if = "Option::is_none")]
1476    pub is_default: Option<bool>,
1477    #[serde(skip_serializing_if = "Option::is_none")]
1478    pub is_primary: Option<bool>,
1479}
1480
1481/// Mirrors TypeScript `NumberingLevelDefinition.suffix`.
1482#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1483pub enum NumberingLevelSuffix {
1484    #[serde(rename = "tab")]
1485    Tab,
1486    #[serde(rename = "space")]
1487    Space,
1488    #[serde(rename = "nothing")]
1489    Nothing,
1490}
1491
1492/// Mirrors TypeScript `NumberingLevelDefinition`.
1493#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1494#[serde(rename_all = "camelCase")]
1495pub struct NumberingLevelDefinition {
1496    pub ilvl: i64,
1497    #[serde(skip_serializing_if = "Option::is_none")]
1498    pub start: Option<i64>,
1499    #[serde(skip_serializing_if = "Option::is_none")]
1500    pub format: Option<String>,
1501    #[serde(skip_serializing_if = "Option::is_none")]
1502    pub text: Option<String>,
1503    #[serde(skip_serializing_if = "Option::is_none")]
1504    pub suffix: Option<NumberingLevelSuffix>,
1505    /// `w:lvlRestart`: one-based level whose use restarts this level; 0 means
1506    /// this level never restarts. Absent means the OOXML default (restart when
1507    /// any shallower level is used).
1508    #[serde(skip_serializing_if = "Option::is_none")]
1509    pub lvl_restart: Option<i64>,
1510    /// `w:isLgl`: legal numbering — every placeholder in this level's text
1511    /// renders as decimal regardless of the referenced levels' formats.
1512    #[serde(skip_serializing_if = "Option::is_none")]
1513    pub is_lgl: Option<bool>,
1514    #[serde(skip_serializing_if = "Option::is_none")]
1515    pub indent: Option<ParagraphIndent>,
1516    #[serde(skip_serializing_if = "Option::is_none")]
1517    pub run_style: Option<TextStyle>,
1518    #[serde(skip_serializing_if = "Option::is_none")]
1519    pub bullet_font_family: Option<String>,
1520    #[serde(skip_serializing_if = "Option::is_none")]
1521    pub bullet_color: Option<String>,
1522    #[serde(skip_serializing_if = "Option::is_none")]
1523    pub picture_bullet_id: Option<i64>,
1524    #[serde(skip_serializing_if = "Option::is_none")]
1525    pub picture_bullet: Option<NumberingPictureBulletDefinition>,
1526}
1527
1528/// Mirrors TypeScript `NumberingAbstractDefinition`.
1529#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1530#[serde(rename_all = "camelCase")]
1531pub struct NumberingAbstractDefinition {
1532    pub abstract_num_id: i64,
1533    pub levels: Vec<NumberingLevelDefinition>,
1534}
1535
1536/// Mirrors TypeScript `NumberingInstanceDefinition`.
1537#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1538#[serde(rename_all = "camelCase")]
1539pub struct NumberingInstanceDefinition {
1540    pub num_id: i64,
1541    pub abstract_num_id: i64,
1542    #[serde(skip_serializing_if = "Option::is_none")]
1543    pub level_start_overrides: Option<HashMap<String, i64>>,
1544    #[serde(skip_serializing_if = "Option::is_none")]
1545    pub level_overrides: Option<Vec<NumberingLevelDefinition>>,
1546}
1547
1548/// Mirrors TypeScript `NumberingDefinitionSet`.
1549#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1550#[serde(rename_all = "camelCase")]
1551pub struct NumberingDefinitionSet {
1552    pub abstracts: Vec<NumberingAbstractDefinition>,
1553    pub instances: Vec<NumberingInstanceDefinition>,
1554}
1555
1556/// Mirrors TypeScript `NumberingPictureBulletDefinition`.
1557#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1558#[serde(rename_all = "camelCase")]
1559pub struct NumberingPictureBulletDefinition {
1560    pub num_pic_bullet_id: i64,
1561    #[serde(skip_serializing_if = "Option::is_none")]
1562    pub src: Option<String>,
1563    #[serde(skip_serializing_if = "Option::is_none")]
1564    pub width_px: Option<f64>,
1565    #[serde(skip_serializing_if = "Option::is_none")]
1566    pub height_px: Option<f64>,
1567    #[serde(skip_serializing_if = "Option::is_none")]
1568    pub part_name: Option<String>,
1569    #[serde(skip_serializing_if = "Option::is_none")]
1570    pub content_type: Option<String>,
1571}
1572
1573/// Mirrors TypeScript `DocumentNoteDefinition`.
1574#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1575#[serde(rename_all = "camelCase")]
1576pub struct DocumentNoteDefinition {
1577    pub id: i64,
1578    pub text: String,
1579    #[serde(skip_serializing_if = "Option::is_none")]
1580    pub nodes: Option<Vec<DocNode>>,
1581}
1582
1583/// Mirrors TypeScript `DocumentCommentDefinition`.
1584#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1585#[serde(rename_all = "camelCase")]
1586pub struct DocumentCommentDefinition {
1587    #[serde(default, skip_serializing_if = "Option::is_none")]
1588    pub nodes: Option<Vec<DocNode>>,
1589    pub id: i64,
1590    #[serde(skip_serializing_if = "Option::is_none")]
1591    pub author: Option<String>,
1592    #[serde(skip_serializing_if = "Option::is_none")]
1593    pub initials: Option<String>,
1594    #[serde(skip_serializing_if = "Option::is_none")]
1595    pub date: Option<String>,
1596    pub text: String,
1597    #[serde(skip_serializing_if = "Option::is_none")]
1598    pub parent_id: Option<i64>,
1599    #[serde(skip_serializing_if = "Option::is_none")]
1600    pub resolved: Option<bool>,
1601    #[serde(skip_serializing_if = "Option::is_none")]
1602    pub source_xml: Option<String>,
1603    #[serde(skip_serializing_if = "Option::is_none")]
1604    pub extended_paragraph_id: Option<String>,
1605    #[serde(skip_serializing_if = "Option::is_none")]
1606    pub source_resolved: Option<bool>,
1607    #[serde(skip_serializing_if = "Option::is_none")]
1608    pub resolution_dirty: Option<bool>,
1609    #[serde(skip_serializing_if = "Option::is_none")]
1610    pub is_new: Option<bool>,
1611}
1612
1613/// Mirrors TypeScript `DocumentCompatibilitySettings`.
1614#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1615#[serde(rename_all = "camelCase")]
1616pub struct DocumentCompatibilitySettings {
1617    #[serde(skip_serializing_if = "Option::is_none")]
1618    pub adjust_line_height_in_table: Option<bool>,
1619    #[serde(skip_serializing_if = "Option::is_none")]
1620    pub no_leading: Option<bool>,
1621    #[serde(
1622        rename = "suppressTopSpacingWP",
1623        skip_serializing_if = "Option::is_none"
1624    )]
1625    pub suppress_top_spacing_wp: Option<bool>,
1626    #[serde(
1627        rename = "truncateFontHeightsLikeWP6",
1628        skip_serializing_if = "Option::is_none"
1629    )]
1630    pub truncate_font_heights_like_wp6: Option<bool>,
1631    #[serde(skip_serializing_if = "Option::is_none")]
1632    pub no_extra_line_spacing: Option<bool>,
1633    #[serde(rename = "spaceForUL", skip_serializing_if = "Option::is_none")]
1634    pub space_for_ul: Option<bool>,
1635    #[serde(skip_serializing_if = "Option::is_none")]
1636    pub suppress_bottom_spacing: Option<bool>,
1637    #[serde(skip_serializing_if = "Option::is_none")]
1638    pub compatibility_mode: Option<i64>,
1639    #[serde(
1640        rename = "enableOpenTypeFeatures",
1641        skip_serializing_if = "Option::is_none"
1642    )]
1643    pub enable_open_type_features: Option<bool>,
1644    #[serde(skip_serializing_if = "Option::is_none")]
1645    pub suppress_spacing_before_after_page_break: Option<bool>,
1646    #[serde(skip_serializing_if = "Option::is_none")]
1647    pub use_printer_metrics: Option<bool>,
1648    #[serde(skip_serializing_if = "Option::is_none")]
1649    pub use_fixed_html_paragraph_spacing: Option<bool>,
1650    #[serde(skip_serializing_if = "Option::is_none")]
1651    pub do_not_break_wrapped_tables: Option<bool>,
1652    #[serde(skip_serializing_if = "Option::is_none")]
1653    pub do_not_break_constrained_forced_table: Option<bool>,
1654    #[serde(skip_serializing_if = "Option::is_none")]
1655    pub even_and_odd_headers: Option<bool>,
1656}
1657
1658/// Mirrors TypeScript `DocModel.metadata`.
1659#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1660#[serde(rename_all = "camelCase")]
1661pub struct DocModelMetadata {
1662    #[serde(default, skip_serializing_if = "Option::is_none")]
1663    pub document_id: Option<String>,
1664    #[serde(default, skip_serializing_if = "Option::is_none")]
1665    pub source_part_names: Option<std::collections::BTreeMap<String, String>>,
1666    pub source_parts: i64,
1667    pub warnings: Vec<String>,
1668    #[serde(skip_serializing_if = "Option::is_none")]
1669    pub document_page_count: Option<i64>,
1670    #[serde(skip_serializing_if = "Option::is_none")]
1671    pub document_open_tag: Option<String>,
1672    #[serde(skip_serializing_if = "Option::is_none")]
1673    pub document_background_color: Option<String>,
1674    #[serde(skip_serializing_if = "Option::is_none")]
1675    pub section_properties_xml: Option<String>,
1676    #[serde(skip_serializing_if = "Option::is_none")]
1677    pub sections: Option<Vec<DocumentSection>>,
1678    pub header_sections: Vec<HeaderSection>,
1679    pub footer_sections: Vec<FooterSection>,
1680    pub paragraph_styles: Vec<ParagraphStyleDefinition>,
1681    #[serde(skip_serializing_if = "Option::is_none")]
1682    pub default_paragraph_style_id: Option<String>,
1683    #[serde(skip_serializing_if = "Option::is_none")]
1684    pub default_tab_stop_twips: Option<i64>,
1685    #[serde(skip_serializing_if = "Option::is_none")]
1686    pub document_settings_imported: Option<bool>,
1687    #[serde(skip_serializing_if = "Option::is_none")]
1688    pub line_spacing_compatibility_imported: Option<bool>,
1689    #[serde(skip_serializing_if = "Option::is_none")]
1690    pub numbering_definitions: Option<NumberingDefinitionSet>,
1691    #[serde(skip_serializing_if = "Option::is_none")]
1692    pub compatibility: Option<DocumentCompatibilitySettings>,
1693    #[serde(skip_serializing_if = "Option::is_none")]
1694    pub footnotes: Option<Vec<DocumentNoteDefinition>>,
1695    #[serde(skip_serializing_if = "Option::is_none")]
1696    pub endnotes: Option<Vec<DocumentNoteDefinition>>,
1697    #[serde(skip_serializing_if = "Option::is_none")]
1698    pub comments: Option<Vec<DocumentCommentDefinition>>,
1699}
1700
1701/// Mirrors TypeScript `DocModel`.
1702#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1703#[serde(rename_all = "camelCase")]
1704pub struct DocModel {
1705    pub nodes: Vec<DocNode>,
1706    pub metadata: DocModelMetadata,
1707}
1708
1709#[cfg(test)]
1710mod deserialize_tests {
1711    use super::{DocModel, DocNode, HeadingLevel, ParagraphAlignment};
1712
1713    #[test]
1714    fn deserializes_minimal_js_doc_model() {
1715        let json = r#"{"nodes":[{"type":"paragraph","children":[{"type":"text","text":"Hi"}]}],"metadata":{"sourceParts":1,"warnings":[],"headerSections":[],"footerSections":[],"paragraphStyles":[]}}"#;
1716        let model: DocModel = serde_json::from_str(json).expect("should deserialize");
1717        assert_eq!(model.nodes.len(), 1);
1718    }
1719
1720    #[test]
1721    fn deserializes_heading_level_two() {
1722        let json = r#"{"type":"paragraph","style":{"headingLevel":2},"children":[{"type":"text","text":"Added"}]}"#;
1723        let node: DocNode = serde_json::from_str(json).expect("heading level 2");
1724        let DocNode::Paragraph(paragraph) = node else {
1725            panic!("expected paragraph");
1726        };
1727        assert_eq!(
1728            paragraph.style.and_then(|style| style.heading_level),
1729            Some(HeadingLevel::Two)
1730        );
1731    }
1732
1733    #[test]
1734    fn deserializes_align_center() {
1735        let json = r#"{"type":"paragraph","style":{"align":"center"},"children":[{"type":"text","text":"Added"}]}"#;
1736        let node: DocNode = serde_json::from_str(json).expect("align center");
1737        let DocNode::Paragraph(paragraph) = node else {
1738            panic!("expected paragraph");
1739        };
1740        assert_eq!(
1741            paragraph.style.and_then(|style| style.align),
1742            Some(ParagraphAlignment::Center)
1743        );
1744    }
1745}
1746
1747/// Fully resolved formatting shared until a caller edits its value.
1748#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1749#[serde(transparent)]
1750pub struct SharedParagraphStyle(std::sync::Arc<ParagraphStyle>);
1751
1752impl SharedParagraphStyle {
1753    pub fn into_owned(self) -> ParagraphStyle {
1754        std::sync::Arc::unwrap_or_clone(self.0)
1755    }
1756}
1757
1758impl From<ParagraphStyle> for SharedParagraphStyle {
1759    fn from(style: ParagraphStyle) -> Self {
1760        Self(std::sync::Arc::new(style))
1761    }
1762}
1763
1764impl std::ops::Deref for SharedParagraphStyle {
1765    type Target = ParagraphStyle;
1766
1767    fn deref(&self) -> &ParagraphStyle {
1768        &self.0
1769    }
1770}
1771
1772impl std::ops::DerefMut for SharedParagraphStyle {
1773    fn deref_mut(&mut self) -> &mut ParagraphStyle {
1774        std::sync::Arc::make_mut(&mut self.0)
1775    }
1776}