#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use super::deser_i32_or_u32;
pub use super::shapes::{
HxConnectLine, HxConnectPoint, HxControlPoint, HxControlPoints, HxCurve, HxCurveSegment,
HxDrawText, HxEllipse, HxFillBrush, HxLine, HxLineShape, HxPolygon, HxRect, HxShadow,
HxShapeComment,
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename = "sec")]
pub struct HxSection {
#[serde(
rename(serialize = "hp:p", deserialize = "p"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub paragraphs: Vec<HxParagraph>,
}
fn default_table_page_break() -> String {
"CELL".to_string()
}
fn default_table_repeat_header() -> u32 {
1
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxParagraph {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@paraPrIDRef", default)]
pub para_pr_id_ref: u32,
#[serde(rename = "@styleIDRef", default)]
pub style_id_ref: u32,
#[serde(rename = "@pageBreak", default)]
pub page_break: u32,
#[serde(rename = "@columnBreak", default)]
pub column_break: u32,
#[serde(rename = "@merged", default)]
pub merged: u32,
#[serde(
rename(serialize = "hp:run", deserialize = "run"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub runs: Vec<HxRun>,
#[serde(
rename(serialize = "hp:linesegarray", deserialize = "linesegarray"),
default,
skip_serializing_if = "Option::is_none"
)]
pub linesegarray: Option<HxLineSegArray>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxRun {
#[serde(rename = "@charPrIDRef", default)]
pub char_pr_id_ref: u32,
#[serde(
rename(serialize = "hp:secPr", deserialize = "secPr"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sec_pr: Option<HxSecPr>,
#[serde(
rename(serialize = "hp:t", deserialize = "t"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub texts: Vec<HxText>,
#[serde(
rename(serialize = "hp:tbl", deserialize = "tbl"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub tables: Vec<HxTable>,
#[serde(
rename(serialize = "hp:pic", deserialize = "pic"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub pictures: Vec<HxPic>,
#[serde(
rename(serialize = "hp:ctrl", deserialize = "ctrl"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub ctrls: Vec<HxCtrl>,
#[serde(
rename(serialize = "hp:rect", deserialize = "rect"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub rects: Vec<HxRect>,
#[serde(
rename(serialize = "hp:line", deserialize = "line"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub lines: Vec<HxLine>,
#[serde(
rename(serialize = "hp:ellipse", deserialize = "ellipse"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub ellipses: Vec<HxEllipse>,
#[serde(
rename(serialize = "hp:polygon", deserialize = "polygon"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub polygons: Vec<HxPolygon>,
#[serde(
rename(serialize = "hp:curve", deserialize = "curve"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub curves: Vec<HxCurve>,
#[serde(
rename(serialize = "hp:connectLine", deserialize = "connectLine"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub connect_lines: Vec<HxConnectLine>,
#[serde(
rename(serialize = "hp:equation", deserialize = "equation"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub equations: Vec<HxEquation>,
#[serde(
rename(serialize = "hp:switch", deserialize = "switch"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub switches: Vec<HxRunSwitch>,
#[serde(
rename(serialize = "hp:titleMark", deserialize = "titleMark"),
default,
skip_serializing_if = "Option::is_none"
)]
pub title_mark: Option<HxTitleMark>,
#[serde(
rename(serialize = "hp:dutmal", deserialize = "dutmal"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub dutmals: Vec<HxDutmal>,
#[serde(
rename(serialize = "hp:compose", deserialize = "compose"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub composes: Vec<HxCompose>,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub struct HxText {
#[serde(rename = "$value", default)]
pub parts: Vec<HxTextPart>,
}
impl Serialize for HxText {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[derive(Serialize)]
struct Helper {
#[serde(rename = "$text")]
text: String,
}
Helper { text: self.text() }.serialize(serializer)
}
}
impl HxText {
pub fn new(text: impl Into<String>) -> Self {
let s = text.into();
if s.is_empty() {
Self { parts: Vec::new() }
} else {
Self { parts: vec![HxTextPart::Text(s)] }
}
}
pub fn text(&self) -> String {
self.parts
.iter()
.map(|p| match p {
HxTextPart::Text(s) => s.as_str(),
HxTextPart::LineBreak {} => "\n",
HxTextPart::Tab {} => "\t",
HxTextPart::FwSpace {} => " ",
HxTextPart::NbSpace {} => "\u{00a0}",
HxTextPart::MarkpenBegin {} | HxTextPart::MarkpenEnd {} => "",
HxTextPart::Other => "",
})
.collect()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub enum HxTextPart {
#[serde(rename = "$text")]
Text(String),
#[serde(rename(serialize = "hp:lineBreak", deserialize = "lineBreak"))]
LineBreak {},
#[serde(rename(serialize = "hp:tab", deserialize = "tab"))]
Tab {},
#[serde(rename(serialize = "hp:fwSpace", deserialize = "fwSpace"))]
FwSpace {},
#[serde(rename(serialize = "hp:nbSpace", deserialize = "nbSpace"))]
NbSpace {},
#[serde(rename(serialize = "hp:markpenBegin", deserialize = "markpenBegin"))]
MarkpenBegin {},
#[serde(rename(serialize = "hp:markpenEnd", deserialize = "markpenEnd"))]
MarkpenEnd {},
#[serde(other)]
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTitleMark {
#[serde(rename = "@ignore")]
pub ignore: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxDutmal {
#[serde(rename = "@posType", default)]
pub pos_type: String,
#[serde(rename = "@szRatio", default)]
pub sz_ratio: u32,
#[serde(rename = "@option", default)]
pub option: u32,
#[serde(rename = "@styleIDRef", default)]
pub style_id_ref: u32,
#[serde(rename = "@align", default)]
pub align: String,
#[serde(rename(serialize = "hp:mainText", deserialize = "mainText"), default)]
pub main_text: String,
#[serde(rename(serialize = "hp:subText", deserialize = "subText"), default)]
pub sub_text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCompose {
#[serde(rename = "@circleType", default)]
pub circle_type: String,
#[serde(rename = "@charSz", default, deserialize_with = "deser_i32_or_u32")]
pub char_sz: i32,
#[serde(rename = "@composeType", default)]
pub compose_type: String,
#[serde(rename = "@charPrCnt", default)]
pub char_pr_cnt: u32,
#[serde(rename = "@composeText", default)]
pub compose_text: String,
#[serde(rename(serialize = "hp:charPr", deserialize = "charPr"), default)]
pub char_prs: Vec<HxComposeCharPr>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxComposeCharPr {
#[serde(rename = "@prIDRef")]
pub pr_id_ref: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxStringParam {
#[serde(rename = "@name", default)]
pub name: String,
#[serde(rename = "@xml:space", default, skip_serializing_if = "String::is_empty")]
pub xml_space: String,
#[serde(rename = "$text", default)]
pub value: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxIntegerParam {
#[serde(rename = "@name", default)]
pub name: String,
#[serde(rename = "$text", default)]
pub value: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxBooleanParam {
#[serde(rename = "@name", default)]
pub name: String,
#[serde(rename = "$text", default)]
pub value: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxFieldParameters {
#[serde(rename = "@cnt", default)]
pub cnt: u32,
#[serde(rename = "@name", default)]
pub name: String,
#[serde(
rename(serialize = "hp:stringParam", deserialize = "stringParam"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub string_params: Vec<HxStringParam>,
#[serde(
rename(serialize = "hp:integerParam", deserialize = "integerParam"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub integer_params: Vec<HxIntegerParam>,
#[serde(
rename(serialize = "hp:booleanParam", deserialize = "booleanParam"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub boolean_params: Vec<HxBooleanParam>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxFieldBegin {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@type", default)]
pub field_type: String,
#[serde(rename = "@name", default)]
pub name: String,
#[serde(rename = "@editable", default)]
pub editable: String,
#[serde(rename = "@dirty", default)]
pub dirty: String,
#[serde(rename = "@zorder", default)]
pub zorder: String,
#[serde(rename = "@fieldid", default)]
pub fieldid: String,
#[serde(rename = "@metaTag", default)]
pub meta_tag: String,
#[serde(
rename(serialize = "hp:parameters", deserialize = "parameters"),
default,
skip_serializing_if = "Option::is_none"
)]
pub parameters: Option<HxFieldParameters>,
#[serde(
rename(serialize = "hp:subList", deserialize = "subList"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sub_list: Option<HxSubList>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxFieldEnd {
#[serde(rename = "@beginIDRef", default)]
pub begin_id_ref: String,
#[serde(rename = "@fieldid", default)]
pub fieldid: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxAutoNumFormat {
#[serde(rename = "@type", default)]
pub format_type: String,
#[serde(rename = "@userChar", default)]
pub user_char: String,
#[serde(rename = "@prefixChar", default)]
pub prefix_char: String,
#[serde(rename = "@suffixChar", default)]
pub suffix_char: String,
#[serde(rename = "@supscript", default)]
pub supscript: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxAutoNum {
#[serde(rename = "@num", default)]
pub num: u32,
#[serde(rename = "@numType", default)]
pub num_type: String,
#[serde(
rename(serialize = "hp:autoNumFormat", deserialize = "autoNumFormat"),
default,
skip_serializing_if = "Option::is_none"
)]
pub auto_num_format: Option<HxAutoNumFormat>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCtrl {
#[serde(
rename(serialize = "hp:colPr", deserialize = "colPr"),
default,
skip_serializing_if = "Option::is_none"
)]
pub col_pr: Option<HxColPr>,
#[serde(
rename(serialize = "hp:header", deserialize = "header"),
default,
skip_serializing_if = "Option::is_none"
)]
pub header: Option<HxHeaderFooter>,
#[serde(
rename(serialize = "hp:footer", deserialize = "footer"),
default,
skip_serializing_if = "Option::is_none"
)]
pub footer: Option<HxHeaderFooter>,
#[serde(
rename(serialize = "hp:pageNum", deserialize = "pageNum"),
default,
skip_serializing_if = "Option::is_none"
)]
pub page_num: Option<HxPageNum>,
#[serde(
rename(serialize = "hp:footNote", deserialize = "footNote"),
default,
skip_serializing_if = "Option::is_none"
)]
pub foot_note: Option<HxFootNote>,
#[serde(
rename(serialize = "hp:endNote", deserialize = "endNote"),
default,
skip_serializing_if = "Option::is_none"
)]
pub end_note: Option<HxEndNote>,
#[serde(
rename(serialize = "hp:bookmark", deserialize = "bookmark"),
default,
skip_serializing_if = "Option::is_none"
)]
pub bookmark: Option<HxBookmark>,
#[serde(
rename(serialize = "hp:indexmark", deserialize = "indexmark"),
default,
skip_serializing_if = "Option::is_none"
)]
pub indexmark: Option<HxIndexMark>,
#[serde(
rename(serialize = "hp:fieldBegin", deserialize = "fieldBegin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub field_begin: Option<HxFieldBegin>,
#[serde(
rename(serialize = "hp:fieldEnd", deserialize = "fieldEnd"),
default,
skip_serializing_if = "Option::is_none"
)]
pub field_end: Option<HxFieldEnd>,
#[serde(
rename(serialize = "hp:autoNum", deserialize = "autoNum"),
default,
skip_serializing_if = "Option::is_none"
)]
pub auto_num: Option<HxAutoNum>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxBookmark {
#[serde(rename = "@name")]
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxIndexMark {
#[serde(rename(serialize = "hp:firstKey", deserialize = "firstKey"))]
pub first_key: String,
#[serde(
rename(serialize = "hp:secondKey", deserialize = "secondKey"),
default,
skip_serializing_if = "Option::is_none"
)]
pub second_key: Option<String>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxColPr {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@type", default)]
pub col_type: String,
#[serde(rename = "@layout", default)]
pub layout: String,
#[serde(rename = "@colCount", default)]
pub col_count: u32,
#[serde(rename = "@sameSz", default)]
pub same_sz: u32,
#[serde(rename = "@sameGap", default, deserialize_with = "deser_i32_or_u32")]
pub same_gap: i32,
#[serde(
rename(serialize = "hp:col", deserialize = "col"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub columns: Vec<HxCol>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCol {
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(rename = "@gap", default, deserialize_with = "deser_i32_or_u32")]
pub gap: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxHeaderFooter {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@applyPageType", default)]
pub apply_page_type: String,
#[serde(
rename(serialize = "hp:subList", deserialize = "subList"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sub_list: Option<HxSubList>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxPageNum {
#[serde(rename = "@pos", default)]
pub pos: String,
#[serde(rename = "@formatType", default)]
pub format_type: String,
#[serde(rename = "@sideChar", default)]
pub side_char: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxFootNote {
#[serde(rename = "@instId", default, skip_serializing_if = "Option::is_none")]
pub inst_id: Option<u32>,
#[serde(rename(serialize = "hp:subList", deserialize = "subList"))]
pub sub_list: HxSubList,
}
pub type HxEndNote = HxFootNote;
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxFootNotePr {
#[serde(rename = "$value", default)]
pub raw_xml: String,
}
pub type HxEndNotePr = HxFootNotePr;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCaption {
#[serde(rename = "@side", default = "default_caption_side")]
pub side: String,
#[serde(rename = "@fullSz", default)]
pub full_sz: u32,
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(
rename = "@gap",
default = "default_caption_gap",
deserialize_with = "deser_i32_or_u32"
)]
pub gap: i32,
#[serde(rename = "@lastWidth", default)]
pub last_width: u32,
#[serde(rename(serialize = "hp:subList", deserialize = "subList"))]
pub sub_list: HxSubList,
}
fn default_caption_side() -> String {
"LEFT".to_string()
}
fn default_caption_gap() -> i32 {
850
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxSecPr {
#[serde(rename = "@textDirection", default)]
pub text_direction: String,
#[serde(rename = "@masterPageCnt", default)]
pub master_page_cnt: u32,
#[serde(
rename(serialize = "hp:visibility", deserialize = "visibility"),
default,
skip_serializing_if = "Option::is_none"
)]
pub visibility: Option<HxVisibility>,
#[serde(
rename(serialize = "hp:lineNumberShape", deserialize = "lineNumberShape"),
default,
skip_serializing_if = "Option::is_none"
)]
pub line_number_shape: Option<HxLineNumberShape>,
#[serde(
rename(serialize = "hp:pagePr", deserialize = "pagePr"),
default,
skip_serializing_if = "Option::is_none"
)]
pub page_pr: Option<HxPagePr>,
#[serde(
rename(serialize = "hp:pageBorderFill", deserialize = "pageBorderFill"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub page_border_fills: Vec<HxPageBorderFill>,
#[serde(rename = "startNum", default, skip_serializing)]
pub start_num: Option<HxStartNum>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct HxVisibility {
#[serde(rename = "@hideFirstHeader", default)]
pub hide_first_header: u8,
#[serde(rename = "@hideFirstFooter", default)]
pub hide_first_footer: u8,
#[serde(rename = "@hideFirstMasterPage", default)]
pub hide_first_master_page: u8,
#[serde(rename = "@border", default)]
pub border: String,
#[serde(rename = "@fill", default)]
pub fill: String,
#[serde(rename = "@hideFirstPageNum", default)]
pub hide_first_page_num: u8,
#[serde(rename = "@hideFirstEmptyLine", default)]
pub hide_first_empty_line: u8,
#[serde(rename = "@showLineNumber", default)]
pub show_line_number: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct HxLineNumberShape {
#[serde(rename = "@restartType", default)]
pub restart_type: String,
#[serde(rename = "@countBy", default)]
pub count_by: u16,
#[serde(rename = "@distance", default, deserialize_with = "deser_i32_or_u32")]
pub distance: i32,
#[serde(rename = "@startNumber", default)]
pub start_number: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct HxStartNum {
#[serde(rename = "@pageStartsOn", default)]
pub page_starts_on: String,
#[serde(rename = "@page", default)]
pub page: u32,
#[serde(rename = "@pic", default)]
pub pic: u32,
#[serde(rename = "@tbl", default)]
pub tbl: u32,
#[serde(rename = "@equation", default)]
pub equation: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxPageBorderFill {
#[serde(rename = "@type", default)]
pub apply_type: String,
#[serde(rename = "@borderFillIDRef", default)]
pub border_fill_id: u32,
#[serde(rename = "@textBorder", default)]
pub text_border: String,
#[serde(rename = "@headerInside", default)]
pub header_inside: u8,
#[serde(rename = "@footerInside", default)]
pub footer_inside: u8,
#[serde(rename = "@fillArea", default)]
pub fill_area: String,
#[serde(
rename(serialize = "hp:offset", deserialize = "offset"),
default,
skip_serializing_if = "Option::is_none"
)]
pub offset: Option<HxPageBorderFillOffset>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct HxPageBorderFillOffset {
#[serde(rename = "@left", default, deserialize_with = "deser_i32_or_u32")]
pub left: i32,
#[serde(rename = "@right", default, deserialize_with = "deser_i32_or_u32")]
pub right: i32,
#[serde(rename = "@top", default, deserialize_with = "deser_i32_or_u32")]
pub top: i32,
#[serde(rename = "@bottom", default, deserialize_with = "deser_i32_or_u32")]
pub bottom: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxPagePr {
#[serde(rename = "@landscape", default)]
pub landscape: String,
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(rename = "@height", default, deserialize_with = "deser_i32_or_u32")]
pub height: i32,
#[serde(rename = "@gutterType", default)]
pub gutter_type: String,
#[serde(
rename(serialize = "hp:margin", deserialize = "margin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub margin: Option<HxPageMargin>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct HxPageMargin {
#[serde(rename = "@header", default, deserialize_with = "deser_i32_or_u32")]
pub header: i32,
#[serde(rename = "@footer", default, deserialize_with = "deser_i32_or_u32")]
pub footer: i32,
#[serde(rename = "@gutter", default, deserialize_with = "deser_i32_or_u32")]
pub gutter: i32,
#[serde(rename = "@left", default, deserialize_with = "deser_i32_or_u32")]
pub left: i32,
#[serde(rename = "@right", default, deserialize_with = "deser_i32_or_u32")]
pub right: i32,
#[serde(rename = "@top", default, deserialize_with = "deser_i32_or_u32")]
pub top: i32,
#[serde(rename = "@bottom", default, deserialize_with = "deser_i32_or_u32")]
pub bottom: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct HxLineSegArray {
#[serde(
rename(serialize = "hp:lineseg", deserialize = "lineseg"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub items: Vec<HxLineSeg>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxLineSeg {
#[serde(rename = "@textpos", default)]
pub textpos: u32,
#[serde(rename = "@vertpos", default, deserialize_with = "deser_i32_or_u32")]
pub vertpos: i32,
#[serde(rename = "@vertsize", default, deserialize_with = "deser_i32_or_u32")]
pub vertsize: i32,
#[serde(rename = "@textheight", default, deserialize_with = "deser_i32_or_u32")]
pub textheight: i32,
#[serde(rename = "@baseline", default, deserialize_with = "deser_i32_or_u32")]
pub baseline: i32,
#[serde(rename = "@spacing", default, deserialize_with = "deser_i32_or_u32")]
pub spacing: i32,
#[serde(rename = "@horzpos", default, deserialize_with = "deser_i32_or_u32")]
pub horzpos: i32,
#[serde(rename = "@horzsize", default, deserialize_with = "deser_i32_or_u32")]
pub horzsize: i32,
#[serde(rename = "@flags", default)]
pub flags: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTable {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@zOrder", default)]
pub z_order: u32,
#[serde(rename = "@numberingType", default)]
pub numbering_type: String,
#[serde(rename = "@textWrap", default)]
pub text_wrap: String,
#[serde(rename = "@textFlow", default)]
pub text_flow: String,
#[serde(rename = "@lock", default)]
pub lock: u32,
#[serde(rename = "@dropcapstyle", default)]
pub dropcap_style: String,
#[serde(rename = "@pageBreak", default = "default_table_page_break")]
pub page_break: String,
#[serde(rename = "@repeatHeader", default = "default_table_repeat_header")]
pub repeat_header: u32,
#[serde(rename = "@rowCnt", default)]
pub row_cnt: u32,
#[serde(rename = "@colCnt", default)]
pub col_cnt: u32,
#[serde(rename = "@cellSpacing", default)]
pub cell_spacing: u32,
#[serde(rename = "@borderFillIDRef", default)]
pub border_fill_id_ref: u32,
#[serde(rename = "@noAdjust", default)]
pub no_adjust: u32,
#[serde(
rename(serialize = "hp:sz", deserialize = "sz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sz: Option<HxTableSz>,
#[serde(
rename(serialize = "hp:pos", deserialize = "pos"),
default,
skip_serializing_if = "Option::is_none"
)]
pub pos: Option<HxTablePos>,
#[serde(
rename(serialize = "hp:outMargin", deserialize = "outMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub out_margin: Option<HxTableMargin>,
#[serde(
rename(serialize = "hp:caption", deserialize = "caption"),
default,
skip_serializing_if = "Option::is_none"
)]
pub caption: Option<HxCaption>,
#[serde(
rename(serialize = "hp:inMargin", deserialize = "inMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub in_margin: Option<HxTableMargin>,
#[serde(
rename(serialize = "hp:tr", deserialize = "tr"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub rows: Vec<HxTableRow>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTableSz {
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(rename = "@widthRelTo", default)]
pub width_rel_to: String,
#[serde(rename = "@height", default, deserialize_with = "deser_i32_or_u32")]
pub height: i32,
#[serde(rename = "@heightRelTo", default)]
pub height_rel_to: String,
#[serde(rename = "@protect", default)]
pub protect: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTablePos {
#[serde(rename = "@treatAsChar", default)]
pub treat_as_char: u32,
#[serde(rename = "@affectLSpacing", default)]
pub affect_l_spacing: u32,
#[serde(rename = "@flowWithText", default)]
pub flow_with_text: u32,
#[serde(rename = "@allowOverlap", default)]
pub allow_overlap: u32,
#[serde(rename = "@holdAnchorAndSO", default)]
pub hold_anchor_and_so: u32,
#[serde(rename = "@vertRelTo", default)]
pub vert_rel_to: String,
#[serde(rename = "@horzRelTo", default)]
pub horz_rel_to: String,
#[serde(rename = "@vertAlign", default)]
pub vert_align: String,
#[serde(rename = "@horzAlign", default)]
pub horz_align: String,
#[serde(rename = "@vertOffset", default, deserialize_with = "deser_i32_or_u32")]
pub vert_offset: i32,
#[serde(rename = "@horzOffset", default, deserialize_with = "deser_i32_or_u32")]
pub horz_offset: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTableMargin {
#[serde(rename = "@left", default, deserialize_with = "deser_i32_or_u32")]
pub left: i32,
#[serde(rename = "@right", default, deserialize_with = "deser_i32_or_u32")]
pub right: i32,
#[serde(rename = "@top", default, deserialize_with = "deser_i32_or_u32")]
pub top: i32,
#[serde(rename = "@bottom", default, deserialize_with = "deser_i32_or_u32")]
pub bottom: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTableRow {
#[serde(
rename(serialize = "hp:tc", deserialize = "tc"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub cells: Vec<HxTableCell>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxTableCell {
#[serde(rename = "@name", default)]
pub name: String,
#[serde(rename = "@header", default)]
pub header: u32,
#[serde(rename = "@hasMargin", default)]
pub has_margin: u32,
#[serde(rename = "@protect", default)]
pub protect: u32,
#[serde(rename = "@editable", default)]
pub editable: u32,
#[serde(rename = "@dirty", default)]
pub dirty: u32,
#[serde(rename = "@borderFillIDRef", default)]
pub border_fill_id_ref: u32,
#[serde(
rename(serialize = "hp:subList", deserialize = "subList"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sub_list: Option<HxSubList>,
#[serde(
rename(serialize = "hp:cellAddr", deserialize = "cellAddr"),
default,
skip_serializing_if = "Option::is_none"
)]
pub cell_addr: Option<HxCellAddr>,
#[serde(
rename(serialize = "hp:cellSpan", deserialize = "cellSpan"),
default,
skip_serializing_if = "Option::is_none"
)]
pub cell_span: Option<HxCellSpan>,
#[serde(
rename(serialize = "hp:cellSz", deserialize = "cellSz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub cell_sz: Option<HxCellSz>,
#[serde(
rename(serialize = "hp:cellMargin", deserialize = "cellMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub cell_margin: Option<HxTableMargin>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCellAddr {
#[serde(rename = "@colAddr", default)]
pub col_addr: u32,
#[serde(rename = "@rowAddr", default)]
pub row_addr: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCellSpan {
#[serde(rename = "@rowSpan", default = "default_one")]
pub row_span: u32,
#[serde(rename = "@colSpan", default = "default_one")]
pub col_span: u32,
}
fn default_one() -> u32 {
1
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxCellSz {
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(rename = "@height", default, deserialize_with = "deser_i32_or_u32")]
pub height: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxSubList {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@textDirection", default)]
pub text_direction: String,
#[serde(rename = "@lineWrap", default)]
pub line_wrap: String,
#[serde(rename = "@vertAlign", default)]
pub vert_align: String,
#[serde(rename = "@linkListIDRef", default)]
pub link_list_id_ref: u32,
#[serde(rename = "@linkListNextIDRef", default)]
pub link_list_next_id_ref: u32,
#[serde(rename = "@textWidth", default)]
pub text_width: u32,
#[serde(rename = "@textHeight", default)]
pub text_height: u32,
#[serde(rename = "@hasTextRef", default)]
pub has_text_ref: u32,
#[serde(rename = "@hasNumRef", default)]
pub has_num_ref: u32,
#[serde(
rename(serialize = "hp:p", deserialize = "p"),
default,
skip_serializing_if = "Vec::is_empty"
)]
pub paragraphs: Vec<HxParagraph>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxPic {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@zOrder", default)]
pub z_order: u32,
#[serde(rename = "@numberingType", default)]
pub numbering_type: String,
#[serde(rename = "@textWrap", default)]
pub text_wrap: String,
#[serde(rename = "@textFlow", default)]
pub text_flow: String,
#[serde(rename = "@lock", default)]
pub lock: u32,
#[serde(rename = "@dropcapstyle", default)]
pub dropcap_style: String,
#[serde(rename = "@href", default)]
pub href: String,
#[serde(rename = "@groupLevel", default)]
pub group_level: u32,
#[serde(rename = "@instid", default)]
pub instid: String,
#[serde(rename = "@reverse", default)]
pub reverse: u32,
#[serde(
rename(serialize = "hp:offset", deserialize = "offset"),
default,
skip_serializing_if = "Option::is_none"
)]
pub offset: Option<HxOffset>,
#[serde(
rename(serialize = "hp:orgSz", deserialize = "orgSz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub org_sz: Option<HxSizeAttr>,
#[serde(
rename(serialize = "hp:curSz", deserialize = "curSz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub cur_sz: Option<HxSizeAttr>,
#[serde(
rename(serialize = "hp:flip", deserialize = "flip"),
default,
skip_serializing_if = "Option::is_none"
)]
pub flip: Option<HxFlip>,
#[serde(
rename(serialize = "hp:rotationInfo", deserialize = "rotationInfo"),
default,
skip_serializing_if = "Option::is_none"
)]
pub rotation_info: Option<HxRotationInfo>,
#[serde(
rename(serialize = "hp:renderingInfo", deserialize = "renderingInfo"),
default,
skip_serializing_if = "Option::is_none"
)]
pub rendering_info: Option<HxRenderingInfo>,
#[serde(
rename(serialize = "hp:imgRect", deserialize = "imgRect"),
default,
skip_serializing_if = "Option::is_none"
)]
pub img_rect: Option<HxImgRect>,
#[serde(
rename(serialize = "hp:imgClip", deserialize = "imgClip"),
default,
skip_serializing_if = "Option::is_none"
)]
pub img_clip: Option<HxImgClip>,
#[serde(
rename(serialize = "hp:inMargin", deserialize = "inMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub in_margin: Option<HxTableMargin>,
#[serde(
rename(serialize = "hp:imgDim", deserialize = "imgDim"),
default,
skip_serializing_if = "Option::is_none"
)]
pub img_dim: Option<HxImgDim>,
#[serde(
rename(serialize = "hc:img", deserialize = "img"),
default,
skip_serializing_if = "Option::is_none"
)]
pub img: Option<HxImg>,
#[serde(
rename(serialize = "hp:sz", deserialize = "sz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sz: Option<HxTableSz>,
#[serde(
rename(serialize = "hp:pos", deserialize = "pos"),
default,
skip_serializing_if = "Option::is_none"
)]
pub pos: Option<HxTablePos>,
#[serde(
rename(serialize = "hp:outMargin", deserialize = "outMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub out_margin: Option<HxTableMargin>,
#[serde(
rename(serialize = "hp:caption", deserialize = "caption"),
default,
skip_serializing_if = "Option::is_none"
)]
pub caption: Option<HxCaption>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxImg {
#[serde(rename = "@binaryItemIDRef", default)]
pub binary_item_id_ref: String,
#[serde(rename = "@bright", default, deserialize_with = "deser_i32_or_u32")]
pub bright: i32,
#[serde(rename = "@contrast", default, deserialize_with = "deser_i32_or_u32")]
pub contrast: i32,
#[serde(rename = "@effect", default, skip_serializing_if = "String::is_empty")]
pub effect: String,
#[serde(rename = "@alpha", default, skip_serializing_if = "String::is_empty")]
pub alpha: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxSizeAttr {
#[serde(rename = "@width", default, deserialize_with = "deser_i32_or_u32")]
pub width: i32,
#[serde(rename = "@height", default, deserialize_with = "deser_i32_or_u32")]
pub height: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxOffset {
#[serde(rename = "@x", default, deserialize_with = "deser_i32_or_u32")]
pub x: i32,
#[serde(rename = "@y", default, deserialize_with = "deser_i32_or_u32")]
pub y: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxFlip {
#[serde(rename = "@horizontal", default)]
pub horizontal: u32,
#[serde(rename = "@vertical", default)]
pub vertical: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxRotationInfo {
#[serde(rename = "@angle", default, deserialize_with = "deser_i32_or_u32")]
pub angle: i32,
#[serde(rename = "@centerX", default, deserialize_with = "deser_i32_or_u32")]
pub center_x: i32,
#[serde(rename = "@centerY", default, deserialize_with = "deser_i32_or_u32")]
pub center_y: i32,
#[serde(rename = "@rotateimage", default)]
pub rotate_image: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxMatrix {
#[serde(rename = "@e1", default)]
pub e1: String,
#[serde(rename = "@e2", default)]
pub e2: String,
#[serde(rename = "@e3", default)]
pub e3: String,
#[serde(rename = "@e4", default)]
pub e4: String,
#[serde(rename = "@e5", default)]
pub e5: String,
#[serde(rename = "@e6", default)]
pub e6: String,
}
impl HxMatrix {
pub fn identity() -> Self {
Self {
e1: "1".to_string(),
e2: "0".to_string(),
e3: "0".to_string(),
e4: "0".to_string(),
e5: "1".to_string(),
e6: "0".to_string(),
}
}
}
impl Default for HxMatrix {
fn default() -> Self {
Self::identity()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxRenderingInfo {
#[serde(rename(serialize = "hc:transMatrix", deserialize = "transMatrix"))]
pub trans_matrix: HxMatrix,
#[serde(rename(serialize = "hc:scaMatrix", deserialize = "scaMatrix"))]
pub sca_matrix: HxMatrix,
#[serde(rename(serialize = "hc:rotMatrix", deserialize = "rotMatrix"))]
pub rot_matrix: HxMatrix,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxEquation {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@zOrder", default)]
pub z_order: u32,
#[serde(rename = "@numberingType", default)]
pub numbering_type: String,
#[serde(rename = "@textWrap", default)]
pub text_wrap: String,
#[serde(rename = "@textFlow", default)]
pub text_flow: String,
#[serde(rename = "@lock", default)]
pub lock: u32,
#[serde(rename = "@dropcapstyle", default)]
pub dropcap_style: String,
#[serde(rename = "@version", default)]
pub version: String,
#[serde(rename = "@baseLine", default)]
pub base_line: u32,
#[serde(rename = "@textColor", default)]
pub text_color: String,
#[serde(rename = "@baseUnit", default)]
pub base_unit: u32,
#[serde(rename = "@lineMode", default)]
pub line_mode: String,
#[serde(rename = "@font", default)]
pub font: String,
#[serde(
rename(serialize = "hp:sz", deserialize = "sz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sz: Option<HxTableSz>,
#[serde(
rename(serialize = "hp:pos", deserialize = "pos"),
default,
skip_serializing_if = "Option::is_none"
)]
pub pos: Option<HxTablePos>,
#[serde(
rename(serialize = "hp:outMargin", deserialize = "outMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub out_margin: Option<HxTableMargin>,
#[serde(
rename(serialize = "hp:shapeComment", deserialize = "shapeComment"),
default,
skip_serializing_if = "Option::is_none"
)]
pub shape_comment: Option<HxShapeComment>,
#[serde(
rename(serialize = "hp:script", deserialize = "script"),
default,
skip_serializing_if = "Option::is_none"
)]
pub script: Option<HxScript>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxScript {
#[serde(rename = "$text", default)]
pub text: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxRunSwitch {
#[serde(
rename(serialize = "hp:case", deserialize = "case"),
default,
skip_serializing_if = "Option::is_none"
)]
pub case: Option<HxRunCase>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxRunCase {
#[serde(rename = "@hp:required-namespace", default)]
pub required_namespace: String,
#[serde(
rename(serialize = "hp:chart", deserialize = "chart"),
default,
skip_serializing_if = "Option::is_none"
)]
pub chart: Option<HxChart>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxChart {
#[serde(rename = "@id", default)]
pub id: String,
#[serde(rename = "@zOrder", default)]
pub z_order: u32,
#[serde(rename = "@numberingType", default)]
pub numbering_type: String,
#[serde(rename = "@textWrap", default)]
pub text_wrap: String,
#[serde(rename = "@textFlow", default)]
pub text_flow: String,
#[serde(rename = "@lock", default)]
pub lock: u32,
#[serde(rename = "@dropcapstyle", default)]
pub dropcap_style: String,
#[serde(rename = "@chartIDRef", default)]
pub chart_id_ref: String,
#[serde(
rename(serialize = "hp:sz", deserialize = "sz"),
default,
skip_serializing_if = "Option::is_none"
)]
pub sz: Option<HxTableSz>,
#[serde(
rename(serialize = "hp:pos", deserialize = "pos"),
default,
skip_serializing_if = "Option::is_none"
)]
pub pos: Option<HxTablePos>,
#[serde(
rename(serialize = "hp:outMargin", deserialize = "outMargin"),
default,
skip_serializing_if = "Option::is_none"
)]
pub out_margin: Option<HxTableMargin>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HxImgRect {
#[serde(rename(serialize = "hc:pt0", deserialize = "pt0"))]
pub pt0: HxPoint,
#[serde(rename(serialize = "hc:pt1", deserialize = "pt1"))]
pub pt1: HxPoint,
#[serde(rename(serialize = "hc:pt2", deserialize = "pt2"))]
pub pt2: HxPoint,
#[serde(rename(serialize = "hc:pt3", deserialize = "pt3"))]
pub pt3: HxPoint,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxImgClip {
#[serde(rename = "@left", default, deserialize_with = "deser_i32_or_u32")]
pub left: i32,
#[serde(rename = "@right", default, deserialize_with = "deser_i32_or_u32")]
pub right: i32,
#[serde(rename = "@top", default, deserialize_with = "deser_i32_or_u32")]
pub top: i32,
#[serde(rename = "@bottom", default, deserialize_with = "deser_i32_or_u32")]
pub bottom: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxImgDim {
#[serde(rename = "@dimwidth", default, deserialize_with = "deser_i32_or_u32")]
pub dim_width: i32,
#[serde(rename = "@dimheight", default, deserialize_with = "deser_i32_or_u32")]
pub dim_height: i32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct HxPoint {
#[serde(rename = "@x", default, deserialize_with = "deser_i32_or_u32")]
pub x: i32,
#[serde(rename = "@y", default, deserialize_with = "deser_i32_or_u32")]
pub y: i32,
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_section(xml: &str) -> HxSection {
quick_xml::de::from_str(xml).expect("failed to parse HxSection")
}
#[test]
fn parse_minimal_section() {
let xml = r#"<hs:sec></hs:sec>"#;
let sec = parse_section(xml);
assert!(sec.paragraphs.is_empty());
}
#[test]
fn parse_single_text_paragraph() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="3" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
<hp:run charPrIDRef="0">
<hp:t>안녕하세요</hp:t>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
assert_eq!(sec.paragraphs.len(), 1);
let p = &sec.paragraphs[0];
assert_eq!(p.para_pr_id_ref, 3);
assert_eq!(p.style_id_ref, 0);
assert_eq!(p.runs.len(), 1);
assert_eq!(p.runs[0].char_pr_id_ref, 0);
assert_eq!(p.runs[0].texts.len(), 1);
assert_eq!(p.runs[0].texts[0].text(), "안녕하세요");
}
#[test]
fn parse_multiple_text_runs() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="3" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>Hello</hp:t>
</hp:run>
<hp:run charPrIDRef="7">
<hp:t>World</hp:t>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let p = &sec.paragraphs[0];
assert_eq!(p.runs.len(), 2);
assert_eq!(p.runs[0].texts[0].text(), "Hello");
assert_eq!(p.runs[1].char_pr_id_ref, 7);
assert_eq!(p.runs[1].texts[0].text(), "World");
}
#[test]
fn parse_empty_text_element() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t/>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
assert_eq!(sec.paragraphs[0].runs[0].texts[0].text(), "");
}
#[test]
fn parse_sec_pr_with_page_settings() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="3" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:secPr textDirection="HORIZONTAL">
<hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY">
<hp:margin header="4252" footer="4252" gutter="0" left="8504" right="8504" top="5668" bottom="4252"/>
</hp:pagePr>
</hp:secPr>
<hp:t>text</hp:t>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let run = &sec.paragraphs[0].runs[0];
let sec_pr = run.sec_pr.as_ref().unwrap();
let page_pr = sec_pr.page_pr.as_ref().unwrap();
assert_eq!(page_pr.width, 59528);
assert_eq!(page_pr.height, 84188);
assert_eq!(page_pr.landscape, "WIDELY");
let margin = page_pr.margin.as_ref().unwrap();
assert_eq!(margin.left, 8504);
assert_eq!(margin.right, 8504);
assert_eq!(margin.top, 5668);
assert_eq!(margin.bottom, 4252);
assert_eq!(margin.header, 4252);
assert_eq!(margin.footer, 4252);
}
#[test]
fn parse_table_basic() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:tbl rowCnt="2" colCnt="2">
<hp:tr>
<hp:tc name="A1">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:cellSz width="1000" height="500"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>Cell 1</hp:t>
</hp:run>
</hp:p>
</hp:subList>
</hp:tc>
<hp:tc name="B1">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:cellSz width="1000" height="500"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>Cell 2</hp:t>
</hp:run>
</hp:p>
</hp:subList>
</hp:tc>
</hp:tr>
<hp:tr>
<hp:tc name="A2">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>Cell 3</hp:t>
</hp:run>
</hp:p>
</hp:subList>
</hp:tc>
<hp:tc name="B2">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>Cell 4</hp:t>
</hp:run>
</hp:p>
</hp:subList>
</hp:tc>
</hp:tr>
</hp:tbl>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let tbl = &sec.paragraphs[0].runs[0].tables[0];
assert_eq!(tbl.row_cnt, 2);
assert_eq!(tbl.col_cnt, 2);
assert_eq!(tbl.rows.len(), 2);
assert_eq!(tbl.rows[0].cells.len(), 2);
let cell0 = &tbl.rows[0].cells[0];
assert_eq!(cell0.name, "A1");
let text = cell0.sub_list.as_ref().unwrap().paragraphs[0].runs[0].texts[0].text();
assert_eq!(text, "Cell 1");
}
#[test]
fn parse_picture() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:pic id="123">
<hp:img binaryItemIDRef="image1.jpg"/>
<hp:orgSz width="5000" height="3000"/>
</hp:pic>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let pic = &sec.paragraphs[0].runs[0].pictures[0];
let img = pic.img.as_ref().unwrap();
assert_eq!(img.binary_item_id_ref, "image1.jpg");
let org = pic.org_sz.as_ref().unwrap();
assert_eq!(org.width, 5000);
assert_eq!(org.height, 3000);
}
#[test]
fn unknown_elements_in_run_are_skipped() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:ctrl>
<hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1"/>
</hp:ctrl>
<hp:t>text after ctrl</hp:t>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let run = &sec.paragraphs[0].runs[0];
assert_eq!(run.texts[0].text(), "text after ctrl");
}
#[test]
fn linesegarray_is_ignored() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:t>text</hp:t>
</hp:run>
<hp:linesegarray>
<hp:lineseg textpos="0" vertpos="0" vertsize="1000"/>
</hp:linesegarray>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
assert_eq!(sec.paragraphs[0].runs[0].texts[0].text(), "text");
}
#[test]
fn multiple_paragraphs() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>First</hp:t></hp:run>
</hp:p>
<hp:p id="1" paraPrIDRef="1" styleIDRef="0">
<hp:run charPrIDRef="1"><hp:t>Second</hp:t></hp:run>
</hp:p>
<hp:p id="2" paraPrIDRef="2" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>Third</hp:t></hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
assert_eq!(sec.paragraphs.len(), 3);
assert_eq!(sec.paragraphs[0].runs[0].texts[0].text(), "First");
assert_eq!(sec.paragraphs[1].runs[0].texts[0].text(), "Second");
assert_eq!(sec.paragraphs[2].runs[0].texts[0].text(), "Third");
}
#[test]
fn parse_caption_standalone_roundtrip() {
let xml = r#"<caption side="BOTTOM" fullSz="0" width="42520" gap="850" lastWidth="42520"><subList id="" textDirection="" lineWrap="" vertAlign="" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0"><p id="0" paraPrIDRef="0" styleIDRef="0"><run charPrIDRef="0"><t>Figure 1. Sample</t></run></p></subList></caption>"#;
let cap: HxCaption = quick_xml::de::from_str(xml).expect("parse HxCaption");
assert_eq!(cap.side, "BOTTOM");
assert_eq!(cap.full_sz, 0);
assert_eq!(cap.width, 42520);
assert_eq!(cap.gap, 850);
assert_eq!(cap.last_width, 42520);
assert_eq!(cap.sub_list.paragraphs.len(), 1);
assert_eq!(cap.sub_list.paragraphs[0].runs[0].texts[0].text(), "Figure 1. Sample");
let serialized = quick_xml::se::to_string(&cap).expect("serialize HxCaption");
let cap2: HxCaption = quick_xml::de::from_str(&serialized).expect("re-parse HxCaption");
assert_eq!(cap.side, cap2.side);
assert_eq!(cap.width, cap2.width);
assert_eq!(cap.gap, cap2.gap);
}
#[test]
fn caption_defaults() {
let xml = r#"<caption><subList><p id="0" paraPrIDRef="0" styleIDRef="0"><run charPrIDRef="0"><t>cap</t></run></p></subList></caption>"#;
let cap: HxCaption = quick_xml::de::from_str(xml).expect("parse");
assert_eq!(cap.side, "LEFT");
assert_eq!(cap.gap, 850);
assert_eq!(cap.full_sz, 0);
assert_eq!(cap.width, 0);
assert_eq!(cap.last_width, 0);
}
#[test]
fn parse_table_with_caption() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:tbl rowCnt="1" colCnt="1">
<hp:sz width="42520" height="5000"/>
<hp:outMargin left="0" right="0" top="0" bottom="0"/>
<hp:caption side="BOTTOM" fullSz="0" width="42520" gap="850" lastWidth="42520">
<hp:subList id="" textDirection="" lineWrap="" vertAlign="">
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>Table 1. Data</hp:t></hp:run>
</hp:p>
</hp:subList>
</hp:caption>
<hp:inMargin left="0" right="0" top="0" bottom="0"/>
<hp:tr>
<hp:tc name="A1">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:cellSz width="42520" height="5000"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>cell</hp:t></hp:run>
</hp:p>
</hp:subList>
</hp:tc>
</hp:tr>
</hp:tbl>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let tbl = &sec.paragraphs[0].runs[0].tables[0];
let cap = tbl.caption.as_ref().expect("table should have caption");
assert_eq!(cap.side, "BOTTOM");
assert_eq!(cap.width, 42520);
assert_eq!(cap.sub_list.paragraphs[0].runs[0].texts[0].text(), "Table 1. Data");
assert_eq!(tbl.rows.len(), 1);
}
#[test]
fn table_without_caption_roundtrip() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:tbl rowCnt="1" colCnt="1">
<hp:tr>
<hp:tc name="A1">
<hp:cellSpan rowSpan="1" colSpan="1"/>
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>ok</hp:t></hp:run>
</hp:p>
</hp:subList>
</hp:tc>
</hp:tr>
</hp:tbl>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let tbl = &sec.paragraphs[0].runs[0].tables[0];
assert!(tbl.caption.is_none());
}
#[test]
fn parse_rect_with_caption() {
let xml = r#"
<hs:sec>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0">
<hp:rect id="1" zOrder="0" numberingType="FIGURE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0">
<hp:sz width="20000" height="10000"/>
<hp:outMargin left="0" right="0" top="0" bottom="0"/>
<hp:caption side="TOP" width="20000" gap="500" lastWidth="20000">
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>Fig caption</hp:t></hp:run>
</hp:p>
</hp:subList>
</hp:caption>
<hp:drawText lastWidth="18000">
<hp:subList>
<hp:p id="0" paraPrIDRef="0" styleIDRef="0">
<hp:run charPrIDRef="0"><hp:t>box text</hp:t></hp:run>
</hp:p>
</hp:subList>
</hp:drawText>
</hp:rect>
</hp:run>
</hp:p>
</hs:sec>"#;
let sec = parse_section(xml);
let rect = &sec.paragraphs[0].runs[0].rects[0];
let cap = rect.caption.as_ref().expect("rect should have caption");
assert_eq!(cap.side, "TOP");
assert_eq!(cap.gap, 500);
assert!(rect.draw_text.is_some());
}
}