Skip to content

Commit

Permalink
fix(html table cell): better support for html lists
Browse files Browse the repository at this point in the history
  • Loading branch information
as-op committed Feb 7, 2024
1 parent 04d22bf commit 7d4a051
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 25 deletions.
71 changes: 61 additions & 10 deletions lib/md_to_pdf/elements/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,11 @@ def data_inlinehtml_tag(tag, node, opts)
when 'img'
result.push({ image: sub.attr('src') })
when 'input'
if sub.attr('type') == 'checkbox'
checkbox = opt_task_list_point_sign(@styles.task_list_point, sub.attributes.key?('checked'))
result.push(text_hash(checkbox, current_opts))
else
data_array, current_opts = handle_unknown_inline_html_tag(sub, node, current_opts)
result.concat(data_array)
end
when 'ul', 'ol', 'li', 'label', 'p'
data_array, current_opts = handle_unknown_inline_html_tag(sub, node, current_opts)
result.concat(data_array)
when 'ul', 'ol'
result.concat(data_inlinehtml_list_tag(sub, node, opts))
when 'label', 'p', 'li'
result.concat(data_inlinehtml_tag(sub, node, opts))
when 'br'
result.push(text_hash_raw("\n", current_opts))
Expand All @@ -126,6 +123,25 @@ def data_inlinehtml_tag(tag, node, opts)
result
end

def data_inlinehtml_list_tag(tag, node, opts)
result = []
points, level, _list_style, content_opts = data_html_list(tag, node, opts)
result.push(text_hash_raw("\n", content_opts).merge({ list_level: level, list_indent: 0 })) if level > 1
points.each do |point|
data = data_inlinehtml_tag(point[:tag], node, content_opts)
data.push(text_hash_raw("\n", content_opts).merge({ list_entry_type: 'end' }))
data[0][:list_entry_type] = 'first' unless data.empty?
data.unshift(text_hash(point[:bullet], point[:opts]).merge({ list_entry_type: 'bullet' }))
data.each do |item|
item[:list_level] = level if item[:list_level].nil?
item[:list_indent] = point[:width] if item[:list_indent].nil?
item[:list_indent_space] = point[:space_width] if item[:list_indent_space].nil?
end
result.concat(data)
end
result
end

def collect_html_table_tag_rows(tag, table_font_opts, opts)
rows = []
tag.children.each do |sub|
Expand Down Expand Up @@ -257,16 +273,51 @@ def collect_html_table_tag_cell(tag, opts)
cell_data
end

def space_stuffing(width, space_width)
amount = (width / space_width).truncate
return '' if amount < 1

Prawn::Text::NBSP * amount
end

def indent_html_table_list_items(cell_data)
cell_data.each do |item|
next if item[:list_level].nil?

# Note: There is no settings for paddings of text fragments in Prawn::Table
# so as a workaround the lists are stuffed with spaces, which is of course not pixel perfect

# first indenting with spaces of multiline list items
# * item
# multiline item
# multiline item
if item[:list_entry_type].nil? && item[:text] != "\n"
item[:text] = "#{space_stuffing(item[:list_indent], item[:list_indent_space])}#{item[:text]}"
end

# second indenting of nested lists
# * item
# multiline item
# * sub list item
# * sub list item
# sub list multiline item
if item[:list_level] > 1 && (item[:list_entry_type].nil? || item[:list_entry_type] == 'bullet') && item[:text] != "\n"
item[:text] = "#{space_stuffing(item[:list_indent], item[:list_indent_space])}#{item[:text]}"
end
end
cell_data
end

def collect_html_table_tag_row(tag, table_font_opts, opts)
cells = []
tag.children.each do |sub|
case sub.name
when 'th'
cell_data = collect_html_table_tag_cell(sub, opts.merge(table_font_opts[:header]))
cells.push(cell_data)
cells.push(indent_html_table_list_items(cell_data))
when 'td'
cell_data = collect_html_table_tag_cell(sub, opts.merge(table_font_opts[:cell]))
cells.push(cell_data)
cells.push(indent_html_table_list_items(cell_data))
end
end
cells
Expand Down
34 changes: 24 additions & 10 deletions lib/md_to_pdf/elements/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,25 @@ def draw_list(node, opts)
end
end

def draw_html_list_tag(tag, node, opts)
def data_html_list(tag, node, opts)
level = count_list_level_html(tag)
is_ordered = tag.name.downcase == 'ol'
list_style = list_style(level, is_ordered, false)
padding = level == 1 ? opts_padding(list_style) : {}
# point_inline = opt_list_point_inline?(list_style)
is_task_list = !tag.search("input[type=checkbox]").first.nil?
list_style = list_style(level, is_ordered, is_task_list)
content_opts = opts_font(list_style, opts).merge(
{
force_paragraph: {
bottom_padding: opt_list_point_spacing(list_style)
}
}
)
points = collect_points_html(tag, level, is_ordered, content_opts)
points = collect_points_html(tag, level, is_ordered, is_task_list, content_opts)
[points, level, list_style, content_opts]
end

def draw_html_list_tag(tag, node, opts)
points, level, list_style, content_opts = data_html_list(tag, node, opts)
padding = level == 1 ? opts_padding(list_style) : {}
with_block_padding_all(padding) do
draw_points_html(points, node, content_opts)
end
Expand Down Expand Up @@ -82,22 +87,31 @@ def draw_inline_points(points, opts)
end
end

def collect_points_html(tag, level, is_ordered, content_opts)
point_style = list_point_style(level, is_ordered, false)
def collect_points_html(tag, level, is_ordered, is_task_list, content_opts)
point_style = list_point_style(level, is_ordered, is_task_list)
auto_span = opt_list_point_spanning?(point_style)
bullet_opts = opts_font(point_style, content_opts)
spacing = opt_list_point_spacing(point_style)
points = []
index = 1
tag.children.each do |sub|
if sub.name.downcase == 'li'
bullet = list_bullet(point_style, is_ordered, false, index, false)
checked = false
if is_task_list
checked_box_tag = tag.search("input[type=checkbox]").first
unless checked_box_tag.nil?
checked = checked_box_tag.attributes.key?('checked')
checked_box_tag.remove
end
end
bullet = list_bullet(point_style, is_ordered, is_task_list, index, checked)
bullet_width = measure_text_width(bullet, bullet_opts) + spacing
points.push({ tag: sub, bullet: bullet, width: bullet_width, opts: bullet_opts })
space_width = measure_text_width(Prawn::Text::NBSP, bullet_opts)
points.push({ tag: sub, bullet: bullet, width: bullet_width, space_width: space_width, opts: bullet_opts })
index += 1
end
end
if auto_span
if auto_span || is_task_list
max_span = max_point_width(points)
points.each { |point| point[:width] = max_span }
end
Expand Down
1 change: 1 addition & 0 deletions spec/fixtures/list/tasklist_html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<ul><li><input type="checkbox" disabled="disabled">aha</li><li><input type="checkbox" checked="checked">oho</li><li><input type="checkbox" disabled="disabled">ehe</li></ul>
4 changes: 4 additions & 0 deletions spec/fixtures/table/linefeed_in_cell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| Description Column 1 |
|-------------------------------|
| Lorem ipsum dolor<br>sit amet |
| Consectetur<br/>adipiscing elit |
1 change: 1 addition & 0 deletions spec/fixtures/table/list_in_cell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<figure><table><tbody><tr><td><ul><li>test1</li><li>test2<br>test2cont</li><li>test3<ul><li>test3.1</li><li>test3.2<br>test3.2cont</li><li>test3.3</li></ul></li></ul></td><td><ul><li><label><input type="checkbox" disabled="disabled">aha</label></li><li><label><input type="checkbox" checked="checked">oho</label></li><li><label><input type="checkbox" disabled="disabled">ehe</label></li></ul></td><td><ol><li>wooo</li><li>waaa</li><li>wiiiii</li></ol></td></tr></tbody></table></figure>
11 changes: 11 additions & 0 deletions spec/markdown_to_pdf/list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@
{ x: 76.032, y: 553.176, text: "three" }])
end

it 'creates task lists by html' do
generator.parse_file('list/tasklist_html.md', {})
expect_pdf([
{ x: 36.0, y: 747.384, text: "[ ]" },
{ x: 52.008, y: 747.384, text: "aha" },
{ x: 36.0, y: 733.512, text: "[x]" },
{ x: 52.008, y: 733.512, text: "oho" },
{ x: 36.0, y: 719.64, text: "[ ]" },
{ x: 52.008, y: 719.64, text: "ehe" }])
end

it 'creates an ordered list with correct numbers inline' do
generator.parse_file('list/inline.md', { ordered_list: { point_inline: true } })
expect_pdf([
Expand Down
39 changes: 34 additions & 5 deletions spec/markdown_to_pdf/table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@
{ x: 421.71429, y: 744.756, text: "Header 5" },
{ x: 498.85714, y: 744.756, text: "Header 6" },
{ x: 36.0, y: 730.884, text: "Entry 1" },
{ x: 344.57143, y: 730.884, text: "[x]" },
{ x: 498.85714, y: 730.884, text: "[x]" },
{ x: 344.57143, y: 730.884, text: "[x] " },
{ x: 498.85714, y: 730.884, text: "[x] " },
{ x: 36.0, y: 717.012, text: "Entry 2" },
{ x: 344.57143, y: 717.012, text: "[x]" },
{ x: 421.71429, y: 717.012, text: "[x]" },
{ x: 344.57143, y: 717.012, text: "[x] " },
{ x: 421.71429, y: 717.012, text: "[x] " },
{ x: 36.0, y: 703.14, text: "Entry 3" },
{ x: 36.0, y: 689.268, text: "Entry 4" },
{ x: 113.14286, y: 689.268, text: "[x]" }])
{ x: 113.14286, y: 689.268, text: "[x] " }])
end

it 'creates a table without bad wrapping with doc font style' do
Expand Down Expand Up @@ -163,4 +163,33 @@
{ x: 36.0, y: 689.268, text: "Color empty cells to the" },
{ x: 36.0, y: 675.396, text: "right" }])
end

it 'creates a html table with linefeeds inside' do
generator.parse_file('table/linefeed_in_cell.md')
expect_pdf([
{ x: 36.0, y: 744.756, text: "Description Column 1" },
{ x: 36.0, y: 730.884, text: "Lorem ipsum dolor" },
{ x: 36.0, y: 717.012, text: "sit amet" },
{ x: 36.0, y: 703.14, text: "Consectetur" },
{ x: 36.0, y: 689.268, text: "adipiscing elit" }])
end

it 'creates a html table with lists inside' do
generator.parse_file('table/list_in_cell.md')
expect_pdf([
{x:36.0, y:744.756, text:"• test1"},
{x:36.0, y:730.884, text:"• test2"},
{x:36.0, y:717.012, text:"  test2cont"},
{x:36.0, y:703.14, text:"• test3"},
{x:36.0, y:689.268, text:"  • test3.1"},
{x:36.0, y:675.396, text:"  • test3.2"},
{x:36.0, y:661.524, text:"    test3.2cont"},
{x:36.0, y:647.652, text:"  • test3.3"},
{x:216.0, y:744.756, text:"[ ] aha"},
{x:216.0, y:730.884, text:"[x] oho"},
{x:216.0, y:717.012, text:"[ ] ehe"},
{x:396.0, y:744.756, text:"1. wooo"},
{x:396.0, y:730.884, text:"2. waaa"},
{x:396.0, y:717.012, text:"3. wiiiii"}])
end
end

0 comments on commit 7d4a051

Please sign in to comment.