From b7d5e7ed921d3a4efed147955c970f2503d3822e Mon Sep 17 00:00:00 2001 From: Yusuke Nishiyama Date: Wed, 1 Sep 2021 20:53:46 +0900 Subject: [PATCH] add cell merge (#10) --- src/rtf/destination.rs | 7 +-- src/rtf/docx.rs | 53 +++++++++++++++---- src/rtf/group.rs | 109 ++++++++++++++++++++++++++++++++++++++- src/rtf/table.rs | 18 +++++++ src/rtf/text.rs | 114 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 274 insertions(+), 27 deletions(-) diff --git a/src/rtf/destination.rs b/src/rtf/destination.rs index 04d7420..42fdd04 100644 --- a/src/rtf/destination.rs +++ b/src/rtf/destination.rs @@ -54,11 +54,8 @@ impl Destination { return; } if let Destination::Text(text) = self { - if !in_table { - text.last_or_new_paragraph(stylesheet, para_style, in_table); - } else { - /* NOP */ - } + text.last_or_new_paragraph(stylesheet, para_style, in_table); + { let base_encoding = text.encoding; let line = text.last_or_new_line(font, style, Some(encoding)); diff --git a/src/rtf/docx.rs b/src/rtf/docx.rs index f98b2cc..67272a0 100644 --- a/src/rtf/docx.rs +++ b/src/rtf/docx.rs @@ -24,6 +24,15 @@ impl Docx for Rtf { } } } + impl std::convert::Into for CellVerticalAlignment { + fn into(self) -> docx_rs::VAlignType { + match self { + Self::Top => VAlignType::Top, + Self::Center => VAlignType::Center, + Self::Bottom => VAlignType::Bottom, + } + } + } impl std::convert::Into for table_border::BorderType { fn into(self) -> docx_rs::BorderType { match self { @@ -132,6 +141,7 @@ impl Docx for Rtf { _ => crate::rtf::Text::decode_line(encoding, &line), }; let run_font = RunFonts::new().east_asia(font.font_name.clone()); + run = run.fonts(run_font); // println!("{} {:?} {:?}", text, encoding, font.charset); text @@ -216,7 +226,7 @@ impl Docx for Rtf { let mut rows: Vec = vec![]; let mut border = None; let mut grid: Vec = vec![]; - let mut make_grid = true; + let mut make_grid = false; for rtf_row in table.rows { if rtf_row.border.is_some() { @@ -228,6 +238,7 @@ impl Docx for Rtf { } let mut cells: Vec = vec![]; let cell_len = rtf_row.cells.len(); + for (cell_index, rtf_cell) in rtf_row.cells.into_iter().enumerate() { if cell_index == cell_len - 1 && rtf_cell.is_empty() { @@ -250,9 +261,33 @@ impl Docx for Rtf { } else { } + if rtf_cell.opts.vert_merge_root { + cell = cell.vertical_merge(VMergeType::Restart); + } else if rtf_cell.opts.vert_merged_cell { + cell = cell.vertical_merge(VMergeType::Continue); + } else { + } + cell = cell.vertical_align(rtf_cell.opts.vert_align.into()); + for para in rtf_cell.paras { + let para_style = + para.style.as_ref().unwrap_or(&stylesheet_para); + let align = para_style + .align + .as_ref() + .or_else(|| stylesheet_para.align.as_ref()); + + let first_indent = para_style + .first_indent + .as_ref() + .or_else(|| stylesheet_para.first_indent.as_ref()); + let special_indent = first_indent.map(|indent| { + SpecialIndentType::FirstLine(indent.clone()) + }); + let make_paragrah = || { let mut p = Paragraph::new(); + if let Some(align) = align { p = p.align(align.clone().into()); } @@ -271,15 +306,10 @@ impl Docx for Rtf { let mut runs: VecDeque = VecDeque::new(); let process_run = |cell: TableCell, runs: &mut VecDeque| { - if runs.len() > 0 { + if !runs.is_empty() { let mut p = make_paragrah(); - loop { - if let Some(run) = runs.pop_front() { - // println!("cell run {:?}", run); - p = p.add_run(run.clone()); - } else { - break; - } + while let Some(run) = runs.pop_front() { + p = p.add_run(run.clone()); } cell.add_paragraph(p) } else { @@ -307,7 +337,7 @@ impl Docx for Rtf { cell = process_run(cell, &mut runs); } if let Some(width) = width { - cell = cell.width(width, WidthType::Auto); + cell = cell.width(width, WidthType::DXA); if make_grid { grid.push(Twips::from_px(width).into()); } @@ -316,10 +346,11 @@ impl Docx for Rtf { grid = vec![]; make_grid = false; } - // println!("{:?}", cell); + cells.push(cell); } let row = docx_rs::TableRow::new(cells); + rows.push(row); make_grid = false; } diff --git a/src/rtf/group.rs b/src/rtf/group.rs index 3ce0fae..47b52a8 100644 --- a/src/rtf/group.rs +++ b/src/rtf/group.rs @@ -374,7 +374,14 @@ impl GroupState { } else { let last_paragraph = text.last_paragraph(false); if let Some(table) = last_paragraph.table.as_mut() { - table.add_row(); + if table.last_row().is_last { + let mut p = Paragraph::new(); + p.table = Some(Table::new()); + text.last_section().paras.push(Paragraph::new()); + text.last_section().paras.push(p); + } else { + table.add_row(); + } } else { last_paragraph.table = Some(Table::new()); } @@ -407,6 +414,32 @@ impl GroupState { text.set_border_width(self.border_select.clone(), border_width); } } + pub fn set_row_last(&mut self) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_row_last(); + } + } + pub fn fit_text(&mut self, twips: i32) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.fit_text(twips); + } + } pub fn set_cell_right(&mut self, right: Twips) { let dest_name = match self.get_destination_name() { Some(name) => name.clone(), @@ -420,6 +453,71 @@ impl GroupState { text.set_cell_right(right); } } + pub fn set_cell_vert_merge_root(&mut self) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_cell_vert_merge_root(); + } + } + pub fn set_cell_vert_align(&mut self, align: CellVerticalAlignment) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_cell_vert_align(align); + } + } + pub fn set_cell_vert_merged_cell(&mut self) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_cell_vert_merged_cell(); + } + } + pub fn set_cell_horiz_merge_root(&mut self) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_cell_horiz_merge_root(); + } + } + pub fn set_cell_horiz_merged_cell(&mut self) { + let dest_name = match self.get_destination_name() { + Some(name) => name.clone(), + None => { + warn!("Document format error: Document text found outside of any document group",); + return; + } + }; + if let Some(Destination::Text(text)) = (*self.destinations).borrow_mut().get_mut(&dest_name) + { + text.set_cell_horiz_merged_cell(); + } + } pub fn set_value(&mut self, name: &str, value: Option) { match name { "f" => { @@ -433,6 +531,9 @@ impl GroupState { self.set_row(); } "intbl" => {} + "lastrow" => { + self.set_row_last(); + } "trbrdrt" => self.border_select = BorderSelect::RowTop, "trbrdrl" => self.border_select = BorderSelect::RowLeft, "trbrdrb" => self.border_select = BorderSelect::RowBottom, @@ -452,6 +553,12 @@ impl GroupState { "brdrhair" => self.set_border_type(BorderType::Hairline), "brdrnone" => self.set_border_type(BorderType::None), "brdrw" => self.set_border_width(value.unwrap_or(0) as usize), + "clvmgf" => self.set_cell_vert_merge_root(), + "clvmrg" => self.set_cell_vert_merged_cell(), + "clvertalt" => self.set_cell_vert_align(CellVerticalAlignment::Top), + "clvertalc" => self.set_cell_vert_align(CellVerticalAlignment::Center), + "clvertalb" => self.set_cell_vert_align(CellVerticalAlignment::Bottom), + "fittext" => self.fit_text(value.unwrap_or(-1)), "cellx" => { if let Some(value) = value { self.set_cell_right((value as usize).into()) diff --git a/src/rtf/table.rs b/src/rtf/table.rs index 96e7ef3..8811230 100644 --- a/src/rtf/table.rs +++ b/src/rtf/table.rs @@ -4,12 +4,28 @@ pub use super::*; pub struct TableCellOption { pub border: Option, pub right: Option, + pub vert_align: CellVerticalAlignment, + pub vert_merge_root: bool, + pub vert_merged_cell: bool, + pub horiz_merge_root: bool, + pub horiz_merged_cell: bool, +} +#[derive(Clone, Debug)] +pub enum CellVerticalAlignment { + Top, + Center, + Bottom, } impl TableCellOption { pub fn new() -> Self { Self { border: None, right: None, + vert_merge_root: false, + vert_merged_cell: false, + horiz_merge_root: false, + horiz_merged_cell: false, + vert_align: CellVerticalAlignment::Top, } } } @@ -36,6 +52,7 @@ pub struct TableRow { pub border: Option, pub cell_opt_pos: usize, pub cell_opts: Vec, + pub is_last: bool, } impl TableRow { pub fn new() -> TableRow { @@ -44,6 +61,7 @@ impl TableRow { border: None, cell_opt_pos: 0, cell_opts: vec![TableCellOption::new()], + is_last: false, } } pub fn add_cell(&mut self) { diff --git a/src/rtf/text.rs b/src/rtf/text.rs index 6c5669b..3504c23 100644 --- a/src/rtf/text.rs +++ b/src/rtf/text.rs @@ -49,6 +49,7 @@ pub struct Line { pub font: Option, pub style: Option, pub encoding: Option<&'static encoding_rs::Encoding>, + pub fit_text: Option, } impl Line { pub fn new() -> Line { @@ -57,6 +58,7 @@ impl Line { font: None, style: None, encoding: None, + fit_text: None, } } } @@ -209,9 +211,28 @@ impl Text { } pub fn new_paragraph(&mut self, follow_table: bool) { if follow_table { - let last_para = self.last_paragraph(follow_table); + let parent = { + self.pages + .last() + .expect("must have page") + .sections + .last() + .expect("must have section") + .paras + .last() + .cloned() + }; + + let last_para = self.last_paragraph(false); if let Some(table) = last_para.table.as_mut() { - table.last_cell().paras.push(Paragraph::new()); + let mut paragraph = Paragraph::new(); + + if let Some(parent) = parent { + paragraph.style = parent.style.clone(); + paragraph.stylesheet = parent.stylesheet; + } + table.last_cell().paras.push(paragraph); + println!("added paragraph {:?}", table.last_cell().paras); } } else { self.last_section().paras.push(Paragraph::new()); @@ -228,7 +249,7 @@ impl Text { let para = self.last_paragraph(in_table); ( - para.lines.len() > 1 || para.lines.last().unwrap().bytes.len() > 0, + para.lines.len() > 1 || !para.lines.last().unwrap().bytes.is_empty(), para.style.clone(), para.stylesheet, had_table, @@ -238,12 +259,14 @@ impl Text { self.last_section().paras.push(Paragraph::new()); let new_para = self.last_paragraph(false); new_para.stylesheet = stylesheet; - new_para.style = style; + new_para.style = style.clone(); if in_table { new_para.table = Some(Table::new()); } - - self.last_paragraph(in_table) + let pr = self.last_paragraph(in_table); + pr.stylesheet = stylesheet; + pr.style = style; + pr } else if used && (para_style != style || para_stylesheet != stylesheet) { { if self.last_line().bytes.is_empty() { @@ -258,6 +281,7 @@ impl Text { new_para } else { let para = self.last_paragraph(in_table); + if para.stylesheet.is_none() { para.stylesheet = stylesheet; } @@ -420,19 +444,89 @@ impl Text { border.width = border_width; } } + pub fn set_row_last(&mut self) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + + last_row.is_last = true; + } + } + pub fn fit_text(&mut self, twips: i32) { + let line = &mut self.last_line(); + + if twips > 0 { + line.fit_text = Some(Twips::from(twips as usize)); + } + } pub fn set_cell_right(&mut self, right: Twips) { let table = &mut self.last_paragraph(false).table; if let Some(table) = table { let mut last_row = table.last_row(); if last_row.cell_opt_pos == 0 { last_row.cells[0].opts.right = Some(right); - } else { - if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { - opt.right = Some(right); - } + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.right = Some(right); } last_row.cell_opt_pos = last_row.cell_opt_pos + 1; last_row.cell_opts.push(TableCellOption::new()); } } + pub fn set_cell_vert_align(&mut self, align: CellVerticalAlignment) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + if last_row.cell_opt_pos == 0 { + last_row.cells[0].opts.vert_align = align; + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.vert_align = align; + } + } + } + pub fn set_cell_vert_merge_root(&mut self) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + if last_row.cell_opt_pos == 0 { + last_row.cells[0].opts.vert_merge_root = true; + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.vert_merge_root = true; + } + } + } + pub fn set_cell_vert_merged_cell(&mut self) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + + if last_row.cell_opt_pos == 0 { + last_row.cells[0].opts.vert_merged_cell = true; + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.vert_merged_cell = true; + } + } + } + pub fn set_cell_horiz_merge_root(&mut self) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + if last_row.cell_opt_pos == 0 { + last_row.cells[0].opts.horiz_merge_root = true; + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.horiz_merge_root = true; + } + } + } + pub fn set_cell_horiz_merged_cell(&mut self) { + let table = &mut self.last_paragraph(false).table; + if let Some(table) = table { + let mut last_row = table.last_row(); + + if last_row.cell_opt_pos == 0 { + last_row.cells[0].opts.horiz_merged_cell = true; + } else if let Some(opt) = last_row.cell_opts.get_mut(last_row.cell_opt_pos) { + opt.horiz_merged_cell = true; + } + } + } }