Skip to content

Commit

Permalink
Support tblppr (#693)
Browse files Browse the repository at this point in the history
* feat: Support table position

* fix: table pr

* fix: wasm for table position

* fix: reader for position property

* fix: snaps

* fix

* fix
  • Loading branch information
bokuweb authored Mar 26, 2024
1 parent 9cb9ea8 commit 666c950
Show file tree
Hide file tree
Showing 23 changed files with 438 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docx-core/src/documents/elements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ 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;
Expand Down Expand Up @@ -238,6 +239,7 @@ 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::*;
Expand Down
5 changes: 5 additions & 0 deletions docx-core/src/documents/elements/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ impl Table {
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
Expand Down
110 changes: 110 additions & 0 deletions docx-core/src/documents/elements/table_position_property.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use serde::Serialize;

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<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub right_from_text: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vertical_anchor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub horizontal_anchor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_x_alignment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_y_alignment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_x: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_y: Option<i32>,
}

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<String>) -> Self {
self.vertical_anchor = Some(v.into());
self
}

pub fn horizontal_anchor(mut self, v: impl Into<String>) -> Self {
self.horizontal_anchor = Some(v.into());
self
}

pub fn position_x_alignment(mut self, v: impl Into<String>) -> Self {
self.position_x_alignment = Some(v.into());
self
}

pub fn position_y_alignment(mut self, v: impl Into<String>) -> 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(&self) -> Vec<u8> {
XMLBuilder::new().table_position_property(self).build()
}
}

#[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#"<w:tblpPr />"#);
}

#[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#"<w:tblpPr w:leftFromText="142" w:rightFromText="142" w:vertAnchor="text" w:horzAnchor="margin" w:tblpXSpec="right" w:tblpY="511" />"#
);
}
}
9 changes: 9 additions & 0 deletions docx-core/src/documents/elements/table_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct TableProperty {
style: Option<TableStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
layout: Option<TableLayout>,
#[serde(skip_serializing_if = "Option::is_none")]
position: Option<TablePositionProperty>,
}

impl Default for TableProperty {
Expand All @@ -34,6 +36,7 @@ impl Default for TableProperty {
indent: None,
style: None,
layout: None,
position: None,
}
}
}
Expand Down Expand Up @@ -139,6 +142,11 @@ impl TableProperty {
self.layout = Some(TableLayout::new(t));
self
}

pub fn position(mut self, p: TablePositionProperty) -> Self {
self.position = Some(p);
self
}
}

impl BuildXML for TableProperty {
Expand All @@ -152,6 +160,7 @@ impl BuildXML for TableProperty {
.add_optional_child(&self.indent)
.add_optional_child(&self.style)
.add_optional_child(&self.layout)
.add_optional_child(&self.position)
.close()
.build()
}
Expand Down
1 change: 1 addition & 0 deletions docx-core/src/reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod table_cell_borders;
mod table_cell_margins;
mod table_cell_property;
mod table_property;
mod table_position_property;
mod table_row;
mod tabs;
mod text_box_content;
Expand Down
5 changes: 5 additions & 0 deletions docx-core/src/reader/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ impl ElementReader for Table {
t = t.width(w as usize, width_type);
continue;
}
XMLElement::TableProperty => {
if let Ok(p) = TableProperty::read(r, &attributes) {
t.property = p;
}
}
XMLElement::Justification => {
t = t.align(TableAlignmentType::from_str(&attributes[0].value)?);
}
Expand Down
55 changes: 55 additions & 0 deletions docx-core/src/reader/table_position_property.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::io::Read;
use std::str::FromStr;

use xml::attribute::OwnedAttribute;
use xml::reader::EventReader;

use super::*;

impl ElementReader for TablePositionProperty {
fn read<R: Read>(
_r: &mut EventReader<R>,
attrs: &[OwnedAttribute],
) -> Result<Self, ReaderError> {
let mut property = TablePositionProperty::new();
for a in attrs {
let local_name = &a.name.local_name;
match local_name.as_str() {
"leftFromText" => {
if let Ok(v) = i32::from_str(&a.value) {
property = property.left_from_text(v);
}
}
"rightFromText" => {
if let Ok(v) = i32::from_str(&a.value) {
property = property.right_from_text(v);
}
}
"vertAnchor" => {
property = property.vertical_anchor(a.value.clone());
}
"horzAnchor" => {
property = property.horizontal_anchor(a.value.clone());
}
"tblpXSpec" => {
property = property.position_x_alignment(a.value.clone());
}
"tblpYSpec" => {
property = property.position_y_alignment(a.value.clone());
}
"tblpX" => {
if let Ok(v) = i32::from_str(&a.value) {
property = property.position_x(v);
}
}
"tblpY" => {
if let Ok(v) = i32::from_str(&a.value) {
property = property.position_y(v);
}
}
_ => {}
}
}
Ok(property)
}
}
30 changes: 30 additions & 0 deletions docx-core/src/reader/table_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ use std::str::FromStr;
use xml::attribute::OwnedAttribute;
use xml::reader::{EventReader, XmlEvent};

use crate::TableAlignmentType;

use super::*;

// TODO: layout: Option<TableLayout>,
impl ElementReader for TableProperty {
fn read<R: Read>(
r: &mut EventReader<R>,
Expand All @@ -31,6 +34,33 @@ impl ElementReader for TableProperty {
tp = tp.set_margins(margins);
}
}
XMLElement::TableWidth => {
if let Ok((w, width_type)) = read_width(&attributes) {
tp = tp.width(w as usize, width_type);
}
}
XMLElement::Justification => {
if let Ok(v) = TableAlignmentType::from_str(&attributes[0].value) {
tp = tp.align(v);
}
}
XMLElement::TableIndent => {
if let Ok((w, _)) = read_width(&attributes) {
if w != 0 {
tp = tp.indent(w as i32);
}
}
}
XMLElement::TableStyle => {
if let Some(s) = read_val(&attributes) {
tp = tp.style(s);
}
}
XMLElement::TablePositionProperty => {
if let Ok(p) = TablePositionProperty::read(r, &attributes) {
tp = tp.position(p);
}
}
_ => {}
}
}
Expand Down
2 changes: 2 additions & 0 deletions docx-core/src/reader/xml_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub enum XMLElement {
TableIndent,
TableBorders,
TableCellMargin,
TablePositionProperty,
TableStyle,
// Change
TableGridChange,
Expand Down Expand Up @@ -327,6 +328,7 @@ impl FromStr for XMLElement {
"tblBorders" => Ok(XMLElement::TableBorders),
"tblCellMar" => Ok(XMLElement::TableCellMargin),
"tblStyle" => Ok(XMLElement::TableStyle),
"tblpPr" => Ok(XMLElement::TablePositionProperty),
"top" => Ok(XMLElement::Top),
"right" => Ok(XMLElement::Right),
"start" => Ok(XMLElement::Start),
Expand Down
48 changes: 48 additions & 0 deletions docx-core/src/xml_builder/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::XMLBuilder;
use super::XmlEvent;
use crate::types::*;
use crate::FrameProperty;
use crate::TablePositionProperty;

const EXPECT_MESSAGE: &str = "should write buf";

Expand Down Expand Up @@ -630,6 +631,53 @@ impl XMLBuilder {
self.close()
}

pub(crate) fn table_position_property(mut self, prop: &TablePositionProperty) -> Self {
let mut w = XmlEvent::start_element("w:tblpPr");

let v: String = format!("{}", prop.left_from_text.unwrap_or_default());
if prop.left_from_text.is_some() {
w = w.attr("w:leftFromText", &v);
}

let v: String = format!("{}", prop.right_from_text.unwrap_or_default());
if prop.right_from_text.is_some() {
w = w.attr("w:rightFromText", &v);
}

let v: String = prop.vertical_anchor.iter().cloned().collect();
if prop.vertical_anchor.is_some() {
w = w.attr("w:vertAnchor", &v);
}

let v: String = prop.horizontal_anchor.iter().cloned().collect();
if prop.horizontal_anchor.is_some() {
w = w.attr("w:horzAnchor", &v);
}

let v: String = prop.position_x_alignment.iter().cloned().collect();
if prop.position_x_alignment.is_some() {
w = w.attr("w:tblpXSpec", &v);
}

let v: String = prop.position_y_alignment.iter().cloned().collect();
if prop.position_y_alignment.is_some() {
w = w.attr("w:tblpYSpec", &v);
}

let v: String = format!("{}", prop.position_x.unwrap_or_default());
if prop.position_x.is_some() {
w = w.attr("w:tblpX", &v);
}

let v: String = format!("{}", prop.position_y.unwrap_or_default());
if prop.position_y.is_some() {
w = w.attr("w:tblpY", &v);
}

self.writer.write(w).expect(EXPECT_MESSAGE);
self.close()
}

pub(crate) fn page_num_type(mut self, start: Option<u32>, chap_style: Option<String>) -> Self {
let mut w = XmlEvent::start_element("w:pgNumType");
let start_string = format!("{}", start.unwrap_or_default());
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docx-core/tests/snapshots/reader__read_insert_table.snap

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docx-wasm/js/json/bindings/TablePositionProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export interface TablePositionProperty { leftFromText?: number, rightFromText?: number, verticalAnchor?: string, horizontalAnchor?: string, positionXAlignment?: string, positionYAlignment?: string, positionX?: number, positionY?: number, }
4 changes: 4 additions & 0 deletions docx-wasm/js/json/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { TableLayoutType } from "../table";
import { DeleteJSONData, InsertJSONData, TableCellBordersJSON } from "..";
import { StructuredTagJSON } from "./structured-data-tag";

import { TablePositionProperty as TablePositionPropertyJSON } from "./bindings/TablePositionProperty";

export { TablePositionProperty as TablePositionPropertyJSON } from "./bindings/TablePositionProperty";
export { TableCellBorder as TableCellBorderJSON } from "./bindings/TableCellBorder";

export { TableCellBorders as TableCellBordersJSON } from "./bindings/TableCellBorders";
Expand Down Expand Up @@ -97,6 +100,7 @@ export type TablePropertyJSON = {
} | null;
style?: string | null;
layout?: TableLayoutType | null;
position?: TablePositionPropertyJSON;
};

export type TableJSON = {
Expand Down
Loading

0 comments on commit 666c950

Please sign in to comment.