mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-24 00:29:30 +08:00
Merge pull request #2736 from aignatov-bio/ai-sci-4779-tinymce-tables-edge-cases
Add tinymce table support to docx [SCI-4779]
This commit is contained in:
commit
ab9b5c6d64
4 changed files with 126 additions and 47 deletions
|
@ -37,6 +37,49 @@ class Reports::Docx
|
|||
def self.link_prepare(scinote_url, link)
|
||||
link[0] == '/' ? scinote_url + link : link
|
||||
end
|
||||
|
||||
def self.render_p_element(docx, element, options = {})
|
||||
scinote_url = options[:scinote_url]
|
||||
link_style = options[:link_style]
|
||||
docx.p do
|
||||
element[:children].each do |text_el|
|
||||
if text_el[:type] == 'text'
|
||||
style = text_el[:style] || {}
|
||||
text text_el[:value], style
|
||||
text ' ' if text_el[:value] != ''
|
||||
elsif text_el[:type] == 'br' && !options[:skip_br]
|
||||
br
|
||||
elsif text_el[:type] == 'a'
|
||||
if text_el[:link]
|
||||
link_url = Reports::Docx.link_prepare(scinote_url, text_el[:link])
|
||||
link text_el[:value], link_url, link_style
|
||||
else
|
||||
text text_el[:value], link_style
|
||||
end
|
||||
text ' ' if text_el[:value] != ''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.render_img_element(docx, element, options = {})
|
||||
style = element[:style]
|
||||
|
||||
if options[:table]
|
||||
max_width = (style[:max_width] / options[:table][:columns].to_f)
|
||||
if style[:width] > max_width
|
||||
style[:height] = (max_width / style[:width].to_f) * style[:height]
|
||||
style[:width] = max_width
|
||||
end
|
||||
end
|
||||
|
||||
docx.img element[:data] do
|
||||
data element[:blob].download
|
||||
width style[:width]
|
||||
height style[:height]
|
||||
align style[:align] || :left
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Style/ClassAndModuleChildren
|
||||
|
|
|
@ -5,49 +5,18 @@ module Reports::Docx::PrivateMethods
|
|||
|
||||
# RTE fields support
|
||||
def html_to_word_converter(text)
|
||||
link_style = @link_style
|
||||
scinote_url = @scinote_url
|
||||
html = Nokogiri::HTML(text)
|
||||
raw_elements = recursive_children(html.css('body').children, [])
|
||||
|
||||
elements = []
|
||||
temp_p = []
|
||||
|
||||
# Combined raw text blocks in paragraphs
|
||||
raw_elements.each do |elem|
|
||||
if elem[:type] == 'image' || elem[:type] == 'newline'
|
||||
unless temp_p.empty?
|
||||
elements.push(type: 'p', children: temp_p)
|
||||
temp_p = []
|
||||
end
|
||||
elements.push(elem)
|
||||
elsif %w(br text a).include? elem[:type]
|
||||
temp_p.push(elem)
|
||||
end
|
||||
end
|
||||
elements.push(type: 'p', children: temp_p)
|
||||
elements = combine_docx_elements(raw_elements)
|
||||
|
||||
# Draw elements
|
||||
elements.each do |elem|
|
||||
if elem[:type] == 'p'
|
||||
@docx.p do
|
||||
elem[:children].each do |text_el|
|
||||
if text_el[:type] == 'text'
|
||||
style = text_el[:style] || {}
|
||||
text text_el[:value], style
|
||||
text ' ' if text_el[:value] != ''
|
||||
elsif text_el[:type] == 'br'
|
||||
br
|
||||
elsif text_el[:type] == 'a'
|
||||
if text_el[:link]
|
||||
link_url = Reports::Docx.link_prepare(scinote_url, text_el[:link])
|
||||
link text_el[:value], link_url, link_style
|
||||
else
|
||||
text text_el[:value], link_style
|
||||
end
|
||||
text ' ' if text_el[:value] != ''
|
||||
end
|
||||
end
|
||||
end
|
||||
Reports::Docx.render_p_element(@docx, elem, scinote_url: @scinote_url, link_style: @link_style)
|
||||
elsif elem[:type] == 'table'
|
||||
tiny_mce_table(elem[:data])
|
||||
elsif elem[:type] == 'newline'
|
||||
style = elem[:style] || {}
|
||||
@docx.p elem[:value] do
|
||||
|
@ -58,19 +27,31 @@ module Reports::Docx::PrivateMethods
|
|||
style style[:style] if style[:style]
|
||||
end
|
||||
elsif elem[:type] == 'image'
|
||||
style = elem[:style]
|
||||
@docx.img elem[:data] do
|
||||
data elem[:blob].download
|
||||
width style[:width]
|
||||
height style[:height]
|
||||
align style[:align] || :left
|
||||
end
|
||||
Reports::Docx.render_img_element(@docx, elem)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def combine_docx_elements(raw_elements)
|
||||
elements = []
|
||||
temp_p = []
|
||||
raw_elements.each do |elem|
|
||||
if %w(image newline table).include? elem[:type]
|
||||
unless temp_p.empty?
|
||||
elements.push(type: 'p', children: temp_p)
|
||||
temp_p = []
|
||||
end
|
||||
elements.push(elem)
|
||||
elsif %w(br text a).include? elem[:type]
|
||||
temp_p.push(elem)
|
||||
end
|
||||
end
|
||||
elements.push(type: 'p', children: temp_p)
|
||||
elements
|
||||
end
|
||||
|
||||
# Convert HTML structure to plain text structure
|
||||
def recursive_children(children, elements)
|
||||
def recursive_children(children, elements, options = {})
|
||||
children.each do |elem|
|
||||
if elem.class == Nokogiri::XML::Text
|
||||
next if elem.text.strip == ' ' # Invisible symbol
|
||||
|
@ -100,6 +81,9 @@ module Reports::Docx::PrivateMethods
|
|||
|
||||
image_path = image_path(image.image)
|
||||
dimension = FastImage.size(image_path)
|
||||
|
||||
next unless dimension
|
||||
|
||||
style = image_styling(elem, dimension)
|
||||
|
||||
elements.push(
|
||||
|
@ -112,7 +96,16 @@ module Reports::Docx::PrivateMethods
|
|||
end
|
||||
|
||||
if elem.name == 'a'
|
||||
elements.push(link_prepare(elem))
|
||||
elements.push(link_element(elem))
|
||||
next
|
||||
end
|
||||
|
||||
if elem.name == 'table'
|
||||
elem = tiny_mce_table(elem, nested_table: true) if options[:nested_tables]
|
||||
elements.push(
|
||||
type: 'table',
|
||||
data: elem
|
||||
)
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -121,7 +114,7 @@ module Reports::Docx::PrivateMethods
|
|||
elements
|
||||
end
|
||||
|
||||
def link_prepare(elem)
|
||||
def link_element(elem)
|
||||
text = elem.text
|
||||
link = elem.attributes['href'].value if elem.attributes['href']
|
||||
if elem.attributes['class']&.value == 'record-info-link'
|
||||
|
@ -197,7 +190,8 @@ module Reports::Docx::PrivateMethods
|
|||
{
|
||||
width: x,
|
||||
height: y,
|
||||
align: align
|
||||
align: align,
|
||||
max_width: max_width
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -292,6 +286,46 @@ module Reports::Docx::PrivateMethods
|
|||
}
|
||||
end
|
||||
|
||||
def tiny_mce_table(table_data, options = {})
|
||||
docx_table = []
|
||||
scinote_url = @scinote_url
|
||||
link_style = @link_style
|
||||
table_data.css('tbody').first.children.each do |row|
|
||||
docx_row = []
|
||||
next unless row.name == 'tr'
|
||||
|
||||
row.children.each do |cell|
|
||||
next unless cell.name == 'td'
|
||||
|
||||
# Parse cell content
|
||||
formated_cell = recursive_children(cell.children, [], nested_tables: true)
|
||||
# Combine text elements to single paragraph
|
||||
formated_cell = combine_docx_elements(formated_cell)
|
||||
|
||||
docx_cell = Caracal::Core::Models::TableCellModel.new do |c|
|
||||
formated_cell.each do |cell_content|
|
||||
if cell_content[:type] == 'p'
|
||||
Reports::Docx.render_p_element(c, cell_content,
|
||||
scinote_url: scinote_url, link_style: link_style, skip_br: true)
|
||||
elsif cell_content[:type] == 'table'
|
||||
c.table formated_cell_content[:data]
|
||||
elsif cell_content[:type] == 'image'
|
||||
Reports::Docx.render_img_element(c, cell_content, table: { columns: row.children.length / 3 })
|
||||
end
|
||||
end
|
||||
end
|
||||
docx_row.push(docx_cell)
|
||||
end
|
||||
docx_table.push(docx_row)
|
||||
end
|
||||
|
||||
if options[:nested_table]
|
||||
docx_table
|
||||
else
|
||||
@docx.table docx_table
|
||||
end
|
||||
end
|
||||
|
||||
def image_path(attachment)
|
||||
attachment.service_url
|
||||
end
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
<% if my_module.description.present? %>
|
||||
<%= custom_auto_link(my_module.prepare_for_report(:description, for_export_all),
|
||||
team: current_team,
|
||||
simple_format: false,
|
||||
base64_encoded_imgs: for_export_all) %>
|
||||
<% else %>
|
||||
<em><%=t "projects.reports.elements.module.no_description" %></em>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<% if protocol.description.present? %>
|
||||
<%= custom_auto_link(protocol.prepare_for_report(:description, for_export_all),
|
||||
team: current_team,
|
||||
simple_format: false,
|
||||
base64_encoded_imgs: for_export_all) %>
|
||||
<% else %>
|
||||
<em><%= t('my_modules.protocols.protocol_status_bar.no_description') %></em>
|
||||
|
|
Loading…
Reference in a new issue