From 60d94921b0dbda39b74d045f756b65af40c356f7 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Tue, 15 Oct 2024 18:30:05 +0900 Subject: [PATCH] Support ptab in r (#756) * feat: add ptab * feat: wasm object * fix: suppoert ptab * spec: add read test for ptab * rc10 * rc11 * rc12 --- .../src/documents/elements/positional_tab.rs | 11 + docx-core/src/documents/elements/run.rs | 13 + docx-core/src/reader/mod.rs | 3 +- docx-core/src/reader/positional_tab.rs | 81 +++++ docx-core/src/reader/run.rs | 5 + docx-core/src/reader/run_property.rs | 5 + docx-core/src/reader/xml_element.rs | 2 + .../src/types/positional_tab_relative_to.rs | 1 + docx-wasm/js/index.ts | 1 + docx-wasm/js/json/bindings/PositionalTab.ts | 2 +- .../json/bindings/PositionalTabRelativeTo.ts | 2 +- docx-wasm/js/json/run.ts | 13 + docx-wasm/js/positional-tab.ts | 50 +++ docx-wasm/js/run.ts | 10 +- docx-wasm/js/tab-leader.ts | 22 ++ docx-wasm/package.json | 2 +- docx-wasm/src/lib.rs | 2 + docx-wasm/src/positional_tab.rs | 38 +++ docx-wasm/src/run.rs | 5 + .../test/__snapshots__/index.test.js.snap | 288 ++++++++++++++++++ docx-wasm/test/index.test.js | 22 ++ fixtures/ptab/ptab.docx | Bin 0 -> 18350 bytes 22 files changed, 573 insertions(+), 5 deletions(-) create mode 100644 docx-core/src/reader/positional_tab.rs create mode 100644 docx-wasm/js/positional-tab.ts create mode 100644 docx-wasm/js/tab-leader.ts create mode 100644 docx-wasm/src/positional_tab.rs create mode 100644 fixtures/ptab/ptab.docx diff --git a/docx-core/src/documents/elements/positional_tab.rs b/docx-core/src/documents/elements/positional_tab.rs index cd08957..91fb181 100644 --- a/docx-core/src/documents/elements/positional_tab.rs +++ b/docx-core/src/documents/elements/positional_tab.rs @@ -7,12 +7,23 @@ 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, diff --git a/docx-core/src/documents/elements/run.rs b/docx-core/src/documents/elements/run.rs index 786ceea..2291135 100644 --- a/docx-core/src/documents/elements/run.rs +++ b/docx-core/src/documents/elements/run.rs @@ -29,6 +29,7 @@ pub enum RunChild { Sym(Sym), DeleteText(DeleteText), Tab(Tab), + PTab(PositionalTab), Break(Break), Drawing(Box), Shape(Box), @@ -72,6 +73,12 @@ impl Serialize for RunChild { 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")?; @@ -201,6 +208,11 @@ impl Run { 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)))); @@ -326,6 +338,7 @@ impl BuildXML for Run { RunChild::Sym(t) => b = b.add_child(t), RunChild::DeleteText(t) => b = b.add_child(t), RunChild::Tab(t) => b = b.add_child(t), + RunChild::PTab(t) => b = b.add_child(t), RunChild::Break(t) => b = b.add_child(t), RunChild::Drawing(t) => b = b.add_child(t), RunChild::Shape(_t) => { diff --git a/docx-core/src/reader/mod.rs b/docx-core/src/reader/mod.rs index 495fe54..0d544ff 100644 --- a/docx-core/src/reader/mod.rs +++ b/docx-core/src/reader/mod.rs @@ -37,6 +37,7 @@ mod paragraph; mod paragraph_property; mod paragraph_property_change; mod pic; +mod positional_tab; mod read_zip; mod rels; mod run; @@ -55,8 +56,8 @@ mod table_cell; mod table_cell_borders; mod table_cell_margins; mod table_cell_property; -mod table_property; mod table_position_property; +mod table_property; mod table_row; mod tabs; mod text_box_content; diff --git a/docx-core/src/reader/positional_tab.rs b/docx-core/src/reader/positional_tab.rs new file mode 100644 index 0000000..8e7363e --- /dev/null +++ b/docx-core/src/reader/positional_tab.rs @@ -0,0 +1,81 @@ +use std::io::Read; +use std::str::FromStr; + +use xml::attribute::OwnedAttribute; +use xml::reader::{EventReader, XmlEvent}; + +use crate::{PositionalTabAlignmentType, PositionalTabRelativeTo, TabLeaderType}; + +use super::*; + +fn read_leader(attributes: &[OwnedAttribute]) -> Result { + for a in attributes { + let local_name = &a.name.local_name; + if local_name == "leader" { + let v = a.value.to_owned(); + if let Ok(t) = TabLeaderType::from_str(&v) { + return Ok(t); + } + } + } + Err(ReaderError::TypeError(crate::TypeError::FromStrError)) +} + +fn read_alignment( + attributes: &[OwnedAttribute], +) -> Result { + for a in attributes { + let local_name = &a.name.local_name; + if local_name == "alignment" { + let v = a.value.to_owned(); + if let Ok(t) = PositionalTabAlignmentType::from_str(&v) { + return Ok(t); + } + } + } + Err(ReaderError::TypeError(crate::TypeError::FromStrError)) +} + +fn read_relative_to(attributes: &[OwnedAttribute]) -> Result { + for a in attributes { + let local_name = &a.name.local_name; + if local_name == "alignment" { + let v = a.value.to_owned(); + if let Ok(t) = PositionalTabRelativeTo::from_str(&v) { + return Ok(t); + } + } + } + Err(ReaderError::TypeError(crate::TypeError::FromStrError)) +} + +impl ElementReader for PositionalTab { + fn read( + r: &mut EventReader, + attrs: &[OwnedAttribute], + ) -> Result { + let mut tab = PositionalTab::default(); + if let Ok(t) = read_alignment(attrs) { + tab = tab.alignment(t); + } + if let Ok(v) = read_relative_to(attrs) { + tab = tab.relative_to(v); + } + if let Ok(leader) = read_leader(attrs) { + tab = tab.leader(leader); + } + loop { + let e = r.next(); + match e { + Ok(XmlEvent::EndElement { name, .. }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + if e == XMLElement::PTab { + return Ok(tab); + } + } + Err(_) => return Err(ReaderError::XMLReadError), + _ => {} + } + } + } +} diff --git a/docx-core/src/reader/run.rs b/docx-core/src/reader/run.rs index e4f5213..4970b59 100644 --- a/docx-core/src/reader/run.rs +++ b/docx-core/src/reader/run.rs @@ -71,6 +71,11 @@ impl ElementReader for Run { XMLElement::Tab => { run = run.add_tab(); } + XMLElement::PTab => { + if let Ok(v) = PositionalTab::read(r, &attributes) { + run = run.add_ptab(v); + } + } XMLElement::Sym => { if let Some(font) = read(&attributes, "font") { if let Some(char) = read(&attributes, "char") { diff --git a/docx-core/src/reader/run_property.rs b/docx-core/src/reader/run_property.rs index 5f21ef4..34ad090 100644 --- a/docx-core/src/reader/run_property.rs +++ b/docx-core/src/reader/run_property.rs @@ -79,6 +79,11 @@ impl ElementReader for RunProperty { } rp = rp.caps(); } + XMLElement::PTab => { + if let Ok(v) = PositionalTab::read(r, &attributes) { + rp = rp.ptab(v) + } + } XMLElement::Highlight => rp = rp.highlight(attributes[0].value.clone()), XMLElement::Strike => { if !read_bool(&attributes) { diff --git a/docx-core/src/reader/xml_element.rs b/docx-core/src/reader/xml_element.rs index ddf100a..4a965e0 100644 --- a/docx-core/src/reader/xml_element.rs +++ b/docx-core/src/reader/xml_element.rs @@ -37,6 +37,7 @@ pub enum XMLElement { Break, Tab, Tabs, + PTab, Sym, ParagraphStyle, ParagraphPropertyChange, @@ -289,6 +290,7 @@ impl FromStr for XMLElement { "name" => Ok(XMLElement::Name), "tab" => Ok(XMLElement::Tab), "tabs" => Ok(XMLElement::Tabs), + "ptab" => Ok(XMLElement::PTab), "br" => Ok(XMLElement::Break), "ind" => Ok(XMLElement::Indent), "numPr" => Ok(XMLElement::NumberingProperty), diff --git a/docx-core/src/types/positional_tab_relative_to.rs b/docx-core/src/types/positional_tab_relative_to.rs index 862139e..c7283f8 100644 --- a/docx-core/src/types/positional_tab_relative_to.rs +++ b/docx-core/src/types/positional_tab_relative_to.rs @@ -11,6 +11,7 @@ use super::errors; #[cfg_attr(feature = "wasm", derive(ts_rs::TS))] #[cfg_attr(feature = "wasm", ts(export))] #[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub enum PositionalTabRelativeTo { Indent, Margin, diff --git a/docx-wasm/js/index.ts b/docx-wasm/js/index.ts index 0192ddf..7b0caec 100644 --- a/docx-wasm/js/index.ts +++ b/docx-wasm/js/index.ts @@ -672,3 +672,4 @@ export * from "./page-num"; export * from "./num-pages"; export * from "./footer"; export * from "./image"; +export * from "./positional-tab"; diff --git a/docx-wasm/js/json/bindings/PositionalTab.ts b/docx-wasm/js/json/bindings/PositionalTab.ts index 5327ded..891c291 100644 --- a/docx-wasm/js/json/bindings/PositionalTab.ts +++ b/docx-wasm/js/json/bindings/PositionalTab.ts @@ -2,4 +2,4 @@ import type { PositionalTabAlignmentType } from "./PositionalTabAlignmentType"; import type { PositionalTabRelativeTo } from "./PositionalTabRelativeTo"; import type { TabLeaderType } from "./TabLeaderType"; -export interface PositionalTab { alignment: PositionalTabAlignmentType, relative_to: PositionalTabRelativeTo, leader: TabLeaderType, } \ No newline at end of file +export interface PositionalTab { alignment: PositionalTabAlignmentType, relativeTo: PositionalTabRelativeTo, leader: TabLeaderType, } \ No newline at end of file diff --git a/docx-wasm/js/json/bindings/PositionalTabRelativeTo.ts b/docx-wasm/js/json/bindings/PositionalTabRelativeTo.ts index 7de1562..9a7ba21 100644 --- a/docx-wasm/js/json/bindings/PositionalTabRelativeTo.ts +++ b/docx-wasm/js/json/bindings/PositionalTabRelativeTo.ts @@ -1,2 +1,2 @@ -export type PositionalTabRelativeTo = "Indent" | "Margin"; \ No newline at end of file +export type PositionalTabRelativeTo = "indent" | "margin"; \ No newline at end of file diff --git a/docx-wasm/js/json/run.ts b/docx-wasm/js/json/run.ts index f0566bd..0d5a573 100644 --- a/docx-wasm/js/json/run.ts +++ b/docx-wasm/js/json/run.ts @@ -11,6 +11,9 @@ import { VertAlignType } from "../run"; import { FieldChar } from "./bindings/FieldChar"; import { InstrHyperlink } from "./bindings/InstrHyperlink"; import { InstrToC } from "./bindings/InstrToC"; +import { PositionalTabAlignmentType } from "./bindings/PositionalTabAlignmentType"; +import { PositionalTabRelativeTo } from "./bindings/PositionalTabRelativeTo"; +import { TabLeaderType } from "./bindings/TabLeaderType"; export type TextBorderJSON = { borderType: BorderType; @@ -61,6 +64,7 @@ export type RunChildJSON = | TabJSON | BreakJSON | DrawingJSON + | PtabJSON | ShapeJSON | CommentRangeStartJSON | CommentRangeEndJSON @@ -75,6 +79,15 @@ export type TextJSON = { }; }; +export type PtabJSON = { + type: "ptab"; + data: { + alignment: PositionalTabAlignmentType; + relativeTo: PositionalTabRelativeTo; + leader: TabLeaderType; + }; +}; + export type SymJSON = { type: "sym"; data: { diff --git a/docx-wasm/js/positional-tab.ts b/docx-wasm/js/positional-tab.ts new file mode 100644 index 0000000..b388d77 --- /dev/null +++ b/docx-wasm/js/positional-tab.ts @@ -0,0 +1,50 @@ +import { PositionalTabAlignmentType } from "./json/bindings/PositionalTabAlignmentType"; +import { PositionalTabRelativeTo } from "./json/bindings/PositionalTabRelativeTo"; +import { TabLeaderType } from "./json/bindings/TabLeaderType"; +import { + createPositionalTab, + PositionalTabAlignmentType as _PositionalTabAlignmentType, + PositionalTabRelativeTo as _PositionalTabRelativeTo, + TabLeaderType as _TabLeaderType, +} from "./pkg/docx_wasm"; +import { convertTabLeader } from "./tab-leader"; + +export class PositionalTab { + _alignment: PositionalTabAlignmentType = "left"; + _relativeTo: PositionalTabRelativeTo = "margin"; + _leader: TabLeaderType = "none"; + + alignment(t: PositionalTabAlignmentType) { + this._alignment = t; + return this; + } + + relativeTo(t: PositionalTabRelativeTo) { + this._relativeTo = t; + return this; + } + + leader(l: TabLeaderType) { + this._leader = l; + return this; + } + + buildWasmObject() { + const alignment = (() => { + if (this._alignment === "left") return _PositionalTabAlignmentType.Left; + if (this._alignment === "center") + return _PositionalTabAlignmentType.Center; + if (this._alignment === "right") return _PositionalTabAlignmentType.Right; + return _PositionalTabAlignmentType.Left; + })(); + + const relativeTo = (() => { + if (this._relativeTo === "indent") return _PositionalTabRelativeTo.Indent; + return _PositionalTabRelativeTo.Margin; + })(); + + const leader = convertTabLeader(this._leader); + + return createPositionalTab(alignment, relativeTo, leader); + } +} diff --git a/docx-wasm/js/run.ts b/docx-wasm/js/run.ts index 03e857b..a279a11 100644 --- a/docx-wasm/js/run.ts +++ b/docx-wasm/js/run.ts @@ -6,8 +6,9 @@ import { Tab } from "./tab"; import { Break, BreakType } from "./break"; import { BorderType } from "./border"; import { Image } from "./image"; +import { PositionalTab } from "./positional-tab"; -export type RunChild = Text | DeleteText | Tab | Break | Image; +export type RunChild = Text | DeleteText | Tab | Break | Image | PositionalTab; export type TextBorder = { borderType: BorderType; @@ -193,6 +194,11 @@ export class Run { return this; } + addPositionalTab(ptab: PositionalTab) { + this.children.push(ptab); + return this; + } + addBreak(type: BreakType) { this.children.push(new Break(type)); return this; @@ -290,6 +296,8 @@ export class Run { run = run.add_delete_text(child.text); } else if (child instanceof Tab) { run = run.add_tab(); + } else if (child instanceof PositionalTab) { + run = run.add_ptab(child.buildWasmObject()); } else if (child instanceof Break) { if (child.type === "column") { run = run.add_break(wasm.BreakType.Column); diff --git a/docx-wasm/js/tab-leader.ts b/docx-wasm/js/tab-leader.ts new file mode 100644 index 0000000..0f787cc --- /dev/null +++ b/docx-wasm/js/tab-leader.ts @@ -0,0 +1,22 @@ +import * as wasm from "./pkg"; +import { TabLeaderType } from "./json/bindings/TabLeaderType"; + +export const convertTabLeader = (leader: TabLeaderType) => { + switch (leader) { + case "dot": + return wasm.TabLeaderType.Dot; + break; + case "heavy": + return wasm.TabLeaderType.Heavy; + case "hyphen": + return wasm.TabLeaderType.Hyphen; + case "middleDot": + return wasm.TabLeaderType.MiddleDot; + case "none": + return wasm.TabLeaderType.None; + case "underscore": + return wasm.TabLeaderType.Underscore; + default: + return wasm.TabLeaderType.None; + } +}; diff --git a/docx-wasm/package.json b/docx-wasm/package.json index 53ca0d5..d7f3f09 100644 --- a/docx-wasm/package.json +++ b/docx-wasm/package.json @@ -1,6 +1,6 @@ { "name": "docx-wasm", - "version": "0.4.18-rc9", + "version": "0.4.18-rc12", "main": "dist/node/index.js", "browser": "dist/web/index.js", "author": "bokuweb ", diff --git a/docx-wasm/src/lib.rs b/docx-wasm/src/lib.rs index 1660a7e..8457e74 100644 --- a/docx-wasm/src/lib.rs +++ b/docx-wasm/src/lib.rs @@ -17,6 +17,7 @@ mod page_num; mod page_num_type; mod paragraph; mod pic; +mod positional_tab; mod reader; mod run; mod run_fonts; @@ -49,6 +50,7 @@ pub use page_num::*; pub use page_num_type::*; pub use paragraph::*; pub use pic::*; +pub use positional_tab::*; pub use reader::*; pub use run::*; pub use run_fonts::*; diff --git a/docx-wasm/src/positional_tab.rs b/docx-wasm/src/positional_tab.rs new file mode 100644 index 0000000..43972e4 --- /dev/null +++ b/docx-wasm/src/positional_tab.rs @@ -0,0 +1,38 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Debug)] +pub struct PositionalTab(docx_rs::PositionalTab); + +#[wasm_bindgen(js_name = createPositionalTab)] +pub fn create_positional_tab( + alignment: docx_rs::PositionalTabAlignmentType, + relative_to: docx_rs::PositionalTabRelativeTo, + leader: docx_rs::TabLeaderType, +) -> PositionalTab { + PositionalTab(docx_rs::PositionalTab::new(alignment, relative_to, leader)) +} + +#[wasm_bindgen] +impl PositionalTab { + pub fn alignment(mut self, alignment: docx_rs::PositionalTabAlignmentType) -> Self { + self.0 = self.0.alignment(alignment); + self + } + + pub fn relative_to(mut self, relative_to: docx_rs::PositionalTabRelativeTo) -> Self { + self.0 = self.0.relative_to(relative_to); + self + } + + pub fn leader(mut self, leader: docx_rs::TabLeaderType) -> Self { + self.0 = self.0.leader(leader); + self + } +} + +impl PositionalTab { + pub fn take(self) -> docx_rs::PositionalTab { + self.0 + } +} diff --git a/docx-wasm/src/run.rs b/docx-wasm/src/run.rs index bc6b3da..2aadfb4 100644 --- a/docx-wasm/src/run.rs +++ b/docx-wasm/src/run.rs @@ -39,6 +39,11 @@ impl Run { self } + pub fn add_ptab(mut self, ptab: PositionalTab) -> Run { + self.0 = self.0.add_ptab(ptab.take()); + self + } + pub fn add_break(mut self, break_type: docx_rs::BreakType) -> Run { self.0 .children diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap index 34ac6ce..86edb8a 100644 --- a/docx-wasm/test/__snapshots__/index.test.js.snap +++ b/docx-wasm/test/__snapshots__/index.test.js.snap @@ -41792,6 +41792,269 @@ Object { } `; +exports[`reader should read ptab 1`] = ` +Object { + "comments": Object { + "comments": Array [], + }, + "commentsExtended": Object { + "children": Array [], + }, + "contentType": Object { + "custom_xml_count": 1, + "footer_count": 0, + "header_count": 0, + "types": Object { + "/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml", + "/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml", + "/docProps/core.xml": "application/vnd.openxmlformats-package.core-properties+xml", + "/docProps/custom.xml": "application/vnd.openxmlformats-officedocument.custom-properties+xml", + "/word/_rels/document.xml.rels": "application/vnd.openxmlformats-package.relationships+xml", + "/word/comments.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", + "/word/commentsExtended.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml", + "/word/document.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", + "/word/fontTable.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", + "/word/numbering.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", + "/word/settings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", + "/word/styles.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", + }, + "web_extension_count": 1, + }, + "customItemProps": Array [], + "customItemRels": Array [], + "customItems": Array [], + "docProps": Object { + "app": Object {}, + "core": Object { + "config": Object { + "created": null, + "creator": null, + "description": null, + "language": null, + "lastModifiedBy": null, + "modified": null, + "revision": null, + "subject": null, + "title": null, + }, + }, + "custom": Object { + "properties": Object {}, + }, + }, + "document": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "children": Array [ + Object { + "data": Object { + "alignment": "right", + "leader": "none", + "relativeTo": "margin", + }, + "type": "ptab", + }, + Object { + "data": Object { + "preserveSpace": true, + "text": "Hello world!!", + }, + "type": "text", + }, + ], + "runProperty": Object {}, + }, + "type": "run", + }, + ], + "hasNumbering": false, + "id": "00000001", + "property": Object { + "runProperty": Object {}, + "tabs": Array [], + }, + }, + "type": "paragraph", + }, + ], + "hasNumbering": false, + "sectionProperty": Object { + "columns": 1, + "pageMargin": Object { + "bottom": 1701, + "footer": 992, + "gutter": 0, + "header": 851, + "left": 1701, + "right": 1701, + "top": 1985, + }, + "pageSize": Object { + "h": 16838, + "orient": null, + "w": 11906, + }, + "space": 425, + "textDirection": "lrTb", + "titlePg": false, + }, + }, + "documentRels": Object { + "customXmlCount": 0, + "footerCount": 0, + "hasComments": false, + "hasFootnotes": false, + "hasNumberings": false, + "headerCount": 0, + "hyperlinks": Array [], + "images": Array [], + }, + "fontTable": Object {}, + "footnotes": Object { + "footnotes": Array [], + }, + "hyperlinks": Array [], + "images": Array [], + "media": Array [], + "numberings": Object { + "abstractNums": Array [], + "numberings": Array [], + }, + "rels": Object { + "rels": Array [ + Array [ + "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", + "rId1", + "docProps/core.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", + "rId2", + "docProps/app.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "rId3", + "word/document.xml", + ], + Array [ + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties", + "rId4", + "docProps/custom.xml", + ], + ], + }, + "settings": Object { + "adjustLineHeightInTable": false, + "defaultTabStop": 840, + "docId": null, + "docVars": Array [], + "evenAndOddHeaders": false, + "zoom": 100, + }, + "styles": Object { + "docDefaults": Object { + "paragraphPropertyDefault": Object { + "paragraphProperty": Object { + "runProperty": Object {}, + "tabs": Array [], + }, + }, + "runPropertyDefault": Object { + "runProperty": Object {}, + }, + }, + "styles": Array [ + Object { + "basedOn": null, + "name": "Normal", + "next": null, + "paragraphProperty": Object { + "runProperty": Object {}, + "tabs": Array [], + }, + "runProperty": Object {}, + "styleId": "Normal", + "styleType": "paragraph", + "tableCellProperty": Object { + "borders": null, + "gridSpan": null, + "shading": null, + "textDirection": null, + "verticalAlign": null, + "verticalMerge": null, + "width": null, + }, + "tableProperty": Object { + "borders": Object { + "bottom": Object { + "borderType": "single", + "color": "000000", + "position": "bottom", + "size": 2, + "space": 0, + }, + "insideH": Object { + "borderType": "single", + "color": "000000", + "position": "insideH", + "size": 2, + "space": 0, + }, + "insideV": Object { + "borderType": "single", + "color": "000000", + "position": "insideV", + "size": 2, + "space": 0, + }, + "left": Object { + "borderType": "single", + "color": "000000", + "position": "left", + "size": 2, + "space": 0, + }, + "right": Object { + "borderType": "single", + "color": "000000", + "position": "right", + "size": 2, + "space": 0, + }, + "top": Object { + "borderType": "single", + "color": "000000", + "position": "top", + "size": 2, + "space": 0, + }, + }, + "justification": "left", + "width": Object { + "width": 0, + "widthType": "auto", + }, + }, + }, + ], + }, + "taskpanes": null, + "taskpanesRels": Object { + "rels": Array [], + }, + "themes": Array [], + "webExtensions": Array [], + "webSettings": Object { + "divs": Array [], + }, +} +`; + exports[`reader should read sectionProperty in ppr 1`] = ` Object { "comments": Object { @@ -171837,6 +172100,31 @@ exports[`writer should write paragraph delete 3`] = ` " `; +exports[`writer should write ptab 1`] = ` +" + + + + + +" +`; + +exports[`writer should write ptab 2`] = ` +" + + Hello world!! + +" +`; + +exports[`writer should write ptab 3`] = ` +" + + +" +`; + exports[`writer should write strike 1`] = ` " diff --git a/docx-wasm/test/index.test.js b/docx-wasm/test/index.test.js index cc3cc5d..bf49fd0 100644 --- a/docx-wasm/test/index.test.js +++ b/docx-wasm/test/index.test.js @@ -214,6 +214,12 @@ describe("reader", () => { const json = w.readDocx(buffer); expect(json).toMatchSnapshot(); }); + + test("should read ptab", () => { + const buffer = readFileSync("../fixtures/ptab/ptab.docx"); + const json = w.readDocx(buffer); + expect(json).toMatchSnapshot(); + }); }); describe("writer", () => { @@ -1063,4 +1069,20 @@ describe("writer", () => { } } }); + + test("should write ptab", () => { + const p = new w.Paragraph().addRun( + new w.Run() + .addPositionalTab(new w.PositionalTab().alignment("right")) + .addText("Hello world!!") + ); + const buffer = new w.Docx().addParagraph(p).build(); + const z = new Zip(Buffer.from(buffer)); + for (const e of z.getEntries()) { + if (e.entryName.match(/document.xml|numbering.xml/)) { + expect(z.readAsText(e)).toMatchSnapshot(); + } + } + writeFileSync("../output/js/ptab.docx", buffer); + }); }); diff --git a/fixtures/ptab/ptab.docx b/fixtures/ptab/ptab.docx new file mode 100644 index 0000000000000000000000000000000000000000..0bc012fa6c6717b202b0366ca5082230cb40172b GIT binary patch literal 18350 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 x=}GWvZaRY>HI(M_nQFBcYPgu8d1*5pg