docx-rs-0.4.18/.cargo_vcs_info.json0000644000000001470000000000100125070ustar { "git": { "sha1": "bf268f6b428aabdaa68962603e04a5207b6ed8fe" }, "path_in_vcs": "docx-core" }docx-rs-0.4.18/Cargo.lock0000644000000377660000000000100105030ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "Inflector" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "adler32" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytemuck" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "console" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" dependencies = [ "encode_unicode", "libc", "once_cell", "terminal_size", "winapi", ] [[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "crossbeam-utils" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", ] [[package]] name = "ctor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" dependencies = [ "quote", "syn 1.0.76", ] [[package]] name = "deflate" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" dependencies = [ "adler32", ] [[package]] name = "diff" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "docx-rs" version = "0.4.18" dependencies = [ "base64", "image", "insta", "pretty_assertions", "serde", "serde_json", "thiserror", "ts-rs", "wasm-bindgen", "xml-rs", "zip", ] [[package]] name = "dtoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "flate2" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "gif" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4" dependencies = [ "color_quant", "weezl", ] [[package]] name = "image" version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd8e4fb07cf672b1642304e731ef8a6a4c7891d67bb4fd4f5ce58cd6ed86803c" dependencies = [ "bytemuck", "byteorder", "color_quant", "gif", "jpeg-decoder", "num-rational", "num-traits", "png", "tiff", ] [[package]] name = "insta" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36fb7ec420af04ce7d1a422945cd19c52bf01772ead45934cee77f056dca1081" dependencies = [ "console", "once_cell", "serde", "serde_json", "serde_yaml", "similar", ] [[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "jpeg-decoder" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] name = "linked-hash-map" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ "cfg-if 0.1.10", ] [[package]] name = "miniz_oxide" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", ] [[package]] name = "num-integer" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-rational" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "output_vt100" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" dependencies = [ "winapi", ] [[package]] name = "png" version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", "deflate", "miniz_oxide", ] [[package]] name = "pretty_assertions" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" dependencies = [ "ctor", "diff", "output_vt100", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" [[package]] name = "serde" version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" dependencies = [ "proc-macro2", "quote", "syn 1.0.76", ] [[package]] name = "serde_json" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43535db9747a4ba938c0ce0a98cc631a46ebf943c9e1d604e091df6007620bf6" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_yaml" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" dependencies = [ "dtoa", "linked-hash-map", "serde", "yaml-rust", ] [[package]] name = "similar" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" [[package]] name = "syn" version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "syn" version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" dependencies = [ "libc", "winapi", ] [[package]] name = "thiserror" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fb62ff737e573b1e677459bea6fd023cd5d6e868c3242d3cdf3ef2f0554824" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24069c0ba08aab54289d6a25f5036d94afc61e1538bbc42ae5501df141c9027d" dependencies = [ "proc-macro2", "quote", "syn 1.0.76", ] [[package]] name = "tiff" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286" dependencies = [ "flate2", "jpeg-decoder", "weezl", ] [[package]] name = "ts-rs" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df082879c2ca6521d54bedd7d6d2e9407b7d457dede64415f1739d3a44a19b05" dependencies = [ "thiserror", "ts-rs-macros", ] [[package]] name = "ts-rs-macros" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f402688b565efbe1a0ffb296e201c12f1986fd02eba37f4dc62c13098a6218c1" dependencies = [ "Inflector", "proc-macro2", "quote", "syn 1.0.76", "termcolor", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.103", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn 2.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "weezl" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0e26e7a4d998e3d7949c69444b8b4916bac810da0d3a82ae612c89e952782f4" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" [[package]] name = "yaml-rust" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" dependencies = [ "linked-hash-map", ] [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zip" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", "crossbeam-utils", "flate2", ] docx-rs-0.4.18/Cargo.toml0000644000000106630000000000100105110ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "docx-rs" version = "0.4.18" authors = ["bokuweb "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "A .docx file writer with Rust/WebAssembly." readme = "README.md" keywords = [ "office", "word", "docx", ] license = "MIT" repository = "https://github.com/bokuweb/docx-rs" [lib] name = "docx_rs" path = "src/lib.rs" [[example]] name = "alignment" path = "examples/alignment.rs" [[example]] name = "bookmark" path = "examples/bookmark.rs" [[example]] name = "comment" path = "examples/comment.rs" [[example]] name = "custom_property" path = "examples/custom_property.rs" [[example]] name = "custom_xml" path = "examples/custom_xml.rs" [[example]] name = "data_binding" path = "examples/data_binding.rs" [[example]] name = "dirty_toc" path = "examples/dirty_toc.rs" [[example]] name = "doc_id" path = "examples/doc_id.rs" [[example]] name = "even_header" path = "examples/even_header.rs" [[example]] name = "first_header" path = "examples/first_header.rs" [[example]] name = "font" path = "examples/font.rs" [[example]] name = "font_size" path = "examples/font_size.rs" [[example]] name = "footer" path = "examples/footer.rs" [[example]] name = "footnotes" path = "examples/footnotes.rs" [[example]] name = "header" path = "examples/header.rs" [[example]] name = "header_with_page_num" path = "examples/header_with_page_num.rs" [[example]] name = "hello" path = "examples/hello.rs" [[example]] name = "history" path = "examples/history.rs" [[example]] name = "hyperlink" path = "examples/hyperlink.rs" [[example]] name = "image_floating" path = "examples/image_floating.rs" [[example]] name = "image_in_header" path = "examples/image_in_header.rs" [[example]] name = "image_inline" path = "examples/image_inline.rs" [[example]] name = "image_inline_rotate" path = "examples/image_inline_rotate.rs" [[example]] name = "image_reader" path = "examples/image_reader.rs" [[example]] name = "indent" path = "examples/indent.rs" [[example]] name = "nested_comment" path = "examples/nested_comment.rs" [[example]] name = "numbering" path = "examples/numbering.rs" [[example]] name = "outline_lvl" path = "examples/outline_lvl.rs" [[example]] name = "page_margin" path = "examples/page_margin.rs" [[example]] name = "page_size" path = "examples/page_size.rs" [[example]] name = "reader" path = "examples/reader.rs" [[example]] name = "sdt" path = "examples/sdt.rs" [[example]] name = "style" path = "examples/style.rs" [[example]] name = "table" path = "examples/table.rs" [[example]] name = "table_border" path = "examples/table_border.rs" [[example]] name = "toc_simple" path = "examples/toc_simple.rs" [[example]] name = "toc_with_comment" path = "examples/toc_with_comment.rs" [[example]] name = "toc_with_hyperlink" path = "examples/toc_with_hyperlink.rs" [[example]] name = "toc_with_item" path = "examples/toc_with_item.rs" [[example]] name = "toc_with_style_level" path = "examples/toc_with_style_level.rs" [[example]] name = "toc_with_tc" path = "examples/toc_with_tc.rs" [[example]] name = "web_ext" path = "examples/web_ext.rs" [[test]] name = "lib" path = "tests/lib.rs" [[test]] name = "reader" path = "tests/reader.rs" [dependencies.base64] version = "0.22.1" [dependencies.image] version = "0.24.4" features = [ "gif", "jpeg", "png", "bmp", "tiff", ] optional = true default-features = false [dependencies.serde] version = "1.0" features = ["derive"] [dependencies.serde_json] version = "1.0" [dependencies.thiserror] version = "1.0" [dependencies.ts-rs] version = "6.1" optional = true [dependencies.wasm-bindgen] version = "0.2.92" optional = true [dependencies.xml-rs] version = "0.8.4" [dependencies.zip] version = "0.6.3" features = ["deflate"] default-features = false [dev-dependencies.insta] version = "1.16" [dev-dependencies.pretty_assertions] version = "1.3.0" [features] default = ["image"] wasm = [ "wasm-bindgen", "ts-rs", "image", ] docx-rs-0.4.18/Cargo.toml.orig000064400000000000000000000020121046102023000141570ustar 00000000000000[package] name = "docx-rs" version = "0.4.18" authors = ["bokuweb "] repository = "https://github.com/bokuweb/docx-rs" edition = "2018" license = "MIT" readme = "../README.md" description = "A .docx file writer with Rust/WebAssembly." keywords = [ "office", "word", "docx", ] [lib] name = "docx_rs" path = "src/lib.rs" [features] default = ["image"] wasm = ["wasm-bindgen", "ts-rs", "image"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] xml-rs = "0.8.4" thiserror = "1.0" zip = { version = "0.6.3", default-features = false, features = ["deflate"] } serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0" } base64 = "0.22.1" image = { version = "0.24.4", default-features = false, features=["gif", "jpeg", "png", "bmp", "tiff"], optional = true } wasm-bindgen = { version = "0.2.92", optional = true } ts-rs = { version = "6.1", optional = true } [dev-dependencies] pretty_assertions = "1.3.0" insta = "1.16" docx-rs-0.4.18/README.md000064400000000000000000000075741046102023000125710ustar 00000000000000

A .docx file `writer` with Rust/WebAssembly.

--- [![GitHub Actions Status](https://github.com/bokuweb/docx-rs/workflows/Continuous%20Integration/badge.svg)](https://github.com/bokuweb/docx-rs/actions) [![docx-rs at crates.io](https://img.shields.io/crates/v/docx-rs.svg)](https://crates.io/crates/docx-rs) [![](https://img.shields.io/npm/v/docx-wasm.svg)](https://www.npmjs.com/package/docx-wasm) ## Installation ### Rust ``` [dependencies] docx-rs = "0.4" ``` ### Browser/Node.js ``` $ pnpm add docx-wasm ``` ## Example ### Rust ```rust use docx_rs::*; pub fn hello() -> Result<(), DocxError> { let path = std::path::Path::new("./hello.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build() .pack(file)?; Ok(()) } ``` ### Browser ```javascript import { saveAs } from "file-saver"; // // Note that a dynamic `import` statement here is required due to webpack/webpack#6615, import("docx-wasm").then((w) => { const { buffer } = new w.Docx() .addParagraph( new w.Paragraph().addRun(new w.Run().addText("Hello world!!")) ) .build(); saveAs(new Blob([buffer]), "hello.docx"); }); ``` ### Node.js ```javascript const w = require("docx-wasm"); const { writeFileSync } = require("fs"); const { buffer } = new w.Docx() .addParagraph(new w.Paragraph().addRun(new w.Run().addText("Hello world!!"))) .build(); writeFileSync("hello.docx", Buffer.from(buffer)); ``` ### More examples - [Minimum](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/hello.rs) - [Indent](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/indent.rs) - [Alignment](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/alignment.rs) - [Font](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/font.rs) - [Numbering](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/numbering.rs) - [Table](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/table.rs) - [Comment](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/comment.rs) - [Image](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/image_inline.rs) - [History](https://github.com/bokuweb/docx-rs/blob/master/docx-core/examples/history.rs) ## Development ### Requirements - Node.js 16+ - pnpm 9+ - wasm-pack0.10.1 (https://rustwasm.github.io/wasm-pack/) - insta (https://github.com/mitsuhiko/insta) ### Examples You can run example with following code. Please see `examples` directory. ```sh $ cargo run --example [EXAMPLE_NAME] ``` For Example if you want to run `hello` example. Please run following command. ```sh $ cargo run --example hello ``` So you can see output file in output directory. ### Testing #### Rust Please run following command. ``` make lint && make test ``` If snapshot testing is failed, fix code or update snapshot files. (See https://insta.rs/). ``` $ cargo-insta review ``` Then re run test. ``` $ make test ``` #### Wasm Please run following command. ``` $ cd docx-wasm && pnpm install && pnpm test ``` If snapshot testing is failed, fix code or update snapshot files. (See https://jestjs.io/docs/snapshot-testing). ``` $ pnpm test -- --updateSnapshot ``` ## Features - [x] Paragraph - [x] Alignment - [x] Indent - [x] Numbering - [x] Run - [x] Bold - [x] Size - [x] Font - [x] Color - [x] Highlight - [x] Underline - [x] vanish - [x] Italic - [x] TextBorder - [x] Footnote - [x] Break - [x] Header - [x] Footer - [x] Comment - [x] Image - [x] Style - [x] Table - [x] HIstory - [x] Table of contents - [ ] Section - [ ] Textbox docx-rs-0.4.18/bindings/FontGroup.ts000064400000000000000000000002341046102023000153640ustar 00000000000000import type { FontSchemeFont } from "./FontSchemeFont"; export interface FontGroup { latin: string, ea: string, cs: string, fonts: Array, }docx-rs-0.4.18/bindings/FontScheme.ts000064400000000000000000000001721046102023000154750ustar 00000000000000import type { FontGroup } from "./FontGroup"; export interface FontScheme { majorFont: FontGroup, minorFont: FontGroup, }docx-rs-0.4.18/bindings/FontSchemeFont.ts000064400000000000000000000001061046102023000163210ustar 00000000000000 export interface FontSchemeFont { script: string, typeface: string, }docx-rs-0.4.18/bindings/Theme.ts000064400000000000000000000001431046102023000145020ustar 00000000000000import type { FontScheme } from "./FontScheme"; export interface Theme { fontSchema: FontScheme, }docx-rs-0.4.18/examples/alignment.rs000064400000000000000000000007401046102023000154400ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./alignment.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text(" World")) .align(AlignmentType::Right), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/bookmark.rs000064400000000000000000000006061046102023000152700ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/bookmark.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_bookmark_start(1, "hello") .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .add_bookmark_end(1) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/comment.rs000064400000000000000000000011601046102023000151210ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/comment.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_comment_start( Comment::new(1) .author("bokuweb") .date("2019-01-01T00:00:00Z") .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))), ) .add_comment_end(1), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/custom_property.rs000064400000000000000000000005631046102023000167430ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/custom_property.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .custom_property("hello", "world") .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/custom_xml.rs000064400000000000000000000010221046102023000156460ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/custom_xml.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .add_custom_item("06AC5857-5C65-A94A-BCEC-37356A209BC3", "") .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/data_binding.rs000064400000000000000000000014251046102023000160660ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/data_binding.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_structured_data_tag( StructuredDataTag::new().data_binding(DataBinding::new().xpath("/root/item1")), ) .add_structured_data_tag( StructuredDataTag::new().data_binding(DataBinding::new().xpath("/root/item2")), ), ) .add_custom_item( "06AC5857-5C65-A94A-BCEC-37356A209BC3", "Hello World!", ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/dirty_toc.rs000064400000000000000000000021661046102023000154660ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/dirty_toc.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); let style2 = Style::new("Heading2", StyleType::Paragraph).name("Heading 2"); let p4 = Paragraph::new() .add_run(Run::new().add_text("Foo")) .style("Heading4") .page_break_before(true); let style4 = Style::new("Heading4", StyleType::Paragraph).name("Heading 4"); Docx::new() .add_style(style1) .add_style(style2) .add_style(style4) .add_table_of_contents(TableOfContents::new().heading_styles_range(1, 3)) .add_paragraph(p1) .add_paragraph(p2) .add_paragraph(p4) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/doc_id.rs000064400000000000000000000005671046102023000147120ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/doc_id.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .doc_id("2F0CF1F9-607F-5941-BF59-8A81BE87AAAA") .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/even_header.rs000064400000000000000000000011361046102023000157270ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/even_header.docx"); let file = std::fs::File::create(path).unwrap(); let header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))); let even_header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Even"))); Docx::new() .header(header) .even_header(even_header) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/first_header.rs000064400000000000000000000011431046102023000161170ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/first_header.docx"); let file = std::fs::File::create(path).unwrap(); let header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))); let first_header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("First"))); Docx::new() .header(header) .first_header(first_header) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/font.rs000064400000000000000000000006741046102023000144360ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./font.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new().add_run( Run::new() .add_text("Hello") .fonts(RunFonts::new().ascii("Arial")), ), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/font_size.rs000064400000000000000000000005131046102023000154600ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/font_size.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello").size(24))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/footer.rs000064400000000000000000000007051046102023000147610ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/footer.docx"); let file = std::fs::File::create(path).unwrap(); let footer = Footer::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))); Docx::new() .footer(footer) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/footnotes.rs000064400000000000000000000011471046102023000155040ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./footnotes.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Hello")) .add_run( Run::new().add_footnote_reference( Footnote::new() .add_content(Paragraph::new().add_run(Run::new().add_text("World"))), ), ), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/header.rs000064400000000000000000000007051046102023000147130ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/header.docx"); let file = std::fs::File::create(path).unwrap(); let header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))); Docx::new() .header(header) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/header_with_page_num.rs000064400000000000000000000012201046102023000176120ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/header_with_page_num.docx"); let file = std::fs::File::create(path).unwrap(); let header = Header::new().add_paragraph( Paragraph::new() .wrap("none") .v_anchor("text") .h_anchor("margin") .x_align("right") .add_run(Run::new().add_text("Hello")) .add_page_num(PageNum::new()), ); Docx::new() .header(header) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/hello.rs000064400000000000000000000005071046102023000145660ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/hello.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/history.rs000064400000000000000000000011211046102023000151550ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./history.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_insert( Insert::new(Run::new().add_text("Hello")) .author("bokuweb") .date("2019-01-01T00:00:00Z"), ) .add_delete(Delete::new().add_run(Run::new().add_delete_text("World"))), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/hyperlink.rs000064400000000000000000000014141046102023000154660ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/hyperlink.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_hyperlink( Hyperlink::new("anchor", HyperlinkType::Anchor).add_run(Run::new().add_text("Hello")), )) .add_bookmark_start(1, "anchor") .add_paragraph( Paragraph::new() .add_hyperlink( Hyperlink::new("https://google.com", HyperlinkType::External) .add_run(Run::new().add_text(" World")), ) .page_break_before(true), ) .add_bookmark_end(1) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/image_floating.rs000064400000000000000000000012131046102023000164230ustar 00000000000000use std::fs::*; use std::io::Read; use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/image_floating.docx"); let file = File::create(path).unwrap(); let mut img = File::open("./images/cat_min.jpg").unwrap(); let mut buf = Vec::new(); let _ = img.read_to_end(&mut buf).unwrap(); let pic = Pic::new(&buf) .size(320 * 9525, 240 * 9525) .floating() .offset_x(300 * 9525) .offset_y(400 * 9525); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_image(pic))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/image_in_header.rs000064400000000000000000000011001046102023000165310ustar 00000000000000use docx_rs::{Docx, Header, Paragraph, Pic, Run}; use std::{error::Error, io::Cursor}; fn main() -> Result<(), Box> { let cat = Pic::new(include_bytes!("../../images/cat_min.jpg")); let header = Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_image(cat.clone()))); let mut out = Vec::new(); let docx = Docx::new() .header(header) .add_paragraph(Paragraph::new().add_run(Run::new().add_image(cat))); docx.build().pack(Cursor::new(&mut out))?; std::fs::write("/tmp/out.docx", &out)?; Ok(()) } docx-rs-0.4.18/examples/image_inline.rs000064400000000000000000000011001046102023000160710ustar 00000000000000use std::fs::*; use std::io::Read; use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/image_inline.docx"); let file = File::create(path).unwrap(); let mut img = File::open("./images/cat_min.jpg").unwrap(); let mut buf = Vec::new(); let _ = img.read_to_end(&mut buf).unwrap(); let pic = Pic::new(&buf).size(320 * 9525, 240 * 9525); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("🐱").add_image(pic))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/image_inline_rotate.rs000064400000000000000000000011511046102023000174550ustar 00000000000000use std::fs::*; use std::io::Read; use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/image_inline_rotate.docx"); let file = File::create(path).unwrap(); let mut img = File::open("./images/cat_min.jpg").unwrap(); let mut buf = Vec::new(); let _ = img.read_to_end(&mut buf).unwrap(); // rotate 180deg. let pic = Pic::new(&buf).size(320 * 9525, 240 * 9525).rotate(180); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("🐱").add_image(pic))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/image_reader.rs000064400000000000000000000006001046102023000160610ustar 00000000000000use docx_rs::*; use std::fs::File; use std::io::{Read, Write}; pub fn main() { let mut file = File::open("./image.docx").unwrap(); let mut buf = vec![]; file.read_to_end(&mut buf).unwrap(); let mut file = File::create("./image.json").unwrap(); let res = read_docx(&buf).unwrap().json(); file.write_all(res.as_bytes()).unwrap(); file.flush().unwrap(); } docx-rs-0.4.18/examples/indent.rs000064400000000000000000000020431046102023000147410ustar 00000000000000use docx_rs::*; pub const DUMMY: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/indent.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text(DUMMY)).indent( Some(840), None, None, None, )) .add_paragraph(Paragraph::new()) .add_paragraph(Paragraph::new().add_run(Run::new().add_text(DUMMY)).indent( Some(840), Some(SpecialIndentType::FirstLine(720)), None, None, )) .add_paragraph(Paragraph::new()) .add_paragraph(Paragraph::new().add_run(Run::new().add_text(DUMMY)).indent( Some(1560), Some(SpecialIndentType::Hanging(720)), None, None, )) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/nested_comment.rs000064400000000000000000000030171046102023000164660ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/nested_comment.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_comment_start( Comment::new(1) .author("bokuweb") .date("2019-01-01T00:00:00Z") .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))), ) .add_comment_end(1) .add_comment_start( Comment::new(2) .author("bokuweb") .date("2019-01-02T00:00:00Z") .parent_comment_id(1) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("World"))), ) .add_comment_end(2) .add_comment_start( Comment::new(3) .author("bokuweb") .date("2019-01-02T00:00:00Z") .parent_comment_id(1) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("!!!!!"))) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("!!!!!"))), ) .add_comment_end(3), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/numbering.rs000064400000000000000000000017621046102023000154550ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./numbering.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Hello")) .numbering(NumberingId::new(2), IndentLevel::new(0)), ) .add_abstract_numbering( AbstractNumbering::new(2).add_level( Level::new( 0, Start::new(1), NumberFormat::new("decimal"), LevelText::new("Section %1."), LevelJc::new("left"), ) .indent( Some(1620), Some(SpecialIndentType::Hanging(320)), None, None, ), ), ) .add_numbering(Numbering::new(2, 2)) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/outline_lvl.rs000064400000000000000000000017471046102023000160260ustar 00000000000000use docx_rs::*; fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./outlineLvl.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Title1")) .outline_lvl(1), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Title2")) .outline_lvl(1), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Title2-1")) .outline_lvl(2), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Title2-2")) .outline_lvl(2), ) .add_paragraph( Paragraph::new() .add_run(Run::new().add_text("Title3")) .outline_lvl(1), ) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/page_margin.rs000064400000000000000000000005041046102023000157310ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/page_margin.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/page_size.rs000064400000000000000000000005371046102023000154340ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/page_size.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .page_size(200, 400) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/reader.rs000064400000000000000000000006001046102023000147170ustar 00000000000000use docx_rs::*; use std::fs::File; use std::io::{Read, Write}; pub fn main() { let mut file = File::open("./hello.docx").unwrap(); let mut buf = vec![]; file.read_to_end(&mut buf).unwrap(); let mut file = File::create("./hello.json").unwrap(); let res = read_docx(&buf).unwrap().json(); file.write_all(res.as_bytes()).unwrap(); file.flush().unwrap(); } docx-rs-0.4.18/examples/sdt.rs000064400000000000000000000007141046102023000142550ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/sdt.docx"); let file = std::fs::File::create(path).unwrap(); let p = Paragraph::new().add_run( Run::new() .add_text("Hello") .fonts(RunFonts::new().ascii("Arial")), ); Docx::new() .add_structured_data_tag(StructuredDataTag::new().add_paragraph(p)) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/style.rs000064400000000000000000000021761046102023000146270ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/style.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello").style("Run1")) .add_run(Run::new().add_text(" World")) .style("Heading1") .page_break_before(true); let table = Table::new(vec![TableRow::new(vec![TableCell::new().add_paragraph( Paragraph::new().add_run(Run::new().add_text("Hello")), )])]) .style("Table1"); let style1 = Style::new("Heading1", StyleType::Paragraph) .name("Heading 1") .align(AlignmentType::Center); let style2 = Style::new("Run1", StyleType::Character) .name("Run test") .bold(); let style3 = Style::new("Table1", StyleType::Table) .name("Table test") .table_align(TableAlignmentType::Center); Docx::new() .add_style(style1) .add_style(style2) .add_style(style3) .add_paragraph(p1) .add_table(table) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/table.rs000064400000000000000000000005371046102023000145550ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./table.docx"); let file = std::fs::File::create(path).unwrap(); let table = Table::new(vec![TableRow::new(vec![ TableCell::new().add_paragraph(Paragraph::new()) ])]); Docx::new().add_table(table).build().pack(file)?; Ok(()) } docx-rs-0.4.18/examples/table_border.rs000064400000000000000000000032001046102023000161000ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./table_border.docx"); let file = std::fs::File::create(path).unwrap(); let table = Table::new(vec![ TableRow::new(vec![ TableCell::new() .add_paragraph(Paragraph::new()) .grid_span(2) .clear_border(TableCellBorderPosition::Left) .clear_border(TableCellBorderPosition::Bottom) .clear_border(TableCellBorderPosition::Right), TableCell::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .vertical_align(VAlignType::Center) .vertical_merge(VMergeType::Restart), ]), TableRow::new(vec![ TableCell::new() .add_paragraph(Paragraph::new()) .vertical_merge(VMergeType::Restart) .clear_all_border(), TableCell::new().add_paragraph(Paragraph::new()), TableCell::new() .add_paragraph(Paragraph::new()) .vertical_merge(VMergeType::Continue), ]), TableRow::new(vec![ TableCell::new() .add_paragraph(Paragraph::new()) .vertical_merge(VMergeType::Continue), TableCell::new().add_paragraph(Paragraph::new()), TableCell::new() .add_paragraph(Paragraph::new()) .vertical_merge(VMergeType::Continue), ]), ]) .set_grid(vec![2000, 2000, 2000]) .indent(1000); Docx::new().add_table(table).build().pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_simple.rs000064400000000000000000000015531046102023000156230ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/toc_simple.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("!!Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); Docx::new() .add_style(style1) .add_table_of_contents( TableOfContents::new() .heading_styles_range(1, 3) .alias("Table of contents") .auto(), ) .add_paragraph(p1) .add_paragraph(p2) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_with_comment.rs000064400000000000000000000023531046102023000170260ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/toc_with_comment.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("!!Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); let p3 = Paragraph::new() .add_comment_start( Comment::new(1) .author("bokuweb") .date("2019-01-01T00:00:00Z") .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Comment"))), ) .add_run(Run::new().add_text("CommentTarget")) .add_comment_end(1); Docx::new() .add_style(style1) .add_table_of_contents( TableOfContents::new() .heading_styles_range(1, 3) .alias("Table of contents") .add_before_paragraph(p3), ) .add_paragraph(p1) .add_paragraph(p2) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_with_hyperlink.rs000064400000000000000000000023601046102023000173670ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/toc_with_hyperlink.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); let style2 = Style::new("Heading2", StyleType::Paragraph).name("Heading 2"); let p4 = Paragraph::new() .add_run(Run::new().add_text("Foo")) .style("Heading4") .page_break_before(true); let style4 = Style::new("Heading4", StyleType::Paragraph).name("Heading 4"); Docx::new() .add_style(style1) .add_style(style2) .add_style(style4) .add_table_of_contents( TableOfContents::new() .heading_styles_range(1, 3) .hyperlink() .alias("table of contents"), ) .add_paragraph(p1) .add_paragraph(p2) .add_paragraph(p4) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_with_item.rs000064400000000000000000000026741046102023000163300ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/toc_with_item.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); let style2 = Style::new("Heading2", StyleType::Paragraph).name("Heading 2"); Docx::new() .add_style(style1) .add_style(style2) .add_table_of_contents( TableOfContents::new() .alias("Table of contents") .heading_styles_range(1, 3) .add_item( TableOfContentsItem::new() .text("Hello") .toc_key("_Toc00000000") .level(1) .page_ref("2"), ) .add_item( TableOfContentsItem::new() .text("World") .toc_key("_Toc00000001") .level(2) .page_ref("3"), ), ) .add_paragraph(p1) .add_paragraph(p2) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_with_style_level.rs000064400000000000000000000031361046102023000177130ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/examples/toc_with_style_level.docx"); let file = std::fs::File::create(path).unwrap(); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let style2 = Style::new("StyleLevel1", StyleType::Paragraph) .name("Style Level1") .based_on("Heading1"); let style3 = Style::new("StyleLevel4", StyleType::Paragraph) .name("Style Level4") .based_on("Heading4"); let style4 = Style::new("Heading4", StyleType::Paragraph).name("Heading 4"); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello")) .style("Heading1") .page_break_before(true); let p2 = Paragraph::new() .add_run(Run::new().add_text("Foo")) .style("StyleLevel1") .page_break_before(true); let p3 = Paragraph::new() .add_run(Run::new().add_text("Bar")) .style("StyleLevel4") .page_break_before(true); Docx::new() .add_style(style1) .add_style(style2) .add_style(style3) .add_style(style4) .add_table_of_contents( TableOfContents::new() .heading_styles_range(1, 3) .add_style_with_level(StyleWithLevel::new("StyleLevel1", 1)) .add_style_with_level(StyleWithLevel::new("StyleLevel4", 4)) .alias("Table of contents") .auto(), ) .add_paragraph(p1) .add_paragraph(p2) .add_paragraph(p3) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/toc_with_tc.rs000064400000000000000000000023151046102023000157700ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/toc_with_tc.docx"); let file = std::fs::File::create(path).unwrap(); let p1 = Paragraph::new() .add_run(Run::new().add_text("Hello")) .style("Heading1") .page_break_before(true); let style1 = Style::new("Heading1", StyleType::Paragraph).name("Heading 1"); let p2 = Paragraph::new() .add_run(Run::new().add_text("World")) .style("Heading2") .page_break_before(true); let tc = Paragraph::new() .add_run( Run::new() .add_field_char(FieldCharType::Begin, false) .add_instr_text(InstrText::TC(InstrTC::new("tc_test").level(4))) .add_field_char(FieldCharType::Separate, false) .add_field_char(FieldCharType::End, false), ) .page_break_before(true); Docx::new() .add_style(style1) .add_table_of_contents( TableOfContents::new().heading_styles_range(1, 3), // .tc_field_level_range(3, 4), ) .add_paragraph(p1) .add_paragraph(p2) .add_paragraph(tc) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/examples/web_ext.rs000064400000000000000000000011641046102023000151200ustar 00000000000000use docx_rs::*; pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./output/web_ext.docx"); let file = std::fs::File::create(path).unwrap(); Docx::new() .taskpanes() .web_extension( WebExtension::new( "7f33b723-fb58-4524-8733-dbedc4b7c095", "abc", "1.0.0.0", "developer", "Registry", ) .property("hello", "\"world\""), ) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build() .pack(file)?; Ok(()) } docx-rs-0.4.18/src/documents/bookmark_id.rs000064400000000000000000000011771046102023000167220ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static BOOKMARK_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_bookmark_id() -> usize { use std::sync::atomic::Ordering; let id = BOOKMARK_ID.load(Ordering::Relaxed); BOOKMARK_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(not(test))] pub fn reset_bookmark_id() { use std::sync::atomic::Ordering; BOOKMARK_ID.load(Ordering::Relaxed); BOOKMARK_ID.store(1, Ordering::Relaxed); } #[cfg(test)] pub fn generate_bookmark_id() -> usize { 1 } #[cfg(test)] pub fn reset_bookmark_id() { // NOP } docx-rs-0.4.18/src/documents/build_xml.rs000064400000000000000000000020501046102023000164070ustar 00000000000000use crate::xml_builder::XMLBuilder; use std::io::Write; pub trait BuildXML { /// Write XML to the output stream. #[doc(hidden)] fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result>; #[doc(hidden)] fn build(&self) -> Vec { self.build_to(XMLBuilder::new(Vec::new()).into_inner().unwrap()) .expect("should write to buf") .into_inner() } } impl<'a, T: BuildXML> BuildXML for &'a T { /// Building XML from `&T` is the same as from `T`. fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { (*self).build_to(stream) } } impl BuildXML for Box { /// Building XML from `Box` is the same as from `T`. fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { (**self).build_to(stream) } } docx-rs-0.4.18/src/documents/comments.rs000064400000000000000000000041061046102023000162610ustar 00000000000000use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct Comments { pub(crate) comments: Vec, } impl Comments { pub fn new() -> Self { Default::default() } pub fn inner(&self) -> &[Comment] { &self.comments } pub fn into_inner(self) -> Vec { self.comments } pub(crate) fn add_comments(&mut self, comments: Vec) { self.comments = comments; } } impl BuildXML for Comments { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_comments()? .add_children(&self.comments)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_comments() { let b = Comments::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/comments_extended.rs000064400000000000000000000024201046102023000201360ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; // i.e. #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CommentsExtended { pub children: Vec, } impl CommentsExtended { pub fn new() -> CommentsExtended { Default::default() } pub fn add_comments_extended(&mut self, c: Vec) { self.children = c; } } impl BuildXML for CommentsExtended { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_comments_extended()? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; use insta::assert_snapshot; use std::str; #[test] fn test_settings() { let mut c = CommentsExtended::new(); c.add_comments_extended(vec![CommentExtended::new("123")]); let b = c.build(); assert_snapshot!("comments_extended_snapshot", str::from_utf8(&b).unwrap()); } } docx-rs-0.4.18/src/documents/content_types.rs000064400000000000000000000171761046102023000173450ustar 00000000000000use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::io::{Read, Write}; use xml::reader::{EventReader, XmlEvent}; use crate::documents::BuildXML; use crate::reader::{FromXML, ReaderError}; use crate::xml_builder::*; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct ContentTypes { types: BTreeMap, web_extension_count: usize, custom_xml_count: usize, header_count: usize, footer_count: usize, } impl ContentTypes { pub fn new() -> ContentTypes { Default::default() } pub fn add_content(mut self, path: impl Into, namespace: impl Into) -> Self { self.types.insert(path.into(), namespace.into()); self } pub fn set_default(mut self) -> ContentTypes { self.types.insert( "/_rels/.rels".to_owned(), "application/vnd.openxmlformats-package.relationships+xml".to_owned(), ); self.types.insert( "/docProps/app.xml".to_owned(), "application/vnd.openxmlformats-officedocument.extended-properties+xml".to_owned(), ); self.types.insert( "/docProps/core.xml".to_owned(), "application/vnd.openxmlformats-package.core-properties+xml".to_owned(), ); self.types.insert( "/word/_rels/document.xml.rels".to_owned(), "application/vnd.openxmlformats-package.relationships+xml".to_owned(), ); self.types.insert( "/word/settings.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" .to_owned(), ); self.types.insert( "/word/fontTable.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" .to_owned(), ); self.types.insert( "/word/document.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" .to_owned(), ); self.types.insert( "/word/styles.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml".to_owned(), ); self.types.insert( "/word/comments.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" .to_owned(), ); self.types.insert( "/word/numbering.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml" .to_owned(), ); self.types.insert( "/word/commentsExtended.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml" .to_owned(), ); self.types.insert( "/docProps/custom.xml".to_owned(), "application/vnd.openxmlformats-officedocument.custom-properties+xml".to_owned(), ); self } pub fn add_taskpanes(mut self) -> Self { self.types.insert( "/word/webextensions/taskpanes.xml".to_owned(), "application/vnd.ms-office.webextensiontaskpanes+xml".to_owned(), ); self } pub fn add_web_extensions(mut self) -> Self { self.types.insert( format!( "/word/webextensions/webextension{}.xml", self.web_extension_count ), "application/vnd.ms-office.webextension+xml".to_owned(), ); self.web_extension_count += 1; self } pub fn add_custom_xml(mut self) -> Self { self.types.insert( format!("/customXml/itemProps{}.xml", self.web_extension_count), "application/vnd.openxmlformats-officedocument.customXmlProperties+xml".to_owned(), ); self.custom_xml_count += 1; self } pub fn add_header(mut self) -> Self { self.header_count += 1; self.types.insert( format!("/word/header{}.xml", self.header_count), "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml".to_owned(), ); self } pub fn add_footer(mut self) -> Self { self.footer_count += 1; self.types.insert( format!("/word/footer{}.xml", self.footer_count), "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml".to_owned(), ); self } pub fn add_footnotes(mut self) -> Self { self.types.insert( "/word/footnotes.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" .to_owned(), ); self } } impl Default for ContentTypes { fn default() -> Self { ContentTypes { types: BTreeMap::new(), web_extension_count: 1, custom_xml_count: 1, header_count: 0, footer_count: 0, } } } impl BuildXML for ContentTypes { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(None)? .open_types("http://schemas.openxmlformats.org/package/2006/content-types")? .add_default("png", "image/png")? .add_default("jpeg", "image/jpeg")? .add_default("jpg", "image/jpg")? .add_default("bmp", "image/bmp")? .add_default("gif", "image/gif")? .add_default( "rels", "application/vnd.openxmlformats-package.relationships+xml", )? .add_default("xml", "application/xml")? .apply_each(self.types.iter(), |(k, v), b| b.add_override(k, v))? .close()? .into_inner() } } 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(path, namespace); } depth += 1; } Ok(XmlEvent::EndElement { .. }) => { depth -= 1; } Err(_) => {} _ => {} } } 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 = BTreeMap::new(); types.insert( "/word/document.xml".to_owned(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" .to_owned(), ); assert_eq!( ContentTypes { types, web_extension_count: 1, custom_xml_count: 1, header_count: 0, footer_count: 0, }, c ); } } docx-rs-0.4.18/src/documents/custom_item.rs000064400000000000000000000045201046102023000167640ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::XMLBuilder; use crate::{ParseXmlError, XmlDocument}; use serde::ser::SerializeSeq; use serde::Serialize; use std::io::Write; use std::str::FromStr; #[derive(Debug, Clone)] pub struct CustomItem(XmlDocument); impl FromStr for CustomItem { type Err = ParseXmlError; fn from_str(s: &str) -> Result { Ok(CustomItem(XmlDocument::from_str(s)?)) } } impl Serialize for CustomItem { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut seq = serializer.serialize_seq(Some(self.0.data.len()))?; for e in self.0.data.iter() { seq.serialize_element(e)?; } seq.end() } } impl BuildXML for CustomItem { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut b = XMLBuilder::from(stream); write!(b.inner_mut()?, "{}", self.0)?; b.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; #[test] fn test_custom_xml() { let c = CustomItem::from_str( r#""#, ) .unwrap(); assert_eq!( c.0.to_string(), r#""# ); assert_eq!( serde_json::to_string(&c).unwrap(), "[{\"name\":\"ds:datastoreItem\",\"attributes\":[[\"ds:itemID\",\"{06AC5857-5C65-A94A-BCEC-37356A209BC3}\"],[\"xmlns:ds\",\"http://schemas.openxmlformats.org/officeDocument/2006/customXml\"]],\"data\":null,\"children\":[{\"name\":\"ds:schemaRefs\",\"attributes\":[],\"data\":null,\"children\":[{\"name\":\"ds:schemaRef\",\"attributes\":[[\"ds:uri\",\"https://hoge.com\"]],\"data\":null,\"children\":[]}]}]}]" ); } } docx-rs-0.4.18/src/documents/custom_item_property.rs000064400000000000000000000015351046102023000207330ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize)] pub struct CustomItemProperty { id: String, } impl CustomItemProperty { pub fn new(id: impl Into) -> Self { Self { id: id.into() } } } impl BuildXML for CustomItemProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(false))? .open_data_store_item( "http://schemas.openxmlformats.org/officeDocument/2006/customXml", &format!("{{{}}}", self.id), )? .open_data_store_schema_refs()? .close()? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/custom_item_rels.rs000064400000000000000000000021771046102023000200170ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CustomItemRels { custom_item_count: usize, } impl CustomItemRels { pub fn new() -> CustomItemRels { Default::default() } pub fn add_item(mut self) -> Self { self.custom_item_count += 1; self } } impl BuildXML for CustomItemRels { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")? .apply_each(0..self.custom_item_count, |id, b| { b.relationship( &format!("rId{}", id + 1), "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps", &format!("itemProps{}.xml", id + 1), ) })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/doc_props/app.rs000064400000000000000000000025611046102023000172070ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct AppProps {} impl AppProps { pub fn new() -> AppProps { Default::default() } } impl BuildXML for AppProps { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_properties( "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", )? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default_doc_props_app_build() { let c = AppProps::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/doc_props/core.rs000064400000000000000000000130531046102023000173550ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CoreProps { config: CorePropsConfig, } #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CorePropsConfig { created: Option, creator: Option, description: Option, language: Option, last_modified_by: Option, modified: Option, revision: Option, subject: Option, title: Option, } impl CoreProps { pub(crate) fn new(config: CorePropsConfig) -> CoreProps { CoreProps { config } } pub fn created_at(mut self, date: &str) -> Self { self.config.created = Some(date.to_owned()); self } pub fn updated_at(mut self, date: &str) -> Self { self.config.modified = Some(date.to_owned()); self } } impl CorePropsConfig { pub fn new() -> Self { CorePropsConfig { created: None, creator: None, description: None, language: None, last_modified_by: None, modified: None, revision: None, subject: None, title: None, } } } impl BuildXML for CoreProps { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_core_properties( "http://schemas.openxmlformats.org/package/2006/metadata/core-properties", "http://purl.org/dc/elements/1.1/", "http://purl.org/dc/terms/", "http://purl.org/dc/dcmitype/", "http://www.w3.org/2001/XMLSchema-instance", )? .dcterms_created( "dcterms:W3CDTF", self.config .created .as_deref() .unwrap_or("1970-01-01T00:00:00Z"), )? .dc_creator(self.config.creator.as_deref().unwrap_or("unknown"))? .cp_last_modified_by(self.config.last_modified_by.as_deref().unwrap_or("unknown"))? .dcterms_modified( "dcterms:W3CDTF", self.config .modified .as_deref() .unwrap_or("1970-01-01T00:00:00Z"), )? .cp_revision(&self.config.revision.unwrap_or(1).to_string())? .apply_opt(self.config.description.as_ref(), |v, b| b.dc_description(v))? .apply_opt(self.config.language.as_ref(), |v, b| b.dc_language(v))? .apply_opt(self.config.subject.as_ref(), |v, b| b.dc_subject(v))? .apply_opt(self.config.title.as_ref(), |v, b| b.dc_title(v))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default_doc_props_core() { let c = CoreProps::new(CorePropsConfig { created: None, creator: None, description: None, language: None, last_modified_by: None, modified: None, revision: None, subject: None, title: None, }); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"1970-01-01T00:00:00Zunknownunknown1970-01-01T00:00:00Z1"# ); } #[test] fn test_configured_doc_props_core_build() { let c = CoreProps::new(CorePropsConfig { created: Some("2019-01-01".to_owned()), creator: Some("foo".to_owned()), description: Some("bar".to_owned()), language: Some("en".to_owned()), last_modified_by: Some("go".to_owned()), modified: Some("2019-01-01".to_owned()), revision: Some(1), subject: Some("subject".to_owned()), title: Some("title".to_owned()), }); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"2019-01-01foogo2019-01-011barensubjecttitle"# ); } } docx-rs-0.4.18/src/documents/doc_props/custom.rs000064400000000000000000000030071046102023000177350ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CustomProps { pub properties: std::collections::HashMap, } impl CustomProps { pub(crate) fn new() -> Self { Self::default() } pub fn add_custom_property(mut self, name: impl Into, item: impl Into) -> Self { self.properties.insert(name.into(), item.into()); self } } impl BuildXML for CustomProps { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_custom_properties( "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", )? .apply_each(self.properties.iter().enumerate(), |(i, (key, item)), b| { b.open_property( "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", // I can not find spec about this id. // It is invalid if pid starts from 1... &format!("{}", i + 2), key, )? .lpwstr(item)? .close() })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/doc_props/mod.rs000064400000000000000000000025101046102023000172000ustar 00000000000000mod app; mod core; mod custom; pub use self::app::*; pub use self::core::*; pub use self::custom::*; use crate::documents::BuildXML; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DocProps { pub app: AppProps, pub core: CoreProps, pub custom: CustomProps, } impl DocProps { pub(crate) fn new(core_config: CorePropsConfig) -> DocProps { let app = AppProps::new(); let core = CoreProps::new(core_config); let custom = CustomProps::new(); DocProps { app, core, custom } } pub fn created_at(mut self, date: &str) -> Self { self.core = self.core.created_at(date); self } pub fn updated_at(mut self, date: &str) -> Self { self.core = self.core.updated_at(date); self } pub fn custom_property(mut self, name: impl Into, item: impl Into) -> Self { self.custom = self.custom.add_custom_property(name.into(), item.into()); self } pub(crate) fn build(&self) -> XMLDocProps { XMLDocProps { app: self.app.build(), core: self.core.build(), custom: self.custom.build(), } } } #[derive(Debug)] pub struct XMLDocProps { pub app: Vec, pub core: Vec, pub custom: Vec, } docx-rs-0.4.18/src/documents/document.rs000064400000000000000000000332331046102023000162550ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Document { pub children: Vec, pub section_property: SectionProperty, pub has_numbering: bool, } #[derive(Debug, Clone, PartialEq)] pub enum DocumentChild { Paragraph(Box), Table(Box), BookmarkStart(BookmarkStart), BookmarkEnd(BookmarkEnd), CommentStart(Box), CommentEnd(CommentRangeEnd), StructuredDataTag(Box), TableOfContents(Box), } 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() } DocumentChild::BookmarkStart(ref c) => { let mut t = serializer.serialize_struct("BookmarkStart", 2)?; t.serialize_field("type", "bookmarkStart")?; t.serialize_field("data", c)?; t.end() } DocumentChild::BookmarkEnd(ref c) => { let mut t = serializer.serialize_struct("BookmarkEnd", 2)?; t.serialize_field("type", "bookmarkEnd")?; t.serialize_field("data", c)?; t.end() } DocumentChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } DocumentChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } DocumentChild::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } DocumentChild::TableOfContents(ref r) => { let mut t = serializer.serialize_struct("TableOfContents", 2)?; t.serialize_field("type", "tableOfContents")?; t.serialize_field("data", r)?; 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() } pub fn add_paragraph(mut self, p: Paragraph) -> Self { if p.has_numbering { self.has_numbering = true } self.children.push(DocumentChild::Paragraph(Box::new(p))); self } pub fn add_table(mut self, t: Table) -> Self { if t.has_numbering { self.has_numbering = true } self.children.push(DocumentChild::Table(Box::new(t))); self } pub fn add_bookmark_start(mut self, id: usize, name: impl Into) -> Self { self.children .push(DocumentChild::BookmarkStart(BookmarkStart::new(id, name))); self } pub fn add_bookmark_end(mut self, id: usize) -> Self { self.children .push(DocumentChild::BookmarkEnd(BookmarkEnd::new(id))); self } pub fn add_comment_start(mut self, comment: Comment) -> Self { self.children.push(DocumentChild::CommentStart(Box::new( CommentRangeStart::new(comment), ))); self } pub fn add_comment_end(mut self, id: usize) -> Self { self.children .push(DocumentChild::CommentEnd(CommentRangeEnd::new(id))); self } pub fn title_pg(mut self) -> Self { self.section_property = self.section_property.title_pg(); self } pub fn page_size(mut self, size: PageSize) -> Self { self.section_property = self.section_property.page_size(size); self } pub fn page_margin(mut self, margin: crate::types::PageMargin) -> Self { self.section_property = self.section_property.page_margin(margin); self } pub fn page_orient(mut self, o: crate::types::PageOrientationType) -> Self { self.section_property = self.section_property.page_orient(o); self } pub fn doc_grid(mut self, doc_grid: DocGrid) -> Self { self.section_property = self.section_property.doc_grid(doc_grid); self } pub fn default_section_property(mut self, property: SectionProperty) -> Self { self.section_property = property; self } pub fn header(mut self, h: Header, rid: &str) -> Self { self.section_property = self.section_property.header(h, rid); self } pub fn first_header(mut self, h: Header, rid: &str) -> Self { self.section_property = self.section_property.first_header(h, rid); self } pub(crate) fn first_header_without_title_pg(mut self, h: Header, rid: &str) -> Self { self.section_property = self.section_property.first_header_without_title_pg(h, rid); self } pub fn even_header(mut self, h: Header, rid: &str) -> Self { self.section_property = self.section_property.even_header(h, rid); self } pub fn footer(mut self, h: Footer, rid: &str) -> Self { self.section_property = self.section_property.footer(h, rid); self } pub fn first_footer(mut self, h: Footer, rid: &str) -> Self { self.section_property = self.section_property.first_footer(h, rid); self } pub(crate) fn first_footer_without_title_pg(mut self, h: Footer, rid: &str) -> Self { self.section_property = self.section_property.first_footer_without_title_pg(h, rid); self } pub fn even_footer(mut self, h: Footer, rid: &str) -> Self { self.section_property = self.section_property.even_footer(h, rid); self } pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { if t.has_numbering { self.has_numbering = true } self.children .push(DocumentChild::StructuredDataTag(Box::new(t))); self } pub fn add_table_of_contents(mut self, t: TableOfContents) -> Self { self.children .push(DocumentChild::TableOfContents(Box::new(t))); self } pub fn columns(mut self, col: usize) -> Self { self.section_property.columns = col; self } pub fn text_direction(mut self, direction: String) -> Self { self.section_property.text_direction = direction; self } pub fn page_num_type(mut self, p: PageNumType) -> Self { self.section_property = self.section_property.page_num_type(p); self } } impl BuildXML for DocumentChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { DocumentChild::Paragraph(v) => v.build_to(stream), DocumentChild::Table(v) => v.build_to(stream), DocumentChild::BookmarkStart(v) => v.build_to(stream), DocumentChild::BookmarkEnd(v) => v.build_to(stream), DocumentChild::CommentStart(v) => v.build_to(stream), DocumentChild::CommentEnd(v) => v.build_to(stream), DocumentChild::StructuredDataTag(v) => v.build_to(stream), DocumentChild::TableOfContents(v) => v.build_to(stream), } } } impl BuildXML for Document { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_document()? .open_body()? .add_children(&self.children)? .add_child(&self.section_property)? .close()? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::super::Run; use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_document() { let b = Document::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_document_with_toc() { let toc = TableOfContents::new().heading_styles_range(1, 3); let b = Document::new().add_table_of_contents(toc).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""# ); } #[test] fn test_document_cols() { let b = Document::new() .columns(2) .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } } docx-rs-0.4.18/src/documents/document_rels.rs000064400000000000000000000112001046102023000172700ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::{escape::*, xml_builder::*}; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct DocumentRels { pub has_comments: bool, pub has_numberings: bool, pub has_footnotes: bool, pub images: Vec<(String, String)>, pub hyperlinks: Vec<(String, String, String)>, pub custom_xml_count: usize, pub header_count: usize, pub footer_count: usize, } impl DocumentRels { pub fn new() -> DocumentRels { Default::default() } pub fn add_custom_item(mut self) -> Self { self.custom_xml_count += 1; self } pub fn add_image(mut self, id: impl Into, path: impl Into) -> Self { self.images.push((id.into(), path.into())); self } pub fn add_hyperlinks( mut self, id: impl Into, path: impl Into, r#type: impl Into, ) -> Self { self.hyperlinks .push((id.into(), escape(&path.into()), r#type.into())); self } } impl BuildXML for DocumentRels { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(None)? .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")? .relationship( "rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml", )? .relationship( "rId2", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable", "fontTable.xml", )? .relationship( "rId3", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", "settings.xml", )? .relationship( "rId5", "http://schemas.microsoft.com/office/2011/relationships/commentsExtended", "commentsExtended.xml", )? .apply_if(self.has_comments, |b| { b.relationship( "rId6", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", "comments.xml", ) })? .apply_if(self.has_numberings, |b| { b.relationship( "rId7", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "numbering.xml", ) })? .apply_if(self.has_footnotes, |b| { b.relationship( "rId8", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "footnotes.xml", ) })? .apply_each(0..self.header_count, |i, b| { b.relationship( &create_header_rid(i + 1), "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", &format!("header{}.xml", i + 1), ) })? .apply_each(0..self.footer_count, |i, b| { b.relationship( &create_footer_rid(i + 1), "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", &format!("footer{}.xml", i + 1), ) })? .apply_each(0..self.custom_xml_count, |i, b| { b.relationship( &format!("rId{}", i + 8), "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml", &format!("../customXml/item{}.xml", i + 1), ) })? .apply_each(self.images.iter(), |(id, path), b| { b.relationship( id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", path, ) })? .apply_each(self.hyperlinks.iter(), |(id, path, r#type), b| { b.relationship_with_mode( id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", path, r#type, ) })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/elements/a_graphic.rs000064400000000000000000000037271046102023000201750ustar 00000000000000use super::*; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct AGraphic { pub children: Vec, } impl AGraphic { pub fn new() -> AGraphic { Default::default() } pub fn add_graphic_data(mut self, g: AGraphicData) -> Self { self.children.push(g); self } } impl BuildXML for AGraphic { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_graphic("http://schemas.openxmlformats.org/drawingml/2006/main")? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; #[test] fn test_a_graphic_with_textbox_json() { let graphic = AGraphic::new().add_graphic_data( AGraphicData::new(GraphicDataType::WpShape).add_shape( WpsShape::new().add_text_box(WpsTextBox::new().add_content( TextBoxContent::new().add_paragraph( Paragraph::new().add_run(Run::new().add_text("pattern1")), ), )), ), ); assert_eq!( serde_json::to_string(&graphic).unwrap(), r#"{"children":[{"dataType":"wpShape","children":[{"type":"shape","data":{"children":[{"type":"textbox","data":{"children":[{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"pattern1"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}}],"has_numbering":false}],"hasNumbering":false}}]}}]}]}"#, ); } } docx-rs-0.4.18/src/documents/elements/a_graphic_data.rs000064400000000000000000000060731046102023000211630ustar 00000000000000use super::*; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use std::str::FromStr; use crate::documents::BuildXML; use crate::xml_builder::*; /* 20.1.2.2.17 graphicData (Graphic Object Data) This element specifies the reference to a graphic object within the document. This graphic object is provided entirely by the document authors who choose to persist this data within the document. */ #[derive(Debug, Clone, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct AGraphicData { pub data_type: GraphicDataType, pub children: Vec, } #[derive(Debug, Clone, PartialEq)] pub enum GraphicDataChild { Shape(WpsShape), Pic(Pic), } impl Serialize for GraphicDataChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { GraphicDataChild::Shape(ref s) => { let mut t = serializer.serialize_struct("Shape", 2)?; t.serialize_field("type", "shape")?; t.serialize_field("data", s)?; t.end() } GraphicDataChild::Pic(ref s) => { let mut t = serializer.serialize_struct("Pic", 2)?; t.serialize_field("type", "pic")?; t.serialize_field("data", s)?; t.end() } } } } impl GraphicDataType { fn to_uri(&self) -> &str { match *self { GraphicDataType::Picture => "http://schemas.openxmlformats.org/drawingml/2006/picture", GraphicDataType::WpShape => { "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" } _ => "", } } } impl FromStr for GraphicDataType { type Err = (); fn from_str(s: &str) -> Result { if s.ends_with("picture") { return Ok(GraphicDataType::Picture); } if s.ends_with("wordprocessingShape") { return Ok(GraphicDataType::WpShape); } Ok(GraphicDataType::Unsupported) } } #[derive(Debug, Clone, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub enum GraphicDataType { Picture, WpShape, Unsupported, } impl AGraphicData { pub fn new(data_type: GraphicDataType) -> AGraphicData { AGraphicData { data_type, children: vec![], } } pub fn add_shape(mut self, shape: WpsShape) -> Self { self.children.push(GraphicDataChild::Shape(shape)); self } } impl BuildXML for AGraphicData { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_graphic_data(self.data_type.to_uri())? .apply_each(&self.children, |ch, b| match ch { GraphicDataChild::Shape(t) => b.add_child(t), GraphicDataChild::Pic(t) => b.add_child(t), })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/elements/abstract_numbering.rs000064400000000000000000000062441046102023000221260ustar 00000000000000use crate::documents::{BuildXML, Level}; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AbstractNumbering { pub id: usize, pub style_link: Option, pub num_style_link: Option, pub levels: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub multi_level_type: Option, } impl AbstractNumbering { pub fn new(id: usize) -> Self { Self { id, style_link: None, num_style_link: None, levels: vec![], multi_level_type: None, } } pub fn add_level(mut self, level: Level) -> Self { self.levels.push(level); self } pub fn num_style_link(mut self, link: impl Into) -> Self { self.num_style_link = Some(link.into()); self } pub fn style_link(mut self, link: impl Into) -> Self { self.style_link = Some(link.into()); self } } impl BuildXML for AbstractNumbering { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut builder = XMLBuilder::from(stream) .open_abstract_num(&self.id.to_string())?; // 添加 multiLevelType 元素(如果存在) if let Some(ref multi_level_type) = self.multi_level_type { builder = builder.multi_level_type(multi_level_type)?; } builder .add_children(&self.levels)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use crate::documents::{Level, LevelJc, LevelText, NumberFormat, Start}; use pretty_assertions::assert_eq; use std::str; #[test] fn test_numbering() { let mut c = AbstractNumbering::new(0); c = c.add_level(Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), )); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_numbering_json() { let mut c = AbstractNumbering::new(0); c = c .add_level(Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), )) .num_style_link("style1"); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{},"tabs":[]},"runProperty":{},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#, ); } } docx-rs-0.4.18/src/documents/elements/adjust_right_ind.rs000064400000000000000000000014221046102023000215670ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct AdjustRightInd(pub isize); impl AdjustRightInd { pub fn new(val: isize) -> AdjustRightInd { AdjustRightInd(val) } } impl BuildXML for AdjustRightInd { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .adjust_right_ind(self.0)? .into_inner() } } impl Serialize for AdjustRightInd { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_i64(self.0 as i64) } } docx-rs-0.4.18/src/documents/elements/based_on.rs000064400000000000000000000021741046102023000200250ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct BasedOn { val: String, } impl BasedOn { pub fn new(val: impl Into) -> BasedOn { BasedOn { val: escape(&val.into()), } } } impl Serialize for BasedOn { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } impl BuildXML for BasedOn { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).based_on(&self.val)?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = BasedOn::new("Normal"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/bold.rs000064400000000000000000000017461046102023000171770ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Bold { val: bool, } impl Bold { pub fn new() -> Bold { Default::default() } pub fn disable(mut self) -> Bold { self.val = false; self } } impl Default for Bold { fn default() -> Self { Self { val: true } } } impl Serialize for Bold { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for Bold { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { if self.val { XMLBuilder::from(stream).b()?.into_inner() } else { XMLBuilder::from(stream).disable_bold()?.into_inner() } } } docx-rs-0.4.18/src/documents/elements/bold_cs.rs000064400000000000000000000015771046102023000176660ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct BoldCs { val: bool, } impl BoldCs { pub fn new() -> BoldCs { Default::default() } pub fn disable(mut self) -> BoldCs { self.val = false; self } } impl Default for BoldCs { fn default() -> Self { Self { val: true } } } impl Serialize for BoldCs { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for BoldCs { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).b_cs()?.into_inner() } } docx-rs-0.4.18/src/documents/elements/bookmark_end.rs000064400000000000000000000016071046102023000207060ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct BookmarkEnd { pub id: usize, } impl BookmarkEnd { pub fn new(id: usize) -> BookmarkEnd { BookmarkEnd { id } } } impl BuildXML for BookmarkEnd { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .bookmark_end(&self.id.to_string())? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_bookmark_end() { let c = BookmarkEnd::new(0); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/bookmark_start.rs000064400000000000000000000021101046102023000212630ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct BookmarkStart { pub id: usize, pub name: String, } impl BookmarkStart { pub fn new(id: usize, name: impl Into) -> BookmarkStart { BookmarkStart { id, name: name.into(), } } } impl BuildXML for BookmarkStart { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .bookmark_start(&self.id.to_string(), &self.name)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_bookmark_start() { let c = BookmarkStart::new(0, "mockname"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/br.rs000064400000000000000000000016771046102023000166650ustar 00000000000000use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::Deserialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Break { break_type: BreakType, } impl Break { pub fn new(t: BreakType) -> Break { Break { break_type: t } } } impl BuildXML for Break { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .br(&self.break_type.to_string())? .into_inner() } } 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() } } docx-rs-0.4.18/src/documents/elements/cant_split.rs000064400000000000000000000011511046102023000204050ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::{xml_builder::XMLBuilder, BuildXML}; #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct CantSplit {} impl BuildXML for CantSplit { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).cant_split()?.into_inner() } } impl Serialize for CantSplit { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str("cantSplit") } } docx-rs-0.4.18/src/documents/elements/caps.rs000064400000000000000000000017301046102023000171760ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::{xml_builder::XMLBuilder, BuildXML}; // use crate::documents::BuildXML; // use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Caps { val: bool, } impl Caps { pub fn new() -> Caps { Default::default() } pub fn disable(mut self) -> Caps { self.val = false; self } } impl Default for Caps { fn default() -> Self { Self { val: true } } } impl Serialize for Caps { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for Caps { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .caps(&self.val.to_string())? .into_inner() } } docx-rs-0.4.18/src/documents/elements/cell_margins.rs000064400000000000000000000055101046102023000207070ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CellMargin { pub val: usize, pub width_type: WidthType, } impl CellMargin { pub fn new(val: usize, t: WidthType) -> Self { Self { val, width_type: t } } } impl Default for CellMargin { fn default() -> CellMargin { CellMargin { val: 55, width_type: WidthType::Dxa, } } } #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct CellMargins { #[serde(skip_serializing_if = "Option::is_none")] top: Option, #[serde(skip_serializing_if = "Option::is_none")] left: Option, #[serde(skip_serializing_if = "Option::is_none")] bottom: Option, #[serde(skip_serializing_if = "Option::is_none")] right: Option, } impl CellMargins { pub fn new() -> CellMargins { Default::default() } pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { self.top = Some(CellMargin::new(v, t)); self } pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { self.right = Some(CellMargin::new(v, t)); self } pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { self.left = Some(CellMargin::new(v, t)); self } pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { self.bottom = Some(CellMargin::new(v, t)); self } } impl BuildXML for CellMargins { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_cell_margins()? .apply_opt(self.top.as_ref(), |top, b| { b.margin_top(top.val as i32, top.width_type) })? .apply_opt(self.left.as_ref(), |left, b| { b.margin_left(left.val as i32, left.width_type) })? .apply_opt(self.bottom.as_ref(), |bottom, b| { b.margin_bottom(bottom.val as i32, bottom.width_type) })? .apply_opt(self.right.as_ref(), |right, b| { b.margin_right(right.val as i32, right.width_type) })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_cell_margin() { let b = CellMargins::new().margin_top(10, WidthType::Dxa).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/character_spacing.rs000064400000000000000000000023411046102023000217070ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::*; #[derive(Debug, Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct CharacterSpacing { value: i32, } impl CharacterSpacing { pub fn new(s: i32) -> CharacterSpacing { Self { value: s } } } impl BuildXML for CharacterSpacing { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).spacing(self.value)?.into_inner() } } impl Serialize for CharacterSpacing { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_i32(self.value) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_spacing() { let b = CharacterSpacing::new(200).build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_spacing_json() { let s = CharacterSpacing { value: 100 }; assert_eq!(serde_json::to_string(&s).unwrap(), r#"100"#); } } docx-rs-0.4.18/src/documents/elements/color.rs000064400000000000000000000020501046102023000173620ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Deserialize, Debug, Clone, PartialEq)] pub struct Color { val: String, } impl Color { pub fn new(val: impl Into) -> Color { Color { val: val.into() } } } impl BuildXML for Color { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).color(&self.val)?.into_inner() } } impl Serialize for Color { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Color::new("FFFFFF"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/comment.rs000064400000000000000000000073461046102023000177230ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Comment { pub id: usize, pub author: String, pub date: String, pub children: Vec, pub parent_comment_id: Option, } #[derive(Debug, Clone, PartialEq)] pub enum CommentChild { Paragraph(Paragraph), Table(Table), } impl Serialize for CommentChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { CommentChild::Paragraph(ref p) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", p)?; t.end() } CommentChild::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 Comment { fn default() -> Comment { Comment { id: 1, author: "unnamed".to_owned(), date: "1970-01-01T00:00:00Z".to_owned(), children: vec![], parent_comment_id: None, } } } impl Comment { pub fn new(id: usize) -> Comment { Self { id, ..Default::default() } } pub fn author(mut self, author: impl Into) -> Comment { self.author = author.into(); self } pub fn date(mut self, date: impl Into) -> Comment { self.date = date.into(); self } pub fn add_paragraph(mut self, p: Paragraph) -> Self { self.children.push(CommentChild::Paragraph(p)); self } pub fn add_table(mut self, t: Table) -> Self { self.children.push(CommentChild::Table(t)); self } pub fn parent_comment_id(mut self, parent_comment_id: usize) -> Comment { self.parent_comment_id = Some(parent_comment_id); self } pub fn id(&self) -> usize { self.id } } impl BuildXML for CommentChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { CommentChild::Paragraph(v) => v.build_to(stream), CommentChild::Table(v) => v.build_to(stream), } } } impl BuildXML for Comment { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_comment(&self.id.to_string(), &self.author, &self.date, "")? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_comment_default() { let b = Comment::new(1).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_comment_with_default_paragraph() { let b = Comment::new(1).add_paragraph(Paragraph::new()).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/comment_extended.rs000064400000000000000000000031371046102023000215750ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CommentExtended { pub paragraph_id: String, pub done: bool, pub parent_paragraph_id: Option, } impl CommentExtended { pub fn new(paragraph_id: impl Into) -> CommentExtended { Self { paragraph_id: paragraph_id.into(), done: false, parent_paragraph_id: None, } } pub fn done(mut self) -> CommentExtended { self.done = true; self } pub fn parent_paragraph_id(mut self, id: impl Into) -> CommentExtended { self.parent_paragraph_id = Some(id.into()); self } } impl BuildXML for CommentExtended { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .comment_extended(&self.paragraph_id, self.done, &self.parent_paragraph_id)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; #[test] fn test_comment_extended_json() { let ex = CommentExtended { paragraph_id: "00002".to_owned(), done: false, parent_paragraph_id: Some("0004".to_owned()), }; assert_eq!( serde_json::to_string(&ex).unwrap(), r#"{"paragraphId":"00002","done":false,"parentParagraphId":"0004"}"# ); } } docx-rs-0.4.18/src/documents/elements/comment_range_end.rs000064400000000000000000000023331046102023000217140ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct CommentRangeEnd { id: usize, } impl CommentRangeEnd { pub fn new(id: usize) -> CommentRangeEnd { CommentRangeEnd { id } } } impl BuildXML for CommentRangeEnd { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_run()? .open_run_property()? .close()? .close()? .comment_range_end(&format!("{}", self.id))? .open_run()? .comment_reference(&format!("{}", self.id))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_comment_range_end() { let c = CommentRangeEnd::new(1); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/comment_range_start.rs000064400000000000000000000025241046102023000223050ustar 00000000000000use serde::Serialize; use std::io::Write; use super::Comment; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct CommentRangeStart { pub id: usize, pub comment: Comment, } impl CommentRangeStart { pub fn new(comment: Comment) -> CommentRangeStart { CommentRangeStart { id: comment.id(), comment, } } pub(crate) fn comment(&mut self, comment: Comment) { self.comment = comment; } pub(crate) fn get_comment(&self) -> Comment { self.comment.clone() } pub(crate) fn get_id(&self) -> usize { self.id } } impl BuildXML for CommentRangeStart { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .comment_range_start(&self.id.to_string())? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_comment_range_start() { let c = CommentRangeStart::new(Comment::new(1)); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/data_binding.rs000064400000000000000000000030111046102023000206450ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq, Default)] pub struct DataBinding { pub xpath: Option, pub prefix_mappings: Option, pub store_item_id: Option, } impl DataBinding { pub fn new() -> Self { Default::default() } pub fn xpath(mut self, xpath: impl Into) -> Self { self.xpath = Some(xpath.into()); self } pub fn prefix_mappings(mut self, m: impl Into) -> Self { self.prefix_mappings = Some(m.into()); self } pub fn store_item_id(mut self, id: impl Into) -> Self { self.store_item_id = Some(id.into()); self } } impl BuildXML for DataBinding { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .data_binding( self.xpath.as_ref(), self.prefix_mappings.as_ref(), self.store_item_id.as_ref(), )? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_delete_default() { let b = DataBinding::new().xpath("root/hello").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/default_tab_stop.rs000064400000000000000000000021761046102023000215740ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::{Serialize, Serializer}; #[derive(Debug, Clone, PartialEq)] pub struct DefaultTabStop { val: usize, } impl DefaultTabStop { pub fn new(val: usize) -> DefaultTabStop { DefaultTabStop { val } } } impl BuildXML for DefaultTabStop { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .default_tab_stop(self.val)? .into_inner() } } impl Serialize for DefaultTabStop { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u64(self.val as u64) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_zoom() { let c = DefaultTabStop::new(20); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/delete.rs000064400000000000000000000070601046102023000175140ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::xml_builder::*; use crate::{documents::*, escape}; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct Delete { pub author: String, pub date: String, pub children: Vec, } #[derive(Debug, Clone, PartialEq)] pub enum DeleteChild { Run(Run), CommentStart(Box), CommentEnd(CommentRangeEnd), } impl Serialize for DeleteChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { DeleteChild::Run(ref r) => { let mut t = serializer.serialize_struct("Run", 2)?; t.serialize_field("type", "run")?; t.serialize_field("data", r)?; t.end() } DeleteChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } DeleteChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } } } } impl Default for Delete { fn default() -> Delete { Delete { author: "unnamed".to_owned(), date: "1970-01-01T00:00:00Z".to_owned(), children: vec![], } } } impl Delete { pub fn new() -> Delete { Self { children: vec![], ..Default::default() } } pub fn add_run(mut self, run: Run) -> Delete { self.children.push(DeleteChild::Run(run)); self } pub fn add_comment_start(mut self, comment: Comment) -> Delete { self.children .push(DeleteChild::CommentStart(Box::new(CommentRangeStart::new( comment, )))); self } pub fn add_comment_end(mut self, id: usize) -> Delete { self.children .push(DeleteChild::CommentEnd(CommentRangeEnd::new(id))); self } pub fn author(mut self, author: impl Into) -> Delete { self.author = escape::escape(&author.into()); self } pub fn date(mut self, date: impl Into) -> Delete { self.date = date.into(); self } } impl HistoryId for Delete {} impl BuildXML for Delete { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let id = self.generate(); XMLBuilder::from(stream) .open_delete(&id, &self.author, &self.date)? .apply_each(&self.children, |ch, b| match ch { DeleteChild::Run(t) => b.add_child(t), DeleteChild::CommentStart(c) => b.add_child(&c), DeleteChild::CommentEnd(c) => b.add_child(c), })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_delete_default() { let b = Delete::new().add_run(Run::new()).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/delete_instr_text.rs000064400000000000000000000055351046102023000220040ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub enum DeleteInstrText { TOC(InstrToC), TC(InstrTC), PAGEREF(InstrPAGEREF), HYPERLINK(InstrHyperlink), Unsupported(String), } impl BuildXML for DeleteInstrText { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_delete_instr_text()? .apply(|b| match self { DeleteInstrText::TOC(toc) => b.add_child(toc), DeleteInstrText::TC(tc) => b.add_child(tc), DeleteInstrText::PAGEREF(page_ref) => b.add_child(page_ref), DeleteInstrText::HYPERLINK(_link) => todo!(), DeleteInstrText::Unsupported(s) => b.plain_text(s), })? .close()? .into_inner() } } impl Serialize for DeleteInstrText { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { DeleteInstrText::TOC(ref s) => { let mut t = serializer.serialize_struct("TOC", 2)?; t.serialize_field("type", "toc")?; t.serialize_field("data", s)?; t.end() } DeleteInstrText::TC(ref s) => { let mut t = serializer.serialize_struct("TC", 2)?; t.serialize_field("type", "tc")?; t.serialize_field("data", s)?; t.end() } DeleteInstrText::PAGEREF(ref s) => { let mut t = serializer.serialize_struct("PAGEREF", 2)?; t.serialize_field("type", "pageref")?; t.serialize_field("data", s)?; t.end() } DeleteInstrText::HYPERLINK(ref s) => { let mut t = serializer.serialize_struct("HYPERLINK", 2)?; t.serialize_field("type", "hyperlink")?; t.serialize_field("data", s)?; t.end() } DeleteInstrText::Unsupported(ref s) => { let mut t = serializer.serialize_struct("Unsupported", 2)?; t.serialize_field("type", "unsupported")?; t.serialize_field("data", s)?; t.end() } } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_delete_toc_instr() { let b = DeleteInstrText::TOC(InstrToC::new().heading_styles_range(1, 3)).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""# ); } } docx-rs-0.4.18/src/documents/elements/delete_text.rs000064400000000000000000000030231046102023000205530ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct DeleteText { text: String, preserve_space: bool, } impl DeleteText { pub fn new(text: impl Into) -> DeleteText { DeleteText { text: escape(&text.into()), preserve_space: true, } } pub(crate) fn without_escape(text: impl Into) -> DeleteText { DeleteText { text: text.into(), preserve_space: true, } } } impl BuildXML for DeleteText { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .delete_text(&self.text, true)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = DeleteText::new("Hello").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_escape() { let b = DeleteText::new("
").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"<div />"# ); } } docx-rs-0.4.18/src/documents/elements/div.rs000064400000000000000000000023041046102023000170300ustar 00000000000000use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Div { pub id: String, pub margin_left: usize, pub margin_right: usize, pub margin_top: usize, pub margin_bottom: usize, pub divs_child: Vec
, } impl Default for Div { fn default() -> Self { Self { id: "".to_string(), margin_left: 0, margin_right: 0, margin_top: 0, margin_bottom: 0, divs_child: vec![], } } } impl Div { pub fn new(id: impl Into) -> Self { Self { id: id.into(), ..Default::default() } } pub fn margin_left(mut self, s: usize) -> Self { self.margin_left = s; self } pub fn margin_right(mut self, s: usize) -> Self { self.margin_right = s; self } pub fn margin_top(mut self, s: usize) -> Self { self.margin_top = s; self } pub fn margin_bottom(mut self, s: usize) -> Self { self.margin_bottom = s; self } pub fn add_child(mut self, s: Div) -> Self { self.divs_child.push(s); self } } docx-rs-0.4.18/src/documents/elements/doc_defaults.rs000064400000000000000000000051741046102023000207120ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::{documents::BuildXML, RunProperty}; use crate::{xml_builder::*, LineSpacing, ParagraphProperty, ParagraphPropertyDefault}; use super::run_property_default::*; use super::RunFonts; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct DocDefaults { run_property_default: RunPropertyDefault, paragraph_property_default: ParagraphPropertyDefault, } impl DocDefaults { pub fn new() -> DocDefaults { Default::default() } pub fn size(mut self, size: usize) -> Self { self.run_property_default = self.run_property_default.size(size); self } pub fn spacing(mut self, spacing: i32) -> Self { self.run_property_default = self.run_property_default.spacing(spacing); self } pub fn fonts(mut self, font: RunFonts) -> Self { self.run_property_default = self.run_property_default.fonts(font); self } pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { self.paragraph_property_default = self.paragraph_property_default.line_spacing(spacing); self } pub(crate) fn run_property(mut self, p: RunProperty) -> Self { self.run_property_default = self.run_property_default.run_property(p); self } pub(crate) fn paragraph_property(mut self, p: ParagraphProperty) -> Self { self.paragraph_property_default = self.paragraph_property_default.paragraph_property(p); self } } impl Default for DocDefaults { fn default() -> Self { let run_property_default = RunPropertyDefault::new(); let paragraph_property_default = ParagraphPropertyDefault::new(); DocDefaults { run_property_default, paragraph_property_default, } } } impl BuildXML for DocDefaults { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_doc_defaults()? .add_child(&self.run_property_default)? .add_child(&self.paragraph_property_default)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = DocDefaults::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/doc_grid.rs000064400000000000000000000026641046102023000200310ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct DocGrid { grid_type: DocGridType, line_pitch: Option, char_space: Option, } impl DocGrid { pub fn new() -> Self { Self::default() } pub fn with_empty() -> Self { Self { grid_type: DocGridType::Default, line_pitch: None, char_space: None, } } pub fn grid_type(mut self, t: DocGridType) -> Self { self.grid_type = t; self } pub fn line_pitch(mut self, line_pitch: usize) -> Self { self.line_pitch = Some(line_pitch); self } pub fn char_space(mut self, char_space: isize) -> Self { self.char_space = Some(char_space); self } } impl Default for DocGrid { fn default() -> Self { Self { grid_type: DocGridType::Lines, line_pitch: Some(360), char_space: None, } } } impl BuildXML for DocGrid { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .doc_grid(&self.grid_type, self.line_pitch, self.char_space)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/doc_id.rs000064400000000000000000000014271046102023000174740ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct DocId { id: String, } impl DocId { pub fn new(id: impl Into) -> DocId { DocId { id: id.into() } } } impl Serialize for DocId { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.id) } } impl BuildXML for DocId { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let id = format!("{{{}}}", self.id); XMLBuilder::from(stream).doc_id(&id)?.into_inner() } } docx-rs-0.4.18/src/documents/elements/doc_var.rs000064400000000000000000000013031046102023000176610ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct DocVar { name: String, val: String, } impl DocVar { pub fn new(name: impl Into, val: impl Into) -> DocVar { DocVar { name: name.into(), val: val.into(), } } } impl BuildXML for DocVar { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .doc_var(&self.name, &self.val)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/drawing.rs000064400000000000000000000267741046102023000177220ustar 00000000000000use super::*; use serde::{ser::*, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Default, Serialize)] pub struct Drawing { #[serde(flatten)] pub data: Option, } #[derive(Debug, Clone, PartialEq)] pub enum DrawingData { Pic(Pic), TextBox(TextBox), } impl Serialize for DrawingData { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { DrawingData::Pic(ref pic) => { let mut t = serializer.serialize_struct("Pic", 2)?; t.serialize_field("type", "pic")?; t.serialize_field("data", pic)?; t.end() } DrawingData::TextBox(ref text_box) => { let mut t = serializer.serialize_struct("TextBox", 2)?; t.serialize_field("type", "textBox")?; t.serialize_field("data", text_box)?; t.end() } } } } impl Drawing { pub fn new() -> Drawing { Default::default() } pub fn pic(mut self, pic: Pic) -> Drawing { self.data = Some(DrawingData::Pic(pic)); self } pub fn text_box(mut self, t: TextBox) -> Drawing { self.data = Some(DrawingData::TextBox(t)); self } } impl BuildXML for Drawing { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let b = XMLBuilder::from(stream); let mut b = b.open_drawing()?; match &self.data { Some(DrawingData::Pic(p)) => { if let DrawingPositionType::Inline { .. } = p.position_type { b = b.open_wp_inline( &format!("{}", p.dist_t), &format!("{}", p.dist_b), &format!("{}", p.dist_l), &format!("{}", p.dist_r), )? } else { b = b .open_wp_anchor( &format!("{}", p.dist_t), &format!("{}", p.dist_b), &format!("{}", p.dist_l), &format!("{}", p.dist_r), "0", if p.simple_pos { "1" } else { "0" }, "0", "0", if p.layout_in_cell { "1" } else { "0" }, &format!("{}", p.relative_height), )? .simple_pos( &format!("{}", p.simple_pos_x), &format!("{}", p.simple_pos_y), )? .open_position_h(&format!("{}", p.relative_from_h))?; match p.position_h { DrawingPosition::Offset(x) => { let x = format!("{}", x as u32); b = b.pos_offset(&x)?.close()?; } DrawingPosition::Align(x) => { b = b.align(&x.to_string())?.close()?; } } b = b.open_position_v(&format!("{}", p.relative_from_v))?; match p.position_v { DrawingPosition::Offset(y) => { let y = format!("{}", y as u32); b = b.pos_offset(&y)?.close()?; } DrawingPosition::Align(a) => { b = b.align(&a.to_string())?.close()?; } } } let w = format!("{}", p.size.0); let h = format!("{}", p.size.1); b = b // Please see 20.4.2.7 extent (Drawing Object Size) // One inch equates to 914400 EMUs and a centimeter is 360000 .wp_extent(&w, &h)? .wp_effect_extent("0", "0", "0", "0")?; if p.allow_overlap { b = b.wrap_none()?; } else if p.position_type == DrawingPositionType::Anchor { b = b.wrap_square("bothSides")?; } b = b .wp_doc_pr("1", "Figure")? .open_wp_c_nv_graphic_frame_pr()? .a_graphic_frame_locks( "http://schemas.openxmlformats.org/drawingml/2006/main", "1", )? .close()? .open_a_graphic("http://schemas.openxmlformats.org/drawingml/2006/main")? .open_a_graphic_data( "http://schemas.openxmlformats.org/drawingml/2006/picture", )? .add_child(&p.clone())? .close()? .close()?; } Some(DrawingData::TextBox(_t)) => unimplemented!("TODO: Support textBox writer"), None => { unimplemented!() } } b.close()?.close()?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_drawing_build_with_pic() { let pic = Pic::new_with_dimensions(Vec::new(), 320, 240); let d = Drawing::new().pic(pic).build(); assert_eq!( str::from_utf8(&d).unwrap(), r#""# ); } #[test] fn test_drawing_build_with_pic_overlap() { let pic = Pic::new_with_dimensions(Vec::new(), 320, 240).overlapping(); let d = Drawing::new().pic(pic).build(); assert_eq!( str::from_utf8(&d).unwrap(), r#""# ); } #[test] fn test_drawing_build_with_pic_align_right() { let mut pic = Pic::new_with_dimensions(Vec::new(), 320, 240).floating(); pic = pic.relative_from_h(RelativeFromHType::Column); pic = pic.relative_from_v(RelativeFromVType::Paragraph); pic = pic.position_h(DrawingPosition::Align(PicAlign::Right)); let d = Drawing::new().pic(pic).build(); assert_eq!( str::from_utf8(&d).unwrap(), r#"right0"# ); } #[test] fn test_issue686() { let pic = Pic::new_with_dimensions(Vec::new(), 320, 240) .size(320 * 9525, 240 * 9525) .floating() .offset_x(300 * 9525) .offset_y(400 * 9525); let d = Drawing::new().pic(pic).build(); assert_eq!( str::from_utf8(&d).unwrap(), r#"28575003810000"# ); } } docx-rs-0.4.18/src/documents/elements/dstrike.rs000064400000000000000000000020101046102023000177050ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Dstrike { pub val: bool, } impl Dstrike { pub fn new() -> Dstrike { Default::default() } pub fn disable(mut self) -> Dstrike { self.val = false; self } } impl Default for Dstrike { fn default() -> Self { Self { val: true } } } impl Serialize for Dstrike { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for Dstrike { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { if self.val { XMLBuilder::from(stream).dstrike()?.into_inner() } else { XMLBuilder::from(stream).disable_dstrike()?.into_inner() } } } docx-rs-0.4.18/src/documents/elements/fld_char.rs000064400000000000000000000025451046102023000200170ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct FieldChar { pub field_char_type: FieldCharType, pub dirty: bool, } impl FieldChar { pub fn new(t: FieldCharType) -> Self { Self { field_char_type: t, dirty: false, } } pub fn dirty(mut self) -> Self { self.dirty = true; self } } impl BuildXML for FieldChar { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .field_character( &format!("{}", self.field_char_type), &format!("{}", &self.dirty), )? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_field_character() { let b = FieldChar::new(FieldCharType::Begin).dirty().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/font.rs000064400000000000000000000025031046102023000172150ustar 00000000000000use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use std::io::Write; #[derive(Debug)] pub struct Font<'a> { name: &'a str, charset: &'a str, family: &'a str, pitch: FontPitchType, } impl<'a> Font<'a> { pub fn new(name: &'a str, charset: &'a str, family: &'a str, pitch: FontPitchType) -> Font<'a> { Font { name, charset, family, pitch, } } } impl<'a> BuildXML for Font<'a> { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_font(self.name)? .charset(self.charset)? .family(self.family)? .pitch(&self.pitch.to_string())? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Font::new("Arial", "00", "swiss", FontPitchType::Variable); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/font_scheme.rs000064400000000000000000000016701046102023000205450ustar 00000000000000use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct FontSchemeFont { pub script: String, pub typeface: String, } #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct FontGroup { pub latin: String, pub ea: String, pub cs: String, pub fonts: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct FontScheme { pub major_font: FontGroup, pub minor_font: FontGroup, } // For now reader only impl FontScheme { pub fn new() -> Self { Self::default() } } docx-rs-0.4.18/src/documents/elements/footer_reference.rs000064400000000000000000000014221046102023000215620ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct FooterReference { pub footer_type: String, pub id: String, } impl FooterReference { pub fn new(t: impl Into, id: impl Into) -> FooterReference { FooterReference { footer_type: t.into(), id: id.into(), } } } impl BuildXML for FooterReference { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .footer_reference(&self.footer_type, &self.id)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/footnote.rs000064400000000000000000000046271046102023000201150ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::*; use footnote_id::generate_footnote_id; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Footnote { pub id: usize, pub content: Vec, } impl Default for Footnote { fn default() -> Self { Footnote { id: 1, content: vec![], } } } impl Footnote { pub fn new() -> Self { Self { id: generate_footnote_id(), ..Default::default() } } pub fn id(&self) -> usize { self.id } pub fn add_content(&mut self, p: Paragraph) -> Self { self.content.push(p); self.clone() } } impl From<&FootnoteReference> for Footnote { fn from(reference: &FootnoteReference) -> Self { Footnote { id: reference.id, content: reference.content.clone(), } } } impl BuildXML for Footnote { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { // To ensure docx compatible XML serialization for footnotes, we default to an empty paragraph. let mut footnote = self.clone(); if self.content == vec![] { eprintln!("{:?}", footnote); footnote.add_content(Paragraph::new()); } XMLBuilder::from(stream) .open_footnote(&format!("{}", self.id))? .add_children(&footnote.content)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_footnote_build_default() { let b = Footnote::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_footnote_build_with_paragraph() { let b = Footnote::new() .add_content(Paragraph::new().add_run(Run::new().add_text("hello"))) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"hello"# ); } } docx-rs-0.4.18/src/documents/elements/footnote_reference.rs000064400000000000000000000037161046102023000221310ustar 00000000000000use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::{xml_builder::*, Footnote, Paragraph}; #[derive(Debug, Clone, PartialEq)] pub struct FootnoteReference { pub id: usize, pub style: String, pub content: Vec, } impl FootnoteReference { pub fn new(id: usize) -> Self { FootnoteReference { id, style: "FootnoteReference".to_string(), content: vec![], } } /// Add footnote content as a Paragraph pub fn footnote(&mut self, p: Paragraph) { self.content.push(p) } } impl From for FootnoteReference { fn from(footnote: Footnote) -> Self { FootnoteReference { id: footnote.id, style: "FootnoteReference".to_string(), content: footnote.content, } } } impl BuildXML for FootnoteReference { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .footnote_reference(self.id)? .into_inner() } } impl Serialize for FootnoteReference { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut t = serializer.serialize_struct("FootnoteReference", 2)?; t.serialize_field("id", &self.id)?; t.end() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_footnotereference_build() { let b = FootnoteReference::new(1).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_footnotereference_json() { let t = FootnoteReference::new(1); assert_eq!(serde_json::to_string(&t).unwrap(), r#"{"id":1}"#); } } docx-rs-0.4.18/src/documents/elements/frame_property.rs000064400000000000000000000103701046102023000213060ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; // https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.frameproperties?view=openxml-3.0.1 #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] pub struct FrameProperty { /// Frame Height /// Represents the following attribute in the schema: w:h #[serde(skip_serializing_if = "Option::is_none")] pub h: Option, /// Frame Height Type /// Represents the following attribute in the schema: w:hRule #[serde(skip_serializing_if = "Option::is_none")] pub h_rule: Option, /// Frame Horizontal Positioning Base /// Represents the following attribute in the schema: w:hAnchor #[serde(skip_serializing_if = "Option::is_none")] pub h_anchor: Option, /// Horizontal Frame Padding /// Represents the following attribute in the schema: w:hSpace #[serde(skip_serializing_if = "Option::is_none")] pub h_space: Option, #[serde(skip_serializing_if = "Option::is_none")] pub v_anchor: Option, #[serde(skip_serializing_if = "Option::is_none")] pub v_space: Option, #[serde(skip_serializing_if = "Option::is_none")] pub w: Option, /// Text Wrapping Around Frame /// Represents the following attribute in the schema: w:wrap #[serde(skip_serializing_if = "Option::is_none")] pub wrap: Option, /// Absolute Horizontal Position /// Represents the following attribute in the schema: w:x #[serde(skip_serializing_if = "Option::is_none")] pub x: Option, /// Relative Horizontal Position /// Represents the following attribute in the schema: w:xAlign #[serde(skip_serializing_if = "Option::is_none")] pub x_align: Option, /// Absolute Vertical Position /// Represents the following attribute in the schema: w:y #[serde(skip_serializing_if = "Option::is_none")] pub y: Option, /// Relative Vertical Position /// Represents the following attribute in the schema: w:yAlign #[serde(skip_serializing_if = "Option::is_none")] pub y_align: Option, } impl FrameProperty { pub fn new() -> FrameProperty { Default::default() } pub fn wrap(mut self, wrap: impl Into) -> Self { self.wrap = Some(wrap.into()); self } pub fn v_anchor(mut self, anchor: impl Into) -> Self { self.v_anchor = Some(anchor.into()); self } pub fn h_anchor(mut self, anchor: impl Into) -> Self { self.h_anchor = Some(anchor.into()); self } pub fn h_rule(mut self, r: impl Into) -> Self { self.h_rule = Some(r.into()); self } pub fn x_align(mut self, align: impl Into) -> Self { self.x_align = Some(align.into()); self } pub fn y_align(mut self, align: impl Into) -> Self { self.y_align = Some(align.into()); self } pub fn h_space(mut self, x: i32) -> Self { self.h_space = Some(x); self } pub fn v_space(mut self, x: i32) -> Self { self.v_space = Some(x); self } pub fn x(mut self, x: i32) -> Self { self.x = Some(x); self } pub fn y(mut self, y: i32) -> Self { self.y = Some(y); self } pub fn width(mut self, n: u32) -> Self { self.w = Some(n); self } pub fn height(mut self, n: u32) -> Self { self.h = Some(n); self } } impl BuildXML for FrameProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).frame_property(self)?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_q_format() { let c = FrameProperty::new().wrap("none"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/grid_span.rs000064400000000000000000000017741046102023000202260ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct GridSpan { val: usize, } impl GridSpan { pub fn new(v: usize) -> GridSpan { GridSpan { val: v } } } impl BuildXML for GridSpan { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).grid_span(self.val)?.into_inner() } } impl Serialize for GridSpan { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.val as u32) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_grid_span() { let b = GridSpan::new(3).build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/header_reference.rs000064400000000000000000000017321046102023000215200ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct HeaderReference { pub header_type: String, pub id: String, } impl Default for HeaderReference { fn default() -> HeaderReference { HeaderReference { header_type: "default".to_owned(), id: "rId4".to_owned(), } } } impl HeaderReference { pub fn new(t: impl Into, id: impl Into) -> HeaderReference { HeaderReference { header_type: t.into(), id: id.into(), } } } impl BuildXML for HeaderReference { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .header_reference(&self.header_type, &self.id)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/highlight.rs000064400000000000000000000024261046102023000202220ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Highlight { val: String, } impl Highlight { pub fn new(val: impl Into) -> Highlight { Highlight { val: val.into() } } } impl BuildXML for Highlight { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).highlight(&self.val)?.into_inner() } } impl Serialize for Highlight { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_highlight() { let c = Highlight::new("FFFFFF"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_highlight_json() { let c = Highlight::new("FFFFFF"); assert_eq!(serde_json::to_string(&c).unwrap(), r#""FFFFFF""#); } } docx-rs-0.4.18/src/documents/elements/hyperlink.rs000064400000000000000000000077121046102023000202630ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::escape::escape; use crate::types::*; use crate::{create_hyperlink_rid, generate_hyperlink_id, xml_builder::*}; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(tag = "type")] #[serde(rename_all = "camelCase")] pub enum HyperlinkData { External { rid: String, // path is writer only #[serde(skip_serializing_if = "String::is_empty")] path: String, }, Anchor { anchor: String, }, } #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Hyperlink { #[serde(flatten)] pub link: HyperlinkData, pub history: Option, pub children: Vec, } impl Hyperlink { pub fn new(value: impl Into, t: HyperlinkType) -> Self { let link = { match t { HyperlinkType::External => HyperlinkData::External { rid: create_hyperlink_rid(generate_hyperlink_id()), path: escape(&value.into()), }, HyperlinkType::Anchor => HyperlinkData::Anchor { anchor: value.into(), }, } }; Hyperlink { link, history: None, children: vec![], } } pub fn add_run(mut self, run: Run) -> Self { self.children.push(ParagraphChild::Run(Box::new(run))); self } pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { self.children .push(ParagraphChild::StructuredDataTag(Box::new(t))); self } pub fn add_insert(mut self, insert: Insert) -> Self { self.children.push(ParagraphChild::Insert(insert)); self } pub fn add_delete(mut self, delete: Delete) -> Self { self.children.push(ParagraphChild::Delete(delete)); self } pub fn add_bookmark_start(mut self, id: usize, name: impl Into) -> Self { self.children .push(ParagraphChild::BookmarkStart(BookmarkStart::new(id, name))); self } pub fn add_bookmark_end(mut self, id: usize) -> Self { self.children .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id))); self } pub fn add_comment_start(mut self, comment: Comment) -> Self { self.children.push(ParagraphChild::CommentStart(Box::new( CommentRangeStart::new(comment), ))); self } pub fn add_comment_end(mut self, id: usize) -> Self { self.children .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id))); self } } impl BuildXML for Hyperlink { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .apply(|b| match self.link { HyperlinkData::Anchor { ref anchor } => b.open_hyperlink( None, Some(anchor.clone()).as_ref(), Some(self.history.unwrap_or(1)), ), HyperlinkData::External { ref rid, .. } => b.open_hyperlink( Some(rid.clone()).as_ref(), None, Some(self.history.unwrap_or(1)), ), })? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_hyperlink() { let l = Hyperlink::new("ToC1", HyperlinkType::Anchor).add_run(Run::new().add_text("hello")); let b = l.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"hello"# ); } } docx-rs-0.4.18/src/documents/elements/indent.rs000064400000000000000000000061111046102023000175270ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct Indent { pub start: Option, pub end: Option, pub special_indent: Option, pub start_chars: Option, // Internal, for reading pub hanging_chars: Option, pub first_line_chars: Option, } impl Indent { pub fn new( start: Option, special_indent: Option, end: Option, start_chars: Option, ) -> Indent { Indent { start, start_chars, end, special_indent, // Internal, for reading hanging_chars: None, first_line_chars: None, } } pub fn end(mut self, end: i32) -> Self { self.end = Some(end); self } pub fn hanging_chars(mut self, chars: i32) -> Self { self.hanging_chars = Some(chars); self } pub fn first_line_chars(mut self, chars: i32) -> Self { self.first_line_chars = Some(chars); self } } impl BuildXML for Indent { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .indent( self.start, self.special_indent, self.end.unwrap_or_default(), self.start_chars, )? .into_inner() } } 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("startChars", &self.start_chars)?; t.serialize_field("end", &self.end)?; t.serialize_field("specialIndent", &self.special_indent)?; t.serialize_field("hangingChars", &self.hanging_chars)?; t.serialize_field("firstLineChars", &self.first_line_chars)?; t.end() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_left() { let b = Indent::new(Some(20), None, None, None).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_first_line() { let b = Indent::new(Some(20), Some(SpecialIndentType::FirstLine(40)), None, None).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_hanging() { let b = Indent::new(Some(20), Some(SpecialIndentType::Hanging(50)), None, None).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/indent_level.rs000064400000000000000000000015301046102023000207160ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; #[derive(Debug, Clone, PartialEq)] pub struct IndentLevel { pub val: usize, } impl IndentLevel { pub fn new(val: usize) -> IndentLevel { IndentLevel { val } } } impl BuildXML for IndentLevel { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .indent_level(self.val)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_indent_level() { let c = IndentLevel::new(20); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/insert.rs000064400000000000000000000111601046102023000175520ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::{BuildXML, HistoryId, Run}; use crate::{escape, xml_builder::*}; #[derive(Debug, Clone, PartialEq)] pub enum InsertChild { Run(Box), Delete(Delete), CommentStart(Box), CommentEnd(CommentRangeEnd), } impl BuildXML for InsertChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { InsertChild::Run(v) => v.build_to(stream), InsertChild::Delete(v) => v.build_to(stream), InsertChild::CommentStart(v) => v.build_to(stream), InsertChild::CommentEnd(v) => v.build_to(stream), } } } impl Serialize for InsertChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { InsertChild::Run(ref r) => { let mut t = serializer.serialize_struct("Run", 2)?; t.serialize_field("type", "run")?; t.serialize_field("data", r)?; t.end() } InsertChild::Delete(ref r) => { let mut t = serializer.serialize_struct("Delete", 2)?; t.serialize_field("type", "delete")?; t.serialize_field("data", r)?; t.end() } InsertChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } InsertChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } } } } #[derive(Serialize, Debug, Clone, PartialEq)] pub struct Insert { pub children: Vec, pub author: String, pub date: String, } impl Default for Insert { fn default() -> Insert { Insert { author: "unnamed".to_owned(), date: "1970-01-01T00:00:00Z".to_owned(), children: vec![], } } } impl Insert { pub fn new(run: Run) -> Insert { Self { children: vec![InsertChild::Run(Box::new(run))], ..Default::default() } } pub fn new_with_empty() -> Insert { Self { ..Default::default() } } pub fn new_with_del(del: Delete) -> Insert { Self { children: vec![InsertChild::Delete(del)], ..Default::default() } } pub fn add_run(mut self, run: Run) -> Insert { self.children.push(InsertChild::Run(Box::new(run))); self } pub fn add_delete(mut self, del: Delete) -> Insert { self.children.push(InsertChild::Delete(del)); self } pub fn add_child(mut self, c: InsertChild) -> Insert { self.children.push(c); self } pub fn add_comment_start(mut self, comment: Comment) -> Self { self.children .push(InsertChild::CommentStart(Box::new(CommentRangeStart::new( comment, )))); self } pub fn add_comment_end(mut self, id: usize) -> Self { self.children .push(InsertChild::CommentEnd(CommentRangeEnd::new(id))); self } pub fn author(mut self, author: impl Into) -> Insert { self.author = escape::escape(&author.into()); self } pub fn date(mut self, date: impl Into) -> Insert { self.date = date.into(); self } } impl HistoryId for Insert {} impl BuildXML for Insert { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_insert(&self.generate(), &self.author, &self.date)? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_ins_default() { let b = Insert::new(Run::new()).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/instr_hyperlink.rs000064400000000000000000000036451046102023000215030ustar 00000000000000use serde::Serialize; // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_HYPERLINKHYPERLINK_topic_ID0EFYG1.html #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct InstrHyperlink { pub target: String, // \l pub anchor: bool, } impl InstrHyperlink { pub fn new(target: impl Into) -> Self { Self { target: target.into(), ..Default::default() } } } // impl BuildXML for instrHyperlink { // fn build(&self) -> Vec { // TODO: // } // } impl std::str::FromStr for InstrHyperlink { type Err = (); fn from_str(instr: &str) -> Result { let mut s = instr.split(' '); let mut target = "".to_string(); let mut anchor = false; loop { if let Some(i) = s.next() { match i { "\\l" => { anchor = true; } "\\m" => { // TODO: } "\\n" => { // TODO: } "\\o" => { // TODO: Support later let _ = s.next(); } "\\t" => { // TODO: Support later let _ = s.next(); } _ => { target = i.replace(""", "").replace('\"', "").to_string(); } } } else { // FIXME: For now, return error if target is not found if target.is_empty() { return Err(()); } return Ok(Self { target, anchor }); } } } } docx-rs-0.4.18/src/documents/elements/instr_num_pages.rs000064400000000000000000000011061046102023000214420ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::XMLBuilder; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct InstrNUMPAGES {} impl InstrNUMPAGES { pub fn new() -> Self { Self {} } } impl BuildXML for InstrNUMPAGES { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .plain_text("NUMPAGES")? .into_inner() } } docx-rs-0.4.18/src/documents/elements/instr_page.rs000064400000000000000000000010341046102023000204000ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::XMLBuilder; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct InstrPAGE {} impl InstrPAGE { pub fn new() -> Self { Self {} } } impl BuildXML for InstrPAGE { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).plain_text("PAGE")?.into_inner() } } docx-rs-0.4.18/src/documents/elements/instr_pageref.rs000064400000000000000000000041621046102023000211020ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::XMLBuilder; // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_PAGEREFPAGEREF_topic_ID0EHXK1.html #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct InstrPAGEREF { pub page_ref: String, pub hyperlink: bool, pub relative_position: bool, } impl InstrPAGEREF { pub fn new(r: impl Into) -> Self { Self { page_ref: r.into(), ..Default::default() } } pub fn hyperlink(mut self) -> Self { self.hyperlink = true; self } pub fn relative_position(mut self) -> Self { self.relative_position = true; self } } impl BuildXML for InstrPAGEREF { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .plain_text("PAGEREF ")? .plain_text(&self.page_ref)? .apply_if(self.relative_position, |b| b.plain_text(" \\p"))? .apply_if(self.hyperlink, |b| b.plain_text(" \\h"))? .into_inner() } } impl std::str::FromStr for InstrPAGEREF { type Err = (); fn from_str(instr: &str) -> Result { let mut s = instr.split(' '); let text = s.next(); let mut page_ref = InstrPAGEREF::new(text.unwrap_or_default()); loop { if let Some(i) = s.next() { match i { "\\h" => page_ref = page_ref.hyperlink(), "\\p" => page_ref = page_ref.relative_position(), _ => {} } } else { return Ok(page_ref); } } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_page_ref() { let b = InstrPAGEREF::new("_Toc00000000").hyperlink().build(); assert_eq!(str::from_utf8(&b).unwrap(), r#"PAGEREF _Toc00000000 \h"#); } } docx-rs-0.4.18/src/documents/elements/instr_tc.rs000064400000000000000000000055431046102023000201030ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::escape::escape; use crate::xml_builder::XMLBuilder; // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TCTC_topic_ID0EU2N1.html #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct InstrTC { pub text: String, // \n Omits the page number for the entry. pub omits_page_number: bool, pub level: Option, // \f pub item_type_identifier: Option, } impl InstrTC { pub fn new(text: impl Into) -> Self { Self { text: escape(&text.into()), ..Default::default() } } pub fn omits_page_number(mut self) -> Self { self.omits_page_number = true; self } pub fn level(mut self, level: usize) -> Self { self.level = Some(level); self } pub fn item_type_identifier(mut self, t: impl Into) -> Self { self.item_type_identifier = Some(t.into()); self } } impl BuildXML for InstrTC { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut b = XMLBuilder::from(stream); let raw = b.inner_mut()?; write!(raw, "TC \"{}\"", self.text)?; if let Some(ref t) = self.item_type_identifier { write!(raw, " \\f {}", t)?; } if let Some(level) = self.level { write!(raw, " \\l {}", level)?; } if self.omits_page_number { write!(raw, " \\n")?; } b.into_inner() } } fn parse_level(i: &str) -> Option { let r = i.replace(""", "").replace('\"', ""); if let Ok(l) = usize::from_str(&r) { return Some(l); } None } impl std::str::FromStr for InstrTC { type Err = (); fn from_str(instr: &str) -> Result { let mut s = instr.split(' '); let text = s.next(); let mut tc = InstrTC::new(text.unwrap_or_default()); loop { if let Some(i) = s.next() { match i { "\\f" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); tc = tc.item_type_identifier(r); } } "\\l" => { if let Some(r) = s.next() { if let Some(l) = parse_level(r) { tc = tc.level(l); } } } "\\n" => tc = tc.omits_page_number(), _ => {} } } else { return Ok(tc); } } } } docx-rs-0.4.18/src/documents/elements/instr_text.rs000064400000000000000000000075311046102023000204600ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub enum InstrText { TOC(InstrToC), TC(InstrTC), PAGE(InstrPAGE), NUMPAGES(InstrNUMPAGES), PAGEREF(InstrPAGEREF), HYPERLINK(InstrHyperlink), Unsupported(String), } impl BuildXML for Box { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_instr_text()? .apply(|b| match self.as_ref() { InstrText::TOC(toc) => b.add_child(toc), InstrText::TC(tc) => b.add_child(tc), InstrText::PAGEREF(page_ref) => b.add_child(page_ref), InstrText::PAGE(page) => b.add_child(page), InstrText::NUMPAGES(page) => b.add_child(page), InstrText::HYPERLINK(_link) => todo!(), InstrText::Unsupported(s) => b.plain_text(s), })? .close()? .into_inner() } } impl Serialize for InstrText { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { InstrText::TOC(ref s) => { let mut t = serializer.serialize_struct("TOC", 2)?; t.serialize_field("type", "toc")?; t.serialize_field("data", s)?; t.end() } InstrText::TC(ref s) => { let mut t = serializer.serialize_struct("TC", 2)?; t.serialize_field("type", "tc")?; t.serialize_field("data", s)?; t.end() } InstrText::PAGEREF(ref s) => { let mut t = serializer.serialize_struct("PAGEREF", 2)?; t.serialize_field("type", "pageref")?; t.serialize_field("data", s)?; t.end() } InstrText::PAGE(ref s) => { let mut t = serializer.serialize_struct("PAGE", 2)?; t.serialize_field("type", "page")?; t.serialize_field("data", s)?; t.end() } InstrText::NUMPAGES(ref s) => { let mut t = serializer.serialize_struct("NUMPAGES", 2)?; t.serialize_field("type", "numPages")?; t.serialize_field("data", s)?; t.end() } InstrText::HYPERLINK(ref s) => { let mut t = serializer.serialize_struct("HYPERLINK", 2)?; t.serialize_field("type", "hyperlink")?; t.serialize_field("data", s)?; t.end() } InstrText::Unsupported(ref s) => { let mut t = serializer.serialize_struct("Unsupported", 2)?; t.serialize_field("type", "unsupported")?; t.serialize_field("data", s)?; t.end() } } } } #[allow(unused_allocation)] #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_toc_instr() { #[allow(unused_allocation)] let b = Box::new(InstrText::TOC(InstrToC::new().heading_styles_range(1, 3))).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""# ); } #[test] fn test_pageref_instr() { #[allow(unused_allocation)] let b = Box::new(InstrText::PAGEREF( InstrPAGEREF::new("_Toc90425847").hyperlink(), )) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"PAGEREF _Toc90425847 \h"# ); } } docx-rs-0.4.18/src/documents/elements/instr_toc.rs000064400000000000000000000417441046102023000202650ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::xml_builder::XMLBuilder; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] pub struct StyleWithLevel(pub (String, usize)); impl StyleWithLevel { pub fn new(s: impl Into, l: usize) -> Self { Self((s.into(), l)) } } // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TOCTOC_topic_ID0ELZO1.html #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct InstrToC { // \o If no heading range is specified, all heading levels used in the document are listed. #[serde(skip_serializing_if = "Option::is_none")] pub heading_styles_range: Option<(usize, usize)>, // \l Includes TC fields that assign entries to one of the levels specified by text in this switch's field-argument as a range having the form startLevel-endLevel, // where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel. // TC fields that assign entries to lower levels are skipped. #[serde(skip_serializing_if = "Option::is_none")] pub tc_field_level_range: Option<(usize, usize)>, // \n Without field-argument, omits page numbers from the table of contents. // .Page numbers are omitted from all levels unless a range of entry levels is specified by text in this switch's field-argument. // A range is specified as for \l. #[serde(skip_serializing_if = "Option::is_none")] pub omit_page_numbers_level_range: Option<(usize, usize)>, // \b includes entries only from the portion of the document marked by the bookmark named by text in this switch's field-argument. #[serde(skip_serializing_if = "Option::is_none")] pub entry_bookmark_name: Option, // \t Uses paragraphs formatted with styles other than the built-in heading styles. // . text in this switch's field-argument specifies those styles as a set of comma-separated doublets, // with each doublet being a comma-separated set of style name and table of content level. \t can be combined with \o. pub styles_with_levels: Vec, // struct S texWin Lis switch's field-argument specifies a sequence of character // . The default is a tab with leader dots. #[serde(skip_serializing_if = "Option::is_none")] pub entry_and_page_number_separator: Option, // \d #[serde(skip_serializing_if = "Option::is_none")] pub sequence_and_page_numbers_separator: Option, // \a pub caption_label: Option, // \c #[serde(skip_serializing_if = "Option::is_none")] pub caption_label_including_numbers: Option, // \s #[serde(skip_serializing_if = "Option::is_none")] pub seq_field_identifier_for_prefix: Option, // \f #[serde(skip_serializing_if = "Option::is_none")] pub tc_field_identifier: Option>, // \h pub hyperlink: bool, // \w pub preserve_tab: bool, // \x pub preserve_new_line: bool, // \u pub use_applied_paragraph_line_level: bool, // \z Hides tab leader and page numbers in Web layout view. pub hide_tab_and_page_numbers_in_webview: bool, } impl InstrToC { pub fn new() -> Self { Self::default() } pub fn with_instr_text(s: &str) -> Self { Self::from_str(s).expect("should convert to InstrToC") } pub fn heading_styles_range(mut self, start: usize, end: usize) -> Self { self.heading_styles_range = Some((start, end)); self } pub fn tc_field_level_range(mut self, start: usize, end: usize) -> Self { self.tc_field_level_range = Some((start, end)); self } pub fn tc_field_identifier(mut self, t: Option) -> Self { self.tc_field_identifier = Some(t); self } pub fn omit_page_numbers_level_range(mut self, start: usize, end: usize) -> Self { self.omit_page_numbers_level_range = Some((start, end)); self } pub fn entry_and_page_number_separator(mut self, t: impl Into) -> Self { self.entry_and_page_number_separator = Some(t.into()); self } pub fn entry_bookmark_name(mut self, t: impl Into) -> Self { self.entry_bookmark_name = Some(t.into()); self } pub fn caption_label(mut self, t: impl Into) -> Self { self.caption_label = Some(t.into()); self } pub fn caption_label_including_numbers(mut self, t: impl Into) -> Self { self.caption_label_including_numbers = Some(t.into()); self } pub fn sequence_and_page_numbers_separator(mut self, t: impl Into) -> Self { self.sequence_and_page_numbers_separator = Some(t.into()); self } pub fn seq_field_identifier_for_prefix(mut self, t: impl Into) -> Self { self.seq_field_identifier_for_prefix = Some(t.into()); self } pub fn hyperlink(mut self) -> Self { self.hyperlink = true; self } pub fn preserve_tab(mut self) -> Self { self.preserve_tab = true; self } pub fn preserve_new_line(mut self) -> Self { self.preserve_new_line = true; self } pub fn use_applied_paragraph_line_level(mut self) -> Self { self.use_applied_paragraph_line_level = true; self } pub fn hide_tab_and_page_numbers_in_webview(mut self) -> Self { self.hide_tab_and_page_numbers_in_webview = true; self } pub fn add_style_with_level(mut self, s: StyleWithLevel) -> Self { self.styles_with_levels.push(s); self } } impl BuildXML for InstrToC { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut b = XMLBuilder::from(stream); let raw = b.inner_mut()?; write!(raw, "TOC")?; // \a if let Some(ref t) = self.caption_label { write!(raw, " \\a "{}"", t)?; } // \b if let Some(ref t) = self.entry_bookmark_name { write!(raw, " \\b "{}"", t)?; } // \c if let Some(ref t) = self.caption_label_including_numbers { write!(raw, " \\c "{}"", t)?; } // \d if let Some(ref t) = self.sequence_and_page_numbers_separator { write!(raw, " \\d "{}"", t)?; } // \f if let Some(ref t) = self.tc_field_identifier { if let Some(ref t) = t { write!(raw, " \\f "{}"", t)?; } else { write!(raw, " \\f")?; } } // \l if let Some(range) = self.tc_field_level_range { write!(raw, " \\l "{}-{}"", range.0, range.1)?; } // \n if let Some(range) = self.omit_page_numbers_level_range { write!(raw, " \\n "{}-{}"", range.0, range.1)?; } // \o if let Some(range) = self.heading_styles_range { write!(raw, " \\o "{}-{}"", range.0, range.1)?; } // \p if let Some(ref t) = self.entry_and_page_number_separator { write!(raw, " \\p "{}"", t)?; } // \s if let Some(ref t) = self.seq_field_identifier_for_prefix { write!(raw, " \\s "{}"", t)?; } // \t if !self.styles_with_levels.is_empty() { let s = self .styles_with_levels .iter() .map(|s| format!("{},{}", (s.0).0, (s.0).1)) .collect::>() .join(","); write!(raw, " \\t "{}"", s)?; } // \h if self.hyperlink { write!(raw, " \\h")?; } // \u if self.use_applied_paragraph_line_level { write!(raw, " \\u")?; } // \w if self.preserve_tab { write!(raw, " \\w")?; } // \x if self.preserve_new_line { write!(raw, " \\x")?; } // \z if self.hide_tab_and_page_numbers_in_webview { write!(raw, " \\z")?; } b.into_inner() } } fn parse_level_range(i: &str) -> Option<(usize, usize)> { let r = i.replace(""", "").replace('\"', ""); let r: Vec<&str> = r.split('-').collect(); if let Some(s) = r.first() { if let Ok(s) = usize::from_str(s) { if let Some(e) = r.get(1) { if let Ok(e) = usize::from_str(e) { return Some((s, e)); } } } } None } impl std::str::FromStr for InstrToC { type Err = (); fn from_str(instr: &str) -> Result { let mut s = instr.split(' ').peekable(); let mut toc = InstrToC::new(); loop { if let Some(i) = s.next() { match i { "\\a" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.caption_label(r); } } "\\b" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.entry_bookmark_name(r); } } "\\c" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.caption_label_including_numbers(r); } } "\\d" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.sequence_and_page_numbers_separator(r); } } "\\f" => { if let Some(n) = s.peek() { if !n.starts_with("\\") { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); if r.is_empty() { toc = toc.tc_field_identifier(None); } else { toc = toc.tc_field_identifier(Some(r)); } } } else { toc = toc.tc_field_identifier(None); } } } "\\h" => toc = toc.hyperlink(), "\\l" => { if let Some(r) = s.next() { if let Some((s, e)) = parse_level_range(r) { toc = toc.tc_field_level_range(s, e); } } } "\\n" => { if let Some(r) = s.next() { if let Some((s, e)) = parse_level_range(r) { toc = toc.omit_page_numbers_level_range(s, e); } } } "\\o" => { if let Some(r) = s.next() { if let Some((s, e)) = parse_level_range(r) { toc = toc.heading_styles_range(s, e); } } } "\\p" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.entry_and_page_number_separator(r); } } "\\s" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); toc = toc.seq_field_identifier_for_prefix(r); } } "\\t" => { if let Some(r) = s.next() { let r = r.replace(""", "").replace('\"', ""); let mut r = r.split(','); loop { if let Some(style) = r.next() { if let Some(level) = r.next() { if let Ok(level) = usize::from_str(level) { toc = toc.add_style_with_level(StyleWithLevel(( style.to_string(), level, ))); continue; } } } break; } } } "\\u" => toc = toc.use_applied_paragraph_line_level(), "\\w" => toc = toc.preserve_tab(), "\\x" => toc = toc.preserve_new_line(), "\\z" => toc = toc.hide_tab_and_page_numbers_in_webview(), _ => {} } } else { return Ok(toc); } } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_toc() { let b = InstrToC::new().heading_styles_range(1, 3).build(); assert_eq!(str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""#); } #[test] fn test_toc_with_styles() { let b = InstrToC::new() .heading_styles_range(1, 3) .add_style_with_level(StyleWithLevel::new("style1", 2)) .add_style_with_level(StyleWithLevel::new("style2", 3)) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3" \t "style1,2,style2,3""# ); } #[test] fn read_toc_with_o_and_h() { let i = r#"TOC \o "1-3" \h"#; let i = InstrToC::from_str(i).unwrap(); assert_eq!(i, InstrToC::new().heading_styles_range(1, 3).hyperlink()); } #[test] fn read_toc_with_l_and_n() { let i = r#"TOC \o "1-3" \l "4-5" \n "1-4" \h"#; let i = InstrToC::from_str(i).unwrap(); assert_eq!( i, InstrToC::new() .heading_styles_range(1, 3) .hyperlink() .omit_page_numbers_level_range(1, 4) .tc_field_level_range(4, 5) ); } #[test] fn read_toc_with_a_and_b_and_t() { let i = r#"TOC \a "hoge" \b "test" \o "1-3" \t "MySpectacularStyle,1,MySpectacularStyle2,4""#; let i = InstrToC::from_str(i).unwrap(); assert_eq!( i, InstrToC::new() .caption_label("hoge") .entry_bookmark_name("test") .heading_styles_range(1, 3) .add_style_with_level(StyleWithLevel::new("MySpectacularStyle", 1)) .add_style_with_level(StyleWithLevel::new("MySpectacularStyle2", 4)) ); } #[test] fn with_instr_text() { let s = r#"TOC \o "1-3" \h \z \u"#; let i = InstrToC::with_instr_text(s); assert_eq!( i, InstrToC::new() .heading_styles_range(1, 3) .use_applied_paragraph_line_level() .hide_tab_and_page_numbers_in_webview() .hyperlink() ); } #[test] fn with_instr_text2() { let s = r#"TOC \f \h \z \u"#; let i = InstrToC::with_instr_text(s); assert_eq!( i, InstrToC::new() .tc_field_identifier(None) .use_applied_paragraph_line_level() .hide_tab_and_page_numbers_in_webview() .hyperlink() ); } #[test] fn with_instr_text3() { let s = r#"TOC \f abc \h \z \u"#; let i = InstrToC::with_instr_text(s); assert_eq!( i, InstrToC::new() .tc_field_identifier(Some("abc".to_string())) .use_applied_paragraph_line_level() .hide_tab_and_page_numbers_in_webview() .hyperlink() ); } } docx-rs-0.4.18/src/documents/elements/is_lgl.rs000064400000000000000000000012701046102023000175200ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq, Default)] pub struct IsLgl {} impl IsLgl { pub fn new() -> IsLgl { IsLgl {} } } impl BuildXML for IsLgl { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).is_lgl()?.into_inner() } } impl Serialize for IsLgl { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(true) } } docx-rs-0.4.18/src/documents/elements/italic.rs000064400000000000000000000017641046102023000175240ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Italic { val: bool, } impl Italic { pub fn new() -> Italic { Default::default() } pub fn disable(mut self) -> Self { self.val = false; self } } impl Default for Italic { fn default() -> Self { Self { val: true } } } impl Serialize for Italic { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for Italic { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { if self.val { XMLBuilder::from(stream).i()?.into_inner() } else { XMLBuilder::from(stream).disable_italic()?.into_inner() } } } docx-rs-0.4.18/src/documents/elements/italic_cs.rs000064400000000000000000000016121046102023000202010ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct ItalicCs { val: bool, } impl ItalicCs { pub fn new() -> ItalicCs { Default::default() } pub fn disable(mut self) -> Self { self.val = false; self } } impl Default for ItalicCs { fn default() -> Self { Self { val: true } } } impl Serialize for ItalicCs { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for ItalicCs { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).i_cs()?.into_inner() } } docx-rs-0.4.18/src/documents/elements/justification.rs000064400000000000000000000033441046102023000211260ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; // 22.1.2.51 // jc (Justification) // This element specifies justification of the math paragraph (a series of adjacent instances of mathematical text // within the same paragraph). A math paragraph can be Left Justified, Right Justified, Centered, or Centered as // Group. If this element is omitted, the math paragraph is Centered as Group. Whether the element is absent or // 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, PartialEq)] pub struct Justification { pub val: String, } impl Justification { pub fn new(val: impl Into) -> Justification { Justification { val: val.into() } } } impl BuildXML for Justification { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .justification(&self.val)? .into_inner() } } impl Serialize for Justification { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_justification() { let c = Justification::new("start"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/level.rs000064400000000000000000000157651046102023000173740ustar 00000000000000use crate::documents::*; use crate::types::*; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Level { pub level: usize, pub start: Start, pub format: NumberFormat, pub text: LevelText, pub jc: LevelJc, pub paragraph_property: ParagraphProperty, pub run_property: RunProperty, pub suffix: LevelSuffixType, pub pstyle: Option, pub level_restart: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_lgl: Option, } impl Level { pub fn new( level: usize, start: Start, format: NumberFormat, text: LevelText, jc: LevelJc, ) -> Level { Self { level, start, format, text, jc, paragraph_property: ParagraphProperty::new(), run_property: RunProperty::new(), suffix: LevelSuffixType::Tab, pstyle: None, level_restart: None, is_lgl: None, } } pub fn indent( mut self, left: Option, special_indent: Option, end: Option, start_chars: Option, ) -> Self { self.paragraph_property = self.paragraph_property .indent(left, special_indent, end, start_chars); self } pub fn paragraph_style(mut self, style_id: impl Into) -> Self { self.pstyle = Some(ParagraphStyle::new(Some(style_id.into()))); self } pub fn suffix(mut self, s: LevelSuffixType) -> Self { self.suffix = s; self } // run property pub fn size(mut self, size: usize) -> Self { self.run_property = self.run_property.size(size); self } pub fn spacing(mut self, v: i32) -> Self { self.run_property = self.run_property.spacing(v); self } pub fn color(mut self, color: impl Into) -> Self { self.run_property = self.run_property.color(color); self } pub fn highlight(mut self, color: impl Into) -> Self { self.run_property = self.run_property.highlight(color); self } pub fn bold(mut self) -> Self { self.run_property = self.run_property.bold(); self } pub fn disable_bold(mut self) -> Self { self.run_property = self.run_property.disable_bold(); self } pub fn italic(mut self) -> Self { self.run_property = self.run_property.italic(); self } pub fn disable_italic(mut self) -> Self { self.run_property = self.run_property.disable_italic(); self } pub fn strike(mut self) -> Self { self.run_property = self.run_property.strike(); self } pub fn disable_strike(mut self) -> Self { self.run_property = self.run_property.disable_strike(); self } pub fn dstrike(mut self) -> Self { self.run_property = self.run_property.dstrike(); self } pub fn disable_dstrike(mut self) -> Self { self.run_property = self.run_property.disable_dstrike(); self } pub fn underline(mut self, line_type: impl Into) -> Self { self.run_property = self.run_property.underline(line_type); self } pub fn vanish(mut self) -> Self { self.run_property = self.run_property.vanish(); self } pub fn fonts(mut self, f: RunFonts) -> Self { self.run_property = self.run_property.fonts(f); self } pub fn level_restart(mut self, v: u32) -> Self { self.level_restart = Some(LevelRestart::new(v)); self } pub fn is_lgl(mut self) -> Self { self.is_lgl = Some(IsLgl::new()); self } } impl BuildXML for Level { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_level(&format!("{}", self.level))? .add_child(&self.start)? .add_child(&self.format)? .add_child(&self.text)? .add_child(&self.jc)? .add_child(&self.paragraph_property)? .add_child(&self.run_property)? .add_optional_child(&self.pstyle)? .add_optional_child(&self.level_restart)? .add_optional_child(&self.is_lgl)? .apply_if(self.suffix != LevelSuffixType::Tab, |b| { b.suffix(&self.suffix.to_string()) })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_level() { let b = Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_level_indent() { let b = Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ) .indent(Some(320), Some(SpecialIndentType::Hanging(200)), None, None) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_level_with_suff() { let b = Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ) .suffix(LevelSuffixType::Space) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_level_with_pstyle() { let b = Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ) .paragraph_style("a-style") .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/level_jc.rs000064400000000000000000000020751046102023000200360ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct LevelJc { val: String, } impl LevelJc { pub fn new(val: impl Into) -> Self { Self { val: val.into() } } } impl BuildXML for LevelJc { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .level_justification(&self.val)? .into_inner() } } impl Serialize for LevelJc { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_level_jc() { let c = LevelJc::new("left"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/level_override.rs000064400000000000000000000052571046102023000212660ustar 00000000000000use super::*; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; use serde::Serialize; /* 17.9.8 lvlOverride (Numbering Level Definition Override) This element specifies an optional override which shall be applied in place of zero or more levels from the abstract numbering definition for a given numbering definition instance. Each instance of this element is used to override the appearance and behavior of a given numbering level definition within the given abstract numbering definition. */ #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct LevelOverride { pub level: usize, pub override_start: Option, pub override_level: Option, } impl LevelOverride { pub fn new(level: usize) -> LevelOverride { LevelOverride { level, override_start: None, override_level: None, } } pub fn start(mut self, start: usize) -> LevelOverride { self.override_start = Some(start); self } pub fn level(mut self, override_level: Level) -> LevelOverride { self.override_level = Some(override_level); self } } impl BuildXML for LevelOverride { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_level_override(&format!("{}", self.level))? .add_optional_child(&self.override_level)? .apply_opt(self.override_start, |start, b| { b.start_override(&format!("{}", start)) })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_level_override() { let c = LevelOverride::new(1).start(2); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_override_with_lvl() { let lvl = Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ); let c = LevelOverride::new(1).level(lvl); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/level_restart.rs000064400000000000000000000014371046102023000211270ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct LevelRestart { val: u32, } impl LevelRestart { pub fn new(val: impl Into) -> Self { Self { val: val.into() } } } impl BuildXML for LevelRestart { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .level_restart(&format!("{}", &self.val))? .into_inner() } } impl Serialize for LevelRestart { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.val) } } docx-rs-0.4.18/src/documents/elements/level_text.rs000064400000000000000000000020461046102023000204240ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct LevelText { val: String, } impl LevelText { pub fn new(val: impl Into) -> Self { Self { val: val.into() } } } impl BuildXML for LevelText { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).level_text(&self.val)?.into_inner() } } impl Serialize for LevelText { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_level_text() { let c = LevelText::new("%4."); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/line_spacing.rs000064400000000000000000000061061046102023000207050ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use crate::line_spacing_type::LineSpacingType; use serde::*; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct LineSpacing { #[serde(skip_serializing_if = "Option::is_none")] line_rule: Option, #[serde(skip_serializing_if = "Option::is_none")] before: Option, #[serde(skip_serializing_if = "Option::is_none")] after: Option, #[serde(skip_serializing_if = "Option::is_none")] before_lines: Option, #[serde(skip_serializing_if = "Option::is_none")] after_lines: Option, #[serde(skip_serializing_if = "Option::is_none")] line: Option, } impl LineSpacing { pub fn new() -> Self { Self::default() } pub fn line_rule(mut self, t: LineSpacingType) -> Self { self.line_rule = Some(t); self } pub fn before(mut self, before: u32) -> Self { self.before = Some(before); self } pub fn after(mut self, after: u32) -> Self { self.after = Some(after); self } pub fn before_lines(mut self, before: u32) -> Self { self.before_lines = Some(before); self } pub fn after_lines(mut self, after: u32) -> Self { self.after_lines = Some(after); self } pub fn line(mut self, line: i32) -> Self { self.line = Some(line); self } } impl BuildXML for LineSpacing { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .line_spacing( self.before, self.after, self.line, self.before_lines, self.after_lines, self.line_rule, )? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_spacing() { let b = LineSpacing::new() .line_rule(LineSpacingType::Auto) .line(100) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_spacing_after_lines() { let b = LineSpacing::new() .line_rule(LineSpacingType::Auto) .after_lines(100) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_spacing_json() { let s = LineSpacing { line_rule: Some(LineSpacingType::Auto), before: None, after: None, before_lines: None, after_lines: None, line: Some(100), }; assert_eq!( serde_json::to_string(&s).unwrap(), r#"{"lineRule":"auto","line":100}"# ); } } docx-rs-0.4.18/src/documents/elements/link.rs000064400000000000000000000014141046102023000172040ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct Link { val: String, } impl Link { pub fn new(val: impl Into) -> Link { Link { val: escape(&val.into()), } } } impl Serialize for Link { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } impl BuildXML for Link { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).link(&self.val)?.into_inner() } } docx-rs-0.4.18/src/documents/elements/mc_fallback.rs000064400000000000000000000007321046102023000204670ustar 00000000000000use crate::documents::BuildXML; use serde::Serialize; use std::io::Write; #[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct McFallback {} impl McFallback { pub fn new() -> McFallback { Default::default() } } impl BuildXML for McFallback { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { // Ignore for now Ok(stream) } } docx-rs-0.4.18/src/documents/elements/mod.rs000064400000000000000000000130001046102023000170200ustar 00000000000000mod a_graphic; mod a_graphic_data; mod abstract_numbering; mod adjust_right_ind; mod based_on; mod bold; mod bold_cs; mod bookmark_end; mod bookmark_start; mod br; mod cant_split; mod caps; mod cell_margins; mod character_spacing; mod color; mod comment; mod comment_extended; mod comment_range_end; mod comment_range_start; mod data_binding; mod default_tab_stop; mod delete; mod delete_instr_text; mod delete_text; mod div; mod doc_defaults; mod doc_grid; mod doc_id; mod doc_var; mod drawing; mod dstrike; mod fld_char; mod font; mod font_scheme; mod footer_reference; mod footnote; mod footnote_reference; mod frame_property; mod grid_span; mod header_reference; mod highlight; mod hyperlink; mod indent; mod indent_level; mod insert; mod instr_hyperlink; mod instr_num_pages; mod instr_page; mod instr_pageref; mod instr_tc; mod instr_text; mod instr_toc; mod is_lgl; mod italic; mod italic_cs; mod justification; mod level; mod level_jc; mod level_override; mod level_restart; mod level_text; mod line_spacing; mod link; mod mc_fallback; mod name; mod next; mod num_pages; mod number_format; mod numbering; mod numbering_id; mod numbering_property; mod outline_lvl; mod page_margin; mod page_num; mod page_num_type; mod page_size; mod paragraph; mod paragraph_borders; mod paragraph_property; mod paragraph_property_change; mod paragraph_property_default; mod paragraph_style; mod pic; mod positional_tab; mod q_format; mod run; mod run_fonts; mod run_property; mod run_property_default; mod run_style; mod section; mod section_property; mod shading; mod shape; mod spec_vanish; mod start; mod stretch; mod strike; mod structured_data_tag; mod structured_data_tag_property; mod style; mod sym; mod sz; mod sz_cs; mod tab; mod table; mod table_borders; mod table_cell; mod table_cell_borders; mod table_cell_margins; mod table_cell_property; mod table_cell_width; mod table_grid; mod table_indent; mod table_layout; mod table_of_contents; mod table_of_contents_item; mod table_position_property; mod table_property; mod table_row; mod table_row_property; mod table_style; mod table_width; mod tabs; mod text; mod text_alignment; mod text_border; mod text_box; mod text_box_content; mod text_direction; mod underline; mod v_align; mod vanish; mod vert_align; mod vertical_merge; mod wp_anchor; mod wps_shape; mod wps_text_box; mod zoom; pub use a_graphic::*; pub use a_graphic_data::*; pub use abstract_numbering::*; pub use adjust_right_ind::*; pub use based_on::*; pub use bold::*; pub use bold_cs::*; pub use bookmark_end::*; pub use bookmark_start::*; pub use br::*; pub use cant_split::*; pub use caps::*; pub use cell_margins::*; pub use character_spacing::*; pub use color::*; pub use comment::*; pub use comment_extended::*; pub use comment_range_end::*; pub use comment_range_start::*; pub use data_binding::*; pub use default_tab_stop::*; pub use delete::*; pub use delete_instr_text::*; pub use delete_text::*; pub use div::*; pub use doc_defaults::*; pub use doc_grid::*; pub use doc_id::*; pub use doc_var::*; pub use drawing::*; pub use dstrike::*; pub use fld_char::*; pub use font::*; pub use font_scheme::*; pub use footer_reference::*; pub use footnote::*; pub use footnote_reference::*; pub use frame_property::*; pub use grid_span::*; pub use header_reference::*; pub use highlight::*; pub use hyperlink::*; pub use indent::*; pub use indent_level::*; pub use insert::*; pub use instr_hyperlink::*; pub use instr_num_pages::*; pub use instr_page::*; pub use instr_pageref::*; pub use instr_tc::*; pub use instr_text::*; pub use instr_toc::*; pub use is_lgl::*; pub use italic::*; pub use italic_cs::*; pub use justification::*; pub use level::*; pub use level_jc::*; pub use level_override::*; pub use level_restart::*; pub use level_text::*; pub use line_spacing::*; pub use link::*; pub use mc_fallback::*; pub use name::*; pub use next::*; pub use num_pages::*; pub use number_format::*; pub use numbering::*; pub use numbering_id::*; pub use numbering_property::*; pub use outline_lvl::*; pub use page_num::*; pub use page_num_type::*; pub use page_size::*; pub use paragraph::*; pub use paragraph_borders::*; pub use paragraph_property::*; pub use paragraph_property_change::*; pub use paragraph_property_default::*; pub use paragraph_style::*; pub use pic::*; pub use positional_tab::*; pub use q_format::*; pub use run::*; pub use run_fonts::*; pub use run_property::*; pub use run_property_default::*; pub use run_style::*; pub use section::*; pub use section_property::*; pub use shading::*; pub use shape::*; pub use spec_vanish::*; pub use start::*; pub use stretch::*; pub use strike::*; pub use structured_data_tag::*; pub use structured_data_tag_property::*; pub use style::*; pub use sym::*; pub use sz::*; pub use sz_cs::*; pub use tab::*; pub use table::*; pub use table_borders::*; pub use table_cell::*; pub use table_cell_borders::*; pub use table_cell_margins::*; pub use table_cell_property::*; pub use table_cell_width::*; pub use table_grid::*; pub use table_indent::*; pub use table_layout::*; pub use table_of_contents::*; pub use table_of_contents_item::*; pub use table_position_property::*; pub use table_property::*; pub use table_row::*; pub use table_row_property::*; pub use table_style::*; pub use table_width::*; pub use tabs::*; pub use text::*; pub use text_alignment::*; pub use text_border::*; pub use text_box::*; pub use text_box_content::*; pub use text_direction::*; pub use underline::*; pub use v_align::*; pub use vanish::*; pub use vert_align::*; pub use vertical_merge::*; pub use wp_anchor::*; pub use wps_shape::*; pub use wps_text_box::*; pub use zoom::*; docx-rs-0.4.18/src/documents/elements/name.rs000064400000000000000000000030201046102023000171620ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use std::str::FromStr; use crate::documents::BuildXML; use crate::{escape, xml_builder::*}; #[derive(Debug, Clone, PartialEq)] pub struct Name { name: String, } impl Name { pub fn new(name: impl Into) -> Name { Name { name: escape::escape(&name.into()), } } pub fn starts_with(&self, s: &str) -> bool { self.name.starts_with(s) } pub fn is_heading(&self) -> bool { self.name.to_lowercase().starts_with("heading") } pub fn get_heading_number(&self) -> Option { let replaced = self.name.to_lowercase().replace("heading ", ""); if let Ok(n) = usize::from_str(&replaced) { Some(n) } else { None } } } impl BuildXML for Name { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).name(&self.name)?.into_inner() } } impl Serialize for Name { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.name) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Name::new("Heading"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/next.rs000064400000000000000000000020041046102023000172210ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct Next { val: String, } impl Next { pub fn new(val: impl Into) -> Next { Next { val: val.into() } } } impl Serialize for Next { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } impl BuildXML for Next { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).next(&self.val)?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_next() { let c = Next::new("Normal"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/num_pages.rs000064400000000000000000000026301046102023000202260ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::types::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct NumPages { pub instr: InstrNUMPAGES, } impl Default for NumPages { fn default() -> Self { Self { instr: InstrNUMPAGES {}, } } } impl NumPages { pub fn new() -> Self { Self::default() } } impl BuildXML for NumPages { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { Run::new() .add_field_char(FieldCharType::Begin, false) .add_instr_text(InstrText::NUMPAGES(self.instr.clone())) .add_field_char(FieldCharType::Separate, false) .add_text("1") .add_field_char(FieldCharType::End, false) .build_to(stream) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_num_pages() { let b = NumPages::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"NUMPAGES1"# ); } } docx-rs-0.4.18/src/documents/elements/number_format.rs000064400000000000000000000021721046102023000211110ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct NumberFormat { pub val: String, } impl NumberFormat { pub fn new(val: impl Into) -> Self { Self { val: val.into() } } } impl BuildXML for NumberFormat { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .number_format(&self.val)? .into_inner() } } impl Serialize for NumberFormat { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_start() { let c = NumberFormat::new("decimal"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/numbering.rs000064400000000000000000000051601046102023000202370ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use super::*; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Numbering { pub id: usize, pub abstract_num_id: usize, pub level_overrides: Vec, } impl Numbering { pub fn new(id: usize, abstract_num_id: usize) -> Self { Self { id, abstract_num_id, level_overrides: vec![], } } pub fn overrides(mut self, overrides: Vec) -> Self { self.level_overrides = overrides; self } pub fn add_override(mut self, o: LevelOverride) -> Self { self.level_overrides.push(o); self } } impl BuildXML for Numbering { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let id = format!("{}", self.id); let abs_id = format!("{}", self.abstract_num_id); XMLBuilder::from(stream) .open_num(&id)? .abstract_num_id(&abs_id)? .add_children(&self.level_overrides)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_numbering() { let c = Numbering::new(0, 2); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_numbering_override() { let c = Numbering::new(0, 2); let overrides = vec![ LevelOverride::new(0).start(1), LevelOverride::new(1).start(1), ]; let b = c.overrides(overrides).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_numbering_override_json() { let c = Numbering::new(0, 2); let overrides = vec![ LevelOverride::new(0).start(1), LevelOverride::new(1).start(1), ]; assert_eq!( serde_json::to_string(&c.overrides(overrides)).unwrap(), r#"{"id":0,"abstractNumId":2,"levelOverrides":[{"level":0,"overrideStart":1,"overrideLevel":null},{"level":1,"overrideStart":1,"overrideLevel":null}]}"# ); } } docx-rs-0.4.18/src/documents/elements/numbering_id.rs000064400000000000000000000015171046102023000207150ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] pub struct NumberingId { pub id: usize, } impl NumberingId { pub fn new(id: usize) -> NumberingId { NumberingId { id } } } impl BuildXML for NumberingId { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).num_id(self.id)?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_num_id() { let c = NumberingId::new(0); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/numbering_property.rs000064400000000000000000000053221046102023000222030ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::{IndentLevel, NumberingId}; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Default)] pub struct NumberingProperty { pub id: Option, pub level: Option, } impl NumberingProperty { pub fn new() -> NumberingProperty { Default::default() } pub fn id(mut self, id: NumberingId) -> NumberingProperty { self.id = Some(id); self } pub fn add_num(mut self, id: NumberingId, level: IndentLevel) -> NumberingProperty { self.id = Some(id); self.level = Some(level); self } } impl BuildXML for NumberingProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_numbering_property()? .add_optional_child(&self.id)? .add_optional_child(&self.level)? .close()? .into_inner() } } impl Serialize for NumberingProperty { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut t = serializer.serialize_struct("NumberProperty", 2)?; let mut id: Option = None; if let Some(n) = &self.id { id = Some(n.id); } t.serialize_field("id", &id)?; let mut level: Option = None; if let Some(n) = &self.level { level = Some(n.val); } t.serialize_field("level", &level)?; t.end() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_num_property() { let c = NumberingProperty::new().add_num(NumberingId::new(0), IndentLevel::new(3)); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_empty_num_property() { let c = NumberingProperty::new(); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_num_property_json() { let c = NumberingProperty::new().add_num(NumberingId::new(0), IndentLevel::new(3)); assert_eq!(serde_json::to_string(&c).unwrap(), r#"{"id":0,"level":3}"#); } #[test] fn test_empty_num_property_json() { let c = NumberingProperty::new(); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"id":null,"level":null}"# ); } } docx-rs-0.4.18/src/documents/elements/outline_lvl.rs000064400000000000000000000021471046102023000206070ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::*; #[derive(Debug, Clone, PartialEq)] pub struct OutlineLvl { pub v: usize, } impl OutlineLvl { pub fn new(v: usize) -> OutlineLvl { assert!(v < 10, "outline level should be less than 10"); OutlineLvl { v } } } impl BuildXML for OutlineLvl { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .outline_lvl(self.v)? // .close()? .into_inner() } } impl Serialize for OutlineLvl { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.v as u32) } } #[cfg(test)] mod tests { use crate::{BuildXML, OutlineLvl}; #[test] fn test_outline_lvl_build() { let bytes = OutlineLvl::new(1).build(); assert_eq!( std::str::from_utf8(&bytes).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/page_margin.rs000064400000000000000000000044361046102023000205270ustar 00000000000000use crate::documents::BuildXML; use crate::types::PageMargin; use crate::xml_builder::*; use std::io::Write; // These values were based on microsoft office word2019 windows edition. // impl Default for PageMargin { fn default() -> PageMargin { PageMargin { top: 1985, left: 1701, bottom: 1701, right: 1701, header: 851, footer: 992, gutter: 0, } } } impl PageMargin { pub fn new() -> PageMargin { Default::default() } pub fn top(self, v: i32) -> PageMargin { PageMargin { top: v, ..self } } pub fn left(self, v: i32) -> PageMargin { PageMargin { left: v, ..self } } pub fn bottom(self, v: i32) -> PageMargin { PageMargin { bottom: v, ..self } } pub fn right(self, v: i32) -> PageMargin { PageMargin { right: v, ..self } } pub fn header(self, v: i32) -> PageMargin { PageMargin { header: v, ..self } } pub fn footer(self, v: i32) -> PageMargin { PageMargin { footer: v, ..self } } pub fn gutter(self, v: i32) -> PageMargin { PageMargin { gutter: v, ..self } } } impl BuildXML for PageMargin { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .page_margin( &format!("{}", self.top), &format!("{}", self.right), &format!("{}", self.bottom), &format!("{}", self.left), &format!("{}", self.header), &format!("{}", self.footer), &format!("{}", self.gutter), )? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_page_margin_default() { let b = PageMargin::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/page_num.rs000064400000000000000000000025761046102023000200540ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::types::*; #[derive(Serialize, Debug, Clone, PartialEq)] pub struct PageNum { pub instr: InstrPAGE, } impl Default for PageNum { fn default() -> Self { Self { instr: InstrPAGE {}, } } } impl PageNum { pub fn new() -> Self { Self::default() } } impl BuildXML for PageNum { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { Run::new() .add_field_char(FieldCharType::Begin, false) .add_instr_text(InstrText::PAGE(self.instr.clone())) .add_field_char(FieldCharType::Separate, false) .add_text("1") .add_field_char(FieldCharType::End, false) .build_to(stream) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_page() { let b = PageNum::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"PAGE1"# ); } } docx-rs-0.4.18/src/documents/elements/page_num_type.rs000064400000000000000000000021731046102023000211060ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use serde::Serialize; use std::io::Write; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct PageNumType { #[serde(skip_serializing_if = "Option::is_none")] pub start: Option, #[serde(skip_serializing_if = "Option::is_none")] pub chap_style: Option, } impl PageNumType { pub fn new() -> Self { Default::default() } pub fn start(self, s: u32) -> Self { Self { start: Some(s), ..self } } pub fn chap_style(self, s: impl Into) -> Self { Self { chap_style: Some(s.into()), ..self } } } impl BuildXML for PageNumType { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .page_num_type(self.start, self.chap_style.clone())? .into_inner() } } docx-rs-0.4.18/src/documents/elements/page_size.rs000064400000000000000000000036401046102023000202200ustar 00000000000000use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct PageSize { w: u32, h: u32, orient: Option, } // These values were based on microsoft office word2019 windows edition. // impl Default for PageSize { fn default() -> PageSize { PageSize { w: 11906, h: 16838, orient: None, } } } impl PageSize { pub fn new() -> PageSize { Default::default() } pub fn size(self, w: u32, h: u32) -> PageSize { PageSize { w, h, orient: self.orient, } } pub fn width(mut self, w: u32) -> PageSize { self.w = w; self } pub fn height(mut self, h: u32) -> PageSize { self.h = h; self } pub fn orient(mut self, o: PageOrientationType) -> PageSize { self.orient = Some(o); self } } impl BuildXML for PageSize { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let w = format!("{}", self.w); let h = format!("{}", self.h); XMLBuilder::from(stream) .apply(|b| match self.orient { None => b.page_size(&w, &h), Some(orient) => b.page_size_with_orient(&w, &h, &orient.to_string()), })? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_page_size_default() { let b = PageSize::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/paragraph.rs000064400000000000000000000473421046102023000202260ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Paragraph { pub id: String, pub children: Vec, pub property: ParagraphProperty, pub has_numbering: bool, } impl Default for Paragraph { fn default() -> Self { Self { id: crate::generate_para_id(), children: Vec::new(), property: ParagraphProperty::new(), has_numbering: false, } } } #[derive(Debug, Clone, PartialEq)] pub enum ParagraphChild { Run(Box), Insert(Insert), Delete(Delete), BookmarkStart(BookmarkStart), Hyperlink(Hyperlink), BookmarkEnd(BookmarkEnd), CommentStart(Box), CommentEnd(CommentRangeEnd), StructuredDataTag(Box), PageNum(Box), NumPages(Box), } impl BuildXML for ParagraphChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { ParagraphChild::Run(v) => v.build_to(stream), ParagraphChild::Insert(v) => v.build_to(stream), ParagraphChild::Delete(v) => v.build_to(stream), ParagraphChild::Hyperlink(v) => v.build_to(stream), ParagraphChild::BookmarkStart(v) => v.build_to(stream), ParagraphChild::BookmarkEnd(v) => v.build_to(stream), ParagraphChild::CommentStart(v) => v.build_to(stream), ParagraphChild::CommentEnd(v) => v.build_to(stream), ParagraphChild::StructuredDataTag(v) => v.build_to(stream), ParagraphChild::PageNum(v) => v.build_to(stream), ParagraphChild::NumPages(v) => v.build_to(stream), } } } 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() } ParagraphChild::Delete(ref r) => { let mut t = serializer.serialize_struct("Delete", 2)?; t.serialize_field("type", "delete")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::Hyperlink(ref r) => { let mut t = serializer.serialize_struct("hyperlink", 2)?; t.serialize_field("type", "hyperlink")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::BookmarkStart(ref r) => { let mut t = serializer.serialize_struct("BookmarkStart", 2)?; t.serialize_field("type", "bookmarkStart")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::BookmarkEnd(ref r) => { let mut t = serializer.serialize_struct("BookmarkEnd", 2)?; t.serialize_field("type", "bookmarkEnd")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::PageNum(ref r) => { let mut t = serializer.serialize_struct("PageNum", 2)?; t.serialize_field("type", "pageNum")?; t.serialize_field("data", r)?; t.end() } ParagraphChild::NumPages(ref r) => { let mut t = serializer.serialize_struct("NumPages", 2)?; t.serialize_field("type", "numPages")?; t.serialize_field("data", r)?; t.end() } } } } impl Paragraph { pub fn new() -> Paragraph { Default::default() } pub fn id(mut self, id: impl Into) -> Self { self.id = id.into(); self } pub fn children(&self) -> &Vec { &self.children } pub fn add_run(mut self, run: Run) -> Paragraph { self.children.push(ParagraphChild::Run(Box::new(run))); self } pub(crate) fn unshift_run(mut self, run: Run) -> Paragraph { self.children.insert(0, ParagraphChild::Run(Box::new(run))); self } pub(crate) fn wrap_by_bookmark(mut self, id: usize, name: impl Into) -> Paragraph { self.children.insert( 0, ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)), ); self.children .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id))); self } pub fn add_hyperlink(mut self, link: Hyperlink) -> Self { self.children.push(ParagraphChild::Hyperlink(link)); self } pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { self.children .push(ParagraphChild::StructuredDataTag(Box::new(t))); self } pub fn add_insert(mut self, insert: Insert) -> Paragraph { self.children.push(ParagraphChild::Insert(insert)); self } pub fn add_delete(mut self, delete: Delete) -> Paragraph { self.children.push(ParagraphChild::Delete(delete)); self } 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: usize) -> Paragraph { self.children .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id))); self } pub fn add_comment_start(mut self, comment: Comment) -> Paragraph { self.children.push(ParagraphChild::CommentStart(Box::new( CommentRangeStart::new(comment), ))); self } pub fn add_comment_end(mut self, id: usize) -> Paragraph { self.children .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id))); self } pub fn align(mut self, alignment_type: AlignmentType) -> Paragraph { self.property = self.property.align(alignment_type); self } pub fn style(mut self, style_id: &str) -> Paragraph { self.property = self.property.style(style_id); self } pub fn snap_to_grid(mut self, v: bool) -> Self { self.property = self.property.snap_to_grid(v); self } pub fn keep_next(mut self, v: bool) -> Self { self.property = self.property.keep_next(v); self } pub fn keep_lines(mut self, v: bool) -> Self { self.property = self.property.keep_lines(v); self } pub fn outline_lvl(mut self, v: usize) -> Self { self.property = self.property.outline_lvl(v); self } pub fn page_break_before(mut self, v: bool) -> Self { self.property = self.property.page_break_before(v); self } pub fn widow_control(mut self, v: bool) -> Self { self.property = self.property.widow_control(v); self } pub fn section_property(mut self, s: SectionProperty) -> Self { self.property = self.property.section_property(s); self } pub fn add_tab(mut self, t: Tab) -> Self { self.property = self.property.add_tab(t); self } pub fn tabs(mut self, tabs: &[Tab]) -> Self { for tab in tabs { self.property = self.property.add_tab(tab.clone()); } self } pub fn indent( mut self, left: Option, special_indent: Option, end: Option, start_chars: Option, ) -> Paragraph { self.property = self.property.indent(left, special_indent, end, start_chars); self } pub fn hanging_chars(mut self, chars: i32) -> Paragraph { self.property = self.property.hanging_chars(chars); self } pub fn first_line_chars(mut self, chars: i32) -> Paragraph { self.property = self.property.first_line_chars(chars); self } pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self { self.property = self.property.numbering(id, level); self.has_numbering = true; self } pub fn size(mut self, size: usize) -> Self { self.property.run_property = self.property.run_property.size(size); self } pub fn color(mut self, c: impl Into) -> Self { self.property.run_property = self.property.run_property.color(c); self } pub fn bold(mut self) -> Self { self.property.run_property = self.property.run_property.bold(); self } pub fn italic(mut self) -> Self { self.property.run_property = self.property.run_property.italic(); self } pub fn fonts(mut self, f: RunFonts) -> Self { self.property.run_property = self.property.run_property.fonts(f); self } pub fn run_property(mut self, p: RunProperty) -> Self { self.property.run_property = p; self } pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { self.property = self.property.line_spacing(spacing); self } pub fn character_spacing(mut self, spacing: i32) -> Self { self.property = self.property.character_spacing(spacing); self } pub fn delete(mut self, author: impl Into, date: impl Into) -> Self { self.property.run_property.del = Some(Delete::new().author(author).date(date)); self } pub fn insert(mut self, author: impl Into, date: impl Into) -> Self { self.property.run_property.ins = Some(Insert::new_with_empty().author(author).date(date)); self } pub fn paragraph_property_change(mut self, p: ParagraphPropertyChange) -> Self { self.property = self.property.paragraph_property_change(p); self } // internal pub(crate) fn paragraph_property(mut self, p: ParagraphProperty) -> Self { self.property = p; self } pub fn raw_text(&self) -> String { let mut s = "".to_string(); // For now support only run and ins. for c in self.children.iter() { match c { ParagraphChild::Insert(i) => { for c in i.children.iter() { if let InsertChild::Run(r) = c { for c in r.children.iter() { if let RunChild::Text(t) = c { s.push_str(&t.text); } } } } } ParagraphChild::Run(run) => { for c in run.children.iter() { if let RunChild::Text(t) = c { s.push_str(&t.text); } } } _ => {} } } s } pub fn add_page_num(mut self, p: PageNum) -> Self { self.children.push(ParagraphChild::PageNum(Box::new(p))); self } pub fn add_num_pages(mut self, p: NumPages) -> Self { self.children.push(ParagraphChild::NumPages(Box::new(p))); self } // frameProperty pub fn wrap(mut self, wrap: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { wrap: Some(wrap.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn v_anchor(mut self, anchor: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { v_anchor: Some(anchor.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn h_anchor(mut self, anchor: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { h_anchor: Some(anchor.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn h_rule(mut self, r: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { h_rule: Some(r.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn x_align(mut self, align: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { x_align: Some(align.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn y_align(mut self, align: impl Into) -> Self { self.property.frame_property = Some(FrameProperty { y_align: Some(align.into()), ..self.property.frame_property.unwrap_or_default() }); self } pub fn h_space(mut self, x: i32) -> Self { self.property.frame_property = Some(FrameProperty { h_space: Some(x), ..self.property.frame_property.unwrap_or_default() }); self } pub fn v_space(mut self, x: i32) -> Self { self.property.frame_property = Some(FrameProperty { v_space: Some(x), ..self.property.frame_property.unwrap_or_default() }); self } pub fn frame_x(mut self, x: i32) -> Self { self.property.frame_property = Some(FrameProperty { x: Some(x), ..self.property.frame_property.unwrap_or_default() }); self } pub fn frame_y(mut self, y: i32) -> Self { self.property.frame_property = Some(FrameProperty { y: Some(y), ..self.property.frame_property.unwrap_or_default() }); self } pub fn frame_width(mut self, n: u32) -> Self { self.property.frame_property = Some(FrameProperty { w: Some(n), ..self.property.frame_property.unwrap_or_default() }); self } pub fn frame_height(mut self, n: u32) -> Self { self.property.frame_property = Some(FrameProperty { h: Some(n), ..self.property.frame_property.unwrap_or_default() }); self } } impl BuildXML for Paragraph { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_paragraph(&self.id)? .add_child(&self.property)? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_paragraph() { let b = Paragraph::new() .add_run(Run::new().add_text("Hello")) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_bookmark() { let b = Paragraph::new() .add_bookmark_start(0, "article") .add_run(Run::new().add_text("Hello")) .add_bookmark_end(0) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_comment() { let b = Paragraph::new() .add_comment_start(Comment::new(1)) .add_run(Run::new().add_text("Hello")) .add_comment_end(1) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_numbering() { let b = Paragraph::new() .add_run(Run::new().add_text("Hello")) .numbering(NumberingId::new(0), IndentLevel::new(1)) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_line_spacing_and_character_spacing() { let spacing = LineSpacing::new() .line_rule(LineSpacingType::Auto) .before(20) .after(30) .line(200); let b = Paragraph::new() .line_spacing(spacing) .add_run(Run::new().add_text("Hello")) .build(); assert_eq!( str::from_utf8(&b).unwrap(), 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#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"#, ); } #[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#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"# ); } #[test] fn test_raw_text() { let b = Paragraph::new() .add_run(Run::new().add_text("Hello")) .add_insert(Insert::new(Run::new().add_text("World"))) .add_delete(Delete::new().add_run(Run::new().add_delete_text("!!!!!"))) .raw_text(); assert_eq!(b, "HelloWorld".to_owned()); } } docx-rs-0.4.18/src/documents/elements/paragraph_borders.rs000064400000000000000000000150341046102023000217370ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ParagraphBorder { position: ParagraphBorderPosition, pub val: BorderType, pub size: usize, pub space: usize, pub color: String, // pub shadow: Option, // pub theme_color: Option, // pub theme_shade: Option, // pub theme_tint: Option, // pub frame: Option, } impl ParagraphBorder { pub fn new(position: ParagraphBorderPosition) -> Self { ParagraphBorder { position, val: BorderType::Single, size: 2, space: 0, color: "auto".to_owned(), // shadow: None, // theme_color: None, // theme_shade: None, // theme_tint: None, // frame: None, } } pub fn val(mut self, val: BorderType) -> Self { self.val = val; self } pub fn size(mut self, size: usize) -> Self { self.size = size; self } pub fn space(mut self, space: usize) -> Self { self.space = space; self } pub fn color(mut self, color: impl Into) -> Self { self.color = color.into(); self } // pub fn shadow(mut self, shadow: bool) -> Self { // self.shadow = Some(shadow); // self // } // // pub fn theme_color(mut self, theme_color: impl Into) -> Self { // self.theme_color = Some(theme_color.into()); // self // } // // pub fn theme_shade(mut self, theme_shade: impl Into) -> Self { // self.theme_shade = Some(theme_shade.into()); // self // } // // pub fn theme_tint(mut self, theme_tint: impl Into) -> Self { // self.theme_tint = Some(theme_tint.into()); // self // } // // pub fn frame(mut self, frame: bool) -> Self { // self.frame = Some(frame); // self // } } impl BuildXML for ParagraphBorder { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let val = self.val.to_string(); let space = self.space.to_string(); let size = self.size.to_string(); let func = match self.position { ParagraphBorderPosition::Top => XMLBuilder::paragraph_border_top, ParagraphBorderPosition::Left => XMLBuilder::paragraph_border_left, ParagraphBorderPosition::Bottom => XMLBuilder::paragraph_border_bottom, ParagraphBorderPosition::Right => XMLBuilder::paragraph_border_right, ParagraphBorderPosition::Between => XMLBuilder::paragraph_border_between, ParagraphBorderPosition::Bar => XMLBuilder::paragraph_border_bar, }; XMLBuilder::from(stream) .apply(|b| func(b, &val, &space, &size, &self.color))? .into_inner() } } #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ParagraphBorders { left: Option, right: Option, top: Option, bottom: Option, between: Option, bar: Option, } impl Default for ParagraphBorders { fn default() -> Self { ParagraphBorders { left: Some(ParagraphBorder::new(ParagraphBorderPosition::Left)), right: Some(ParagraphBorder::new(ParagraphBorderPosition::Right)), top: Some(ParagraphBorder::new(ParagraphBorderPosition::Top)), bottom: Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom)), between: None, bar: None, } } } impl ParagraphBorders { pub fn new() -> Self { Self::default() } pub fn with_empty() -> Self { ParagraphBorders { left: None, right: None, top: None, bottom: None, between: None, bar: None, } } pub fn set(mut self, border: ParagraphBorder) -> Self { match border.position { ParagraphBorderPosition::Top => self.top = Some(border), ParagraphBorderPosition::Left => self.left = Some(border), ParagraphBorderPosition::Bottom => self.bottom = Some(border), ParagraphBorderPosition::Right => self.right = Some(border), ParagraphBorderPosition::Between => self.between = Some(border), ParagraphBorderPosition::Bar => self.bar = Some(border), }; self } pub fn clear(mut self, position: ParagraphBorderPosition) -> Self { let nil = ParagraphBorder::new(position.clone()).val(BorderType::Nil); match position { ParagraphBorderPosition::Top => self.top = Some(nil), ParagraphBorderPosition::Left => self.left = Some(nil), ParagraphBorderPosition::Bottom => self.bottom = Some(nil), ParagraphBorderPosition::Right => self.right = Some(nil), ParagraphBorderPosition::Between => self.between = Some(nil), ParagraphBorderPosition::Bar => self.bar = Some(nil), }; self } pub fn clear_all(mut self) -> Self { self.left = Some(ParagraphBorder::new(ParagraphBorderPosition::Left).val(BorderType::Nil)); self.right = Some(ParagraphBorder::new(ParagraphBorderPosition::Right).val(BorderType::Nil)); self.top = Some(ParagraphBorder::new(ParagraphBorderPosition::Top).val(BorderType::Nil)); self.bottom = Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom).val(BorderType::Nil)); self.between = Some(ParagraphBorder::new(ParagraphBorderPosition::Between).val(BorderType::Nil)); self.bar = Some(ParagraphBorder::new(ParagraphBorderPosition::Bar).val(BorderType::Nil)); self } } impl BuildXML for ParagraphBorders { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_paragraph_borders()? .add_optional_child(&self.left)? .add_optional_child(&self.right)? .add_optional_child(&self.top)? .add_optional_child(&self.bottom)? .add_optional_child(&self.between)? .add_optional_child(&self.bar)? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/elements/paragraph_property.rs000064400000000000000000000253441046102023000221700ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::{AlignmentType, SpecialIndentType}; use crate::ParagraphBorderPosition; use crate::{xml_builder::*, TextAlignmentType}; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct ParagraphProperty { pub run_property: RunProperty, #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, #[serde(skip_serializing_if = "Option::is_none")] pub numbering_property: Option, #[serde(skip_serializing_if = "Option::is_none")] pub alignment: Option, #[serde(skip_serializing_if = "Option::is_none")] pub indent: Option, #[serde(skip_serializing_if = "Option::is_none")] pub line_spacing: Option, #[serde(skip_serializing_if = "Option::is_none")] pub keep_next: Option, #[serde(skip_serializing_if = "Option::is_none")] pub keep_lines: Option, #[serde(skip_serializing_if="Option::is_none")] pub bidi:Option, #[serde(skip_serializing_if = "Option::is_none")] pub page_break_before: Option, #[serde(skip_serializing_if = "Option::is_none")] pub widow_control: Option, #[serde(skip_serializing_if = "Option::is_none")] pub outline_lvl: Option, #[serde(skip_serializing_if = "Option::is_none")] pub section_property: Option, pub tabs: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub paragraph_property_change: Option, #[serde(skip_serializing_if = "Option::is_none")] pub borders: Option, #[serde(skip_serializing_if = "Option::is_none")] pub frame_property: Option, #[serde(skip_serializing_if = "Option::is_none")] pub text_alignment: Option, #[serde(skip_serializing_if = "Option::is_none")] pub adjust_right_ind: Option, #[serde(skip_serializing_if = "Option::is_none")] pub snap_to_grid: Option, // read only #[serde(skip_serializing_if = "Option::is_none")] pub(crate) div_id: Option, } // 17.3.1.26 // pPr (Paragraph Properties) // This element specifies a set of paragraph properties which shall be applied to the contents of the parent // paragraph after all style/numbering/table properties have been applied to the text. These properties are defined // as direct formatting, since they are directly applied to the paragraph and supersede any formatting from styles. impl ParagraphProperty { pub fn new() -> ParagraphProperty { Default::default() } pub fn align(mut self, alignment_type: AlignmentType) -> Self { self.alignment = Some(Justification::new(alignment_type.to_string())); self } pub fn style(mut self, style_id: &str) -> Self { self.style = Some(ParagraphStyle::new(Some(style_id))); self } pub fn indent( mut self, left: Option, special_indent: Option, end: Option, start_chars: Option, ) -> Self { self.indent = Some(Indent::new(left, special_indent, end, start_chars)); self } pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self { self.numbering_property = Some(NumberingProperty::new().add_num(id, level)); self } pub fn numbering_property(mut self, np: NumberingProperty) -> Self { self.numbering_property = Some(np); self } pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { self.line_spacing = Some(spacing); self } pub fn character_spacing(mut self, spacing: i32) -> Self { self.run_property.character_spacing = Some(CharacterSpacing::new(spacing)); self } pub fn snap_to_grid(mut self, v: bool) -> Self { self.snap_to_grid = Some(v); self } pub fn keep_next(mut self, v: bool) -> Self { self.keep_next = Some(v); self } pub fn keep_lines(mut self, v: bool) -> Self { self.keep_lines = Some(v); self } pub fn outline_lvl(mut self, v: usize) -> Self { if v >= 10 { // clamped self.outline_lvl = Some(OutlineLvl::new(9)); return self; } self.outline_lvl = Some(OutlineLvl::new(v)); self } pub fn page_break_before(mut self, v: bool) -> Self { self.page_break_before = Some(v); self } pub fn bidi(mut self,v:bool)->Self{ self.bidi=Some(v); self } pub fn widow_control(mut self, v: bool) -> Self { self.widow_control = Some(v); self } pub fn add_tab(mut self, t: Tab) -> Self { self.tabs.push(t); self } pub fn section_property(mut self, s: SectionProperty) -> Self { self.section_property = Some(s); self } pub fn paragraph_property_change(mut self, p: ParagraphPropertyChange) -> Self { self.paragraph_property_change = Some(p); self } pub fn frame_property(mut self, s: FrameProperty) -> Self { self.frame_property = Some(s); self } pub fn run_property(mut self, s: RunProperty) -> Self { self.run_property = s; self } pub fn text_alignment(mut self, s: TextAlignmentType) -> Self { self.text_alignment = Some(TextAlignment::new(s)); self } pub fn adjust_right_ind(mut self, s: isize) -> Self { self.adjust_right_ind = Some(AdjustRightInd::new(s)); self } pub(crate) fn hanging_chars(mut self, chars: i32) -> Self { if let Some(indent) = self.indent { self.indent = Some(indent.hanging_chars(chars)); } self } pub(crate) fn first_line_chars(mut self, chars: i32) -> Self { if let Some(indent) = self.indent { self.indent = Some(indent.first_line_chars(chars)); } self } pub fn set_borders(mut self, borders: ParagraphBorders) -> Self { self.borders = Some(borders); self } pub fn set_border(mut self, border: ParagraphBorder) -> Self { self.borders = Some(self.borders.unwrap_or_default().set(border)); self } pub fn clear_border(mut self, position: ParagraphBorderPosition) -> Self { self.borders = Some(self.borders.unwrap_or_default().clear(position)); self } pub fn clear_all_borders(mut self) -> Self { self.borders = Some(self.borders.unwrap_or_default().clear_all()); self } } impl BuildXML for ParagraphProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_paragraph_property()? .add_child(&self.run_property)? .add_optional_child(&self.style)? .add_optional_child(&self.numbering_property)? .add_optional_child(&self.frame_property)? .add_optional_child(&self.alignment)? .add_optional_child(&self.indent)? .add_optional_child(&self.line_spacing)? .add_optional_child(&self.outline_lvl)? .add_optional_child(&self.paragraph_property_change)? .add_optional_child(&self.borders)? .add_optional_child(&self.text_alignment)? .add_optional_child(&self.adjust_right_ind)? .apply_opt(self.snap_to_grid, |v, b| b.snap_to_grid(v))? .apply_if(self.keep_next, |b| b.keep_next())? .apply_if(self.keep_lines, |b| b.keep_lines())? .apply_if(self.page_break_before, |b| b.page_break_before())? .apply_if(self.bidi,|b| b.bidi())? .apply_opt(self.widow_control, |flag, b| { b.widow_control(if flag { "1" } else { "0" }) })? .apply_if(!self.tabs.is_empty(), |b| { b.open_tabs()? .apply_each(&self.tabs, |tab, b| b.tab(tab.val, tab.leader, tab.pos))? .close() })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; use crate::types::LineSpacingType; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let c = ParagraphProperty::new(); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_bidi(){ let c=ParagraphProperty::new().bidi(true); let b=c.build(); println!("-----Test bidi: {}", str::from_utf8(&b).unwrap()); assert_eq!(str::from_utf8(&b).unwrap(),r#""#); } #[test] fn test_alignment() { let c = ParagraphProperty::new(); let b = c.align(AlignmentType::Right).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_indent() { let c = ParagraphProperty::new(); let b = c.indent(Some(20), None, None, None).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_keep_next() { let c = ParagraphProperty::new(); let b = c.keep_next(true).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_outline_lvl() { let props = ParagraphProperty::new(); let bytes = props.outline_lvl(1).build(); assert_eq!( str::from_utf8(&bytes).unwrap(), r#""# ) } #[test] fn test_indent_json() { let c = ParagraphProperty::new(); let b = c.indent(Some(20), Some(SpecialIndentType::FirstLine(10)), None, None); assert_eq!( serde_json::to_string(&b).unwrap(), r#"{"runProperty":{},"indent":{"start":20,"startChars":null,"end":null,"specialIndent":{"type":"firstLine","val":10},"hangingChars":null,"firstLineChars":null},"tabs":[]}"# ); } #[test] fn test_line_spacing() { let props = ParagraphProperty::new(); let spacing = LineSpacing::new() .line_rule(LineSpacingType::AtLeast) .line(100); let bytes = props.line_spacing(spacing).build(); assert_eq!( str::from_utf8(&bytes).unwrap(), r#""# ) } } docx-rs-0.4.18/src/documents/elements/paragraph_property_change.rs000064400000000000000000000041361046102023000234710ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::escape; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ParagraphPropertyChange { pub author: String, pub date: String, pub property: Box, } impl Default for ParagraphPropertyChange { fn default() -> ParagraphPropertyChange { Self { author: "unnamed".to_owned(), date: "1970-01-01T00:00:00Z".to_owned(), property: Default::default(), } } } impl ParagraphPropertyChange { pub fn new() -> ParagraphPropertyChange { Self { ..Default::default() } } pub fn property(mut self, p: ParagraphProperty) -> ParagraphPropertyChange { self.property = Box::new(p); self } pub fn author(mut self, author: impl Into) -> ParagraphPropertyChange { self.author = escape::escape(&author.into()); self } pub fn date(mut self, date: impl Into) -> ParagraphPropertyChange { self.date = date.into(); self } } impl ParagraphPropertyChangeId for ParagraphPropertyChange {} impl BuildXML for ParagraphPropertyChange { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let id = self.generate(); XMLBuilder::from(stream) .open_paragraph_property_change(&id, &self.author, &self.date)? .add_child(&self.property)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_ppr_change_default() { let b = ParagraphPropertyChange::new() .property(ParagraphProperty::new()) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/paragraph_property_default.rs000064400000000000000000000031371046102023000236700ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct ParagraphPropertyDefault { paragraph_property: ParagraphProperty, } impl ParagraphPropertyDefault { pub fn new() -> Self { Default::default() } pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { self.paragraph_property = self.paragraph_property.line_spacing(spacing); self } pub fn paragraph_property(mut self, p: ParagraphProperty) -> Self { self.paragraph_property = p; self } } impl Default for ParagraphPropertyDefault { fn default() -> Self { let paragraph_property = ParagraphProperty::new(); ParagraphPropertyDefault { paragraph_property } } } impl BuildXML for ParagraphPropertyDefault { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_paragraph_property_default()? .add_child(&self.paragraph_property)? .close()? .into_inner() } } mod tests { #[allow(unused_imports)] use super::*; #[cfg(test)] use pretty_assertions::assert_eq; #[test] fn test_build() { let c = ParagraphPropertyDefault::new(); let b = c.build(); assert_eq!( std::str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/paragraph_style.rs000064400000000000000000000036561046102023000214460ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct ParagraphStyle { pub val: String, } impl Default for ParagraphStyle { fn default() -> Self { ParagraphStyle { val: "Normal".to_owned(), } } } // 17.9.23 // pStyle (Paragraph Style's Associated Numbering Level) // This element specifies the name of a paragraph style which shall automatically this numbering level when // applied to the contents of the document. When a paragraph style is defined to include a numbering definition, // any numbering level defined by the numPr element (§17.3.1.19) shall be ignored, and instead this element shall // specify the numbering level associated with that paragraph style. impl ParagraphStyle { pub fn new(val: Option>) -> ParagraphStyle { if let Some(v) = val { ParagraphStyle { val: escape(&v.into()), } } else { Default::default() } } } impl BuildXML for ParagraphStyle { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .paragraph_style(&self.val)? .into_inner() } } impl Serialize for ParagraphStyle { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_p_style() { let c = ParagraphStyle::new(Some("Heading")); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/pic.rs000064400000000000000000000201051046102023000170200ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct Pic { pub id: String, // For writer only #[serde(skip_serializing_if = "Vec::is_empty")] pub image: Vec, // (width, height). unit is emu pub size: (u32, u32), pub position_type: DrawingPositionType, /// Specifies that this object shall be positioned using the positioning information in the /// simplePos child element (§20.4.2.13). This positioning, when specified, positions the /// object on the page by placing its top left point at the x-y coordinates specified by that /// element. pub simple_pos: bool, // unit is emu pub simple_pos_x: i32, pub simple_pos_y: i32, /// Specifies how this DrawingML object behaves when its anchor is located in a table cell; /// and its specified position would cause it to intersect with a table cell displayed in the /// document. That behavior shall be as follows: pub layout_in_cell: bool, /// Specifies the relative Z-ordering of all DrawingML objects in this document. Each floating /// DrawingML object shall have a Z-ordering value, which determines which object is /// displayed when any two objects intersect. Higher values shall indicate higher Z-order; /// lower values shall indicate lower Z-order. pub relative_height: u32, pub allow_overlap: bool, pub position_h: DrawingPosition, pub position_v: DrawingPosition, pub relative_from_h: RelativeFromHType, pub relative_from_v: RelativeFromVType, /// Specifies the minimum distance which shall be maintained between the top edge of this drawing object and any subsequent text within the document when this graphical object is displayed within the document's contents., /// The distance shall be measured in EMUs (English Metric Units)., pub dist_t: i32, pub dist_b: i32, pub dist_l: i32, pub dist_r: i32, // deg pub rot: u16, } impl Pic { #[cfg(feature = "image")] /// Make a `Pic`. /// /// Converts the passed image to PNG internally and computes its size. pub fn new(buf: &[u8]) -> Pic { let img = ::image::load_from_memory(buf).expect("Should load image from memory."); let (w, h) = ::image::GenericImageView::dimensions(&img); let mut buf = std::io::Cursor::new(vec![]); img.write_to(&mut buf, ::image::ImageFormat::Png) .expect("Unable to write dynamic image"); Self::new_with_dimensions(buf.into_inner(), w, h) } /// Make a `Pic` element. For now only PNG is supported. /// /// Use [Pic::new] method, to call `image` crate do conversion for you. pub fn new_with_dimensions(buffer: Vec, width_px: u32, height_px: u32) -> Pic { let id = create_pic_rid(generate_pic_id()); Self { id, image: buffer, size: (from_px(width_px), from_px(height_px)), position_type: DrawingPositionType::Inline, simple_pos: false, simple_pos_x: 0, simple_pos_y: 0, layout_in_cell: false, relative_height: 190500, allow_overlap: false, position_v: DrawingPosition::Offset(0), position_h: DrawingPosition::Offset(0), relative_from_h: RelativeFromHType::default(), relative_from_v: RelativeFromVType::default(), dist_t: 0, dist_b: 0, dist_l: 0, dist_r: 0, rot: 0, } } pub(crate) fn with_empty() -> Pic { Self { id: "".to_string(), image: vec![], size: (0, 0), position_type: DrawingPositionType::Inline, simple_pos: false, simple_pos_x: 0, simple_pos_y: 0, layout_in_cell: false, relative_height: 190500, allow_overlap: false, position_v: DrawingPosition::Offset(0), position_h: DrawingPosition::Offset(0), relative_from_h: RelativeFromHType::default(), relative_from_v: RelativeFromVType::default(), dist_t: 0, dist_b: 0, dist_l: 0, dist_r: 0, rot: 0, } } pub fn id(mut self, id: impl Into) -> Pic { self.id = id.into(); self } // unit is emu pub fn size(mut self, w_emu: u32, h_emu: u32) -> Pic { self.size = (w_emu, h_emu); self } // unit is deg pub fn rotate(mut self, deg: u16) -> Pic { self.rot = deg; self } pub fn floating(mut self) -> Pic { self.position_type = DrawingPositionType::Anchor; self } pub fn overlapping(mut self) -> Pic { self.allow_overlap = true; self } pub fn offset_x(mut self, x: i32) -> Pic { self.position_h = DrawingPosition::Offset(x); self } pub fn offset_y(mut self, y: i32) -> Pic { self.position_v = DrawingPosition::Offset(y); self } pub fn position_h(mut self, pos: DrawingPosition) -> Self { self.position_h = pos; self } pub fn position_v(mut self, pos: DrawingPosition) -> Self { self.position_v = pos; self } pub fn relative_from_h(mut self, t: RelativeFromHType) -> Self { self.relative_from_h = t; self } pub fn relative_from_v(mut self, t: RelativeFromVType) -> Self { self.relative_from_v = t; self } pub fn dist_t(mut self, v: i32) -> Self { self.dist_t = v; self } pub fn dist_b(mut self, v: i32) -> Self { self.dist_b = v; self } pub fn dist_l(mut self, v: i32) -> Self { self.dist_l = v; self } pub fn dist_r(mut self, v: i32) -> Self { self.dist_r = v; self } pub fn simple_pos(mut self, v: bool) -> Self { self.simple_pos = v; self } pub fn relative_height(mut self, v: u32) -> Self { self.relative_height = v; self } } impl BuildXML for Pic { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")? .open_pic_nv_pic_pr()? .pic_c_nv_pr("0", "")? .open_pic_c_nv_pic_pr()? .a_pic_locks("1", "1")? .close()? .close()? .open_blip_fill()? .a_blip(&self.id)? .a_src_rect()? .open_a_stretch()? .a_fill_rect()? .close()? .close()? .open_pic_sp_pr("auto")? .open_a_xfrm_with_rot(&format!("{}", (self.rot as u32) * 60 * 1000))? .a_off("0", "0")? .a_ext(&format!("{}", self.size.0), &format!("{}", self.size.1))? .close()? .open_a_prst_geom("rect")? .a_av_lst()? .close()? .close()? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_pic_build() { let b = Pic::new_with_dimensions(Vec::new(), 320, 240).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/positional_tab.rs000064400000000000000000000032551046102023000212630ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct PositionalTab { pub alignment: PositionalTabAlignmentType, pub relative_to: PositionalTabRelativeTo, pub leader: TabLeaderType, } impl Default for PositionalTab { fn default() -> Self { Self { alignment: PositionalTabAlignmentType::Left, relative_to: PositionalTabRelativeTo::Margin, leader: TabLeaderType::None, } } } impl PositionalTab { pub fn new( alignment: PositionalTabAlignmentType, relative_to: PositionalTabRelativeTo, leader: TabLeaderType, ) -> Self { Self { alignment, relative_to, leader, } } pub fn relative_to(mut self, relative_to: PositionalTabRelativeTo) -> Self { self.relative_to = relative_to; self } pub fn leader(mut self, leader: TabLeaderType) -> Self { self.leader = leader; self } pub fn alignment(mut self, alignment: PositionalTabAlignmentType) -> Self { self.alignment = alignment; self } } impl BuildXML for PositionalTab { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .ptab(self.alignment, self.relative_to, self.leader)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/q_format.rs000064400000000000000000000023611046102023000200610ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; //17.7.4.14 // qFormat (Primary Style) // This element specifies whether this style shall be treated as a primary style when this document is loaded by an // application. If this element is set, then this style has been designated as being particularly important for the // current document, and this information can be used by an application in any means desired. [Note: This setting // 637ECMA-376 Part 1 does not imply any behavior for the style, only that the style is of particular significance for this document. end note] #[derive(Default)] pub struct QFormat {} impl QFormat { pub fn new() -> QFormat { Default::default() } } impl BuildXML for QFormat { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).q_format()?.into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_q_format() { let c = QFormat::new(); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/run.rs000064400000000000000000000361641046102023000170650ustar 00000000000000use super::*; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Run { pub run_property: RunProperty, pub children: Vec, } impl Default for Run { fn default() -> Self { let run_property = RunProperty::new(); Self { run_property, children: vec![], } } } #[derive(Debug, Clone, PartialEq)] pub enum RunChild { Text(Text), Sym(Sym), DeleteText(DeleteText), Tab(Tab), PTab(PositionalTab), Break(Break), Drawing(Box), Shape(Box), CommentStart(Box), CommentEnd(CommentRangeEnd), FieldChar(FieldChar), InstrText(Box), DeleteInstrText(Box), // For reader InstrTextString(String), FootnoteReference(FootnoteReference), Shading(Shading), } 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::Sym(ref s) => { let mut t = serializer.serialize_struct("Sym", 2)?; t.serialize_field("type", "sym")?; 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::PTab(ref s) => { let mut t = serializer.serialize_struct("PTab", 1)?; t.serialize_field("type", "ptab")?; t.serialize_field("data", s)?; 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() } RunChild::Drawing(ref s) => { let mut t = serializer.serialize_struct("Drawing", 2)?; t.serialize_field("type", "drawing")?; t.serialize_field("data", s)?; t.end() } RunChild::Shape(ref s) => { let mut t = serializer.serialize_struct("Shape", 2)?; t.serialize_field("type", "shape")?; t.serialize_field("data", s)?; t.end() } RunChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } RunChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } RunChild::FieldChar(ref f) => { let mut t = serializer.serialize_struct("FieldChar", 2)?; t.serialize_field("type", "fieldChar")?; t.serialize_field("data", f)?; t.end() } RunChild::InstrText(ref i) => { let mut t = serializer.serialize_struct("InstrText", 2)?; t.serialize_field("type", "instrText")?; t.serialize_field("data", i)?; t.end() } RunChild::DeleteInstrText(ref i) => { let mut t = serializer.serialize_struct("DeleteInstrText", 2)?; t.serialize_field("type", "deleteInstrText")?; t.serialize_field("data", i)?; t.end() } RunChild::InstrTextString(ref i) => { let mut t = serializer.serialize_struct("InstrTextString", 2)?; t.serialize_field("type", "instrTextString")?; t.serialize_field("data", i)?; t.end() } RunChild::FootnoteReference(ref f) => { let mut t = serializer.serialize_struct("FootnoteReference", 2)?; t.serialize_field("type", "footnoteReference")?; t.serialize_field("data", f)?; t.end() } RunChild::Shading(ref f) => { let mut t = serializer.serialize_struct("Shading", 2)?; t.serialize_field("type", "shading")?; t.serialize_field("data", f)?; t.end() } } } } impl Run { pub fn new() -> Run { Run { ..Default::default() } } pub fn add_text(mut self, text: impl Into) -> Run { self.children .push(RunChild::Text(Text::new(text.into().replace('\n', "")))); self } pub(crate) fn add_text_without_escape(mut self, text: impl Into) -> Run { self.children.push(RunChild::Text(Text::without_escape( text.into().replace('\n', ""), ))); self } pub fn add_delete_text(mut self, text: impl Into) -> Run { self.children.push(RunChild::DeleteText(DeleteText::new( text.into().replace('\n', ""), ))); self } pub(crate) fn add_delete_text_without_escape(mut self, text: impl Into) -> Run { self.children .push(RunChild::DeleteText(DeleteText::without_escape( text.into().replace('\n', ""), ))); self } pub fn add_field_char(mut self, t: crate::types::FieldCharType, dirty: bool) -> Run { let mut f = FieldChar::new(t); if dirty { f = f.dirty(); }; self.children.push(RunChild::FieldChar(f)); self } pub fn add_tc(mut self, tc: InstrTC) -> Run { self = self.add_field_char(crate::types::FieldCharType::Begin, false); self = self.add_instr_text(InstrText::TC(tc)); self = self.add_field_char(crate::types::FieldCharType::End, false); self } pub fn add_instr_text(mut self, i: InstrText) -> Run { self.children.push(RunChild::InstrText(Box::new(i))); self } pub fn add_delete_instr_text(mut self, i: DeleteInstrText) -> Run { self.children.push(RunChild::DeleteInstrText(Box::new(i))); self } pub fn add_tab(mut self) -> Run { self.children.push(RunChild::Tab(Tab::new())); self } pub fn add_ptab(mut self, ptab: PositionalTab) -> Run { self.children.push(RunChild::PTab(ptab)); self } pub fn add_image(mut self, pic: Pic) -> Run { self.children .push(RunChild::Drawing(Box::new(Drawing::new().pic(pic)))); self } pub(crate) fn add_drawing(mut self, d: Drawing) -> Run { self.children.push(RunChild::Drawing(Box::new(d))); self } // For now reader only // pub(crate) fn add_shape(mut self, d: Shape) -> Run { // self.children.push(RunChild::Shape(Box::new(d))); // self // } pub fn add_break(mut self, break_type: BreakType) -> Run { self.children.push(RunChild::Break(Break::new(break_type))); self } pub fn add_sym(mut self, sym: Sym) -> Run { self.children.push(RunChild::Sym(sym)); self } pub fn style(mut self, style_id: &str) -> Self { self.run_property = self.run_property.style(style_id); self } pub fn size(mut self, size: usize) -> Run { self.run_property = self.run_property.size(size); self } pub fn character_spacing(mut self, v: i32) -> Run { self.run_property = self.run_property.spacing(v); self } pub fn stretch(mut self, v: i32) -> Run { self.run_property = self.run_property.stretch(v); self } pub fn color(mut self, color: impl Into) -> Run { self.run_property = self.run_property.color(color); self } pub fn highlight(mut self, color: impl Into) -> Run { self.run_property = self.run_property.highlight(color); self } pub fn bold(mut self) -> Run { self.run_property = self.run_property.bold(); self } pub fn disable_bold(mut self) -> Run { self.run_property = self.run_property.disable_bold(); self } pub fn italic(mut self) -> Run { self.run_property = self.run_property.italic(); self } pub fn strike(mut self) -> Run { self.run_property = self.run_property.strike(); self } pub fn dstrike(mut self) -> Run { self.run_property = self.run_property.dstrike(); self } pub fn text_border(mut self, b: TextBorder) -> Run { self.run_property = self.run_property.text_border(b); self } pub fn disable_italic(mut self) -> Run { self.run_property = self.run_property.disable_italic(); self } pub fn underline(mut self, line_type: impl Into) -> Run { self.run_property = self.run_property.underline(line_type); self } pub fn vanish(mut self) -> Run { self.run_property = self.run_property.vanish(); self } pub fn fonts(mut self, f: RunFonts) -> Run { self.run_property = self.run_property.fonts(f); self } pub(crate) fn set_property(mut self, p: RunProperty) -> Run { self.run_property = p; self } pub fn add_footnote_reference(mut self, footnote: Footnote) -> Run { self.run_property = RunProperty::new().style("FootnoteReference"); self.children .push(RunChild::FootnoteReference(footnote.into())); self } pub fn shading(mut self, shading: Shading) -> Run { self.run_property = self.run_property.shading(shading); self } } impl BuildXML for RunChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { RunChild::Text(t) => t.build_to(stream), RunChild::Sym(t) => t.build_to(stream), RunChild::DeleteText(t) => t.build_to(stream), RunChild::Tab(t) => t.build_to(stream), RunChild::PTab(t) => t.build_to(stream), RunChild::Break(t) => t.build_to(stream), RunChild::Drawing(t) => t.build_to(stream), RunChild::Shape(_t) => { todo!("Support shape writer.") } RunChild::CommentStart(c) => c.build_to(stream), RunChild::CommentEnd(c) => c.build_to(stream), RunChild::FieldChar(c) => c.build_to(stream), RunChild::InstrText(c) => c.build_to(stream), RunChild::DeleteInstrText(c) => c.build_to(stream), RunChild::InstrTextString(_) => unreachable!(), RunChild::FootnoteReference(c) => c.build_to(stream), RunChild::Shading(s) => s.build_to(stream), } } } impl BuildXML for Run { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_run()? .add_child(&self.run_property)? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = Run::new().add_text("Hello").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_underline() { let b = Run::new().add_text("Hello").underline("single").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_strike() { let b = Run::new().add_text("Hello").strike().build(); assert_eq!( str::from_utf8(&b).unwrap(), 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()), character_spacing: Some(CharacterSpacing::new(100)), ..RunProperty::default() }, }; 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,"characterSpacing":100},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"#, ); } #[test] fn test_run_footnote_reference() { let c = RunChild::FootnoteReference(FootnoteReference::new(1)); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"type":"footnoteReference","data":{"id":1}}"# ); } #[test] fn test_run_shading() { let c = RunChild::Shading(Shading::new()); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"type":"shading","data":{"shdType":"clear","color":"auto","fill":"FFFFFF"}}"# ); } } docx-rs-0.4.18/src/documents/elements/run_fonts.rs000064400000000000000000000113561046102023000202720ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; /* 17.3.2.26 rFonts (Run Fonts) This element specifies the fonts which shall be used to display the text contents of this run. Within a single run, there can be up to four types of font slot which shall each be allowed to use a unique font: - ASCII (i.e., the first 128 Unicode code points) - High ANSI - Complex Script */ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct RunFonts { #[serde(skip_serializing_if = "Option::is_none")] ascii: Option, #[serde(skip_serializing_if = "Option::is_none")] hi_ansi: Option, #[serde(skip_serializing_if = "Option::is_none")] east_asia: Option, #[serde(skip_serializing_if = "Option::is_none")] cs: Option, #[serde(skip_serializing_if = "Option::is_none")] ascii_theme: Option, #[serde(skip_serializing_if = "Option::is_none")] hi_ansi_theme: Option, #[serde(skip_serializing_if = "Option::is_none")] east_asia_theme: Option, #[serde(skip_serializing_if = "Option::is_none")] cs_theme: Option, #[serde(skip_serializing_if = "Option::is_none")] hint: Option, } impl RunFonts { pub fn new() -> RunFonts { Default::default() } pub fn ascii(mut self, f: impl Into) -> Self { let s = f.into(); self.ascii = Some(escape(&s)); self } pub fn hi_ansi(mut self, f: impl Into) -> Self { let s = f.into(); self.hi_ansi = Some(escape(&s)); self } pub fn east_asia(mut self, f: impl Into) -> Self { let s = f.into(); self.east_asia = Some(escape(&s)); self } pub fn cs(mut self, f: impl Into) -> Self { let s = f.into(); self.cs = Some(escape(&s)); self } pub fn ascii_theme(mut self, f: impl Into) -> Self { let s = f.into(); self.ascii_theme = Some(escape(&s)); self } pub fn hi_ansi_theme(mut self, f: impl Into) -> Self { let s = f.into(); self.hi_ansi_theme = Some(escape(&s)); self } pub fn east_asia_theme(mut self, f: impl Into) -> Self { let s = f.into(); self.east_asia_theme = Some(escape(&s)); self } pub fn cs_theme(mut self, f: impl Into) -> Self { let s = f.into(); self.cs_theme = Some(escape(&s)); self } pub fn hint(mut self, f: impl Into) -> Self { let s = f.into(); self.hint = Some(escape(&s)); self } } impl BuildXML for RunFonts { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .run_fonts( self.ascii.as_ref(), self.hi_ansi.as_ref(), self.cs.as_ref(), self.east_asia.as_ref(), self.ascii_theme.as_ref(), self.hi_ansi_theme.as_ref(), self.cs_theme.as_ref(), self.east_asia_theme.as_ref(), self.hint.as_ref(), )? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_run_fonts_east_asia_build() { let c = RunFonts::new().east_asia("Hiragino"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_run_fonts_ascii_build() { let c = RunFonts::new().ascii("Arial"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_run_fonts_hi_ansi_build() { let c = RunFonts::new().hi_ansi("Arial"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_run_fonts_cs_build() { let c = RunFonts::new().cs("Arial"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_run_fonts_with_escape() { let c = RunFonts::new().east_asia(r#""Calibri",sans-serif"#); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""#, ); } } docx-rs-0.4.18/src/documents/elements/run_property.rs000064400000000000000000000252041046102023000210220ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct RunProperty { #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, #[serde(skip_serializing_if = "Option::is_none")] pub sz: Option, #[serde(skip_serializing_if = "Option::is_none")] pub sz_cs: Option, #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, #[serde(skip_serializing_if = "Option::is_none")] pub highlight: Option, #[serde(skip_serializing_if = "Option::is_none")] pub vert_align: Option, #[serde(skip_serializing_if = "Option::is_none")] pub underline: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bold: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bold_cs: Option, #[serde(skip_serializing_if = "Option::is_none")] pub caps: Option, #[serde(skip_serializing_if = "Option::is_none")] pub italic: Option, #[serde(skip_serializing_if = "Option::is_none")] pub italic_cs: Option, #[serde(skip_serializing_if = "Option::is_none")] pub vanish: Option, #[serde(skip_serializing_if = "Option::is_none")] pub spec_vanish: Option, #[serde(skip_serializing_if = "Option::is_none")] pub character_spacing: Option, #[serde(skip_serializing_if = "Option::is_none")] pub stretch: Option, #[serde(skip_serializing_if = "Option::is_none")] pub fonts: Option, #[serde(skip_serializing_if = "Option::is_none")] pub text_border: Option, #[serde(skip_serializing_if = "Option::is_none")] pub del: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ins: Option, #[serde(skip_serializing_if = "Option::is_none")] pub strike: Option, #[serde(skip_serializing_if = "Option::is_none")] pub dstrike: Option, #[serde(skip_serializing_if = "Option::is_none")] pub positional_tab: Option, #[serde(skip_serializing_if = "Option::is_none")] pub shading: Option, } impl RunProperty { pub fn new() -> RunProperty { Default::default() } pub fn style(mut self, style_id: &str) -> Self { self.style = Some(RunStyle::new(style_id)); self } pub fn size(mut self, size: usize) -> RunProperty { self.sz = Some(Sz::new(size)); self.sz_cs = Some(SzCs::new(size)); self } pub fn spacing(mut self, spacing: i32) -> RunProperty { self.character_spacing = Some(CharacterSpacing::new(spacing)); self } pub fn color(mut self, color: impl Into) -> RunProperty { self.color = Some(Color::new(color)); self } pub fn highlight(mut self, color: impl Into) -> RunProperty { self.highlight = Some(Highlight::new(color)); self } pub fn vert_align(mut self, a: VertAlignType) -> Self { self.vert_align = Some(VertAlign::new(a)); self } pub fn bold(mut self) -> RunProperty { self.bold = Some(Bold::new()); self.bold_cs = Some(BoldCs::new()); self } pub fn disable_bold(mut self) -> RunProperty { self.bold = Some(Bold::new().disable()); self.bold_cs = Some(BoldCs::new().disable()); self } pub fn caps(mut self) -> RunProperty { self.caps = Some(Caps::new()); self } pub fn italic(mut self) -> RunProperty { self.italic = Some(Italic::new()); self.italic_cs = Some(ItalicCs::new()); self } pub fn strike(mut self) -> RunProperty { self.strike = Some(Strike::new()); self.dstrike = None; self } pub fn disable_strike(mut self) -> RunProperty { self.strike = Some(Strike::new().disable()); self } pub fn dstrike(mut self) -> RunProperty { self.dstrike = Some(Dstrike::new()); self.strike = None; self } pub fn disable_dstrike(mut self) -> RunProperty { self.dstrike = Some(Dstrike::new().disable()); self } pub fn disable_italic(mut self) -> RunProperty { self.italic = Some(Italic::new().disable()); self.italic_cs = Some(ItalicCs::new().disable()); self } pub fn underline(mut self, line_type: impl Into) -> RunProperty { self.underline = Some(Underline::new(line_type)); self } pub fn vanish(mut self) -> RunProperty { self.vanish = Some(Vanish::new()); self } pub fn spec_vanish(mut self) -> RunProperty { self.spec_vanish = Some(SpecVanish::new()); self } pub fn fonts(mut self, font: RunFonts) -> RunProperty { self.fonts = Some(font); self } pub fn character_spacing(mut self, v: i32) -> RunProperty { self.character_spacing = Some(CharacterSpacing::new(v)); self } pub fn stretch(mut self, v: i32) -> RunProperty { self.stretch = Some(Stretch::new(v)); self } pub fn text_border(mut self, b: TextBorder) -> Self { self.text_border = Some(b); self } pub fn delete(mut self, d: Delete) -> Self { self.del = Some(d); self } pub fn insert(mut self, i: Insert) -> Self { self.ins = Some(i); self } pub fn ptab(mut self, ptab: PositionalTab) -> Self { self.positional_tab = Some(ptab); self } pub fn shading(mut self, s: Shading) -> Self { self.shading = Some(s); self } } impl BuildXML for RunProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_run_property()? .add_optional_child(&self.sz)? .add_optional_child(&self.sz_cs)? .add_optional_child(&self.color)? .add_optional_child(&self.bold)? .add_optional_child(&self.bold_cs)? .add_optional_child(&self.caps)? .add_optional_child(&self.italic)? .add_optional_child(&self.italic_cs)? .add_optional_child(&self.strike)? .add_optional_child(&self.dstrike)? .add_optional_child(&self.highlight)? .add_optional_child(&self.underline)? .add_optional_child(&self.vanish)? .add_optional_child(&self.spec_vanish)? .add_optional_child(&self.fonts)? .add_optional_child(&self.text_border)? .add_optional_child(&self.ins)? .add_optional_child(&self.del)? .add_optional_child(&self.vert_align)? .add_optional_child(&self.character_spacing)? .add_optional_child(&self.stretch)? .add_optional_child(&self.style)? .add_optional_child(&self.positional_tab)? .add_optional_child(&self.shading)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_size() { let c = RunProperty::new().size(10).color("FFFFFF"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_highlight() { let c = RunProperty::new().highlight("FFFFFF"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_bold() { let c = RunProperty::new().bold(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_strike() { let c = RunProperty::new().strike(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_underline() { let c = RunProperty::new().underline("single"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_vanish() { let c = RunProperty::new().vanish(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_run_fonts() { let c = RunProperty::new().fonts(RunFonts::new().east_asia("Hiragino")); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_character_spacing() { let c = RunProperty::new().spacing(20); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_stretch() { let c = RunProperty::new().stretch(80); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_ptab() { let c = RunProperty::new().ptab(PositionalTab::new( PositionalTabAlignmentType::Left, PositionalTabRelativeTo::Margin, TabLeaderType::None, )); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_character_shading() { let c = RunProperty::new().shading( Shading::new() .shd_type(ShdType::Clear) .fill("FFFFFF") .color("auto"), ); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_dstrike() { let c = RunProperty::new().dstrike(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/run_property_default.rs000064400000000000000000000033461046102023000225310ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct RunPropertyDefault { run_property: RunProperty, } impl RunPropertyDefault { pub fn new() -> RunPropertyDefault { Default::default() } pub fn size(mut self, size: usize) -> Self { self.run_property = self.run_property.size(size); self } pub fn spacing(mut self, spacing: i32) -> Self { self.run_property = self.run_property.spacing(spacing); self } pub fn fonts(mut self, font: RunFonts) -> Self { self.run_property = self.run_property.fonts(font); self } pub(crate) fn run_property(mut self, p: RunProperty) -> Self { self.run_property = p; self } } impl Default for RunPropertyDefault { fn default() -> Self { let run_property = RunProperty::new(); RunPropertyDefault { run_property } } } impl BuildXML for RunPropertyDefault { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_run_property_default()? .add_child(&self.run_property)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = RunPropertyDefault::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/run_style.rs000064400000000000000000000024161046102023000202760ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct RunStyle { pub val: String, } impl Default for RunStyle { fn default() -> Self { RunStyle { val: "Normal".to_owned(), } } } impl RunStyle { pub fn new(val: impl Into) -> RunStyle { RunStyle { val: escape(&val.into()), } } } impl BuildXML for RunStyle { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).run_style(&self.val)?.into_inner() } } impl Serialize for RunStyle { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_r_style() { let c = RunStyle::new("Heading"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/section.rs000064400000000000000000000027221046102023000177160ustar 00000000000000use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Section { property: SectionProperty, } impl Section { pub fn new() -> Section { Default::default() } } impl Default for Section { fn default() -> Self { Self { property: SectionProperty::new(), } } } impl BuildXML for Section { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let id = crate::generate_para_id(); XMLBuilder::from(stream) .open_paragraph(&id)? .open_paragraph_property()? .add_child(&self.property)? .close()? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_section_property_default() { let c = Section::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/section_property.rs000064400000000000000000000222071046102023000216620ustar 00000000000000use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use crate::{Footer, Header}; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SectionProperty { pub page_size: PageSize, pub page_margin: PageMargin, pub columns: usize, pub space: usize, pub title_pg: bool, pub text_direction: String, #[serde(skip_serializing_if = "Option::is_none")] pub doc_grid: Option, #[serde(skip_serializing_if = "Option::is_none")] pub header_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub header: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub first_header_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub first_header: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub even_header_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub even_header: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub footer_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub footer: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub first_footer_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub first_footer: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub even_footer_reference: Option, #[serde(skip_serializing_if = "Option::is_none")] pub even_footer: Option
, #[serde(skip_serializing_if = "Option::is_none")] pub section_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub page_num_type: Option, } impl SectionProperty { pub fn new() -> SectionProperty { Default::default() } pub fn page_size(mut self, size: PageSize) -> Self { self.page_size = size; self } pub fn page_margin(mut self, margin: PageMargin) -> Self { self.page_margin = margin; self } pub fn page_orient(mut self, o: PageOrientationType) -> Self { self.page_size = self.page_size.orient(o); self } pub fn doc_grid(mut self, doc_grid: DocGrid) -> Self { self.doc_grid = Some(doc_grid); self } pub fn text_direction(mut self, direction: String) -> Self { self.text_direction = direction; self } pub fn title_pg(mut self) -> Self { self.title_pg = true; self } pub fn header(mut self, h: Header, rid: &str) -> Self { self.header_reference = Some(HeaderReference::new("default", rid)); self.header = Some(h); self } pub fn first_header(mut self, h: Header, rid: &str) -> Self { self.first_header_reference = Some(HeaderReference::new("first", rid)); self.first_header = Some(h); self.title_pg = true; self } pub fn first_header_without_title_pg(mut self, h: Header, rid: &str) -> Self { self.first_header_reference = Some(HeaderReference::new("first", rid)); self.first_header = Some(h); self } pub fn even_header(mut self, h: Header, rid: &str) -> Self { self.even_header_reference = Some(HeaderReference::new("even", rid)); self.even_header = Some(h); self } pub fn footer(mut self, h: Footer, rid: &str) -> Self { self.footer_reference = Some(FooterReference::new("default", rid)); self.footer = Some(h); self } pub fn first_footer(mut self, h: Footer, rid: &str) -> Self { self.first_footer_reference = Some(FooterReference::new("first", rid)); self.first_footer = Some(h); self.title_pg = true; self } pub fn first_footer_without_title_pg(mut self, h: Footer, rid: &str) -> Self { self.first_footer_reference = Some(FooterReference::new("first", rid)); self.first_footer = Some(h); self } pub fn even_footer(mut self, h: Footer, rid: &str) -> Self { self.even_footer_reference = Some(FooterReference::new("even", rid)); self.even_footer = Some(h); self } pub fn get_headers(&self) -> Vec<&Header> { let mut headers = vec![]; if let Some(ref header) = self.header { headers.push(header); } if let Some(ref header) = self.first_header { headers.push(header); } if let Some(ref header) = self.even_header { headers.push(header); } headers } pub fn get_footers(&self) -> Vec<&Footer> { let mut footers = vec![]; if let Some(ref footer) = self.footer { footers.push(footer); } if let Some(ref footer) = self.first_footer { footers.push(footer); } if let Some(ref footer) = self.even_footer { footers.push(footer); } footers } pub fn page_num_type(mut self, h: PageNumType) -> Self { self.page_num_type = Some(h); self } } impl Default for SectionProperty { fn default() -> Self { Self { page_size: PageSize::new(), page_margin: PageMargin::new(), columns: 1, space: 425, title_pg: false, text_direction: "lrTb".to_string(), doc_grid: None, // headers header_reference: None, header: None, first_header_reference: None, first_header: None, even_header_reference: None, even_header: None, // footers footer_reference: None, footer: None, first_footer_reference: None, first_footer: None, even_footer_reference: None, even_footer: None, section_type: None, page_num_type: None, } } } impl BuildXML for SectionProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_section_property()? .add_child(&self.page_size)? .add_child(&self.page_margin)? .columns(&format!("{}", &self.space), &format!("{}", &self.columns))? .add_optional_child(&self.doc_grid)? .add_optional_child(&self.header_reference)? .add_optional_child(&self.first_header_reference)? .add_optional_child(&self.even_header_reference)? .add_optional_child(&self.footer_reference)? .add_optional_child(&self.first_footer_reference)? .add_optional_child(&self.even_footer_reference)? .add_optional_child(&self.page_num_type)? .apply_if(self.text_direction != "lrTb", |b| { b.text_direction(&self.text_direction) })? .apply_opt(self.section_type, |t, b| b.type_tag(&t.to_string()))? .apply_if(self.title_pg, |b| b.title_pg())? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn text_section_text_direction() { let mut c = SectionProperty::new(); c = c.text_direction("tbRl".to_string()); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ) } #[test] fn test_section_property_default() { let c = SectionProperty::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_section_property_with_footer() { let c = SectionProperty::new().footer(Footer::new(), "rId6"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_section_property_with_title_pf() { let c = SectionProperty::new().title_pg(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/shading.rs000064400000000000000000000024141046102023000176650ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; // INFO: Theme is not supported now. #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Shading { pub shd_type: ShdType, pub color: String, pub fill: String, } impl Default for Shading { fn default() -> Self { Shading { shd_type: ShdType::Clear, color: "auto".to_owned(), fill: "FFFFFF".to_owned(), } } } impl Shading { pub fn new() -> Shading { Shading::default() } pub fn color(mut self, color: impl Into) -> Shading { self.color = color.into(); self } pub fn fill(mut self, fill: impl Into) -> Shading { self.fill = fill.into(); self } pub fn shd_type(mut self, shd_type: ShdType) -> Shading { self.shd_type = shd_type; self } } impl BuildXML for Shading { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .shd(&self.shd_type.to_string(), &self.color, &self.fill)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/shape.rs000064400000000000000000000023161046102023000173510ustar 00000000000000use serde::Serialize; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct Shape { #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, #[serde(skip_serializing_if = "Option::is_none")] pub image_data: Option, } // Experimental, For now reader only. #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct ImageData { pub id: String, } impl Shape { pub fn new() -> Self { Default::default() } pub fn style(mut self, style: impl Into) -> Self { self.style = Some(style.into()); self } pub fn image_data(mut self, id: impl Into) -> Self { self.image_data = Some(ImageData { id: id.into() }); self } } // impl BuildXML for Shape { // fn build(&self) -> Vec { // let b = XMLBuilder::new(); // let mut b = b.open_pict(); // b = b.add_child(t), // b.close().build() // } // } docx-rs-0.4.18/src/documents/elements/spec_vanish.rs000064400000000000000000000013331046102023000205510ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq, Default)] pub struct SpecVanish {} impl SpecVanish { pub fn new() -> SpecVanish { SpecVanish {} } } impl BuildXML for SpecVanish { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).spec_vanish()?.into_inner() } } impl Serialize for SpecVanish { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(true) } } docx-rs-0.4.18/src/documents/elements/start.rs000064400000000000000000000017671046102023000174170ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Default)] pub struct Start { val: usize, } impl Start { pub fn new(val: usize) -> Start { Start { val } } } impl BuildXML for Start { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).start(self.val)?.into_inner() } } impl Serialize for Start { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.val as u32) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_start() { let c = Start::new(1); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/stretch.rs000064400000000000000000000021631046102023000177250ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Stretch { value: i32, } impl Stretch { pub fn new(s: i32) -> Stretch { Self { value: s } } } impl BuildXML for Stretch { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).w(self.value)?.into_inner() } } impl Serialize for Stretch { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_i32(self.value) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_stretch() { let b = Stretch::new(200).build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_stretch_json() { let s = Stretch { value: 100 }; assert_eq!(serde_json::to_string(&s).unwrap(), r#"100"#); } } docx-rs-0.4.18/src/documents/elements/strike.rs000064400000000000000000000017771046102023000175640ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Strike { pub val: bool, } impl Strike { pub fn new() -> Strike { Default::default() } pub fn disable(mut self) -> Strike { self.val = false; self } } impl Default for Strike { fn default() -> Self { Self { val: true } } } impl Serialize for Strike { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(self.val) } } impl BuildXML for Strike { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { if self.val { XMLBuilder::from(stream).strike()?.into_inner() } else { XMLBuilder::from(stream).disable_strike()?.into_inner() } } } docx-rs-0.4.18/src/documents/elements/structured_data_tag.rs000064400000000000000000000141611046102023000223020ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; // use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StructuredDataTag { pub children: Vec, pub property: StructuredDataTagProperty, pub has_numbering: bool, } impl Default for StructuredDataTag { fn default() -> Self { Self { children: Vec::new(), property: StructuredDataTagProperty::new(), has_numbering: false, } } } #[derive(Debug, Clone, PartialEq)] pub enum StructuredDataTagChild { Run(Box), Paragraph(Box), Table(Box
), BookmarkStart(BookmarkStart), BookmarkEnd(BookmarkEnd), CommentStart(Box), CommentEnd(CommentRangeEnd), StructuredDataTag(Box), } impl BuildXML for StructuredDataTagChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { StructuredDataTagChild::Run(v) => v.build_to(stream), StructuredDataTagChild::Paragraph(v) => v.build_to(stream), StructuredDataTagChild::Table(v) => v.build_to(stream), StructuredDataTagChild::BookmarkStart(v) => v.build_to(stream), StructuredDataTagChild::BookmarkEnd(v) => v.build_to(stream), StructuredDataTagChild::CommentStart(v) => v.build_to(stream), StructuredDataTagChild::CommentEnd(v) => v.build_to(stream), StructuredDataTagChild::StructuredDataTag(v) => v.build_to(stream), } } } impl Serialize for StructuredDataTagChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { StructuredDataTagChild::Run(ref r) => { let mut t = serializer.serialize_struct("Run", 2)?; t.serialize_field("type", "run")?; t.serialize_field("data", r)?; t.end() } StructuredDataTagChild::Paragraph(ref r) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", r)?; t.end() } StructuredDataTagChild::Table(ref r) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", r)?; t.end() } StructuredDataTagChild::BookmarkStart(ref c) => { let mut t = serializer.serialize_struct("BookmarkStart", 2)?; t.serialize_field("type", "bookmarkStart")?; t.serialize_field("data", c)?; t.end() } StructuredDataTagChild::BookmarkEnd(ref c) => { let mut t = serializer.serialize_struct("BookmarkEnd", 2)?; t.serialize_field("type", "bookmarkEnd")?; t.serialize_field("data", c)?; t.end() } StructuredDataTagChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } StructuredDataTagChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } StructuredDataTagChild::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } } } } impl StructuredDataTag { pub fn new() -> Self { Default::default() } pub fn add_run(mut self, run: Run) -> Self { self.children .push(StructuredDataTagChild::Run(Box::new(run))); self } pub fn add_paragraph(mut self, p: Paragraph) -> Self { if p.has_numbering { self.has_numbering = true } self.children .push(StructuredDataTagChild::Paragraph(Box::new(p))); self } pub fn add_table(mut self, t: Table) -> Self { if t.has_numbering { self.has_numbering = true } self.children .push(StructuredDataTagChild::Table(Box::new(t))); self } pub fn data_binding(mut self, d: DataBinding) -> Self { self.property = self.property.data_binding(d); self } pub fn alias(mut self, v: impl Into) -> Self { self.property = self.property.alias(v); self } } impl BuildXML for StructuredDataTag { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_structured_tag()? .add_child(&self.property)? .open_structured_tag_content()? .add_children(&self.children)? .close()? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_sdt() { let b = StructuredDataTag::new() .data_binding(DataBinding::new().xpath("root/hello")) .add_run(Run::new().add_text("Hello")) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } } docx-rs-0.4.18/src/documents/elements/structured_data_tag_property.rs000064400000000000000000000040021046102023000242370ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; // use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StructuredDataTagProperty { pub run_property: RunProperty, pub data_binding: Option, pub alias: Option, } impl Default for StructuredDataTagProperty { fn default() -> Self { Self { run_property: RunProperty::new(), data_binding: None, alias: None, } } } impl StructuredDataTagProperty { pub fn new() -> Self { Default::default() } pub fn data_binding(mut self, d: DataBinding) -> Self { self.data_binding = Some(d); self } pub fn alias(mut self, v: impl Into) -> Self { self.alias = Some(v.into()); self } } impl BuildXML for StructuredDataTagProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_structured_tag_property()? .add_child(&self.run_property)? .add_optional_child(&self.data_binding)? .apply_opt(self.alias.as_ref(), |alias, b| b.alias(alias))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let c = StructuredDataTagProperty::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_with_alias() { let c = StructuredDataTagProperty::new().alias("summary"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/style.rs000064400000000000000000000253441046102023000174170ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::types::*; use crate::xml_builder::*; use crate::StyleType; use super::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Style { pub style_id: String, pub name: Name, pub style_type: StyleType, pub run_property: RunProperty, pub paragraph_property: ParagraphProperty, pub table_property: TableProperty, pub table_cell_property: TableCellProperty, pub based_on: Option, pub next: Option, #[serde(skip_serializing_if = "Option::is_none")] pub link: Option, } impl Default for Style { fn default() -> Self { let name = Name::new(""); let rpr = RunProperty::new(); let ppr = ParagraphProperty::new(); Style { style_id: "".to_owned(), style_type: StyleType::Paragraph, name, run_property: rpr, paragraph_property: ppr, table_property: TableProperty::new(), table_cell_property: TableCellProperty::new(), based_on: None, next: None, link: None, } } } impl Style { pub fn new(style_id: impl Into, style_type: StyleType) -> Self { let default = Default::default(); Style { style_id: escape(&style_id.into()), style_type, ..default } } pub fn name(mut self, name: impl Into) -> Self { self.name = Name::new(name); self } pub fn based_on(mut self, base: impl Into) -> Self { self.based_on = Some(BasedOn::new(base)); self } pub fn next(mut self, next: impl Into) -> Self { self.next = Some(Next::new(next)); self } pub fn link(mut self, link: impl Into) -> Self { self.link = Some(Link::new(link)); self } pub fn size(mut self, size: usize) -> Self { self.run_property = self.run_property.size(size); self } pub fn color(mut self, color: impl Into) -> Self { self.run_property = self.run_property.color(color); self } pub fn highlight(mut self, color: impl Into) -> Self { self.run_property = self.run_property.highlight(color); self } pub fn bold(mut self) -> Self { self.run_property = self.run_property.bold(); self } pub fn italic(mut self) -> Self { self.run_property = self.run_property.italic(); self } pub fn underline(mut self, line_type: impl Into) -> Self { self.run_property = self.run_property.underline(line_type); self } pub fn vanish(mut self) -> Self { self.run_property = self.run_property.vanish(); self } pub fn text_border(mut self, b: TextBorder) -> Self { self.run_property = self.run_property.text_border(b); self } pub fn fonts(mut self, f: RunFonts) -> Self { self.run_property = self.run_property.fonts(f); self } pub fn align(mut self, alignment_type: AlignmentType) -> Self { self.paragraph_property = self.paragraph_property.align(alignment_type); self } pub fn text_alignment(mut self, alignment_type: TextAlignmentType) -> Self { self.paragraph_property = self.paragraph_property.text_alignment(alignment_type); self } pub fn snap_to_grid(mut self, v: bool) -> Self { self.paragraph_property = self.paragraph_property.snap_to_grid(v); self } pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { self.paragraph_property = self.paragraph_property.line_spacing(spacing); self } pub fn indent( mut self, left: Option, special_indent: Option, end: Option, start_chars: Option, ) -> Self { self.paragraph_property = self.paragraph_property .indent(left, special_indent, end, start_chars); self } pub fn hanging_chars(mut self, chars: i32) -> Self { self.paragraph_property = self.paragraph_property.hanging_chars(chars); self } pub fn first_line_chars(mut self, chars: i32) -> Self { self.paragraph_property = self.paragraph_property.first_line_chars(chars); self } pub fn outline_lvl(mut self, l: usize) -> Self { self.paragraph_property = self.paragraph_property.outline_lvl(l); self } pub fn table_property(mut self, p: TableProperty) -> Self { self.table_property = p; self } pub fn table_indent(mut self, v: i32) -> Self { self.table_property = self.table_property.indent(v); self } pub fn table_align(mut self, v: TableAlignmentType) -> Self { self.table_property = self.table_property.align(v); self } pub fn style(mut self, s: impl Into) -> Self { self.table_property = self.table_property.style(s); self } pub fn layout(mut self, t: TableLayoutType) -> Self { self.table_property = self.table_property.layout(t); self } pub fn width(mut self, w: usize, t: WidthType) -> Self { self.table_property = self.table_property.width(w, t); self } pub fn margins(mut self, margins: TableCellMargins) -> Self { self.table_property = self.table_property.set_margins(margins); self } pub fn set_borders(mut self, borders: TableBorders) -> Self { self.table_property = self.table_property.set_borders(borders); self } pub fn set_border(mut self, border: TableBorder) -> Self { self.table_property = self.table_property.set_border(border); self } pub fn clear_border(mut self, position: TableBorderPosition) -> Self { self.table_property = self.table_property.clear_border(position); self } pub fn clear_all_border(mut self) -> Self { self.table_property = self.table_property.clear_all_border(); self } pub fn table_cell_property(mut self, p: TableCellProperty) -> Self { self.table_cell_property = p; self } // frameProperty pub fn wrap(mut self, wrap: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { wrap: Some(wrap.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn v_anchor(mut self, anchor: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { v_anchor: Some(anchor.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn h_anchor(mut self, anchor: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { h_anchor: Some(anchor.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn h_rule(mut self, r: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { h_rule: Some(r.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn x_align(mut self, align: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { x_align: Some(align.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn y_align(mut self, align: impl Into) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { y_align: Some(align.into()), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn h_space(mut self, x: i32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { h_space: Some(x), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn v_space(mut self, x: i32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { v_space: Some(x), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn frame_x(mut self, x: i32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { x: Some(x), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn frame_y(mut self, y: i32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { y: Some(y), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn frame_width(mut self, n: u32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { w: Some(n), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } pub fn frame_height(mut self, n: u32) -> Self { self.paragraph_property.frame_property = Some(FrameProperty { h: Some(n), ..self.paragraph_property.frame_property.unwrap_or_default() }); self } } impl BuildXML for Style { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { // Set "Normal" as default if you need change these values please fix it XMLBuilder::from(stream) .open_style(self.style_type, &self.style_id)? .add_child(&self.name)? .add_child(&self.run_property)? .add_child(&self.paragraph_property)? .apply_if(self.style_type == StyleType::Table, |b| { b.add_child(&self.table_cell_property)? .add_child(&self.table_property) })? .add_optional_child(&self.next)? .add_optional_child(&self.link)? .add_child(&QFormat::new())? .add_optional_child(&self.based_on)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Style::new("Heading", StyleType::Paragraph).name("Heading1"); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/sym.rs000064400000000000000000000020751046102023000170630ustar 00000000000000use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::Deserialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Sym { pub font: String, pub char: String, } impl Sym { pub fn new(font: impl Into, char: impl Into) -> Self { Self { font: font.into(), char: char.into(), } } } impl BuildXML for Sym { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .sym(&self.font, &self.char)? .into_inner() } } impl Serialize for Sym { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut t = serializer.serialize_struct("Sym", 1)?; t.serialize_field("font", &self.font)?; t.serialize_field("char", &self.char)?; t.end() } } docx-rs-0.4.18/src/documents/elements/sz.rs000064400000000000000000000017571046102023000167150ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Sz { val: usize, } impl Sz { pub fn new(val: usize) -> Sz { Sz { val } } } impl BuildXML for Sz { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).sz(self.val)?.into_inner() } } impl Serialize for Sz { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.val as u32) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Sz::new(20); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/sz_cs.rs000064400000000000000000000020011046102023000173610ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; #[derive(Deserialize, Debug, Clone, PartialEq)] pub struct SzCs { val: usize, } impl SzCs { pub fn new(val: usize) -> SzCs { SzCs { val } } } impl BuildXML for SzCs { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).sz_cs(self.val)?.into_inner() } } impl Serialize for SzCs { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u32(self.val as u32) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_sz_cs() { let c = SzCs::new(20); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/tab.rs000064400000000000000000000020731046102023000170170ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] pub struct Tab { pub val: Option, pub leader: Option, pub pos: Option, } impl Tab { pub fn new() -> Self { Default::default() } pub fn val(mut self, v: TabValueType) -> Self { self.val = Some(v); self } pub fn leader(mut self, v: TabLeaderType) -> Self { self.leader = Some(v); self } pub fn pos(mut self, v: usize) -> Self { self.pos = Some(v); self } } impl BuildXML for Tab { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .tab(self.val, self.leader, self.pos)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/table.rs000064400000000000000000000151611046102023000173420ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Table { pub rows: Vec, pub grid: Vec, pub has_numbering: bool, pub property: TableProperty, } #[derive(Debug, Clone, PartialEq)] pub enum TableChild { TableRow(TableRow), } impl BuildXML for TableChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { TableChild::TableRow(v) => v.build_to(stream), } } } impl Table { pub fn new(rows: Vec) -> Table { let property = TableProperty::new(); let has_numbering = rows.iter().any(|c| c.has_numbering); let grid = vec![]; let rows = rows.into_iter().map(TableChild::TableRow).collect(); Self { property, rows, grid, has_numbering, } } pub fn without_borders(rows: Vec) -> Table { let property = TableProperty::without_borders(); let has_numbering = rows.iter().any(|c| c.has_numbering); let grid = vec![]; let rows = rows.into_iter().map(TableChild::TableRow).collect(); Self { property, rows, grid, has_numbering, } } pub fn add_row(mut self, row: TableRow) -> Table { self.rows.push(TableChild::TableRow(row)); self } pub fn set_grid(mut self, grid: Vec) -> Table { self.grid = grid; self } pub fn indent(mut self, v: i32) -> Table { self.property = self.property.indent(v); self } pub fn align(mut self, v: TableAlignmentType) -> Table { self.property = self.property.align(v); self } pub fn style(mut self, s: impl Into) -> Table { self.property = self.property.style(s); self } pub fn layout(mut self, t: TableLayoutType) -> Table { self.property = self.property.layout(t); self } pub fn position(mut self, p: TablePositionProperty) -> Self { self.property = self.property.position(p); self } pub fn width(mut self, w: usize, t: WidthType) -> Table { self.property = self.property.width(w, t); self } pub fn margins(mut self, margins: TableCellMargins) -> Self { self.property = self.property.set_margins(margins); self } pub fn set_borders(mut self, borders: TableBorders) -> Self { self.property = self.property.set_borders(borders); self } pub fn set_border(mut self, border: TableBorder) -> Self { self.property = self.property.set_border(border); self } pub fn clear_border(mut self, position: TableBorderPosition) -> Self { self.property = self.property.clear_border(position); self } pub fn clear_all_border(mut self) -> Self { self.property = self.property.clear_all_border(); self } } impl BuildXML for Table { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let grid = TableGrid::new(self.grid.clone()); XMLBuilder::from(stream) .open_table()? .add_child(&self.property)? .add_child(&grid)? .add_children(&self.rows)? .close()? .into_inner() } } impl Serialize for TableChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { TableChild::TableRow(ref r) => { let mut t = serializer.serialize_struct("TableRow", 2)?; t.serialize_field("type", "tableRow")?; t.serialize_field("data", r)?; t.end() } } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table() { let b = Table::new(vec![TableRow::new(vec![])]).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_table_grid() { let b = Table::new(vec![TableRow::new(vec![])]) .set_grid(vec![100, 200]) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[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":{"borderType":"single","size":2,"color":"000000","position":"top","space":0},"left":{"borderType":"single","size":2,"color":"000000","position":"left","space":0},"bottom":{"borderType":"single","size":2,"color":"000000","position":"bottom","space":0},"right":{"borderType":"single","size":2,"color":"000000","position":"right","space":0},"insideH":{"borderType":"single","size":2,"color":"000000","position":"insideH","space":0},"insideV":{"borderType":"single","size":2,"color":"000000","position":"insideV","space":0}}}}"# ); } } docx-rs-0.4.18/src/documents/elements/table_borders.rs000064400000000000000000000161541046102023000210650ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; /* Please see. L.4.3.2.18 Cell Border Properties left – left border right – right border top – top border bottom – bottom border insideH – inner horizontal borders insideV – inner vertical borders tl2br – diagonal border from top left corner to bottom right corner tr2bl – diagonal border from top right corner to bottom left corner */ #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TableBorder { pub border_type: BorderType, pub size: usize, pub color: String, position: TableBorderPosition, space: usize, } impl TableBorder { pub fn new(position: TableBorderPosition) -> TableBorder { TableBorder { position, border_type: BorderType::Single, size: 2, space: 0, color: "000000".to_owned(), } } pub fn color(mut self, color: impl Into) -> TableBorder { self.color = color.into(); self } pub fn size(mut self, size: usize) -> TableBorder { self.size = size; self } pub fn border_type(mut self, border_type: BorderType) -> TableBorder { self.border_type = border_type; self } } impl BuildXML for TableBorder { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let func = match self.position { TableBorderPosition::Top => XMLBuilder::border_top, TableBorderPosition::Left => XMLBuilder::border_left, TableBorderPosition::Bottom => XMLBuilder::border_bottom, TableBorderPosition::Right => XMLBuilder::border_right, TableBorderPosition::InsideH => XMLBuilder::border_inside_h, TableBorderPosition::InsideV => XMLBuilder::border_inside_v, }; XMLBuilder::from(stream) .apply(|b| func(b, self.border_type, self.size, self.space, &self.color))? .into_inner() } } #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TableBorders { top: Option, left: Option, bottom: Option, right: Option, inside_h: Option, inside_v: Option, } impl Default for TableBorders { fn default() -> TableBorders { TableBorders { top: Some(TableBorder::new(TableBorderPosition::Top)), left: Some(TableBorder::new(TableBorderPosition::Left)), bottom: Some(TableBorder::new(TableBorderPosition::Bottom)), right: Some(TableBorder::new(TableBorderPosition::Right)), inside_h: Some(TableBorder::new(TableBorderPosition::InsideH)), inside_v: Some(TableBorder::new(TableBorderPosition::InsideV)), } } } impl TableBorders { pub fn new() -> TableBorders { Default::default() } pub fn with_empty() -> TableBorders { TableBorders { top: None, left: None, bottom: None, right: None, inside_h: None, inside_v: None, } } pub fn set(mut self, border: TableBorder) -> Self { match border.position { TableBorderPosition::Top => self.top = Some(border), TableBorderPosition::Left => self.left = Some(border), TableBorderPosition::Bottom => self.bottom = Some(border), TableBorderPosition::Right => self.right = Some(border), TableBorderPosition::InsideH => self.inside_h = Some(border), TableBorderPosition::InsideV => self.inside_v = Some(border), }; self } pub fn clear(mut self, position: TableBorderPosition) -> Self { let nil = TableBorder::new(position.clone()).border_type(BorderType::Nil); match position { TableBorderPosition::Top => self.top = Some(nil), TableBorderPosition::Left => self.left = Some(nil), TableBorderPosition::Bottom => self.bottom = Some(nil), TableBorderPosition::Right => self.right = Some(nil), TableBorderPosition::InsideH => self.inside_h = Some(nil), TableBorderPosition::InsideV => self.inside_v = Some(nil), }; self } pub fn clear_all(mut self) -> Self { self.top = Some(TableBorder::new(TableBorderPosition::Top).border_type(BorderType::Nil)); self.left = Some(TableBorder::new(TableBorderPosition::Left).border_type(BorderType::Nil)); self.bottom = Some(TableBorder::new(TableBorderPosition::Bottom).border_type(BorderType::Nil)); self.right = Some(TableBorder::new(TableBorderPosition::Right).border_type(BorderType::Nil)); self.inside_h = Some(TableBorder::new(TableBorderPosition::InsideH).border_type(BorderType::Nil)); self.inside_v = Some(TableBorder::new(TableBorderPosition::InsideV).border_type(BorderType::Nil)); self } } impl BuildXML for TableBorders { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_borders()? .add_optional_child(&self.top)? .add_optional_child(&self.left)? .add_optional_child(&self.bottom)? .add_optional_child(&self.right)? .add_optional_child(&self.inside_h)? .add_optional_child(&self.inside_v)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_borders() { let b = TableBorders::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_table_borders_set() { let b = TableBorders::new() .set(TableBorder::new(TableBorderPosition::Left).color("AAAAAA")) .clear(TableBorderPosition::Top) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_cell.rs000064400000000000000000000156351046102023000203470ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TableCell { pub children: Vec, pub property: TableCellProperty, pub has_numbering: bool, } #[derive(Debug, Clone, PartialEq)] pub enum TableCellContent { Paragraph(Paragraph), Table(Table), StructuredDataTag(Box), TableOfContents(Box), } 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() } TableCellContent::Table(ref s) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", s)?; t.end() } TableCellContent::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } TableCellContent::TableOfContents(ref r) => { let mut t = serializer.serialize_struct("TableOfContents", 2)?; t.serialize_field("type", "tableOfContents")?; t.serialize_field("data", r)?; t.end() } } } } impl TableCell { pub fn new() -> TableCell { Default::default() } pub fn add_paragraph(mut self, p: Paragraph) -> TableCell { if p.has_numbering { self.has_numbering = true } self.children.push(TableCellContent::Paragraph(p)); self } pub fn add_table_of_contents(mut self, t: TableOfContents) -> Self { self.children .push(TableCellContent::TableOfContents(Box::new(t))); self } pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { self.children .push(TableCellContent::StructuredDataTag(Box::new(t))); self } pub fn add_table(mut self, t: Table) -> TableCell { if t.has_numbering { self.has_numbering = true } self.children.push(TableCellContent::Table(t)); self } pub fn vertical_merge(mut self, t: VMergeType) -> TableCell { self.property = self.property.vertical_merge(t); self } pub fn shading(mut self, s: Shading) -> TableCell { self.property = self.property.shading(s); self } pub fn vertical_align(mut self, t: VAlignType) -> TableCell { self.property = self.property.vertical_align(t); self } pub fn text_direction(mut self, t: TextDirectionType) -> TableCell { self.property = self.property.text_direction(t); self } pub fn grid_span(mut self, v: usize) -> TableCell { self.property = self.property.grid_span(v); self } pub fn width(mut self, v: usize, t: WidthType) -> TableCell { self.property = self.property.width(v, t); self } pub fn set_border(mut self, border: TableCellBorder) -> Self { self.property = self.property.set_border(border); self } pub fn set_borders(mut self, borders: TableCellBorders) -> Self { self.property = self.property.set_borders(borders); self } pub fn clear_border(mut self, position: TableCellBorderPosition) -> Self { self.property = self.property.clear_border(position); self } pub fn clear_all_border(mut self) -> Self { self.property = self.property.clear_all_border(); self } } impl Default for TableCell { fn default() -> Self { let property = TableCellProperty::new(); let children = vec![]; Self { property, children, has_numbering: false, } } } impl BuildXML for TableCell { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_cell()? .add_child(&self.property)? .apply_each(&self.children, |ch, b| { match ch { TableCellContent::Paragraph(p) => b.add_child(p), TableCellContent::Table(t) => { b.add_child(t)? // INFO: We need to add empty paragraph when parent cell includes only cell. .apply_if(self.children.len() == 1, |b| b.add_child(&Paragraph::new())) } TableCellContent::StructuredDataTag(t) => b.add_child(&t), TableCellContent::TableOfContents(t) => b.add_child(&t), } })? // INFO: We need to add empty paragraph when parent cell includes only cell. .apply_if(self.children.is_empty(), |b| b.add_child(&Paragraph::new()))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_cell() { let b = TableCell::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_cell_add_p() { let b = TableCell::new() .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build(); assert_eq!( str::from_utf8(&b).unwrap(), 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":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#, ); } } docx-rs-0.4.18/src/documents/elements/table_cell_borders.rs000064400000000000000000000165151046102023000220650ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; /* Please see. L.4.3.2.18 Cell Border Properties left – left border right – right border top – top border bottom – bottom border insideH – inner horizontal borders insideV – inner vertical borders tl2br – diagonal border from top left corner to bottom right corner tr2bl – diagonal border from top right corner to bottom left corner */ #[derive(Serialize, Debug, Clone, PartialEq)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct TableCellBorder { pub border_type: BorderType, pub size: usize, pub color: String, position: TableCellBorderPosition, space: usize, } impl TableCellBorder { pub fn new(position: TableCellBorderPosition) -> TableCellBorder { TableCellBorder { position, border_type: BorderType::Single, size: 2, space: 0, color: "000000".to_owned(), } } pub fn color(mut self, color: impl Into) -> TableCellBorder { self.color = color.into(); self } pub fn size(mut self, size: usize) -> TableCellBorder { self.size = size; self } pub fn border_type(mut self, border_type: BorderType) -> TableCellBorder { self.border_type = border_type; self } pub fn get_size(&self) -> usize { self.size } pub fn get_color(&self) -> String { self.color.clone() } pub fn get_border_type(&self) -> BorderType { self.border_type } } impl BuildXML for TableCellBorder { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let func = match self.position { TableCellBorderPosition::Top => XMLBuilder::border_top, TableCellBorderPosition::Left => XMLBuilder::border_left, TableCellBorderPosition::Bottom => XMLBuilder::border_bottom, TableCellBorderPosition::Right => XMLBuilder::border_right, TableCellBorderPosition::InsideH => XMLBuilder::border_inside_h, TableCellBorderPosition::InsideV => XMLBuilder::border_inside_v, TableCellBorderPosition::Tr2bl => XMLBuilder::border_tr2bl, TableCellBorderPosition::Tl2br => XMLBuilder::border_tl2br, }; XMLBuilder::from(stream) .apply(|b| func(b, self.border_type, self.size, self.space, &self.color))? .into_inner() } } #[derive(Serialize, Debug, Clone, PartialEq)] #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct TableCellBorders { top: Option, left: Option, bottom: Option, right: Option, inside_h: Option, inside_v: Option, tr2bl: Option, tl2br: Option, } impl Default for TableCellBorders { fn default() -> TableCellBorders { TableCellBorders { top: Some(TableCellBorder::new(TableCellBorderPosition::Top)), left: Some(TableCellBorder::new(TableCellBorderPosition::Left)), bottom: Some(TableCellBorder::new(TableCellBorderPosition::Bottom)), right: Some(TableCellBorder::new(TableCellBorderPosition::Right)), inside_h: Some(TableCellBorder::new(TableCellBorderPosition::InsideH)), inside_v: Some(TableCellBorder::new(TableCellBorderPosition::InsideV)), tr2bl: None, tl2br: None, } } } impl TableCellBorders { pub fn new() -> TableCellBorders { Default::default() } pub fn with_empty() -> TableCellBorders { TableCellBorders { top: None, left: None, bottom: None, right: None, inside_h: None, inside_v: None, tr2bl: None, tl2br: None, } } pub fn set(mut self, border: TableCellBorder) -> Self { match border.position { TableCellBorderPosition::Top => self.top = Some(border), TableCellBorderPosition::Left => self.left = Some(border), TableCellBorderPosition::Bottom => self.bottom = Some(border), TableCellBorderPosition::Right => self.right = Some(border), TableCellBorderPosition::InsideH => self.inside_h = Some(border), TableCellBorderPosition::InsideV => self.inside_v = Some(border), TableCellBorderPosition::Tr2bl => self.tr2bl = Some(border), TableCellBorderPosition::Tl2br => self.tl2br = Some(border), }; self } pub fn clear(mut self, position: TableCellBorderPosition) -> Self { let nil = TableCellBorder::new(position.clone()).border_type(BorderType::Nil); match position { TableCellBorderPosition::Top => self.top = Some(nil), TableCellBorderPosition::Left => self.left = Some(nil), TableCellBorderPosition::Bottom => self.bottom = Some(nil), TableCellBorderPosition::Right => self.right = Some(nil), TableCellBorderPosition::InsideH => self.inside_h = Some(nil), TableCellBorderPosition::InsideV => self.inside_v = Some(nil), TableCellBorderPosition::Tr2bl => self.tr2bl = Some(nil), TableCellBorderPosition::Tl2br => self.tl2br = Some(nil), }; self } pub fn clear_all(mut self) -> Self { self.top = Some(TableCellBorder::new(TableCellBorderPosition::Top).border_type(BorderType::Nil)); self.left = Some(TableCellBorder::new(TableCellBorderPosition::Left).border_type(BorderType::Nil)); self.bottom = Some( TableCellBorder::new(TableCellBorderPosition::Bottom).border_type(BorderType::Nil), ); self.right = Some(TableCellBorder::new(TableCellBorderPosition::Right).border_type(BorderType::Nil)); self.inside_h = Some( TableCellBorder::new(TableCellBorderPosition::InsideH).border_type(BorderType::Nil), ); self.inside_v = Some( TableCellBorder::new(TableCellBorderPosition::InsideV).border_type(BorderType::Nil), ); self.tl2br = Some(TableCellBorder::new(TableCellBorderPosition::Tl2br).border_type(BorderType::Nil)); self.tr2bl = Some(TableCellBorder::new(TableCellBorderPosition::Tr2bl).border_type(BorderType::Nil)); self } } impl BuildXML for TableCellBorders { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_cell_borders()? .add_optional_child(&self.top)? .add_optional_child(&self.left)? .add_optional_child(&self.bottom)? .add_optional_child(&self.right)? .add_optional_child(&self.inside_h)? .add_optional_child(&self.inside_v)? .add_optional_child(&self.tl2br)? .add_optional_child(&self.tr2bl)? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/elements/table_cell_margins.rs000064400000000000000000000062401046102023000220570ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use crate::CellMargin; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TableCellMargins { top: CellMargin, left: CellMargin, bottom: CellMargin, right: CellMargin, } impl Default for TableCellMargins { fn default() -> TableCellMargins { TableCellMargins { top: CellMargin { val: 0, width_type: WidthType::Dxa, }, left: CellMargin::default(), bottom: CellMargin { val: 0, width_type: WidthType::Dxa, }, right: CellMargin::default(), } } } impl TableCellMargins { pub fn new() -> TableCellMargins { Default::default() } pub fn margin(self, top: usize, right: usize, bottom: usize, left: usize) -> TableCellMargins { TableCellMargins { top: CellMargin::new(top, WidthType::Dxa), left: CellMargin::new(left, WidthType::Dxa), bottom: CellMargin::new(bottom, WidthType::Dxa), right: CellMargin::new(right, WidthType::Dxa), } } pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { self.top = CellMargin::new(v, t); self } pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { self.right = CellMargin::new(v, t); self } pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { self.left = CellMargin::new(v, t); self } pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { self.bottom = CellMargin::new(v, t); self } } impl BuildXML for TableCellMargins { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_cell_margins()? .margin_top(self.top.val as i32, self.top.width_type)? .margin_left(self.left.val as i32, self.left.width_type)? .margin_bottom(self.bottom.val as i32, self.bottom.width_type)? .margin_right(self.right.val as i32, self.right.width_type)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_cell_margin() { let b = TableCellMargins::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_table_cell_margin_setter() { let b = TableCellMargins::new().margin(10, 20, 30, 40).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_cell_property.rs000064400000000000000000000150341046102023000223040ustar 00000000000000use serde::Serialize; use std::io::Write; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::*; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct TableCellProperty { width: Option, borders: Option, grid_span: Option, vertical_merge: Option, vertical_align: Option, text_direction: Option, shading: Option, #[serde(skip_serializing_if = "Option::is_none")] margins: Option, } impl TableCellProperty { pub fn new() -> TableCellProperty { Default::default() } pub fn width(mut self, v: usize, t: WidthType) -> TableCellProperty { self.width = Some(TableCellWidth::new(v, t)); self } pub fn vertical_merge(mut self, t: VMergeType) -> TableCellProperty { self.vertical_merge = Some(VMerge::new(t)); self } pub fn vertical_align(mut self, t: VAlignType) -> TableCellProperty { self.vertical_align = Some(VAlign::new(t)); self } pub fn text_direction(mut self, t: TextDirectionType) -> Self { self.text_direction = Some(TextDirection::new(t)); self } pub fn grid_span(mut self, v: usize) -> TableCellProperty { self.grid_span = Some(GridSpan::new(v)); self } pub fn shading(mut self, s: Shading) -> Self { self.shading = Some(s); self } pub fn set_borders(mut self, borders: TableCellBorders) -> Self { self.borders = Some(borders); self } pub fn set_border(mut self, border: TableCellBorder) -> Self { self.borders = Some(self.borders.unwrap_or_default().set(border)); self } pub fn clear_border(mut self, position: TableCellBorderPosition) -> Self { self.borders = Some(self.borders.unwrap_or_default().clear(position)); self } pub fn clear_all_border(mut self) -> Self { self.borders = Some(self.borders.unwrap_or_default().clear_all()); self } pub fn margins(mut self, margins: CellMargins) -> Self { self.margins = Some(margins); self } pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_top(v, t)); } else { let margins = CellMargins::new(); self.margins = Some(margins.margin_top(v, t)); } self } pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_right(v, t)); } else { let margins = CellMargins::new(); self.margins = Some(margins.margin_right(v, t)); } self } pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_bottom(v, t)); } else { let margins = CellMargins::new(); self.margins = Some(margins.margin_bottom(v, t)); } self } pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_left(v, t)); } else { let margins = CellMargins::new(); self.margins = Some(margins.margin_left(v, t)); } self } } impl BuildXML for TableCellProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_cell_property()? .add_optional_child(&self.width)? .add_optional_child(&self.borders)? .add_optional_child(&self.grid_span)? .add_optional_child(&self.vertical_merge)? .add_optional_child(&self.vertical_align)? .add_optional_child(&self.text_direction)? .add_optional_child(&self.shading)? .add_optional_child(&self.margins)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let c = TableCellProperty::new(); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_grid_span() { let c = TableCellProperty::new().grid_span(3); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_vmerge() { let c = TableCellProperty::new().vertical_merge(VMergeType::Continue); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_valign() { let c = TableCellProperty::new().vertical_align(VAlignType::Center); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_shd() { let c = TableCellProperty::new() .shading(Shading::new().shd_type(ShdType::Clear).fill("FF0000")); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), 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","verticalAlign":null,"textDirection":null,"shading":null}"# ); } #[test] fn test_table_cell_prop_json_with_valign() { let c = TableCellProperty::new().vertical_align(VAlignType::Center); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"width":null,"borders":null,"gridSpan":null,"verticalMerge":null,"verticalAlign":"center","textDirection":null,"shading":null}"# ); } } docx-rs-0.4.18/src/documents/elements/table_cell_width.rs000064400000000000000000000021121046102023000215300ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TableCellWidth { width: usize, width_type: WidthType, } impl TableCellWidth { pub fn new(width: usize, width_type: WidthType) -> TableCellWidth { TableCellWidth { width, width_type } } } impl BuildXML for TableCellWidth { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_cell_width(self.width as i32, self.width_type)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_width() { let b = TableCellWidth::new(20, WidthType::Dxa).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_grid.rs000064400000000000000000000020531046102023000203430ustar 00000000000000use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use std::io::Write; #[derive(Debug, Clone)] pub struct TableGrid { grid: Vec, } impl TableGrid { pub fn new(grid: Vec) -> TableGrid { TableGrid { grid } } } impl BuildXML for TableGrid { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_grid()? .apply_each(&self.grid, |g, b| b.grid_column(*g as i32, WidthType::Dxa))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_indent() { let b = TableGrid::new(vec![100, 200]).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_indent.rs000064400000000000000000000020551046102023000207010ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TableIndent { width: i32, width_type: WidthType, } impl TableIndent { pub fn new(width: i32, width_type: WidthType) -> TableIndent { TableIndent { width, width_type } } } impl BuildXML for TableIndent { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_indent(self.width, self.width_type)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_indent() { let b = TableIndent::new(20, WidthType::Dxa).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_layout.rs000064400000000000000000000015501046102023000207340ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct TableLayout { layout_type: TableLayoutType, } impl TableLayout { pub fn new(t: TableLayoutType) -> TableLayout { TableLayout { layout_type: t } } } impl BuildXML for TableLayout { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_layout(&self.layout_type.to_string())? .into_inner() } } impl Serialize for TableLayout { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.layout_type.to_string()) } } docx-rs-0.4.18/src/documents/elements/table_of_contents.rs000064400000000000000000000301101046102023000217320ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::types::*; use crate::xml_builder::*; use crate::{documents::*, escape}; #[derive(Debug, Clone, PartialEq)] pub enum TocContent { Paragraph(Box), Table(Box
), } impl Serialize for TocContent { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { TocContent::Paragraph(ref p) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", p)?; t.end() } TocContent::Table(ref c) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", c)?; t.end() } } } } #[derive(Serialize, Debug, Clone, PartialEq, Default)] pub struct TableOfContentsReviewData { pub author: String, pub date: String, } /// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_TOCTOC_topic_ID0ELZO1.html /// This struct is only used by writers #[derive(Serialize, Debug, Clone, PartialEq, Default)] pub struct TableOfContents { pub instr: InstrToC, pub items: Vec, // don't use pub auto: bool, pub dirty: bool, /// Skip StructuredDataTag rendering pub without_sdt: bool, pub alias: Option, pub page_ref_placeholder: Option, // it is inserted in before toc. #[serde(skip_serializing_if = "Vec::is_empty")] pub before_contents: Vec, // it is inserted in after toc. #[serde(skip_serializing_if = "Vec::is_empty")] pub after_contents: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub delete: Option, #[serde(skip_serializing_if = "Option::is_none")] pub paragraph_property: Option, } impl TableOfContents { pub fn new() -> Self { Self::default() } pub fn with_instr_text(s: &str) -> Self { let instr = InstrToC::with_instr_text(s); Self { instr, ..Self::default() } } pub fn heading_styles_range(mut self, start: usize, end: usize) -> Self { self.instr = self.instr.heading_styles_range(start, end); self } pub fn tc_field_identifier(mut self, f: Option) -> Self { self.instr = self.instr.tc_field_identifier(f); self } pub fn add_style_with_level(mut self, s: StyleWithLevel) -> Self { self.instr = self.instr.add_style_with_level(s); self } pub fn hyperlink(mut self) -> Self { self.instr = self.instr.hyperlink(); self } pub fn alias(mut self, a: impl Into) -> Self { self.alias = Some(a.into()); self } pub fn delete(mut self, author: impl Into, date: impl Into) -> Self { self.delete = Some(TableOfContentsReviewData { author: escape::escape(&author.into()), date: date.into(), }); self } // pub fn tc_field_level_range(mut self, start: usize, end: usize) -> Self { // self.instr = self.instr.tc_field_level_range(start, end); // self // } pub fn add_item(mut self, t: TableOfContentsItem) -> Self { self.items.push(t); self } pub fn auto(mut self) -> Self { self.auto = true; self } pub fn dirty(mut self) -> Self { self.dirty = true; self } pub fn add_before_paragraph(mut self, p: Paragraph) -> Self { self.before_contents .push(TocContent::Paragraph(Box::new(p))); self } pub fn add_after_paragraph(mut self, p: Paragraph) -> Self { self.after_contents.push(TocContent::Paragraph(Box::new(p))); self } pub fn add_before_table(mut self, t: Table) -> Self { self.before_contents.push(TocContent::Table(Box::new(t))); self } pub fn add_after_table(mut self, t: Table) -> Self { self.after_contents.push(TocContent::Table(Box::new(t))); self } pub fn without_sdt(mut self) -> Self { self.without_sdt = true; self } pub fn paragraph_property(mut self, p: ParagraphProperty) -> Self { self.paragraph_property = Some(p); self } } impl BuildXML for TableOfContents { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut p = StructuredDataTagProperty::new(); if let Some(ref alias) = self.alias { p = p.alias(alias); } if self.items.is_empty() { let mut b = XMLBuilder::from(stream); if !self.without_sdt { b = b .open_structured_tag()? .add_child(&p)? .open_structured_tag_content()?; } for c in self.before_contents.iter() { match c { TocContent::Paragraph(p) => { b = b.add_child(&p)?; } TocContent::Table(t) => { b = b.add_child(&t)?; } } } let mut p1 = if let Some(ref del) = self.delete { Paragraph::new().add_delete( Delete::new().author(&del.author).date(&del.date).add_run( Run::new() .add_field_char(FieldCharType::Begin, true) .add_delete_instr_text(DeleteInstrText::TOC(self.instr.clone())) .add_field_char(FieldCharType::Separate, false), ), ) } else { Paragraph::new().add_run( Run::new() .add_field_char(FieldCharType::Begin, true) .add_instr_text(InstrText::TOC(self.instr.clone())) .add_field_char(FieldCharType::Separate, false), ) }; if let Some(ref p) = self.paragraph_property { p1 = p1.paragraph_property(p.clone()); } b = b.add_child(&p1)?; let p2 = Paragraph::new().add_run(Run::new().add_field_char(FieldCharType::End, false)); if self.after_contents.is_empty() { b = b.add_child(&p2)?; } else { for (i, c) in self.after_contents.iter().enumerate() { match c { TocContent::Paragraph(p) => { // Merge paragraph if i == 0 { let mut new_p = p.clone(); new_p.children.insert( 0, ParagraphChild::Run(Box::new( Run::new().add_field_char(FieldCharType::End, false), )), ); b = b.add_child(&new_p)? } else { b = b.add_child(&p)?; } } TocContent::Table(t) => { // insert empty line for table // because it causes docx error if table is added after TOC if i == 0 { b = b.add_child( &Paragraph::new().add_run(Run::new().add_text("")), )?; } b = b.add_child(&t)?; } } } } if !self.without_sdt { b = b.close()?.close()?; } b.into_inner() } else { let items: Vec = self .items .iter() .map(|item| { let mut item = item.clone(); item.instr = self.instr.clone(); item.dirty = self.dirty; if item.page_ref.is_none() { item.page_ref = self.page_ref_placeholder.clone(); } item }) .collect(); let mut b = XMLBuilder::from(stream); if !self.without_sdt { b = b .open_structured_tag()? .add_child(&p)? .open_structured_tag_content()?; } for c in self.before_contents.iter() { match c { TocContent::Paragraph(p) => { b = b.add_child(&p)?; } TocContent::Table(t) => { b = b.add_child(&t)?; } } } b = b.add_child(&items)?; for (i, c) in self.after_contents.iter().enumerate() { match c { TocContent::Paragraph(p) => { b = b.add_child(&p)?; } TocContent::Table(t) => { // insert empty line for table // because it causes docx error if table is added after TOC if i == 0 { b = b.add_child(&Paragraph::new().add_run(Run::new().add_text("")))?; } b = b.add_child(&t)?; } } } if !self.without_sdt { b = b.close()?.close()?; } b.into_inner() } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_toc() { let b = TableOfContents::new().heading_styles_range(1, 3).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""# ); } #[test] fn test_toc_without_sdt() { let b = TableOfContents::new() .without_sdt() .heading_styles_range(1, 3) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3""# ); } /* #[test] fn test_toc_with_items() { let b = TableOfContents::new() .heading_styles_range(1, 3) .add_items(Paragraph::new().add_run(Run::new().add_text("Hello"))) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"TOC \o "1-3"Hello"# ); } */ } docx-rs-0.4.18/src/documents/elements/table_of_contents_item.rs000064400000000000000000000070461046102023000227640ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::*; use crate::types::*; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq, Default)] pub struct TableOfContentsItem { pub instr: InstrToC, pub text: String, pub toc_key: String, pub level: usize, pub dirty: bool, pub page_ref: Option, } impl TableOfContentsItem { pub fn new() -> Self { Self { level: 1, ..Default::default() } } pub fn instr(mut self, instr: InstrToC) -> Self { self.instr = instr; self } pub fn text(mut self, text: impl Into) -> Self { self.text = text.into(); self } pub fn level(mut self, level: usize) -> Self { self.level = level; self } pub fn toc_key(mut self, key: impl Into) -> Self { self.toc_key = key.into(); self } pub fn page_ref(mut self, r: impl Into) -> Self { self.page_ref = Some(r.into()); self } } impl BuildXML for Vec { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let mut b = XMLBuilder::from(stream) .open_structured_tag()? .open_structured_tag_property()? .close()? .open_structured_tag_content()?; for (i, t) in self.iter().enumerate() { let mut p = Paragraph::new().style(&format!("ToC{}", t.level)); if i == 0 { p = p.unshift_run( Run::new() .add_field_char(FieldCharType::Begin, t.dirty) .add_instr_text(InstrText::TOC(t.instr.clone())) .add_field_char(FieldCharType::Separate, false), ); } { p = p.add_tab( Tab::new() .val(TabValueType::Right) .leader(TabLeaderType::Dot) // TODO: for now set 80000 .pos(80000), ); let run = Run::new().add_text(&t.text); let page_ref = Run::new() .add_field_char(FieldCharType::Begin, false) .add_instr_text(InstrText::PAGEREF( InstrPAGEREF::new(&t.toc_key).hyperlink(), )) .add_field_char(FieldCharType::Separate, false) .add_text(t.page_ref.to_owned().unwrap_or_else(|| "1".to_string())) .add_field_char(FieldCharType::End, false); if t.instr.hyperlink { p = p.add_hyperlink( Hyperlink::new(&t.toc_key, HyperlinkType::Anchor) .add_run(run) .add_run(Run::new().add_tab()) .add_run(page_ref), ); } else { p = p .add_run(run) .add_run(Run::new().add_tab()) .add_run(page_ref); } b = b.add_child(&p)?; } if i == self.len() - 1 { let mut p = Paragraph::new().style(&format!("ToC{}", t.level)); p = p.add_run(Run::new().add_field_char(FieldCharType::End, false)); b = b.add_child(&p)?; } } b.close()?.close()?.into_inner() } } docx-rs-0.4.18/src/documents/elements/table_position_property.rs000064400000000000000000000065361046102023000232400ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; /// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.tablepositionproperties?view=openxml-3.0.1 #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "wasm", derive(ts_rs::TS), ts(export))] pub struct TablePositionProperty { #[serde(skip_serializing_if = "Option::is_none")] pub left_from_text: Option, #[serde(skip_serializing_if = "Option::is_none")] pub right_from_text: Option, #[serde(skip_serializing_if = "Option::is_none")] pub vertical_anchor: Option, #[serde(skip_serializing_if = "Option::is_none")] pub horizontal_anchor: Option, #[serde(skip_serializing_if = "Option::is_none")] pub position_x_alignment: Option, #[serde(skip_serializing_if = "Option::is_none")] pub position_y_alignment: Option, #[serde(skip_serializing_if = "Option::is_none")] pub position_x: Option, #[serde(skip_serializing_if = "Option::is_none")] pub position_y: Option, } impl TablePositionProperty { pub fn new() -> TablePositionProperty { Default::default() } pub fn left_from_text(mut self, v: i32) -> Self { self.left_from_text = Some(v); self } pub fn right_from_text(mut self, v: i32) -> Self { self.right_from_text = Some(v); self } pub fn vertical_anchor(mut self, v: impl Into) -> Self { self.vertical_anchor = Some(v.into()); self } pub fn horizontal_anchor(mut self, v: impl Into) -> Self { self.horizontal_anchor = Some(v.into()); self } pub fn position_x_alignment(mut self, v: impl Into) -> Self { self.position_x_alignment = Some(v.into()); self } pub fn position_y_alignment(mut self, v: impl Into) -> Self { self.position_y_alignment = Some(v.into()); self } pub fn position_x(mut self, v: i32) -> Self { self.position_x = Some(v); self } pub fn position_y(mut self, v: i32) -> Self { self.position_y = Some(v); self } } impl BuildXML for TablePositionProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_position_property(self)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let b = TablePositionProperty::new().build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_some_attrs() { let b = TablePositionProperty::new() .left_from_text(142) .right_from_text(142) .vertical_anchor("text") .horizontal_anchor("margin") .position_x_alignment("right") .position_y(511) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_property.rs000064400000000000000000000147601046102023000213120ustar 00000000000000use serde::Serialize; use std::io::Write; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::*; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TableProperty { width: TableWidth, justification: Justification, borders: TableBorders, #[serde(skip_serializing_if = "Option::is_none")] margins: Option, #[serde(skip_serializing_if = "Option::is_none")] indent: Option, #[serde(skip_serializing_if = "Option::is_none")] style: Option, #[serde(skip_serializing_if = "Option::is_none")] layout: Option, #[serde(skip_serializing_if = "Option::is_none")] position: Option, } impl Default for TableProperty { fn default() -> Self { TableProperty { width: TableWidth::new(0, WidthType::Auto), justification: Justification::new("left"), borders: TableBorders::new(), margins: None, indent: None, style: None, layout: None, position: None, } } } impl TableProperty { pub fn new() -> TableProperty { Default::default() } pub fn without_borders() -> TableProperty { TableProperty { borders: TableBorders::with_empty(), ..Default::default() } } pub fn indent(mut self, v: i32) -> TableProperty { self.indent = Some(TableIndent::new(v, WidthType::Dxa)); self } pub fn width(mut self, v: usize, t: WidthType) -> TableProperty { self.width = TableWidth::new(v, t); self } pub fn align(mut self, v: TableAlignmentType) -> TableProperty { self.justification = Justification::new(v.to_string()); self } pub fn set_margins(mut self, margins: TableCellMargins) -> Self { self.margins = Some(margins); self } pub fn cell_margin_top(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_top(v, t)); } else { let margins = TableCellMargins::new(); self.margins = Some(margins.margin_top(v, t)); } self } pub fn cell_margin_right(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_right(v, t)); } else { let margins = TableCellMargins::new(); self.margins = Some(margins.margin_right(v, t)); } self } pub fn cell_margin_bottom(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_bottom(v, t)); } else { let margins = TableCellMargins::new(); self.margins = Some(margins.margin_bottom(v, t)); } self } pub fn cell_margin_left(mut self, v: usize, t: WidthType) -> Self { if let Some(margins) = self.margins { self.margins = Some(margins.margin_left(v, t)); } else { let margins = TableCellMargins::new(); self.margins = Some(margins.margin_left(v, t)); } self } pub fn set_borders(mut self, borders: TableBorders) -> Self { self.borders = borders; self } pub fn set_border(mut self, border: TableBorder) -> Self { self.borders = self.borders.set(border); self } pub fn clear_border(mut self, position: TableBorderPosition) -> Self { self.borders = self.borders.clear(position); self } pub fn clear_all_border(mut self) -> Self { self.borders = self.borders.clear_all(); self } pub fn style(mut self, s: impl Into) -> Self { self.style = Some(TableStyle::new(s)); self } pub fn layout(mut self, t: TableLayoutType) -> Self { self.layout = Some(TableLayout::new(t)); self } pub fn position(mut self, p: TablePositionProperty) -> Self { self.position = Some(p); self } } impl BuildXML for TableProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_property()? .add_child(&self.width)? .add_child(&self.justification)? .add_child(&self.borders)? .add_optional_child(&self.margins)? .add_optional_child(&self.indent)? .add_optional_child(&self.style)? .add_optional_child(&self.layout)? .add_optional_child(&self.position)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let c = TableProperty::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[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":{"borderType":"single","size":2,"color":"000000","position":"top","space":0},"left":{"borderType":"single","size":2,"color":"000000","position":"left","space":0},"bottom":{"borderType":"single","size":2,"color":"000000","position":"bottom","space":0},"right":{"borderType":"single","size":2,"color":"000000","position":"right","space":0},"insideH":{"borderType":"single","size":2,"color":"000000","position":"insideH","space":0},"insideV":{"borderType":"single","size":2,"color":"000000","position":"insideV","space":0}},"indent":{"width":100,"widthType":"dxa"}}"# ); } } docx-rs-0.4.18/src/documents/elements/table_row.rs000064400000000000000000000105261046102023000202310ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::{Delete, Insert, TableCell, TableRowProperty}; use crate::xml_builder::*; use crate::{documents::BuildXML, HeightRule}; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TableRow { pub cells: Vec, pub has_numbering: bool, pub property: TableRowProperty, } #[derive(Debug, Clone, PartialEq)] pub enum TableRowChild { TableCell(TableCell), } impl BuildXML for TableRowChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { TableRowChild::TableCell(v) => v.build_to(stream), } } } impl TableRow { pub fn new(cells: Vec) -> TableRow { let property = TableRowProperty::new(); let has_numbering = cells.iter().any(|c| c.has_numbering); let cells = cells.into_iter().map(TableRowChild::TableCell).collect(); Self { cells, property, has_numbering, } } pub fn grid_after(mut self, grid_after: u32) -> TableRow { self.property = self.property.grid_after(grid_after); self } pub fn width_after(mut self, w: f32) -> TableRow { self.property = self.property.width_after(w); self } pub fn grid_before(mut self, grid_before: u32) -> TableRow { self.property = self.property.grid_before(grid_before); self } pub fn width_before(mut self, w: f32) -> TableRow { self.property = self.property.width_before(w); self } pub fn row_height(mut self, h: f32) -> TableRow { self.property = self.property.row_height(h); self } pub fn height_rule(mut self, r: HeightRule) -> TableRow { self.property = self.property.height_rule(r); self } pub fn delete(mut self, d: Delete) -> TableRow { self.property = self.property.delete(d); self } pub fn insert(mut self, i: Insert) -> TableRow { self.property = self.property.insert(i); self } pub fn cant_split(mut self) -> TableRow { self.property = self.property.cant_split(); self } } impl BuildXML for TableRow { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_row()? .add_child(&self.property)? .add_children(&self.cells)? .close()? .into_inner() } } impl Serialize for TableRowChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { TableRowChild::TableCell(ref r) => { let mut t = serializer.serialize_struct("TableCell", 2)?; t.serialize_field("type", "tableCell")?; t.serialize_field("data", r)?; t.end() } } } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_row() { let b = TableRow::new(vec![TableCell::new()]).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } #[test] fn test_row_json() { let r = TableRow::new(vec![TableCell::new()]); assert_eq!( serde_json::to_string(&r).unwrap(), r#"{"cells":[{"type":"tableCell","data":{"children":[],"property":{"width":null,"borders":null,"gridSpan":null,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}}],"hasNumbering":false,"property":{"gridAfter":null,"widthAfter":null,"gridBefore":null,"widthBefore":null}}"# ); } #[test] fn test_row_cant_split() { let b = TableRow::new(vec![TableCell::new()]).cant_split().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_row_property.rs000064400000000000000000000057761046102023000222100ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::xml_builder::*; use crate::{documents::BuildXML, HeightRule}; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct TableRowProperty { grid_after: Option, width_after: Option, grid_before: Option, width_before: Option, #[serde(skip_serializing_if = "Option::is_none")] row_height: Option, #[serde(skip_serializing_if = "Option::is_none")] height_rule: Option, #[serde(skip_serializing_if = "Option::is_none")] pub del: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ins: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cant_split: Option, } impl TableRowProperty { pub fn new() -> TableRowProperty { Default::default() } pub fn grid_after(mut self, after: u32) -> Self { self.grid_after = Some(after); self } pub fn width_after(mut self, w: f32) -> Self { self.width_after = Some(w); self } pub fn grid_before(mut self, before: u32) -> Self { self.grid_before = Some(before); self } pub fn width_before(mut self, w: f32) -> Self { self.width_before = Some(w); self } pub fn row_height(mut self, h: f32) -> Self { self.row_height = Some(h); self } pub fn height_rule(mut self, r: HeightRule) -> Self { self.height_rule = Some(r); self } pub fn delete(mut self, d: Delete) -> Self { self.del = Some(d); self } pub fn insert(mut self, i: Insert) -> Self { self.ins = Some(i); self } pub fn cant_split(mut self) -> Self { self.cant_split = Some(CantSplit::default()); self } } impl BuildXML for TableRowProperty { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_table_row_property()? .add_optional_child(&self.del)? .add_optional_child(&self.ins)? .add_optional_child(&self.cant_split)? .apply_opt(self.row_height, |h, b| { b.table_row_height( &format!("{}", h), &self.height_rule.unwrap_or_default().to_string(), ) })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_default() { let b = TableRowProperty::new().build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } #[test] fn test_cant_split() { let b = TableRowProperty::new().cant_split().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/table_style.rs000064400000000000000000000014301046102023000205540ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct TableStyle { val: String, } impl TableStyle { pub fn new(val: impl Into) -> TableStyle { TableStyle { val: val.into() } } } impl BuildXML for TableStyle { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_style(&self.val)? .into_inner() } } impl Serialize for TableStyle { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } docx-rs-0.4.18/src/documents/elements/table_width.rs000064400000000000000000000020561046102023000205400ustar 00000000000000use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TableWidth { width: usize, width_type: WidthType, } impl TableWidth { pub fn new(width: usize, width_type: WidthType) -> TableWidth { TableWidth { width, width_type } } } impl BuildXML for TableWidth { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .table_width(self.width as i32, self.width_type)? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_table_width() { let b = TableWidth::new(20, WidthType::Dxa).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/tabs.rs000064400000000000000000000013321046102023000171770ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; use crate::Tab; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] pub struct Tabs { pub tabs: Vec, } impl Tabs { pub fn new() -> Self { Default::default() } pub fn add_tab(mut self, t: Tab) -> Self { self.tabs.push(t); self } } impl BuildXML for Tabs { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .apply_each(&self.tabs, |t, b| b.tab(t.val, t.leader, t.pos))? .into_inner() } } docx-rs-0.4.18/src/documents/elements/text.rs000064400000000000000000000034521046102023000172370ustar 00000000000000use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::Deserialize; use std::io::Write; use crate::documents::BuildXML; use crate::escape::escape; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Text { pub text: String, pub preserve_space: bool, } impl Text { pub fn new(text: impl Into) -> Text { Text { text: escape(&text.into()), preserve_space: true, } } pub(crate) fn without_escape(text: impl Into) -> Text { Text { text: text.into(), preserve_space: true, } } } impl BuildXML for Text { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .text(&self.text, true)? .into_inner() } } 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 { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = Text::new("Hello").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_json() { let t = Text::new("Hello"); assert_eq!( serde_json::to_string(&t).unwrap(), r#"{"preserveSpace":true,"text":"Hello"}"# ); } } docx-rs-0.4.18/src/documents/elements/text_alignment.rs000064400000000000000000000015431046102023000212740ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::{xml_builder::*, TextAlignmentType}; #[derive(Debug, Clone, PartialEq)] pub struct TextAlignment(pub TextAlignmentType); impl TextAlignment { pub fn new(val: TextAlignmentType) -> TextAlignment { TextAlignment(val) } } impl BuildXML for TextAlignment { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .text_alignment(&format!("{}", self.0))? .into_inner() } } impl Serialize for TextAlignment { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let v = format!("{}", self.0); serializer.serialize_str(&v) } } docx-rs-0.4.18/src/documents/elements/text_border.rs000064400000000000000000000026271046102023000205770ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TextBorder { pub border_type: BorderType, pub size: usize, pub color: String, pub space: usize, } impl TextBorder { pub fn new() -> Self { TextBorder::default() } pub fn color(mut self, color: impl Into) -> Self { self.color = color.into(); self } pub fn size(mut self, size: usize) -> Self { self.size = size; self } pub fn space(mut self, space: usize) -> Self { self.space = space; self } pub fn border_type(mut self, border_type: BorderType) -> Self { self.border_type = border_type; self } } impl Default for TextBorder { fn default() -> Self { TextBorder { border_type: BorderType::Single, size: 4, space: 0, color: "auto".to_owned(), } } } impl BuildXML for TextBorder { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .text_border(self.border_type, self.size, self.space, &self.color)? .into_inner() } } docx-rs-0.4.18/src/documents/elements/text_box.rs000064400000000000000000000123441046102023000201070ustar 00000000000000use serde::Serialize; use crate::documents::*; use crate::types::*; #[derive(Debug, Clone, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TextBox { // For writer only pub children: Vec, // unit is emu pub size: (u32, u32), pub position_type: DrawingPositionType, /// Specifies that this object shall be positioned using the positioning information in the /// simplePos child element (§20.4.2.13). This positioning, when specified, positions the /// object on the page by placing its top left point at the x-y coordinates specified by that /// element. pub simple_pos: bool, // unit is emu pub simple_pos_x: i32, pub simple_pos_y: i32, /// Specifies how this DrawingML object behaves when its anchor is located in a table cell; /// and its specified position would cause it to intersect with a table cell displayed in the /// document. That behavior shall be as follows: pub layout_in_cell: bool, /// Specifies the relative Z-ordering of all DrawingML objects in this document. Each floating /// DrawingML object shall have a Z-ordering value, which determines which object is /// displayed when any two objects intersect. Higher values shall indicate higher Z-order; /// lower values shall indicate lower Z-order. pub relative_height: u32, pub allow_overlap: bool, pub position_h: DrawingPosition, pub position_v: DrawingPosition, pub relative_from_h: RelativeFromHType, pub relative_from_v: RelativeFromVType, /// Specifies the minimum distance which shall be maintained between the top edge of this drawing object and any subsequent text within the document when this graphical object is displayed within the document's contents., /// The distance shall be measured in EMUs (English Metric Units)., pub dist_t: i32, pub dist_b: i32, pub dist_l: i32, pub dist_r: i32, } impl Default for TextBox { fn default() -> Self { Self::new() } } impl TextBox { pub fn new() -> Self { Self { children: vec![], size: (from_px(100), from_px(100)), position_type: DrawingPositionType::Inline, simple_pos: false, simple_pos_x: 0, simple_pos_y: 0, layout_in_cell: false, relative_height: 190500, allow_overlap: false, position_v: DrawingPosition::Offset(0), position_h: DrawingPosition::Offset(0), relative_from_h: RelativeFromHType::default(), relative_from_v: RelativeFromVType::default(), dist_t: 0, dist_b: 0, dist_l: 0, dist_r: 0, } } // unit is emu pub fn size(mut self, w_emu: u32, h_emu: u32) -> Self { self.size = (w_emu, h_emu); self } pub fn floating(mut self) -> Self { self.position_type = DrawingPositionType::Anchor; self } pub fn offset_x(mut self, x: i32) -> Self { self.position_h = DrawingPosition::Offset(x); self } pub fn offset_y(mut self, y: i32) -> Self { self.position_v = DrawingPosition::Offset(y); self } pub fn position_h(mut self, pos: DrawingPosition) -> Self { self.position_h = pos; self } pub fn position_v(mut self, pos: DrawingPosition) -> Self { self.position_v = pos; self } pub fn relative_from_h(mut self, t: RelativeFromHType) -> Self { self.relative_from_h = t; self } pub fn relative_from_v(mut self, t: RelativeFromVType) -> Self { self.relative_from_v = t; self } pub fn dist_t(mut self, v: i32) -> Self { self.dist_t = v; self } pub fn dist_b(mut self, v: i32) -> Self { self.dist_b = v; self } pub fn dist_l(mut self, v: i32) -> Self { self.dist_l = v; self } pub fn dist_r(mut self, v: i32) -> Self { self.dist_r = v; self } pub fn simple_pos(mut self, v: bool) -> Self { self.simple_pos = v; self } pub fn relative_height(mut self, v: u32) -> Self { self.relative_height = v; self } } /* impl BuildXML for TextBox { fn build_to(&self, stream: xml::writer::EventWriter) -> xml::writer::Result> { let w = format!("{}", self.size.0); let h = format!("{}", self.size.1); XMLBuilder::from(stream) .open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")? .open_pic_nv_pic_pr()? .pic_c_nv_pr("0", "")? .open_pic_c_nv_pic_pr()? .a_pic_locks("1", "1")? .close()? .close()? .open_blip_fill()? .a_blip(&self.id)? .a_src_rect()? .open_a_stretch()? .a_fill_rect()? .close()? .close()? .open_pic_sp_pr("auto")? .open_a_xfrm()? .a_off("0", "0")? .a_ext(&w, &h)? .close()? .open_a_prst_geom("rect")? .a_av_lst()? .close()? .close()? .close()? .into_inner() } } */ docx-rs-0.4.18/src/documents/elements/text_box_content.rs000064400000000000000000000055161046102023000216440ustar 00000000000000use super::*; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] pub struct TextBoxContent { pub children: Vec, pub has_numbering: bool, } #[derive(Debug, Clone, PartialEq)] pub enum TextBoxContentChild { Paragraph(Box), Table(Box
), } impl Serialize for TextBoxContentChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { TextBoxContentChild::Paragraph(ref p) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", p)?; t.end() } TextBoxContentChild::Table(ref c) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", c)?; t.end() } } } } impl TextBoxContent { pub fn new() -> TextBoxContent { Default::default() } pub fn add_paragraph(mut self, p: Paragraph) -> Self { if p.has_numbering { self.has_numbering = true } self.children .push(TextBoxContentChild::Paragraph(Box::new(p))); self } pub fn add_table(mut self, t: Table) -> Self { if t.has_numbering { self.has_numbering = true } self.children.push(TextBoxContentChild::Table(Box::new(t))); self } } impl BuildXML for TextBoxContentChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { TextBoxContentChild::Paragraph(p) => p.build_to(stream), TextBoxContentChild::Table(t) => t.build_to(stream), } } } impl BuildXML for TextBoxContent { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_text_box_content()? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_text_box_content_build() { let b = TextBoxContent::new() .add_paragraph(Paragraph::new()) .build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/text_direction.rs000064400000000000000000000015361046102023000213000ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct TextDirection { val: TextDirectionType, } impl TextDirection { pub fn new(t: TextDirectionType) -> TextDirection { TextDirection { val: t } } } impl BuildXML for TextDirection { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .text_direction(&self.val.to_string())? .into_inner() } } impl Serialize for TextDirection { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!("{}", &self.val)) } } docx-rs-0.4.18/src/documents/elements/underline.rs000064400000000000000000000021101046102023000202260ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq)] pub struct Underline { val: String, } impl Underline { pub fn new(val: impl Into) -> Underline { Underline { val: val.into() } } } impl BuildXML for Underline { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).underline(&self.val)?.into_inner() } } impl Serialize for Underline { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.val) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_underline() { let c = Underline::new("single"); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/v_align.rs000064400000000000000000000021651046102023000176720ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct VAlign { val: VAlignType, } impl VAlign { pub fn new(v: VAlignType) -> VAlign { VAlign { val: v } } } impl BuildXML for VAlign { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .vertical_align(&self.val.to_string())? .into_inner() } } impl Serialize for VAlign { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!("{}", &self.val)) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = VAlign::new(VAlignType::Center).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/vanish.rs000064400000000000000000000017351046102023000175450ustar 00000000000000use serde::{Deserialize, Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Deserialize, PartialEq, Default)] pub struct Vanish {} impl Vanish { pub fn new() -> Vanish { Vanish {} } } impl BuildXML for Vanish { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream).vanish()?.into_inner() } } impl Serialize for Vanish { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bool(true) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Vanish::new(); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/vert_align.rs000064400000000000000000000014641046102023000204060ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct VertAlign { val: VertAlignType, } impl VertAlign { pub fn new(val: VertAlignType) -> VertAlign { Self { val } } } impl Serialize for VertAlign { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!("{}", &self.val)) } } impl BuildXML for VertAlign { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .vert_align(&self.val.to_string())? .into_inner() } } docx-rs-0.4.18/src/documents/elements/vertical_merge.rs000064400000000000000000000021711046102023000212400ustar 00000000000000use serde::{Serialize, Serializer}; use std::io::Write; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq)] pub struct VMerge { val: VMergeType, } impl VMerge { pub fn new(v: VMergeType) -> VMerge { VMerge { val: v } } } impl BuildXML for VMerge { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .vertical_merge(&self.val.to_string())? .into_inner() } } impl Serialize for VMerge { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!("{}", &self.val)) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = VMerge::new(VMergeType::Continue).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/wp_anchor.rs000064400000000000000000000032201046102023000202240ustar 00000000000000use super::*; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct WpAnchor { pub children: Vec, } /* 20.4.2.3 anchor (WpAnchor for Floating DrawingML Object) This element specifies that the DrawingML object located at this position in the document is a floating object. Within a WordprocessingML document, drawing objects can exist in two states: - Inline - The drawing object is in line with the text, and affects the line height and layout of its line (like a - character glyph of similar size). Floating - The drawing object is anchored within the text, but can be absolutely positioned in the document relative to the page. When this element encapsulates the DrawingML object's i */ impl WpAnchor { pub fn new() -> WpAnchor { Default::default() } pub fn add_graphic(mut self, g: AGraphic) -> WpAnchor { self.children.push(g); self } } impl BuildXML for WpAnchor { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_anchor()? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_anchor_build() { let b = WpAnchor::new().build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/elements/wps_shape.rs000064400000000000000000000032611046102023000202420ustar 00000000000000use super::*; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct WpsShape { children: Vec, } #[derive(Debug, Clone, PartialEq)] pub enum WpsShapeChild { WpsTextBox(WpsTextBox), } impl Serialize for WpsShapeChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { WpsShapeChild::WpsTextBox(ref s) => { let mut t = serializer.serialize_struct("WpsTextBox", 2)?; t.serialize_field("type", "textbox")?; t.serialize_field("data", s)?; t.end() } } } } impl WpsShape { pub fn new() -> WpsShape { Default::default() } pub fn add_text_box(mut self, text_box: WpsTextBox) -> Self { self.children.push(WpsShapeChild::WpsTextBox(text_box)); self } } impl BuildXML for WpsShapeChild { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { match self { WpsShapeChild::WpsTextBox(t) => t.build_to(stream), } } } impl BuildXML for WpsShape { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_wp_text_box()? .add_children(&self.children)? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/elements/wps_text_box.rs000064400000000000000000000026171046102023000210020ustar 00000000000000use super::*; use serde::Serialize; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, Serialize, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct WpsTextBox { pub children: Vec, pub has_numbering: bool, } impl WpsTextBox { pub fn new() -> WpsTextBox { Default::default() } pub fn add_content(mut self, c: TextBoxContent) -> Self { if c.has_numbering { self.has_numbering = true } self.children.push(c); self } } impl BuildXML for WpsTextBox { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .open_wp_text_box()? .add_children(&self.children)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_wp_text_box_build() { let c = TextBoxContent::new().add_paragraph(Paragraph::new()); let b = WpsTextBox::new().add_content(c).build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/elements/zoom.rs000064400000000000000000000020241046102023000172310ustar 00000000000000use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::{Serialize, Serializer}; #[derive(Debug, Clone, PartialEq)] pub struct Zoom { val: usize, } impl Zoom { pub fn new(val: usize) -> Zoom { Zoom { val } } } impl BuildXML for Zoom { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .zoom(&format!("{}", self.val))? .into_inner() } } impl Serialize for Zoom { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_u64(self.val as u64) } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_zoom() { let c = Zoom::new(20); let b = c.build(); assert_eq!(str::from_utf8(&b).unwrap(), r#""#); } } docx-rs-0.4.18/src/documents/font_table.rs000064400000000000000000000035651046102023000165610ustar 00000000000000use super::Font; use crate::documents::BuildXML; use crate::types::FontPitchType; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct FontTable {} impl FontTable { pub fn new() -> FontTable { Default::default() } } impl BuildXML for FontTable { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { let b = XMLBuilder::from(stream); let times = Font::new("Times New Roman", "00", "roman", FontPitchType::Variable); let symbol = Font::new("Symbol", "02", "roman", FontPitchType::Variable); let arial = Font::new("Arial", "00", "swiss", FontPitchType::Variable); b.declaration(Some(true))? .open_fonts()? .add_child(×)? .add_child(&symbol)? .add_child(&arial)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_str_eq; use std::str; #[test] fn test_settings() { let c = FontTable::new(); let b = c.build(); assert_str_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/footer.rs000064400000000000000000000076631046102023000157450ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct Footer { pub has_numbering: bool, pub children: Vec, } impl Footer { pub fn new() -> Footer { Default::default() } pub fn add_paragraph(mut self, p: Paragraph) -> Self { if p.has_numbering { self.has_numbering = true } self.children.push(FooterChild::Paragraph(Box::new(p))); self } pub fn add_table(mut self, t: Table) -> Self { if t.has_numbering { self.has_numbering = true } self.children.push(FooterChild::Table(Box::new(t))); self } /// reader only pub(crate) fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { if t.has_numbering { self.has_numbering = true } self.children .push(FooterChild::StructuredDataTag(Box::new(t))); self } } #[derive(Debug, Clone, PartialEq)] pub enum FooterChild { Paragraph(Box), Table(Box
), StructuredDataTag(Box), } impl Serialize for FooterChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { FooterChild::Paragraph(ref p) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", p)?; t.end() } FooterChild::Table(ref c) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", c)?; t.end() } FooterChild::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } } } } impl BuildXML for Footer { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_footer()? .apply_each(&self.children, |c, b| match c { FooterChild::Paragraph(p) => b.add_child(&p), FooterChild::Table(t) => b.add_child(&t), FooterChild::StructuredDataTag(t) => b.add_child(&t), })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_settings() { let c = Footer::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/footer_id.rs000064400000000000000000000007461046102023000164140ustar 00000000000000/* #[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static FOOTER_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_pic_id() -> usize { use std::sync::atomic::Ordering; let id = FOOTER_ID.load(Ordering::Relaxed); FOOTER_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(test)] pub fn generate_footer_id() -> usize { 123 } */ pub fn create_footer_rid(id: usize) -> String { format!("rIdFooter{}", id) } docx-rs-0.4.18/src/documents/footer_rels.rs000064400000000000000000000024211046102023000167550ustar 00000000000000use crate::documents::BuildXML; use crate::{xml_builder::*, ImageIdAndPath}; use serde::Serialize; use std::io::Write; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct FooterRels { pub images: Vec<(String, String)>, } impl FooterRels { pub fn new() -> FooterRels { Default::default() } pub fn add_image(mut self, id: impl Into, path: impl Into) -> Self { self.images.push((id.into(), path.into())); self } pub(crate) fn set_images(&mut self, images: Vec) { self.images = images; } } impl BuildXML for FooterRels { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(None)? .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")? .apply_each(&self.images, |(id, path), b| { b.relationship( id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", path, ) })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/footnote_id.rs000064400000000000000000000006311046102023000167440ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static FOOTNOTE_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_footnote_id() -> usize { use std::sync::atomic::Ordering; let id = FOOTNOTE_ID.load(Ordering::Relaxed); FOOTNOTE_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(test)] pub fn generate_footnote_id() -> usize { 1 } docx-rs-0.4.18/src/documents/footnotes.rs000064400000000000000000000071311046102023000164550ustar 00000000000000use super::Footnote; use crate::documents::BuildXML; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct Footnotes { pub(crate) footnotes: Vec, } impl Footnotes { pub fn new() -> Self { Self::default() } pub(crate) fn add(&mut self, footnotes: Vec) { self.footnotes.extend(footnotes) } } impl BuildXML for Footnotes { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_footnotes()? .add_children(&self.footnotes)? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_footnotes() { let b = Footnotes::new().build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/header.rs000064400000000000000000000076631046102023000156770ustar 00000000000000use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct Header { pub has_numbering: bool, pub children: Vec, } impl Header { pub fn new() -> Header { Default::default() } pub fn add_paragraph(mut self, p: Paragraph) -> Self { if p.has_numbering { self.has_numbering = true } self.children.push(HeaderChild::Paragraph(Box::new(p))); self } pub fn add_table(mut self, t: Table) -> Self { if t.has_numbering { self.has_numbering = true } self.children.push(HeaderChild::Table(Box::new(t))); self } /// reader only pub(crate) fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { if t.has_numbering { self.has_numbering = true } self.children .push(HeaderChild::StructuredDataTag(Box::new(t))); self } } #[derive(Debug, Clone, PartialEq)] pub enum HeaderChild { Paragraph(Box), Table(Box
), StructuredDataTag(Box), } impl Serialize for HeaderChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { HeaderChild::Paragraph(ref p) => { let mut t = serializer.serialize_struct("Paragraph", 2)?; t.serialize_field("type", "paragraph")?; t.serialize_field("data", p)?; t.end() } HeaderChild::Table(ref c) => { let mut t = serializer.serialize_struct("Table", 2)?; t.serialize_field("type", "table")?; t.serialize_field("data", c)?; t.end() } HeaderChild::StructuredDataTag(ref r) => { let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; t.serialize_field("type", "structuredDataTag")?; t.serialize_field("data", r)?; t.end() } } } } impl BuildXML for Header { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_header()? .apply_each(&self.children, |c, b| match c { HeaderChild::Paragraph(p) => b.add_child(&p), HeaderChild::Table(t) => b.add_child(&t), HeaderChild::StructuredDataTag(t) => b.add_child(&t), })? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_settings() { let c = Header::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/header_id.rs000064400000000000000000000007261046102023000163440ustar 00000000000000/* use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static HEADER_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_header_id() -> usize { use std::sync::atomic::Ordering; let id = HEADER_ID.load(Ordering::Relaxed); HEADER_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(test)] pub fn generate_header_id() -> usize { 123 } */ pub fn create_header_rid(id: usize) -> String { format!("rIdHeader{}", id) } docx-rs-0.4.18/src/documents/header_rels.rs000064400000000000000000000024211046102023000167070ustar 00000000000000use crate::documents::BuildXML; use crate::{xml_builder::*, ImageIdAndPath}; use serde::Serialize; use std::io::Write; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct HeaderRels { pub images: Vec<(String, String)>, } impl HeaderRels { pub fn new() -> HeaderRels { Default::default() } pub fn add_image(mut self, id: impl Into, path: impl Into) -> Self { self.images.push((id.into(), path.into())); self } pub(crate) fn set_images(&mut self, images: Vec) { self.images = images; } } impl BuildXML for HeaderRels { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(None)? .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")? .apply_each(&self.images, |(id, path), b| { b.relationship( id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", path, ) })? .close()? .into_inner() } } docx-rs-0.4.18/src/documents/history_id.rs000064400000000000000000000007331046102023000166130ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static HISTORY_ID: AtomicUsize = AtomicUsize::new(0); #[cfg(not(test))] pub trait HistoryId { fn generate(&self) -> String { use std::sync::atomic::Ordering; let id = HISTORY_ID.load(Ordering::Relaxed); HISTORY_ID.store(id + 1, Ordering::Relaxed); format!("{}", id) } } #[cfg(test)] pub trait HistoryId { fn generate(&self) -> &str { "123" } } docx-rs-0.4.18/src/documents/hyperlink_id.rs000064400000000000000000000007701046102023000171200ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static HYPERLINK_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_hyperlink_id() -> usize { use std::sync::atomic::Ordering; let id = HYPERLINK_ID.load(Ordering::Relaxed); HYPERLINK_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(test)] pub fn generate_hyperlink_id() -> usize { 123 } pub fn create_hyperlink_rid(id: usize) -> String { format!("rIdHyperlink{}", id) } docx-rs-0.4.18/src/documents/image_collector.rs000064400000000000000000000161401046102023000175650ustar 00000000000000use crate::{ DeleteChild, DrawingData, InsertChild, Paragraph, ParagraphChild, RunChild, StructuredDataTagChild, Table, TableCellContent, TableChild, TableRowChild, TocContent, }; pub(crate) fn collect_images_from_paragraph( paragraph: &mut Paragraph, images: &mut Vec<(String, String)>, image_bufs: &mut Vec<(String, Vec)>, id_prefix: Option<&str>, ) { for child in &mut paragraph.children { if let ParagraphChild::Run(run) = child { for child in &mut run.children { if let RunChild::Drawing(d) = child { if let Some(DrawingData::Pic(pic)) = &mut d.data { let b = std::mem::take(&mut pic.image); let buf = image_bufs.iter().find(|x| x.0 == pic.id || x.1 == b); let pic_id = if let Some(prefix) = id_prefix { format!("{}{}", prefix, pic.id) } else { pic.id.clone() }; if buf.as_ref().is_none() { images.push(( pic_id.clone(), // For now only png supported format!("media/{}.png", pic_id), )); image_bufs.push((pic_id.clone(), b)); pic.id = pic_id; } else { pic.id = buf.unwrap().0.clone(); } } } } } else if let ParagraphChild::Insert(ins) = child { for child in &mut ins.children { match child { InsertChild::Run(run) => { for child in &mut run.children { if let RunChild::Drawing(d) = child { if let Some(DrawingData::Pic(pic)) = &mut d.data { images.push(( pic.id.clone(), // For now only png supported format!("media/{}.png", pic.id), )); let b = std::mem::take(&mut pic.image); image_bufs.push((pic.id.clone(), b)); } } } } InsertChild::Delete(del) => { for d in &mut del.children { if let DeleteChild::Run(run) = d { for child in &mut run.children { if let RunChild::Drawing(d) = child { if let Some(DrawingData::Pic(pic)) = &mut d.data { images.push(( pic.id.clone(), // For now only png supported format!("media/{}.png", pic.id), )); let b = std::mem::take(&mut pic.image); image_bufs.push((pic.id.clone(), b)); } } } } } } _ => {} } } } else if let ParagraphChild::Delete(del) = child { for d in &mut del.children { if let DeleteChild::Run(run) = d { for child in &mut run.children { if let RunChild::Drawing(d) = child { if let Some(DrawingData::Pic(pic)) = &mut d.data { images.push(( pic.id.clone(), // For now only png supported format!("media/{}.png", pic.id), )); let b = std::mem::take(&mut pic.image); image_bufs.push((pic.id.clone(), b)); } } } } } } } } pub(crate) fn collect_images_from_table( table: &mut Table, images: &mut Vec<(String, String)>, image_bufs: &mut Vec<(String, Vec)>, id_prefix: Option<&str>, ) { for TableChild::TableRow(row) in &mut table.rows { for TableRowChild::TableCell(cell) in &mut row.cells { for content in &mut cell.children { match content { TableCellContent::Paragraph(paragraph) => { collect_images_from_paragraph(paragraph, images, image_bufs, id_prefix); } TableCellContent::Table(table) => { collect_images_from_table(table, images, image_bufs, id_prefix) } TableCellContent::StructuredDataTag(tag) => { for child in &mut tag.children { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, images, image_bufs, id_prefix, ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table(table, images, image_bufs, id_prefix); } } } TableCellContent::TableOfContents(t) => { for child in &mut t.before_contents { if let TocContent::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, images, image_bufs, id_prefix, ); } if let TocContent::Table(table) = child { collect_images_from_table(table, images, image_bufs, id_prefix); } } for child in &mut t.after_contents { if let TocContent::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, images, image_bufs, id_prefix, ); } if let TocContent::Table(table) = child { collect_images_from_table(table, images, image_bufs, id_prefix); } } } } } } } } docx-rs-0.4.18/src/documents/mod.rs000064400000000000000000001660301046102023000152200ustar 00000000000000use std::{collections::HashMap, str::FromStr}; mod bookmark_id; mod build_xml; mod comments; mod comments_extended; mod content_types; mod custom_item; mod custom_item_property; mod custom_item_rels; mod doc_props; mod document; mod document_rels; mod elements; mod font_table; mod footer; mod footer_id; mod footer_rels; mod footnote_id; mod footnotes; mod header; mod header_id; mod header_rels; mod history_id; mod hyperlink_id; mod image_collector; mod numberings; mod paragraph_id; mod paragraph_property_change_id; mod pic_id; mod preset_styles; mod rels; mod settings; mod styles; mod taskpanes; mod taskpanes_rels; mod theme; mod toc_key; mod web_settings; mod webextension; mod xml_docx; pub use build_xml::BuildXML; pub(crate) use history_id::HistoryId; pub(crate) use hyperlink_id::*; pub(crate) use paragraph_id::*; pub(crate) use paragraph_property_change_id::ParagraphPropertyChangeId; pub(crate) use pic_id::*; pub use bookmark_id::*; pub use comments::*; pub use comments_extended::*; pub use content_types::*; pub use custom_item::*; pub use custom_item_property::*; pub use custom_item_rels::*; pub use doc_props::*; pub use document::*; pub use document_rels::*; pub use elements::*; pub use font_table::*; pub use footer::*; pub use footer_id::*; pub use footer_rels::*; pub use footnotes::*; pub use header::*; pub use header_id::*; pub use header_rels::*; pub use numberings::*; pub use rels::*; pub use settings::*; pub use styles::*; pub use taskpanes::*; pub use taskpanes_rels::*; pub use theme::*; pub use toc_key::*; pub use web_settings::*; pub use webextension::*; pub use xml_docx::*; use base64::Engine; use serde::{ser, Serialize}; use self::image_collector::{collect_images_from_paragraph, collect_images_from_table}; #[derive(Debug, Clone)] pub struct Image(pub Vec); #[derive(Debug, Clone)] pub struct Png(pub Vec); pub type ImageIdAndPath = (String, String); pub type ImageIdAndBuf = (String, Vec); impl ser::Serialize for Image { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { let base64 = base64::engine::general_purpose::STANDARD.encode(&self.0); serializer.collect_str(&base64) } } impl ser::Serialize for Png { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { let base64 = base64::engine::general_purpose::STANDARD.encode(&self.0); serializer.collect_str(&base64) } } #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct Docx { pub content_type: ContentTypes, pub rels: Rels, pub document_rels: DocumentRels, pub doc_props: DocProps, pub styles: Styles, pub document: Document, pub comments: Comments, pub numberings: Numberings, pub settings: Settings, pub font_table: FontTable, pub media: Vec<(String, Vec)>, pub comments_extended: CommentsExtended, pub web_settings: WebSettings, pub taskpanes: Option, pub taskpanes_rels: TaskpanesRels, pub web_extensions: Vec, pub custom_items: Vec, pub custom_item_props: Vec, pub custom_item_rels: Vec, // reader only pub themes: Vec, // reader only pub images: Vec<(String, String, Image, Png)>, // reader only pub hyperlinks: Vec<(String, String, String)>, pub footnotes: Footnotes, } impl Default for Docx { fn default() -> Self { let content_type = ContentTypes::new().set_default(); let rels = Rels::new().set_default(); let doc_props = DocProps::new(CorePropsConfig::new()); let styles = Styles::new(); let document = Document::new(); let document_rels = DocumentRels::new(); let settings = Settings::new(); let font_table = FontTable::new(); let comments = Comments::new(); let numberings = Numberings::new(); let media = vec![]; let comments_extended = CommentsExtended::new(); let web_settings = WebSettings::new(); let footnotes = Footnotes::default(); Docx { content_type, rels, document_rels, doc_props, styles, document, comments, numberings, settings, font_table, media, comments_extended, web_settings, taskpanes: None, taskpanes_rels: TaskpanesRels::new(), web_extensions: vec![], custom_items: vec![], custom_item_props: vec![], custom_item_rels: vec![], themes: vec![], images: vec![], hyperlinks: vec![], footnotes, } } } impl Docx { pub fn new() -> Docx { Default::default() } pub fn document(mut self, d: Document) -> Docx { for child in &self.document.children { match child { DocumentChild::Paragraph(paragraph) => { if paragraph.has_numbering { self.document_rels.has_numberings = true; } } DocumentChild::Table(table) => { if table.has_numbering { self.document_rels.has_numberings = true; } } _ => {} } } self.document = d; self } pub fn styles(mut self, s: Styles) -> Self { self.styles = s; self } pub fn add_style(mut self, s: Style) -> Self { self.styles = self.styles.add_style(s); self } pub fn numberings(mut self, n: Numberings) -> Self { self.numberings = n; self } pub fn settings(mut self, s: Settings) -> Self { self.settings = s; self } // reader only pub(crate) fn web_settings(mut self, s: WebSettings) -> Self { self.web_settings = s; self } // reader only pub(crate) fn add_image( mut self, id: impl Into, path: impl Into, buf: Vec, ) -> Self { #[cfg(feature = "image")] if let Ok(dimg) = image::load_from_memory(&buf) { let mut png = std::io::Cursor::new(vec![]); // For now only png supported dimg.write_to(&mut png, image::ImageFormat::Png) .expect("Unable to write dynamic image"); self.images .push((id.into(), path.into(), Image(buf), Png(png.into_inner()))); } #[cfg(not(feature = "image"))] // without 'image' crate we can only test for PNG file signature if buf.starts_with(&[137, 80, 78, 71, 13, 10, 26, 10]) { self.images .push((id.into(), path.into(), Image(buf.clone()), Png(buf))); } self } // reader only pub(crate) fn add_hyperlink( mut self, id: impl Into, path: impl Into, r#type: impl Into, ) -> Self { self.hyperlinks .push((id.into(), path.into(), r#type.into())); self } pub fn comments(mut self, c: Comments) -> Self { self.comments = c; self } pub fn comments_extended(mut self, c: CommentsExtended) -> Self { self.comments_extended = c; 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. // This is because numberings.xml without numbering cause an error on word online. self.document_rels.has_numberings = true; } self.document = self.document.add_paragraph(p); self } pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx { if t.has_numbering { // If this document has numbering, set numberings.xml to document_rels. // This is because numberings.xml without numbering cause an error on word online. self.document_rels.has_numberings = true; } self.document = self.document.add_structured_data_tag(t); self } pub fn add_table_of_contents(mut self, t: TableOfContents) -> Docx { self.document = self.document.add_table_of_contents(t); self } pub fn add_bookmark_start(mut self, id: usize, name: impl Into) -> Docx { self.document = self.document.add_bookmark_start(id, name); self } pub fn add_bookmark_end(mut self, id: usize) -> Docx { self.document = self.document.add_bookmark_end(id); self } pub fn add_table(mut self, t: Table) -> Docx { if t.has_numbering { // If this document has numbering, set numberings.xml to document_rels. // This is because numberings.xml without numbering cause an error on word online. self.document_rels.has_numberings = true; } self.document = self.document.add_table(t); self } pub fn header(mut self, header: Header) -> Self { if header.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.header_count + 1; self.document.section_property = self .document .section_property .header(header, &create_header_rid(count)); self.document_rels.header_count = count; self.content_type = self.content_type.add_header(); self } pub fn first_header(mut self, header: Header) -> Self { if header.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.header_count + 1; self.document.section_property = self .document .section_property .first_header(header, &create_header_rid(count)); self.document_rels.header_count = count; self.content_type = self.content_type.add_header(); self } pub fn even_header(mut self, header: Header) -> Self { if header.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.header_count + 1; self.document.section_property = self .document .section_property .even_header(header, &create_header_rid(count)); self.document_rels.header_count = count; self.content_type = self.content_type.add_header(); self.settings = self.settings.even_and_odd_headers(); self } pub fn footer(mut self, footer: Footer) -> Self { if footer.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.footer_count + 1; self.document.section_property = self .document .section_property .footer(footer, &create_footer_rid(count)); self.document_rels.footer_count = count; self.content_type = self.content_type.add_footer(); self } pub fn first_footer(mut self, footer: Footer) -> Self { if footer.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.footer_count + 1; self.document.section_property = self .document .section_property .first_footer(footer, &create_footer_rid(count)); self.document_rels.footer_count = count; self.content_type = self.content_type.add_footer(); self } pub fn even_footer(mut self, footer: Footer) -> Self { if footer.has_numbering { self.document_rels.has_numberings = true; } let count = self.document_rels.footer_count + 1; self.document.section_property = self .document .section_property .even_footer(footer, &create_footer_rid(count)); self.document_rels.footer_count = count; self.content_type = self.content_type.add_footer(); self.settings = self.settings.even_and_odd_headers(); self } pub fn add_abstract_numbering(mut self, num: AbstractNumbering) -> Docx { self.numberings = self.numberings.add_abstract_numbering(num); self } pub fn add_numbering(mut self, num: Numbering) -> Docx { self.numberings = self.numberings.add_numbering(num); self } pub fn created_at(mut self, date: &str) -> Self { self.doc_props = self.doc_props.created_at(date); self } pub fn updated_at(mut self, date: &str) -> Self { self.doc_props = self.doc_props.updated_at(date); self } pub fn custom_property(mut self, name: impl Into, item: impl Into) -> Self { self.doc_props = self.doc_props.custom_property(name, item); self } pub fn doc_id(mut self, id: &str) -> Self { self.settings = self.settings.doc_id(id); self } pub fn default_tab_stop(mut self, stop: usize) -> Self { self.settings = self.settings.default_tab_stop(stop); self } pub fn add_doc_var(mut self, name: &str, val: &str) -> Self { self.settings = self.settings.add_doc_var(name, val); self } pub fn title_pg(mut self) -> Self { self.document = self.document.title_pg(); self } pub fn page_size(mut self, w: u32, h: u32) -> Self { self.document = self.document.page_size(PageSize::new().size(w, h)); self } pub fn page_margin(mut self, margin: crate::types::PageMargin) -> Self { self.document = self.document.page_margin(margin); self } pub fn page_orient(mut self, o: crate::types::PageOrientationType) -> Self { self.document = self.document.page_orient(o); self } pub fn default_size(mut self, size: usize) -> Self { self.styles = self.styles.default_size(size); self } pub fn default_spacing(mut self, spacing: i32) -> Self { self.styles = self.styles.default_spacing(spacing); self } pub fn default_fonts(mut self, font: RunFonts) -> Self { self.styles = self.styles.default_fonts(font); self } pub fn default_line_spacing(mut self, spacing: LineSpacing) -> Self { self.styles = self.styles.default_line_spacing(spacing); self } pub fn taskpanes(mut self) -> Self { self.taskpanes = Some(Taskpanes::new()); self.rels = self.rels.add_taskpanes_rel(); self.content_type = self.content_type.add_taskpanes(); self } pub fn web_extension(mut self, ext: WebExtension) -> Self { self.web_extensions.push(ext); self.taskpanes_rels = self.taskpanes_rels.add_rel(); self.content_type = self.content_type.add_web_extensions(); self } pub fn add_custom_item(mut self, id: &str, xml: &str) -> Self { let x = CustomItem::from_str(xml).expect("should parse xml string"); self.content_type = self.content_type.add_custom_xml(); let rel = CustomItemRels::new().add_item(); self.custom_item_props.push(CustomItemProperty::new(id)); self.document_rels = self.document_rels.add_custom_item(); self.custom_item_rels.push(rel); self.custom_items.push(x); self } pub fn page_num_type(mut self, p: PageNumType) -> Self { self.document = self.document.page_num_type(p); self } pub fn build(mut self) -> XMLDocx { self.reset(); self.update_dependencies(); let tocs: Vec<(usize, Box)> = self .document .children .iter() .enumerate() .filter_map(|(i, child)| { if let DocumentChild::TableOfContents(toc) = child { Some((i, toc.clone())) } else { None } }) .collect(); let has_toc = !tocs.is_empty(); for (i, toc) in tocs { if toc.items.is_empty() && toc.auto { let children = update_document_by_toc(self.document.children, &self.styles, *toc, i); self.document.children = children; } } let (images, mut images_bufs) = self.images_in_doc(); let (header_images, header_images_bufs) = self.images_in_header(); let (footer_images, footer_images_bufs) = self.images_in_footer(); images_bufs.extend(header_images_bufs); images_bufs.extend(footer_images_bufs); let mut header_rels = vec![HeaderRels::new(); 3]; for (i, images) in header_images.iter().enumerate() { if let Some(h) = header_rels.get_mut(i) { h.set_images(images.to_owned()); } } let mut footer_rels = vec![FooterRels::new(); 3]; for (i, images) in footer_images.iter().enumerate() { if let Some(f) = footer_rels.get_mut(i) { f.set_images(images.to_owned()); } } let web_extensions = self.web_extensions.iter().map(|ext| ext.build()).collect(); let custom_items = self.custom_items.iter().map(|xml| xml.build()).collect(); let custom_item_props = self.custom_item_props.iter().map(|p| p.build()).collect(); let custom_item_rels = self .custom_item_rels .iter() .map(|rel| rel.build()) .collect(); self.document_rels.images = images; let headers: Vec> = self .document .section_property .get_headers() .iter() .map(|h| h.build()) .collect(); let footers: Vec> = self .document .section_property .get_footers() .iter() .map(|h| h.build()) .collect(); // Collect footnotes if self.collect_footnotes() { // Relationship entry for footnotes self.content_type = self.content_type.add_footnotes(); self.document_rels.has_footnotes = true; } if has_toc { for i in 1..=9 { if !self .styles .styles .iter() .any(|s| s.name == Name::new(format!("toc {}", i))) { self.styles = self .styles .add_style(crate::documents::preset_styles::toc(i)); } } } XMLDocx { content_type: self.content_type.build(), rels: self.rels.build(), doc_props: self.doc_props.build(), styles: self.styles.build(), document: self.document.build(), comments: self.comments.build(), document_rels: self.document_rels.build(), header_rels: header_rels.into_iter().map(|r| r.build()).collect(), footer_rels: footer_rels.into_iter().map(|r| r.build()).collect(), settings: self.settings.build(), font_table: self.font_table.build(), numberings: self.numberings.build(), media: images_bufs, headers, footers, comments_extended: self.comments_extended.build(), taskpanes: self.taskpanes.map(|taskpanes| taskpanes.build()), taskpanes_rels: self.taskpanes_rels.build(), web_extensions, custom_items, custom_item_rels, custom_item_props, footnotes: self.footnotes.build(), } } pub fn json(&self) -> String { self.reset(); serde_json::to_string_pretty(&self).unwrap() } // Internal: for docx-wasm pub fn json_with_update_comments(&mut self) -> String { self.reset(); self.update_dependencies(); serde_json::to_string_pretty(&self).unwrap() } // Internal: for docx-wasm pub fn comments_json(&mut self) -> String { self.reset(); self.update_dependencies(); serde_json::to_string_pretty(&self.comments).unwrap() } fn reset(&self) { crate::reset_para_id(); } fn insert_comment_to_map( &self, comment_map: &mut HashMap, c: &CommentRangeStart, ) { let comment = c.get_comment(); let comment_id = comment.id(); for child in comment.children { if let CommentChild::Paragraph(child) = child { let para_id = child.id.clone(); comment_map.insert(comment_id, para_id.clone()); } // TODO: Support table in comment } } // Traverse and clone comments from document and add to comments node. fn update_dependencies(&mut self) { let mut comments: Vec = vec![]; let mut comments_extended: Vec = vec![]; let mut comment_map: HashMap = HashMap::new(); let mut hyperlink_map: HashMap = HashMap::new(); for child in &self.document.children { match child { DocumentChild::Paragraph(paragraph) => { for child in ¶graph.children { if let ParagraphChild::CommentStart(c) = child { self.insert_comment_to_map(&mut comment_map, c); } if let ParagraphChild::Hyperlink(h) = child { if let HyperlinkData::External { rid, path } = h.link.clone() { hyperlink_map.insert(rid, path); }; for child in &h.children { if let ParagraphChild::CommentStart(c) = child { self.insert_comment_to_map(&mut comment_map, c); } } } } } DocumentChild::Table(table) => { collect_dependencies_in_table( table, &mut comments, &mut comments_extended, &mut comment_map, &mut hyperlink_map, ); } _ => {} } } for child in &self.document.children { match child { DocumentChild::Paragraph(paragraph) => { for child in ¶graph.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } if let ParagraphChild::Hyperlink(h) = child { if let HyperlinkData::External { rid, path } = h.link.clone() { hyperlink_map.insert(rid, path); }; for child in &h.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } } } } } DocumentChild::Table(table) => { collect_dependencies_in_table( table, &mut comments, &mut comments_extended, &mut comment_map, &mut hyperlink_map, ); } DocumentChild::TableOfContents(toc) => { // TODO:refine later for child in &toc.before_contents { if let TocContent::Paragraph(paragraph) = child { for child in ¶graph.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } if let ParagraphChild::Hyperlink(h) = child { if let HyperlinkData::External { rid, path } = h.link.clone() { hyperlink_map.insert(rid, path); }; for child in &h.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } } } } } if let TocContent::Table(table) = child { collect_dependencies_in_table( table, &mut comments, &mut comments_extended, &mut comment_map, &mut hyperlink_map, ); } } for child in &toc.after_contents { if let TocContent::Paragraph(paragraph) = child { for child in ¶graph.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } if let ParagraphChild::Hyperlink(h) = child { if let HyperlinkData::External { rid, path } = h.link.clone() { hyperlink_map.insert(rid, path); }; for child in &h.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended( &mut comments, &mut comments_extended, &comment_map, c, ); } } } } } if let TocContent::Table(table) = child { collect_dependencies_in_table( table, &mut comments, &mut comments_extended, &mut comment_map, &mut hyperlink_map, ); } } } _ => {} } } // If this document has comments, set comments.xml to document_rels. // This is because comments.xml without comment cause an error on word online. if !comments.is_empty() { self.document_rels.has_comments = true; } self.comments_extended .add_comments_extended(comments_extended); self.comments.add_comments(comments); for (id, d) in hyperlink_map { self.document_rels .hyperlinks .push((id, d, "External".to_string())); // Now support external only } } // Traverse and clone comments from document and add to comments node. // reader only pub(crate) fn store_comments(&mut self, comments: &[Comment]) { for child in &mut self.document.children { match child { DocumentChild::Paragraph(paragraph) => { for child in &mut paragraph.children { if let ParagraphChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } if let ParagraphChild::Insert(ref mut insert) = child { for child in &mut insert.children { if let InsertChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } else if let InsertChild::Delete(ref mut d) = child { for child in &mut d.children { if let DeleteChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } } } } } if let ParagraphChild::Delete(ref mut delete) = child { for child in &mut delete.children { if let DeleteChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } } } } } DocumentChild::Table(table) => store_comments_in_table(table, comments), _ => {} } } if !comments.is_empty() { self.document_rels.has_comments = true; } } // Traverse and collect images from document. fn images_in_doc(&mut self) -> (Vec, Vec) { let mut images: Vec<(String, String)> = vec![]; let mut image_bufs: Vec<(String, Vec)> = vec![]; for child in &mut self.document.children { match child { DocumentChild::Paragraph(paragraph) => { collect_images_from_paragraph(paragraph, &mut images, &mut image_bufs, None); } DocumentChild::Table(table) => { collect_images_from_table(table, &mut images, &mut image_bufs, None); } _ => {} } } (images, image_bufs) } fn images_in_header(&mut self) -> (Vec>, Vec) { let mut header_images: Vec> = vec![vec![]; 3]; let mut image_bufs: Vec<(String, Vec)> = vec![]; if let Some(header) = &mut self.document.section_property.header.as_mut() { let mut images: Vec = vec![]; for child in header.children.iter_mut() { match child { HeaderChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } header_images[0] = images; } if let Some(header) = &mut self.document.section_property.first_header.as_mut() { let mut images: Vec = vec![]; for child in header.children.iter_mut() { match child { HeaderChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } header_images[1] = images; } if let Some(header) = &mut self.document.section_property.even_header.as_mut() { let mut images: Vec = vec![]; for child in header.children.iter_mut() { match child { HeaderChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } HeaderChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } header_images[2] = images; } (header_images, image_bufs) } // Traverse and collect images from header. fn images_in_footer(&mut self) -> (Vec>, Vec) { let mut footer_images: Vec> = vec![vec![]; 3]; let mut image_bufs: Vec<(String, Vec)> = vec![]; if let Some(footer) = &mut self.document.section_property.footer.as_mut() { let mut images: Vec = vec![]; for child in footer.children.iter_mut() { match child { FooterChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } footer_images[0] = images; } if let Some(footer) = &mut self.document.section_property.first_footer.as_mut() { let mut images: Vec = vec![]; for child in footer.children.iter_mut() { match child { FooterChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } footer_images[1] = images; } if let Some(footer) = &mut self.document.section_property.even_footer.as_mut() { let mut images: Vec = vec![]; for child in footer.children.iter_mut() { match child { FooterChild::Paragraph(paragraph) => { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::Table(table) => { collect_images_from_table( table, &mut images, &mut image_bufs, Some("footer"), ); } FooterChild::StructuredDataTag(tag) => { for child in tag.children.iter_mut() { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_images_from_paragraph( paragraph, &mut images, &mut image_bufs, Some("header"), ); } if let StructuredDataTagChild::Table(table) = child { collect_images_from_table( table, &mut images, &mut image_bufs, Some("header"), ); } } } } } footer_images[2] = images; } (footer_images, image_bufs) } /// Collect footnotes from all Runs to the docx footnotes node. pub fn collect_footnotes(&mut self) -> bool { let footnotes: Vec = self .document .children .iter() .filter_map(|child| match child { DocumentChild::Paragraph(paragraph) => Some(¶graph.children), _ => None, }) .flat_map(|children| children.iter()) .filter_map(|para_child| match para_child { ParagraphChild::Run(run) => Some(&run.children), _ => None, }) .flat_map(|children| children.iter()) .filter_map(|run_child| match run_child { RunChild::FootnoteReference(footnote_ref) => Some(footnote_ref), _ => None, }) .map(Into::::into) .collect(); let is_footnotes = !footnotes.is_empty(); self.footnotes.add(footnotes); is_footnotes } } fn collect_dependencies_in_paragraph( paragraph: &Paragraph, comments: &mut Vec, comments_extended: &mut Vec, comment_map: &mut HashMap, hyperlink_map: &mut HashMap, ) { for child in ¶graph.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended(comments, comments_extended, comment_map, c); } if let ParagraphChild::Hyperlink(h) = child { if let HyperlinkData::External { rid, path } = h.link.clone() { hyperlink_map.insert(rid, path); }; for child in &h.children { if let ParagraphChild::CommentStart(c) = child { push_comment_and_comment_extended(comments, comments_extended, comment_map, c); } } } } } fn collect_dependencies_in_table( table: &Table, comments: &mut Vec, comments_extended: &mut Vec, comment_map: &mut HashMap, hyperlink_map: &mut HashMap, ) { for TableChild::TableRow(row) in &table.rows { for TableRowChild::TableCell(cell) in &row.cells { for content in &cell.children { match content { TableCellContent::Paragraph(paragraph) => { collect_dependencies_in_paragraph( paragraph, comments, comments_extended, comment_map, hyperlink_map, ); } TableCellContent::Table(table) => collect_dependencies_in_table( table, comments, comments_extended, comment_map, hyperlink_map, ), TableCellContent::StructuredDataTag(tag) => { for child in &tag.children { if let StructuredDataTagChild::Paragraph(paragraph) = child { collect_dependencies_in_paragraph( paragraph, comments, comments_extended, comment_map, hyperlink_map, ); } if let StructuredDataTagChild::Table(table) = child { collect_dependencies_in_table( table, comments, comments_extended, comment_map, hyperlink_map, ); } } } TableCellContent::TableOfContents(t) => { for child in &t.before_contents { if let TocContent::Paragraph(paragraph) = child { collect_dependencies_in_paragraph( paragraph, comments, comments_extended, comment_map, hyperlink_map, ); } if let TocContent::Table(table) = child { collect_dependencies_in_table( table, comments, comments_extended, comment_map, hyperlink_map, ); } } for child in &t.after_contents { if let TocContent::Paragraph(paragraph) = child { collect_dependencies_in_paragraph( paragraph, comments, comments_extended, comment_map, hyperlink_map, ); } if let TocContent::Table(table) = child { collect_dependencies_in_table( table, comments, comments_extended, comment_map, hyperlink_map, ); } } } } } } } } fn store_comments_in_paragraph(paragraph: &mut Paragraph, comments: &[Comment]) { for child in &mut paragraph.children { if let ParagraphChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } if let ParagraphChild::Insert(ref mut insert) = child { for child in &mut insert.children { if let InsertChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } } } if let ParagraphChild::Delete(ref mut delete) = child { for child in &mut delete.children { if let DeleteChild::CommentStart(ref mut c) = child { let comment_id = c.get_id(); if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) { let comment = comment.clone(); c.as_mut().comment(comment); } } } } } } fn store_comments_in_table(table: &mut Table, comments: &[Comment]) { for TableChild::TableRow(row) in &mut table.rows { for TableRowChild::TableCell(cell) in &mut row.cells { for content in &mut cell.children { match content { TableCellContent::Paragraph(paragraph) => { store_comments_in_paragraph(paragraph, comments) } TableCellContent::Table(ref mut table) => { store_comments_in_table(table, comments); } TableCellContent::StructuredDataTag(ref mut tag) => { for child in &mut tag.children { if let StructuredDataTagChild::Paragraph(paragraph) = child { store_comments_in_paragraph(paragraph, comments); } if let StructuredDataTagChild::Table(table) = child { store_comments_in_table(table, comments); } } } TableCellContent::TableOfContents(ref mut t) => { for child in &mut t.before_contents { if let TocContent::Paragraph(paragraph) = child { store_comments_in_paragraph(paragraph, comments); } if let TocContent::Table(table) = child { store_comments_in_table(table, comments); } } for child in &mut t.after_contents { if let TocContent::Paragraph(paragraph) = child { store_comments_in_paragraph(paragraph, comments); } if let TocContent::Table(table) = child { store_comments_in_table(table, comments); } } } } } } } } fn push_comment_and_comment_extended( comments: &mut Vec, comments_extended: &mut Vec, comment_map: &HashMap, c: &CommentRangeStart, ) { let comment = c.get_comment(); for child in comment.children { if let CommentChild::Paragraph(child) = child { let para_id = child.id.clone(); comments.push(c.get_comment()); let comment_extended = CommentExtended::new(para_id); if let Some(parent_comment_id) = comment.parent_comment_id { if let Some(parent_para_id) = comment_map.get(&parent_comment_id) { comments_extended .push(comment_extended.parent_paragraph_id(parent_para_id.clone())); } } else { comments_extended.push(comment_extended); } } // TODO: Support table in comment } } fn update_document_by_toc( document_children: Vec, styles: &Styles, toc: TableOfContents, toc_index: usize, ) -> Vec { let heading_map = styles.create_heading_style_map(); let mut items = vec![]; let mut children = vec![]; let style_map: std::collections::HashMap = toc .instr .styles_with_levels .iter() .map(|sl| sl.0.clone()) .collect(); if toc.instr.heading_styles_range.is_none() && !toc.instr.styles_with_levels.is_empty() { // INFO: if \t option set without heading styles ranges, Microsoft word does not show ToC items... return document_children; } let (min, max) = toc.instr.heading_styles_range.unwrap_or((0, 9)); for child in document_children.into_iter() { match child { DocumentChild::Paragraph(mut paragraph) => { if let Some(heading_level) = paragraph .property .style .as_ref() .map(|p| p.val.to_string()) .and_then(|sid| heading_map.get(&sid)) { if min <= *heading_level && max >= *heading_level { let toc_key = TocKey::generate(); items.push( TableOfContentsItem::new() .text(paragraph.raw_text()) .toc_key(&toc_key) .level(*heading_level), ); paragraph = Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key)); } if let Some((_min, _max)) = toc.instr.tc_field_level_range { // TODO: check tc field } } // Support \t option. Collect toc items if style id matched. if let Some(level) = paragraph .property .style .as_ref() .and_then(|s| style_map.get(&s.val)) { if min <= *level && max >= *level { let toc_key = TocKey::generate(); items.push( TableOfContentsItem::new() .text(paragraph.raw_text()) .toc_key(&toc_key) .level(*level), ); paragraph = Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key)); } } children.push(DocumentChild::Paragraph(paragraph)); } DocumentChild::Table(ref _table) => { // TODO: // for row in &table.rows { // for cell in &row.cells { // for content in &cell.children { // match content { // TableCellContent::Paragraph(paragraph) => {} // TableCellContent::Table(_) => { // // TODO: Support table in table // } // } // } // } // } children.push(child); } _ => { children.push(child); } } } let mut toc = toc; toc.items = items; children[toc_index] = DocumentChild::TableOfContents(Box::new(toc)); children } docx-rs-0.4.18/src/documents/numberings.rs000064400000000000000000000112741046102023000166110ustar 00000000000000use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; use std::io::Write; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct Numberings { pub abstract_nums: Vec, pub numberings: Vec, } impl Numberings { pub fn new() -> Self { Default::default() } pub fn add_abstract_numbering(mut self, n: AbstractNumbering) -> Self { self.abstract_nums.push(n); self } pub fn add_numbering(mut self, n: Numbering) -> Self { self.numberings.push(n); self } } impl BuildXML for Numberings { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_numbering()? .add_child(&create_default_numbering())? .add_children(&self.abstract_nums)? .add_child(&Numbering::new(1, 1))? .add_children(&self.numberings)? .close()? .into_inner() } } fn create_default_numbering() -> AbstractNumbering { AbstractNumbering::new(1) .add_level( Level::new( 0, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%1."), LevelJc::new("left"), ) .indent(Some(420), Some(SpecialIndentType::Hanging(420)), None, None), ) .add_level( Level::new( 1, Start::new(1), NumberFormat::new("decimal"), LevelText::new("(%2)"), LevelJc::new("left"), ) .indent(Some(840), Some(SpecialIndentType::Hanging(420)), None, None), ) .add_level( Level::new( 2, Start::new(1), NumberFormat::new("decimalEnclosedCircle"), LevelText::new("%3"), LevelJc::new("left"), ) .indent( Some(1260), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 3, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%4."), LevelJc::new("left"), ) .indent( Some(1680), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 4, Start::new(1), NumberFormat::new("decimal"), LevelText::new("(%5)"), LevelJc::new("left"), ) .indent( Some(2100), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 5, Start::new(1), NumberFormat::new("decimalEnclosedCircle"), LevelText::new("%6"), LevelJc::new("left"), ) .indent( Some(2520), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 6, Start::new(1), NumberFormat::new("decimal"), LevelText::new("%7."), LevelJc::new("left"), ) .indent( Some(2940), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 7, Start::new(1), NumberFormat::new("decimal"), LevelText::new("(%8)"), LevelJc::new("left"), ) .indent( Some(3360), Some(SpecialIndentType::Hanging(420)), None, None, ), ) .add_level( Level::new( 8, Start::new(1), NumberFormat::new("decimalEnclosedCircle"), LevelText::new("%9"), LevelJc::new("left"), ) .indent( Some(3780), Some(SpecialIndentType::Hanging(420)), None, None, ), ) } docx-rs-0.4.18/src/documents/paragraph_id.rs000064400000000000000000000011221046102023000170500ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static PARA_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_para_id() -> String { use std::sync::atomic::Ordering; let id = PARA_ID.fetch_add(1, Ordering::Relaxed); format!("{:08x}", id) } #[cfg(not(test))] pub fn reset_para_id() { use std::sync::atomic::Ordering; PARA_ID.load(Ordering::Relaxed); PARA_ID.store(1, Ordering::Relaxed); } #[cfg(test)] pub fn generate_para_id() -> String { "12345678".to_owned() } #[cfg(test)] pub fn reset_para_id() { // NOP } docx-rs-0.4.18/src/documents/paragraph_property_change_id.rs000064400000000000000000000007731046102023000223340ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static HISTORY_ID: AtomicUsize = AtomicUsize::new(0); #[cfg(not(test))] pub trait ParagraphPropertyChangeId { fn generate(&self) -> String { use std::sync::atomic::Ordering; let id = HISTORY_ID.load(Ordering::Relaxed); HISTORY_ID.store(id + 1, Ordering::Relaxed); format!("{}", id) } } #[cfg(test)] pub trait ParagraphPropertyChangeId { fn generate(&self) -> &str { "123" } } docx-rs-0.4.18/src/documents/pic_id.rs000064400000000000000000000007201046102023000156610ustar 00000000000000#[cfg(not(test))] use std::sync::atomic::AtomicUsize; #[cfg(not(test))] static PIC_ID: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] pub fn generate_pic_id() -> usize { use std::sync::atomic::Ordering; let id = PIC_ID.load(Ordering::Relaxed); PIC_ID.store(id.wrapping_add(1), Ordering::Relaxed); id } #[cfg(test)] pub fn generate_pic_id() -> usize { 123 } pub fn create_pic_rid(id: usize) -> String { format!("rIdImage{}", id) } docx-rs-0.4.18/src/documents/preset_styles/mod.rs000064400000000000000000000000321046102023000201120ustar 00000000000000mod toc; pub use toc::*; docx-rs-0.4.18/src/documents/preset_styles/toc.rs000064400000000000000000000006301046102023000201240ustar 00000000000000use crate::documents::*; use crate::types::*; pub fn toc(level: i32) -> Style { let spacing = LineSpacing::new().after(100); Style::new(format!("ToC{}", level), StyleType::Paragraph) .name(format!("toc {}", level)) .align(AlignmentType::Both) .snap_to_grid(false) .indent(Some((level - 1) * 200), None, None, Some((level - 1) * 100)) .line_spacing(spacing) } docx-rs-0.4.18/src/documents/rels.rs000064400000000000000000000066441046102023000154120ustar 00000000000000use serde::{Deserialize, Serialize}; use std::io::Write; use crate::documents::BuildXML; use crate::xml_builder::*; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)] 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.rels.push(( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" .to_owned(), "rId4".to_owned(), "docProps/custom.xml".to_owned(), )); self } pub fn add_taskpanes_rel(mut self) -> Self { self = self.add_rel( "http://schemas.microsoft.com/office/2011/relationships/webextensiontaskpanes", "word/webextensions/taskpanes.xml", ); self } pub fn add_rel(mut self, rel_type: impl Into, target: impl Into) -> Self { self.rels.push(( rel_type.into(), format!("rId{}", self.rels.len() + 1), 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 BuildXML for Rels { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(None)? .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships")? .apply_each(&self.rels, |(k, id, v), b| b.relationship(id, k, v))? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let c = Rels::new().set_default(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } docx-rs-0.4.18/src/documents/settings.rs000064400000000000000000000130001046102023000162650ustar 00000000000000use super::*; use std::io::Write; use crate::documents::BuildXML; use crate::types::CharacterSpacingValues; use crate::xml_builder::*; use serde::Serialize; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Settings { default_tab_stop: DefaultTabStop, zoom: Zoom, doc_id: Option, doc_vars: Vec, even_and_odd_headers: bool, adjust_line_height_in_table: bool, #[serde(skip_serializing_if = "Option::is_none")] character_spacing_control: Option, } impl Settings { pub fn new() -> Settings { Default::default() } pub fn doc_id(mut self, id: impl Into) -> Self { self.doc_id = Some(DocId::new(id.into())); self } pub fn default_tab_stop(mut self, tab_stop: usize) -> Self { self.default_tab_stop = DefaultTabStop::new(tab_stop); self } pub fn add_doc_var(mut self, name: impl Into, val: impl Into) -> Self { self.doc_vars.push(DocVar::new(name, val)); self } pub fn even_and_odd_headers(mut self) -> Self { self.even_and_odd_headers = true; self } pub fn adjust_line_height_in_table(mut self) -> Self { self.adjust_line_height_in_table = true; self } pub fn character_spacing_control(mut self, val: CharacterSpacingValues) -> Self { self.character_spacing_control = Some(val); self } } impl Default for Settings { fn default() -> Self { Self { default_tab_stop: DefaultTabStop::new(840), zoom: Zoom::new(100), doc_id: None, doc_vars: vec![], even_and_odd_headers: false, adjust_line_height_in_table: false, character_spacing_control: None, } } } impl BuildXML for Settings { fn build_to( &self, stream: xml::writer::EventWriter, ) -> xml::writer::Result> { XMLBuilder::from(stream) .declaration(Some(true))? .open_settings()? .add_child(&self.default_tab_stop)? .add_child(&self.zoom)? .open_compat()? .space_for_ul()? .balance_single_byte_double_byte_width()? .do_not_leave_backslash_alone()? .ul_trail_space()? .do_not_expand_shift_return()? .apply_opt(self.character_spacing_control, |v, b| { b.character_spacing_control(&v.to_string()) })? .apply_if(self.adjust_line_height_in_table, |b| { b.adjust_line_height_table() })? .use_fe_layout()? .compat_setting( "compatibilityMode", "http://schemas.microsoft.com/office/word", "15", )? .compat_setting( "overrideTableStyleFontSizeAndJustification", "http://schemas.microsoft.com/office/word", "1", )? .compat_setting( "enableOpenTypeFeatures", "http://schemas.microsoft.com/office/word", "1", )? .compat_setting( "doNotFlipMirrorIndents", "http://schemas.microsoft.com/office/word", "1", )? .compat_setting( "differentiateMultirowTableHeaders", "http://schemas.microsoft.com/office/word", "1", )? .compat_setting( "useWord2013TrackBottomHyphenation", "http://schemas.microsoft.com/office/word", "0", )? .close()? .add_optional_child(&self.doc_id)? .apply_if(!self.doc_vars.is_empty(), |b| { b.open_doc_vars()?.add_children(&self.doc_vars)?.close() })? .apply_if(self.even_and_odd_headers, |b| b.even_and_odd_headers())? .close()? .into_inner() } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; use std::str; #[test] fn test_settings() { let c = Settings::new(); let b = c.build(); assert_eq!( str::from_utf8(&b).unwrap(), r#""# ); } } ././@LongLink00006440000000000000000000000165000000000000007775Lustar docx-rs-0.4.18/src/documents/snapshots/docx_rs__documents__comments_extended__tests__comments_extended_snapshot.snapdocx-rs-0.4.18/src/documents/snapshots/docx_rs__documents__comments_extended__tests__comments_extend000064400000000000000000000044431046102023000326400ustar 00000000000000--- source: docx-core/src/documents/comments_extended.rs expression: "str::from_utf8(&b).unwrap()" --- docx-rs-0.4.18/src/documents/snapshots/docx_rs__documents__comments_extended__tests__settings.snap000064400000000000000000000045471046102023000322510ustar 00000000000000--- source: docx-core/src/documents/comments_extended.rs expression: "str::from_utf8(&b).unwrap()" --- "" docx-rs-0.4.18/src/documents/styles.rs000064400000000000000000000135141046102023000157620ustar 00000000000000use serde::Serialize; use std::io::Write; use super::*; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Styles { pub doc_defaults: DocDefaults, pub styles: Vec