diff --git a/docx-core/src/documents/elements/instr_pageref.rs b/docx-core/src/documents/elements/instr_pageref.rs new file mode 100644 index 0000000..e846a02 --- /dev/null +++ b/docx-core/src/documents/elements/instr_pageref.rs @@ -0,0 +1,67 @@ +use serde::Serialize; + +use crate::documents::*; + +// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_PAGEREFPAGEREF_topic_ID0EHXK1.html +#[derive(Serialize, Debug, Clone, PartialEq, Default)] +pub struct InstrPAGEREF { + pub page_ref: String, + pub hyperlink: bool, + pub relative_position: bool, +} + +impl InstrPAGEREF { + pub fn new(r: impl Into) -> Self { + Self { + page_ref: r.into(), + ..Default::default() + } + } + + pub fn hyperlink(mut self) -> Self { + self.hyperlink = true; + self + } + + pub fn relative_position(mut self) -> Self { + self.relative_position = true; + self + } +} + +impl BuildXML for InstrPAGEREF { + fn build(&self) -> Vec { + let mut instr = format!("PAGEREF {}", self.page_ref); + + if self.relative_position { + instr = format!("{} \\p", instr); + } + + if self.hyperlink { + instr = format!("{} \\h", instr); + } + + instr.into() + } +} + +impl std::str::FromStr for InstrPAGEREF { + type Err = (); + + fn from_str(instr: &str) -> Result { + let mut s = instr.split(' '); + let text = s.next(); + let mut page_ref = InstrPAGEREF::new(text.unwrap_or_default()); + loop { + if let Some(i) = s.next() { + match i { + "\\h" => page_ref = page_ref.hyperlink(), + "\\p" => page_ref = page_ref.relative_position(), + _ => {} + } + } else { + return Ok(page_ref); + } + } + } +} diff --git a/docx-core/src/documents/elements/instr_tc.rs b/docx-core/src/documents/elements/instr_tc.rs new file mode 100644 index 0000000..4425b1d --- /dev/null +++ b/docx-core/src/documents/elements/instr_tc.rs @@ -0,0 +1,99 @@ +use serde::Serialize; + +use crate::documents::*; + +// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TCTC_topic_ID0EU2N1.html +#[derive(Serialize, Debug, Clone, PartialEq, Default)] +pub struct InstrTC { + pub text: String, + // \n Omits the page number for the entry. + pub omits_page_number: bool, + pub level: Option, + // \f + pub item_type_identifier: Option, +} + +impl InstrTC { + pub fn new(text: impl Into) -> Self { + Self { + text: text.into(), + ..Default::default() + } + } + + pub fn omits_page_number(mut self) -> Self { + self.omits_page_number = true; + self + } + + pub fn level(mut self, level: usize) -> Self { + self.level = Some(level); + self + } + + pub fn item_type_identifier(mut self, t: impl Into) -> Self { + self.item_type_identifier = Some(t.into()); + self + } +} + +impl BuildXML for InstrTC { + fn build(&self) -> Vec { + let mut instr = format!("TC {}", self.text); + + if let Some(ref t) = self.item_type_identifier { + instr = format!("{} \\f {}", instr, t); + } + + if let Some(level) = self.level { + instr = format!("{} \\l {}", instr, level); + } + + if self.omits_page_number { + instr = format!("{} \\n", instr); + } + + instr.into() + } +} + +fn parse_level(i: &str) -> Option { + let r = i.replace(""", "").replace("\"", ""); + if let Ok(l) = usize::from_str(&r) { + return Some(l); + } + None +} + +impl std::str::FromStr for InstrTC { + type Err = (); + + fn from_str(instr: &str) -> Result { + let mut s = instr.split(' '); + let text = s.next(); + let mut tc = InstrTC::new(text.unwrap_or_default()); + loop { + if let Some(i) = s.next() { + match i { + "\\f" => { + if let Some(r) = s.next() { + let r = r.replace(""", "").replace("\"", ""); + tc = tc.item_type_identifier(r); + } + } + "\\l" => { + if let Some(r) = s.next() { + if let Some(l) = parse_level(r) { + tc = tc.level(l); + } + } + } + "\\n" => tc = tc.omits_page_number(), + _ => {} + } + } else { + return Ok(tc); + } + } + } +} diff --git a/docx-core/src/documents/elements/instr_text.rs b/docx-core/src/documents/elements/instr_text.rs index 77cfe15..ecf1274 100644 --- a/docx-core/src/documents/elements/instr_text.rs +++ b/docx-core/src/documents/elements/instr_text.rs @@ -7,6 +7,8 @@ use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub enum InstrText { TOC(InstrToC), + TC(InstrTC), + PAGEREF(InstrPAGEREF), Unsupported(String), } @@ -14,6 +16,8 @@ impl BuildXML for Box { fn build(&self) -> Vec { let instr = match self.as_ref() { InstrText::TOC(toc) => toc.build(), + InstrText::TC(tc) => tc.build(), + InstrText::PAGEREF(page_ref) => page_ref.build(), InstrText::Unsupported(s) => s.as_bytes().to_vec(), }; XMLBuilder::new() @@ -36,6 +40,18 @@ impl Serialize for InstrText { t.serialize_field("data", s)?; t.end() } + InstrText::TC(ref s) => { + let mut t = serializer.serialize_struct("TC", 2)?; + t.serialize_field("type", "tc")?; + t.serialize_field("data", s)?; + t.end() + } + InstrText::PAGEREF(ref s) => { + let mut t = serializer.serialize_struct("PAGEREF", 2)?; + t.serialize_field("type", "pageref")?; + t.serialize_field("data", s)?; + t.end() + } InstrText::Unsupported(ref s) => { let mut t = serializer.serialize_struct("Unsupported", 2)?; t.serialize_field("type", "unsupported")?; @@ -62,4 +78,16 @@ mod tests { r#"TOC \o "1-3""# ); } + + #[test] + fn test_pageref_instr() { + let b = Box::new(InstrText::PAGEREF( + InstrPAGEREF::new("_Toc90425847").hyperlink(), + )) + .build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#"PAGEREF _Toc90425847 \h"# + ); + } } diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index 58d9fb1..a885b7c 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -18,7 +18,6 @@ mod default_tab_stop; mod delete; mod delete_text; mod div; -mod instr_toc; mod doc_defaults; mod doc_grid; mod doc_id; @@ -33,7 +32,10 @@ mod highlight; mod indent; mod indent_level; mod insert; +mod instr_pageref; +mod instr_tc; mod instr_text; +mod instr_toc; mod italic; mod italic_cs; mod justification; @@ -136,7 +138,10 @@ pub use highlight::*; pub use indent::*; pub use indent_level::*; pub use insert::*; +pub use instr_pageref::*; +pub use instr_tc::*; pub use instr_text::*; +pub use instr_toc::*; pub use italic::*; pub use italic_cs::*; pub use justification::*; @@ -204,4 +209,3 @@ pub use wp_anchor::*; pub use wps_shape::*; pub use wps_text_box::*; pub use zoom::*; -pub use instr_toc::*; diff --git a/docx-core/src/reader/instr_text.rs b/docx-core/src/reader/instr_text.rs index c8f4768..c7a56d6 100644 --- a/docx-core/src/reader/instr_text.rs +++ b/docx-core/src/reader/instr_text.rs @@ -29,10 +29,19 @@ impl ElementReader for InstrText { return Err(ReaderError::XMLReadError); } else { if instr.starts_with("TOC") { - for i in instr.split(' ') { - dbg!(i); + if let Ok(instr) = InstrToC::from_str(instr) { + return Ok(InstrText::TOC(instr)); + } + } + if instr.starts_with("TC") { + if let Ok(instr) = InstrTC::from_str(instr) { + return Ok(InstrText::TC(instr)); + } + } + if instr.starts_with("PAGEREF") { + if let Ok(instr) = InstrPAGEREF::from_str(instr) { + return Ok(InstrText::PAGEREF(instr)); } - return Ok(InstrText::TOC(InstrToC::new())); } return Ok(InstrText::Unsupported(instr.to_string())); } diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap index 9a75aef..bb4b50a 100644 --- a/docx-wasm/test/__snapshots__/index.test.js.snap +++ b/docx-wasm/test/__snapshots__/index.test.js.snap @@ -23126,15 +23126,19 @@ Object { "caption_label_including_numbers": null, "entry_and_page_number_separator": null, "entry_bookmark_name": null, - "hide_tab_and_page_numbers_in_webview": false, - "hyperlink": false, + "heading_styles_range": Array [ + 1, + 3, + ], + "hide_tab_and_page_numbers_in_webview": true, + "hyperlink": true, "preserve_new_line": false, "preserve_tab": false, "seq_field_identifier_for_prefix": null, "sequence_and_page_numbers_separator": null, "styles_with_levels": Array [], "tc_field_identifier": null, - "use_applied_paragraph_line_level": false, + "use_applied_paragraph_line_level": true, }, "type": "toc", }, @@ -23351,8 +23355,12 @@ Object { "children": Array [ Object { "data": Object { - "data": "PAGEREF _Toc89816785 \\\\h", - "type": "unsupported", + "data": Object { + "hyperlink": true, + "page_ref": "PAGEREF", + "relative_position": false, + }, + "type": "pageref", }, "type": "instrText", }, @@ -23695,8 +23703,12 @@ Object { "children": Array [ Object { "data": Object { - "data": "PAGEREF _Toc89816786 \\\\h", - "type": "unsupported", + "data": Object { + "hyperlink": true, + "page_ref": "PAGEREF", + "relative_position": false, + }, + "type": "pageref", }, "type": "instrText", }, @@ -24039,8 +24051,12 @@ Object { "children": Array [ Object { "data": Object { - "data": "PAGEREF _Toc89816787 \\\\h", - "type": "unsupported", + "data": Object { + "hyperlink": true, + "page_ref": "PAGEREF", + "relative_position": false, + }, + "type": "pageref", }, "type": "instrText", }, @@ -24383,8 +24399,12 @@ Object { "children": Array [ Object { "data": Object { - "data": "PAGEREF _Toc89816788 \\\\h", - "type": "unsupported", + "data": Object { + "hyperlink": true, + "page_ref": "PAGEREF", + "relative_position": false, + }, + "type": "pageref", }, "type": "instrText", },