fix: read sdt in cell (#601)

* fix: read sdt in cell

* fix

* fix: add without_sdt

* fix

* fix

* fix

* fix
main
bokuweb 2023-03-16 18:48:24 +09:00 committed by GitHub
parent 11c1618be8
commit ccc6eda27d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 518 additions and 84 deletions

View File

@ -0,0 +1,38 @@
use 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(())
}

View File

@ -147,10 +147,8 @@ impl StructuredDataTag {
self.property = self.property.alias(v);
self
}
}
impl BuildXML for StructuredDataTag {
fn build(&self) -> Vec<u8> {
fn inner_build(&self) -> Vec<u8> {
XMLBuilder::new()
.open_structured_tag()
.add_child(&self.property)
@ -162,6 +160,18 @@ impl BuildXML for StructuredDataTag {
}
}
impl BuildXML for StructuredDataTag {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
impl BuildXML for Box<StructuredDataTag> {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
#[cfg(test)]
mod tests {

View File

@ -18,6 +18,8 @@ pub struct TableCell {
pub enum TableCellContent {
Paragraph(Paragraph),
Table(Table),
StructuredDataTag(Box<StructuredDataTag>),
TableOfContents(Box<TableOfContents>),
}
impl Serialize for TableCellContent {
@ -38,6 +40,18 @@ impl Serialize for TableCellContent {
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()
}
}
}
}
@ -55,6 +69,18 @@ impl TableCell {
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
@ -140,6 +166,8 @@ impl BuildXML for TableCell {
b = b.add_child(&Paragraph::new())
}
}
TableCellContent::StructuredDataTag(t) => b = b.add_child(t),
TableCellContent::TableOfContents(t) => b = b.add_child(t),
}
}
// INFO: We need to add empty paragraph when parent cell includes only cell.

View File

@ -39,8 +39,8 @@ pub struct TableOfContentsReviewData {
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
/// 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,
@ -48,6 +48,8 @@ pub struct TableOfContents {
// don't use
pub auto: bool,
pub dirty: bool,
/// Skip StructuredDataTag rendering
pub without_sdt: bool,
pub alias: Option<String>,
pub page_ref_placeholder: Option<String>,
// it is inserted in before toc.
@ -141,19 +143,26 @@ impl TableOfContents {
self.after_contents.push(TocContent::Table(Box::new(t)));
self
}
pub fn without_sdt(mut self) -> Self {
self.without_sdt = true;
self
}
impl BuildXML for TableOfContents {
fn build(&self) -> Vec<u8> {
fn inner_build(&self) -> Vec<u8> {
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::new()
let mut b = XMLBuilder::new();
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 {
@ -207,13 +216,22 @@ impl BuildXML for TableOfContents {
}
}
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);
}
}
}
}
b.close().close().build()
if !self.without_sdt {
b = b.close().close();
}
b.build()
} else {
let items: Vec<TableOfContentsItem> = self
.items
@ -229,10 +247,14 @@ impl BuildXML for TableOfContents {
})
.collect();
let mut b = XMLBuilder::new()
let mut b = XMLBuilder::new();
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 {
@ -247,19 +269,39 @@ impl BuildXML for TableOfContents {
b = b.add_child(&items);
for c in self.after_contents.iter() {
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);
}
}
}
b.close().close().build()
if !self.without_sdt {
b = b.close().close();
}
b.build()
}
}
}
impl BuildXML for TableOfContents {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
impl BuildXML for Box<TableOfContents> {
fn build(&self) -> Vec<u8> {
self.inner_build()
}
}
@ -281,6 +323,18 @@ mod tests {
);
}
#[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#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="begin" w:dirty="true" /><w:instrText>TOC \o &quot;1-3&quot;</w:instrText><w:fldChar w:fldCharType="separate" w:dirty="false" /></w:r></w:p><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="end" w:dirty="false" /></w:r></w:p>"#
);
}
/*
#[test]
fn test_toc_with_items() {

View File

@ -692,6 +692,85 @@ impl Docx {
&mut hyperlink_map,
);
}
DocumentChild::TableOfContents(toc) => {
// TODO:refine later
for child in &toc.before_contents {
if let TocContent::Paragraph(paragraph) = child {
for child in &paragraph.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 &paragraph.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,
);
}
}
}
_ => {}
}
}
@ -794,6 +873,61 @@ impl Docx {
&mut image_bufs,
);
}
TableCellContent::StructuredDataTag(tag) => {
for child in &mut tag.children {
if let StructuredDataTagChild::Paragraph(paragraph) =
child
{
collect_images_from_paragraph(
paragraph,
&mut images,
&mut image_bufs,
);
}
if let StructuredDataTagChild::Table(table) = child {
collect_images_from_table(
table,
&mut images,
&mut image_bufs,
);
}
}
}
TableCellContent::TableOfContents(t) => {
for child in &mut t.before_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(
paragraph,
&mut images,
&mut image_bufs,
);
}
if let TocContent::Table(table) = child {
collect_images_from_table(
table,
&mut images,
&mut image_bufs,
);
}
}
for child in &mut t.after_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(
paragraph,
&mut images,
&mut image_bufs,
);
}
if let TocContent::Table(table) = child {
collect_images_from_table(
table,
&mut images,
&mut image_bufs,
);
}
}
}
}
}
}
@ -806,6 +940,30 @@ impl Docx {
}
}
fn collect_dependencies_in_paragraph(
paragraph: &Paragraph,
comments: &mut Vec<Comment>,
comments_extended: &mut Vec<CommentExtended>,
comment_map: &mut HashMap<usize, String>,
hyperlink_map: &mut HashMap<String, String>,
) {
for child in &paragraph.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<Comment>,
@ -818,32 +976,14 @@ fn collect_dependencies_in_table(
for content in &cell.children {
match content {
TableCellContent::Paragraph(paragraph) => {
for child in &paragraph.children {
if let ParagraphChild::CommentStart(c) = child {
push_comment_and_comment_extended(
collect_dependencies_in_paragraph(
paragraph,
comments,
comments_extended,
comment_map,
c,
hyperlink_map,
);
}
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,
);
}
}
}
}
}
TableCellContent::Table(table) => collect_dependencies_in_table(
table,
comments,
@ -851,6 +991,105 @@ fn collect_dependencies_in_table(
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);
}
}
}
}
@ -863,47 +1102,40 @@ fn store_comments_in_table(table: &mut Table, comments: &[Comment]) {
for content in &mut cell.children {
match content {
TableCellContent::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);
}
}
}
}
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);
}
}
}
}
}
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);
}
}
}
}
}
}
@ -1039,6 +1271,35 @@ fn collect_images_from_table(
TableCellContent::Table(table) => {
collect_images_from_table(table, images, image_bufs)
}
TableCellContent::StructuredDataTag(tag) => {
for child in &mut tag.children {
if let StructuredDataTagChild::Paragraph(paragraph) = child {
collect_images_from_paragraph(paragraph, images, image_bufs);
}
if let StructuredDataTagChild::Table(table) = child {
collect_images_from_table(table, images, image_bufs);
}
}
}
TableCellContent::TableOfContents(t) => {
for child in &mut t.before_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(paragraph, images, image_bufs);
}
if let TocContent::Table(table) = child {
collect_images_from_table(table, images, image_bufs);
}
}
for child in &mut t.after_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(paragraph, images, image_bufs);
}
if let TocContent::Table(table) = child {
collect_images_from_table(table, images, image_bufs);
}
}
}
}
}
}

View File

@ -23,6 +23,12 @@ impl ElementReader for TableCell {
cell = cell.add_paragraph(p);
continue;
}
XMLElement::StructuredDataTag => {
if let Ok(tag) = StructuredDataTag::read(r, &attributes) {
cell = cell.add_structured_data_tag(tag);
}
continue;
}
XMLElement::TableCellProperty => {
if let Ok(p) = TableCellProperty::read(r, &attributes) {
cell.property = p;

View File

@ -5,8 +5,9 @@ import { TextDirectionType } from "../table-cell";
import { ShadingJSON } from "./shading";
import { TableLayoutType } from "../table";
import { DeleteJSONData, InsertJSONData } from "..";
import { StructuredTagJSON } from "./structured-data-tag";
export type TableCellChildJSON = ParagraphJSON | TableJSON;
export type TableCellChildJSON = ParagraphJSON | TableJSON | StructuredTagJSON;
export type WidthType = "dxa" | "auto" | "pct" | "nil";

View File

@ -5,6 +5,7 @@ import { TableCellBorders, PositionKeys } from "./table-cell-borders";
import { TableCellBorderPosition, TableCellBorder } from "./table-cell-border";
import * as wasm from "./pkg";
import { convertBorderType } from "./run";
import { TableOfContents } from "./table-of-contents";
import { build } from "./builder";
export type VMergeType = "restart" | "continue";
@ -63,7 +64,7 @@ export type CellProperty = {
};
export class TableCell {
children: (Paragraph | Table)[] = [];
children: (Paragraph | Table | TableOfContents)[] = [];
hasNumberings = false;
property: CellProperty = {
borders: new TableCellBorders(),
@ -77,6 +78,11 @@ export class TableCell {
return this;
}
addTableOfContents(t: TableOfContents) {
this.children.push(t);
return this;
}
addTable(t: Table) {
if (t.hasNumberings) {
this.hasNumberings = true;
@ -229,6 +235,12 @@ export class TableCell {
} else if (c instanceof Table) {
const table = c.build();
cell = cell.add_table(table);
} else if (c instanceof TableOfContents) {
cell = cell.add_table_of_contents(c.buildWasmObject());
} else {
// eslint-disable-next-line
const _: never = c;
console.error(_);
}
});

View File

@ -11,6 +11,7 @@ export class TableOfContents {
_hyperlink = false;
_alias = "";
_auto = false;
_withoutSdt = false;
_dirty = false;
_items: TableOfContentsItem[] = [];
_pageRefPlaceholder = "";
@ -77,6 +78,11 @@ export class TableOfContents {
return this;
};
withoutSdt = () => {
this._withoutSdt = true;
return this;
};
delete = (author: string, date: string) => {
this._delete = { author, date };
return this;
@ -114,6 +120,10 @@ export class TableOfContents {
toc = toc.dirty();
}
if (this._withoutSdt) {
toc = toc.without_sdt();
}
if (this._pageRefPlaceholder) {
toc = toc.page_ref_placeholder(this._pageRefPlaceholder);
}

View File

@ -1,6 +1,6 @@
{
"name": "docx-wasm",
"version": "0.0.277-rc4",
"version": "0.0.277-sdt5",
"main": "dist/node/index.js",
"browser": "dist/web/index.js",
"author": "bokuweb <bokuweb12@gmail.com>",

View File

@ -35,6 +35,15 @@ impl TableCell {
self
}
pub fn add_table_of_contents(mut self, t: TableOfContents) -> TableCell {
self.0
.children
.push(docx_rs::TableCellContent::TableOfContents(Box::new(
t.take(),
)));
self
}
pub fn vertical_merge(mut self, t: docx_rs::VMergeType) -> TableCell {
self.0.property = self.0.property.vertical_merge(t);
self

View File

@ -67,6 +67,11 @@ impl TableOfContents {
self
}
pub fn without_sdt(mut self) -> Self {
self.0.without_sdt = true;
self
}
pub fn delete(mut self, author: &str, date: &str) -> Self {
self.0 = self.0.delete(author, date);
self