From 13b8d9bc443d31560f002e549525bdc554baf798 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Mon, 18 Mar 2024 21:29:16 +0900 Subject: [PATCH] feat: Support w:textAlignment (#690) --- docx-core/src/documents/elements/mod.rs | 2 + .../documents/elements/paragraph_property.rs | 12 ++++- docx-core/src/documents/elements/style.rs | 5 +++ .../src/documents/elements/text_alignment.rs | 31 +++++++++++++ docx-core/src/reader/paragraph_property.rs | 6 +++ docx-core/src/reader/xml_element.rs | 2 + docx-core/src/types/mod.rs | 2 + docx-core/src/types/text_alignment_type.rs | 45 +++++++++++++++++++ docx-core/src/xml_builder/elements.rs | 2 + .../js/json/bindings/TextAlignmentType.ts | 2 + docx-wasm/js/paragraph-property.ts | 37 +++++++++++++++ docx-wasm/src/paragraph.rs | 5 +++ docx-wasm/src/style.rs | 5 +++ .../test/__snapshots__/index.test.js.snap | 17 +++++++ 14 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 docx-core/src/documents/elements/text_alignment.rs create mode 100644 docx-core/src/types/text_alignment_type.rs create mode 100644 docx-wasm/js/json/bindings/TextAlignmentType.ts diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index 7695e2f..4fb2d2a 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -112,6 +112,7 @@ mod table_style; mod table_width; mod tabs; mod text; +mod text_alignment; mod text_border; mod text_box; mod text_box_content; @@ -240,6 +241,7 @@ 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::*; diff --git a/docx-core/src/documents/elements/paragraph_property.rs b/docx-core/src/documents/elements/paragraph_property.rs index 06ab0c5..db56a0f 100644 --- a/docx-core/src/documents/elements/paragraph_property.rs +++ b/docx-core/src/documents/elements/paragraph_property.rs @@ -3,8 +3,8 @@ use serde::Serialize; use super::*; use crate::documents::BuildXML; use crate::types::{AlignmentType, SpecialIndentType}; -use crate::xml_builder::*; use crate::ParagraphBorderPosition; +use crate::{xml_builder::*, TextAlignmentType}; #[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] @@ -42,6 +42,8 @@ pub struct ParagraphProperty { 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, } // 17.3.1.26 @@ -145,6 +147,11 @@ impl ParagraphProperty { self } + pub fn text_alignment(mut self, s: TextAlignmentType) -> Self { + self.text_alignment = Some(TextAlignment::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)); @@ -192,7 +199,8 @@ fn inner_build(p: &ParagraphProperty) -> Vec { .add_optional_child(&p.line_spacing) .add_optional_child(&p.outline_lvl) .add_optional_child(&p.paragraph_property_change) - .add_optional_child(&p.borders); + .add_optional_child(&p.borders) + .add_optional_child(&p.text_alignment); if let Some(v) = p.keep_next { if v { diff --git a/docx-core/src/documents/elements/style.rs b/docx-core/src/documents/elements/style.rs index a0c03da..ddfe1d9 100644 --- a/docx-core/src/documents/elements/style.rs +++ b/docx-core/src/documents/elements/style.rs @@ -123,6 +123,11 @@ impl Style { self } + pub fn text_alignment(mut self, alignment_type: TextAlignmentType) -> Self { + self.paragraph_property = self.paragraph_property.text_alignment(alignment_type); + self + } + pub fn indent( mut self, left: Option, diff --git a/docx-core/src/documents/elements/text_alignment.rs b/docx-core/src/documents/elements/text_alignment.rs new file mode 100644 index 0000000..afe9f3e --- /dev/null +++ b/docx-core/src/documents/elements/text_alignment.rs @@ -0,0 +1,31 @@ +use serde::{Serialize, Serializer}; + +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(&self) -> Vec { + let b = XMLBuilder::new(); + let v = format!("{}", self.0); + b.text_alignment(&v).build() + } +} + +impl Serialize for TextAlignment { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let v = format!("{}", self.0); + serializer.serialize_str(&v) + } +} diff --git a/docx-core/src/reader/paragraph_property.rs b/docx-core/src/reader/paragraph_property.rs index fbd3061..a80d7d6 100644 --- a/docx-core/src/reader/paragraph_property.rs +++ b/docx-core/src/reader/paragraph_property.rs @@ -50,6 +50,12 @@ impl ElementReader for ParagraphProperty { } continue; } + XMLElement::TextAlignment => { + if let Ok(v) = TextAlignmentType::from_str(&attributes[0].value) { + p = p.text_alignment(v); + } + continue; + } XMLElement::ParagraphStyle => { p = p.style(&attributes[0].value); continue; diff --git a/docx-core/src/reader/xml_element.rs b/docx-core/src/reader/xml_element.rs index c5536a0..732b852 100644 --- a/docx-core/src/reader/xml_element.rs +++ b/docx-core/src/reader/xml_element.rs @@ -160,6 +160,7 @@ pub enum XMLElement { Type, PageNumType, FrameProperty, + TextAlignment, H, HAnchor, HSpace, @@ -405,6 +406,7 @@ impl FromStr for XMLElement { "sdt" => Ok(XMLElement::StructuredDataTag), "pgNumType" => Ok(XMLElement::PageNumType), "framePr" => Ok(XMLElement::FrameProperty), + "textAlignment" => Ok(XMLElement::TextAlignment), "h" => Ok(XMLElement::H), "hAnchor" => Ok(XMLElement::HAnchor), "hSpace" => Ok(XMLElement::HSpace), diff --git a/docx-core/src/types/mod.rs b/docx-core/src/types/mod.rs index 0af1d35..b5ac028 100644 --- a/docx-core/src/types/mod.rs +++ b/docx-core/src/types/mod.rs @@ -24,6 +24,7 @@ pub mod tab_leader_type; pub mod tab_value_type; pub mod table_alignment_type; pub mod table_layout_type; +pub mod text_alignment_type; pub mod text_direction_type; pub mod vert_align_type; pub mod vertical_align_type; @@ -56,6 +57,7 @@ pub use tab_leader_type::*; pub use tab_value_type::*; pub use table_alignment_type::*; pub use table_layout_type::*; +pub use text_alignment_type::*; pub use text_direction_type::*; pub use vert_align_type::*; pub use vertical_align_type::*; diff --git a/docx-core/src/types/text_alignment_type.rs b/docx-core/src/types/text_alignment_type.rs new file mode 100644 index 0000000..30d2e01 --- /dev/null +++ b/docx-core/src/types/text_alignment_type.rs @@ -0,0 +1,45 @@ +use std::fmt; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +use serde::Serialize; + +use super::errors; +use std::str::FromStr; + +#[cfg_attr(feature = "wasm", wasm_bindgen, derive(ts_rs::TS), ts(export))] +#[derive(Debug, Clone, Copy, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum TextAlignmentType { + Auto, + Baseline, + Bottom, + Center, + Top, +} + +impl fmt::Display for TextAlignmentType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TextAlignmentType::Auto => write!(f, "auto"), + TextAlignmentType::Baseline => write!(f, "baseline"), + TextAlignmentType::Bottom => write!(f, "bottom"), + TextAlignmentType::Center => write!(f, "center"), + TextAlignmentType::Top => write!(f, "top"), + } + } +} + +impl FromStr for TextAlignmentType { + type Err = errors::TypeError; + fn from_str(s: &str) -> Result { + match s { + "auto" => Ok(TextAlignmentType::Auto), + "baseline" => Ok(TextAlignmentType::Baseline), + "bottom" => Ok(TextAlignmentType::Bottom), + "center" => Ok(TextAlignmentType::Center), + "top" => Ok(TextAlignmentType::Top), + _ => Err(errors::TypeError::FromStrError), + } + } +} diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 8b23b97..c64d466 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -166,6 +166,8 @@ impl XMLBuilder { // i.e. closed_with_usize!(sz_cs, "w:szCs"); + closed_with_str!(text_alignment, "w:textAlignment"); + closed!(field_character, "w:fldChar", "w:fldCharType", "w:dirty"); open!(open_instr_text, "w:instrText"); diff --git a/docx-wasm/js/json/bindings/TextAlignmentType.ts b/docx-wasm/js/json/bindings/TextAlignmentType.ts new file mode 100644 index 0000000..a125b95 --- /dev/null +++ b/docx-wasm/js/json/bindings/TextAlignmentType.ts @@ -0,0 +1,2 @@ + +export type TextAlignmentType = "auto" | "baseline" | "bottom" | "center" | "top"; \ No newline at end of file diff --git a/docx-wasm/js/paragraph-property.ts b/docx-wasm/js/paragraph-property.ts index ee21eb6..f05985e 100644 --- a/docx-wasm/js/paragraph-property.ts +++ b/docx-wasm/js/paragraph-property.ts @@ -1,6 +1,7 @@ import { RunProperty, createDefaultRunProperty } from "./run"; import * as wasm from "./pkg"; +import { TextAlignmentType } from "./json/bindings/TextAlignmentType"; export type AlignmentType = | "center" @@ -51,6 +52,7 @@ export class LineSpacing { export type ParagraphProperty = { align?: AlignmentType; + textAlignment?: TextAlignmentType; styleId?: string; indent?: { left: number; @@ -113,6 +115,31 @@ export const createParagraphAlignment = ( } }; +export const createParagraphTextAlignment = ( + align?: TextAlignmentType | undefined +): wasm.TextAlignmentType | null => { + switch (align) { + case "auto": { + return wasm.TextAlignmentType.Auto; + } + case "baseline": { + return wasm.TextAlignmentType.Baseline; + } + case "bottom": { + return wasm.TextAlignmentType.Bottom; + } + case "center": { + return wasm.TextAlignmentType.Center; + } + case "top": { + return wasm.TextAlignmentType.Top; + } + default: { + return null; + } + } +}; + export class ParagraphPropertyChange { _author: string = ""; _date: string = ""; @@ -133,6 +160,11 @@ export class ParagraphPropertyChange { return this; } + textAlignment(type: TextAlignmentType) { + this._property.textAlignment = type; + return this; + } + style(id: string) { this._property.styleId = id; return this; @@ -209,6 +241,11 @@ export const setParagraphProperty = ( target = target.align(alignment) as T; } + const textAlignment = createParagraphTextAlignment(property.textAlignment); + if (textAlignment != null) { + target = target.text_alignment(textAlignment) as T; + } + if (typeof property.indent !== "undefined") { const { indent } = property; let kind; diff --git a/docx-wasm/src/paragraph.rs b/docx-wasm/src/paragraph.rs index c27c43f..dc0bab0 100644 --- a/docx-wasm/src/paragraph.rs +++ b/docx-wasm/src/paragraph.rs @@ -131,6 +131,11 @@ impl Paragraph { self } + pub fn text_alignment(mut self, alignment_type: docx_rs::TextAlignmentType) -> Paragraph { + self.0.property = self.0.property.text_alignment(alignment_type); + self + } + pub fn outline_lvl(mut self, level: usize) -> Paragraph { self.0.property = self.0.property.outline_lvl(level); self diff --git a/docx-wasm/src/style.rs b/docx-wasm/src/style.rs index 74e214f..8beb8d6 100644 --- a/docx-wasm/src/style.rs +++ b/docx-wasm/src/style.rs @@ -104,6 +104,11 @@ impl Style { self } + pub fn text_alignment(mut self, alignment_type: docx_rs::TextAlignmentType) -> Self { + self.0.paragraph_property = self.0.paragraph_property.text_alignment(alignment_type); + self + } + pub fn indent( mut self, left: i32, diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap index 0755f92..c98ff87 100644 --- a/docx-wasm/test/__snapshots__/index.test.js.snap +++ b/docx-wasm/test/__snapshots__/index.test.js.snap @@ -96369,6 +96369,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "baseline", }, "runProperty": Object { "fonts": Object { @@ -97842,6 +97843,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "bottom", }, "runProperty": Object { "fonts": Object { @@ -99859,6 +99861,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -99944,6 +99947,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -102528,6 +102532,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "baseline", }, "runProperty": Object { "fonts": Object { @@ -102691,6 +102696,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", "widowControl": true, }, "runProperty": Object { @@ -102867,6 +102873,7 @@ Object { "val": "left", }, ], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -103226,6 +103233,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -103391,6 +103399,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -103476,6 +103485,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", }, "runProperty": Object { "fonts": Object { @@ -103560,6 +103570,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "baseline", "widowControl": true, }, "runProperty": Object { @@ -103650,6 +103661,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "auto", "widowControl": true, }, "runProperty": Object { @@ -105009,6 +105021,7 @@ Object { "val": "left", }, ], + "textAlignment": "baseline", }, "runProperty": Object { "fonts": Object { @@ -107763,6 +107776,7 @@ Object { }, "runProperty": Object {}, "tabs": Array [], + "textAlignment": "baseline", }, "runProperty": Object { "fonts": Object { @@ -109307,6 +109321,7 @@ Object { "val": "clear", }, ], + "textAlignment": "auto", }, "runProperty": Object { "bold": false, @@ -143646,6 +143661,7 @@ Object { "val": "clear", }, ], + "textAlignment": "baseline", }, "runProperty": Object { "characterSpacing": 0, @@ -143762,6 +143778,7 @@ Object { "val": "clear", }, ], + "textAlignment": "baseline", }, "runProperty": Object { "characterSpacing": 0,