mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-18 23:21:30 +08:00
180 lines
6 KiB
Ruby
180 lines
6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Reports
|
|
class DocxRenderer
|
|
def self.render_p_element(docx, element, options = {})
|
|
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'
|
|
Reports::DocxRenderer.render_link_element(self, text_el, options)
|
|
elsif text_el[:type] == 'sup'
|
|
text text_el[:value] do
|
|
vertical_align 'superscript'
|
|
end
|
|
elsif text_el[:type] == 'sub'
|
|
text text_el[:value] do
|
|
vertical_align 'subscript'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.render_link_element(node, link_item, options = {})
|
|
scinote_url = options[:scinote_url]
|
|
link_style = options[:link_style]
|
|
|
|
if link_item[:link]
|
|
link_url = Reports::Utils.link_prepare(scinote_url, link_item[:link])
|
|
node.link link_item[:value], link_url, link_style
|
|
else
|
|
node.text link_item[:value], link_style
|
|
end
|
|
node.text ' ' if link_item[:value] != ''
|
|
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
|
|
|
|
def self.render_list_element(docx, element, options = {})
|
|
bookmark_items = Reports::DocxRenderer.recursive_list_items_renderer(docx, element)
|
|
|
|
bookmark_items.each_with_index do |(key, item), index|
|
|
if item[:type] == 'image'
|
|
docx.bookmark_start id: index, name: key
|
|
docx.p do
|
|
br
|
|
text item[:blob]&.filename.to_s
|
|
end
|
|
Reports::DocxRenderer.render_img_element(docx, item)
|
|
docx.bookmark_end id: index
|
|
elsif item[:type] == 'table'
|
|
docx.bookmark_start id: index, name: key
|
|
|
|
# Bookmark won't work with table only, empty p element added
|
|
docx.p do
|
|
br
|
|
text ''
|
|
end
|
|
Reports::DocxRenderer.render_table_element(docx, item, options)
|
|
docx.bookmark_end id: index
|
|
end
|
|
end
|
|
end
|
|
|
|
# rubocop:disable Metrics/BlockLength
|
|
def self.recursive_list_items_renderer(node, element, bookmark_items: {})
|
|
node.public_send(element[:type]) do
|
|
element[:data].each do |values_array|
|
|
li do
|
|
values_array.each do |item|
|
|
case item
|
|
when Hash
|
|
if %w(ul ol li).include?(item[:type])
|
|
Reports::DocxRenderer.recursive_list_items_renderer(self, item, bookmark_items: bookmark_items)
|
|
elsif %w(a).include?(item[:type])
|
|
Reports::DocxRenderer.render_link_element(self, item)
|
|
elsif %w(image).include?(item[:type])
|
|
bookmark_items[item[:bookmark_id]] = item
|
|
link I18n.t('projects.reports.renderers.lists.appended_image',
|
|
name: item[:blob]&.filename), item[:bookmark_id] do
|
|
internal true
|
|
end
|
|
elsif %w(table).include?(item[:type])
|
|
bookmark_items[item[:bookmark_id]] = item
|
|
link I18n.t('projects.reports.renderers.lists.appended_table'), item[:bookmark_id] do
|
|
internal true
|
|
end
|
|
elsif %w(text).include?(item[:type])
|
|
# TODO: Text with styles, not working yet.
|
|
style = item[:style] || {}
|
|
text item[:value], style
|
|
end
|
|
else
|
|
text item
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
bookmark_items
|
|
end
|
|
# rubocop:enable Metrics/BlockLength
|
|
|
|
def self.render_table_element(docx, element, options = {})
|
|
docx_table = []
|
|
element[:data].each do |row|
|
|
docx_row = []
|
|
row[:data].each do |cell|
|
|
docx_cell = Caracal::Core::Models::TableCellModel.new do |c|
|
|
cell.each do |content|
|
|
if content[:type] == 'p'
|
|
Reports::DocxRenderer.render_p_element(c, content, options.merge({ skip_br: true }))
|
|
elsif content[:type] == 'table'
|
|
Reports::DocxRenderer.render_table_element(c, content, options)
|
|
elsif content[:type] == 'image'
|
|
Reports::DocxRenderer.render_img_element(c, content, table: { columns: row[:data].length })
|
|
end
|
|
end
|
|
end
|
|
docx_row.push(docx_cell)
|
|
end
|
|
docx_table.push(docx_row)
|
|
end
|
|
docx.table docx_table, border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
|
|
end
|
|
|
|
def self.render_asset_image(docx, asset)
|
|
return unless asset
|
|
|
|
asset_preview = Reports::Utils.image_prepare(asset)
|
|
|
|
dimension = FastImage.size(asset_preview.processed.service_url)
|
|
return unless dimension
|
|
|
|
x = dimension[0]
|
|
y = dimension[1]
|
|
if x > 300
|
|
y = y * 300 / x
|
|
x = 300
|
|
end
|
|
|
|
blob_data = if asset_preview.instance_of? ActiveStorage::Preview
|
|
asset_preview.image.download
|
|
else
|
|
asset_preview.blob.download
|
|
end
|
|
|
|
docx.img asset_preview.processed.service_url.split('&')[0] do
|
|
data blob_data
|
|
width x
|
|
height y
|
|
end
|
|
rescue SocketError, Caracal::Errors::InvalidModelError => e # invalid URL or broken image
|
|
Rails.logger.warn("Unable to render docx image due to #{e.class}: #{e}")
|
|
nil
|
|
end
|
|
end
|
|
end
|