From 5fe4e4d646db3205af1d87f94e26647c90c7eb69 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Tue, 11 Feb 2020 17:01:39 +0900 Subject: [PATCH] Add reader (#33) * feat: Add contenttypes and rel deserializer * feat: Add reader fundation * feat: Add run reader * feat: Add run reader * feat: Add paragraph reader foundation * fix:test * feat: Add book mark * fix:bookmark id * feat: Add bookmark reader * feat: Add paragraph json fondation * fix:fix reader * feat: Add table reader foundation * feat: Add table reader foundation * feat: Add table cell prop serializer * chore: rename content to children * feat: Add row serializer * feat: Add table property serializer * fix: lint error * feat: Add table reader * feat: reader --- Cargo.lock | 79 +++- docx-core/Cargo.toml | 5 +- docx-core/examples/indent.rs | 22 +- docx-core/examples/numbering.rs | 3 +- docx-core/examples/reader.rs | 10 + docx-core/src/documents/comments.rs | 5 +- docx-core/src/documents/content_types.rs | 169 ++++++--- docx-core/src/documents/doc_props/app.rs | 5 +- docx-core/src/documents/doc_props/core.rs | 8 +- docx-core/src/documents/doc_props/mod.rs | 12 +- docx-core/src/documents/document.rs | 52 ++- docx-core/src/documents/document_rels.rs | 9 +- docx-core/src/documents/elements/bold.rs | 13 +- docx-core/src/documents/elements/bold_cs.rs | 13 +- .../src/documents/elements/bookmark_end.rs | 19 +- .../src/documents/elements/bookmark_start.rs | 17 +- docx-core/src/documents/elements/br.rs | 16 +- docx-core/src/documents/elements/color.rs | 13 +- docx-core/src/documents/elements/comment.rs | 4 +- .../documents/elements/comment_range_end.rs | 4 +- .../documents/elements/comment_range_start.rs | 4 +- .../documents/elements/default_tab_stop.rs | 13 +- docx-core/src/documents/elements/delete.rs | 4 +- .../src/documents/elements/delete_text.rs | 5 +- .../src/documents/elements/doc_defaults.rs | 5 +- docx-core/src/documents/elements/grid_span.rs | 13 +- docx-core/src/documents/elements/highlight.rs | 20 +- docx-core/src/documents/elements/indent.rs | 56 ++- .../src/documents/elements/indent_level.rs | 4 +- docx-core/src/documents/elements/insert.rs | 5 +- docx-core/src/documents/elements/italic.rs | 13 +- docx-core/src/documents/elements/italic_cs.rs | 13 +- .../src/documents/elements/justification.rs | 15 +- docx-core/src/documents/elements/level.rs | 28 +- docx-core/src/documents/elements/level_jc.rs | 13 +- .../src/documents/elements/level_text.rs | 13 +- docx-core/src/documents/elements/name.rs | 13 +- .../src/documents/elements/number_format.rs | 13 +- docx-core/src/documents/elements/numbering.rs | 5 +- .../src/documents/elements/numbering_id.rs | 4 +- .../documents/elements/numbering_property.rs | 21 +- .../src/documents/elements/page_margin.rs | 5 +- docx-core/src/documents/elements/page_size.rs | 5 +- docx-core/src/documents/elements/paragraph.rs | 81 +++- .../documents/elements/paragraph_property.rs | 38 +- .../src/documents/elements/paragraph_style.rs | 13 +- docx-core/src/documents/elements/run.rs | 83 +++- .../src/documents/elements/run_property.rs | 25 +- .../elements/run_property_default.rs | 5 +- .../documents/elements/section_property.rs | 5 +- docx-core/src/documents/elements/start.rs | 13 +- docx-core/src/documents/elements/style.rs | 15 +- docx-core/src/documents/elements/sz.rs | 13 +- docx-core/src/documents/elements/sz_cs.rs | 12 +- docx-core/src/documents/elements/tab.rs | 4 +- docx-core/src/documents/elements/table.rs | 27 +- .../src/documents/elements/table_borders.rs | 24 +- .../src/documents/elements/table_cell.rs | 49 ++- .../documents/elements/table_cell_borders.rs | 24 +- .../documents/elements/table_cell_margins.rs | 5 +- .../documents/elements/table_cell_property.rs | 17 +- .../documents/elements/table_cell_width.rs | 5 +- .../src/documents/elements/table_indent.rs | 5 +- .../src/documents/elements/table_property.rs | 14 +- docx-core/src/documents/elements/table_row.rs | 18 +- .../documents/elements/table_row_property.rs | 4 +- .../src/documents/elements/table_width.rs | 5 +- docx-core/src/documents/elements/text.rs | 31 +- docx-core/src/documents/elements/underline.rs | 13 +- docx-core/src/documents/elements/vanish.rs | 13 +- .../src/documents/elements/vertical_merge.rs | 13 +- docx-core/src/documents/elements/zoom.rs | 13 +- docx-core/src/documents/font_table.rs | 5 +- docx-core/src/documents/mod.rs | 27 +- docx-core/src/documents/numberings.rs | 23 +- docx-core/src/documents/rels.rs | 72 ++-- docx-core/src/documents/settings.rs | 5 +- docx-core/src/documents/styles.rs | 5 +- docx-core/src/lib.rs | 2 + docx-core/src/reader/attributes/mod.rs | 3 + docx-core/src/reader/attributes/width.rs | 21 ++ docx-core/src/reader/delete.rs | 49 +++ docx-core/src/reader/document.rs | 42 +++ docx-core/src/reader/errors.rs | 17 + docx-core/src/reader/from_xml.rs | 8 + docx-core/src/reader/insert.rs | 48 +++ docx-core/src/reader/mod.rs | 49 +++ docx-core/src/reader/numbering_property.rs | 53 +++ docx-core/src/reader/paragraph.rs | 355 ++++++++++++++++++ docx-core/src/reader/rels.rs | 67 ++++ docx-core/src/reader/run.rs | 158 ++++++++ docx-core/src/reader/table.rs | 118 ++++++ docx-core/src/reader/table_cell.rs | 130 +++++++ docx-core/src/reader/table_row.rs | 35 ++ docx-core/src/reader/xml_element.rs | 127 +++++++ docx-core/src/types/alignment_type.rs | 16 + docx-core/src/types/border_position.rs | 9 +- docx-core/src/types/border_type.rs | 5 +- docx-core/src/types/break_type.rs | 19 +- docx-core/src/types/errors.rs | 9 + docx-core/src/types/mod.rs | 2 + docx-core/src/types/special_indent_type.rs | 29 +- docx-core/src/types/style_type.rs | 5 +- docx-core/src/types/table_alignment_type.rs | 15 + docx-core/src/types/vertical_merge_type.rs | 16 +- docx-core/src/types/width_type.rs | 20 +- docx-core/src/xml_builder/elements.rs | 15 +- docx-core/tests/lib.rs | 24 +- docx-wasm/js/bookmark-end.ts | 4 +- docx-wasm/js/bookmark-start.ts | 4 +- docx-wasm/js/paragraph.ts | 4 +- docx-wasm/src/level.rs | 2 +- docx-wasm/src/lib.rs | 2 + docx-wasm/src/paragraph.rs | 22 +- docx-wasm/src/reader.rs | 12 + docx-wasm/src/table.rs | 2 +- docx-wasm/src/table_cell.rs | 2 +- fixtures/bookmark/[Content_Types].xml | 2 +- fixtures/bookmark/bookmark.docx | Bin 4203 -> 4205 bytes fixtures/bookmark/docProps/app.xml | 2 +- fixtures/bookmark/docProps/core.xml | 2 +- fixtures/bookmark/word/document.xml | 18 +- fixtures/paragraph/[Content_Types].xml | 3 + fixtures/paragraph/_rels/.rels | 3 + fixtures/paragraph/docProps/app.xml | 2 + fixtures/paragraph/docProps/core.xml | 2 + fixtures/paragraph/paragraph.docx | Bin 0 -> 5130 bytes .../paragraph/word/_rels/document.xml.rels | 3 + fixtures/paragraph/word/document.xml | 77 ++++ fixtures/paragraph/word/fontTable.xml | 2 + fixtures/paragraph/word/numbering.xml | 2 + fixtures/paragraph/word/settings.xml | 2 + fixtures/paragraph/word/styles.xml | 2 + fixtures/run_props/[Content_Types].xml | 3 + fixtures/run_props/_rels/.rels | 3 + fixtures/run_props/docProps/app.xml | 2 + fixtures/run_props/docProps/core.xml | 2 + fixtures/run_props/run_props.docx | Bin 0 -> 4392 bytes .../run_props/word/_rels/document.xml.rels | 3 + fixtures/run_props/word/document.xml | 103 +++++ fixtures/run_props/word/fontTable.xml | 2 + fixtures/run_props/word/settings.xml | 2 + fixtures/run_props/word/styles.xml | 2 + 143 files changed, 2887 insertions(+), 369 deletions(-) create mode 100644 docx-core/examples/reader.rs create mode 100644 docx-core/src/reader/attributes/mod.rs create mode 100644 docx-core/src/reader/attributes/width.rs create mode 100644 docx-core/src/reader/delete.rs create mode 100644 docx-core/src/reader/document.rs create mode 100644 docx-core/src/reader/errors.rs create mode 100644 docx-core/src/reader/from_xml.rs create mode 100644 docx-core/src/reader/insert.rs create mode 100644 docx-core/src/reader/mod.rs create mode 100644 docx-core/src/reader/numbering_property.rs create mode 100644 docx-core/src/reader/paragraph.rs create mode 100644 docx-core/src/reader/rels.rs create mode 100644 docx-core/src/reader/run.rs create mode 100644 docx-core/src/reader/table.rs create mode 100644 docx-core/src/reader/table_cell.rs create mode 100644 docx-core/src/reader/table_row.rs create mode 100644 docx-core/src/reader/xml_element.rs create mode 100644 docx-core/src/types/errors.rs create mode 100644 docx-wasm/src/reader.rs create mode 100644 fixtures/paragraph/[Content_Types].xml create mode 100644 fixtures/paragraph/_rels/.rels create mode 100644 fixtures/paragraph/docProps/app.xml create mode 100644 fixtures/paragraph/docProps/core.xml create mode 100644 fixtures/paragraph/paragraph.docx create mode 100644 fixtures/paragraph/word/_rels/document.xml.rels create mode 100644 fixtures/paragraph/word/document.xml create mode 100644 fixtures/paragraph/word/fontTable.xml create mode 100644 fixtures/paragraph/word/numbering.xml create mode 100644 fixtures/paragraph/word/settings.xml create mode 100644 fixtures/paragraph/word/styles.xml create mode 100644 fixtures/run_props/[Content_Types].xml create mode 100644 fixtures/run_props/_rels/.rels create mode 100644 fixtures/run_props/docProps/app.xml create mode 100644 fixtures/run_props/docProps/core.xml create mode 100644 fixtures/run_props/run_props.docx create mode 100644 fixtures/run_props/word/_rels/document.xml.rels create mode 100644 fixtures/run_props/word/document.xml create mode 100644 fixtures/run_props/word/fontTable.xml create mode 100644 fixtures/run_props/word/settings.xml create mode 100644 fixtures/run_props/word/styles.xml diff --git a/Cargo.lock b/Cargo.lock index 997342b..ee09f73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ansi_term" version = "0.11.0" @@ -79,10 +84,12 @@ name = "docx-rs" version = "0.2.0" dependencies = [ "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -114,6 +121,17 @@ dependencies = [ "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "flate2" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "futures" version = "0.1.29" @@ -127,6 +145,11 @@ dependencies = [ "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "js-sys" version = "0.3.30" @@ -158,6 +181,14 @@ name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nom" version = "4.2.3" @@ -228,11 +259,44 @@ name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "scoped-tls" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sourcefile" version = "0.1.4" @@ -442,14 +506,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "zip" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" @@ -462,13 +528,16 @@ dependencies = [ "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" "checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum js-sys 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "a60f6ca5eb7ae3014e3ab34e3189a1560267245216e19f76a021a4c669817e62" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" @@ -478,7 +547,11 @@ dependencies = [ "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" @@ -503,4 +576,4 @@ dependencies = [ "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" -"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6" +"checksum zip 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e41ff37ba788e2169b19fa70253b70cb53d9f2db9fb9aea9bcfc5047e02c3bae" diff --git a/docx-core/Cargo.toml b/docx-core/Cargo.toml index ad80998..4231bae 100644 --- a/docx-core/Cargo.toml +++ b/docx-core/Cargo.toml @@ -23,8 +23,9 @@ path = "src/lib.rs" xml-rs = "0.8.0" wasm-bindgen = "0.2.50" thiserror = "1.0" -zip = { version = "0.5", default-features = false } +zip = { version = "0.5.4", default-features = false, features = ["deflate"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [dev-dependencies] pretty_assertions = "0.6.1" - diff --git a/docx-core/examples/indent.rs b/docx-core/examples/indent.rs index f09d0a2..9ce5f12 100644 --- a/docx-core/examples/indent.rs +++ b/docx-core/examples/indent.rs @@ -9,20 +9,20 @@ pub fn main() -> Result<(), DocxError> { .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(DUMMY)) - .indent(840, None), + .indent(840, None, None), ) .add_paragraph(Paragraph::new()) - .add_paragraph( - Paragraph::new() - .add_run(Run::new().add_text(DUMMY)) - .indent(840, Some(SpecialIndentType::FirstLine(720))), - ) + .add_paragraph(Paragraph::new().add_run(Run::new().add_text(DUMMY)).indent( + 840, + Some(SpecialIndentType::FirstLine(720)), + None, + )) .add_paragraph(Paragraph::new()) - .add_paragraph( - Paragraph::new() - .add_run(Run::new().add_text(DUMMY)) - .indent(1560, Some(SpecialIndentType::Hanging(720))), - ) + .add_paragraph(Paragraph::new().add_run(Run::new().add_text(DUMMY)).indent( + 1560, + Some(SpecialIndentType::Hanging(720)), + None, + )) .build() .pack(file)?; Ok(()) diff --git a/docx-core/examples/numbering.rs b/docx-core/examples/numbering.rs index 1d11356..0a303ad 100644 --- a/docx-core/examples/numbering.rs +++ b/docx-core/examples/numbering.rs @@ -1,6 +1,5 @@ use docx_rs::*; - pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./numbering.docx"); let file = std::fs::File::create(&path).unwrap(); @@ -19,7 +18,7 @@ pub fn main() -> Result<(), DocxError> { LevelText::new("Section %1."), LevelJc::new("left"), ) - .indent(1620, Some(SpecialIndentType::Hanging(320))), + .indent(1620, Some(SpecialIndentType::Hanging(320)), None), ), ) .build() diff --git a/docx-core/examples/reader.rs b/docx-core/examples/reader.rs new file mode 100644 index 0000000..ca28116 --- /dev/null +++ b/docx-core/examples/reader.rs @@ -0,0 +1,10 @@ +use docx_rs::*; +use std::fs::*; +use std::io::Read; + +pub fn main() { + let mut file = File::open("./fixtures/paragraph/paragraph.docx").unwrap(); + let mut buf = vec![]; + file.read_to_end(&mut buf).unwrap(); + dbg!(read_docx(&buf).unwrap().json()); +} diff --git a/docx-core/src/documents/comments.rs b/docx-core/src/documents/comments.rs index bc11170..5d066f8 100644 --- a/docx-core/src/documents/comments.rs +++ b/docx-core/src/documents/comments.rs @@ -2,7 +2,10 @@ use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Comments { comments: Vec, } diff --git a/docx-core/src/documents/content_types.rs b/docx-core/src/documents/content_types.rs index 0a1fa39..4ce5c93 100644 --- a/docx-core/src/documents/content_types.rs +++ b/docx-core/src/documents/content_types.rs @@ -1,67 +1,142 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::io::Read; +use xml::reader::{EventReader, XmlEvent}; + use crate::documents::BuildXML; +use crate::reader::{FromXML, ReaderError}; use crate::xml_builder::*; -#[derive(Debug)] -pub struct ContentTypes {} +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct ContentTypes { + types: HashMap, +} impl ContentTypes { pub fn new() -> ContentTypes { Default::default() } + + pub fn add_content(mut self, namespace: impl Into, path: impl Into) -> Self { + self.types.insert(namespace.into(), path.into()); + self + } + + pub fn set_default(mut self) -> ContentTypes { + self.types.insert( + "application/vnd.openxmlformats-package.relationships+xml".to_owned(), + "/_rels/.rels".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.extended-properties+xml".to_owned(), + "/docProps/app.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-package.core-properties+xml".to_owned(), + "/docProps/core.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-package.relationships+xml".to_owned(), + "/word/_rels/document.xml.rels".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" + .to_owned(), + "/word/settings.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" + .to_owned(), + "/word/fontTable.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" + .to_owned(), + "/word/document.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml".to_owned(), + "/word/styles.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" + .to_owned(), + "/word/comments.xml".to_owned(), + ); + self.types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml" + .to_owned(), + "/word/numbering.xml".to_owned(), + ); + self + } } impl Default for ContentTypes { fn default() -> Self { - ContentTypes {} + ContentTypes { + types: HashMap::new(), + } } } impl BuildXML for ContentTypes { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.declaration(None) - .open_types("http://schemas.openxmlformats.org/package/2006/content-types") - .add_override( - "/_rels/.rels", - "application/vnd.openxmlformats-package.relationships+xml", - ) - .add_override( - "/docProps/app.xml", - "application/vnd.openxmlformats-officedocument.extended-properties+xml", - ) - .add_override( - "/docProps/core.xml", - "application/vnd.openxmlformats-package.core-properties+xml", - ) - .add_override( - "/word/_rels/document.xml.rels", - "application/vnd.openxmlformats-package.relationships+xml", - ) - .add_override( - "/word/settings.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", - ) - .add_override( - "/word/fontTable.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", - ) - .add_override( - "/word/document.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", - ) - .add_override( - "/word/styles.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", - ) - .add_override( - "/word/comments.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", - ) - .add_override( - "/word/numbering.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", - ) - .close() - .build() + let mut b = b + .declaration(None) + .open_types("http://schemas.openxmlformats.org/package/2006/content-types"); + for (k, v) in self.types.iter() { + b = b.add_override(k, v); + } + b.close().build() + } +} + +impl FromXML for ContentTypes { + fn from_xml(reader: R) -> Result { + let parser = EventReader::new(reader); + let mut s = Self::default(); + let mut depth = 0; + for e in parser { + match e { + Ok(XmlEvent::StartElement { attributes, .. }) => { + if depth == 1 { + let namespace = attributes[0].value.clone(); + let path = attributes[1].value.clone(); + s = s.add_content(namespace, path); + } + depth += 1; + } + Ok(XmlEvent::EndElement { .. }) => { + depth -= 1; + } + Err(_) => return Err(ReaderError::XMLReadError), + _ => {} + } + } + Ok(s) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + + #[test] + fn test_from_xml() { + let xml = r#" + "#; + let c = ContentTypes::from_xml(xml.as_bytes()).unwrap(); + let mut types = HashMap::new(); + types.insert( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" + .to_owned(), + "/word/document.xml".to_owned(), + ); + assert_eq!(ContentTypes { types }, c); } } diff --git a/docx-core/src/documents/doc_props/app.rs b/docx-core/src/documents/doc_props/app.rs index 3235bdc..e3dfcbf 100644 --- a/docx-core/src/documents/doc_props/app.rs +++ b/docx-core/src/documents/doc_props/app.rs @@ -1,7 +1,10 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct AppProps {} impl AppProps { diff --git a/docx-core/src/documents/doc_props/core.rs b/docx-core/src/documents/doc_props/core.rs index eab3987..d1ac025 100644 --- a/docx-core/src/documents/doc_props/core.rs +++ b/docx-core/src/documents/doc_props/core.rs @@ -1,12 +1,16 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct CoreProps { config: CorePropsConfig, } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct CorePropsConfig { created: Option, creator: Option, diff --git a/docx-core/src/documents/doc_props/mod.rs b/docx-core/src/documents/doc_props/mod.rs index e05b3aa..ff4738a 100644 --- a/docx-core/src/documents/doc_props/mod.rs +++ b/docx-core/src/documents/doc_props/mod.rs @@ -3,12 +3,16 @@ mod core; pub use self::app::*; pub use self::core::*; + use crate::documents::BuildXML; -#[derive(Debug)] -pub(crate) struct DocProps { - app: AppProps, - core: CoreProps, +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DocProps { + pub app: AppProps, + pub core: CoreProps, } impl DocProps { diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs index 4e38dc6..3a50a9d 100644 --- a/docx-core/src/documents/document.rs +++ b/docx-core/src/documents/document.rs @@ -1,20 +1,56 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + use super::{Paragraph, SectionProperty, Table}; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Document { - pub(crate) children: Vec, + pub children: Vec, pub section_property: SectionProperty, pub has_numbering: bool, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum DocumentChild { Paragraph(Paragraph), Table(Table), } +impl Serialize for DocumentChild { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + DocumentChild::Paragraph(ref p) => { + let mut t = serializer.serialize_struct("Paragraph", 2)?; + t.serialize_field("type", "paragraph")?; + t.serialize_field("data", p)?; + t.end() + } + DocumentChild::Table(ref c) => { + let mut t = serializer.serialize_struct("Table", 2)?; + t.serialize_field("type", "table")?; + t.serialize_field("data", c)?; + t.end() + } + } + } +} + +impl Default for Document { + fn default() -> Self { + Self { + children: Vec::new(), + section_property: SectionProperty::new(), + has_numbering: false, + } + } +} + impl Document { pub fn new() -> Document { Default::default() @@ -37,16 +73,6 @@ impl Document { } } -impl Default for Document { - fn default() -> Self { - Self { - children: Vec::new(), - section_property: SectionProperty::new(), - has_numbering: false, - } - } -} - impl BuildXML for Document { fn build(&self) -> Vec { let mut b = XMLBuilder::new() diff --git a/docx-core/src/documents/document_rels.rs b/docx-core/src/documents/document_rels.rs index 21855ce..9e6d4eb 100644 --- a/docx-core/src/documents/document_rels.rs +++ b/docx-core/src/documents/document_rels.rs @@ -1,10 +1,13 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct DocumentRels { - pub(crate) has_comments: bool, - pub(crate) has_numberings: bool, + pub has_comments: bool, + pub has_numberings: bool, } impl DocumentRels { diff --git a/docx-core/src/documents/elements/bold.rs b/docx-core/src/documents/elements/bold.rs index 2010689..fc72e3d 100644 --- a/docx-core/src/documents/elements/bold.rs +++ b/docx-core/src/documents/elements/bold.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Bold {} impl Bold { @@ -16,6 +18,15 @@ impl Default for Bold { } } +impl Serialize for Bold { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(true) + } +} + impl BuildXML for Bold { fn build(&self) -> Vec { let b = XMLBuilder::new(); diff --git a/docx-core/src/documents/elements/bold_cs.rs b/docx-core/src/documents/elements/bold_cs.rs index 4e63d90..894a6a4 100644 --- a/docx-core/src/documents/elements/bold_cs.rs +++ b/docx-core/src/documents/elements/bold_cs.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct BoldCs {} impl BoldCs { @@ -16,6 +18,15 @@ impl Default for BoldCs { } } +impl Serialize for BoldCs { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(true) + } +} + impl BuildXML for BoldCs { fn build(&self) -> Vec { let b = XMLBuilder::new(); diff --git a/docx-core/src/documents/elements/bookmark_end.rs b/docx-core/src/documents/elements/bookmark_end.rs index ffda2f3..b62f395 100644 --- a/docx-core/src/documents/elements/bookmark_end.rs +++ b/docx-core/src/documents/elements/bookmark_end.rs @@ -1,21 +1,23 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct BookmarkEnd { - id: String, + id: usize, } impl BookmarkEnd { - pub fn new(id: impl Into) -> BookmarkEnd { - BookmarkEnd { id: id.into() } + pub fn new(id: usize) -> BookmarkEnd { + BookmarkEnd { id } } } impl BuildXML for BookmarkEnd { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.bookmark_end(&self.id).build() + b.bookmark_end(&format!("{}", self.id)).build() } } @@ -29,11 +31,8 @@ mod tests { #[test] fn test_bookmark_end() { - let c = BookmarkEnd::new("mockid"); + let c = BookmarkEnd::new(0); let b = c.build(); - assert_eq!( - str::from_utf8(&b).unwrap(), - r#""# - ); + assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } diff --git a/docx-core/src/documents/elements/bookmark_start.rs b/docx-core/src/documents/elements/bookmark_start.rs index caac7c0..3b9d6d7 100644 --- a/docx-core/src/documents/elements/bookmark_start.rs +++ b/docx-core/src/documents/elements/bookmark_start.rs @@ -1,16 +1,18 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct BookmarkStart { - id: String, + id: usize, name: String, } impl BookmarkStart { - pub fn new(id: impl Into, name: impl Into) -> BookmarkStart { + pub fn new(id: usize, name: impl Into) -> BookmarkStart { BookmarkStart { - id: id.into(), + id, name: name.into(), } } @@ -19,7 +21,8 @@ impl BookmarkStart { impl BuildXML for BookmarkStart { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.bookmark_start(&self.id, &self.name).build() + b.bookmark_start(&format!("{}", self.id), &self.name) + .build() } } @@ -33,11 +36,11 @@ mod tests { #[test] fn test_bookmark_start() { - let c = BookmarkStart::new("mockid", "mockname"); + let c = BookmarkStart::new(0, "mockname"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# ); } } diff --git a/docx-core/src/documents/elements/br.rs b/docx-core/src/documents/elements/br.rs index 8624b9e..ee7a5ad 100644 --- a/docx-core/src/documents/elements/br.rs +++ b/docx-core/src/documents/elements/br.rs @@ -1,8 +1,11 @@ +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde::Deserialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Break { break_type: BreakType, } @@ -19,3 +22,14 @@ impl BuildXML for Break { b.br(&self.break_type.to_string()).build() } } + +impl Serialize for Break { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut t = serializer.serialize_struct("Break", 1)?; + t.serialize_field("breakType", &format!("{}", &self.break_type))?; + t.end() + } +} diff --git a/docx-core/src/documents/elements/color.rs b/docx-core/src/documents/elements/color.rs index aff1e95..9c4543a 100644 --- a/docx-core/src/documents/elements/color.rs +++ b/docx-core/src/documents/elements/color.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Deserialize, Debug, Clone, PartialEq)] pub struct Color { val: String, } @@ -18,6 +20,15 @@ impl BuildXML for Color { } } +impl Serialize for Color { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/comment.rs b/docx-core/src/documents/elements/comment.rs index 46547e3..9761369 100644 --- a/docx-core/src/documents/elements/comment.rs +++ b/docx-core/src/documents/elements/comment.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + use crate::documents::{BuildXML, Paragraph}; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct Comment { pub id: usize, pub author: String, diff --git a/docx-core/src/documents/elements/comment_range_end.rs b/docx-core/src/documents/elements/comment_range_end.rs index 277f3ee..5313f78 100644 --- a/docx-core/src/documents/elements/comment_range_end.rs +++ b/docx-core/src/documents/elements/comment_range_end.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct CommentRangeEnd { id: usize, } diff --git a/docx-core/src/documents/elements/comment_range_start.rs b/docx-core/src/documents/elements/comment_range_start.rs index 3d45805..a47b5e9 100644 --- a/docx-core/src/documents/elements/comment_range_start.rs +++ b/docx-core/src/documents/elements/comment_range_start.rs @@ -1,8 +1,10 @@ +use serde::Serialize; + use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct CommentRangeStart { id: usize, comment: Comment, diff --git a/docx-core/src/documents/elements/default_tab_stop.rs b/docx-core/src/documents/elements/default_tab_stop.rs index 97ff0f0..a588573 100644 --- a/docx-core/src/documents/elements/default_tab_stop.rs +++ b/docx-core/src/documents/elements/default_tab_stop.rs @@ -1,7 +1,9 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::{Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq)] pub struct DefaultTabStop { val: usize, } @@ -19,6 +21,15 @@ impl BuildXML for DefaultTabStop { } } +impl Serialize for DefaultTabStop { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.val as u64) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/delete.rs b/docx-core/src/documents/elements/delete.rs index 85ea7b6..ee1a25d 100644 --- a/docx-core/src/documents/elements/delete.rs +++ b/docx-core/src/documents/elements/delete.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + use crate::documents::{BuildXML, HistoryId, Run}; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct Delete { pub author: String, pub date: String, diff --git a/docx-core/src/documents/elements/delete_text.rs b/docx-core/src/documents/elements/delete_text.rs index 848f3e7..5d79949 100644 --- a/docx-core/src/documents/elements/delete_text.rs +++ b/docx-core/src/documents/elements/delete_text.rs @@ -1,7 +1,10 @@ +use serde::{Deserialize, Serialize}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct DeleteText { text: String, preserve_space: bool, diff --git a/docx-core/src/documents/elements/doc_defaults.rs b/docx-core/src/documents/elements/doc_defaults.rs index 1ede151..d5e7d0f 100644 --- a/docx-core/src/documents/elements/doc_defaults.rs +++ b/docx-core/src/documents/elements/doc_defaults.rs @@ -1,9 +1,12 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; use super::run_property_default::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct DocDefaults { run_property_default: RunPropertyDefault, } diff --git a/docx-core/src/documents/elements/grid_span.rs b/docx-core/src/documents/elements/grid_span.rs index a5d8d35..325a78b 100644 --- a/docx-core/src/documents/elements/grid_span.rs +++ b/docx-core/src/documents/elements/grid_span.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct GridSpan { val: usize, } @@ -18,6 +20,15 @@ impl BuildXML for GridSpan { } } +impl Serialize for GridSpan { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.val as u32) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/highlight.rs b/docx-core/src/documents/elements/highlight.rs index a7746be..fcbc233 100644 --- a/docx-core/src/documents/elements/highlight.rs +++ b/docx-core/src/documents/elements/highlight.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Highlight { val: String, } @@ -18,6 +20,15 @@ impl BuildXML for Highlight { } } +impl Serialize for Highlight { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { @@ -35,4 +46,11 @@ mod tests { r#""# ); } + + #[test] + fn test_highlight_json() { + let c = Highlight::new("FFFFFF"); + + assert_eq!(serde_json::to_string(&c).unwrap(), r#""FFFFFF""#); + } } diff --git a/docx-core/src/documents/elements/indent.rs b/docx-core/src/documents/elements/indent.rs index 5f1924b..8936b44 100644 --- a/docx-core/src/documents/elements/indent.rs +++ b/docx-core/src/documents/elements/indent.rs @@ -1,30 +1,61 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Indent { - left: usize, + start: usize, + end: Option, special_indent: Option, } impl Indent { - pub fn new(left: usize, special_indent: Option) -> Indent { + pub fn new( + start: usize, + special_indent: Option, + end: Option, + ) -> Indent { Indent { - left, + start, + end, special_indent, } } + + pub fn end(mut self, end: usize) -> Self { + self.end = Some(end); + self + } } impl BuildXML for Indent { fn build(&self) -> Vec { XMLBuilder::new() - .indent(self.left, self.special_indent) + .indent( + self.start, + self.special_indent, + self.end.unwrap_or_default(), + ) .build() } } +impl Serialize for Indent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut t = serializer.serialize_struct("Indent", 3)?; + t.serialize_field("start", &self.start)?; + t.serialize_field("end", &self.end)?; + t.serialize_field("specialIndent", &self.special_indent)?; + t.end() + } +} + #[cfg(test)] mod tests { @@ -35,25 +66,28 @@ mod tests { #[test] fn test_left() { - let b = Indent::new(20, None).build(); - assert_eq!(str::from_utf8(&b).unwrap(), r#""#); + let b = Indent::new(20, None, None).build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); } #[test] fn test_first_line() { - let b = Indent::new(20, Some(SpecialIndentType::FirstLine(40))).build(); + let b = Indent::new(20, Some(SpecialIndentType::FirstLine(40)), None).build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# ); } #[test] fn test_hanging() { - let b = Indent::new(20, Some(SpecialIndentType::Hanging(50))).build(); + let b = Indent::new(20, Some(SpecialIndentType::Hanging(50)), None).build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# ); } } diff --git a/docx-core/src/documents/elements/indent_level.rs b/docx-core/src/documents/elements/indent_level.rs index f65075e..704bc50 100644 --- a/docx-core/src/documents/elements/indent_level.rs +++ b/docx-core/src/documents/elements/indent_level.rs @@ -1,9 +1,9 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct IndentLevel { - val: usize, + pub val: usize, } impl IndentLevel { diff --git a/docx-core/src/documents/elements/insert.rs b/docx-core/src/documents/elements/insert.rs index 79178c8..9927837 100644 --- a/docx-core/src/documents/elements/insert.rs +++ b/docx-core/src/documents/elements/insert.rs @@ -1,7 +1,10 @@ +use serde::Serialize; + use crate::documents::{BuildXML, HistoryId, Run}; + use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] pub struct Insert { pub run: Run, pub author: String, diff --git a/docx-core/src/documents/elements/italic.rs b/docx-core/src/documents/elements/italic.rs index bfd755b..57a53b4 100644 --- a/docx-core/src/documents/elements/italic.rs +++ b/docx-core/src/documents/elements/italic.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Italic {} impl Italic { @@ -16,6 +18,15 @@ impl Default for Italic { } } +impl Serialize for Italic { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(true) + } +} + impl BuildXML for Italic { fn build(&self) -> Vec { let b = XMLBuilder::new(); diff --git a/docx-core/src/documents/elements/italic_cs.rs b/docx-core/src/documents/elements/italic_cs.rs index f97cce1..e1b347e 100644 --- a/docx-core/src/documents/elements/italic_cs.rs +++ b/docx-core/src/documents/elements/italic_cs.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct ItalicCs {} impl ItalicCs { @@ -16,6 +18,15 @@ impl Default for ItalicCs { } } +impl Serialize for ItalicCs { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(true) + } +} + impl BuildXML for ItalicCs { fn build(&self) -> Vec { let b = XMLBuilder::new(); diff --git a/docx-core/src/documents/elements/justification.rs b/docx-core/src/documents/elements/justification.rs index bf43447..61e4cd4 100644 --- a/docx-core/src/documents/elements/justification.rs +++ b/docx-core/src/documents/elements/justification.rs @@ -1,3 +1,5 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; @@ -9,9 +11,9 @@ use crate::xml_builder::*; // present without the val attribute, the default of the val attribute is centerGroup . This means that the instances // of mathematical text can be aligned with respect to each other, but the entire group of mathematical text is // centered as a whole. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Justification { - val: String, + pub val: String, } impl Justification { @@ -27,6 +29,15 @@ impl BuildXML for Justification { } } +impl Serialize for Justification { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/level.rs b/docx-core/src/documents/elements/level.rs index f3b3c9e..a8befc6 100644 --- a/docx-core/src/documents/elements/level.rs +++ b/docx-core/src/documents/elements/level.rs @@ -2,13 +2,16 @@ use crate::documents::{BuildXML, LevelJc, LevelText, NumberFormat, ParagraphProp use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Level { - level: usize, - start: Start, - format: NumberFormat, - text: LevelText, - jc: LevelJc, + pub level: usize, + pub start: Start, + pub format: NumberFormat, + pub text: LevelText, + pub jc: LevelJc, pub paragraph_property: ParagraphProperty, } @@ -30,8 +33,13 @@ impl Level { } } - pub fn indent(mut self, left: usize, special_indent: Option) -> Self { - self.paragraph_property = self.paragraph_property.indent(left, special_indent); + pub fn indent( + mut self, + left: usize, + special_indent: Option, + end: Option, + ) -> Self { + self.paragraph_property = self.paragraph_property.indent(left, special_indent, end); self } } @@ -83,11 +91,11 @@ mod tests { LevelText::new("%4."), LevelJc::new("left"), ) - .indent(320, Some(SpecialIndentType::Hanging(200))) + .indent(320, Some(SpecialIndentType::Hanging(200)), None) .build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# ); } } diff --git a/docx-core/src/documents/elements/level_jc.rs b/docx-core/src/documents/elements/level_jc.rs index 14c20d5..104e021 100644 --- a/docx-core/src/documents/elements/level_jc.rs +++ b/docx-core/src/documents/elements/level_jc.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct LevelJc { val: String, } @@ -19,6 +21,15 @@ impl BuildXML for LevelJc { } } +impl Serialize for LevelJc { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/level_text.rs b/docx-core/src/documents/elements/level_text.rs index da40304..dc8416d 100644 --- a/docx-core/src/documents/elements/level_text.rs +++ b/docx-core/src/documents/elements/level_text.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct LevelText { val: String, } @@ -19,6 +21,15 @@ impl BuildXML for LevelText { } } +impl Serialize for LevelText { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/name.rs b/docx-core/src/documents/elements/name.rs index 9c39957..3fd7498 100644 --- a/docx-core/src/documents/elements/name.rs +++ b/docx-core/src/documents/elements/name.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq)] pub struct Name { name: String, } @@ -19,6 +21,15 @@ impl BuildXML for Name { } } +impl Serialize for Name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.name) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/number_format.rs b/docx-core/src/documents/elements/number_format.rs index 5359109..1865719 100644 --- a/docx-core/src/documents/elements/number_format.rs +++ b/docx-core/src/documents/elements/number_format.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct NumberFormat { val: String, } @@ -19,6 +21,15 @@ impl BuildXML for NumberFormat { } } +impl Serialize for NumberFormat { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/numbering.rs b/docx-core/src/documents/elements/numbering.rs index 415b0e2..159bb35 100644 --- a/docx-core/src/documents/elements/numbering.rs +++ b/docx-core/src/documents/elements/numbering.rs @@ -1,7 +1,10 @@ use crate::documents::{BuildXML, Level}; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Numbering { id: usize, levels: Vec, diff --git a/docx-core/src/documents/elements/numbering_id.rs b/docx-core/src/documents/elements/numbering_id.rs index af48517..c1583a6 100644 --- a/docx-core/src/documents/elements/numbering_id.rs +++ b/docx-core/src/documents/elements/numbering_id.rs @@ -1,9 +1,9 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct NumberingId { - id: usize, + pub id: usize, } impl NumberingId { diff --git a/docx-core/src/documents/elements/numbering_property.rs b/docx-core/src/documents/elements/numbering_property.rs index 627a53e..fa6704e 100644 --- a/docx-core/src/documents/elements/numbering_property.rs +++ b/docx-core/src/documents/elements/numbering_property.rs @@ -1,11 +1,14 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + use super::{IndentLevel, NumberingId}; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct NumberingProperty { - id: NumberingId, - level: IndentLevel, + pub id: NumberingId, + pub level: IndentLevel, } impl NumberingProperty { @@ -25,6 +28,18 @@ impl BuildXML for NumberingProperty { } } +impl Serialize for NumberingProperty { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut t = serializer.serialize_struct("NumberProperty", 2)?; + t.serialize_field("id", &self.id.id)?; + t.serialize_field("level", &self.level.val)?; + t.end() + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/page_margin.rs b/docx-core/src/documents/elements/page_margin.rs index 8bb2ecb..c9405f3 100644 --- a/docx-core/src/documents/elements/page_margin.rs +++ b/docx-core/src/documents/elements/page_margin.rs @@ -1,7 +1,10 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct PageMargin { top: usize, left: usize, diff --git a/docx-core/src/documents/elements/page_size.rs b/docx-core/src/documents/elements/page_size.rs index f3ccefb..531d7d7 100644 --- a/docx-core/src/documents/elements/page_size.rs +++ b/docx-core/src/documents/elements/page_size.rs @@ -1,7 +1,10 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct PageSize { w: usize, h: usize, diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index 2bfc4ea..b9cd7ef 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -1,14 +1,18 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct Paragraph { pub children: Vec, pub property: ParagraphProperty, pub has_numbering: bool, - attrs: Vec<(String, String)>, + pub attrs: Vec<(String, String)>, } impl Default for Paragraph { @@ -22,7 +26,7 @@ impl Default for Paragraph { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum ParagraphChild { Run(Run), Insert(Insert), @@ -47,6 +51,33 @@ impl BuildXML for ParagraphChild { } } +impl Serialize for ParagraphChild { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + ParagraphChild::Run(ref r) => { + let mut t = serializer.serialize_struct("Run", 2)?; + t.serialize_field("type", "run")?; + t.serialize_field("data", r)?; + t.end() + } + ParagraphChild::Insert(ref r) => { + let mut t = serializer.serialize_struct("Insert", 2)?; + t.serialize_field("type", "insert")?; + t.serialize_field("data", r)?; + t.end() + } + _ => { + let mut t = serializer.serialize_struct("Unsupported", 2)?; + t.serialize_field("type", "unsupported")?; + t.end() + } + } + } +} + impl Paragraph { pub fn new() -> Paragraph { Default::default() @@ -76,17 +107,13 @@ impl Paragraph { self } - pub fn add_bookmark_start( - mut self, - id: impl Into, - name: impl Into, - ) -> Paragraph { + pub fn add_bookmark_start(mut self, id: usize, name: impl Into) -> Paragraph { self.children .push(ParagraphChild::BookmarkStart(BookmarkStart::new(id, name))); self } - pub fn add_bookmark_end(mut self, id: impl Into) -> Paragraph { + pub fn add_bookmark_end(mut self, id: usize) -> Paragraph { self.children .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id))); self @@ -115,8 +142,13 @@ impl Paragraph { self } - pub fn indent(mut self, left: usize, special_indent: Option) -> Paragraph { - self.property = self.property.indent(left, special_indent); + pub fn indent( + mut self, + left: usize, + special_indent: Option, + end: Option, + ) -> Paragraph { + self.property = self.property.indent(left, special_indent, end); self } @@ -172,13 +204,13 @@ mod tests { #[test] fn test_bookmark() { let b = Paragraph::new() - .add_bookmark_start("1234-5678", "article") + .add_bookmark_start(0, "article") .add_run(Run::new().add_text("Hello")) - .add_bookmark_end("1234-5678") + .add_bookmark_end(0) .build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#"Hello"# + r#"Hello"# ); } @@ -212,4 +244,25 @@ mod tests { r#"Hello"# ); } + + #[test] + fn test_paragraph_run_json() { + let run = Run::new().add_text("Hello"); + let p = Paragraph::new().add_run(run); + assert_eq!( + serde_json::to_string(&p).unwrap(), + r#"{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"style":"Normal","numberingProperty":null,"alignment":null,"indent":null},"hasNumbering":false,"attrs":[]}"# + ); + } + + #[test] + fn test_paragraph_insert_json() { + let run = Run::new().add_text("Hello"); + let ins = Insert::new(run); + let p = Paragraph::new().add_insert(ins); + assert_eq!( + serde_json::to_string(&p).unwrap(), + r#"{"children":[{"type":"insert","data":{"run":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]},"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"style":"Normal","numberingProperty":null,"alignment":null,"indent":null},"hasNumbering":false,"attrs":[]}"# + ); + } } diff --git a/docx-core/src/documents/elements/paragraph_property.rs b/docx-core/src/documents/elements/paragraph_property.rs index 9449508..2fb4eac 100644 --- a/docx-core/src/documents/elements/paragraph_property.rs +++ b/docx-core/src/documents/elements/paragraph_property.rs @@ -1,15 +1,18 @@ +use serde::Serialize; + use super::*; use crate::documents::BuildXML; use crate::types::{AlignmentType, SpecialIndentType}; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct ParagraphProperty { - run_property: RunProperty, - style: ParagraphStyle, - numbering_property: Option, - alignment: Option, - indent: Option, + pub run_property: RunProperty, + pub style: ParagraphStyle, + pub numbering_property: Option, + pub alignment: Option, + pub indent: Option, } impl Default for ParagraphProperty { @@ -45,8 +48,13 @@ impl ParagraphProperty { self } - pub fn indent(mut self, left: usize, special_indent: Option) -> Self { - self.indent = Some(Indent::new(left, special_indent)); + pub fn indent( + mut self, + left: usize, + special_indent: Option, + end: Option, + ) -> Self { + self.indent = Some(Indent::new(left, special_indent, end)); self } @@ -101,10 +109,20 @@ mod tests { #[test] fn test_indent() { let c = ParagraphProperty::new(); - let b = c.indent(20, None).build(); + let b = c.indent(20, None, None).build(); assert_eq!( str::from_utf8(&b).unwrap(), - r#""# + r#""# + ); + } + + #[test] + fn test_indent_json() { + let c = ParagraphProperty::new(); + let b = c.indent(20, Some(SpecialIndentType::FirstLine(10)), None); + assert_eq!( + serde_json::to_string(&b).unwrap(), + r#"{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"style":"Normal","numberingProperty":null,"alignment":null,"indent":{"start":20,"end":null,"specialIndent":{"type":"firstLine","val":10}}}"# ); } } diff --git a/docx-core/src/documents/elements/paragraph_style.rs b/docx-core/src/documents/elements/paragraph_style.rs index 4b30508..f9b8c34 100644 --- a/docx-core/src/documents/elements/paragraph_style.rs +++ b/docx-core/src/documents/elements/paragraph_style.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ParagraphStyle { val: String, } @@ -36,6 +38,15 @@ impl BuildXML for ParagraphStyle { } } +impl Serialize for ParagraphStyle { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/run.rs b/docx-core/src/documents/elements/run.rs index 3da4933..686adde 100644 --- a/docx-core/src/documents/elements/run.rs +++ b/docx-core/src/documents/elements/run.rs @@ -1,9 +1,13 @@ use super::{Break, DeleteText, RunProperty, Tab, Text}; +use serde::ser::{SerializeStruct, Serializer}; +use serde::{Deserialize, Serialize}; + use crate::documents::BuildXML; use crate::types::BreakType; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct Run { pub run_property: RunProperty, pub children: Vec, @@ -19,7 +23,7 @@ impl Default for Run { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub enum RunChild { Text(Text), DeleteText(DeleteText), @@ -27,6 +31,39 @@ pub enum RunChild { Break(Break), } +impl Serialize for RunChild { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + RunChild::Text(ref s) => { + let mut t = serializer.serialize_struct("Text", 2)?; + t.serialize_field("type", "text")?; + t.serialize_field("data", s)?; + t.end() + } + RunChild::DeleteText(ref s) => { + let mut t = serializer.serialize_struct("DeleteText", 2)?; + t.serialize_field("type", "deleteText")?; + t.serialize_field("data", s)?; + t.end() + } + RunChild::Tab(_) => { + let mut t = serializer.serialize_struct("Tab", 1)?; + t.serialize_field("type", "tab")?; + t.end() + } + RunChild::Break(ref s) => { + let mut t = serializer.serialize_struct("Break", 2)?; + t.serialize_field("type", "break")?; + t.serialize_field("data", s)?; + t.end() + } + } + } +} + impl Run { pub fn new() -> Run { Run { @@ -34,12 +71,12 @@ impl Run { } } - pub fn add_text(mut self, text: &str) -> Run { + pub fn add_text(mut self, text: impl Into) -> Run { self.children.push(RunChild::Text(Text::new(text))); self } - pub fn add_delete_text(mut self, text: &str) -> Run { + pub fn add_delete_text(mut self, text: impl Into) -> Run { self.children .push(RunChild::DeleteText(DeleteText::new(text))); self @@ -110,6 +147,7 @@ impl BuildXML for Run { #[cfg(test)] mod tests { + use super::super::*; use super::*; #[cfg(test)] use pretty_assertions::assert_eq; @@ -132,4 +170,41 @@ mod tests { r#"Hello"# ); } + + #[test] + fn test_child_json() { + let c = RunChild::Text(Text::new("Hello")); + assert_eq!( + serde_json::to_string(&c).unwrap(), + r#"{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}"# + ); + } + + #[test] + fn test_run_json() { + let run = Run { + children: vec![ + RunChild::Tab(Tab::new()), + RunChild::Text(Text::new("Hello")), + RunChild::Break(Break::new(BreakType::Page)), + RunChild::DeleteText(DeleteText::new("deleted")), + ], + run_property: RunProperty { + sz: Some(Sz::new(30)), + sz_cs: Some(SzCs::new(30)), + color: Some(Color::new("C9211E")), + highlight: Some(Highlight::new("yellow")), + underline: Some(Underline::new("single")), + bold: Some(Bold::new()), + bold_cs: Some(BoldCs::new()), + italic: Some(Italic::new()), + italic_cs: Some(ItalicCs::new()), + vanish: Some(Vanish::new()), + }, + }; + assert_eq!( + serde_json::to_string(&run).unwrap(), + r#"{"runProperty":{"sz":30,"szCs":30,"color":"C9211E","highlight":"yellow","underline":"single","bold":true,"boldCs":true,"italic":true,"italicCs":true,"vanish":true},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"# + ); + } } diff --git a/docx-core/src/documents/elements/run_property.rs b/docx-core/src/documents/elements/run_property.rs index fd126d2..4cbb88a 100644 --- a/docx-core/src/documents/elements/run_property.rs +++ b/docx-core/src/documents/elements/run_property.rs @@ -1,19 +1,22 @@ +use serde::{Deserialize, Serialize}; + use super::{Bold, BoldCs, Color, Highlight, Italic, ItalicCs, Sz, SzCs, Underline, Vanish}; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct RunProperty { - sz: Option, - sz_cs: Option, - color: Option, - highlight: Option, - underline: Option, - bold: Option, - bold_cs: Option, - italic: Option, - italic_cs: Option, - vanish: Option, + pub sz: Option, + pub sz_cs: Option, + pub color: Option, + pub highlight: Option, + pub underline: Option, + pub bold: Option, + pub bold_cs: Option, + pub italic: Option, + pub italic_cs: Option, + pub vanish: Option, } impl RunProperty { diff --git a/docx-core/src/documents/elements/run_property_default.rs b/docx-core/src/documents/elements/run_property_default.rs index fb4f8c7..f62ccfc 100644 --- a/docx-core/src/documents/elements/run_property_default.rs +++ b/docx-core/src/documents/elements/run_property_default.rs @@ -1,8 +1,11 @@ +use serde::Serialize; + use super::RunProperty; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct RunPropertyDefault { run_property: RunProperty, } diff --git a/docx-core/src/documents/elements/section_property.rs b/docx-core/src/documents/elements/section_property.rs index aee38b2..42d7b71 100644 --- a/docx-core/src/documents/elements/section_property.rs +++ b/docx-core/src/documents/elements/section_property.rs @@ -2,7 +2,10 @@ use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct SectionProperty { page_size: PageSize, page_margin: PageMargin, diff --git a/docx-core/src/documents/elements/start.rs b/docx-core/src/documents/elements/start.rs index 8f69b1a..3c8e775 100644 --- a/docx-core/src/documents/elements/start.rs +++ b/docx-core/src/documents/elements/start.rs @@ -1,7 +1,9 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Start { val: usize, } @@ -19,6 +21,15 @@ impl BuildXML for Start { } } +impl Serialize for Start { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.val as u32) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/style.rs b/docx-core/src/documents/elements/style.rs index 313d29d..2ab0795 100644 --- a/docx-core/src/documents/elements/style.rs +++ b/docx-core/src/documents/elements/style.rs @@ -1,16 +1,19 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; use crate::StyleType; use super::{BasedOn, Name, Next, ParagraphProperty, QFormat, RunProperty}; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Style { - style_id: String, - name: Name, - style_type: StyleType, - run_property: RunProperty, - paragraph_property: ParagraphProperty, + pub style_id: String, + pub name: Name, + pub style_type: StyleType, + pub run_property: RunProperty, + pub paragraph_property: ParagraphProperty, } impl Default for Style { diff --git a/docx-core/src/documents/elements/sz.rs b/docx-core/src/documents/elements/sz.rs index ba0ba9b..d93d1b5 100644 --- a/docx-core/src/documents/elements/sz.rs +++ b/docx-core/src/documents/elements/sz.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Sz { val: usize, } @@ -19,6 +21,15 @@ impl BuildXML for Sz { } } +impl Serialize for Sz { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.val as u32) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/sz_cs.rs b/docx-core/src/documents/elements/sz_cs.rs index 3f99e64..fef38b2 100644 --- a/docx-core/src/documents/elements/sz_cs.rs +++ b/docx-core/src/documents/elements/sz_cs.rs @@ -1,7 +1,8 @@ use crate::documents::BuildXML; use crate::xml_builder::*; +use serde::{Deserialize, Serialize, Serializer}; -#[derive(Debug, Clone)] +#[derive(Deserialize, Debug, Clone, PartialEq)] pub struct SzCs { val: usize, } @@ -18,6 +19,15 @@ impl BuildXML for SzCs { } } +impl Serialize for SzCs { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.val as u32) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/tab.rs b/docx-core/src/documents/elements/tab.rs index 5002737..b9628f4 100644 --- a/docx-core/src/documents/elements/tab.rs +++ b/docx-core/src/documents/elements/tab.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Tab {} impl Tab { diff --git a/docx-core/src/documents/elements/table.rs b/docx-core/src/documents/elements/table.rs index baa256b..9b378c1 100644 --- a/docx-core/src/documents/elements/table.rs +++ b/docx-core/src/documents/elements/table.rs @@ -1,14 +1,17 @@ +use serde::Serialize; + use super::{TableGrid, TableProperty, TableRow}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Table { pub rows: Vec, pub grid: Vec, - pub(crate) has_numbering: bool, - property: TableProperty, + pub has_numbering: bool, + pub property: TableProperty, } impl Table { @@ -24,6 +27,11 @@ impl Table { } } + pub fn add_row(mut self, row: TableRow) -> Table { + self.rows.push(row); + self + } + pub fn set_grid(mut self, grid: Vec) -> Table { self.grid = grid; self @@ -39,8 +47,8 @@ impl Table { self } - pub fn width(mut self, w: usize) -> Table { - self.property = self.property.width(w, WidthType::DXA); + pub fn width(mut self, w: usize, t: WidthType) -> Table { + self.property = self.property.width(w, t); self } } @@ -97,4 +105,13 @@ mod tests { "# ); } + + #[test] + fn test_table_json() { + let t = Table::new(vec![]).set_grid(vec![100, 200, 300]); + assert_eq!( + serde_json::to_string(&t).unwrap(), + r#"{"rows":[],"grid":[100,200,300],"hasNumbering":false,"property":{"width":{"width":0,"widthType":"Auto"},"justification":"left","borders":{"top":{"position":"top","borderType":"single","size":2,"space":0,"color":"000000"},"left":{"position":"left","borderType":"single","size":2,"space":0,"color":"000000"},"bottom":{"position":"bottom","borderType":"single","size":2,"space":0,"color":"000000"},"right":{"position":"right","borderType":"single","size":2,"space":0,"color":"000000"},"insideH":{"position":"insideH","borderType":"single","size":2,"space":0,"color":"000000"},"insideV":{"position":"insideV","borderType":"single","size":2,"space":0,"color":"000000"}},"margins":{"top":55,"left":54,"bottom":55,"right":55},"indent":null}}"# + ); + } } diff --git a/docx-core/src/documents/elements/table_borders.rs b/docx-core/src/documents/elements/table_borders.rs index 65ef846..0c4a7e8 100644 --- a/docx-core/src/documents/elements/table_borders.rs +++ b/docx-core/src/documents/elements/table_borders.rs @@ -1,3 +1,5 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; @@ -15,7 +17,8 @@ use crate::xml_builder::*; tr2bl – diagonal border from top right corner to bottom left corner */ -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableBorder { position: BorderPosition, border_type: BorderType, @@ -57,10 +60,10 @@ impl BuildXML for TableBorder { BorderPosition::Right => { base.border_right(self.border_type, self.size, self.space, &self.color) } - BorderPosition::IndideH => { + BorderPosition::InsideH => { base.border_inside_h(self.border_type, self.size, self.space, &self.color) } - BorderPosition::IndideV => { + BorderPosition::InsideV => { base.border_inside_v(self.border_type, self.size, self.space, &self.color) } }; @@ -80,7 +83,8 @@ impl BuildXML for TableBorder { // If there is no cell border, then the table-level exception border shall be displayed // If this element is omitted, then this table shall have the borders specified by the associated table level borders // (§17.4.38). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableBorders { top: Option, left: Option, @@ -97,8 +101,8 @@ impl Default for TableBorders { left: Some(TableBorder::new(BorderPosition::Left)), bottom: Some(TableBorder::new(BorderPosition::Bottom)), right: Some(TableBorder::new(BorderPosition::Right)), - inside_h: Some(TableBorder::new(BorderPosition::IndideH)), - inside_v: Some(TableBorder::new(BorderPosition::IndideV)), + inside_h: Some(TableBorder::new(BorderPosition::InsideH)), + inside_v: Some(TableBorder::new(BorderPosition::InsideV)), } } } @@ -114,8 +118,8 @@ impl TableBorders { BorderPosition::Left => self.left = Some(border), BorderPosition::Bottom => self.bottom = Some(border), BorderPosition::Right => self.right = Some(border), - BorderPosition::IndideH => self.inside_h = Some(border), - BorderPosition::IndideV => self.inside_v = Some(border), + BorderPosition::InsideH => self.inside_h = Some(border), + BorderPosition::InsideV => self.inside_v = Some(border), }; self } @@ -126,8 +130,8 @@ impl TableBorders { BorderPosition::Left => self.left = None, BorderPosition::Bottom => self.bottom = None, BorderPosition::Right => self.right = None, - BorderPosition::IndideH => self.inside_h = None, - BorderPosition::IndideV => self.inside_v = None, + BorderPosition::InsideH => self.inside_h = None, + BorderPosition::InsideV => self.inside_v = None, }; self } diff --git a/docx-core/src/documents/elements/table_cell.rs b/docx-core/src/documents/elements/table_cell.rs index 8bdf79c..acfe694 100644 --- a/docx-core/src/documents/elements/table_cell.rs +++ b/docx-core/src/documents/elements/table_cell.rs @@ -1,20 +1,40 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + use super::{Paragraph, TableCellProperty}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct TableCell { - pub contents: Vec, + pub children: Vec, pub property: TableCellProperty, pub has_numbering: bool, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TableCellContent { Paragraph(Paragraph), } +impl Serialize for TableCellContent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + TableCellContent::Paragraph(ref s) => { + let mut t = serializer.serialize_struct("Paragraph", 2)?; + t.serialize_field("type", "paragraph")?; + t.serialize_field("data", s)?; + t.end() + } + } + } +} + impl TableCell { pub fn new() -> TableCell { Default::default() @@ -24,7 +44,7 @@ impl TableCell { if p.has_numbering { self.has_numbering = true } - self.contents.push(TableCellContent::Paragraph(p)); + self.children.push(TableCellContent::Paragraph(p)); self } @@ -38,8 +58,8 @@ impl TableCell { self } - pub fn width(mut self, v: usize) -> TableCell { - self.property = self.property.width(v, WidthType::DXA); + pub fn width(mut self, v: usize, t: WidthType) -> TableCell { + self.property = self.property.width(v, t); self } } @@ -47,10 +67,10 @@ impl TableCell { impl Default for TableCell { fn default() -> Self { let property = TableCellProperty::new(); - let contents = vec![]; + let children = vec![]; Self { property, - contents, + children, has_numbering: false, } } @@ -60,7 +80,7 @@ impl BuildXML for TableCell { fn build(&self) -> Vec { let b = XMLBuilder::new(); let mut b = b.open_table_cell().add_child(&self.property); - for c in &self.contents { + for c in &self.children { match c { TableCellContent::Paragraph(p) => b = b.add_child(p), } @@ -94,4 +114,15 @@ mod tests { r#"Hello"# ); } + + #[test] + fn test_cell_json() { + let c = TableCell::new() + .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) + .grid_span(2); + assert_eq!( + serde_json::to_string(&c).unwrap(), + r#"{"children":[{"type":"paragraph","data":{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null},"style":"Normal","numberingProperty":null,"alignment":null,"indent":null},"hasNumbering":false,"attrs":[]}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null},"hasNumbering":false}"# + ); + } } diff --git a/docx-core/src/documents/elements/table_cell_borders.rs b/docx-core/src/documents/elements/table_cell_borders.rs index 8e2407d..b7c1764 100644 --- a/docx-core/src/documents/elements/table_cell_borders.rs +++ b/docx-core/src/documents/elements/table_cell_borders.rs @@ -1,3 +1,5 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; @@ -15,7 +17,8 @@ use crate::xml_builder::*; tr2bl – diagonal border from top right corner to bottom left corner */ -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct TableCellBorder { position: BorderPosition, border_type: BorderType, @@ -57,10 +60,10 @@ impl BuildXML for TableCellBorder { BorderPosition::Right => { base.border_right(self.border_type, self.size, self.space, &self.color) } - BorderPosition::IndideH => { + BorderPosition::InsideH => { base.border_inside_h(self.border_type, self.size, self.space, &self.color) } - BorderPosition::IndideV => { + BorderPosition::InsideV => { base.border_inside_v(self.border_type, self.size, self.space, &self.color) } }; @@ -68,7 +71,8 @@ impl BuildXML for TableCellBorder { } } -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct TableCellBorders { top: Option, left: Option, @@ -85,8 +89,8 @@ impl Default for TableCellBorders { left: Some(TableCellBorder::new(BorderPosition::Left)), bottom: Some(TableCellBorder::new(BorderPosition::Bottom)), right: Some(TableCellBorder::new(BorderPosition::Right)), - inside_h: Some(TableCellBorder::new(BorderPosition::IndideH)), - inside_v: Some(TableCellBorder::new(BorderPosition::IndideV)), + inside_h: Some(TableCellBorder::new(BorderPosition::InsideH)), + inside_v: Some(TableCellBorder::new(BorderPosition::InsideV)), } } } @@ -102,8 +106,8 @@ impl TableCellBorders { BorderPosition::Left => self.left = Some(border), BorderPosition::Bottom => self.bottom = Some(border), BorderPosition::Right => self.right = Some(border), - BorderPosition::IndideH => self.inside_h = Some(border), - BorderPosition::IndideV => self.inside_v = Some(border), + BorderPosition::InsideH => self.inside_h = Some(border), + BorderPosition::InsideV => self.inside_v = Some(border), }; self } @@ -114,8 +118,8 @@ impl TableCellBorders { BorderPosition::Left => self.left = None, BorderPosition::Bottom => self.bottom = None, BorderPosition::Right => self.right = None, - BorderPosition::IndideH => self.inside_h = None, - BorderPosition::IndideV => self.inside_v = None, + BorderPosition::InsideH => self.inside_h = None, + BorderPosition::InsideV => self.inside_v = None, }; self } diff --git a/docx-core/src/documents/elements/table_cell_margins.rs b/docx-core/src/documents/elements/table_cell_margins.rs index ee2475b..2f1d0f1 100644 --- a/docx-core/src/documents/elements/table_cell_margins.rs +++ b/docx-core/src/documents/elements/table_cell_margins.rs @@ -1,8 +1,11 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableCellMargins { top: usize, left: usize, diff --git a/docx-core/src/documents/elements/table_cell_property.rs b/docx-core/src/documents/elements/table_cell_property.rs index 2034c7b..f45b208 100644 --- a/docx-core/src/documents/elements/table_cell_property.rs +++ b/docx-core/src/documents/elements/table_cell_property.rs @@ -1,9 +1,12 @@ +use serde::Serialize; + use super::{GridSpan, TableCellBorders, TableCellWidth, VMerge}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct TableCellProperty { width: Option, borders: Option, @@ -90,4 +93,16 @@ mod tests { r#""# ); } + + #[test] + fn test_table_cell_prop_json() { + let c = TableCellProperty::new() + .vertical_merge(VMergeType::Continue) + .grid_span(3) + .width(200, WidthType::DXA); + assert_eq!( + serde_json::to_string(&c).unwrap(), + r#"{"width":{"width":200,"widthType":"DXA"},"borders":null,"gridSpan":3,"verticalMerge":"continue"}"# + ); + } } diff --git a/docx-core/src/documents/elements/table_cell_width.rs b/docx-core/src/documents/elements/table_cell_width.rs index 931f020..f22bef6 100644 --- a/docx-core/src/documents/elements/table_cell_width.rs +++ b/docx-core/src/documents/elements/table_cell_width.rs @@ -1,8 +1,11 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct TableCellWidth { width: usize, width_type: WidthType, diff --git a/docx-core/src/documents/elements/table_indent.rs b/docx-core/src/documents/elements/table_indent.rs index 03ec49e..0487f6c 100644 --- a/docx-core/src/documents/elements/table_indent.rs +++ b/docx-core/src/documents/elements/table_indent.rs @@ -1,8 +1,11 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableIndent { width: usize, width_type: WidthType, diff --git a/docx-core/src/documents/elements/table_property.rs b/docx-core/src/documents/elements/table_property.rs index efd649d..ec7c406 100644 --- a/docx-core/src/documents/elements/table_property.rs +++ b/docx-core/src/documents/elements/table_property.rs @@ -1,9 +1,12 @@ +use serde::Serialize; + use super::{Justification, TableBorders, TableCellMargins, TableIndent, TableWidth}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableProperty { width: TableWidth, justification: Justification, @@ -81,4 +84,13 @@ mod tests { "# ); } + + #[test] + fn test_table_property_json() { + let p = TableProperty::new().indent(100); + assert_eq!( + serde_json::to_string(&p).unwrap(), + r#"{"width":{"width":0,"widthType":"Auto"},"justification":"left","borders":{"top":{"position":"top","borderType":"single","size":2,"space":0,"color":"000000"},"left":{"position":"left","borderType":"single","size":2,"space":0,"color":"000000"},"bottom":{"position":"bottom","borderType":"single","size":2,"space":0,"color":"000000"},"right":{"position":"right","borderType":"single","size":2,"space":0,"color":"000000"},"insideH":{"position":"insideH","borderType":"single","size":2,"space":0,"color":"000000"},"insideV":{"position":"insideV","borderType":"single","size":2,"space":0,"color":"000000"}},"margins":{"top":55,"left":54,"bottom":55,"right":55},"indent":{"width":100,"widthType":"DXA"}}"# + ); + } } diff --git a/docx-core/src/documents/elements/table_row.rs b/docx-core/src/documents/elements/table_row.rs index 939098d..84812f2 100644 --- a/docx-core/src/documents/elements/table_row.rs +++ b/docx-core/src/documents/elements/table_row.rs @@ -1,12 +1,15 @@ +use serde::Serialize; + use super::{TableCell, TableRowProperty}; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableRow { pub cells: Vec, - pub(crate) has_numbering: bool, - property: TableRowProperty, + pub has_numbering: bool, + pub property: TableRowProperty, } impl TableRow { @@ -47,4 +50,13 @@ mod tests { r#""# ); } + + #[test] + fn test_row_json() { + let r = TableRow::new(vec![TableCell::new()]); + assert_eq!( + serde_json::to_string(&r).unwrap(), + r#"{"cells":[{"children":[],"property":{"width":null,"borders":null,"gridSpan":null,"verticalMerge":null},"hasNumbering":false}],"hasNumbering":false,"property":{}}"# + ); + } } diff --git a/docx-core/src/documents/elements/table_row_property.rs b/docx-core/src/documents/elements/table_row_property.rs index 51d618e..cd3a4cf 100644 --- a/docx-core/src/documents/elements/table_row_property.rs +++ b/docx-core/src/documents/elements/table_row_property.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] pub struct TableRowProperty {} impl TableRowProperty { diff --git a/docx-core/src/documents/elements/table_width.rs b/docx-core/src/documents/elements/table_width.rs index 76d3ed7..5b5ce55 100644 --- a/docx-core/src/documents/elements/table_width.rs +++ b/docx-core/src/documents/elements/table_width.rs @@ -1,8 +1,11 @@ +use serde::Serialize; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TableWidth { width: usize, width_type: WidthType, diff --git a/docx-core/src/documents/elements/text.rs b/docx-core/src/documents/elements/text.rs index 9b97103..fab1abc 100644 --- a/docx-core/src/documents/elements/text.rs +++ b/docx-core/src/documents/elements/text.rs @@ -1,17 +1,21 @@ +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde::Deserialize; + use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct Text { text: String, preserve_space: bool, } impl Text { - pub fn new(text: &str) -> Text { + pub fn new(text: impl Into) -> Text { Text { - text: escape(text), + text: escape(&text.into()), preserve_space: true, } } @@ -23,6 +27,18 @@ impl BuildXML for Text { } } +impl Serialize for Text { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut t = serializer.serialize_struct("Text", 2)?; + t.serialize_field("preserveSpace", &self.preserve_space)?; + t.serialize_field("text", &self.text)?; + t.end() + } +} + #[cfg(test)] mod tests { @@ -39,4 +55,13 @@ mod tests { r#"Hello"# ); } + + #[test] + fn test_json() { + let t = Text::new("Hello"); + assert_eq!( + serde_json::to_string(&t).unwrap(), + r#"{"preserveSpace":true,"text":"Hello"}"# + ); + } } diff --git a/docx-core/src/documents/elements/underline.rs b/docx-core/src/documents/elements/underline.rs index ba555f5..1a8a88f 100644 --- a/docx-core/src/documents/elements/underline.rs +++ b/docx-core/src/documents/elements/underline.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Underline { val: String, } @@ -18,6 +20,15 @@ impl BuildXML for Underline { } } +impl Serialize for Underline { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.val) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/vanish.rs b/docx-core/src/documents/elements/vanish.rs index c7ec549..5103425 100644 --- a/docx-core/src/documents/elements/vanish.rs +++ b/docx-core/src/documents/elements/vanish.rs @@ -1,7 +1,9 @@ +use serde::{Deserialize, Serialize, Serializer}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Vanish {} impl Vanish { @@ -23,6 +25,15 @@ impl BuildXML for Vanish { } } +impl Serialize for Vanish { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bool(true) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/vertical_merge.rs b/docx-core/src/documents/elements/vertical_merge.rs index 6b018ca..f7e08b8 100644 --- a/docx-core/src/documents/elements/vertical_merge.rs +++ b/docx-core/src/documents/elements/vertical_merge.rs @@ -1,8 +1,10 @@ +use serde::{Serialize, Serializer}; + use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct VMerge { val: VMergeType, } @@ -21,6 +23,15 @@ impl BuildXML for VMerge { } } +impl Serialize for VMerge { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}", &self.val)) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/elements/zoom.rs b/docx-core/src/documents/elements/zoom.rs index 820a28e..a032b87 100644 --- a/docx-core/src/documents/elements/zoom.rs +++ b/docx-core/src/documents/elements/zoom.rs @@ -1,7 +1,9 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug, Clone)] +use serde::{Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq)] pub struct Zoom { val: usize, } @@ -19,6 +21,15 @@ impl BuildXML for Zoom { } } +impl Serialize for Zoom { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.val as u64) + } +} + #[cfg(test)] mod tests { diff --git a/docx-core/src/documents/font_table.rs b/docx-core/src/documents/font_table.rs index dd5accc..e9d2a01 100644 --- a/docx-core/src/documents/font_table.rs +++ b/docx-core/src/documents/font_table.rs @@ -3,7 +3,10 @@ use crate::documents::BuildXML; use crate::types::FontPitchType; use crate::xml_builder::*; -#[derive(Debug)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct FontTable {} impl FontTable { diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index cc59643..eaaa0c9 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -29,12 +29,15 @@ pub use settings::*; pub use styles::*; pub use xml_docx::*; -#[derive(Debug)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Docx { - content_type: ContentTypes, - rels: Rels, - document_rels: DocumentRels, - doc_props: DocProps, + pub content_type: ContentTypes, + pub rels: Rels, + pub document_rels: DocumentRels, + pub doc_props: DocProps, pub styles: Styles, pub document: Document, pub comments: Comments, @@ -45,7 +48,7 @@ pub struct Docx { impl Default for Docx { fn default() -> Self { - let content_type = ContentTypes::new(); + let content_type = ContentTypes::new().set_default(); let rels = Rels::new(); let doc_props = DocProps::new(CorePropsConfig::new()); let styles = Styles::new(); @@ -75,6 +78,11 @@ impl Docx { Default::default() } + pub fn document(mut self, d: Document) -> Docx { + self.document = d; + self + } + pub fn add_paragraph(mut self, p: Paragraph) -> Docx { if p.has_numbering { // If this document has numbering, set numberings.xml to document_rels. @@ -126,6 +134,11 @@ impl Docx { } } + pub fn json(&mut self) -> String { + self.update_comments(); + serde_json::to_string(&self).unwrap() + } + // Traverse and clone comments from document and add to comments node. fn update_comments(&mut self) { let mut comments: Vec = vec![]; @@ -141,7 +154,7 @@ impl Docx { DocumentChild::Table(table) => { for row in &table.rows { for cell in &row.cells { - for content in &cell.contents { + for content in &cell.children { match content { TableCellContent::Paragraph(paragraph) => { for child in ¶graph.children { diff --git a/docx-core/src/documents/numberings.rs b/docx-core/src/documents/numberings.rs index bcd8c6c..eb19326 100644 --- a/docx-core/src/documents/numberings.rs +++ b/docx-core/src/documents/numberings.rs @@ -3,7 +3,10 @@ use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Numberings { numberings: Vec, } @@ -46,7 +49,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("%1."), LevelJc::new("left"), ) - .indent(420, Some(SpecialIndentType::Hanging(420))), + .indent(420, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -56,7 +59,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("(%2)"), LevelJc::new("left"), ) - .indent(840, Some(SpecialIndentType::Hanging(420))), + .indent(840, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -66,7 +69,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("%3"), LevelJc::new("left"), ) - .indent(1260, Some(SpecialIndentType::Hanging(420))), + .indent(1260, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -76,7 +79,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("%4."), LevelJc::new("left"), ) - .indent(1680, Some(SpecialIndentType::Hanging(420))), + .indent(1680, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -86,7 +89,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("(%5)"), LevelJc::new("left"), ) - .indent(2100, Some(SpecialIndentType::Hanging(420))), + .indent(2100, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -96,7 +99,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("%6"), LevelJc::new("left"), ) - .indent(2520, Some(SpecialIndentType::Hanging(420))), + .indent(2520, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -106,7 +109,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("%7."), LevelJc::new("left"), ) - .indent(2940, Some(SpecialIndentType::Hanging(420))), + .indent(2940, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -116,7 +119,7 @@ fn create_default_numbering() -> Numbering { LevelText::new("(%8)"), LevelJc::new("left"), ) - .indent(3360, Some(SpecialIndentType::Hanging(420))), + .indent(3360, Some(SpecialIndentType::Hanging(420)), None), ) .add_level( Level::new( @@ -126,6 +129,6 @@ fn create_default_numbering() -> Numbering { LevelText::new("%9"), LevelJc::new("left"), ) - .indent(3780, Some(SpecialIndentType::Hanging(420))), + .indent(3780, Some(SpecialIndentType::Hanging(420)), None), ) } diff --git a/docx-core/src/documents/rels.rs b/docx-core/src/documents/rels.rs index 334e4b1..0c39cf4 100644 --- a/docx-core/src/documents/rels.rs +++ b/docx-core/src/documents/rels.rs @@ -1,43 +1,69 @@ +use serde::{Deserialize, Serialize}; + use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] -pub struct Rels {} +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct Rels { + pub rels: Vec<(String, String, String)>, +} impl Rels { pub fn new() -> Rels { Default::default() } + + pub fn set_default(mut self) -> Self { + self.rels.push(( + "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" + .to_owned(), + "rId1".to_owned(), + "docProps/core.xml".to_owned(), + )); + self.rels.push( + ("http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties".to_owned(), + "rId2".to_owned(), "docProps/app.xml".to_owned()), + ); + self.rels.push(( + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" + .to_owned(), + "rId3".to_owned(), + "word/document.xml".to_owned(), + )); + self + } + + pub fn add_rel( + mut self, + id: impl Into, + rel_type: impl Into, + target: impl Into, + ) -> Self { + self.rels.push((rel_type.into(), id.into(), target.into())); + self + } + + pub fn find_target(&self, rel_type: &str) -> Option<&(String, String, String)> { + self.rels.iter().find(|rel| rel.0 == rel_type) + } } impl Default for Rels { fn default() -> Self { - Rels {} + Rels { rels: Vec::new() } } } impl BuildXML for Rels { fn build(&self) -> Vec { let b = XMLBuilder::new(); - b.declaration(None) - .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships") - .relationship( - "rId1", - "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", - "docProps/core.xml" - ) - .relationship( - "rId2", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", - "docProps/app.xml" - ) - .relationship( - "rId3", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", - "word/document.xml" - ) - .close() - .build() + let mut b = b + .declaration(None) + .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships"); + for (k, id, v) in self.rels.iter() { + b = b.relationship(id, k, v); + } + b.close().build() } } @@ -51,7 +77,7 @@ mod tests { #[test] fn test_build() { - let c = Rels::new(); + let c = Rels::new().set_default(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), diff --git a/docx-core/src/documents/settings.rs b/docx-core/src/documents/settings.rs index 778b245..cc7f5c2 100644 --- a/docx-core/src/documents/settings.rs +++ b/docx-core/src/documents/settings.rs @@ -2,7 +2,10 @@ use super::{DefaultTabStop, Zoom}; use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Settings { default_tab_stop: DefaultTabStop, zoom: Zoom, diff --git a/docx-core/src/documents/styles.rs b/docx-core/src/documents/styles.rs index 66edbdc..84e7fcb 100644 --- a/docx-core/src/documents/styles.rs +++ b/docx-core/src/documents/styles.rs @@ -1,9 +1,12 @@ +use serde::Serialize; + use super::{DocDefaults, Style}; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Styles { doc_defaults: DocDefaults, styles: Vec